From 78334b0d4607b242273f9d0cde94f6a6fba51832 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Wed, 26 Sep 2018 20:50:34 +0000 Subject: luaotfload (26sep18) git-svn-id: svn://tug.org/texlive/trunk@48771 c570f23f-e606-0410-a88d-b1316a301751 --- .../luatex/luaotfload/fontloader-2017-02-11.lua | 21726 -------------- .../luatex/luaotfload/fontloader-2018-09-24.lua | 29266 +++++++++++++++++++ .../luatex/luaotfload/fontloader-basics-gen.lua | 187 +- .../luatex/luaotfload/fontloader-basics-nod.lua | 251 +- .../tex/luatex/luaotfload/fontloader-basics.tex | 93 - .../tex/luatex/luaotfload/fontloader-font-afk.lua | 2 +- .../tex/luatex/luaotfload/fontloader-font-cff.lua | 1260 +- .../tex/luatex/luaotfload/fontloader-font-cid.lua | 2 +- .../tex/luatex/luaotfload/fontloader-font-con.lua | 405 +- .../tex/luatex/luaotfload/fontloader-font-def.lua | 205 +- .../tex/luatex/luaotfload/fontloader-font-dsp.lua | 2108 +- .../tex/luatex/luaotfload/fontloader-font-gbn.lua | 251 - .../luaotfload/fontloader-font-imp-effects.lua | 414 + .../luaotfload/fontloader-font-imp-italics.lua | 147 + .../luaotfload/fontloader-font-imp-ligatures.lua | 136 + .../luatex/luaotfload/fontloader-font-imp-tex.lua | 144 + .../tex/luatex/luaotfload/fontloader-font-ini.lua | 29 +- .../tex/luatex/luaotfload/fontloader-font-map.lua | 216 +- .../tex/luatex/luaotfload/fontloader-font-ocl.lua | 714 +- .../tex/luatex/luaotfload/fontloader-font-one.lua | 24 +- .../tex/luatex/luaotfload/fontloader-font-onr.lua | 62 +- .../tex/luatex/luaotfload/fontloader-font-osd.lua | 1019 +- .../tex/luatex/luaotfload/fontloader-font-ota.lua | 22 +- .../tex/luatex/luaotfload/fontloader-font-otc.lua | 342 +- .../tex/luatex/luaotfload/fontloader-font-oti.lua | 100 + .../tex/luatex/luaotfload/fontloader-font-otj.lua | 1181 +- .../tex/luatex/luaotfload/fontloader-font-otl.lua | 193 +- .../tex/luatex/luaotfload/fontloader-font-oto.lua | 174 +- .../tex/luatex/luaotfload/fontloader-font-otr.lua | 1056 +- .../tex/luatex/luaotfload/fontloader-font-ots.lua | 4353 +-- .../tex/luatex/luaotfload/fontloader-font-ott.lua | 1159 + .../tex/luatex/luaotfload/fontloader-font-oup.lua | 953 +- .../tex/luatex/luaotfload/fontloader-font-tfm.lua | 14 +- .../tex/luatex/luaotfload/fontloader-font-ttf.lua | 1233 +- .../tex/luatex/luaotfload/fontloader-font-vfc.lua | 99 + .../tex/luatex/luaotfload/fontloader-fonts-def.lua | 98 + .../luaotfload/fontloader-fonts-demo-vf-1.lua | 52 - .../tex/luatex/luaotfload/fontloader-fonts-enc.lua | 3 +- .../tex/luatex/luaotfload/fontloader-fonts-ext.lua | 211 +- .../tex/luatex/luaotfload/fontloader-fonts-gbn.lua | 300 + .../tex/luatex/luaotfload/fontloader-fonts-lig.lua | 2067 ++ .../tex/luatex/luaotfload/fontloader-fonts-mis.lua | 31 + .../tex/luatex/luaotfload/fontloader-fonts-syn.lua | 7 +- .../tex/luatex/luaotfload/fontloader-fonts.lua | 324 - .../tex/luatex/luaotfload/fontloader-fonts.tex | 140 - .../tex/luatex/luaotfload/fontloader-l-file.lua | 62 +- .../tex/luatex/luaotfload/fontloader-l-io.lua | 266 +- .../tex/luatex/luaotfload/fontloader-l-lpeg.lua | 300 +- .../tex/luatex/luaotfload/fontloader-l-lua.lua | 69 +- .../tex/luatex/luaotfload/fontloader-l-math.lua | 119 +- .../tex/luatex/luaotfload/fontloader-l-string.lua | 28 +- .../tex/luatex/luaotfload/fontloader-l-table.lua | 175 +- .../tex/luatex/luaotfload/fontloader-l-unicode.lua | 1361 + .../tex/luatex/luaotfload/fontloader-languages.lua | 50 - .../tex/luatex/luaotfload/fontloader-languages.tex | 19 - .../tex/luatex/luaotfload/fontloader-math.lua | 53 - .../tex/luatex/luaotfload/fontloader-math.tex | 1874 -- .../tex/luatex/luaotfload/fontloader-mplib.lua | 591 - .../tex/luatex/luaotfload/fontloader-mplib.tex | 140 - .../tex/luatex/luaotfload/fontloader-plain.tex | 55 - .../luaotfload/fontloader-preprocessor-test.tex | 30 - .../luatex/luaotfload/fontloader-preprocessor.lua | 163 - .../luatex/luaotfload/fontloader-preprocessor.tex | 14 - .../tex/luatex/luaotfload/fontloader-reference.lua | 20635 +++++++++---- .../luatex/luaotfload/fontloader-swiglib-test.lua | 743 - .../luatex/luaotfload/fontloader-swiglib-test.tex | 11 - .../tex/luatex/luaotfload/fontloader-swiglib.lua | 63 - .../tex/luatex/luaotfload/fontloader-swiglib.tex | 20 - .../tex/luatex/luaotfload/fontloader-test.tex | 173 - .../tex/luatex/luaotfload/fontloader-util-fil.lua | 243 +- .../tex/luatex/luaotfload/fontloader-util-str.lua | 510 +- .../tex/luatex/luaotfload/luaotfload-auxiliary.lua | 2 +- .../luatex/luaotfload/luaotfload-characters.lua | 17820 +++++++---- .../tex/luatex/luaotfload/luaotfload-colors.lua | 2 +- .../luatex/luaotfload/luaotfload-configuration.lua | 2 +- .../tex/luatex/luaotfload/luaotfload-database.lua | 94 +- .../tex/luatex/luaotfload/luaotfload-features.lua | 18 +- .../tex/luatex/luaotfload/luaotfload-filelist.lua | 417 + .../tex/luatex/luaotfload/luaotfload-init.lua | 45 +- .../luatex/luaotfload/luaotfload-letterspace.lua | 27 +- .../tex/luatex/luaotfload/luaotfload-log.lua | 2 +- .../tex/luatex/luaotfload/luaotfload-main.lua | 8 +- .../tex/luatex/luaotfload/luaotfload-parsers.lua | 37 +- .../tex/luatex/luaotfload/luaotfload-status.lua | 151 +- .../tex/luatex/luaotfload/luaotfload.sty | 8 +- 85 files changed, 75681 insertions(+), 43492 deletions(-) delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-2017-02-11.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-2018-09-24.lua delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics.tex delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-gbn.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-effects.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-italics.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-ligatures.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-tex.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ott.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-vfc.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-def.lua delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-demo-vf-1.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-gbn.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-lig.lua create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-mis.lua delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts.lua delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts.tex create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-unicode.lua delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-languages.lua delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-languages.tex delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-math.lua delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-math.tex delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-mplib.lua delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-mplib.tex delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-plain.tex delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-preprocessor-test.tex delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-preprocessor.lua delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-preprocessor.tex delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib-test.lua delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib-test.tex delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib.lua delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib.tex delete mode 100644 Master/texmf-dist/tex/luatex/luaotfload/fontloader-test.tex create mode 100644 Master/texmf-dist/tex/luatex/luaotfload/luaotfload-filelist.lua (limited to 'Master/texmf-dist/tex/luatex') diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-2017-02-11.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-2017-02-11.lua deleted file mode 100644 index f8f6f0658a2..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-2017-02-11.lua +++ /dev/null @@ -1,21726 +0,0 @@ ---[[info----------------------------------------------------------------------- - Luaotfload fontloader package - build 2017-02-11 18:16:05 by phg@phlegethon -------------------------------------------------------------------------------- - - © 2017 PRAGMA ADE / ConTeXt Development Team - - The code in this file is provided under the GPL v2.0 license. See the - file COPYING in the Luaotfload repository for details. - - Report bugs to github.com/lualatex/luaotfload - - This file has been assembled from components taken from Context. See - the Luaotfload documentation for details: - - $ texdoc luaotfload - $ man 1 luaotfload-tool - $ man 5 luaotfload.conf - - Included files: - - · fontloader-data-con.lua - · fontloader-basics-nod.lua - · fontloader-font-ini.lua - · fontloader-font-con.lua - · fontloader-fonts-enc.lua - · fontloader-font-cid.lua - · fontloader-font-map.lua - · fontloader-font-oti.lua - · fontloader-font-otr.lua - · fontloader-font-cff.lua - · fontloader-font-ttf.lua - · fontloader-font-dsp.lua - · fontloader-font-oup.lua - · fontloader-font-otl.lua - · fontloader-font-oto.lua - · fontloader-font-otj.lua - · fontloader-font-ota.lua - · fontloader-font-ots.lua - · fontloader-font-osd.lua - · fontloader-font-ocl.lua - · fontloader-font-otc.lua - · fontloader-font-onr.lua - · fontloader-font-one.lua - · fontloader-font-afk.lua - · fontloader-font-tfm.lua - · fontloader-font-lua.lua - · fontloader-font-def.lua - · fontloader-fonts-ext.lua - · fontloader-font-gbn.lua - ---info]]----------------------------------------------------------------------- - - -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(" ") - 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 supnum0 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 - 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 - 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 xxmax then xmax=x end - if yymax 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 xxmax then xmax=x end - if yymax 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 x1xmax then xmax=x1 end - if y1ymax then ymax=y1 end - else - xmin=x1 - ymin=y1 - xmax=x1 - ymax=y1 - checked=true - end - if x2xmax then xmax=x2 end - if y2ymax then ymax=y2 end - if x3xmax then xmax=x3 end - if y3ymax 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("",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("",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 n0 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 unicode0 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 "",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']={ - 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) -local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end) -local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end) -local report_prepare=logs.reporter("fonts","otf prepare") -local fonts=fonts -local otf=fonts.handlers.otf -local otffeatures=otf.features -local registerotffeature=otffeatures.register -otf.defaultbasealternate="none" -local wildcard="*" -local default="dflt" -local formatters=string.formatters -local f_unicode=formatters["%U"] -local f_uniname=formatters["%U (%s)"] -local f_unilist=formatters["% t (% t)"] -local function gref(descriptions,n) - if type(n)=="number" then - local name=descriptions[n].name - if name then - return f_uniname(n,name) - else - return f_unicode(n) - end - elseif n then - local num,nam,j={},{},0 - for i=1,#n do - local ni=n[i] - if tonumber(ni) then - j=j+1 - local di=descriptions[ni] - num[j]=f_unicode(ni) - nam[j]=di and di.name or "-" - end - end - return f_unilist(num,nam) - else - return "" - 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_alternate(feature,sequence,descriptions,unicode,replacement,value,comment) - report_prepare("%s: base alternate %s => %s (%S => %S)", - cref(feature,sequence), - gref(descriptions,unicode), - replacement and gref(descriptions,replacement), - value, - comment) -end -local function report_substitution(feature,sequence,descriptions,unicode,substitution) - report_prepare("%s: base substitution %s => %S", - cref(feature,sequence), - gref(descriptions,unicode), - gref(descriptions,substitution)) -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 -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 - applied={} -end -local function registerbasefeature(feature,value) - applied[#applied+1]=feature.."="..tostring(value) -end -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 - 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 trace_singles then - report_substitution(feature,sequence,descriptions,unicode,data) - end - changed[unicode]=data - end - end - elseif kind=="gsub_alternate" then - for i=1,#steps do - for unicode,data in next,steps[i].coverage do - if not changed[unicode] then - local replacement=data[alternate] - if replacement then - changed[unicode]=replacement - if trace_alternatives then - report_alternate(feature,sequence,descriptions,unicode,replacement,value,"normal") - end - elseif defaultalt=="first" then - replacement=data[1] - changed[unicode]=replacement - if trace_alternatives then - report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt) - end - elseif defaultalt=="last" then - replacement=data[#data] - 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 - 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,"" } - 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 - 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 - 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) -end -local function featuresinitializer(tfmdata,value) - if true then - local starttime=trace_preparing and os.clock() - local features=tfmdata.shared.features - local fullname=tfmdata.properties.fullname or "?" - if features then - 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 - if basesubstitutions or basepositionings then - local sequences=tfmdata.resources.sequences - for s=1,#sequences do - local sequence=sequences[s] - local sfeatures=sequence.features - if sfeatures then - local order=sequence.order - if order then - for i=1,#order do - local feature=order[i] - local value=features[feature] - if value then - local validlookups,lookuplist=collectlookups(rawdata,feature,script,language) - if not validlookups then - elseif basesubstitutions and basesubstitutions[feature] then - if trace_preparing then - report_prepare("filtering base %s feature %a for %a with value %a","sub",feature,fullname,value) - end - preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist) - registerbasefeature(feature,value) - elseif basepositionings and basepositionings[feature] then - if trace_preparing then - report_prepare("filtering base %a feature %a for %a with value %a","pos",feature,fullname,value) - end - preparepositionings(tfmdata,feature,value,validlookups,lookuplist) - registerbasefeature(feature,value) - end - end - end - end - end - end - end - registerbasehash(tfmdata) - end - if trace_preparing then - report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,fullname) - end - end -end -registerotffeature { - name="features", - description="features", - default=true, - initializers={ - base=featuresinitializer, - } -} -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 "" - 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 l1 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 nl 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 ns 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 >> 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 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 position0 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] --- - -if not modules then modules={} end modules ['font-afk']={ - version=1.001, - comment="companion to font-afm.lua", - author="Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright="PRAGMA ADE / ConTeXt Development Team", - license="see context related readme files", - dataonly=true, -} -local allocate=utilities.storage.allocate -fonts.handlers.afm.helpdata={ - ligatures=allocate { - ['f']={ - { 'f','ff' }, - { 'i','fi' }, - { 'l','fl' }, - }, - ['ff']={ - { 'i','ffi' } - }, - ['fi']={ - { 'i','fii' } - }, - ['fl']={ - { 'i','fli' } - }, - ['s']={ - { 't','st' } - }, - ['i']={ - { 'j','ij' } - }, - }, - texligatures=allocate { - ['quoteleft']={ - { 'quoteleft','quotedblleft' } - }, - ['quoteright']={ - { 'quoteright','quotedblright' } - }, - ['hyphen']={ - { 'hyphen','endash' } - }, - ['endash']={ - { 'hyphen','emdash' } - } - }, - leftkerned=allocate { - AEligature="A",aeligature="a", - OEligature="O",oeligature="o", - IJligature="I",ijligature="i", - AE="A",ae="a", - OE="O",oe="o", - IJ="I",ij="i", - Ssharp="S",ssharp="s", - }, - rightkerned=allocate { - AEligature="E",aeligature="e", - OEligature="E",oeligature="e", - IJligature="J",ijligature="j", - AE="E",ae="e", - OE="E",oe="e", - IJ="J",ij="j", - Ssharp="S",ssharp="s", - }, - bothkerned=allocate { - Acircumflex="A",acircumflex="a", - Ccircumflex="C",ccircumflex="c", - Ecircumflex="E",ecircumflex="e", - Gcircumflex="G",gcircumflex="g", - Hcircumflex="H",hcircumflex="h", - Icircumflex="I",icircumflex="i", - Jcircumflex="J",jcircumflex="j", - Ocircumflex="O",ocircumflex="o", - Scircumflex="S",scircumflex="s", - Ucircumflex="U",ucircumflex="u", - Wcircumflex="W",wcircumflex="w", - Ycircumflex="Y",ycircumflex="y", - Agrave="A",agrave="a", - Egrave="E",egrave="e", - Igrave="I",igrave="i", - Ograve="O",ograve="o", - Ugrave="U",ugrave="u", - Ygrave="Y",ygrave="y", - Atilde="A",atilde="a", - Itilde="I",itilde="i", - Otilde="O",otilde="o", - Utilde="U",utilde="u", - Ntilde="N",ntilde="n", - Adiaeresis="A",adiaeresis="a",Adieresis="A",adieresis="a", - Ediaeresis="E",ediaeresis="e",Edieresis="E",edieresis="e", - Idiaeresis="I",idiaeresis="i",Idieresis="I",idieresis="i", - Odiaeresis="O",odiaeresis="o",Odieresis="O",odieresis="o", - Udiaeresis="U",udiaeresis="u",Udieresis="U",udieresis="u", - Ydiaeresis="Y",ydiaeresis="y",Ydieresis="Y",ydieresis="y", - Aacute="A",aacute="a", - Cacute="C",cacute="c", - Eacute="E",eacute="e", - Iacute="I",iacute="i", - Lacute="L",lacute="l", - Nacute="N",nacute="n", - Oacute="O",oacute="o", - Racute="R",racute="r", - Sacute="S",sacute="s", - Uacute="U",uacute="u", - Yacute="Y",yacute="y", - Zacute="Z",zacute="z", - Dstroke="D",dstroke="d", - Hstroke="H",hstroke="h", - Tstroke="T",tstroke="t", - Cdotaccent="C",cdotaccent="c", - Edotaccent="E",edotaccent="e", - Gdotaccent="G",gdotaccent="g", - Idotaccent="I",idotaccent="i", - Zdotaccent="Z",zdotaccent="z", - Amacron="A",amacron="a", - Emacron="E",emacron="e", - Imacron="I",imacron="i", - Omacron="O",omacron="o", - Umacron="U",umacron="u", - Ccedilla="C",ccedilla="c", - Kcedilla="K",kcedilla="k", - Lcedilla="L",lcedilla="l", - Ncedilla="N",ncedilla="n", - Rcedilla="R",rcedilla="r", - Scedilla="S",scedilla="s", - Tcedilla="T",tcedilla="t", - Ohungarumlaut="O",ohungarumlaut="o", - Uhungarumlaut="U",uhungarumlaut="u", - Aogonek="A",aogonek="a", - Eogonek="E",eogonek="e", - Iogonek="I",iogonek="i", - Uogonek="U",uogonek="u", - Aring="A",aring="a", - Uring="U",uring="u", - Abreve="A",abreve="a", - Ebreve="E",ebreve="e", - Gbreve="G",gbreve="g", - Ibreve="I",ibreve="i", - Obreve="O",obreve="o", - Ubreve="U",ubreve="u", - Ccaron="C",ccaron="c", - Dcaron="D",dcaron="d", - Ecaron="E",ecaron="e", - Lcaron="L",lcaron="l", - Ncaron="N",ncaron="n", - Rcaron="R",rcaron="r", - Scaron="S",scaron="s", - Tcaron="T",tcaron="t", - Zcaron="Z",zcaron="z", - dotlessI="I",dotlessi="i", - dotlessJ="J",dotlessj="j", - AEligature="AE",aeligature="ae",AE="AE",ae="ae", - OEligature="OE",oeligature="oe",OE="OE",oe="oe", - IJligature="IJ",ijligature="ij",IJ="IJ",ij="ij", - Lstroke="L",lstroke="l",Lslash="L",lslash="l", - Ostroke="O",ostroke="o",Oslash="O",oslash="o", - Ssharp="SS",ssharp="ss", - Aumlaut="A",aumlaut="a", - Eumlaut="E",eumlaut="e", - Iumlaut="I",iumlaut="i", - Oumlaut="O",oumlaut="o", - Uumlaut="U",uumlaut="u", - } -} - -end --- [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> - 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”] --- - - ---- vim:ft=lua:sw=2:ts=8:et:tw=79 diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-2018-09-24.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-2018-09-24.lua new file mode 100644 index 00000000000..99e38f6399a --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-2018-09-24.lua @@ -0,0 +1,29266 @@ +--[[info----------------------------------------------------------------------- + Luaotfload fontloader package + build 2018-09-24 22:51:46 by fischer@troubleshooting-tex.de +------------------------------------------------------------------------------- + + © 2018 PRAGMA ADE / ConTeXt Development Team + + The code in this file is provided under the GPL v2.0 license. See the + file COPYING in the Luaotfload repository for details. + + Report bugs to github.com/lualatex/luaotfload + + This file has been assembled from components taken from Context. See + the Luaotfload documentation for details: + + $ texdoc luaotfload + $ man 1 luaotfload-tool + $ man 5 luaotfload.conf + + Included files: + + · fontloader-data-con.lua + · fontloader-basics-nod.lua + · fontloader-font-ini.lua + · fontloader-fonts-mis.lua + · fontloader-font-con.lua + · fontloader-fonts-enc.lua + · fontloader-font-cid.lua + · fontloader-font-map.lua + · fontloader-font-vfc.lua + · fontloader-font-otr.lua + · fontloader-font-oti.lua + · fontloader-font-ott.lua + · fontloader-font-cff.lua + · fontloader-font-ttf.lua + · fontloader-font-dsp.lua + · fontloader-font-oup.lua + · fontloader-font-otl.lua + · fontloader-font-oto.lua + · fontloader-font-otj.lua + · fontloader-font-ota.lua + · fontloader-font-ots.lua + · fontloader-font-osd.lua + · fontloader-font-ocl.lua + · fontloader-font-otc.lua + · fontloader-font-onr.lua + · fontloader-font-one.lua + · fontloader-font-afk.lua + · fontloader-font-tfm.lua + · fontloader-font-lua.lua + · fontloader-font-def.lua + · fontloader-fonts-def.lua + · fontloader-fonts-ext.lua + · fontloader-font-imp-tex.lua + · fontloader-font-imp-ligatures.lua + · fontloader-font-imp-italics.lua + · fontloader-font-imp-effects.lua + · fontloader-fonts-lig.lua + · fontloader-fonts-gbn.lua + +--info]]----------------------------------------------------------------------- + + +do --- [luaotfload, fontloader-2018-09-24.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-2018-09-24.lua scope for “data-con”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “basics-nod” 78f56219685f3145b9393c2b688aad94] --- + +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 + 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.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 k,v in next,glyphcodes do + glyphcodes[v]=k +end +for k,v in next,glyphcodes do + disccodes[v]=k +end +nodes.nodecodes=nodecodes +nodes.glyphcodes=glyphcodes +nodes.disccodes=disccodes +local flush_node=node.flush_node +local remove_node=node.remove +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 +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 +nuts.getchar=direct.getchar +nuts.setchar=direct.setchar +nuts.getdisc=direct.getdisc +nuts.setdisc=direct.setdisc +nuts.setlink=direct.setlink +nuts.setsplit=direct.setsplit +nuts.getlist=direct.getlist +nuts.setlist=direct.setlist +nuts.getoffsets=direct.getoffsets or + function(n) + return getfield(n,"xoffset"),getfield(n,"yoffset") + end +nuts.setoffsets=direct.setoffsets or + function(n,x,y) + if x then setfield(n,"xoffset",x) end + if y then setfield(n,"xoffset",y) end + end +nuts.getleader=direct.getleader or function(n) return getfield(n,"leader") end +nuts.setleader=direct.setleader or function(n,l) setfield(n,"leader",l) end +nuts.getcomponents=direct.getcomponents or function(n) return getfield(n,"components") end +nuts.setcomponents=direct.setcomponents or function(n,c) setfield(n,"components",c) end +nuts.getkern=direct.getkern or function(n) return getfield(n,"kern") end +nuts.setkern=direct.setkern or function(n,k) setfield(n,"kern",k) end +nuts.getdir=direct.getdir or function(n) return getfield(n,"dir") end +nuts.setdir=direct.setdir or function(n,d) setfield(n,"dir",d) end +nuts.getwidth=direct.getwidth or function(n) return getfield(n,"width") end +nuts.setwidth=direct.setwidth or function(n,w) return setfield(n,"width",w) end +nuts.getheight=direct.getheight or function(n) return getfield(n,"height") end +nuts.setheight=direct.setheight or function(n,h) return setfield(n,"height",h) end +nuts.getdepth=direct.getdepth or function(n) return getfield(n,"depth") end +nuts.setdepth=direct.setdepth or function(n,d) return setfield(n,"depth",d) 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.traverse_glyph=direct.traverse_glyph +nuts.ligaturing=direct.ligaturing +nuts.kerning=direct.kerning +nuts.new=direct.new +nuts.getprop=nuts.getattr +nuts.setprop=nuts.setattr +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 +local setprev=nuts.setprev +local setnext=nuts.setnext +local getnext=nuts.getnext +local setlink=nuts.setlink +local getfield=nuts.getfield +local setfield=nuts.setfield +local getcomponents=nuts.getcomponents +local setcomponents=nuts.setcomponents +local find_tail=nuts.tail +local flush_list=nuts.flush_list +local flush_node=nuts.flush_node +local traverse_id=nuts.traverse_id +local copy_node=nuts.copy_node +local glyph_code=nodes.nodecodes.glyph +function nuts.set_components(target,start,stop) + local head=getcomponents(target) + if head then + flush_list(head) + head=nil + end + if start then + setprev(start) + else + return nil + end + if stop then + setnext(stop) + end + local tail=nil + while start do + local c=getcomponents(start) + local n=getnext(start) + if c then + if head then + setlink(tail,c) + else + head=c + end + tail=find_tail(c) + setcomponents(start) + flush_node(start) + else + if head then + setlink(tail,start) + else + head=start + end + tail=start + end + start=n + end + setcomponents(target,head) + return head +end +nuts.get_components=nuts.getcomponents +function nuts.take_components(target) + local c=getcomponents(target) + setcomponents(target) + return c +end +function nuts.count_components(n,marks) + local components=getcomponents(n) + if components then + if marks then + local i=0 + for g in traverse_id(glyph_code,components) do + if not marks[getchar(g)] then + i=i+1 + end + end + return i + else + return count(glyph_code,components) + end + else + return 0 + end +end +function nuts.copy_no_components(g,copyinjection) + local components=getcomponents(g) + if components then + setcomponents(g) + local n=copy_node(g) + if copyinjection then + copyinjection(n,g) + end + setcomponents(g,components) + return n + else + local n=copy_node(g) + if copyinjection then + copyinjection(n,g) + end + return n + end +end +function nuts.copy_only_glyphs(current) + local head=nil + local previous=nil + for n in traverse_id(glyph_code,current) do + n=copy_node(n) + if head then + setlink(previous,n) + else + head=n + end + previous=n + end + return head +end +nuts.uses_font=direct.uses_font +if not nuts.uses_font then + local getdisc=nuts.getdisc + local getfont=nuts.getfont + function nuts.uses_font(n,font) + local pre,post,replace=getdisc(n) + if pre then + for n in traverse_id(glyph_code,pre) do + if getfont(n)==font then + return true + end + end + end + if post then + for n in traverse_id(glyph_code,post) do + if getfont(n)==font then + return true + end + end + end + if replace then + for n in traverse_id(glyph_code,replace) do + if getfont(n)==font then + return true + end + end + end + return false + end +end +do + local dummy=tonut(node.new("glyph")) + nuts.traversers={ + glyph=nuts.traverse_id(nodecodes.glyph,dummy), + glue=nuts.traverse_id(nodecodes.glue,dummy), + disc=nuts.traverse_id(nodecodes.disc,dummy), + boundary=nuts.traverse_id(nodecodes.boundary,dummy), + char=nuts.traverse_char(dummy), + node=nuts.traverse(dummy), + } +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “basics-nod”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-ini” 6f615d3bc72fb29f409ed2b8f6364c35] --- + +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=fonts.hashes or { 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 } +if context then + fontloader=nil +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-ini”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “fonts-mis” fd0a52acf5180695d2870da654db7272] --- + +if not modules then modules={} end modules ['luatex-font-mis']={ + 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 + os.exit() +end +local currentfont=font.current +local hashes=fonts.hashes +local identifiers=hashes.identifiers or {} +local marks=hashes.marks or {} +hashes.identifiers=identifiers +hashes.marks=marks +table.setmetatableindex(marks,function(t,k) + if k==true then + return marks[currentfont()] + else + local resources=identifiers[k].resources or {} + local marks=resources.marks or {} + t[k]=marks + return marks + end +end) + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “fonts-mis”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-con” 3218bd21a24c2abb13b284085da8ced4] --- + +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,tonumber,rawget=next,tostring,tonumber,rawget +local format,match,lower,gsub,find=string.format,string.match,string.lower,string.gsub,string.find +local sort,insert,concat=table.sort,table.insert,table.concat +local sortedkeys,sortedhash,serialize,fastcopy=table.sortedkeys,table.sortedhash,table.serialize,table.fastcopy +local derivetable=table.derive +local ioflush=io.flush +local round=math.round +local setmetatable,getmetatable,rawget,rawset=setmetatable,getmetatable,rawget,rawset +local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) +local trace_scaling=false trackers.register("fonts.scaling",function(v) trace_scaling=v end) +local report_defining=logs.reporter("fonts","defining") +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.getprivate(tfmdata) + local properties=tfmdata.properties + local private=properties.private + properties.private=private+1 + return private +end +function constructors.setmathparameter(tfmdata,name,value) + local m=tfmdata.mathparameters + local c=tfmdata.MathConstants + if m then + m[name]=value + end + if c and c~=m then + c[name]=value + end +end +function constructors.getmathparameter(tfmdata,name) + local p=tfmdata.mathparameters or tfmdata.MathConstants + if p then + return p[name] + end +end +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 +local synonyms={ + exheight="x_height", + xheight="x_height", + ex="x_height", + emwidth="quad", + em="quad", + spacestretch="space_stretch", + stretch="space_stretch", + spaceshrink="space_shrink", + shrink="space_shrink", + extraspace="extra_space", + xspace="extra_space", + slantperpoint="slant", +} +function constructors.enhanceparameters(parameters) + local mt=getmetatable(parameters) + local getter=function(t,k) + if not k then + return nil + end + local s=synonyms[k] + if s then + return rawget(t,s) or (mt and mt[s]) or nil + end + if k=="spacing" then + return { + width=t.space, + stretch=t.space_stretch, + shrink=t.space_shrink, + extra=t.extra_space, + } + end + return mt and mt[k] or nil + end + local setter=function(t,k,v) + if not k then + return 0 + end + local s=synonyms[k] + if s then + rawset(t,s,v) + elseif k=="spacing" then + if type(v)=="table" then + rawset(t,"space",v.width or 0) + rawset(t,"space_stretch",v.stretch or 0) + rawset(t,"space_shrink",v.shrink or 0) + rawset(t,"extra_space",v.extra or 0) + end + else + rawset(t,k,v) + end + end + setmetatable(parameters,{ + __index=getter, + __newindex=setter, + }) +end +local function mathkerns(v,vdelta) + local k={} + for i=1,#v do + local entry=v[i] + local height=entry.height + local kern=entry.kern + k[i]={ + height=height and vdelta*height or 0, + kern=kern and vdelta*kern or 0, + } + end + return k +end +local psfake=0 +local function fixedpsname(psname,fallback) + local usedname=psname + if psname and psname~="" then + if find(psname," ",1,true) then + usedname=gsub(psname,"[%s]+","-") + else + 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 + else + 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 + 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 + end + local slantfactor=parameters.slantfactor or 0 + if slantfactor~=0 then + target.slant=slantfactor*1000 + else + target.slant=0 + 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 squeezefactor=parameters.squeezefactor or 0 + if squeezefactor~=0 and squeezefactor~=1 then + vdelta=vdelta*squeezefactor + target.squeeze=squeezefactor*1000 + else + target.squeeze=1000 + end + local mode=parameters.mode or 0 + if mode~=0 then + target.mode=mode + end + local width=parameters.width or 0 + if width~=0 then + target.width=width*delta*1000/655360 + end + targetparameters.factor=delta + targetparameters.hfactor=hdelta + targetparameters.vfactor=vdelta + targetparameters.size=scaledpoints + targetparameters.units=units + targetparameters.scaledpoints=askedscaledpoints + targetparameters.mode=mode + targetparameters.width=width + local isvirtual=properties.virtualized or tfmdata.type=="virtual" + local hasquality=parameters.expansion or parameters.protrusion + 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" + local vfonts=target.fonts + if vfonts and #vfonts>0 then + target.fonts=fastcopy(vfonts) + elseif isvirtual then + target.fonts={ { id=0 } } + end + 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 and c~=unicode then + if c then + description=descriptions[c] or descriptions[unicode] or character + character=characters[c] or character + index=description.index or c + else + description=descriptions[unicode] or character + index=description.index or unicode + end + else + description=descriptions[unicode] or character + index=description.index or unicode + end + else + description=descriptions[unicode] or character + index=description.index or unicode + end + local width=description.width + local height=description.height + local depth=description.depth + 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] + local s=vvi["start"] or 0 + local e=vvi["end"] or 0 + local a=vvi["advance"] or 0 + t[i]={ + ["start"]=s==0 and 0 or s*vdelta, + ["end"]=e==0 and 0 or e*vdelta, + ["advance"]=a==0 and 0 or a*vdelta, + ["extender"]=vvi["extender"], + ["glyph"]=vvi["glyph"], + } + end + chr.vert_variants=t + else + local hv=character.horiz_variants + if hv then + local t={} + for i=1,#hv do + local hvi=hv[i] + local s=hvi["start"] or 0 + local e=hvi["end"] or 0 + local a=hvi["advance"] or 0 + t[i]={ + ["start"]=s==0 and 0 or s*hdelta, + ["end"]=e==0 and 0 or e*hdelta, + ["advance"]=a==0 and 0 or a*hdelta, + ["extender"]=hvi["extender"], + ["glyph"]=hvi["glyph"], + } + end + chr.horiz_variants=t + end + end + 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" or key=="rule" then + ok=true + break + end + end + if ok then + local tt={} + for i=1,#vc do + local ivc=vc[i] + local key=ivc[1] + if key=="right" then + tt[i]={ key,ivc[2]*hdelta } + elseif key=="down" then + tt[i]={ key,ivc[2]*vdelta } + elseif key=="rule" then + tt[i]={ key,ivc[2]*vdelta,ivc[3]*hdelta } + else + tt[i]=ivc + end + end + chr.commands=tt + else + chr.commands=vc + end + end + end + targetcharacters[unicode]=chr + end + properties.setitalics=hasitalics + constructors.aftercopyingcharacters(target,tfmdata) + constructors.trytosharefont(target,tfmdata) + local vfonts=target.fonts +if isvirtual or target.type=="virtual" or properties.virtualized then + properties.virtualized=true +target.type="virtual" + if not vfonts or #vfonts==0 then + target.fonts={ { id=0 } } + end + elseif vfonts then + properties.virtualized=true + target.type="virtual" + if #vfonts==0 then + target.fonts={ { id=0 } } + end + end + return target +end +function constructors.finalize(tfmdata) + if tfmdata.properties and tfmdata.properties.finalized then + return + end + if not tfmdata.characters then + return nil + end + if not tfmdata.goodies then + tfmdata.goodies={} + end + local parameters=tfmdata.parameters + if not parameters then + return nil + end + if not parameters.expansion then + parameters.expansion={ + stretch=tfmdata.stretch or 0, + shrink=tfmdata.shrink or 0, + step=tfmdata.step or 0, + } + end + if not parameters.size then + parameters.size=tfmdata.size + end + if not parameters.mode then + parameters.mode=0 + end + if not parameters.width then + parameters.width=0 + end + if not parameters.slantfactor then + parameters.slantfactor=tfmdata.slant or 0 + end + if not parameters.extendfactor then + parameters.extendfactor=tfmdata.extend or 0 + end + if not parameters.squeezefactor then + parameters.squeezefactor=tfmdata.squeeze 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.slant=nil + tfmdata.extend=nil + tfmdata.squeeze=nil + tfmdata.mode=nil + tfmdata.width=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,n={},0 + for category,list in sortedhash(features) do + if next(list) then + local hasher=hashmethods[category] + if hasher then + local hash=hasher(list) + if hash then + n=n+1 + t[n]=category..":"..hash + end + end + end + end + if n>0 then + return concat(t," & ") + end + end + return "unknown" +end +hashmethods.normal=function(list) + local s={} + local n=0 + for k,v in next,list do + if not k then + elseif k=="number" or k=="features" then + else + n=n+1 + if type(v)=="table" then + local t={} + local m=0 + for k,v in next,v do + m=m+1 + t[m]=k..'='..tostring(v) + end + s[n]=k..'={'..concat(t,",").."}" + else + s[n]=k..'='..tostring(v) + end + end + end + if n>0 then + sort(s) + return concat(s,"+") + end +end +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=round(constructors.scaled(size,designsizes[hash])) + else + size=round(size) + end + specification.size=size + if fallbacks then + return hash..' @ '..size..' @ '..fallbacks + else + return hash..' @ '..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 known={} + local nofsteps=0 + local patches={ before=before,after=after } + local trace=false + local report=logs.reporter("fonts",format.." enhancing") + trackers.register(format..".loading",function(v) trace=v end) + local function enhance(name,data,filename,raw) + local enhancer=actions[name] + if enhancer then + if trace then + report("apply enhancement %a to file %a",name,filename) + ioflush() + end + enhancer(data,filename,raw) + else + 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,nofsteps do + local enhancer=order[e] + local b=before[enhancer] + if b then + for pattern,action in next,b do + if find(basename,pattern) then + action(data,filename,raw) + end + end + end + enhance(enhancer,data,filename,raw) + 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 + nofsteps=nofsteps+1 + order[nofsteps]=what + known[what]=nofsteps + 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 } + if not known[where] then + nofsteps=nofsteps+1 + order[nofsteps]=where + known[where]=nofsteps + end + end + end + end + enhancers={ + register=register, + apply=apply, + patch=patch, + report=report, + patches={ + register=patch, + report=report, + }, + } + 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-2018-09-24.lua scope for “font-con”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “fonts-enc” 5ff4ca50493d7c4ecea0e15c203099f0] --- + +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 + 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 + logs.report("fonts","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-2018-09-24.lua scope for “fonts-enc”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-cid” 22b0367742fb253deef84ef7ccf5e8de] --- + +if not modules then modules={} end modules ['font-cid']={ + version=1.001, + comment="companion to font-ini.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local format,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 supnum0 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-2018-09-24.lua scope for “font-cid”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-map” 2c03dc61afabb5887ef18a88e6ec86bd] --- + +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 formatters=string.formatters +local sortedhash,sortedkeys=table.sortedhash,table.sortedkeys +local rshift=bit32.rshift +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(rshift(unicode,10)+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(rshift(u,10)+0xD800,u%1024+0xDC00) + end + end + return concat(t) +end +local unknown=f_single(0xFFFD) +local hash={} +local conc={} +table.setmetatableindex(hash,function(t,k) + if type(k)=="table" then + local n=#k + for l=1,n do + conc[l]=hash[k[l]] + end + return concat(conc,"",1,n) + end + local v + if k>=0x00E000 and k<=0x00F8FF then + v=unknown + elseif k>=0x0F0000 and k<=0x0FFFFF then + v=unknown + elseif k>=0x100000 and k<=0x10FFFF then + v=unknown + elseif k<0xD7FF or (k>0xDFFF and k<=0xFFFF) then + v=f_single(k) + else + k=k-0x10000 + v=f_double(rshift(k,10)+0xD800,k%1024+0xDC00) + end + t[k]=v + return v +end) +local function tounicode(unicode) + return hash[unicode] +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={ + 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=allocate {} + 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,forceligatures) + 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 + 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 unicoded=0 + local collected=fonts.handlers.otf.readers.getcomponents(data) + local function resolve(glyph,u) + 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 + if not collected then + elseif forceligatures or force_ligatures then + for i=1,#dlist do + local du=dlist[i] + if du>=private or (du>=0xE000 and du<=0xF8FF) then + local u=collected[du] + if u then + resolve(descriptions[du],u) + end + end + end + else + for i=1,#dlist do + local du=dlist[i] + if du>=private or (du>=0xE000 and du<=0xF8FF) then + local glyph=descriptions[du] + if glyph.class=="ligature" and not glyph.unicode then + local u=collected[du] + if u then + resolve(glyph,u) + end + 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-2018-09-24.lua scope for “font-map”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-vfc” 237aff1862009b7850653c2098473bd4] --- + +if not modules then modules={} end modules ['font-vfc']={ + version=1.001, + comment="companion to font-ini.mkiv and hand-ini.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local select=select +local insert=table.insert +local fonts=fonts +local helpers=fonts.helpers +local setmetatableindex=table.setmetatableindex +local makeweak=table.makeweak +local push={ "push" } +local pop={ "pop" } +local dummy={ "comment" } +function helpers.prependcommands(commands,...) + insert(commands,1,push) + for i=select("#",...),1,-1 do + local s=select(i,...) + if s then + insert(commands,1,s) + end + end + insert(commands,pop) + return commands +end +function helpers.appendcommands(commands,...) + insert(commands,1,push) + insert(commands,pop) + for i=1,select("#",...) do + local s=select(i,...) + if s then + insert(commands,s) + end + end + return commands +end +local char=setmetatableindex(function(t,k) + local v={ "slot",0,k } + t[k]=v + return v +end) +local right=setmetatableindex(function(t,k) + local v={ "right",k } + t[k]=v + return v +end) +local left=setmetatableindex(function(t,k) + local v={ "right",-k } + t[k]=v + return v +end) +local down=setmetatableindex(function(t,k) + local v={ "down",k } + t[k]=v + return v +end) +local up=setmetatableindex(function(t,k) + local v={ "down",-k } + t[k]=v + return v +end) +helpers.commands=utilities.storage.allocate { + char=char, + right=right, + left=left, + down=down, + up=up, + push=push, + pop=pop, + dummy=dummy, +} + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-vfc”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-otr” 4a1dae571a43d7cb8afce7e906df89c6] --- + +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,tonumber=next,type,tonumber +local byte,lower,char,gsub=string.byte,string.lower,string.char,string.gsub +local floor,round=math.floor,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 rshift=bit32.rshift +local setmetatableindex=table.setmetatableindex +local formatters=string.formatters +local sortedkeys=table.sortedkeys +local sortedhash=table.sortedhash +local stripstring=string.nospaces +local utf16_to_utf8_be=utf.utf16_to_utf8_be +local report=logs.reporter("otf reader") +local trace_cmap=false +local trace_cmap_detail=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 read2dot14=streamreader.read2dot14 +local readfword=readshort +local readufword=readushort +local readoffset=readushort +local readcardinaltable=streamreader.readcardinaltable +local readintegertable=streamreader.readintegertable +function streamreader.readtag(f) + return lower(stripstring(readstring(f,4))) +end +local short=2 +local ushort=2 +local ulong=4 +directives.register("fonts.streamreader",function() + streamreader=utilities.streams + openfile=streamreader.open + closefile=streamreader.close + setposition=streamreader.setposition + skipshort=streamreader.skipshort + readbytes=streamreader.readbytes + readstring=streamreader.readstring + readbyte=streamreader.readcardinal1 + readushort=streamreader.readcardinal2 + readuint=streamreader.readcardinal3 + readulong=streamreader.readcardinal4 + readshort=streamreader.readinteger2 + readlong=streamreader.readinteger4 + readfixed=streamreader.readfixed4 + read2dot14=streamreader.read2dot14 + readfword=readshort + readufword=readushort + readoffset=readushort + readcardinaltable=streamreader.readcardinaltable + readintegertable=streamreader.readintegertable + function streamreader.readtag(f) + return lower(stripstring(readstring(f,4))) + end +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 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", + "variationspostscriptnameprefix", +} +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 helpers={} +readers.helpers=helpers +local function gotodatatable(f,fontdata,tag,criterium) + if criterium and f then + local datatable=fontdata.tables[tag] + if datatable then + local tableoffset=datatable.offset + setposition(f,tableoffset) + return tableoffset + end + end +end +local function reportskippedtable(f,fontdata,tag,criterium) + if criterium and f then + local datatable=fontdata.tables[tag] + if datatable then + report("loading of table %a skipped",tag) + end + end +end +local function setvariabledata(fontdata,tag,data) + local variabledata=fontdata.variabledata + if variabledata then + variabledata[tag]=data + else + fontdata.variabledata={ [tag]=data } + end +end +helpers.gotodatatable=gotodatatable +helpers.setvariabledata=setvariabledata +helpers.reportskippedtable=reportskippedtable +local platformnames={ + postscriptname=true, + fullname=true, + family=true, + subfamily=true, + typographicfamily=true, + typographicsubfamily=true, + compatiblefullname=true, +} +function readers.name(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"name",true) + if tableoffset then + local format=readushort(f) + local nofnames=readushort(f) + local offset=readushort(f) + local start=tableoffset+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 index=readushort(f) + local name=reservednames[index] + namelist[#namelist+1]={ + platform=platform, + encoding=encoding, + language=language, + name=name, + index=index, + length=readushort(f), + offset=start+readushort(f), + } + 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 extras={} + local function filter(platform,e,l) + local namelist=namelists[platform] + for i=1,#namelist do + local name=namelist[i] + local nametag=name.name + local index=name.index + if not done[nametag or i] 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 + if nametag then + names[nametag]={ + content=content, + platform=platform, + encoding=encoding, + language=language, + } + end + extras[index]=content + done[nametag or i]=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 + fontdata.extras=extras + 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 tableoffset=gotodatatable(f,fontdata,"os/2",true) + if tableoffset then + 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 tableoffset=gotodatatable(f,fontdata,"head",true) + if tableoffset then + 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) + local tableoffset=gotodatatable(f,fontdata,"hhea",specification.details) + if tableoffset then + 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 +readers.vhea=function(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"vhea",specification.details) + if tableoffset then + fontdata.verticalheader={ + version=readfixed(f), + 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 +readers.maxp=function(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"maxp",specification.details) + if tableoffset then + local version=readfixed(f) + local nofglyphs=readushort(f) + fontdata.nofglyphs=nofglyphs + if version==0.5 then + fontdata.maximumprofile={ + version=version, + nofglyphs=nofglyphs, + } + 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), + } + else + fontdata.maximumprofile={ + version=version, + nofglyphs=0, + } + end + end +end +readers.hmtx=function(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"hmtx",specification.glyphs) + if tableoffset then + 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 +readers.vmtx=function(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"vmtx",specification.glyphs) + if tableoffset then + 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 +readers.vorg=function(f,fontdata,specification) + reportskippedtable(f,fontdata,"vorg",specification.glyphs) +end +readers.post=function(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"post",true) + if tableoffset then + 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) + reportskippedtable(f,fontdata,"cff",specification.glyphs) +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 }, +{ 0,4,12 }, + { 3,10,13 }, +} +local supported={} +for i=1,#sequence do + local si=sequence[i] + local sp,se,sf=si[1],si[2],si[3] + 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 mapping=fontdata.mapping + local glyphs=fontdata.glyphs + local duplicates=fontdata.duplicates + local nofdone=0 + local endchars=readcardinaltable(f,nofsegments,ushort) + local reserved=readushort(f) + local startchars=readcardinaltable(f,nofsegments,ushort) + local deltas=readcardinaltable(f,nofsegments,ushort) + local offsets=readcardinaltable(f,nofsegments,ushort) + local size=(length-2*2-5*2-4*2*nofsegments)/2 + local indices=readcardinaltable(f,size-1,ushort) + 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_detail 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_detail 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_detail 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_detail 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 + 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 + if trace_cmap then + report("cmap checked: platform %i (%s), encoding %i (%s), format %i, new unicodes %i",platform,p,encoding,e and e[encoding] or "?",format,n) + end + return n +end +function readers.cmap(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"cmap",specification.glyphs) + if tableoffset then + 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 + if trace_cmap then + report("found cmaps:") + end + for platform,record in sortedhash(records) do + local p=platforms[platform] + local e=encodings[p] + local sp=supported[platform] + local ps=p or "?" + if trace_cmap then + if sp then + report(" platform %i: %s",platform,ps) + else + report(" platform %i: %s (unsupported)",platform,ps) + end + end + for encoding,subtables in sortedhash(record) do + local se=sp and sp[encoding] + local es=e and e[encoding] or "?" + if trace_cmap then + if se then + report(" encoding %i: %s",encoding,es) + else + report(" encoding %i: %s (unsupported)",encoding,es) + end + 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 + if trace_cmap then + 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 + end + local ok=false + for i=1,#sequence do + local si=sequence[i] + local sp,se,sf=si[1],si[2],si[3] + 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 +function readers.loca(f,fontdata,specification) + reportskippedtable(f,fontdata,"loca",specification.glyphs) +end +function readers.glyf(f,fontdata,specification) + reportskippedtable(f,fontdata,"glyf",specification.glyphs) +end +function readers.colr(f,fontdata,specification) + reportskippedtable(f,fontdata,"colr",specification.glyphs) +end +function readers.cpal(f,fontdata,specification) + reportskippedtable(f,fontdata,"cpal",specification.glyphs) +end +function readers.svg(f,fontdata,specification) + reportskippedtable(f,fontdata,"svg",specification.glyphs) +end +function readers.sbix(f,fontdata,specification) + reportskippedtable(f,fontdata,"sbix",specification.glyphs) +end +function readers.cbdt(f,fontdata,specification) + reportskippedtable(f,fontdata,"cbdt",specification.glyphs) +end +function readers.cblc(f,fontdata,specification) + reportskippedtable(f,fontdata,"cblc",specification.glyphs) +end +function readers.ebdt(f,fontdata,specification) + reportskippedtable(f,fontdata,"ebdt",specification.glyphs) +end +function readers.ebsc(f,fontdata,specification) + reportskippedtable(f,fontdata,"ebsc",specification.glyphs) +end +function readers.eblc(f,fontdata,specification) + reportskippedtable(f,fontdata,"eblc",specification.glyphs) +end +function readers.kern(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"kern",specification.kerns) + if tableoffset then + 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=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 +function readers.gdef(f,fontdata,specification) + reportskippedtable(f,fontdata,"gdef",specification.details) +end +function readers.gsub(f,fontdata,specification) + reportskippedtable(f,fontdata,"gsub",specification.details) +end +function readers.gpos(f,fontdata,specification) + reportskippedtable(f,fontdata,"gpos",specification.details) +end +function readers.math(f,fontdata,specification) + reportskippedtable(f,fontdata,"math",specification.details) +end +local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo,instancenames) + 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 + if platformnames then + platformnames=fontdata.platformnames + end + if instancenames then + local variabledata=fontdata.variabledata + if variabledata then + local instances=variabledata and variabledata.instances + if instances then + instancenames={} + for i=1,#instances do + instancenames[i]=lower(stripstring(instances[i].subfamily)) + end + else + instancenames=nil + end + else + instancenames=nil + 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 or nil, + instancenames=instancenames 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, + foundtables=false, + } + 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 + fontdata.foundtables=sortedkeys(tables) + if tables.cff or tables.cff2 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 readtable(tag,f,fontdata,specification,...) + local reader=readers[tag] + if reader then + reader(f,fontdata,specification,...) + end +end +local variablefonts_supported=(context and true) or (logs and logs.application and true) or false +local function readdata(f,offset,specification) + local fontdata=loadtables(f,specification,offset) + if specification.glyphs then + prepareglyps(fontdata) + end + if not variablefonts_supported then + specification.instance=nil + specification.variable=nil + specification.factors=nil + end + fontdata.temporary={} + readtable("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 + readtable("stat",f,fontdata,specification) + readtable("avar",f,fontdata,specification) + readtable("fvar",f,fontdata,specification) + if variablefonts_supported then + local variabledata=fontdata.variabledata + if variabledata then + local instances=variabledata.instances + local axis=variabledata.axis + if axis and (not instances or #instances==0) then + instances={} + variabledata.instances=instances + local function add(n,subfamily,value) + local values={} + for i=1,#axis do + local a=axis[i] + values[i]={ + axis=a.tag, + value=i==n and value or a.default, + } + end + instances[#instances+1]={ + subfamily=subfamily, + values=values, + } + end + for i=1,#axis do + local a=axis[i] + local tag=a.tag + add(i,"default"..tag,a.default) + add(i,"minimum"..tag,a.minimum) + add(i,"maximum"..tag,a.maximum) + end + end + end + if not specification.factors then + local instance=specification.instance + if type(instance)=="string" then + local factors=helpers.getfactors(fontdata,instance) + if factors then + specification.factors=factors + fontdata.factors=factors + fontdata.instance=instance + report("user instance: %s, factors: % t",instance,factors) + else + report("user instance: %s, bad factors",instance) + end + end + end + if not fontdata.factors then + if fontdata.variabledata then + local factors=helpers.getfactors(fontdata,true) + if factors then + specification.factors=factors + fontdata.factors=factors + end + else + end + end + end + readtable("os/2",f,fontdata,specification) + readtable("head",f,fontdata,specification) + readtable("maxp",f,fontdata,specification) + readtable("hhea",f,fontdata,specification) + readtable("vhea",f,fontdata,specification) + readtable("hmtx",f,fontdata,specification) + readtable("vmtx",f,fontdata,specification) + readtable("vorg",f,fontdata,specification) + readtable("post",f,fontdata,specification) + readtable("mvar",f,fontdata,specification) + readtable("hvar",f,fontdata,specification) + readtable("vvar",f,fontdata,specification) + readtable("gdef",f,fontdata,specification) + readtable("cff",f,fontdata,specification) + readtable("cff2",f,fontdata,specification) + readtable("cmap",f,fontdata,specification) + readtable("loca",f,fontdata,specification) + readtable("glyf",f,fontdata,specification) + readtable("colr",f,fontdata,specification) + readtable("cpal",f,fontdata,specification) + readtable("svg",f,fontdata,specification) + readtable("sbix",f,fontdata,specification) + readtable("cbdt",f,fontdata,specification) + readtable("cblc",f,fontdata,specification) + readtable("ebdt",f,fontdata,specification) + readtable("eblc",f,fontdata,specification) + readtable("kern",f,fontdata,specification) + readtable("gsub",f,fontdata,specification) + readtable("gpos",f,fontdata,specification) + readtable("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 ttcversion=readulong(f) + local nofsubfonts=readulong(f) + local offsets=readcardinaltable(f,nofsubfonts,ulong) + 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,instance) + if type(specification)=="string" then + specification={ + filename=specification, + info=true, + details=true, + glyphs=true, + shapes=true, + kerns=true, + variable=true, + globalkerns=true, + lookups=true, + subfont=n or true, + tounicode=false, + instance=instance + } + 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 + if specification.instance or instance then + specification.variable=true + specification.instance=specification.instance or instance + 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,instance,streams) + local fontdata=loadfont { + filename=filename, + shapes=true, + streams=streams, + variable=true, + subfont=n, + instance=instance, + } + 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,instance) + local fontdata=loadfont { + filename=filename, + glyphs=true, + shapes=false, + lookups=true, + variable=true, + subfont=n, + instance=instance, + } + 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,true), + properties={ + hasitalics=fontdata.hasitalics or false, + maxcolorclass=fontdata.maxcolorclass, + hascolor=fontdata.hascolor or false, + instance=fontdata.instance, + factors=fontdata.factors, + }, + 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, + sbixshapes=fontdata.sbixshapes, + variabledata=fontdata.variabledata, + foundtables=fontdata.foundtables, + }, + } + end +end +function readers.getinfo(filename,specification) + local subfont=nil + local platformnames=false + local rawfamilynames=false + local instancenames=true + 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, + instancenames=true, + } + if fontdata then + local subfonts=fontdata.subfonts + if not subfonts then + return getinfo(fontdata,nil,platformnames,rawfamilynames,false,instancenames) + elseif not subfont then + local info={} + for i=1,#subfonts do + info[i]=getinfo(fontdata,i,platformnames,rawfamilynames,false,instancenames) + end + return info + elseif subfont>=1 and subfont<=#subfonts then + return getinfo(fontdata,subfont,platformnames,rawfamilynames,false,instancenames) + 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-2018-09-24.lua scope for “font-otr”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-oti” 309a75f9c14b77d87e94eba827dc4e71] --- + +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-2018-09-24.lua scope for “font-oti”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-ott” a4dd9bc516bdb7669ee61d5207f899c6] --- + +if not modules then modules={} end modules ["font-ott"]={ + 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,tostring,rawget,rawset=type,next,tonumber,tostring,rawget,rawset +local gsub,lower,format,match,gmatch,find=string.gsub,string.lower,string.format,string.match,string.gmatch,string.find +local sequenced=table.sequenced +local is_boolean=string.is_boolean +local setmetatableindex=table.setmetatableindex +local setmetatablenewindex=table.setmetatablenewindex +local allocate=utilities.storage.allocate +local fonts=fonts +local otf=fonts.handlers.otf +local otffeatures=otf.features +local tables=otf.tables or {} +otf.tables=tables +local statistics=otf.statistics or {} +otf.statistics=statistics +local scripts=allocate { + ["arab"]="arabic", + ["armi"]="imperial aramaic", + ["armn"]="armenian", + ["avst"]="avestan", + ["bali"]="balinese", + ["bamu"]="bamum", + ["batk"]="batak", + ["beng"]="bengali", + ["bng2"]="bengali variant 2", + ["bopo"]="bopomofo", + ["brah"]="brahmi", + ["brai"]="braille", + ["bugi"]="buginese", + ["buhd"]="buhid", + ["byzm"]="byzantine music", + ["cakm"]="chakma", + ["cans"]="canadian syllabics", + ["cari"]="carian", + ["cham"]="cham", + ["cher"]="cherokee", + ["copt"]="coptic", + ["cprt"]="cypriot syllabary", + ["cyrl"]="cyrillic", + ["deva"]="devanagari", + ["dev2"]="devanagari variant 2", + ["dsrt"]="deseret", + ["egyp"]="egyptian heiroglyphs", + ["ethi"]="ethiopic", + ["geor"]="georgian", + ["glag"]="glagolitic", + ["goth"]="gothic", + ["grek"]="greek", + ["gujr"]="gujarati", + ["gjr2"]="gujarati variant 2", + ["guru"]="gurmukhi", + ["gur2"]="gurmukhi variant 2", + ["hang"]="hangul", + ["hani"]="cjk ideographic", + ["hano"]="hanunoo", + ["hebr"]="hebrew", + ["ital"]="old italic", + ["jamo"]="hangul jamo", + ["java"]="javanese", + ["kali"]="kayah li", + ["kana"]="hiragana and katakana", + ["khar"]="kharosthi", + ["khmr"]="khmer", + ["knda"]="kannada", + ["knd2"]="kannada variant 2", + ["kthi"]="kaithi", + ["lana"]="tai tham", + ["lao" ]="lao", + ["latn"]="latin", + ["lepc"]="lepcha", + ["limb"]="limbu", + ["linb"]="linear b", + ["lisu"]="lisu", + ["lyci"]="lycian", + ["lydi"]="lydian", + ["mand"]="mandaic and mandaean", + ["math"]="mathematical alphanumeric symbols", + ["merc"]="meroitic cursive", + ["mero"]="meroitic hieroglyphs", + ["mlym"]="malayalam", + ["mlm2"]="malayalam variant 2", + ["mong"]="mongolian", + ["mtei"]="meitei Mayek", + ["musc"]="musical symbols", + ["mym2"]="myanmar variant 2", + ["mymr"]="myanmar", + ["nko" ]='n"ko', + ["ogam"]="ogham", + ["olck"]="ol chiki", + ["orkh"]="old turkic and orkhon runic", + ["orya"]="oriya", + ["ory2"]="odia variant 2", + ["osma"]="osmanya", + ["phag"]="phags-pa", + ["phli"]="inscriptional pahlavi", + ["phnx"]="phoenician", + ["prti"]="inscriptional parthian", + ["rjng"]="rejang", + ["runr"]="runic", + ["samr"]="samaritan", + ["sarb"]="old south arabian", + ["saur"]="saurashtra", + ["shaw"]="shavian", + ["shrd"]="sharada", + ["sinh"]="sinhala", + ["sora"]="sora sompeng", + ["sund"]="sundanese", + ["sylo"]="syloti nagri", + ["syrc"]="syriac", + ["tagb"]="tagbanwa", + ["takr"]="takri", + ["tale"]="tai le", + ["talu"]="tai lu", + ["taml"]="tamil", + ["tavt"]="tai viet", + ["telu"]="telugu", + ["tel2"]="telugu variant 2", + ["tfng"]="tifinagh", + ["tglg"]="tagalog", + ["thaa"]="thaana", + ["thai"]="thai", + ["tibt"]="tibetan", + ["tml2"]="tamil variant 2", + ["ugar"]="ugaritic cuneiform", + ["vai" ]="vai", + ["xpeo"]="old persian cuneiform", + ["xsux"]="sumero-akkadian cuneiform", + ["yi" ]="yi", +} +local languages=allocate { + ["aba" ]="abaza", + ["abk" ]="abkhazian", + ["ach" ]="acholi", + ["acr" ]="achi", + ["ady" ]="adyghe", + ["afk" ]="afrikaans", + ["afr" ]="afar", + ["agw" ]="agaw", + ["aio" ]="aiton", + ["aka" ]="akan", + ["als" ]="alsatian", + ["alt" ]="altai", + ["amh" ]="amharic", + ["ang" ]="anglo-saxon", + ["apph"]="phonetic transcription—americanist conventions", + ["ara" ]="arabic", + ["arg" ]="aragonese", + ["ari" ]="aari", + ["ark" ]="rakhine", + ["asm" ]="assamese", + ["ast" ]="asturian", + ["ath" ]="athapaskan", + ["avr" ]="avar", + ["awa" ]="awadhi", + ["aym" ]="aymara", + ["azb" ]="torki", + ["aze" ]="azerbaijani", + ["bad" ]="badaga", + ["bad0"]="banda", + ["bag" ]="baghelkhandi", + ["bal" ]="balkar", + ["ban" ]="balinese", + ["bar" ]="bavarian", + ["bau" ]="baulé", + ["bbc" ]="batak toba", + ["bbr" ]="berber", + ["bch" ]="bench", + ["bcr" ]="bible cree", + ["bdy" ]="bandjalang", + ["bel" ]="belarussian", + ["bem" ]="bemba", + ["ben" ]="bengali", + ["bgc" ]="haryanvi", + ["bgq" ]="bagri", + ["bgr" ]="bulgarian", + ["bhi" ]="bhili", + ["bho" ]="bhojpuri", + ["bik" ]="bikol", + ["bil" ]="bilen", + ["bis" ]="bislama", + ["bjj" ]="kanauji", + ["bkf" ]="blackfoot", + ["bli" ]="baluchi", + ["blk" ]="pa'o karen", + ["bln" ]="balante", + ["blt" ]="balti", + ["bmb" ]="bambara (bamanankan)", + ["bml" ]="bamileke", + ["bos" ]="bosnian", + ["bpy" ]="bishnupriya manipuri", + ["bre" ]="breton", + ["brh" ]="brahui", + ["bri" ]="braj bhasha", + ["brm" ]="burmese", + ["brx" ]="bodo", + ["bsh" ]="bashkir", + ["bti" ]="beti", + ["bts" ]="batak simalungun", + ["bug" ]="bugis", + ["cak" ]="kaqchikel", + ["cat" ]="catalan", + ["cbk" ]="zamboanga chavacano", + ["ceb" ]="cebuano", + ["cgg" ]="chiga", + ["cha" ]="chamorro", + ["che" ]="chechen", + ["chg" ]="chaha gurage", + ["chh" ]="chattisgarhi", + ["chi" ]="chichewa (chewa, nyanja)", + ["chk" ]="chukchi", + ["chk0"]="chuukese", + ["cho" ]="choctaw", + ["chp" ]="chipewyan", + ["chr" ]="cherokee", + ["chu" ]="chuvash", + ["chy" ]="cheyenne", + ["cmr" ]="comorian", + ["cop" ]="coptic", + ["cor" ]="cornish", + ["cos" ]="corsican", + ["cpp" ]="creoles", + ["cre" ]="cree", + ["crr" ]="carrier", + ["crt" ]="crimean tatar", + ["csb" ]="kashubian", + ["csl" ]="church slavonic", + ["csy" ]="czech", + ["ctg" ]="chittagonian", + ["cuk" ]="san blas kuna", + ["dan" ]="danish", + ["dar" ]="dargwa", + ["dax" ]="dayi", + ["dcr" ]="woods cree", + ["deu" ]="german", + ["dgo" ]="dogri", + ["dgr" ]="dogri", + ["dhg" ]="dhangu", + ["dhv" ]="divehi (dhivehi, maldivian)", + ["diq" ]="dimli", + ["div" ]="divehi (dhivehi, maldivian)", + ["djr" ]="zarma", + ["djr0"]="djambarrpuyngu", + ["dng" ]="dangme", + ["dnj" ]="dan", + ["dnk" ]="dinka", + ["dri" ]="dari", + ["duj" ]="dhuwal", + ["dun" ]="dungan", + ["dzn" ]="dzongkha", + ["ebi" ]="ebira", + ["ecr" ]="eastern cree", + ["edo" ]="edo", + ["efi" ]="efik", + ["ell" ]="greek", + ["emk" ]="eastern maninkakan", + ["eng" ]="english", + ["erz" ]="erzya", + ["esp" ]="spanish", + ["esu" ]="central yupik", + ["eti" ]="estonian", + ["euq" ]="basque", + ["evk" ]="evenki", + ["evn" ]="even", + ["ewe" ]="ewe", + ["fan" ]="french antillean", + ["fan0"]=" fang", + ["far" ]="persian", + ["fat" ]="fanti", + ["fin" ]="finnish", + ["fji" ]="fijian", + ["fle" ]="dutch (flemish)", + ["fne" ]="forest nenets", + ["fon" ]="fon", + ["fos" ]="faroese", + ["fra" ]="french", + ["frc" ]="cajun french", + ["fri" ]="frisian", + ["frl" ]="friulian", + ["frp" ]="arpitan", + ["fta" ]="futa", + ["ful" ]="fulah", + ["fuv" ]="nigerian fulfulde", + ["gad" ]="ga", + ["gae" ]="scottish gaelic (gaelic)", + ["gag" ]="gagauz", + ["gal" ]="galician", + ["gar" ]="garshuni", + ["gaw" ]="garhwali", + ["gez" ]="ge'ez", + ["gih" ]="githabul", + ["gil" ]="gilyak", + ["gil0"]="kiribati (gilbertese)", + ["gkp" ]="kpelle (guinea)", + ["glk" ]="gilaki", + ["gmz" ]="gumuz", + ["gnn" ]="gumatj", + ["gog" ]="gogo", + ["gon" ]="gondi", + ["grn" ]="greenlandic", + ["gro" ]="garo", + ["gua" ]="guarani", + ["guc" ]="wayuu", + ["guf" ]="gupapuyngu", + ["guj" ]="gujarati", + ["guz" ]="gusii", + ["hai" ]="haitian (haitian creole)", + ["hal" ]="halam", + ["har" ]="harauti", + ["hau" ]="hausa", + ["haw" ]="hawaiian", + ["hay" ]="haya", + ["haz" ]="hazaragi", + ["hbn" ]="hammer-banna", + ["her" ]="herero", + ["hil" ]="hiligaynon", + ["hin" ]="hindi", + ["hma" ]="high mari", + ["hmn" ]="hmong", + ["hmo" ]="hiri motu", + ["hnd" ]="hindko", + ["ho" ]="ho", + ["hri" ]="harari", + ["hrv" ]="croatian", + ["hun" ]="hungarian", + ["hye" ]="armenian", + ["hye0"]="armenian east", + ["iba" ]="iban", + ["ibb" ]="ibibio", + ["ibo" ]="igbo", + ["ido" ]="ido", + ["ijo" ]="ijo languages", + ["ile" ]="interlingue", + ["ilo" ]="ilokano", + ["ina" ]="interlingua", + ["ind" ]="indonesian", + ["ing" ]="ingush", + ["inu" ]="inuktitut", + ["ipk" ]="inupiat", + ["ipph"]="phonetic transcription—ipa conventions", + ["iri" ]="irish", + ["irt" ]="irish traditional", + ["isl" ]="icelandic", + ["ism" ]="inari sami", + ["ita" ]="italian", + ["iwr" ]="hebrew", + ["jam" ]="jamaican creole", + ["jan" ]="japanese", + ["jav" ]="javanese", + ["jbo" ]="lojban", + ["jii" ]="yiddish", + ["jud" ]="ladino", + ["jul" ]="jula", + ["kab" ]="kabardian", + ["kab0"]="kabyle", + ["kac" ]="kachchi", + ["kal" ]="kalenjin", + ["kan" ]="kannada", + ["kar" ]="karachay", + ["kat" ]="georgian", + ["kaz" ]="kazakh", + ["kde" ]="makonde", + ["kea" ]="kabuverdianu (crioulo)", + ["keb" ]="kebena", + ["kek" ]="kekchi", + ["kge" ]="khutsuri georgian", + ["kha" ]="khakass", + ["khk" ]="khanty-kazim", + ["khm" ]="khmer", + ["khs" ]="khanty-shurishkar", + ["kht" ]="khamti shan", + ["khv" ]="khanty-vakhi", + ["khw" ]="khowar", + ["kik" ]="kikuyu (gikuyu)", + ["kir" ]="kirghiz (kyrgyz)", + ["kis" ]="kisii", + ["kiu" ]="kirmanjki", + ["kjd" ]="southern kiwai", + ["kjp" ]="eastern pwo karen", + ["kkn" ]="kokni", + ["klm" ]="kalmyk", + ["kmb" ]="kamba", + ["kmn" ]="kumaoni", + ["kmo" ]="komo", + ["kms" ]="komso", + ["knr" ]="kanuri", + ["kod" ]="kodagu", + ["koh" ]="korean old hangul", + ["kok" ]="konkani", + ["kom" ]="komi", + ["kon" ]="kikongo", + ["kon0"]="kongo", + ["kop" ]="komi-permyak", + ["kor" ]="korean", + ["kos" ]="kosraean", + ["koz" ]="komi-zyrian", + ["kpl" ]="kpelle", + ["kri" ]="krio", + ["krk" ]="karakalpak", + ["krl" ]="karelian", + ["krm" ]="karaim", + ["krn" ]="karen", + ["krt" ]="koorete", + ["ksh" ]="kashmiri", + ["ksh0"]="ripuarian", + ["ksi" ]="khasi", + ["ksm" ]="kildin sami", + ["ksw" ]="s’gaw karen", + ["kua" ]="kuanyama", + ["kui" ]="kui", + ["kul" ]="kulvi", + ["kum" ]="kumyk", + ["kur" ]="kurdish", + ["kuu" ]="kurukh", + ["kuy" ]="kuy", + ["kyk" ]="koryak", + ["kyu" ]="western kayah", + ["lad" ]="ladin", + ["lah" ]="lahuli", + ["lak" ]="lak", + ["lam" ]="lambani", + ["lao" ]="lao", + ["lat" ]="latin", + ["laz" ]="laz", + ["lcr" ]="l-cree", + ["ldk" ]="ladakhi", + ["lez" ]="lezgi", + ["lij" ]="ligurian", + ["lim" ]="limburgish", + ["lin" ]="lingala", + ["lis" ]="lisu", + ["ljp" ]="lampung", + ["lki" ]="laki", + ["lma" ]="low mari", + ["lmb" ]="limbu", + ["lmo" ]="lombard", + ["lmw" ]="lomwe", + ["lom" ]="loma", + ["lrc" ]="luri", + ["lsb" ]="lower sorbian", + ["lsm" ]="lule sami", + ["lth" ]="lithuanian", + ["ltz" ]="luxembourgish", + ["lua" ]="luba-lulua", + ["lub" ]="luba-katanga", + ["lug" ]="ganda", + ["luh" ]="luyia", + ["luo" ]="luo", + ["lvi" ]="latvian", + ["mad" ]="madura", + ["mag" ]="magahi", + ["mah" ]="marshallese", + ["maj" ]="majang", + ["mak" ]="makhuwa", + ["mal" ]="malayalam reformed", + ["mam" ]="mam", + ["man" ]="mansi", + ["map" ]="mapudungun", + ["mar" ]="marathi", + ["maw" ]="marwari", + ["mbn" ]="mbundu", + ["mch" ]="manchu", + ["mcr" ]="moose cree", + ["mde" ]="mende", + ["mdr" ]="mandar", + ["men" ]="me'en", + ["mer" ]="meru", + ["mfe" ]="morisyen", + ["min" ]="minangkabau", + ["miz" ]="mizo", + ["mkd" ]="macedonian", + ["mkr" ]="makasar", + ["mkw" ]="kituba", + ["mle" ]="male", + ["mlg" ]="malagasy", + ["mln" ]="malinke", + ["mly" ]="malay", + ["mnd" ]="mandinka", + ["mng" ]="mongolian", + ["mni" ]="manipuri", + ["mnk" ]="maninka", + ["mnx" ]="manx", + ["moh" ]="mohawk", + ["mok" ]="moksha", + ["mol" ]="moldavian", + ["mon" ]="mon", + ["mor" ]="moroccan", + ["mos" ]="mossi", + ["mri" ]="maori", + ["mth" ]="maithili", + ["mts" ]="maltese", + ["mun" ]="mundari", + ["mus" ]="muscogee", + ["mwl" ]="mirandese", + ["mww" ]="hmong daw", + ["myn" ]="mayan", + ["mzn" ]="mazanderani", + ["nag" ]="naga-assamese", + ["nah" ]="nahuatl", + ["nan" ]="nanai", + ["nap" ]="neapolitan", + ["nas" ]="naskapi", + ["nau" ]="nauruan", + ["nav" ]="navajo", + ["ncr" ]="n-cree", + ["ndb" ]="ndebele", + ["ndc" ]="ndau", + ["ndg" ]="ndonga", + ["nds" ]="low saxon", + ["nep" ]="nepali", + ["new" ]="newari", + ["nga" ]="ngbaka", + ["ngr" ]="nagari", + ["nhc" ]="norway house cree", + ["nis" ]="nisi", + ["niu" ]="niuean", + ["nkl" ]="nyankole", + ["nko" ]="n'ko", + ["nld" ]="dutch", + ["noe" ]="nimadi", + ["nog" ]="nogai", + ["nor" ]="norwegian", + ["nov" ]="novial", + ["nsm" ]="northern sami", + ["nso" ]="sotho, northern", + ["nta" ]="northern tai", + ["nto" ]="esperanto", + ["nym" ]="nyamwezi", + ["nyn" ]="norwegian nynorsk", + ["oci" ]="occitan", + ["ocr" ]="oji-cree", + ["ojb" ]="ojibway", + ["ori" ]="odia", + ["oro" ]="oromo", + ["oss" ]="ossetian", + ["paa" ]="palestinian aramaic", + ["pag" ]="pangasinan", + ["pal" ]="pali", + ["pam" ]="pampangan", + ["pan" ]="punjabi", + ["pap" ]="palpa", + ["pap0"]="papiamentu", + ["pas" ]="pashto", + ["pau" ]="palauan", + ["pcc" ]="bouyei", + ["pcd" ]="picard", + ["pdc" ]="pennsylvania german", + ["pgr" ]="polytonic greek", + ["phk" ]="phake", + ["pih" ]="norfolk", + ["pil" ]="filipino", + ["plg" ]="palaung", + ["plk" ]="polish", + ["pms" ]="piemontese", + ["pnb" ]="western panjabi", + ["poh" ]="pocomchi", + ["pon" ]="pohnpeian", + ["pro" ]="provencal", + ["ptg" ]="portuguese", + ["pwo" ]="western pwo karen", + ["qin" ]="chin", + ["quc" ]="k’iche’", + ["quh" ]="quechua (bolivia)", + ["quz" ]="quechua", + ["qvi" ]="quechua (ecuador)", + ["qwh" ]="quechua (peru)", + ["raj" ]="rajasthani", + ["rar" ]="rarotongan", + ["rbu" ]="russian buriat", + ["rcr" ]="r-cree", + ["rej" ]="rejang", + ["ria" ]="riang", + ["rif" ]="tarifit", + ["rit" ]="ritarungo", + ["rkw" ]="arakwal", + ["rms" ]="romansh", + ["rmy" ]="vlax romani", + ["rom" ]="romanian", + ["roy" ]="romany", + ["rsy" ]="rusyn", + ["rtm" ]="rotuman", + ["rua" ]="kinyarwanda", + ["run" ]="rundi", + ["rup" ]="aromanian", + ["rus" ]="russian", + ["sad" ]="sadri", + ["san" ]="sanskrit", + ["sas" ]="sasak", + ["sat" ]="santali", + ["say" ]="sayisi", + ["scn" ]="sicilian", + ["sco" ]="scots", + ["sek" ]="sekota", + ["sel" ]="selkup", + ["sga" ]="old irish", + ["sgo" ]="sango", + ["sgs" ]="samogitian", + ["shi" ]="tachelhit", + ["shn" ]="shan", + ["sib" ]="sibe", + ["sid" ]="sidamo", + ["sig" ]="silte gurage", + ["sks" ]="skolt sami", + ["sky" ]="slovak", + ["sla" ]="slavey", + ["slv" ]="slovenian", + ["sml" ]="somali", + ["smo" ]="samoan", + ["sna" ]="sena", + ["sna0"]="shona", + ["snd" ]="sindhi", + ["snh" ]="sinhala (sinhalese)", + ["snk" ]="soninke", + ["sog" ]="sodo gurage", + ["sop" ]="songe", + ["sot" ]="sotho, southern", + ["sqi" ]="albanian", + ["srb" ]="serbian", + ["srd" ]="sardinian", + ["srk" ]="saraiki", + ["srr" ]="serer", + ["ssl" ]="south slavey", + ["ssm" ]="southern sami", + ["stq" ]="saterland frisian", + ["suk" ]="sukuma", + ["sun" ]="sundanese", + ["sur" ]="suri", + ["sva" ]="svan", + ["sve" ]="swedish", + ["swa" ]="swadaya aramaic", + ["swk" ]="swahili", + ["swz" ]="swati", + ["sxt" ]="sutu", + ["sxu" ]="upper saxon", + ["syl" ]="sylheti", + ["syr" ]="syriac", + ["szl" ]="silesian", + ["tab" ]="tabasaran", + ["taj" ]="tajiki", + ["tam" ]="tamil", + ["tat" ]="tatar", + ["tcr" ]="th-cree", + ["tdd" ]="dehong dai", + ["tel" ]="telugu", + ["tet" ]="tetum", + ["tgl" ]="tagalog", + ["tgn" ]="tongan", + ["tgr" ]="tigre", + ["tgy" ]="tigrinya", + ["tha" ]="thai", + ["tht" ]="tahitian", + ["tib" ]="tibetan", + ["tiv" ]="tiv", + ["tkm" ]="turkmen", + ["tmh" ]="tamashek", + ["tmn" ]="temne", + ["tna" ]="tswana", + ["tne" ]="tundra nenets", + ["tng" ]="tonga", + ["tod" ]="todo", + ["tod0"]="toma", + ["tpi" ]="tok pisin", + ["trk" ]="turkish", + ["tsg" ]="tsonga", + ["tua" ]="turoyo aramaic", + ["tul" ]="tulu", + ["tuv" ]="tuvin", + ["tvl" ]="tuvalu", + ["twi" ]="twi", + ["tyz" ]="tày", + ["tzm" ]="tamazight", + ["tzo" ]="tzotzil", + ["udm" ]="udmurt", + ["ukr" ]="ukrainian", + ["umb" ]="umbundu", + ["urd" ]="urdu", + ["usb" ]="upper sorbian", + ["uyg" ]="uyghur", + ["uzb" ]="uzbek", + ["vec" ]="venetian", + ["ven" ]="venda", + ["vit" ]="vietnamese", + ["vol" ]="volapük", + ["vro" ]="võro", + ["wa" ]="wa", + ["wag" ]="wagdi", + ["war" ]="waray-waray", + ["wcr" ]="west-cree", + ["wel" ]="welsh", + ["wlf" ]="wolof", + ["wln" ]="walloon", + ["xbd" ]="lü", + ["xhs" ]="xhosa", + ["xjb" ]="minjangbal", + ["xog" ]="soga", + ["xpe" ]="kpelle (liberia)", + ["yak" ]="sakha", + ["yao" ]="yao", + ["yap" ]="yapese", + ["yba" ]="yoruba", + ["ycr" ]="y-cree", + ["yic" ]="yi classic", + ["yim" ]="yi modern", + ["zea" ]="zealandic", + ["zgh" ]="standard morrocan tamazigh", + ["zha" ]="zhuang", + ["zhh" ]="chinese, hong kong sar", + ["zhp" ]="chinese phonetic", + ["zhs" ]="chinese simplified", + ["zht" ]="chinese traditional", + ["znd" ]="zande", + ["zul" ]="zulu", + ["zza" ]="zazaki", +} +local features=allocate { + ["aalt"]="access all alternates", + ["abvf"]="above-base forms", + ["abvm"]="above-base mark positioning", + ["abvs"]="above-base substitutions", + ["afrc"]="alternative fractions", + ["akhn"]="akhands", + ["blwf"]="below-base forms", + ["blwm"]="below-base mark positioning", + ["blws"]="below-base substitutions", + ["c2pc"]="petite capitals from capitals", + ["c2sc"]="small capitals from capitals", + ["calt"]="contextual alternates", + ["case"]="case-sensitive forms", + ["ccmp"]="glyph composition/decomposition", + ["cfar"]="conjunct form after ro", + ["cjct"]="conjunct forms", + ["clig"]="contextual ligatures", + ["cpct"]="centered cjk punctuation", + ["cpsp"]="capital spacing", + ["cswh"]="contextual swash", + ["curs"]="cursive positioning", + ["dflt"]="default processing", + ["dist"]="distances", + ["dlig"]="discretionary ligatures", + ["dnom"]="denominators", + ["dtls"]="dotless forms", + ["expt"]="expert forms", + ["falt"]="final glyph alternates", + ["fin2"]="terminal forms #2", + ["fin3"]="terminal forms #3", + ["fina"]="terminal forms", + ["flac"]="flattened accents over capitals", + ["frac"]="fractions", + ["fwid"]="full width", + ["half"]="half forms", + ["haln"]="halant forms", + ["halt"]="alternate half width", + ["hist"]="historical forms", + ["hkna"]="horizontal kana alternates", + ["hlig"]="historical ligatures", + ["hngl"]="hangul", + ["hojo"]="hojo kanji forms", + ["hwid"]="half width", + ["init"]="initial forms", + ["isol"]="isolated forms", + ["ital"]="italics", + ["jalt"]="justification alternatives", + ["jp04"]="jis2004 forms", + ["jp78"]="jis78 forms", + ["jp83"]="jis83 forms", + ["jp90"]="jis90 forms", + ["kern"]="kerning", + ["lfbd"]="left bounds", + ["liga"]="standard ligatures", + ["ljmo"]="leading jamo forms", + ["lnum"]="lining figures", + ["locl"]="localized forms", + ["ltra"]="left-to-right alternates", + ["ltrm"]="left-to-right mirrored forms", + ["mark"]="mark positioning", + ["med2"]="medial forms #2", + ["medi"]="medial forms", + ["mgrk"]="mathematical greek", + ["mkmk"]="mark to mark positioning", + ["mset"]="mark positioning via substitution", + ["nalt"]="alternate annotation forms", + ["nlck"]="nlc kanji forms", + ["nukt"]="nukta forms", + ["numr"]="numerators", + ["onum"]="old style figures", + ["opbd"]="optical bounds", + ["ordn"]="ordinals", + ["ornm"]="ornaments", + ["palt"]="proportional alternate width", + ["pcap"]="petite capitals", + ["pkna"]="proportional kana", + ["pnum"]="proportional figures", + ["pref"]="pre-base forms", + ["pres"]="pre-base substitutions", + ["pstf"]="post-base forms", + ["psts"]="post-base substitutions", + ["pwid"]="proportional widths", + ["qwid"]="quarter widths", + ["rand"]="randomize", + ["rclt"]="required contextual alternates", + ["rkrf"]="rakar forms", + ["rlig"]="required ligatures", + ["rphf"]="reph form", + ["rtbd"]="right bounds", + ["rtla"]="right-to-left alternates", + ["rtlm"]="right to left mirrored forms", + ["rvrn"]="required variation alternates", + ["ruby"]="ruby notation forms", + ["salt"]="stylistic alternates", + ["sinf"]="scientific inferiors", + ["size"]="optical size", + ["smcp"]="small capitals", + ["smpl"]="simplified forms", + ["ssty"]="script style", + ["stch"]="stretching glyph decomposition", + ["subs"]="subscript", + ["sups"]="superscript", + ["swsh"]="swash", + ["titl"]="titling", + ["tjmo"]="trailing jamo forms", + ["tnam"]="traditional name forms", + ["tnum"]="tabular figures", + ["trad"]="traditional forms", + ["twid"]="third widths", + ["unic"]="unicase", + ["valt"]="alternate vertical metrics", + ["vatu"]="vattu variants", + ["vert"]="vertical writing", + ["vhal"]="alternate vertical half metrics", + ["vjmo"]="vowel jamo forms", + ["vkna"]="vertical kana alternates", + ["vkrn"]="vertical kerning", + ["vpal"]="proportional alternate vertical metrics", + ["vrt2"]="vertical rotation", + ["zero"]="slashed zero", + ["trep"]="traditional tex replacements", + ["tlig"]="traditional tex ligatures", + ["ss.."]="stylistic set ..", + ["cv.."]="character variant ..", + ["js.."]="justification ..", + ["dv.."]="devanagari ..", + ["ml.."]="malayalam ..", +} +local baselines=allocate { + ["hang"]="hanging baseline", + ["icfb"]="ideographic character face bottom edge baseline", + ["icft"]="ideographic character face tope edige baseline", + ["ideo"]="ideographic em-box bottom edge baseline", + ["idtp"]="ideographic em-box top edge baseline", + ["math"]="mathematical centered baseline", + ["romn"]="roman baseline" +} +tables.scripts=scripts +tables.languages=languages +tables.features=features +tables.baselines=baselines +local acceptscripts=true directives.register("otf.acceptscripts",function(v) acceptscripts=v end) +local acceptlanguages=true directives.register("otf.acceptlanguages",function(v) acceptlanguages=v end) +local report_checks=logs.reporter("fonts","checks") +if otffeatures.features then + for k,v in next,otffeatures.features do + features[k]=v + end + otffeatures.features=features +end +local function swapped(h) + local r={} + for k,v in next,h do + r[gsub(v,"[^a-z0-9]","")]=k + end + return r +end +local verbosescripts=allocate(swapped(scripts )) +local verboselanguages=allocate(swapped(languages)) +local verbosefeatures=allocate(swapped(features )) +local verbosebaselines=allocate(swapped(baselines)) +local function resolve(t,k) + if k then + k=gsub(lower(k),"[^a-z0-9]","") + local v=rawget(t,k) + if v then + return v + end + end +end +setmetatableindex(verbosescripts,resolve) +setmetatableindex(verboselanguages,resolve) +setmetatableindex(verbosefeatures,resolve) +setmetatableindex(verbosebaselines,resolve) +setmetatableindex(scripts,function(t,k) + if k then + k=lower(k) + if k=="dflt" then + return k + end + local v=rawget(t,k) + if v then + return v + end + k=gsub(k," ","") + v=rawget(t,v) + if v then + return v + elseif acceptscripts then + report_checks("registering extra script %a",k) + rawset(t,k,k) + return k + end + end + return "dflt" +end) +setmetatableindex(languages,function(t,k) + if k then + k=lower(k) + if k=="dflt" then + return k + end + local v=rawget(t,k) + if v then + return v + end + k=gsub(k," ","") + v=rawget(t,v) + if v then + return v + elseif acceptlanguages then + report_checks("registering extra language %a",k) + rawset(t,k,k) + return k + end + end + return "dflt" +end) +if setmetatablenewindex then + setmetatablenewindex(languages,"ignore") + setmetatablenewindex(scripts,"ignore") + setmetatablenewindex(baselines,"ignore") +end +local function resolve(t,k) + if k then + k=lower(k) + local v=rawget(t,k) + if v then + return v + end + k=gsub(k," ","") + local v=rawget(t,k) + if v then + return v + end + local tag,dd=match(k,"(..)(%d+)") + if tag and dd then + local v=rawget(t,tag) + if v then + return v + else + local v=rawget(t,tag.."..") + if v then + return (gsub(v,"%.%.",tonumber(dd))) + end + end + end + end + return k +end +setmetatableindex(features,resolve) +local function assign(t,k,v) + if k and v then + v=lower(v) + rawset(t,k,v) + end +end +if setmetatablenewindex then + setmetatablenewindex(features,assign) +end +local checkers={ + rand=function(v) + return v==true and "random" or v + end +} +if not storage then + return +end +local usedfeatures=statistics.usedfeatures or {} +statistics.usedfeatures=usedfeatures +table.setmetatableindex(usedfeatures,function(t,k) if k then local v={} t[k]=v return v end end) +storage.register("fonts/otf/usedfeatures",usedfeatures,"fonts.handlers.otf.statistics.usedfeatures" ) +local normalizedaxis=otf.readers.helpers.normalizedaxis or function(s) return s end +function otffeatures.normalize(features) + if features then + local h={} + for key,value in next,features do + local k=lower(key) + if k=="language" then + local v=gsub(lower(value),"[^a-z0-9]","") + h.language=rawget(verboselanguages,v) or (languages[v] and v) or "dflt" + elseif k=="script" then + local v=gsub(lower(value),"[^a-z0-9]","") + h.script=rawget(verbosescripts,v) or (scripts[v] and v) or "dflt" + elseif k=="axis" then + h[k]=normalizedaxis(value) + if not callbacks.supported.glyph_stream_provider then + h.variableshapes=true + end + else + local uk=usedfeatures[key] + local uv=uk[value] + if uv then + else + uv=tonumber(value) + if uv then + elseif type(value)=="string" then + local b=is_boolean(value) + if type(b)=="nil" then + uv=lower(value) + else + uv=b + end + elseif type(value)=="table" then + uv=sequenced(t,",") + else + uv=value + end + if not rawget(features,k) then + k=rawget(verbosefeatures,k) or k + end + local c=checkers[k] + if c then + uv=c(uv) or vc + end + uk[value]=uv + end + h[k]=uv + end + end + return h + end +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-ott”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-cff” eefa156275ba0ac91aa651a944d35978] --- + +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,char,gmatch=string.byte,string.char,string.gmatch +local concat,remove=table.concat,table.remove +local floor,abs,round,ceil,min,max=math.floor,math.abs,math.round,math.ceil,math.min,math.max +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 bytetable=string.bytetable +local readers=fonts.handlers.otf.readers +local streamreader=readers.streamreader +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 readbytetable=streamreader.readbytetable +directives.register("fonts.streamreader",function() + streamreader=utilities.streams + readstring=streamreader.readstring + readbyte=streamreader.readcardinal1 + readushort=streamreader.readcardinal2 + readuint=streamreader.readcardinal3 + readulong=streamreader.readcardinal4 + setposition=streamreader.setposition + getposition=streamreader.getposition + readbytetable=streamreader.readbytetable +end) +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 startparsing +local stopparsing +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 major=readbyte(f) + local header={ + offset=offset, + major=major, + minor=readbyte(f), + size=readbyte(f), + } + if major==1 then + header.dsize=readbyte(f) + elseif major==2 then + header.dsize=readushort(f) + else + end + setposition(f,offset+header.size) + return header +end +local function readlengths(f,longcount) + local count=longcount and readulong(f) or 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) + local length=offset-previous + if length<0 then + report("bad offset: %i",length) + length=0 + end + lengths[i]=length + 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] + top=0 + end+P("\20")/function() + result.defaultwidthx=stack[top] + top=0 + end+P("\21")/function() + result.nominalwidthx=stack[top] + top=0 + end ++P("\24")/function() + result.vstore=stack[top] + top=0 + end+P("\25")/function() + result.maxstack=stack[top] + top=0 + 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,what) + stack={} + strings=data.strings + for i=1,#dictionaries do + top=0 + result=what=="cff" and { + 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, + } + } or { + charstringtype=2, + charset=0, + vstore=0, + cid={ + }, + } + 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 nominalwidth=0 + local defaultwidth=0 + local charset=false + 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 regions=false + local nofregions=0 + local region=false + local factors=false + local axis=false + local vsindex=0 + 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 xymoveto() + if keepcurve then + r=r+1 + result[r]={ x,y,"m" } + end + if checked then + if x>xmax then xmax=x elseif xymax then ymax=y elseif yxmax then + xmax=x + elseif xymax then + ymax=y + elseif yxmax then xmax=x elseif xymax then ymax=y elseif yxmax then + xmax=x + elseif xymax then + ymax=y + elseif yxmax then xmax=x1 elseif x1ymax then ymax=y1 elseif y1xmax then xmax=x2 elseif x2ymax then ymax=y2 elseif y2xmax then xmax=x3 elseif x3ymax then ymax=y3 elseif y32 then + width=stack[1] + if trace_charstrings then + showvalue("backtrack width",width) + end + else + width=true + end + end + if trace_charstrings then + showstate("rmoveto") + end + x=x+stack[top-1] + y=y+stack[top] + top=0 + xymoveto() + end + local function hmoveto() + if not width then + if top>1 then + width=stack[1] + if trace_charstrings then + showvalue("backtrack width",width) + end + else + width=true + end + end + if trace_charstrings then + showstate("hmoveto") + end + x=x+stack[top] + top=0 + xmoveto() + end + local function vmoveto() + if not width then + if top>1 then + width=stack[1] + if trace_charstrings then + showvalue("backtrack width",width) + end + else + width=true + end + end + if trace_charstrings then + showstate("vmoveto") + end + y=y+stack[top] + top=0 + ymoveto() + 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] + xylineto() + end + top=0 + end + local function hlineto() + if trace_charstrings then + showstate("hlineto") + end + if top==1 then + x=x+stack[1] + xlineto() + else + local swap=true + for i=1,top do + if swap then + x=x+stack[i] + xlineto() + swap=false + else + y=y+stack[i] + ylineto() + swap=true + end + end + end + top=0 + end + local function vlineto() + if trace_charstrings then + showstate("vlineto") + end + if top==1 then + y=y+stack[1] + ylineto() + else + local swap=false + for i=1,top do + if swap then + x=x+stack[i] + xlineto() + swap=false + else + y=y+stack[i] + ylineto() + swap=true + end + end + end + top=0 + 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] + xycurveto(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 + xycurveto(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] + xycurveto(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 + 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 + xycurveto(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] + xycurveto(ax,ay,bx,by,x,y) + end + x=x+stack[top-1] + y=y+stack[top] + xylineto() + 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] + xylineto() + 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] + xycurveto(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] + xycurveto(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] + xycurveto(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 + xycurveto(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] + xycurveto(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 + xycurveto(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] + xycurveto(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] + xycurveto(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 + xycurveto(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 reginit=false + local function updateregions(n) + if regions then + local current=regions[n] or regions[1] + nofregions=#current + if axis and n~=reginit then + factors={} + for i=1,nofregions do + local region=current[i] + local s=1 + for j=1,#axis do + local f=axis[j] + local r=region[j] + local start=r.start + local peak=r.peak + local stop=r.stop + if start>peak or peak>stop then + elseif start<0 and stop>0 and peak~=0 then + elseif peak==0 then + elseif fstop then + s=0 + break + elseif fpeak then + s=s*(stop-f)/(stop-peak) + else + end + end + factors[i]=s + end + end + end + reginit=n + end + local function setvsindex() + local vsindex=stack[top] + if trace_charstrings then + showstate(formatters["vsindex %i"](vsindex)) + end + updateregions(vsindex) + top=top-1 + end + local function blend() + local n=stack[top] + top=top-1 + if axis then + if trace_charstrings then + local t=top-nofregions*n + local m=t-n + for i=1,n do + local k=m+i + local d=m+n+(i-1)*nofregions + local old=stack[k] + local new=old + for r=1,nofregions do + new=new+stack[d+r]*factors[r] + end + stack[k]=new + showstate(formatters["blend %i of %i: %s -> %s"](i,n,old,new)) + end + top=t + elseif n==1 then + top=top-nofregions + local v=stack[top] + for r=1,nofregions do + v=v+stack[top+r]*factors[r] + end + stack[top]=v + else + top=top-nofregions*n + local d=top + local k=top-n + for i=1,n do + k=k+1 + local v=stack[k] + for r=1,nofregions do + v=v+stack[d+r]*factors[r] + end + stack[k]=v + d=d+nofregions + end + end + else + end + end + local actions={ [0]=unsupported, + getstem, + unsupported, + getstem, + vmoveto, + rlineto, + hlineto, + vlineto, + rrcurveto, + unsupported, + unsupported, + unsupported, + unsupported, + hsbw, + unsupported, + setvsindex, + blend, + 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 c_endchar=char(14) + local passon do + local rshift=bit32.rshift + local band=bit32.band + local round=math.round + local encode=table.setmetatableindex(function(t,i) + for i=-2048,-1130 do + t[i]=char(28,band(rshift(i,8),0xFF),band(i,0xFF)) + end + for i=-1131,-108 do + local v=0xFB00-i-108 + t[i]=char(band(rshift(v,8),0xFF),band(v,0xFF)) + end + for i=-107,107 do + t[i]=char(i+139) + end + for i=108,1131 do + local v=0xF700+i-108 + t[i]=char(band(rshift(v,8),0xFF),band(v,0xFF)) + end + for i=1132,2048 do + t[i]=char(28,band(rshift(i,8),0xFF),band(i,0xFF)) + end + return t[i] + end) + local function setvsindex() + local vsindex=stack[top] + updateregions(vsindex) + top=top-1 + end + local function blend() + local n=stack[top] + top=top-1 + if not axis then + elseif n==1 then + top=top-nofregions + local v=stack[top] + for r=1,nofregions do + v=v+stack[top+r]*factors[r] + end + stack[top]=round(v) + else + top=top-nofregions*n + local d=top + local k=top-n + for i=1,n do + k=k+1 + local v=stack[k] + for r=1,nofregions do + v=v+stack[d+r]*factors[r] + end + stack[k]=round(v) + d=d+nofregions + end + end + end + passon=function(operation) + if operation==15 then + setvsindex() + elseif operation==16 then + blend() + else + for i=1,top do + r=r+1 + result[r]=encode[stack[i]] + end + r=r+1 + result[r]=char(operation) + top=0 + end + end + end + local process + local function call(scope,list,bias) + 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 + process(tab) + else + showstate(formatters["unknown %s call %i"](scope,index)) + top=0 + end + end + depth=depth-1 + end + local justpass=false + process=function(tab) + local i=1 + local n=#tab + while i<=n do + local t=tab[i] + if t>=32 then + top=top+1 + if t<=246 then + stack[top]=t-139 + i=i+1 + elseif t<=250 then + stack[top]=t*256-63124+tab[i+1] + i=i+2 + elseif t<=254 then + stack[top]=-t*256+64148-tab[i+1] + i=i+2 + else + local n=0x100*tab[i+1]+tab[i+2] + if n>=0x8000 then + stack[top]=n-0x10000+(0x100*tab[i+3]+tab[i+4])/0xFFFF + else + stack[top]=n+(0x100*tab[i+3]+tab[i+4])/0xFFFF + end + i=i+5 + end + elseif t==28 then + top=top+1 + local n=0x100*tab[i+1]+tab[i+2] + if n>=0x8000 then + stack[top]=n-0x10000 + else + stack[top]=n + end + i=i+3 + elseif t==11 then + if trace_charstrings then + showstate("return") + end + return + elseif t==10 then + call("local",locals,localbias) + 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) + 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("",t) + end + top=0 + end + i=i+1 + elseif justpass then + passon(t) + i=i+1 + else + local a=actions[t] + if a then + local s=a(t) + if s then + i=i+s+1 + else + i=i+1 + end + else + if trace_charstrings then + showvalue("",t) + end + top=0 + i=i+1 + end + 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 + local function processshape(tab,index) + tab=bytetable(tab) + 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 + if regions then + updateregions(vsindex) + 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 justpass then + r=r+1 + result[r]=c_endchar + local stream=concat(result) + if glyph then + glyph.stream=stream + else + glyphs[index]={ stream=stream } + end + elseif glyph then + glyph.segments=keepcurve~=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 + elseif keepcurve then + glyphs[index]={ + segments=result, + boundingbox=boundingbox, + width=width, + name=charset and charset[index] or nil, + } + else + glyphs[index]={ + boundingbox=boundingbox, + width=width, + name=charset and charset[index] or nil, + } + end + if trace_charstrings then + report("width : %s",tostring(width)) + report("boundingbox: % t",boundingbox) + end + end + startparsing=function(fontdata,data,streams) + reginit=false + axis=false + regions=data.regions + justpass=streams==true + if regions then + regions={ regions } + axis=data.factors or false + end + end + stopparsing=function(fontdata,data) + stack={} + glyphs=false + result={} + top=0 + locals=false + globals=false + strings=false + end + local function setwidths(private) + if not private then + return 0,0 + end + local privatedata=private.data + if not privatedata then + return 0,0 + end + return privatedata.nominalwidthx or 0,privatedata.defaultwidthx or 0 + end + parsecharstrings=function(fontdata,data,glphs,doshapes,tversion,streams) + local dictionary=data.dictionaries[1] + local charstrings=dictionary.charstrings + keepcurve=doshapes + version=tversion + strings=data.strings + globals=data.routines or {} + locals=dictionary.subroutines or {} + charset=dictionary.charset + vsindex=dictionary.vsindex or 0 + glyphs=glphs or {} + globalbias,localbias=setbias(globals,locals) + nominalwidth,defaultwidth=setwidths(dictionary.private) + startparsing(fontdata,data,streams) + for index=1,#charstrings do + processshape(charstrings[index],index-1) + charstrings[index]=nil + end + stopparsing(fontdata,data) + return glyphs + end + parsecharstring=function(fontdata,data,dictionary,tab,glphs,index,doshapes,tversion) + keepcurve=doshapes + version=tversion + strings=data.strings + globals=data.routines or {} + locals=dictionary.subroutines or {} + charset=false + vsindex=dictionary.vsindex or 0 + glyphs=glphs or {} + globalbias,localbias=setbias(globals,locals) + nominalwidth,defaultwidth=setwidths(dictionary.private) + processshape(tab,index-1) + end +end +local function readglobals(f,data) + local routines=readlengths(f) + for i=1,#routines do + routines[i]=readbytetable(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 and 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 + else + dictionary.nocharset=true + dictionary.charset=nil + 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]=readbytetable(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,what) + local header=data.header + local dictionaries=data.dictionaries + local dictionary=dictionaries[1] + local stringtype=dictionary.charstringtype + local offset=dictionary.charstrings + if type(offset)~="number" then + elseif stringtype==2 then + setposition(f,header.offset+offset) + local charstrings=readlengths(f,what=="cff2") + 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",stringtype) + 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 +readers.parsecharstrings=parsecharstrings +local function readnoselect(f,fontdata,data,glyphs,doshapes,version,streams) + local dictionaries=data.dictionaries + local dictionary=dictionaries[1] + readglobals(f,data) + readcharstrings(f,data,version) + if version=="cff2" then + dictionary.charset=nil + else + readencodings(f,data) + readcharsets(f,data,dictionary) + end + readprivates(f,data) + parseprivates(data,data.dictionaries) + readlocals(f,data,dictionary) + startparsing(fontdata,data,streams) + parsecharstrings(fontdata,data,glyphs,doshapes,version,streams) + stopparsing(fontdata,data) +end +local function readfdselect(f,fontdata,data,glyphs,doshapes,version,streams) + 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,version) + if version~="cff2" then + readencodings(f,data) + end + 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 + startparsing(fontdata,data,streams) + for i=1,#charstrings do + parsecharstring(fontdata,data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version) + charstrings[i]=nil + end + stopparsing(fontdata,data) + end +end +local gotodatatable=readers.helpers.gotodatatable +local function cleanup(data,dictionaries) +end +function readers.cff(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"cff",specification.details) + if tableoffset then + local header=readheader(f) + if header.major~=1 then + report("only version %s is supported for table %a",1,"cff") + return + end + local glyphs=fontdata.glyphs + 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,dictionaries,"cff") + local dic=dictionaries[1] + local cid=dic.cid + fontdata.cffinfo={ + familynamename=dic.familyname, + fullname=dic.fullname, + boundingbox=dic.boundingbox, + weight=dic.weight, + italicangle=dic.italicangle, + underlineposition=dic.underlineposition, + underlinethickness=dic.underlinethickness, + monospaced=dic.monospaced, + } + fontdata.cidinfo=cid and { + registry=cid.registry, + ordering=cid.ordering, + supplement=cid.supplement, + } + if specification.glyphs then + local all=specification.shapes or false + if cid and cid.fdselect then + readfdselect(f,fontdata,data,glyphs,all,"cff") + else + readnoselect(f,fontdata,data,glyphs,all,"cff") + end + end + cleanup(data,dictionaries) + end +end +function readers.cff2(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"cff2",specification.glyphs) + if tableoffset then + local header=readheader(f) + if header.major~=2 then + report("only version %s is supported for table %a",2,"cff2") + return + end + local glyphs=fontdata.glyphs + local dictionaries={ readstring(f,header.dsize) } + local data={ + header=header, + dictionaries=dictionaries, + nofglyphs=fontdata.nofglyphs, + } + parsedictionaries(data,dictionaries,"cff2") + local offset=dictionaries[1].vstore + if offset>0 then + local storeoffset=dictionaries[1].vstore+data.header.offset+2 + local regions,deltas=readers.helpers.readvariationdata(f,storeoffset,factors) + data.regions=regions + data.deltas=deltas + else + data.regions={} + data.deltas={} + end + data.factors=specification.factors + local cid=data.dictionaries[1].cid + local all=specification.shapes or false + if cid and cid.fdselect then + readfdselect(f,fontdata,data,glyphs,all,"cff2",specification.streams) + else + readnoselect(f,fontdata,data,glyphs,all,"cff2",specification.streams) + end + cleanup(data,dictionaries) + end +end +function readers.cffcheck(filename) + local f=io.open(filename,"rb") + if f then + local fontdata={ + glyphs={}, + } + local header=readheader(f) + if header.major~=1 then + report("only version %s is supported for table %a",1,"cff") + return + end + local names=readfontnames(f) + local dictionaries=readtopdictionaries(f) + local strings=readstrings(f) + local glyphs={} + local data={ + header=header, + names=names, + dictionaries=dictionaries, + strings=strings, + glyphs=glyphs, + nofglyphs=4, + } + parsedictionaries(data,dictionaries,"cff") + local cid=data.dictionaries[1].cid + if cid and cid.fdselect then + readfdselect(f,fontdata,data,glyphs,false) + else + readnoselect(f,fontdata,data,glyphs,false) + end + return data + end +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-cff”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-ttf” ff587471f4297aa8ba0fa022609adc6e] --- + +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 band,rshift=bit32.band,bit32.rshift +local sqrt,round=math.sqrt,math.round +local char=string.char +local concat=table.concat +local report=logs.reporter("otf reader","ttf") +local trace_deltas=false +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 readinteger=streamreader.readinteger1 +local readcardinaltable=streamreader.readcardinaltable +local readintegertable=streamreader.readintegertable +directives.register("fonts.streamreader",function() + streamreader=utilities.streams + setposition=streamreader.setposition + getposition=streamreader.getposition + skipbytes=streamreader.skip + readbyte=streamreader.readcardinal1 + readushort=streamreader.readcardinal2 + readulong=streamreader.readcardinal4 + readchar=streamreader.readinteger1 + readshort=streamreader.readinteger2 + read2dot14=streamreader.read2dot14 + readinteger=streamreader.readinteger1 + readcardinaltable=streamreader.readcardinaltable + readintegertable=streamreader.readintegertable +end) +local short=2 +local ushort=2 +local ulong=4 +local helpers=readers.helpers +local gotodatatable=helpers.gotodatatable +local function mergecomposites(glyphs,shapes) + local function merge(index,shape,components) + local contours={} + local points={} + local nofcontours=0 + local nofpoints=0 + local offset=0 + local deltas=shape.deltas + for i=1,#components do + local component=components[i] + local subindex=component.index + local subshape=shapes[subindex] + local subcontours=subshape.contours + local subpoints=subshape.points + if not subcontours then + local subcomponents=subshape.components + if subcomponents then + subcontours,subpoints=merge(subindex,subshape,subcomponents) + end + end + if subpoints 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,#subpoints do + local p=subpoints[i] + local x=p[1] + local y=p[2] + nofpoints=nofpoints+1 + points[nofpoints]={ + xscale*x+xrotate*y+xoffset, + yscale*y+yrotate*x+yoffset, + p[3] + } + end + for i=1,#subcontours do + nofcontours=nofcontours+1 + contours[nofcontours]=offset+subcontours[i] + end + offset=offset+#subpoints + else + report("missing contours composite %s, component %s of %s, glyph %s",index,i,#components,subindex) + end + end + shape.points=points + shape.contours=contours + shape.components=nil + return contours,points + end + for index=1,#glyphs do + local shape=shapes[index] + if shape then + local components=shape.components + if components then + merge(index,shape,components) + end + 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 applyaxis(glyph,shape,deltas,dowidth) + local points=shape.points + if points then + local nofpoints=#points + local h=nofpoints+2 + local l=nofpoints+1 + local dw=0 + local dl=0 + for i=1,#deltas do + local deltaset=deltas[i] + local xvalues=deltaset.xvalues + local yvalues=deltaset.yvalues + local dpoints=deltaset.points + local factor=deltaset.factor + if dpoints then + local nofdpoints=#dpoints + for i=1,nofdpoints do + local d=dpoints[i] + local p=points[d] + if p then + if xvalues then + local x=xvalues[i] + if x and x~=0 then + p[1]=p[1]+factor*x + end + end + if yvalues then + local y=yvalues[i] + if y and y~=0 then + p[2]=p[2]+factor*y + end + end + elseif dowidth then + if d==h then + local x=xvalues[i] + if x then + dw=dw+factor*x + end + elseif d==l then + local x=xvalues[i] + if x then + dl=dl+factor*x + end + end + end + end + else + for i=1,nofpoints do + local p=points[i] + if xvalues then + local x=xvalues[i] + if x and x~=0 then + p[1]=p[1]+factor*x + end + end + if yvalues then + local y=yvalues[i] + if y and y~=0 then + p[2]=p[2]+factor*y + end + end + end + if dowidth then + local x=xvalues[h] + if x then + dw=dw+factor*x + end + local x=xvalues[l] + if x then + dl=dl+factor*x + end + end + end + end + if dowidth then + local width=glyph.width or 0 + glyph.width=width+dw-dl + end + else + report("no points for glyph %a",glyph.name) + end +end +local quadratic=false +local function contours2outlines_normal(glyphs,shapes) + for index=1,#glyphs do + local shape=shapes[index] + if shape then + local glyph=glyphs[index] + local contours=shape.contours + local points=shape.points + if contours then + local nofcontours=#contours + local segments={} + local nofsegments=0 + glyph.segments=segments + if nofcontours>0 then + local px,py=0,0 + local first=1 + for i=1,nofcontours do + local last=contours[i] + if last>=first then + local first_pt=points[first] + local first_on=first_pt[3] + if first==last then + first_pt[3]="m" + nofsegments=nofsegments+1 + segments[nofsegments]=first_pt + else + local first_on=first_pt[3] + local last_pt=points[last] + 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 + local x,y=first_pt[1],first_pt[2] + if not done then + xmin,ymin,xmax,ymax=x,y,x,y + done=true + end + nofsegments=nofsegments+1 + segments[nofsegments]={ x,y,"m" } + if not quadratic then + px,py=x,y + end + local previous_pt=first_pt + for i=first,last do + local current_pt=points[i] + local current_on=current_pt[3] + local previous_on=previous_pt[3] + if previous_on then + if current_on then + local x,y=current_pt[1],current_pt[2] + nofsegments=nofsegments+1 + segments[nofsegments]={ x,y,"l" } + if not quadratic then + px,py=x,y + end + else + control_pt=current_pt + end + elseif current_on then + local x1,y1=control_pt[1],control_pt[2] + local x2,y2=current_pt[1],current_pt[2] + nofsegments=nofsegments+1 + if quadratic then + segments[nofsegments]={ x1,y1,x2,y2,"q" } + else + x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2) + segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } + end + control_pt=false + else + local x2,y2=(previous_pt[1]+current_pt[1])/2,(previous_pt[2]+current_pt[2])/2 + local x1,y1=control_pt[1],control_pt[2] + nofsegments=nofsegments+1 + if quadratic then + segments[nofsegments]={ x1,y1,x2,y2,"q" } + else + x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2) + segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } + end + control_pt=current_pt + end + previous_pt=current_pt + end + if first_pt==last_pt then + else + nofsegments=nofsegments+1 + local x2,y2=first_pt[1],first_pt[2] + if not control_pt then + segments[nofsegments]={ x2,y2,"l" } + elseif quadratic then + local x1,y1=control_pt[1],control_pt[2] + segments[nofsegments]={ x1,y1,x2,y2,"q" } + else + local x1,y1=control_pt[1],control_pt[2] + x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2) + segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } + end + end + end + end + first=last+1 + end + end + end + end + end +end +local function contours2outlines_shaped(glyphs,shapes,keepcurve) + for index=1,#glyphs do + local shape=shapes[index] + if shape then + local glyph=glyphs[index] + local contours=shape.contours + local points=shape.points + if contours then + local nofcontours=#contours + local segments=keepcurve and {} or nil + local nofsegments=0 + if keepcurve then + glyph.segments=segments + end + if nofcontours>0 then + local xmin,ymin,xmax,ymax,done=0,0,0,0,false + local px,py=0,0 + local first=1 + for i=1,nofcontours do + local last=contours[i] + if last>=first then + local first_pt=points[first] + local first_on=first_pt[3] + if first==last then + if keepcurve then + first_pt[3]="m" + nofsegments=nofsegments+1 + segments[nofsegments]=first_pt + end + else + local first_on=first_pt[3] + local last_pt=points[last] + 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 + local x,y=first_pt[1],first_pt[2] + if not done then + xmin,ymin,xmax,ymax=x,y,x,y + done=true + else + if xxmax then xmax=x end + if yymax then ymax=y end + end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x,y,"m" } + end + if not quadratic then + px,py=x,y + end + local previous_pt=first_pt + for i=first,last do + local current_pt=points[i] + local current_on=current_pt[3] + local previous_on=previous_pt[3] + if previous_on then + if current_on then + local x,y=current_pt[1],current_pt[2] + if xxmax then xmax=x end + if yymax then ymax=y end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x,y,"l" } + end + if not quadratic then + px,py=x,y + end + else + control_pt=current_pt + end + elseif current_on then + local x1,y1=control_pt[1],control_pt[2] + local x2,y2=current_pt[1],current_pt[2] + if quadratic then + if x1xmax then xmax=x1 end + if y1ymax then ymax=y1 end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x1,y1,x2,y2,"q" } + end + else + x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2) + if x1xmax then xmax=x1 end + if y1ymax then ymax=y1 end + if x2xmax then xmax=x2 end + if y2ymax then ymax=y2 end + if pxxmax then xmax=px end + if pyymax then ymax=py end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } + end + end + control_pt=false + else + local x2,y2=(previous_pt[1]+current_pt[1])/2,(previous_pt[2]+current_pt[2])/2 + local x1,y1=control_pt[1],control_pt[2] + if quadratic then + if x1xmax then xmax=x1 end + if y1ymax then ymax=y1 end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x1,y1,x2,y2,"q" } + end + else + x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2) + if x1xmax then xmax=x1 end + if y1ymax then ymax=y1 end + if x2xmax then xmax=x2 end + if y2ymax then ymax=y2 end + if pxxmax then xmax=px end + if pyymax then ymax=py end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } + end + end + control_pt=current_pt + end + previous_pt=current_pt + end + if first_pt==last_pt then + elseif not control_pt then + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ first_pt[1],first_pt[2],"l" } + end + else + local x1,y1=control_pt[1],control_pt[2] + local x2,y2=first_pt[1],first_pt[2] + if x1xmax then xmax=x1 end + if y1ymax then ymax=y1 end + if quadratic then + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x1,y1,x2,y2,"q" } + end + else + x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2) + if x2xmax then xmax=x2 end + if y2ymax then ymax=y2 end + if pxxmax then xmax=px end + if pyymax then ymax=py end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } + end + end + end + end + end + first=last+1 + end + glyph.boundingbox={ round(xmin),round(ymin),round(xmax),round(ymax) } + end + end + end + end +end +local c_zero=char(0) +local s_zero=char(0,0) +local function toushort(n) + return char(band(rshift(n,8),0xFF),band(n,0xFF)) +end +local function toshort(n) + if n<0 then + n=n+0x10000 + end + return char(band(rshift(n,8),0xFF),band(n,0xFF)) +end +local function repackpoints(glyphs,shapes) + local noboundingbox={ 0,0,0,0 } + local result={} + for index=1,#glyphs do + local shape=shapes[index] + if shape then + local r=0 + local glyph=glyphs[index] + if false then + else + local contours=shape.contours + local nofcontours=contours and #contours or 0 + local boundingbox=glyph.boundingbox or noboundingbox + r=r+1 result[r]=toshort(nofcontours) + r=r+1 result[r]=toshort(boundingbox[1]) + r=r+1 result[r]=toshort(boundingbox[2]) + r=r+1 result[r]=toshort(boundingbox[3]) + r=r+1 result[r]=toshort(boundingbox[4]) + if nofcontours>0 then + for i=1,nofcontours do + r=r+1 result[r]=toshort(contours[i]-1) + end + r=r+1 result[r]=s_zero + local points=shape.points + local currentx=0 + local currenty=0 + local xpoints={} + local ypoints={} + local x=0 + local y=0 + local lastflag=nil + local nofflags=0 + for i=1,#points do + local pt=points[i] + local px=pt[1] + local py=pt[2] + local fl=pt[3] and 0x01 or 0x00 + if px==currentx then + fl=fl+0x10 + else + local dx=round(px-currentx) + if dx<-255 or dx>255 then + x=x+1 xpoints[x]=toshort(dx) + elseif dx<0 then + fl=fl+0x02 + x=x+1 xpoints[x]=char(-dx) + elseif dx>0 then + fl=fl+0x12 + x=x+1 xpoints[x]=char(dx) + else + fl=fl+0x02 + x=x+1 xpoints[x]=c_zero + end + end + if py==currenty then + fl=fl+0x20 + else + local dy=round(py-currenty) + if dy<-255 or dy>255 then + y=y+1 ypoints[y]=toshort(dy) + elseif dy<0 then + fl=fl+0x04 + y=y+1 ypoints[y]=char(-dy) + elseif dy>0 then + fl=fl+0x24 + y=y+1 ypoints[y]=char(dy) + else + fl=fl+0x04 + y=y+1 ypoints[y]=c_zero + end + end + currentx=px + currenty=py + if lastflag==fl then + nofflags=nofflags+1 + else + if nofflags==1 then + r=r+1 result[r]=char(lastflag) + elseif nofflags==2 then + r=r+1 result[r]=char(lastflag,lastflag) + elseif nofflags>2 then + lastflag=lastflag+0x08 + r=r+1 result[r]=char(lastflag,nofflags-1) + end + nofflags=1 + lastflag=fl + end + end + if nofflags==1 then + r=r+1 result[r]=char(lastflag) + elseif nofflags==2 then + r=r+1 result[r]=char(lastflag,lastflag) + elseif nofflags>2 then + lastflag=lastflag+0x08 + r=r+1 result[r]=char(lastflag,nofflags-1) + end + r=r+1 result[r]=concat(xpoints) + r=r+1 result[r]=concat(ypoints) + end + end + glyph.stream=concat(result,"",1,r) + else + end + end +end +local function readglyph(f,nofcontours) + local points={} + local instructions={} + local flags={} + local contours={} + for i=1,nofcontours do + contours[i]=readshort(f)+1 + end + local nofpoints=contours[nofcontours] + local nofinstructions=readushort(f) + skipbytes(f,nofinstructions) + local i=1 + while i<=nofpoints do + local flag=readbyte(f) + flags[i]=flag + if band(flag,0x08)~=0 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=band(flag,0x02)~=0 + local same=band(flag,0x10)~=0 + 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,0,band(flag,0x01)~=0 } + end + local y=0 + for i=1,nofpoints do + local flag=flags[i] + local short=band(flag,0x04)~=0 + local same=band(flag,0x20)~=0 + 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 + return { + type="glyph", + points=points, + contours=contours, + nofpoints=nofpoints, + } +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=band(flags,0x0002)~=0 + local f_offset=band(flags,0x0800)~=0 + 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 band(flags,0x0001)~=0 then + xoffset=readshort(f) + yoffset=readshort(f) + else + xoffset=readchar(f) + yoffset=readchar(f) + end + else + if band(flags,0x0001)~=0 then + base=readshort(f) + reference=readshort(f) + else + base=readchar(f) + reference=readchar(f) + end + end + if band(flags,0x0008)~=0 then + xscale=read2dot14(f) + yscale=xscale + if f_xyarg and f_offset then + xoffset=xoffset*xscale + yoffset=yoffset*yscale + end + elseif band(flags,0x0040)~=0 then + xscale=read2dot14(f) + yscale=read2dot14(f) + if f_xyarg and f_offset then + xoffset=xoffset*xscale + yoffset=yoffset*yscale + end + elseif band(flags,0x0080)~=0 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=band(flags,0x0200)~=0, + round=band(flags,0x0006)~=0, + base=base, + reference=reference, + matrix={ xscale,xrotate,yrotate,yscale,xoffset,yoffset }, + } + if band(flags,0x0100)~=0 then + instructions=true + end + if not band(flags,0x0020)~=0 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-2 + for i=0,nofglyphs do + locations[i]=offset+readulong(f) + end + fontdata.nofglyphs=nofglyphs + else + local nofglyphs=datatable.length/2-2 + 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) + local tableoffset=gotodatatable(f,fontdata,"glyf",specification.glyphs) + if tableoffset 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 or specification.instance + 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 + if readers.gvar then + readers.gvar(f,fontdata,specification,glyphs,shapes) + end + mergecomposites(glyphs,shapes) + if specification.instance then + if specification.streams then + repackpoints(glyphs,shapes) + else + contours2outlines_shaped(glyphs,shapes,specification.shapes) + end + elseif specification.shapes then + contours2outlines_normal(glyphs,shapes) + end + end + end + end +end +local function readtuplerecord(f,nofaxis) + local record={} + for i=1,nofaxis do + record[i]=read2dot14(f) + end + return record +end +local function readpoints(f) + local count=readbyte(f) + if count==0 then + return nil,0 + else + if count<128 then + elseif band(count,0x80)~=0 then + count=band(count,0x7F)*256+readbyte(f) + else + end + local points={} + local p=0 + local n=1 + while p0 do + local control=readbyte(f) +if not control then + break +end + local allzero=band(control,0x80)~=0 + local runlength=band(control,0x3F)+1 + if allzero then + z=z+runlength + else + local runreader=band(control,0x40)~=0 and readshort or readinteger + if z>0 then + for i=1,z do + p=p+1 + deltas[p]=0 + end + z=0 + end + for i=1,runlength do + p=p+1 + deltas[p]=runreader(f) + end + end + nofpoints=nofpoints-runlength + end + if p>0 then + return deltas + else + end +end +local function readdeltas(f,nofpoints) + local deltas={} + local p=0 + while nofpoints>0 do + local control=readbyte(f) + if control then + local allzero=band(control,0x80)~=0 + local runlength=band(control,0x3F)+1 + if allzero then + for i=1,runlength do + p=p+1 + deltas[p]=0 + end + else + local runreader=band(control,0x40)~=0 and readshort or readinteger + for i=1,runlength do + p=p+1 + deltas[p]=runreader(f) + end + end + nofpoints=nofpoints-runlength + else + break + end + end + if p>0 then + return deltas + else + end +end +function readers.gvar(f,fontdata,specification,glyphdata,shapedata) + local instance=specification.instance + if not instance then + return + end + local factors=specification.factors + if not factors then + return + end + local tableoffset=gotodatatable(f,fontdata,"gvar",specification.variable or specification.shapes) + if tableoffset then + local version=readulong(f) + local nofaxis=readushort(f) + local noftuples=readushort(f) + local tupleoffset=tableoffset+readulong(f) + local nofglyphs=readushort(f) + local flags=readushort(f) + local dataoffset=tableoffset+readulong(f) + local data={} + local tuples={} + local glyphdata=fontdata.glyphs + local dowidth=not fontdata.variabledata.hvarwidths + if band(flags,0x0001)~=0 then + for i=1,nofglyphs+1 do + data[i]=dataoffset+readulong(f) + end + else + for i=1,nofglyphs+1 do + data[i]=dataoffset+2*readushort(f) + end + end + if noftuples>0 then + setposition(f,tupleoffset) + for i=1,noftuples do + tuples[i]=readtuplerecord(f,nofaxis) + end + end + local nextoffset=false + local startoffset=data[1] + for i=1,nofglyphs do + nextoffset=data[i+1] + local glyph=glyphdata[i-1] + local name=trace_deltas and glyph.name + if startoffset==nextoffset then + if name then + report("no deltas for glyph %a",name) + end + else + local shape=shapedata[i-1] + if not shape then + if name then + report("no shape for glyph %a",name) + end + else + lastoffset=startoffset + setposition(f,startoffset) + local flags=readushort(f) + local count=band(flags,0x0FFF) + local offset=startoffset+readushort(f) + local deltas={} + local allpoints=(shape.nofpoints or 0) + local shared=false + local nofshared=0 + if band(flags,0x8000)~=0 then + local current=getposition(f) + setposition(f,offset) + shared,nofshared=readpoints(f) + offset=getposition(f) + setposition(f,current) + end + for j=1,count do + local size=readushort(f) + local flags=readushort(f) + local index=band(flags,0x0FFF) + local haspeak=band(flags,0x8000)~=0 + local intermediate=band(flags,0x4000)~=0 + local private=band(flags,0x2000)~=0 + local peak=nil + local start=nil + local stop=nil + local xvalues=nil + local yvalues=nil + local points=shared + local nofpoints=nofshared + if haspeak then + peak=readtuplerecord(f,nofaxis) + else + if index+1>#tuples then + report("error, bad tuple index",index) + end + peak=tuples[index+1] + end + if intermediate then + start=readtuplerecord(f,nofaxis) + stop=readtuplerecord(f,nofaxis) + end + if size>0 then + local current=getposition(f) + setposition(f,offset) + if private then + points,nofpoints=readpoints(f) + end + if nofpoints==0 then + nofpoints=allpoints+4 + end + if nofpoints>0 then + xvalues=readdeltas(f,nofpoints) + yvalues=readdeltas(f,nofpoints) + end + offset=offset+size + setposition(f,current) + end + if not xvalues and not yvalues then + points=nil + end + local s=1 + for i=1,nofaxis do + local f=factors[i] + local peak=peak and peak [i] or 0 + local start=start and start[i] or (peak<0 and peak or 0) + local stop=stop and stop [i] or (peak>0 and peak or 0) + if start>peak or peak>stop then + elseif start<0 and stop>0 and peak~=0 then + elseif peak==0 then + elseif fstop then + s=0 + break + elseif fpeak then + s=s*(stop-f)/(stop-peak) + else + end + end + if s==0 then + if name then + report("no deltas applied for glyph %a",name) + end + else + deltas[#deltas+1]={ + factor=s, + points=points, + xvalues=xvalues, + yvalues=yvalues, + } + end + end + if shape.type=="glyph" then + applyaxis(glyph,shape,deltas,dowidth) + else + shape.deltas=deltas + end + end + end + startoffset=nextoffset + end + end +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-ttf”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-dsp” 9cade9beb226c61b4323729aca1f4a80] --- + +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 band=bit32.band +local extract=bit32.extract +local bor=bit32.bor +local lshift=bit32.lshift +local rshift=bit32.rshift +local gsub=string.gsub +local lower=string.lower +local sub=string.sub +local strip=string.strip +local tohash=table.tohash +local concat=table.concat +local copy=table.copy +local reversed=table.reversed +local sort=table.sort +local insert=table.insert +local round=math.round +local settings_to_hash=utilities.parsers.settings_to_hash_colon_too +local setmetatableindex=table.setmetatableindex +local formatters=string.formatters +local sortedkeys=table.sortedkeys +local sortedhash=table.sortedhash +local sequenced=table.sequenced +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 readushort=streamreader.readcardinal2 +local readulong=streamreader.readcardinal4 +local readinteger=streamreader.readinteger1 +local readshort=streamreader.readinteger2 +local readstring=streamreader.readstring +local readtag=streamreader.readtag +local readbytes=streamreader.readbytes +local readfixed=streamreader.readfixed4 +local read2dot14=streamreader.read2dot14 +local skipshort=streamreader.skipshort +local skipbytes=streamreader.skip +local readbytetable=streamreader.readbytetable +local readbyte=streamreader.readbyte +local readcardinaltable=streamreader.readcardinaltable +local readintegertable=streamreader.readintegertable +local readfword=readshort +local short=2 +local ushort=2 +local ulong=4 +directives.register("fonts.streamreader",function() + streamreader=utilities.streams + setposition=streamreader.setposition + getposition=streamreader.getposition + readushort=streamreader.readcardinal2 + readulong=streamreader.readcardinal4 + readinteger=streamreader.readinteger1 + readshort=streamreader.readinteger2 + readstring=streamreader.readstring + readtag=streamreader.readtag + readbytes=streamreader.readbytes + readfixed=streamreader.readfixed4 + read2dot14=streamreader.read2dot14 + skipshort=streamreader.skipshort + skipbytes=streamreader.skip + readbytetable=streamreader.readbytetable + readbyte=streamreader.readbyte + readcardinaltable=streamreader.readcardinaltable + readintegertable=streamreader.readintegertable + readfword=readshort +end) +local gsubhandlers={} +local gposhandlers={} +readers.gsubhandlers=gsubhandlers +readers.gposhandlers=gposhandlers +local helpers=readers.helpers +local gotodatatable=helpers.gotodatatable +local setvariabledata=helpers.setvariabledata +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 function setmetrics(data,where,tag,d) + local w=data[where] + if w then + local v=w[tag] + if v then + w[tag]=v+d + end + end +end +local variabletags={ + hasc=function(data,d) setmetrics(data,"windowsmetrics","typoascender",d) end, + hdsc=function(data,d) setmetrics(data,"windowsmetrics","typodescender",d) end, + hlgp=function(data,d) setmetrics(data,"windowsmetrics","typolinegap",d) end, + hcla=function(data,d) setmetrics(data,"windowsmetrics","winascent",d) end, + hcld=function(data,d) setmetrics(data,"windowsmetrics","windescent",d) end, + vasc=function(data,d) setmetrics(data,"vhea not done","ascent",d) end, + vdsc=function(data,d) setmetrics(data,"vhea not done","descent",d) end, + vlgp=function(data,d) setmetrics(data,"vhea not done","linegap",d) end, + xhgt=function(data,d) setmetrics(data,"windowsmetrics","xheight",d) end, + cpht=function(data,d) setmetrics(data,"windowsmetrics","capheight",d) end, + sbxs=function(data,d) setmetrics(data,"windowsmetrics","subscriptxsize",d) end, + sbys=function(data,d) setmetrics(data,"windowsmetrics","subscriptysize",d) end, + sbxo=function(data,d) setmetrics(data,"windowsmetrics","subscriptxoffset",d) end, + sbyo=function(data,d) setmetrics(data,"windowsmetrics","subscriptyoffset",d) end, + spxs=function(data,d) setmetrics(data,"windowsmetrics","superscriptxsize",d) end, + spys=function(data,d) setmetrics(data,"windowsmetrics","superscriptysize",d) end, + spxo=function(data,d) setmetrics(data,"windowsmetrics","superscriptxoffset",d) end, + spyo=function(data,d) setmetrics(data,"windowsmetrics","superscriptyoffset",d) end, + strs=function(data,d) setmetrics(data,"windowsmetrics","strikeoutsize",d) end, + stro=function(data,d) setmetrics(data,"windowsmetrics","strikeoutpos",d) end, + unds=function(data,d) setmetrics(data,"postscript","underlineposition",d) end, + undo=function(data,d) setmetrics(data,"postscript","underlinethickness",d) end, +} +local read_cardinal={ + streamreader.readcardinal1, + streamreader.readcardinal2, + streamreader.readcardinal3, + streamreader.readcardinal4, +} +local read_integer={ + streamreader.readinteger1, + streamreader.readinteger2, + streamreader.readinteger3, + streamreader.readinteger4, +} +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={ + band(k,0x0008)~=0 and true or false, + band(k,0x0004)~=0 and true or false, + band(k,0x0002)~=0 and true or false, + band(k,0x0001)~=0 and true or false, + } + t[k]=v + return v +end) +local function axistofactors(str) + local t=settings_to_hash(str) + for k,v in next,t do + t[k]=tonumber(v) or v + end + return t +end +local hash=table.setmetatableindex(function(t,k) + local v=sequenced(axistofactors(k),",") + t[k]=v + return v +end) +helpers.normalizedaxishash=hash +local cleanname=fonts.names and fonts.names.cleanname or function(name) + return name and (gsub(lower(name),"[^%a%d]","")) or nil +end +helpers.cleanname=cleanname +function helpers.normalizedaxis(str) + return hash[str] or str +end +local function getaxisscale(segments,minimum,default,maximum,user) + if not minimum or not default or not maximum then + return false + end + if usermaximum then + user=maximum + end + if userdefault then + default=(user-default)/(maximum-default) + else + default=0 + end + if not segments then + return default + end + local e + for i=1,#segments do + local s=segments[i] + if type(s)~="number" then + report("using default axis scale") + return default + elseif s[1]>=default then + if s[2]==default then + return default + else + e=i + break + end + end + end + if e then + local b=segments[e-1] + local e=segments[e] + return b[2]+(e[2]-b[2])*(default-b[1])/(e[1]-b[1]) + else + return false + end +end +local function getfactors(data,instancespec) + if instancespec==true then + elseif type(instancespec)~="string" or instancespec=="" then + return + end + local variabledata=data.variabledata + if not variabledata then + return + end + local instances=variabledata.instances + local axis=variabledata.axis + local segments=variabledata.segments + if instances and axis then + local values + if instancespec==true then + values={} + for i=1,#axis do + values[i]={ + value=axis[i].default, + } + end + else + for i=1,#instances do + local instance=instances[i] + if cleanname(instance.subfamily)==instancespec then + values=instance.values + break + end + end + end + if values then + local factors={} + for i=1,#axis do + local a=axis[i] + factors[i]=getaxisscale(segments,a.minimum,a.default,a.maximum,values[i].value) + end + return factors + end + local values=axistofactors(hash[instancespec] or instancespec) + if values then + local factors={} + for i=1,#axis do + local a=axis[i] + local d=a.default + factors[i]=getaxisscale(segments,a.minimum,d,a.maximum,values[a.name or a.tag] or d) + end + return factors + end + end +end +local function getscales(regions,factors) + local scales={} + for i=1,#regions do + local region=regions[i] + local s=1 + for j=1,#region do + local axis=region[j] + local f=factors[j] + local start=axis.start + local peak=axis.peak + local stop=axis.stop + if start>peak or peak>stop then + elseif start<0 and stop>0 and peak~=0 then + elseif peak==0 then + elseif fstop then + s=0 + break + elseif fpeak then + s=s*(stop-f)/(stop-peak) + else + end + end + scales[i]=s + end + return scales +end +helpers.getaxisscale=getaxisscale +helpers.getfactors=getfactors +helpers.getscales=getscales +helpers.axistofactors=axistofactors +local function readvariationdata(f,storeoffset,factors) + local position=getposition(f) + setposition(f,storeoffset) + local format=readushort(f) + local regionoffset=storeoffset+readulong(f) + local nofdeltadata=readushort(f) + local deltadata=readcardinaltable(f,nofdeltadata,ulong) + setposition(f,regionoffset) + local nofaxis=readushort(f) + local nofregions=readushort(f) + local regions={} + for i=1,nofregions do + local t={} + for i=1,nofaxis do + t[i]={ + start=read2dot14(f), + peak=read2dot14(f), + stop=read2dot14(f), + } + end + regions[i]=t + end + if factors then + for i=1,nofdeltadata do + setposition(f,storeoffset+deltadata[i]) + local nofdeltasets=readushort(f) + local nofshorts=readushort(f) + local nofregions=readushort(f) + local usedregions={} + local deltas={} + for i=1,nofregions do + usedregions[i]=regions[readushort(f)+1] + end + for i=1,nofdeltasets do + local t=readintegertable(f,nofshorts,short) + for i=nofshorts+1,nofregions do + t[i]=readinteger(f) + end + deltas[i]=t + end + deltadata[i]={ + regions=usedregions, + deltas=deltas, + scales=factors and getscales(usedregions,factors) or nil, + } + end + end + setposition(f,position) + return regions,deltadata +end +helpers.readvariationdata=readvariationdata +local function readcoverage(f,offset,simple) + setposition(f,offset) + local coverageformat=readushort(f) + if coverageformat==1 then + local nofcoverage=readushort(f) + if simple then + if nofcoverage==1 then + return { readushort(f) } + elseif nofcoverage==2 then + return { readushort(f),readushort(f) } + else + return readcardinaltable(f,nofcoverage,ushort) + end + elseif nofcoverage==1 then + return { [readushort(f)]=0 } + elseif nofcoverage==2 then + return { [readushort(f)]=0,[readushort(f)]=1 } + else + local coverage={} + for i=0,nofcoverage-1 do + coverage[readushort(f)]=i + end + return coverage + end + elseif coverageformat==2 then + local nofranges=readushort(f) + local coverage={} + 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 + return coverage + else + report("unknown coverage format %a ",coverageformat) + return {} + end +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 skips={ [0]=0, + 1, + 1, + 2, + 1, + 2, + 2, + 3, + 2, + 2, + 3, + 2, + 3, + 3, + 4, +} +local function readvariation(f,offset) + local p=getposition(f) + setposition(f,offset) + local outer=readushort(f) + local inner=readushort(f) + local format=readushort(f) + setposition(f,p) + if format==0x8000 then + return outer,inner + end +end +local function readposition(f,format,mainoffset,getdelta) + if format==0 then + return false + end + if format==0x04 then + local h=readshort(f) + if h==0 then + return true + else + return { 0,0,h,0 } + end + end + if format==0x05 then + local x=readshort(f) + local h=readshort(f) + if x==0 and h==0 then + return true + else + return { x,0,h,0 } + end + end + if format==0x44 then + local h=readshort(f) + if getdelta then + local d=readshort(f) + if d>0 then + local outer,inner=readvariation(f,mainoffset+d) + if outer then + h=h+getdelta(outer,inner) + end + end + else + skipshort(f,1) + end + if h==0 then + return true + else + return { 0,0,h,0 } + end + end + local x=band(format,0x1)~=0 and readshort(f) or 0 + local y=band(format,0x2)~=0 and readshort(f) or 0 + local h=band(format,0x4)~=0 and readshort(f) or 0 + local v=band(format,0x8)~=0 and readshort(f) or 0 + if format>=0x10 then + local X=band(format,0x10)~=0 and skipshort(f) or 0 + local Y=band(format,0x20)~=0 and skipshort(f) or 0 + local H=band(format,0x40)~=0 and skipshort(f) or 0 + local V=band(format,0x80)~=0 and skipshort(f) or 0 + local s=skips[extract(format,4,4)] + if s>0 then + skipshort(f,s) + end + if getdelta then + if X>0 then + local outer,inner=readvariation(f,mainoffset+X) + if outer then + x=x+getdelta(outer,inner) + end + end + if Y>0 then + local outer,inner=readvariation(f,mainoffset+Y) + if outer then + y=y+getdelta(outer,inner) + end + end + if H>0 then + local outer,inner=readvariation(f,mainoffset+H) + if outer then + h=h+getdelta(outer,inner) + end + end + if V>0 then + local outer,inner=readvariation(f,mainoffset+V) + if outer then + v=v+getdelta(outer,inner) + end + end + end + return { x,y,h,v } + elseif x==0 and y==0 and h==0 and v==0 then + return true + else + return { x,y,h,v } + end +end +local function readanchor(f,offset,getdelta) + if not offset or offset==0 then + return nil + end + setposition(f,offset) + local format=readshort(f) + local x=readshort(f) + local y=readshort(f) + if format==3 then + if getdelta then + local X=readshort(f) + local Y=readshort(f) + if X>0 then + local outer,inner=readvariation(f,offset+X) + if outer then + x=x+getdelta(outer,inner) + end + end + if Y>0 then + local outer,inner=readvariation(f,offset+Y) + if outer then + y=y+getdelta(outer,inner) + end + end + else + skipshort(f,2) + end + return { x,y } + else + return { x,y } + end +end +local function readfirst(f,offset) + if offset then + setposition(f,offset) + end + return { readushort(f) } +end +function readarray(f,offset) + if offset then + setposition(f,offset) + end + local n=readushort(f) + if n==1 then + return { readushort(f) },1 + elseif n>0 then + return readcardinaltable(f,n,ushort),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 + local lookup=readushort(f)+1 + local list=lookups[index] + if list then + list[#list+1]=lookup + else + lookups[index]={ lookup } + end + 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=readcardinaltable(f,nofreplacements,ushort) + 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=readcardinaltable(f,nofsequence,ushort) + for i=1,nofsequence do + setposition(f,tableoffset+sequences[i]) + sequences[i]=readcardinaltable(f,readushort(f),ushort) + 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=readcardinaltable(f,nofsets,ushort) + for i=1,nofsets do + local offset=lookupoffset+offset+ligatures[i] + setposition(f,offset) + local n=readushort(f) + if n==1 then + ligatures[i]={ offset+readushort(f) } + else + local l={} + for i=1,n do + l[i]=offset+readushort(f) + end + ligatures[i]=l + end + 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 { + format="reversecoverage", + rules={ + { + 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,mainoffset,getdelta) + local done={} + for i=1,#sets do + local offset=sets[i] + local reused=done[offset] + if not reused then + offset=tableoffset+offset + setposition(f,offset) + local n=readushort(f) + reused={} + for i=1,n do + reused[i]={ + readushort(f), + readposition(f,format1,offset,getdelta), + readposition(f,format2,offset,getdelta), + } + end + done[offset]=reused + end + sets[i]=reused + end + return sets +end +local function readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,mainoffset,getdelta) + local classlist1={} + for i=1,nofclasses1 do + local classlist2={} + classlist1[i]=classlist2 + for j=1,nofclasses2 do + local one=readposition(f,format1,mainoffset,getdelta) + local two=readposition(f,format2,mainoffset,getdelta) + 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) + local getdelta=fontdata.temporary.getdelta + if subtype==1 then + local coverage=readushort(f) + local format=readushort(f) + local value=readposition(f,format,tableoffset,getdelta) + local coverage=readcoverage(f,tableoffset+coverage) + for index,newindex in next,coverage do + coverage[index]=value + end + return { + format="single", + coverage=coverage, + } + elseif subtype==2 then + local coverage=readushort(f) + local format=readushort(f) + local nofvalues=readushort(f) + local values={} + for i=1,nofvalues do + values[i]=readposition(f,format,tableoffset,getdelta) + end + local coverage=readcoverage(f,tableoffset+coverage) + for index,newindex in next,coverage do + coverage[index]=values[newindex+1] + end + return { + format="single", + 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) + local getdelta=fontdata.temporary.getdelta + 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,mainoffset,getdelta) + 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 or nil } + 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,tableoffset,getdelta) + 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 or nil } + 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) + local getdelta=fontdata.temporary.getdelta + 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~=0 and (tableoffset+entry) or false, + exit~=0 and (tableoffset+exit ) or nil, + } + end + local cc=(fontdata.temporary.cursivecount or 0)+1 + fontdata.temporary.cursivecount=cc + cc="cc-"..cc + coverage=readcoverage(f,coverage) + for i=1,nofrecords do + local r=records[i] + records[i]={ + cc, + readanchor(f,r[1],getdelta) or false, + readanchor(f,r[2],getdelta) 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) + local getdelta=fontdata.temporary.getdelta + 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],getdelta) + 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],getdelta) + 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],getdelta) + 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 fontstyleid=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=readcardinaltable(f,noflookups,ushort) + feature.lookups=lookups + for j=1,noflookups do + lookups[j]=lookups[j]+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 noflookups=readushort(f) + local lookups=readcardinaltable(f,noflookups,ushort) + for lookupid=1,noflookups do + local offset=lookups[lookupid] + setposition(f,lookupoffset+offset) + 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 + subtables[j]=offset+readushort(f) + end + local markclass=band(flagbits,0x0010)~=0 + 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 f_lookupname=formatters["%s_%s_%s"] + local function resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset) + 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) + local usedlookups=false + 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 + local replacements=rule.replacements + if before then + for i=1,#before do + before[i]=tohash(before[i]) + end + rule.before=reversed(before) + end + if current then + if replacements then + local first=current[1] + local hash={} + local repl={} + for i=1,#first do + local c=first[i] + hash[c]=true + repl[c]=replacements[i] + end + rule.current={ hash } + rule.replacements=repl + else + for i=1,#current do + current[i]=tohash(current[i]) + end + end + else + end + if after then + for i=1,#after do + after[i]=tohash(after[i]) + end + end + if usedlookups then + local lookups=rule.lookups + if lookups then + for k,v in next,lookups do + if v then + for k,v in next,v do + usedlookups[v]=usedlookups[v]+1 + end + end + end + 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 + if usedlookups then + report("used %s lookups: % t",what,sortedkeys(usedlookups)) + 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 lookuplist=rlookups[index] + if lookuplist then + local length=#lookuplist + local found={} + local noffound=0 + for index=1,length do + local lookupid=lookuplist[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 + local l={ + index=nofsublookups, + name=f_lookupname(lookupprefix,"d",lookupid+lookupidoffset), + derived=true, + steps=d.steps, + nofsteps=d.nofsteps, + type=d.lookuptype or "gsub_single", + markclass=d.markclass or nil, + flags=d.flags, + } + sublookuplist[nofsublookups]=copy(l) + 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 + if h then + noffound=noffound+1 + found[noffound]=h + end + end + end + rlookups[index]=noffound>0 and found 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 loadvariations(f,fontdata,variationsoffset,lookuptypes,featurehash,featureorder) + setposition(f,variationsoffset) + local version=readulong(f) + local nofrecords=readulong(f) + local records={} + for i=1,nofrecords do + records[i]={ + conditions=readulong(f), + substitutions=readulong(f), + } + end + for i=1,nofrecords do + local record=records[i] + local offset=record.conditions + if offset==0 then + record.condition=nil + record.matchtype="always" + else + local offset=variationsoffset+offset + setposition(f,offset) + local nofconditions=readushort(f) + local conditions={} + for i=1,nofconditions do + conditions[i]=offset+readulong(f) + end + record.conditions=conditions + record.matchtype="condition" + end + end + for i=1,nofrecords do + local record=records[i] + if record.matchtype=="condition" then + local conditions=record.conditions + for i=1,#conditions do + setposition(f,conditions[i]) + conditions[i]={ + format=readushort(f), + axis=readushort(f), + minvalue=read2dot14(f), + maxvalue=read2dot14(f), + } + end + end + end + for i=1,nofrecords do + local record=records[i] + local offset=record.substitutions + if offset==0 then + record.substitutions={} + else + setposition(f,variationsoffset+offset) + local version=readulong(f) + local nofsubstitutions=readushort(f) + local substitutions={} + for i=1,nofsubstitutions do + substitutions[readushort(f)]=readulong(f) + end + for index,alternates in sortedhash(substitutions) do + if index==0 then + record.substitutions=false + else + local tableoffset=variationsoffset+offset+alternates + setposition(f,tableoffset) + local parameters=readulong(f) + local noflookups=readushort(f) + local lookups=readcardinaltable(f,noflookups,ushort) + record.substitutions=lookups + end + end + end + end + setvariabledata(fontdata,"features",records) + end + local function readscripts(f,fontdata,what,lookuptypes,lookuphandlers,lookupstoo) + local tableoffset=gotodatatable(f,fontdata,what,true) + if tableoffset then + local version=readulong(f) + local scriptoffset=tableoffset+readushort(f) + local featureoffset=tableoffset+readushort(f) + local lookupoffset=tableoffset+readushort(f) + local variationsoffset=version>0x00010000 and (tableoffset+readulong(f)) or 0 + 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,tableoffset) + end + if variationsoffset>0 then + loadvariations(f,fontdata,variationsoffset,lookuptypes,featurehash,featureorder) + end + 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 + setposition(f,datatable.offset) + local version=readushort(f) + local noftables=readushort(f) + if noftables>1 then + report("adding global kern table as gpos feature %a",name) + local kerns=setmetatableindex("table") + for i=1,noftables do + local version=readushort(f) + local length=readushort(f) + local coverage=readushort(f) + local format=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 }, + } + else + report("ignoring empty kern table of feature %a",name) + end + 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 not specification.glyphs then + return + end + local datatable=fontdata.tables.gdef + if datatable then + local tableoffset=datatable.offset + setposition(f,tableoffset) + local version=readulong(f) + local classoffset=readushort(f) + local attachmentoffset=readushort(f) + local ligaturecarets=readushort(f) + local markclassoffset=readushort(f) + local marksetsoffset=version>=0x00010002 and readushort(f) or 0 + local varsetsoffset=version>=0x00010003 and readulong(f) or 0 + local glyphs=fontdata.glyphs + local marks={} + local markclasses=setmetatableindex("table") + local marksets=setmetatableindex("table") + fontdata.marks=marks + fontdata.markclasses=markclasses + fontdata.marksets=marksets + if classoffset~=0 then + setposition(f,tableoffset+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 + end + if markclassoffset~=0 then + setposition(f,tableoffset+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 + end + if marksetsoffset~=0 then + marksetsoffset=tableoffset+marksetsoffset + setposition(f,marksetsoffset) + local format=readushort(f) + if format==1 then + local nofsets=readushort(f) + local sets=readcardinaltable(f,nofsets,ulong) + for i=1,nofsets do + local offset=sets[i] + if offset~=0 then + marksets[i]=readcoverage(f,marksetsoffset+offset) + end + end + end + end + local factors=specification.factors + if (specification.variable or factors) and varsetsoffset~=0 then + local regions,deltas=readvariationdata(f,tableoffset+varsetsoffset,factors) + if factors then + fontdata.temporary.getdelta=function(outer,inner) + local delta=deltas[outer+1] + if delta then + local d=delta.deltas[inner+1] + if d then + local scales=delta.scales + local dd=0 + for i=1,#scales do + local di=d[i] + if di then + dd=dd+scales[i]*di + else + break + end + end + return round(dd) + end + end + return 0 + 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=readcardinaltable(f,vnofglyphs,ushort) + local hconstruction=readcardinaltable(f,hnofglyphs,ushort) + 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 band(flags,0x0001)~=0 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) + local tableoffset=gotodatatable(f,fontdata,"math",specification.glyphs) + if tableoffset then + local version=readulong(f) + 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 +function readers.colr(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"colr",specification.glyphs) + if tableoffset then + 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 +function readers.cpal(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"cpal",specification.glyphs) + if tableoffset then + local version=readushort(f) + local nofpaletteentries=readushort(f) + local nofpalettes=readushort(f) + local nofcolorrecords=readushort(f) + local firstcoloroffset=readulong(f) + local colorrecords={} + local palettes=readcardinaltable(f,nofpalettes,ushort) + 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 +function readers.svg(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"svg",specification.glyphs) + if tableoffset then + local version=readushort(f) + 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 +function readers.sbix(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"sbix",specification.glyphs) + if tableoffset then + local version=readushort(f) + local flags=readushort(f) + local nofstrikes=readulong(f) + local strikes={} + local nofglyphs=fontdata.nofglyphs + for i=1,nofstrikes do + strikes[i]=readulong(f) + end + local shapes={} + local done=0 + for i=1,nofstrikes do + local strikeoffset=strikes[i]+tableoffset + setposition(f,strikeoffset) + strikes[i]={ + ppem=readushort(f), + ppi=readushort(f), + offset=strikeoffset + } + end + sort(strikes,function(a,b) + if b.ppem==a.ppem then + return b.ppi0 then + setposition(f,strikeoffset+glyphoffset) + shapes[i]={ + x=readshort(f), + y=readshort(f), + tag=readtag(f), + data=readstring(f,datasize-8), + ppem=strikeppem, + ppi=strikeppi, + } + done=done+1 + if done==nofglyphs then + break + end + end + end + glyphoffset=nextoffset + end + end + fontdata.sbixshapes=shapes + end +end +function readers.stat(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"stat",true) + if tableoffset then + local extras=fontdata.extras + local version=readulong(f) + local axissize=readushort(f) + local nofaxis=readushort(f) + local axisoffset=readulong(f) + local nofvalues=readushort(f) + local valuesoffset=readulong(f) + local fallbackname=extras[readushort(f)] + local axis={} + local values={} + setposition(f,tableoffset+axisoffset) + for i=1,nofaxis do + local tag=readtag(f) + axis[i]={ + tag=tag, + name=lower(extras[readushort(f)] or tag), + ordering=readushort(f), + variants={} + } + end + setposition(f,tableoffset+valuesoffset) + for i=1,nofvalues do + values[i]=readushort(f) + end + for i=1,nofvalues do + setposition(f,tableoffset+valuesoffset+values[i]) + local format=readushort(f) + local index=readushort(f)+1 + local flags=readushort(f) + local name=lower(extras[readushort(f)] or "no name") + local value=readfixed(f) + local variant + if format==1 then + variant={ + flags=flags, + name=name, + value=value, + } + elseif format==2 then + variant={ + flags=flags, + name=name, + value=value, + minimum=readfixed(f), + maximum=readfixed(f), + } + elseif format==3 then + variant={ + flags=flags, + name=name, + value=value, + link=readfixed(f), + } + end + insert(axis[index].variants,variant) + end + sort(axis,function(a,b) + return a.ordering=lastto then + else + values[#values+1]={ f,t } + lastfrom,lastto=f,t + end + end + nofvalues=#values + if nofvalues>2 then + local some=values[1] + if some[1]==-1 and some[2]==-1 then + some=values[nofvalues] + if some[1]==1 and some[2]==1 then + for i=2,nofvalues-1 do + some=values[i] + if some[1]==0 and some[2]==0 then + return values + end + end + end + end + end + return false + end + local majorversion=readushort(f) + local minorversion=readushort(f) + local reserved=readushort(f) + local nofaxis=readushort(f) + local segments={} + for i=1,nofaxis do + segments[i]=collect() + end + setvariabledata(fontdata,"segments",segments) + end +end +function readers.fvar(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"fvar",true) + if tableoffset then + local version=readulong(f) + local offsettoaxis=tableoffset+readushort(f) + local reserved=skipshort(f) + local nofaxis=readushort(f) + local sizeofaxis=readushort(f) + local nofinstances=readushort(f) + local sizeofinstances=readushort(f) + local extras=fontdata.extras + local axis={} + local instances={} + setposition(f,offsettoaxis) + for i=1,nofaxis do + axis[i]={ + tag=readtag(f), + minimum=readfixed(f), + default=readfixed(f), + maximum=readfixed(f), + flags=readushort(f), + name=lower(extras[readushort(f)] or "bad name"), + } + local n=sizeofaxis-20 + if n>0 then + skipbytes(f,n) + elseif n<0 then + end + end + local nofbytes=2+2+2+nofaxis*4 + local readpsname=nofbytes<=sizeofinstances + local skippable=sizeofinstances-nofbytes + for i=1,nofinstances do + local subfamid=readushort(f) + local flags=readushort(f) + local values={} + for i=1,nofaxis do + values[i]={ + axis=axis[i].tag, + value=readfixed(f), + } + end + local psnameid=readpsname and readushort(f) or 0xFFFF + if subfamid==2 or subfamid==17 then + elseif subfamid==0xFFFF then + subfamid=nil + elseif subfamid<=256 or subfamid>=32768 then + subfamid=nil + end + if psnameid==6 then + elseif psnameid==0xFFFF then + psnameid=nil + elseif psnameid<=256 or psnameid>=32768 then + psnameid=nil + end + instances[i]={ + subfamily=extras[subfamid], + psname=psnameid and extras[psnameid] or nil, + values=values, + } + if skippable>0 then + skipbytes(f,skippable) + end + end + setvariabledata(fontdata,"axis",axis) + setvariabledata(fontdata,"instances",instances) + end +end +function readers.hvar(f,fontdata,specification) + local factors=specification.factors + if not factors then + return + end + local tableoffset=gotodatatable(f,fontdata,"hvar",specification.variable) + if not tableoffset then + return + end + local version=readulong(f) + local variationoffset=tableoffset+readulong(f) + local advanceoffset=tableoffset+readulong(f) + local lsboffset=tableoffset+readulong(f) + local rsboffset=tableoffset+readulong(f) + local regions={} + local variations={} + local innerindex={} + local outerindex={} + if variationoffset>0 then + regions,deltas=readvariationdata(f,variationoffset,factors) + end + if not regions then + return + end + if advanceoffset>0 then + setposition(f,advanceoffset) + local format=readushort(f) + local mapcount=readushort(f) + local entrysize=rshift(band(format,0x0030),4)+1 + local nofinnerbits=band(format,0x000F)+1 + local innermask=lshift(1,nofinnerbits)-1 + local readcardinal=read_cardinal[entrysize] + for i=0,mapcount-1 do + local mapdata=readcardinal(f) + outerindex[i]=rshift(mapdata,nofinnerbits) + innerindex[i]=band(mapdata,innermask) + end + setvariabledata(fontdata,"hvarwidths",true) + local glyphs=fontdata.glyphs + for i=0,fontdata.nofglyphs-1 do + local glyph=glyphs[i] + local width=glyph.width + if width then + local outer=outerindex[i] or 0 + local inner=innerindex[i] or i + if outer and inner then + local delta=deltas[outer+1] + if delta then + local d=delta.deltas[inner+1] + if d then + local scales=delta.scales + local deltaw=0 + for i=1,#scales do + local di=d[i] + if di then + deltaw=deltaw+scales[i]*di + else + break + end + end + glyph.width=width+round(deltaw) + end + end + end + end + end + end +end +function readers.vvar(f,fontdata,specification) + if not specification.variable then + return + end +end +function readers.mvar(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"mvar",specification.variable) + if tableoffset then + local version=readulong(f) + local reserved=skipshort(f,1) + local recordsize=readushort(f) + local nofrecords=readushort(f) + local offsettostore=tableoffset+readushort(f) + local dimensions={} + local factors=specification.factors + if factors then + local regions,deltas=readvariationdata(f,offsettostore,factors) + for i=1,nofrecords do + local tag=readtag(f) + local var=variabletags[tag] + if var then + local outer=readushort(f) + local inner=readushort(f) + local delta=deltas[outer+1] + if delta then + local d=delta.deltas[inner+1] + if d then + local scales=delta.scales + local dd=0 + for i=1,#scales do + dd=dd+scales[i]*d[i] + end + var(fontdata,round(dd)) + end + end + else + skipshort(f,2) + end + if recordsize>8 then + skipbytes(recordsize-8) + end + end + end + end +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-dsp”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-oup” 020f2630f5a18d39e209f1cbf7a334ea] --- + +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 setmetatableindex=table.setmetatableindex +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=true +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 n0 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",sortedkeys(done)) + end + end +end +local function unifymissing(fontdata) + if not fonts.mappings then + require("font-map") + require("font-agl") + end + local unicodes={} + local resources=fontdata.resources + resources.unicodes=unicodes + for unicode,d in next,fontdata.descriptions do + if unicode=private or (unicode>=0xE000 and unicode<=0xF8FF) or unicode==0xFFFE or unicode==0xFFFF 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 c=steps[i].coverage + if c then + for k,v in next,c 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="unicodes" + 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="unicodes" + fontdata.names=nil + else + readers.rehash(fontdata,"unicodes") + 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_normal_cc(v) + local tag=tabstr_normal(v) + local ht=h[tag] + if ht then + c[ht]=c[ht]+1 + return ht + else + v[1]=0 + 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_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_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_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 pack_final_cc(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,pack_normal_cc + else + return pack_final,pack_final,pack_final,pack_final,pack_final,pack_final_cc + end + end + local resources=data.resources + local sequences=resources.sequences + local sublookups=resources.sublookups + local features=resources.features + local palettes=resources.colorpalettes + local variable=resources.variabledata + 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,pack_normal_cc=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=="pair" then + for g1,d1 in next,c do + for g2,d2 in next,d1 do + local f=d2[1] if f and f~=true then d2[1]=pack_indexed(f) end + local s=d2[2] if s and s~=true then d2[2]=pack_indexed(s) end + end + end + else + for g1,d1 in next,c do + c[g1]=pack_normal(d1) + end + end + end + elseif kind=="gpos_single" then + local c=step.coverage + if c then + if step.format=="single" then + for g1,d1 in next,c do + if d1 and d1~=true then + c[g1]=pack_indexed(d1) + end + end + else + step.coverage=pack_normal(c) + 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.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 variable then + local instances=variable.instances + if instances then + for i=1,#instances do + local v=instances[i].values + for j=1,#v do + v[j]=pack_normal(v[j]) + end + end + end + local function packdeltas(main) + if main then + local deltas=main.deltas + if deltas then + for i=1,#deltas do + local di=deltas[i] + local d=di.deltas + for j=1,#d do + d[j]=pack_indexed(d[j]) + end + di.regions=pack_indexed(di.regions) + end + end + local regions=main.regions + if regions then + for i=1,#regions do + local r=regions[i] + for j=1,#r do + r[j]=pack_normal(r[j]) + end + end + end + end + end + packdeltas(variable.global) + packdeltas(variable.horizontal) + packdeltas(variable.vertical) + packdeltas(variable.metrics) + 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,pack_normal_cc=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=="pair" then + for g1,d1 in next,c do + for g2,d2 in next,d1 do + d1[g2]=pack_normal(d2) + end + 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 + d1[g2]=pack_normal(d2) + 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 variable then + local function unpackdeltas(main) + if main then + local regions=main.regions + if regions then + main.regions=pack_normal(regions) + end + end + end + unpackdeltas(variable.global) + unpackdeltas(variable.horizontal) + unpackdeltas(variable.vertical) + unpackdeltas(variable.metrics) + 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,pack_normal_cc=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=="pair" then + for g1,d1 in next,c do + c[g1]=pack_normal(d1) + end + end + end + elseif kind=="gpos_cursive" then + local c=step.coverage + if c then + for g1,d1 in next,c do + c[g1]=pack_normal_cc(d1) + 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 variable=resources.variabledata + 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 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 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=="pair" then + 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 + 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_single" then + local c=step.coverage + if c then + if step.format=="single" then + for g1,d1 in next,c do + local tv=tables[d1] + if tv then + c[g1]=tv + end + end + else + local tv=tables[c] + if tv then + step.coverage=tv + end + end + end + elseif kind=="gpos_cursive" then + local c=step.coverage + if c then + for g1,d1 in next,c do + local tv=tables[d1] + if tv then + d1=tv + c[g1]=d1 + end + 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 + local tv=tables[d2] + if tv then + d2=tv + d1[g2]=d2 + end + 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 replacements=rule.replacements + if replacements then + local tv=tables[replacements] + if tv then + rule.replacements=tv + end + end + end + 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 + if variable then + local instances=variable.instances + if instances then + for i=1,#instances do + local v=instances[i].values + for j=1,#v do + local tv=tables[v[j]] + if tv then + v[j]=tv + end + end + end + end + local function unpackdeltas(main) + if main then + local deltas=main.deltas + if deltas then + for i=1,#deltas do + local di=deltas[i] + local d=di.deltas + local r=di.regions + for j=1,#d do + local tv=tables[d[j]] + if tv then + d[j]=tv + end + end + local tv=di.regions + if tv then + di.regions=tv + end + end + end + local regions=main.regions + if regions then + local tv=tables[regions] + if tv then + main.regions=tv + regions=tv + end + for i=1,#regions do + local r=regions[i] + for j=1,#r do + local tv=tables[r[j]] + if tv then + r[j]=tv + end + end + end + end + end + end + unpackdeltas(variable.global) + unpackdeltas(variable.horizontal) + unpackdeltas(variable.vertical) + unpackdeltas(variable.metrics) + 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 + local c=steps[i].coverage + if c then + for k,v in next,c do + if not target[k] then + target[k]=v + end + end + end + end + lookup.nofsteps=1 + lookup.merged=true + lookup.steps={ first } + return nofsteps-1 +end +local function mergesteps_2(lookup) + 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 + local c=steps[i].coverage + if c then + for k,v in next,c do + local tk=target[k] + if tk then + for kk,vv in next,v do + if tk[kk]==nil then + tk[kk]=vv + end + end + else + target[k]=v + end + end + end + end + lookup.nofsteps=1 + lookup.merged=true + lookup.steps={ first } + return nofsteps-1 +end +local function mergesteps_3(lookup,strict) + local steps=lookup.steps + local nofsteps=lookup.nofsteps + report("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name) + local coverage={} + for i=1,nofsteps do + local c=steps[i].coverage + if c then + for k,v in next,c do + local tk=coverage[k] + if tk then + report("quitting merge due to multiple checks") + return nofsteps + else + coverage[k]=v + end + end + end + end + local first=steps[1] + local baseclasses={} + 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 + v[1]=offset+v[1] + end + end + first.baseclasses=baseclasses + first.coverage=coverage + lookup.nofsteps=1 + lookup.merged=true + 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 + local c=steps[i].coverage + if c then + for k,v in next,c do + local tk=target[k] + if tk then + nested(v,tk) + else + target[k]=v + end + end + end + end + lookup.nofsteps=1 + lookup.steps={ first } + return nofsteps-1 +end +local function mergesteps_5(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 + local hash=nil + for k,v in next,target do + hash=v[1] + break + end + for i=2,nofsteps do + local c=steps[i].coverage + if c then + for k,v in next,c do + local tk=target[k] + if tk then + if not tk[2] then + tk[2]=v[2] + end + if not tk[3] then + tk[3]=v[3] + end + else + target[k]=v + v[1]=hash + end + end + end + end + lookup.nofsteps=1 + lookup.merged=true + lookup.steps={ first } + return nofsteps-1 +end +local function checkkerns(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 + if d1==true then + elseif not d1 then + elseif 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) + local c={} + for g1,d1 in next,coverage do + if d1 and d1~=true then + c[g1]=d1[3] + end + end + step.coverage=c + step.format="move" + kerned=kerned+1 + end + end + end + return kerned +end +local function checkpairs(lookup) + local steps=lookup.steps + local nofsteps=lookup.nofsteps + local kerned=0 + local function onlykerns(step) + local coverage=step.coverage + for g1,d1 in next,coverage do + for g2,d2 in next,d1 do + if d2[2] then + return false + else + local v=d2[1] + if v==true then + elseif v and (v[1]~=0 or v[2]~=0 or v[4]~=0) then + return false + end + end + end + end + return coverage + end + for i=1,nofsteps do + local step=steps[i] + if step.format=="pair" then + local coverage=onlykerns(step) + if coverage 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 + local d={} + for g2,d2 in next,d1 do + local v=d2[1] + if v==true then + elseif v then + d[g2]=v[3] + end + end + coverage[g1]=d + end + step.format="move" + kerned=kerned+1 + end + end + end + return kerned +end +local compact_pairs=true +local compact_singles=true +local merge_pairs=true +local merge_singles=true +local merge_substitutions=true +local merge_alternates=true +local merge_multiples=true +local merge_ligatures=true +local merge_cursives=true +local merge_marks=true +directives.register("otf.compact.pairs",function(v) compact_pairs=v end) +directives.register("otf.compact.singles",function(v) compact_singles=v end) +directives.register("otf.merge.pairs",function(v) merge_pairs=v end) +directives.register("otf.merge.singles",function(v) merge_singles=v end) +directives.register("otf.merge.substitutions",function(v) merge_substitutions=v end) +directives.register("otf.merge.alternates",function(v) merge_alternates=v end) +directives.register("otf.merge.multiples",function(v) merge_multiples=v end) +directives.register("otf.merge.ligatures",function(v) merge_ligatures=v end) +directives.register("otf.merge.cursives",function(v) merge_cursives=v end) +directives.register("otf.merge.marks",function(v) merge_marks=v 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 + local kind=lookup.type + allsteps=allsteps+nofsteps + if nofsteps>1 then + local merg=merged + if kind=="gsub_single" then + if merge_substitutions then + merged=merged+mergesteps_1(lookup) + end + elseif kind=="gsub_alternate" then + if merge_alternates then + merged=merged+mergesteps_1(lookup) + end + elseif kind=="gsub_multiple" then + if merge_multiples then + merged=merged+mergesteps_1(lookup) + end + elseif kind=="gsub_ligature" then + if merge_ligatures then + merged=merged+mergesteps_4(lookup) + end + elseif kind=="gpos_single" then + if merge_singles then + merged=merged+mergesteps_1(lookup,true) + end + if compact_singles then + kerned=kerned+checkkerns(lookup) + end + elseif kind=="gpos_pair" then + if merge_pairs then + merged=merged+mergesteps_2(lookup) + end + if compact_pairs then + kerned=kerned+checkpairs(lookup) + end + elseif kind=="gpos_cursive" then + if merge_cursives then + merged=merged+mergesteps_5(lookup) + end + elseif kind=="gpos_mark2mark" or kind=="gpos_mark2base" or kind=="gpos_mark2ligature" then + if merge_marks then + merged=merged+mergesteps_3(lookup) + end + end + if merg~=merged then + lookup.merged=true + end + elseif nofsteps==1 then + local kern=kerned + if kind=="gpos_single" then + if compact_singles then + kerned=kerned+checkkerns(lookup) + end + elseif kind=="gpos_pair" then + if compact_pairs then + kerned=kerned+checkpairs(lookup) + end + end + if kern~=kerned then + 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 +local function mergesteps(t,k) + if k=="merged" then + local merged={} + for i=1,#t do + local step=t[i] + local coverage=step.coverage + for k in next,coverage do + local m=merged[k] + if m then + m[2]=i + else + merged[k]={ i,i } + end + end + end + t.merged=merged + return merged + end +end +local function checkmerge(sequence) + local steps=sequence.steps + if steps then + setmetatableindex(steps,mergesteps) + end +end +local function checkflags(sequence,resources) + if not sequence.skiphash then + local flags=sequence.flags + if flags then + local skipmark=flags[1] + local skipligature=flags[2] + local skipbase=flags[3] + local markclass=sequence.markclass + local skipsome=skipmark or skipligature or skipbase or markclass or false + if skipsome then + sequence.skiphash=setmetatableindex(function(t,k) + local c=resources.classes[k] + local v=c==skipmark + or (markclass and c=="mark" and not markclass[k]) + or c==skipligature + or c==skipbase + or false + t[k]=v + return v + end) + else + sequence.skiphash=false + end + else + sequence.skiphash=false + end + end +end +local function checksteps(sequence) + local steps=sequence.steps + if steps then + for i=1,#steps do + steps[i].index=i + end + end +end +if fonts.helpers then + fonts.helpers.checkmerge=checkmerge + fonts.helpers.checkflags=checkflags + fonts.helpers.checksteps=checksteps +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 "",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 nofsteps=sequence.nofsteps + 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,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={ n=0 } + local rulesize=0 + local coverage={} + local lookuptype=sequence.type + local nofrules=#rules + step.coverage=coverage + for currentrule=1,nofrules do + local rule=rules[currentrule] + 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 i=1,#lookups do + local lookups=lookups[i] + 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 + end + end + if sequence[1] then + sequence.n=#sequence + local ruledata={ + currentrule, + lookuptype, + sequence, + start, + stop, + lookups, + replacements, + subtype, + } + rulesize=rulesize+1 + rulehash[rulesize]=ruledata + rulehash.n=rulesize + if true then + for unic in next,sequence[start] do + local cu=coverage[unic] + if cu then + local n=#cu+1 + cu[n]=ruledata + cu.n=n + else + coverage[unic]={ ruledata,n=1 } + end + end + else + for unic in next,sequence[start] do + local cu=coverage[unic] + if cu then + else + coverage[unic]=rulehash + end + end + end + end + end + end + end + checkmerge(sequence) + checkflags(sequence,resources) + checksteps(sequence) + end + end + end + end + expandlookups(sequences) + expandlookups(sublookups) +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-oup”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-otl” fcc55ae6cff5fd55b5054f923c21cafa] --- + +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 lower=string.lower +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.104 +otf.cache=containers.define("fonts","otl",otf.version,true) +otf.svgcache=containers.define("fonts","svg",otf.version,true) +otf.sbixcache=containers.define("fonts","sbix",otf.version,true) +otf.pdfcache=containers.define("fonts","pdf",otf.version,true) +otf.svgenabled=false +otf.sbixenabled=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 privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000 +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) +local checkmemory=utilities.lua and utilities.lua.checkmemory +local threshold=100 +local tracememory=false +registertracker("fonts.otf.loader.memory",function(v) tracememory=v end) +if not checkmemory then + local collectgarbage=collectgarbage + checkmemory=function(previous,threshold) + local current=collectgarbage("count") + if previous then + local checked=(threshold or 64)*1024 + if current-previous>checked then + collectgarbage("collect") + current=collectgarbage("count") + end + end + return current + end +end +function otf.load(filename,sub,instance) + 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 sub=="" then + sub=false + end + local hash=name + if sub then + hash=hash.."-"..sub + end + if instance then + hash=hash.."-"..instance + end + hash=containers.cleanname(hash) + 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,instance) + if data then + local used=checkmemory() + local resources=data.resources + local svgshapes=resources.svgshapes + local sbixshapes=resources.sbixshapes + if cleanup==0 then + checkmemory(used,threshold,tracememory) + end + 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 + if cleanup>1 then + collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) + end + end + if sbixshapes then + resources.sbixshapes=nil + if otf.sbixenabled then + local timestamp=os.date() + containers.write(otf.sbixcache,hash,{ + sbixshapes=sbixshapes, + timestamp=timestamp, + }) + data.properties.sbix={ + hash=hash, + timestamp=timestamp, + } + end + if cleanup>1 then + collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) + end + end + otfreaders.compact(data) + if cleanup==0 then + checkmemory(used,threshold,tracememory) + end + otfreaders.rehash(data,"unicodes") + otfreaders.addunicodetable(data) + otfreaders.extend(data) + if cleanup==0 then + checkmemory(used,threshold,tracememory) + end + 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") + else + checkmemory(used,threshold,tracememory) + end + stoptiming(otfreaders) + if elapsedtime then + report_otf("loading, optimizing, packing and caching time %s",elapsedtime(otfreaders)) + end + if cleanup>3 then + collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) + end + data=containers.read(otf.cache,hash) + if cleanup>2 then + collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) + 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 + local classes=data.resources.classes + if not classes then + local descriptions=data.descriptions + classes=setmetatableindex(function(t,k) + local d=descriptions[k] + local v=(d and d.class or "base") or false + t[k]=v + return v + end) + data.resources.classes=classes + end + 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 subfont=metadata.subfontindex + 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 + properties.subfont=subfont + properties.private=properties.private or data.private or privateoffset + 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 instance=specification.instance or (features and features.axis) + local rawdata=otf.load(filename,sub,instance) + 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, + single=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-2018-09-24.lua scope for “font-otl”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-oto” d78a6165b7ff6c21f4c3c4c3c0553648] --- + +if not modules then modules={} end modules ['font-oto']={ + 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) +local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end) +local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end) +local report_prepare=logs.reporter("fonts","otf prepare") +local fonts=fonts +local otf=fonts.handlers.otf +local otffeatures=otf.features +local registerotffeature=otffeatures.register +otf.defaultbasealternate="none" +local getprivate=fonts.constructors.getprivate +local wildcard="*" +local default="dflt" +local formatters=string.formatters +local f_unicode=formatters["%U"] +local f_uniname=formatters["%U (%s)"] +local f_unilist=formatters["% t (% t)"] +local function gref(descriptions,n) + if type(n)=="number" then + local name=descriptions[n].name + if name then + return f_uniname(n,name) + else + return f_unicode(n) + end + elseif n then + local num,nam,j={},{},0 + for i=1,#n do + local ni=n[i] + if tonumber(ni) then + j=j+1 + local di=descriptions[ni] + num[j]=f_unicode(ni) + nam[j]=di and di.name or "-" + end + end + return f_unilist(num,nam) + else + return "" + 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 +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 + applied={} +end +local function registerbasefeature(feature,value) + applied[#applied+1]=feature.."="..tostring(value) +end +local function makefake(tfmdata,name,present) + local private=getprivate(tfmdata) + local character={ intermediate=true,ligatures={} } + resources.unicodes[name]=private + tfmdata.characters[private]=character + tfmdata.descriptions[private]={ name=name } + 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 + 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,"" } + 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 + 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] + local format=step.format + if format=="kern" or format=="move" 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 + local other=kern[2] + if other==true or (not other and not (kerns and kerns[otherunicode])) then + local kern=kern[1] + if kern==true then + elseif kern[1]~=0 or kern[2]~=0 or kern[4]~=0 then + 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) +end +local function checkmathreplacements(tfmdata,fullname,fixitalics) + 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 fixitalics then + local ui=u.italic + if ui and not r.italic then + if trace_preparing then + report_prepare("using %i units of italic correction from %C for %U",ui,unicode,replacement) + end + r.italic=ui + end + end + 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 + 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 + 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,features.fixitalics) + end + registerbasehash(tfmdata) + end + if trace_preparing then + report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,fullname) + end + end +end +registerotffeature { + name="features", + description="features", + default=true, + initializers={ + base=featuresinitializer, + } +} +otf.basemodeinitializer=featuresinitializer + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-oto”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-otj” 578448ae37a167319dbccd5af04738da] --- + +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,tonumber=next,rawget,tonumber +local fastcopy=table.fastcopy +local registertracker=trackers.register +local registerdirective=directives.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 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 +local fontmarks=hashes.marks +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 tonode=nuts.tonode +local tonut=nuts.tonut +local setfield=nuts.setfield +local getnext=nuts.getnext +local getprev=nuts.getprev +local getid=nuts.getid +local getfont=nuts.getfont +local getchar=nuts.getchar +local getoffsets=nuts.getoffsets +local getboth=nuts.getboth +local getdisc=nuts.getdisc +local setdisc=nuts.setdisc +local setoffsets=nuts.setoffsets +local ischar=nuts.is_char +local getkern=nuts.getkern +local setkern=nuts.setkern +local setlink=nuts.setlink +local setwidth=nuts.setwidth +local getwidth=nuts.getwidth +local nextchar=nuts.traversers.char +local nextglue=nuts.traversers.glue +local insert_node_before=nuts.insert_before +local insert_node_after=nuts.insert_after +local properties=nodes.properties.data +local fontkern=nuts.pool and nuts.pool.fontkern +local italickern=nuts.pool and nuts.pool.italickern +local useitalickerns=false +directives.register("fonts.injections.useitalics",function(v) + if v then + report_injections("using italics for space kerns (tracing only)") + end + useitalickerns=v +end) +do if not fontkern then + local thekern=nuts.new("kern",0) + local setkern=nuts.setkern + local copy_node=nuts.copy_node + fontkern=function(k) + local n=copy_node(thekern) + setkern(n,k) + return n + end +end end +do if not italickern then + local thekern=nuts.new("kern",3) + local setkern=nuts.setkern + local copy_node=nuts.copy_node + italickern=function(k) + local n=copy_node(thekern) + setkern(n,k) + return n + end +end end +function injections.installnewkern() end +local nofregisteredkerns=0 +local nofregisteredpositions=0 +local nofregisteredmarks=0 +local nofregisteredcursives=0 +local keepregisteredcounts=false +function injections.keepcounts() + keepregisteredcounts=true +end +function injections.resetcounts() + nofregisteredkerns=0 + nofregisteredpositions=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 + properties[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,r2lflag) + 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.setposition(kind,current,factor,rlmode,spec,injection) + local x=factor*(spec[1] or 0) + local y=factor*(spec[2] or 0) + local w=factor*(spec[3] or 0) + local h=factor*(spec[4] or 0) + 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 + nofregisteredpositions=nofregisteredpositions+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=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,nofregisteredpositions + 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=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.setmove(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 rlmode and rlmode<0 then + if p then + local i=p[injection] + if i then + i.rightkern=dx+(i.rightkern or 0) + else + p[injection]={ + rightkern=dx, + } + end + else + properties[current]={ + [injection]={ + rightkern=dx, + }, + } + end + else + if p then + local i=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 + 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 + if dx~=0 then + i.markx=dx + end + if y~=0 then + i.marky=dy + end + if rlmode then + i.markdir=rlmode + end + 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=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 nextchar,n do + showchar(n,where) + show(n,what,where," ") + end + report_injections("end subrun") +end +local function trace(head,where) + report_injections() + report_injections("begin run %s: %s kerns, %s positions, %s marks and %s cursives registered", + where or "",nofregisteredkerns,nofregisteredpositions,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 + local w=getwidth(current) + local x,y=getoffsets(current) + report_injections("char: %C, width %p, xoffset %p, yoffset %p",getchar(current),w,x,y) + skipping=false + elseif id==kern_code then + report_injections("kern: %p",getkern(current)) + skipping=false + elseif not skipping then + report_injections() + skipping=true + end + current=getnext(current) + end + report_injections() +end +local function inject_kerns_only(head,where) + if trace_injections then + trace(head,"kerns") + end + local current=head + local prev=nil + local next=nil + local prevdisc=nil + local pre=nil + local post=nil + local replace=nil + local pretail=nil + local posttail=nil + local replacetail=nil + while current do + local next=getnext(current) + local char,id=ischar(current) + if char 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 + head=insert_node_before(head,current,fontkern(leftkern)) + 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 + setlink(posttail,fontkern(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 + setlink(replacetail,fontkern(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",fontkern(leftkern)) + end + end + end + if done then + setdisc(prevdisc,pre,post,replace) + end + end + end + prevdisc=nil + elseif char==false then + prevdisc=nil + elseif id==disc_code then + pre,post,replace,pretail,posttail,replacetail=getdisc(current,true) + local done=false + if pre then + for n in nextchar,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 + pre=insert_node_before(pre,n,fontkern(leftkern)) + done=true + end + end + end + end + end + if post then + for n in nextchar,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 + post=insert_node_before(post,n,fontkern(leftkern)) + done=true + end + end + end + end + end + if replace then + for n in nextchar,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 + replace=insert_node_before(replace,n,fontkern(leftkern)) + done=true + end + end + end + end + end + if done then + setdisc(current,pre,post,replace) + end + prevdisc=current + else + prevdisc=nil + end + prev=current + current=next + end + if keepregisteredcounts then + keepregisteredcounts=false + else + nofregisteredkerns=0 + end + if trace_injections then + show_result(head) + end + return head +end +local function inject_positions_only(head,where) + if trace_injections then + trace(head,"positions") + 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 next=getnext(current) + local char,id=ischar(current) + if char 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 + setoffsets(current,false,yoffset) + end + local leftkern=i.leftkern + local rightkern=i.rightkern + if leftkern and leftkern~=0 then + if rightkern and leftkern==-rightkern then + setoffsets(current,leftkern,false) + rightkern=0 + else + head=insert_node_before(head,current,fontkern(leftkern)) + end + end + if rightkern and rightkern~=0 then + insert_node_after(head,current,fontkern(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",fontkern(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 + setlink(posttail,fontkern(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 + setlink(replacetail,fontkern(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",fontkern(leftkern)) + end + end + end + if done then + setdisc(prevdisc,pre,post,replace) + end + end + end + prevdisc=nil + prevglyph=current + elseif char==false then + 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 nextchar,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 + setoffsets(n,false,yoffset) + end + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + pre=insert_node_before(pre,n,fontkern(leftkern)) + done=true + end + local rightkern=i.rightkern + if rightkern and rightkern~=0 then + insert_node_after(pre,n,fontkern(rightkern)) + done=true + end + end + end + end + end + if post then + for n in nextchar,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 + setoffsets(n,false,yoffset) + end + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + post=insert_node_before(post,n,fontkern(leftkern)) + done=true + end + local rightkern=i.rightkern + if rightkern and rightkern~=0 then + insert_node_after(post,n,fontkern(rightkern)) + done=true + end + end + end + end + end + if replace then + for n in nextchar,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 + setoffsets(n,false,yoffset) + end + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + replace=insert_node_before(replace,n,fontkern(leftkern)) + done=true + end + local rightkern=i.rightkern + if rightkern and rightkern~=0 then + insert_node_after(replace,n,fontkern(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,fontkern(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,fontkern(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 + nofregisteredpositions=0 + end + if trace_injections then + show_result(head) + end + return head +end +local function showoffset(n,flag) + local x,y=getoffsets(n) + if x~=0 or y~=0 then + setcolor(n,"darkgray") + end +end +local function inject_everything(head,where) + if trace_injections then + trace(head,"everything") + end + local hascursives=nofregisteredcursives>0 + local hasmarks=nofregisteredmarks>0 + local current=head + local last=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,py=getoffsets(p) + local nx,ny=getoffsets(n) + 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 + local markdir=pn.markdir + if rightkern then + ox=px-(pn.markx or 0)-rightkern + if markdir and markdir<0 then + if not pn.markmark then + ox=ox+(pn.leftkern or 0) + end + else + if false then + local leftkern=pp.leftkern + if leftkern then + ox=ox-leftkern + end + end + end + else + ox=px-(pn.markx or 0) + if markdir and markdir<0 then + if not pn.markmark then + local leftkern=pn.leftkern + if leftkern then + ox=ox+leftkern + end + end + end + if pn.checkmark then + local wn=getwidth(n) + if wn and 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,fontkern(-wn)) + insert_node_after(n,n,fontkern(-wn)) + end + end + end + local oy=ny+py+(pn.marky or 0) + if not pn.markmark then + local yoffset=pn.yoffset + if yoffset then + oy=oy+yoffset + end + end + setoffsets(n,ox,oy) + if trace_marks then + showoffset(n,true) + end + end + while current do + local next=getnext(current) + local char,id=ischar(current) + if char 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 + setoffsets(current,false,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 nx,ny=getoffsets(current) + for i=maxc,minc,-1 do + local ti=glyphs[i] + ny=ny+properties[ti].cursivedy + setoffsets(ti,false,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 nx,ny=getoffsets(current) + for i=maxc,minc,-1 do + local ti=glyphs[i] + ny=ny+properties[ti].cursivedy + setoffsets(ti,false,ny) + if trace_cursive then + showoffset(ti) + end + end + maxc=0 + end + cursiveanchor=nil + end + end + local leftkern=i.leftkern + local rightkern=i.rightkern + if leftkern and leftkern~=0 then + if rightkern and leftkern==-rightkern then + setoffsets(current,leftkern,false) + rightkern=0 + else + head=insert_node_before(head,current,fontkern(leftkern)) + end + end + if rightkern and rightkern~=0 then + insert_node_after(head,current,fontkern(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",fontkern(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 + setlink(posttail,fontkern(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 + setlink(replacetail,fontkern(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",fontkern(leftkern)) + end + end + end + if done then + setdisc(prevdisc,pre,post,replace) + end + end + end + else + if hascursives and maxc>0 then + local nx,ny=getoffsets(current) + for i=maxc,minc,-1 do + local ti=glyphs[i] + ny=ny+properties[ti].cursivedy + local xi,yi=getoffsets(ti) + setoffsets(ti,xi,yi+ny) + end + maxc=0 + cursiveanchor=nil + end + end + prevdisc=nil + prevglyph=current + elseif char==false then + 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 nextchar,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 + setoffsets(n,false,yoffset) + end + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + pre=insert_node_before(pre,n,fontkern(leftkern)) + done=true + end + local rightkern=i.rightkern + if rightkern and rightkern~=0 then + insert_node_after(pre,n,fontkern(rightkern)) + done=true + end + if hasmarks then + local pm=i.markbasenode + if pm then + processmark(pm,n,i) + end + end + end + end + end + end + if post then + for n in nextchar,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 + setoffsets(n,false,yoffset) + end + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + post=insert_node_before(post,n,fontkern(leftkern)) + done=true + end + local rightkern=i.rightkern + if rightkern and rightkern~=0 then + insert_node_after(post,n,fontkern(rightkern)) + done=true + end + if hasmarks then + local pm=i.markbasenode + if pm then + processmark(pm,n,i) + end + end + end + end + end + end + if replace then + for n in nextchar,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 + setoffsets(n,false,yoffset) + end + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + replace=insert_node_before(replace,n,fontkern(leftkern)) + done=true + end + local rightkern=i.rightkern + if rightkern and rightkern~=0 then + insert_node_after(replace,n,fontkern(rightkern)) + done=true + end + if hasmarks then + local pm=i.markbasenode + if pm then + processmark(pm,n,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,fontkern(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,fontkern(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 nx,ny=getoffsets(last) + for i=maxc,minc,-1 do + local ti=glyphs[i] + ny=ny+properties[ti].cursivedy + setoffsets(ti,false,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 + nofregisteredpositions=0 + nofregisteredmarks=0 + nofregisteredcursives=0 + end + if trace_injections then + show_result(head) + end + return head +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,id) + if (id or getid(n))==glue_code then + local w=getwidth(n) + if threshold and w>threshold then + return 32 + end + end +end +local getspaceboth=getboth +function injections.installgetspaceboth(gb) + getspaceboth=gb or getboth +end +local function injectspaces(head) + if not triggers then + return head + 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 nextglue,head do + local prev,next=getspaceboth(n) + local prevchar=prev and ischar(prev) + local nextchar=next and 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=getwidth(n) + if old>threshold then + if rightkern then + if useitalickerns then + local lnew=leftkern*factor + local rnew=rightkern*factor + if trace_spaces then + report_spaces("%C [%p + %p + %p] %C",prevchar,lnew,old,rnew,nextchar) + end + head=insert_node_before(head,n,italickern(lnew)) + insert_node_after(head,n,italickern(rnew)) + else + local new=old+(leftkern+rightkern)*factor + if trace_spaces then + report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar) + end + setwidth(n,new) + end + rightkern=false + else + if useitalickerns then + local new=leftkern*factor + if trace_spaces then + report_spaces("%C [%p + %p]",prevchar,old,new) + end + insert_node_after(head,n,italickern(new)) + else + local new=old+leftkern*factor + if trace_spaces then + report_spaces("%C [%p -> %p]",prevchar,old,new) + end + setwidth(n,new) + end + end + end + leftkern=false + elseif rightkern then + local old=getwidth(n) + if old>threshold then + if useitalickerns then + local new=rightkern*factor + if trace_spaces then + report_spaces("%C [%p + %p]",nextchar,old,new) + end + insert_node_after(head,n,italickern(new)) + else + local new=old+rightkern*factor + if trace_spaces then + report_spaces("[%p -> %p] %C",nextchar,old,new) + end + setwidth(n,new) + end + end + rightkern=false + end + end + triggers=false + return head +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 nofregisteredpositions>0 then + if trace_injections then + report_injections("injection variant %a","positions") + end + return inject_positions_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 + end +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-otj”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-ota” d883cf661bb8c27bfa9b7cc66420c1ed] --- + +if not modules then modules={} end modules ['font-ota']={ + 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=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 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 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=allocate { + 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=allocate { + 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=allocate { + 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-2018-09-24.lua scope for “font-ota”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-ots” 624f23e63b7430a51212c8824d09924f] --- + +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 registertracker=trackers.register +local logs=logs +local trackers=trackers +local nodes=nodes +local attributes=attributes +local fonts=fonts +local otf=fonts.handlers.otf +local tracers=nodes.tracers +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_plugins=false registertracker("otf.plugins",function(v) trace_plugins=v end) +local trace_chains=false registertracker("otf.chains",function(v) trace_chains=v end) +local trace_kernruns=false registertracker("otf.kernruns",function(v) trace_kernruns=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 forcediscretionaries=false +local forcepairadvance=false +directives.register("otf.forcediscretionaries",function(v) + forcediscretionaries=v +end) +directives.register("otf.forcepairadvance",function(v) + forcepairadvance=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.substitutions","otf.singles","otf.multiples","otf.alternatives","otf.ligatures") +registertracker("otf.positions","otf.marks","otf.kerns","otf.cursive") +registertracker("otf.actions","otf.substitutions","otf.positions") +registertracker("otf.sample","otf.steps","otf.substitutions","otf.positions","otf.analyzing") +registertracker("otf.sample.silent","otf.steps=silent","otf.substitutions","otf.positions","otf.analyzing") +local nuts=nodes.nuts +local getfield=nuts.getfield +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 getcomponents=nuts.getcomponents +local setcomponents=nuts.setcomponents +local getdir=nuts.getdir +local getwidth=nuts.getwidth +local ischar=nuts.is_char +local usesfont=nuts.uses_font +local insert_node_after=nuts.insert_after +local copy_node=nuts.copy +local copy_node_list=nuts.copy_list +local remove_node=nuts.remove +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 set_components=nuts.set_components +local take_components=nuts.take_components +local count_components=nuts.count_components +local copy_no_components=nuts.copy_no_components +local copy_only_glyphs=nuts.copy_only_glyphs +local setmetatable=setmetatable +local setmetatableindex=table.setmetatableindex +local nextnode=nuts.traversers.node +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 a_state=attributes.private('state') +local a_noligature=attributes.private("noligature") +local injections=nodes.injections +local setmark=injections.setmark +local setcursive=injections.setcursive +local setkern=injections.setkern +local setmove=injections.setmove +local setposition=injections.setposition +local resetinjection=injections.reset +local copyinjection=injections.copy +local setligaindex=injections.setligaindex +local getligaindex=injections.getligaindex +local fontdata=fonts.hashes.identifiers +local fontfeatures=fonts.hashes.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 classes=false +local currentfont=false +local factor=0 +local threshold=0 +local checkmarks=false +local discs=false +local spaces=false +local sweepnode=nil +local sweephead={} +local notmatchpre={} +local notmatchpost={} +local notmatchreplace={} +local handlers={} +local isspace=injections.isspace +local getthreshold=injections.getthreshold +local checkstep=(tracers and tracers.steppers.check) or function() end +local registerstep=(tracers and tracers.steppers.register) or function() end +local registermessage=(tracers and tracers.steppers.message) or function() end +local function logprocess(...) + if trace_steps then + registermessage(...) + if trace_steps=="silent" then + return + end + end + report_direct(...) +end +local function logwarning(...) + report_direct(...) +end +local gref do + local f_unicode=formatters["U+%X"] + local f_uniname=formatters["U+%X (%s)"] + local f_unilist=formatters["% t"] + gref=function(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 t={} + for i=1,#n do + local ni=n[i] + if tonumber(ni) then + local di=descriptions[ni] + local nn=di and di.name + if nn then + t[#t+1]=f_uniname(ni,nn) + else + t[#t+1]=f_unicode(ni) + end + end + end + return f_unilist(t) + else + return "" + end + end +end +local function cref(dataset,sequence,index) + if not dataset then + return "no valid dataset" + end + local merged=sequence.merged and "merged " or "" + if index then + return formatters["feature %a, type %a, %schain lookup %a, index %a"]( + dataset[4],sequence.type,merged,sequence.name,index) + else + return formatters["feature %a, type %a, %schain lookup %a"]( + dataset[4],sequence.type,merged,sequence.name) + end +end +local function pref(dataset,sequence) + return formatters["feature %a, type %a, %slookup %a"]( + dataset[4],sequence.type,sequence.merged and "merged " or "",sequence.name) +end +local function mref(rlmode) + if not rlmode or rlmode>=0 then + return "l2r" + else + return "r2l" + 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 + 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=posthead + end + if replace then + setlink(replacetail,replacehead) + else + replace=replacehead + end + setdisc(disc,pre,post,replace) +end +local take_components=getcomponents +local set_components=setcomponents +local function count_components(start,marks) + if getid(start)~=glyph_code then + return 0 + elseif getsubtype(start)==ligature_code then + local i=0 + local components=getcomponents(start) + while components do + i=i+count_components(components,marks) + components=getnext(components) + end + return i + elseif not marks[getchar(start)] then + return 1 + else + return 0 + end +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_no_components(start,copyinjection) + if head==start then + head=base + end + resetinjection(base) + setchar(base,char) + setsubtype(base,ligature_code) + set_components(base,start) + setlink(prev,base,next) + return head,base + end +end +local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfound,hasmarks) + 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 prev=getprev(start) + local next=getnext(stop) + local comp=start + setprev(start) + setnext(stop) + local base=copy_no_components(start,copyinjection) + if start==head then + head=base + end + resetinjection(base) + setchar(base,char) + setsubtype(base,ligature_code) + set_components(base,comp) + setlink(prev,base,next) + if not discfound then + local deletemarks=not skiphash or hasmarks + 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=count_components(start,marks) + 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 comp=take_components(base) + local copied=copy_only_glyphs(comp) + if pre then + setlink(discprev,pre) + else + setnext(discprev) + end + pre=comp + if post then + setlink(posttail,discnext) + setprev(post) + else + post=discnext + setprev(discnext) + end + setlink(prev,discfound,next) + setboth(base) + set_components(base,copied) + replace=base + if forcediscretionaries then + setdisc(discfound,pre,post,replace,discretionary_code) + else + setdisc(discfound,pre,post,replace) + end + base=prev + end + end + end + return head,base +end +local function multiple_glyphs(head,start,multiple,skiphash,what,stop) + 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 n==1 then + return alternatives[1],trace_alternatives and "1 (only one present)" + elseif 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,rlmode,skiphash) + 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,skiphash,dataset[1]) +end +function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skiphash) + local current=getnext(start) + if not current then + return head,start,false,nil + end + local stop=nil + local startchar=getchar(start) + if skiphash and skiphash[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 discfound=false + local lastdisc=nil + local hasmarks=marks[startchar] + while current do + local char,id=ischar(current,currentfont) + if char then + if skiphash and skiphash[char] then + current=getnext(current) + else + local lg=ligature[char] + if lg then + if not discfound and lastdisc then + discfound=lastdisc + lastdisc=nil + end + if marks[char] then + hasmarks=true + 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 + if marks[char] then + hasmarks=true + end + 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,skiphash,discfound,hasmarks) + 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,skiphash,discfound,hasmarks) + 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,skiphash,step,injection) + local startchar=getchar(start) + local format=step.format + if format=="single" or type(kerns)=="table" then + local dx,dy,w,h=setposition(0,start,factor,rlmode,kerns,injection) + if trace_kerns then + logprocess("%s: shifting single %s by %s xy (%p,%p) and wh (%p,%p)",pref(dataset,sequence),gref(startchar),format,dx,dy,w,h) + end + else + local k=(format=="move" and setmove or setkern)(start,factor,rlmode,kerns,injection) + if trace_kerns then + logprocess("%s: shifting single %s by %s %p",pref(dataset,sequence),gref(startchar),format,k) + end + end + return head,start,true +end +function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection) + local snext=getnext(start) + if not snext then + return head,start,false + else + local prev=start + while snext do + local nextchar=ischar(snext,currentfont) + if nextchar then + if skiphash and skiphash[nextchar] then + prev=snext + snext=getnext(snext) + else + local krn=kerns[nextchar] + if not krn then + break + end + local format=step.format + if format=="pair" then + local a,b=krn[1],krn[2] + if a==true then + elseif a then + local x,y,w,h=setposition(1,start,factor,rlmode,a,injection) + if trace_kerns then + local startchar=getchar(start) + logprocess("%s: shifting first of pair %s and %s by xy (%p,%p) and wh (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections") + end + end + if b==true then + start=snext + elseif b then + local x,y,w,h=setposition(2,snext,factor,rlmode,b,injection) + if trace_kerns then + local startchar=getchar(start) + logprocess("%s: shifting second of pair %s and %s by xy (%p,%p) and wh (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections") + end + start=snext + elseif forcepairadvance then + start=snext + end + return head,start,true + elseif krn~=0 then + local k=(format=="move" and setmove or setkern)(snext,factor,rlmode,krn,injection) + if trace_kerns then + logprocess("%s: inserting %s %p between %s and %s as %s",pref(dataset,sequence),format,k,gref(getchar(prev)),gref(nextchar),injection or "injections") + end + return head,start,true + else + break + end + end + else + break + end + end + return head,start,false + end +end +function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode,skiphash) + 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, bound %s, anchoring mark %s to basechar %s => (%p,%p)", + pref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) + end + return head,start,true + elseif trace_bugs then + logwarning("%s: mark %s is not anchored to %s",pref(dataset,sequence),gref(markchar),gref(basechar)) + 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,skiphash) + 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, index %s, bound %s, anchoring mark %s to baselig %s at index %s => (%p,%p)", + pref(dataset,sequence),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,skiphash) + 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, bound %s, anchoring mark %s to basemark %s => (%p,%p)", + pref(dataset,sequence),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,skiphash,step) + 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 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 r2lflag=sequence.flags[4] + local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar],r2lflag) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%p,%p) using bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,bound,mref(rlmode)) + end + return head,start,true + end + end + end + break + end + end + end + return head,start,false +end +local chainprocs={} +local function logprocess(...) + if trace_steps then + registermessage(...) + if trace_steps=="silent" then + return + end + end + report_subchain(...) +end +local logwarning=report_subchain +local function logprocess(...) + if trace_steps then + registermessage(...) + if trace_steps=="silent" then + return + end + end + report_chain(...) +end +local logwarning=report_chain +local function reversesub(head,start,stop,dataset,sequence,replacements,rlmode,skiphash) + 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 reportzerosteps(dataset,sequence) + logwarning("%s: no steps",cref(dataset,sequence)) +end +local function reportmoresteps(dataset,sequence) + logwarning("%s: more than 1 step",cref(dataset,sequence)) +end +local function getmapping(dataset,sequence,currentlookup) + local steps=currentlookup.steps + local nofsteps=currentlookup.nofsteps + if nofsteps==0 then + reportzerosteps(dataset,sequence) + currentlookup.mapping=false + return false + else + if nofsteps>1 then + reportmoresteps(dataset,sequence) + end + local mapping=steps[1].coverage + currentlookup.mapping=mapping + currentlookup.format=steps[1].format + return mapping + end +end +function chainprocs.gsub_remove(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + if trace_chains then + logprocess("%s: removing character %s",cref(dataset,sequence,chainindex),gref(getchar(start))) + end + head,start=remove_node(head,start,true) + return head,getprev(start),true +end +function chainprocs.gsub_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local current=start + while current do + local currentchar=ischar(current) + if currentchar then + local replacement=mapping[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 + end + return head,start,false +end +function chainprocs.gsub_alternate(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + 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=mapping[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(currentchar),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(currentchar),comment) + end + end + end + return head,start,true + elseif currentchar==false then + break + elseif current==stop then + break + else + current=getnext(current) + end + end + end + return head,start,false +end +function chainprocs.gsub_multiple(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local startchar=getchar(start) + local replacement=mapping[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,skiphash,dataset[1],stop) + end + end + return head,start,false +end +function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local startchar=getchar(start) + local ligatures=mapping[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 hasmarks=marks[startchar] + local current=getnext(start) + local discfound=false + local last=stop + local nofreplacements=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 skiphash and skiphash[schar] then + current=getnext(current) + else + local lg=ligatures[schar] + if lg then + ligatures=lg + last=current + nofreplacements=nofreplacements+1 + if marks[char] then + hasmarks=true + end + 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,skiphash,discfound,hasmarks) + 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 + end + return head,start,false,0,false +end +function chainprocs.gpos_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local startchar=getchar(start) + local kerns=mapping[startchar] + if kerns then + local format=currentlookup.format + if format=="single" then + local dx,dy,w,h=setposition(0,start,factor,rlmode,kerns) + if trace_kerns then + logprocess("%s: shifting single %s by %s (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),format,dx,dy,w,h) + end + else + local k=(format=="move" and setmove or setkern)(start,factor,rlmode,kerns,injection) + if trace_kerns then + logprocess("%s: shifting single %s by %s %p",cref(dataset,sequence),gref(startchar),format,k) + end + end + return head,start,true + end + end + return head,start,false +end +function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local snext=getnext(start) + if snext then + local startchar=getchar(start) + local kerns=mapping[startchar] + if kerns then + local prev=start + while snext do + local nextchar=ischar(snext,currentfont) + if not nextchar then + break + end + if skiphash and skiphash[nextchar] then + prev=snext + snext=getnext(snext) + else + local krn=kerns[nextchar] + if not krn then + break + end + local format=currentlookup.format + if format=="pair" then + local a,b=krn[1],krn[2] + if a==true then + elseif a then + local x,y,w,h=setposition(1,start,factor,rlmode,a,"injections") + if trace_kerns then + local startchar=getchar(start) + 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==true then + start=snext + elseif b then + local x,y,w,h=setposition(2,snext,factor,rlmode,b,"injections") + if trace_kerns then + local startchar=getchar(start) + 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 + start=snext + elseif forcepairadvance then + start=snext + end + return head,start,true + elseif krn~=0 then + local k=(format=="move" and setmove or setkern)(snext,factor,rlmode,krn) + if trace_kerns then + logprocess("%s: inserting %s %p between %s and %s",cref(dataset,sequence),format,k,gref(getchar(prev)),gref(nextchar)) + end + return head,start,true + else + break + end + end + end + end + end + end + return head,start,false +end +function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local markchar=getchar(start) + if marks[markchar] then + local markanchors=mapping[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, bound %s, anchoring mark %s to basechar %s => (%p,%p)", + cref(dataset,sequence),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 + end + return head,start,false +end +function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local markchar=getchar(start) + if marks[markchar] then + local markanchors=mapping[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, bound %s, anchoring mark %s to baselig %s at index %s => (%p,%p)", + cref(dataset,sequence),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 + end + return head,start,false +end +function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local markchar=getchar(start) + if marks[markchar] then + local markanchors=mapping[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, bound %s, anchoring mark %s to basemark %s => (%p,%p)", + cref(dataset,sequence),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 + end + return head,start,false +end +function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local startchar=getchar(start) + local exitanchors=mapping[startchar] + if exitanchors then + 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 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 r2lflag=sequence.flags[4] + local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar],r2lflag) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%p,%p) using bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,bound,mref(rlmode)) + end + return head,start,true + end + end + elseif trace_bugs then + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + end + break + end + end + end + elseif trace_cursive and trace_details then + logprocess("%s, cursive %s is already done",pref(dataset,sequence),gref(getchar(start)),alreadydone) + end + end + return head,start,false +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 userkern=nuts.pool and nuts.pool.newkern +do if not userkern then + local thekern=nuts.new("kern",1) + local setkern=nuts.setkern + userkern=function(k) + local n=copy_node(thekern) + setkern(n,k) + return n + end +end end +local function checked(head) + local current=head + while current do + if getid(current)==glue_code then + local kern=userkern(getwidth(current)) + 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,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 noflags={ false,false,false,false } +local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) + local size=ck[5]-ck[4]+1 + local chainlookups=ck[6] + local done=false + if chainlookups then + if size==1 then + local chainlookup=chainlookups[1] + for j=1,#chainlookup do + local chainstep=chainlookup[j] + local chainkind=chainstep.type + local chainproc=chainprocs[chainkind] + if chainproc then + local ok + head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash) + if ok then + done=true + end + else + logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind) + end + end + else + local i=1 + local laststart=start + local nofchainlookups=#chainlookups + while start do + if skiphash then + while start do + local char=ischar(start,currentfont) + if char then + if skiphash and skiphash[char] then + start=getnext(start) + else + break + end + else + break + end + end + end + local chainlookup=chainlookups[i] + if chainlookup then + for j=1,#chainlookup do + local chainstep=chainlookup[j] + local chainkind=chainstep.type + local chainproc=chainprocs[chainkind] + if chainproc then + local ok,n + head,start,ok,n=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,i) + if ok then + done=true + if n and n>1 and i+n>nofchainlookups then + i=size + break + end + end + else + logprocess("%s: %s is not yet supported (2)",cref(dataset,sequence),chainkind) + end + end + else + end + i=i+1 + if i>size or not start then + break + elseif start then + laststart=start + start=getnext(start) + end + end + if not start then + start=laststart + end + end + else + local replacements=ck[7] + if replacements then + head,start,done=reversesub(head,start,last,dataset,sequence,replacements,rlmode,skiphash) + else + done=true + if trace_contexts then + logprocess("%s: skipping match",cref(dataset,sequence)) + end + end + end + return head,start,done +end +local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) + 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 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 + lookaheaddisc=current + local replace=getfield(current,"replace") + if not replace then + sweepoverflow=true + sweepnode=current + current=getnext(current) + else + while replace and i<=l do + if getid(replace)==glyph_code then + i=i+1 + end + replace=getnext(replace) + end + current=getnext(replace) + end + last=current + 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 l1 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 done=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 + setlink(cprev,lookaheaddisc) + 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 + if pre then + setlink(find_node_tail(cf),pre) + end + if replace then + local tail=find_node_tail(new) + setlink(tail,replace) + end + for i=1,insertedmarks do + cnew=getnext(cnew) + end + cl=start + local clast=cnew + for i=f,l do + cl=getnext(cl) + clast=getnext(clast) + end + if not notmatchpre[lookaheaddisc] then + local ok=false + cf,start,ok=chainrun(cf,start,cl,dataset,sequence,rlmode,skiphash,ck) + if ok then + done=true + end + end + if not notmatchreplace[lookaheaddisc] then + local ok=false + new,cnew,ok=chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck) + if ok then + done=true + end + end + if hasglue then + setdiscchecked(lookaheaddisc,cf,post,new) + else + setdisc(lookaheaddisc,cf,post,new) + end + start=getprev(lookaheaddisc) + sweephead[cf]=getnext(clast) or false + sweephead[new]=getnext(cl) or false + 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 + setlink(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 + local ok=false + cf,start,ok=chainrun(cf,start,last,dataset,sequence,rlmode,skiphash,ck) + if ok then + done=true + end + end + if not notmatchreplace[backtrackdisc] then + local ok=false + new,cnew,ok=chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck) + if ok then + done=true + end + 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) or false + sweephead[replace]=getnext(last) or false + else + local ok=false + head,start,ok=chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) + if ok then + done=true + end + end + return head,start,done +end +local function chaintrac(head,start,dataset,sequence,rlmode,skiphash,ck,match,discseen,sweepnode) + local rule=ck[1] + local lookuptype=ck[8] or ck[2] + local nofseq=#ck[3] + local first=ck[4] + local last=ck[5] + local char=getchar(start) + logwarning("%s: rule %s %s at char %s for (%s,%s,%s) chars, lookuptype %a, %sdisc seen, %ssweeping", + cref(dataset,sequence),rule,match and "matches" or "nomatch", + gref(char),first-1,last-first+1,nofseq-last,lookuptype, + discseen and "" or "no ",sweepnode and "" or "not ") +end +local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,skiphash) + local sweepnode=sweepnode + local sweeptype=sweeptype + local postreplace + local prereplace + local checkdisc + local discseen + if sweeptype then + if sweeptype=="replace" then + postreplace=true + prereplace=true + else + postreplace=sweeptype=="post" + prereplace=sweeptype=="pre" + end + checkdisc=getprev(head) + end + local currentfont=currentfont + local skipped + local startprev, + startnext=getboth(start) + local done + local nofcontexts=contexts.n + local startchar=nofcontext==1 or ischar(start,currentfont) + for k=1,nofcontexts do + local ck=contexts[k] + local seq=ck[3] + local f=ck[4] + if not startchar or not seq[f][startchar] then + goto next + end + local s=seq.n + local l=ck[5] + local current=start + local last=start + if l>f then + local discfound + local n=f+1 + last=startnext + while n<=l do + if postreplace and not last then + last=getnext(sweepnode) + sweeptype=nil + end + if last then + local char,id=ischar(last,currentfont) + if char then + if skiphash and skiphash[char] then + skipped=true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + last=getnext(last) + elseif seq[n][char] then + if nl then + break + end + pre=getnext(pre) + else + notmatchpre[last]=true + break + end + end + else + notmatchpre[last]=true + end + if replace then + while replace do + if seq[n][getchar(replace)] then + n=n+1 + if n>l then + break + end + replace=getnext(replace) + else + notmatchreplace[last]=true + if notmatchpre[last] then + goto next + else + break + end + end + end + if notmatchpre[last] then + goto next + end + end + last=getnext(last) + else + goto next + end + else + goto next + end + end + end + if f>1 then + if startprev then + local prev=startprev + if prereplace and prev==checkdisc then + prev=getprev(sweepnode) + end + if prev then + local discfound + local n=f-1 + while n>=1 do + if prev then + local char,id=ischar(prev,currentfont) + if char then + if skiphash and skiphash[char] then + skipped=true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + prev=getprev(prev) + elseif seq[n][char] then + if n>1 then + prev=getprev(prev) + end + n=n-1 + elseif discfound then + notmatchreplace[discfound]=true + if notmatchpost[discfound] then + goto next + else + break + end + else + goto next + end + elseif char==false then + if discfound then + notmatchreplace[discfound]=true + if notmatchpost[discfound] then + goto next + end + else + goto next + end + break + elseif id==disc_code then + discseen=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 or n<1 then + break + else + posttail=getprev(posttail) + 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 or n<1 then + break + else + replacetail=getprev(replacetail) + end + else + notmatchreplace[prev]=true + if notmatchpost[prev] then + goto next + else + break + end + end + end + else + notmatchreplace[prev]=true + end + end + prev=getprev(prev) + elseif id==glue_code then + local sn=seq[n] + if (sn[32] and spaces[prev]) or sn[0xFFFC] then + n=n-1 + prev=getprev(prev) + else + goto next + end + elseif seq[n][0xFFFC] then + n=n-1 + prev=getprev(prev) + else + goto next + end + else + goto next + end + end + else + goto next + end + else + goto next + end + end + if s>l then + local current=last and getnext(last) + if not current and postreplace then + current=getnext(sweepnode) + end + if current then + local discfound + local n=l+1 + while n<=s do + if current then + local char,id=ischar(current,currentfont) + if char then + if skiphash and skiphash[char] then + skipped=true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + current=getnext(current) + elseif seq[n][char] then + if ns then + break + else + pre=getnext(pre) + 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 + if n>s then + break + else + replace=getnext(replace) + end + else + notmatchreplace[current]=true + if notmatchpre[current] then + goto next + else + break + end + end + end + else + notmatchreplace[current]=true + end + current=getnext(current) + elseif id==glue_code then + local sn=seq[n] + if (sn[32] and spaces[current]) or sn[0xFFFC] then + n=n+1 + current=getnext(current) + else + goto next + end + elseif seq[n][0xFFFC] then + n=n+1 + current=getnext(current) + else + goto next + end + else + goto next + end + end + else + goto next + end + end + if trace_contexts then + chaintrac(head,start,dataset,sequence,rlmode,skipped and skiphash,ck,true,discseen,sweepnode) + end + if discseen or sweepnode then + head,start,done=chaindisk(head,start,dataset,sequence,rlmode,skipped and skiphash,ck) + else + head,start,done=chainrun(head,start,last,dataset,sequence,rlmode,skipped and skiphash,ck) + end + if done then + break + end + ::next:: + end + if discseen 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,skiphash) + local steps=currentlookup.steps + local nofsteps=currentlookup.nofsteps + if nofsteps>1 then + reportmoresteps(dataset,sequence) + end + local l=steps[1].coverage[getchar(start)] + if l then + return handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash) + else + return head,start,false + end +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 logwarning=report_process +local resolved={} +local function logprocess(...) + if trace_steps then + registermessage(...) + if trace_steps=="silent" then + return + end + end + report_process(...) +end +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) +do + local autofeatures=fonts.analyzers.features + local featuretypes=otf.tables.featuretypes + local defaultscript=otf.features.checkeddefaultscript + local defaultlanguage=otf.features.checkeddefaultlanguage + local wildcard="*" + local default="dflt" + 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 +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 + setlink(prev,pre) + if k_run(prevmarks,"preinjections",pre,font,attr,...) then + done=true + end + setprev(pre) + setlink(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) + setlink(disc,next) + end + end + if replace then + if k_run(replace,"injections",nil,font,attr,...) then + done=true + end + if prev then + setlink(prev,replace) + if k_run(prevmarks,"replaceinjections",replace,font,attr,...) then + done=true + end + setprev(replace) + setlink(prev,disc) + end + if next then + setlink(replacetail,next) + if k_run(replacetail,"replaceinjections",next,font,attr,...) then + done=true + end + setnext(replacetail) + setlink(disc,next) + end + elseif prev and next then + setlink(prev,next) + if k_run(prevmarks,"emptyinjections",next,font,attr,...) then + done=true + end + setlink(prev,disc,next) + end + if done and trace_testruns then + report_disc("done",disc) + 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 + if trace_testruns then + report_disc("done",disc) + end + 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 renewed=false + if (post or replace) and prev then + if post then + setlink(posttail,next) + else + post=next + end + if replace then + setlink(replacetail,next) + else + replace=next + end + local d_post=t_run(post,next,...) + local d_replace=t_run(replace,next,...) + if d_post>0 or d_replace>0 then + local d=d_replace>d_post and d_replace or d_post + local head=getnext(disc) + local tail=head + for i=1,d do + local nx=getnext(tail) + local id=getid(nx) + if id==disc_code then + head,tail=flattendisk(head,nx) + elseif id==glyph_code then + tail=nx + else + break + end + end + next=getnext(tail) + setnext(tail) + setprev(head) + local new=copy_node_list(head) + if posttail then + setlink(posttail,head) + else + post=head + end + if replacetail then + setlink(replacetail,new) + else + replace=new + end + else + if posttail then + setnext(posttail) + else + post=nil + end + if replacetail then + setnext(replacetail) + else + replace=nil + end + end + setlink(disc,next) + end + if trace_testruns then + report_disc("more",disc) + end + 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) + if trace_testruns then + report_disc("done",disc) + end + end + return getnext(disc),renewed +end +local nesting=0 +local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + local done=false + local sweep=sweephead[head] + if sweep then + start=sweep + sweephead[head]=false + else + start=head + end + while start do + local char,id=ischar(start,font) + if char then + local a + if attr then + a=getattr(start,0) + end + 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,skiphash,step) + 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) + local lastd=nil + while start~=stop do + local char=ischar(start,font) + if char then + local a + if attr then + a=getattr(start,0) + end + local startnext=getnext(start) + if not a or (a==attr) then + local lookupmatch=lookupcache[char] + if lookupmatch then + local s=startnext + local ss=nil + local sstop=s==stop + if not s then + s=ss + ss=nil + end + while getid(s)==disc_code do + ss=getnext(s) + s=getfield(s,"replace") + if not s then + s=ss + ss=nil + end + end + local l=nil + local d=0 + while s do + local char=ischar(s,font) + if char then + local lg=lookupmatch[char] + if lg then + if sstop then + d=1 + elseif d>0 then + d=d+1 + end + l=lg + s=getnext(s) + sstop=s==stop + if not s then + s=ss + ss=nil + end + while getid(s)==disc_code do + ss=getnext(s) + s=getfield(s,"replace") + if not s then + s=ss + ss=nil + end + end + else + break + end + else + break + end + end + if l and l.ligature then + lastd=d + end + else + end + else + end + if lastd then + return lastd + end + start=startnext + else + break + end + end + return 0 +end +local function k_run_single(sub,injection,last,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + local a + if attr then + a=getattr(sub,0) + end + if not a or (a==attr) then + for n in nextnode,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,skiphash,step,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,skiphash,handler) + local done=false + local sweep=sweephead[head] + if sweep then + start=sweep + sweephead[head]=false + else + start=head + end + while start do + local char=ischar(start,font) + if char then + local a + if attr then + a=getattr(start,0) + end + if not a or (a==attr) then + for i=1,nofsteps do + local step=steps[i] + local lookupcache=step.coverage + local lookupmatch=lookupcache[char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + done=true + break + elseif not start then + break + end + 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) + local lastd=nil + while start~=stop do + local char=ischar(start,font) + if char then + local a + if attr then + a=getattr(start,0) + end + local startnext=getnext(start) + if not a or (a==attr) then + for i=1,nofsteps do + local step=steps[i] + local lookupcache=step.coverage + local lookupmatch=lookupcache[char] + if lookupmatch then + local s=startnext + local ss=nil + local sstop=s==stop + if not s then + s=ss + ss=nil + end + while getid(s)==disc_code do + ss=getnext(s) + s=getfield(s,"replace") + if not s then + s=ss + ss=nil + end + end + local l=nil + local d=0 + while s do + local char=ischar(s) + if char then + local lg=lookupmatch[char] + if lg then + if sstop then + d=1 + elseif d>0 then + d=d+1 + end + l=lg + s=getnext(s) + sstop=s==stop + if not s then + s=ss + ss=nil + end + while getid(s)==disc_code do + ss=getnext(s) + s=getfield(s,"replace") + if not s then + s=ss + ss=nil + end + end + else + break + end + else + break + end + end + if l and l.ligature then + lastd=d + end + end + end + else + end + if lastd then + return lastd + end + start=startnext + else + break + end + end + return 0 +end +local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + local a + if attr then + a=getattr(sub,0) + end + if not a or (a==attr) then + for n in nextnode,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 + local lookupmatch=lookupcache[char] + if lookupmatch then + local h,d,ok=handler(sub,n,dataset,sequence,lookupmatch,rlmode,skiphash,step,injection) + if ok then + return true + end + end + end + end + end + end +end +local function txtdirstate(start,stack,top,rlparmode) + local nxt=getnext(start) + local dir=getdir(start) + if dir=="+TRT" then + top=top+1 + stack[top]=dir + return nxt,top,-1 + elseif dir=="+TLT" then + top=top+1 + stack[top]=dir + return nxt,top,1 + elseif dir=="-TRT" or dir=="-TLT" then + if top==1 then + return nxt,0,rlparmode + else + top=top-1 + if stack[top]=="+TRT" then + return nxt,top,-1 + else + return nxt,top,1 + end + end + else + return nxt,top,rlparmode + end +end +local function pardirstate(start) + local nxt=getnext(start) + local dir=getdir(start) + if dir=="TLT" then + return nxt,1,1 + elseif dir=="TRT" then + return nxt,-1,-1 + else + return nxt,0,0 + end +end +otf.helpers=otf.helpers or {} +otf.helpers.txtdirstate=txtdirstate +otf.helpers.pardirstate=pardirstate +do + local fastdisc=true + local testdics=false + directives.register("otf.fastdisc",function(v) fastdisc=v end) + local otfdataset=nil + local getfastdisc={ __index=function(t,k) + local v=usesfont(k,currentfont) + t[k]=v + return v + end } + local getfastspace={ __index=function(t,k) + local v=isspace(k,threshold) or false + t[k]=v + return v + end } + function otf.featuresprocessor(head,font,attr,direction,n) + local sequences=sequencelists[font] + nesting=nesting+1 + if nesting==1 then + currentfont=font + tfmdata=fontdata[font] + descriptions=tfmdata.descriptions + characters=tfmdata.characters + local resources=tfmdata.resources + marks=resources.marks + classes=resources.classes + threshold, + factor=getthreshold(font) + checkmarks=tfmdata.properties.checkmarks + if not otfdataset then + otfdataset=otf.dataset + end + discs=fastdisc and n and n>1 and setmetatable({},getfastdisc) + spaces=setmetatable({},getfastspace) + elseif currentfont~=font then + report_warning("nested call with a different font, level %s, quitting",nesting) + nesting=nesting-1 + return head,false + end + if trace_steps then + checkstep(head) + end + local initialrl=direction=="TRT" and -1 or 0 + local datasets=otfdataset(tfmdata,font,attr) + local dirstack={ nil } + sweephead={} + for s=1,#datasets do + local dataset=datasets[s] + local attribute=dataset[2] + local sequence=dataset[3] + local rlparmode=initialrl + local topstack=0 + local typ=sequence.type + local gpossing=typ=="gpos_single" or typ=="gpos_pair" + local forcetestrun=typ=="gsub_ligature" + local handler=handlers[typ] + local steps=sequence.steps + local nofsteps=sequence.nofsteps + local skiphash=sequence.skiphash + if not steps then + local h,ok=handler(head,dataset,sequence,initialrl,font,attr) + if h and h~=head then + head=h + end + elseif typ=="gsub_reversecontextchain" then + local start=find_node_tail(head) + local rlmode=0 + local merged=steps.merged + while start do + local char=ischar(start,font) + if char then + local m=merged[char] + if m then + local a + if attr then + a=getattr(start,0) + end + if not a or (a==attr) then + for i=m[1],m[2] do + local step=steps[i] + local lookupcache=step.coverage + local lookupmatch=lookupcache[char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + break + end + end + end + if start then + start=getprev(start) + end + else + start=getprev(start) + end + else + start=getprev(start) + end + else + start=getprev(start) + end + end + else + local start=head + local rlmode=initialrl + if nofsteps==1 then + local step=steps[1] + local lookupcache=step.coverage + while start do + local char,id=ischar(start,font) + if char then + if skiphash and skiphash[char] then + start=getnext(start) + else + local lookupmatch=lookupcache[char] + if lookupmatch then + local a + if attr then + if getattr(start,0)==attr and (not attribute or getprop(start,a_state)==attribute) then + a=true + end + elseif not attribute or getprop(start,a_state)==attribute then + a=true + end + if a then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if start then + start=getnext(start) + end + else + start=getnext(start) + end + else + start=getnext(start) + end + end + elseif char==false or id==glue_code then + start=getnext(start) + elseif id==disc_code then + if not discs or discs[start]==true then + local ok + if gpossing then + start,ok=kernrun(start,k_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + elseif forcetestrun then + start,ok=testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + else + start,ok=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + 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 + else + local merged=steps.merged + while start do + local char,id=ischar(start,font) + if char then + if skiphash and skiphash[char] then + start=getnext(start) + else + local m=merged[char] + if m then + local a + if attr then + if getattr(start,0)==attr and (not attribute or getprop(start,a_state)==attribute) then + a=true + end + elseif not attribute or getprop(start,a_state)==attribute then + a=true + end + if a then + for i=m[1],m[2] do + local step=steps[i] + local lookupcache=step.coverage + local lookupmatch=lookupcache[char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + break + elseif not start then + break + end + end + end + if start then + start=getnext(start) + end + else + start=getnext(start) + end + else + start=getnext(start) + end + end + elseif char==false or id==glue_code then + start=getnext(start) + elseif id==disc_code then + if not discs or discs[start]==true then + local ok + if gpossing then + start,ok=kernrun(start,k_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + elseif forcetestrun then + start,ok=testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + else + start,ok=comprun(start,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + 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 trace_steps then + registerstep(head) + end + end + nesting=nesting-1 + return head + end + function otf.datasetpositionprocessor(head,font,direction,dataset) + currentfont=font + tfmdata=fontdata[font] + descriptions=tfmdata.descriptions + characters=tfmdata.characters + local resources=tfmdata.resources + marks=resources.marks + classes=resources.classes + threshold, + factor=getthreshold(font) + checkmarks=tfmdata.properties.checkmarks + if type(dataset)=="number" then + dataset=otfdataset(tfmdata,font,0)[dataset] + end + local sequence=dataset[3] + local typ=sequence.type + local handler=handlers[typ] + local steps=sequence.steps + local nofsteps=sequence.nofsteps + local done=false + local dirstack={ nil } + local start=head + local initialrl=direction=="TRT" and -1 or 0 + local rlmode=initialrl + local rlparmode=initialrl + local topstack=0 + local merged=steps.merged + local position=0 + while start do + local char,id=ischar(start,font) + if char then + position=position+1 + local m=merged[char] + if m then + if skiphash and skiphash[char] then + start=getnext(start) + else + for i=m[1],m[2] do + local step=steps[i] + local lookupcache=step.coverage + local lookupmatch=lookupcache[char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + break + elseif not start then + break + end + end + end + if start then + start=getnext(start) + end + end + else + start=getnext(start) + end + elseif char==false or id==glue_code then + start=getnext(start) + 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 + return head + end +end +local plugins={} +otf.plugins=plugins +local report=logs.reporter("fonts") +function otf.registerplugin(name,f) + if type(name)=="string" and type(f)=="function" then + plugins[name]={ name,f } + report() + report("plugin %a has been loaded, please be aware of possible side effects",name) + report() + if logs.pushtarget then + logs.pushtarget("log") + end + report("Plugins are not officially supported unless stated otherwise. This is because") + report("they bypass the regular font handling and therefore some features in ConTeXt") + report("(especially those related to fonts) might not work as expected or might not work") + report("at all. Some plugins are for testing and development only and might change") + report("whenever we feel the need for it.") + report() + if logs.poptarget then + logs.poptarget() + end + end +end +function otf.plugininitializer(tfmdata,value) + if type(value)=="string" then + tfmdata.shared.plugin=plugins[value] + end +end +function otf.pluginprocessor(head,font,attr,direction) + 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,attr,direction) + else + return head,false + end +end +function otf.featuresinitializer(tfmdata,value) +end +registerotffeature { + name="features", + description="features", + default=true, + initializers={ + position=1, + node=otf.featuresinitializer, + plug=otf.plugininitializer, + }, + processors={ + node=otf.featuresprocessor, + plug=otf.pluginprocessor, + } +} +otf.handlers=handlers +local setspacekerns=nodes.injections.setspacekerns if not setspacekerns then os.exit() end +local tag="kern" +if fontfeatures then + function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,attr) + local features=fontfeatures[font] + local enabled=features and features.spacekern and features[tag] + if enabled then + setspacekerns(font,sequence) + end + return head,enabled + end +else + function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,attr) + local shared=fontdata[font].shared + local features=shared and shared.features + local enabled=features and features.spacekern and features[tag] + if enabled then + setspacekerns(font,sequence) + end + return head,enabled + end +end +local function hasspacekerns(data) + local resources=data.resources + local sequences=resources.sequences + local validgpos=resources.features.gpos + if validgpos and sequences then + for i=1,#sequences do + local sequence=sequences[i] + local steps=sequence.steps + if steps and sequence.features[tag] then + local kind=sequence.type + if kind=="gpos_pair" or kind=="gpos_single" then + for i=1,#steps do + local step=steps[i] + local coverage=step.coverage + local rules=step.rules + if rules then + elseif not coverage then + elseif kind=="gpos_single" then + elseif kind=="gpos_pair" then + local format=step.format + if format=="move" or format=="kern" then + local kerns=coverage[32] + if kerns then + return true + end + for k,v in next,coverage do + if v[32] then + return true + end + end + elseif format=="pair" then + local kerns=coverage[32] + if kerns then + for k,v in next,kerns do + local one=v[1] + if one and one~=true then + return true + end + end + end + for k,v in next,coverage do + local kern=v[32] + if kern then + local one=kern[1] + if one and one~=true then + return true + end + end + end + end + 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 + if value and spacekerns==nil then + local rawdata=tfmdata.shared and tfmdata.shared.rawdata + local properties=rawdata.properties + if properties and properties.hasspacekerns then + local sequences=resources.sequences + local validgpos=resources.features.gpos + if validgpos and sequences then + 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[tag] + if kern then + local kind=sequence.type + if kind=="gpos_pair" or kind=="gpos_single" 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 + if rules then + elseif not coverage then + elseif kind=="gpos_single" then + elseif kind=="gpos_pair" then + local format=step.format + if format=="move" or format=="kern" then + local kerns=coverage[32] + if kerns then + for k,v in next,kerns do + right[k]=v + end + end + for k,v in next,coverage do + local kern=v[32] + if kern then + left[k]=kern + end + end + elseif format=="pair" then + local kerns=coverage[32] + if kerns then + for k,v in next,kerns do + local one=v[1] + if one and one~=true then + right[k]=one[3] + end + end + end + for k,v in next,coverage do + local kern=v[32] + if kern then + local one=kern[1] + if one and one~=true then + left[k]=one[3] + end + end + end + end + end + end + last=i + end + else + end + 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={ [tag]=feat or { dflt={ dflt=true,} } }, + flags=noflags, + name="trigger_space_kerns", + order={ tag }, + type="trigger_space_kerns", + left=left, + right=right, + } + insert(sequences,last,triggersequence) + end + end + end + 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-2018-09-24.lua scope for “font-ots”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-osd” d2b542031aa693bb423b6d3272820c9a] --- + +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=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 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 c_nbsp=0x00A0 +local c_zwnj=0x200C +local c_zwj=0x200D +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 processcharacters=nil +if context then + local fontprocesses=fonts.hashes.processes + function processcharacters(head,font) + local processors=fontprocesses[font] + for i=1,#processors do + head=processors[i](head,font,0) + end + return head + end +else + function processcharacters(head,font) + local processors=fontdata[font].shared.processes + for i=1,#processors do + head=processors[i](head,font,0) + end + return head + end +end +local indicgroups=characters and characters.indicgroups +if not indicgroups and characters then + local indic={ + c={}, + i={}, + d={}, + m={}, + s={}, + o={}, + } + local indicmarks={ + l={}, + t={}, + b={}, + r={}, + s={}, + } + local indicclasses={ + nukta={}, + halant={}, + ra={}, + anudatta={}, + } + local indicorders={ + bp={}, + ap={}, + bs={}, + as={}, + bh={}, + ah={}, + bm={}, + am={}, + } + for k,v in next,characters.data do + local i=v.indic + if i then + indic[i][k]=true + i=v.indicmark + if i then + if i=="s" then + local s=v.specials + indicmarks[i][k]={ s[2],s[3] } + else + indicmarks[i][k]=true + end + end + i=v.indicclass + if i then + indicclasses[i][k]=true + end + i=v.indicorder + if i then + indicorders[i][k]=true + end + end + end + indicgroups={ + consonant=indic.c, + independent_vowel=indic.i, + dependent_vowel=indic.d, + vowel_modifier=indic.m, + stress_tone_mark=indic.s, + pre_mark=indicmarks.l, + above_mark=indicmarks.t, + below_mark=indicmarks.b, + post_mark=indicmarks.r, + twopart_mark=indicmarks.s, + nukta=indicclasses.nukta, + halant=indicclasses.halant, + ra=indicclasses.ra, + anudatta=indicclasses.anudatta, + before_postscript=indicorders.bp, + after_postscript=indicorders.ap, + before_half=indicorders.bh, + after_half=indicorders.ah, + before_subscript=indicorders.bs, + after_subscript=indicorders.as, + before_main=indicorders.bm, + after_main=indicorders.am, + } + indic=nil + indicmarks=nil + indicclasses=nil + indicorders=nil + characters.indicgroups=indicgroups +end +local consonant=indicgroups.consonant +local independent_vowel=indicgroups.independent_vowel +local dependent_vowel=indicgroups.dependent_vowel +local vowel_modifier=indicgroups.vowel_modifier +local stress_tone_mark=indicgroups.stress_tone_mark +local pre_mark=indicgroups.pre_mark +local above_mark=indicgroups.above_mark +local below_mark=indicgroups.below_mark +local post_mark=indicgroups.post_mark +local twopart_mark=indicgroups.twopart_mark +local nukta=indicgroups.nukta +local halant=indicgroups.halant +local ra=indicgroups.ra +local anudatta=indicgroups.anudatta +local before_postscript=indicgroups.before_postscript +local after_postscript=indicgroups.after_postscript +local before_half=indicgroups.before_half +local after_half=indicgroups.after_half +local before_subscript=indicgroups.before_subscript +local after_subscript=indicgroups.after_subscript +local before_main=indicgroups.before_main +local after_main=indicgroups.after_main +local mark_four=table.merged ( + pre_mark, + above_mark, + below_mark, + post_mark +) +local mark_above_below_post=table.merged ( + above_mark, + below_mark, + post_mark +) +local zw_char={ + [c_zwnj]=true, + [c_zwj ]=true, +} +local dflt_true={ + dflt=true +} +local two_defaults={ + dev2=dflt_true, +} +local one_defaults={ + dev2=dflt_true, + deva=dflt_true, +} +local false_flags={ false,false,false,false } +local sequence_reorder_matras={ + features={ dv01=two_defaults }, + flags=false_flags, + name="dv01_reorder_matras", + order={ "dv01" }, + type="devanagari_reorder_matras", + nofsteps=1, + steps={ + { + coverage=pre_mark, + } + } +} +local sequence_reorder_reph={ + features={ dv02=two_defaults }, + flags=false_flags, + name="dv02_reorder_reph", + order={ "dv02" }, + type="devanagari_reorder_reph", + nofsteps=1, + steps={ + { + coverage={}, + } + } +} +local sequence_reorder_pre_base_reordering_consonants={ + features={ dv03=two_defaults }, + flags=false_flags, + name="dv03_reorder_pre_base_reordering_consonants", + order={ "dv03" }, + type="devanagari_reorder_pre_base_reordering_consonants", + nofsteps=1, + steps={ + { + coverage={}, + } + } +} +local sequence_remove_joiners={ + features={ dv04=one_defaults }, + flags=false_flags, + name="dv04_remove_joiners", + order={ "dv04" }, + type="devanagari_remove_joiners", + nofsteps=1, + steps={ + { + coverage=zw_char, + }, + } +} +local basic_shaping_forms={ + akhn=true, + blwf=true, + cjct=true, + half=true, + nukt=true, + pref=true, + pstf=true, + rkrf=true, + rphf=true, + vatu=true, +} +local valid={ + abvs=true, + akhn=true, + blwf=true, + calt=true, + cjct=true, + half=true, + haln=true, + nukt=true, + pref=true, + pres=true, + pstf=true, + psts=true, + rkrf=true, + rphf=true, + vatu=true, + pres=true, + abvs=true, + blws=true, + psts=true, + haln=true, + calt=true, +} +local scripts={} +local scripts_one={ "deva","mlym","beng","gujr","guru","knda","orya","taml","telu" } +local scripts_two={ "dev2","mlm2","bng2","gjr2","gur2","knd2","ory2","tml2","tel2" } +local nofscripts=#scripts_one +for i=1,nofscripts do + local one=scripts_one[i] + local two=scripts_two[i] + scripts[one]=true + scripts[two]=true + two_defaults[one]=dflt_true + one_defaults[one]=dflt_true + one_defaults[two]=dflt_true +end +local function valid_one(s) for i=1,nofscripts do if s[scripts_one[i]] then return true end end end +local function valid_two(s) for i=1,nofscripts do if s[scripts_two[i]] then return true end end end +local function initializedevanagi(tfmdata) + local script,language=otf.scriptandlanguage(tfmdata,attr) + if scripts[script] then + local resources=tfmdata.resources + local devanagari=resources.devanagari + if not devanagari then + report("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"]=two_defaults + gsubfeatures["dv02"]=two_defaults + gsubfeatures["dv03"]=two_defaults + gsubfeatures["dv04"]=one_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 valid[kind] and valid_two(spec)then + for i=1,nofsteps do + local step=steps[i] + local coverage=step.coverage + if coverage then + local reph=false + if kind=="rphf" 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 + 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"]=two_defaults + insert(sequences,insertindex,sequence_reorder_pre_base_reordering_consonants) + elseif script=="taml" then + sharedfeatures["dv04"]=true +sharedfeatures["pstf"]=true + elseif script=="tml2" then + else + report("todo: enable the right features for script %a",script) + end + end + end +end +registerotffeature { + name="devanagari", + description="inject additional features", + default=true, + initializers={ + node=initializedevanagi, + }, +} +local show_syntax_errors=false +local function inject_syntax_error(head,current,char) + local signal=copy_node(current) + copyinjection(signal,current) + if pre_mark[char] then + setchar(signal,dotted_circle) + else + setchar(current,dotted_circle) + end + return insert_node_after(head,current,signal) +end +local function initialize_one(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 reorder_one(head,start,stop,font,attr,nbspaces) + local reph,vattu,blwfcache=initialize_one(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 + local char=getchar(current) + if nukta[char] then + current=getnext(current) + char=getchar(current) + end + if char==c_zwj and 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) + if show_syntax_errors then + head,current=inject_syntax_error(head,current,char) + end + 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 + 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) + ::step_1:: + ::step_2:: + 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 + ::step_3:: + ::step_4:: + 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) + setlink(getprev(current),start) + setlink(start,current) + start=startnext + startattr=getprop(start,a_syllabe) + break + end + current=getnext(current) + else + break + end + end + end + ::step_5:: + 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 not after_subscript[char] then + c=current + end + current=getnext(current) + else + break + end + end + if c then + startnext=getnext(start) + head=remove_node(head,start) + setlink(getprev(c),start) + setlink(start,c) + start=startnext + startattr=getprop(start,a_syllabe) + end + end + ::step_6:: + 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) + setlink(start,getnext(current)) + 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) + setlink(getprev(current),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 initialize_two(font,attr) + local devanagari=fontdata[font].resources.devanagari + if devanagari then + return devanagari.seqsubset or {},devanagari.reorderreph or {} + else + return {},{} + end +end +local function reorder_two(head,start,stop,font,attr,nbspaces) + local seqsubset,reorderreph=initialize_two(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=afternext + 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) + 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) + if show_syntax_errors then + head,current=inject_syntax_error(head,current,char) + end + 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 + setlink(getprev(halfpos),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 + setlink(current,getnext(target)) + 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 + setlink(getprev(c),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 anudatta[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 + 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 method_one(head,font,attr) + 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=reorder_one(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=reorder_one(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 + if show_syntax_errors then + local mark=mark_four[char] + if mark then + head,current=inject_syntax_error(head,current,char) + end + end + current=getnext(current) + end + end + else + current=getnext(current) + end + start=false + end + if nbspaces>0 then + head=replace_all_nbsp(head) + end + return head,done +end +local function method_two(head,font,attr) + 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=reorder_two(head,syllablestart,syllableend,font,attr,nbspaces) + end + if not syllableend and show_syntax_errors 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,char) + end + end + end + start=false + current=getnext(current) + end + if nbspaces>0 then + head=replace_all_nbsp(head) + end + return head,done +end +for i=1,nofscripts do + methods[scripts_one[i]]=method_one + methods[scripts_two[i]]=method_two +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-osd”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-ocl” 49cf3230228aa7f2d19cd491f55f5395] --- + +if not modules then modules={} end modules ['font-ocl']={ + 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 tostring,tonumber,next=tostring,tonumber,next +local round,max=math.round,math.round +local sortedkeys,sortedhash=table.sortedkeys,table.sortedhash +local setmetatableindex=table.setmetatableindex +local formatters=string.formatters +local tounicode=fonts.mappings.tounicode +local helpers=fonts.helpers +local charcommand=helpers.commands.char +local rightcommand=helpers.commands.right +local leftcommand=helpers.commands.left +local downcommand=helpers.commands.down +local otf=fonts.handlers.otf +local f_color=formatters["%.3f %.3f %.3f rg"] +local f_gray=formatters["%.3f 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 >> BDC", + "EMC" + end +end +local sharedpalettes={} +local hash=setmetatableindex(function(t,k) + local v={ "pdf","direct",k } + t[k]=v + return v +end) +if context then + local colors=attributes.list[attributes.private('color')] or {} + local transparencies=attributes.list[attributes.private('transparency')] or {} + function otf.registerpalette(name,values) + sharedpalettes[name]=values + for i=1,#values do + local v=values[i] + local c=nil + local t=nil + if type(v)=="table" then + c=colors.register(name,"rgb", + max(round((v.r or 0)*255),255)/255, + max(round((v.g or 0)*255),255)/255, + max(round((v.b or 0)*255),255)/255 + ) + else + c=colors[v] + t=transparencies[v] + end + if c and t then + values[i]=hash[lpdf.color(1,c).." "..lpdf.transparency(t)] + elseif c then + values[i]=hash[lpdf.color(1,c)] + elseif t then + values[i]=hash[lpdf.color(1,t)] + end + end + end +else + function otf.registerpalette(name,values) + sharedpalettes[name]=values + for i=1,#values do + local v=values[i] + values[i]=hash[f_color( + max(round((v.r or 0)*255),255)/255, + max(round((v.g or 0)*255),255)/255, + max(round((v.b or 0)*255),255)/255 + )] + end + end +end +local function convert(t,k) + local v={} + for i=1,#k do + local p=k[i] + local r,g,b=p[1],p[2],p[3] + if r==g and g==b then + v[i]=hash[f_gray(r/255)] + else + v[i]=hash[f_color(r/255,g/255,b/255)] + end + end + t[k]=v + return v +end +local start={ "pdf","mode","font" } +local push={ "pdf","page","q" } +local pop={ "pdf","page","Q" } +if not LUATEXFUNCTIONALITY or LUATEXFUNCTIONALITY<6472 then + start={ "nop" } +end +local function initialize(tfmdata,kind,value) + if value then + local resources=tfmdata.resources + local palettes=resources.colorpalettes + if palettes then + local converted=resources.converted + if not converted then + converted=setmetatableindex(convert) + resources.converted=converted + end + local colorvalues=sharedpalettes[value] or converted[palettes[tonumber(value) or 1] or palettes[1]] or {} + local classes=#colorvalues + if classes==0 then + return + end + local characters=tfmdata.characters + local descriptions=tfmdata.descriptions + local properties=tfmdata.properties + properties.virtualized=true + tfmdata.fonts={ + { id=0 } + } + local getactualtext=otf.getactualtext + local default=colorvalues[#colorvalues] + local b,e=getactualtext(tounicode(0xFFFD)) + local actualb={ "pdf","page",b } + local actuale={ "pdf","page",e } + for unicode,character in next,characters do + local description=descriptions[unicode] + if description then + local colorlist=description.colors + if colorlist then + local u=description.unicode or characters[unicode].unicode + local w=character.width or 0 + local s=#colorlist + local goback=w~=0 and leftcommand[w] or nil + local t={ + start, + not u and actualb or { "pdf","page",(getactualtext(tounicode(u))) } + } + local n=2 + local l=nil + local f=false + for i=1,s do + local entry=colorlist[i] + local v=colorvalues[entry.class] or default + if v and l~=v then + if f then + n=n+1 t[n]=pop + end + n=n+1 t[n]=push + f=true + n=n+1 t[n]=v + l=v + end + n=n+1 t[n]=charcommand[entry.slot] + if s>1 and i temp-otf-svg-shape.log","w") + end + end + function otfsvg.topdf(svgshapes) + local pdfshapes={} + local inkscape=runner() + if inkscape then + 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 + local nofdone=0 + 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 + nofdone=nofdone+1 + if nofdone%100==0 then + report_svg("%i shapes processed",nofdone) + end + 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() or "-") + end + end + return pdfshapes + end +end +local function initializesvg(tfmdata,kind,value) + if value and otf.svgenabled then + local svg=tfmdata.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 + pdftovirtual(tfmdata,pdfshapes,"svg") + end +end +fonts.handlers.otf.features.register { + name="svg", + description="svg glyphs", + manipulators={ + base=initializesvg, + node=initializesvg, + } +} +local otfsbix=otf.sbix or {} +otf.sbix=otfsbix +otf.sbixenabled=true +do + local report_sbix=logs.reporter("fonts","sbix conversion") + local loaddata=io.loaddata + local savedata=io.savedata + local remove=os.remove + local runner=sandbox and sandbox.registerrunner { + name="otfsbix", + program="gm", + template="convert -quality 100 temp-otf-sbix-shape.sbix temp-otf-sbix-shape.pdf > temp-otf-svg-shape.log", + } + if not runner then + runner=function() + return os.execute("gm convert -quality 100 temp-otf-sbix-shape.sbix temp-otf-sbix-shape.pdf > temp-otf-svg-shape.log") + end + end + function otfsbix.topdf(sbixshapes) + local pdfshapes={} + local sbixfile="temp-otf-sbix-shape.sbix" + local pdffile="temp-otf-sbix-shape.pdf" + local nofdone=0 + local indices=sortedkeys(sbixshapes) + local nofindices=#indices + report_sbix("processing %i sbix containers",nofindices) + statistics.starttiming() + for i=1,nofindices do + local index=indices[i] + local entry=sbixshapes[index] + local data=entry.data + local x=entry.x + local y=entry.y + savedata(sbixfile,data) + runner() + pdfshapes[index]={ + x=x~=0 and x or nil, + y=y~=0 and y or nil, + data=loaddata(pdffile), + } + nofdone=nofdone+1 + if nofdone%100==0 then + report_sbix("%i shapes processed",nofdone) + end + end + report_sbix("processing %i pdf results",nofindices) + remove(sbixfile) + remove(pdffile) + statistics.stoptiming() + if statistics.elapsedseconds then + report_sbix("sbix conversion time %s",statistics.elapsedseconds() or "-") + end + return pdfshapes + end +end +local function initializesbix(tfmdata,kind,value) + if value and otf.sbixenabled then + local sbix=tfmdata.properties.sbix + local hash=sbix and sbix.hash + local timestamp=sbix and sbix.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 sbixfile=containers.read(otf.sbixcache,hash) + local sbixshapes=sbixfile and sbixfile.sbixshapes + pdfshapes=sbixshapes and otfsbix.topdf(sbixshapes) or {} + containers.write(otf.pdfcache,hash,{ + pdfshapes=pdfshapes, + timestamp=timestamp, + }) + end + pdftovirtual(tfmdata,pdfshapes,"sbix") + end +end +fonts.handlers.otf.features.register { + name="sbix", + description="sbix glyphs", + manipulators={ + base=initializesbix, + node=initializesbix, + } +} + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-ocl”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-otc” 9dcd93d83bc61c90f0fb6161a1911a55] --- + +if not modules then modules={} end modules ['font-otc']={ + 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 insert,sortedkeys,sortedhash,tohash=table.insert,table.sortedkeys,table.sortedhash,table.tohash +local type,next=type,next +local lpegmatch=lpeg.match +local utfbyte,utflen=utf.byte,utf.len +local sortedhash=table.sortedhash +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 checkmerge=fonts.helpers.checkmerge +local checkflags=fonts.helpers.checkflags +local checksteps=fonts.helpers.checksteps +local normalized={ + substitution="substitution", + single="substitution", + ligature="ligature", + alternate="alternate", + multiple="multiple", + kern="kern", + pair="pair", + single="single", + chainsubstitution="chainsubstitution", + chainposition="chainposition", +} +local types={ + substitution="gsub_single", + ligature="gsub_ligature", + alternate="gsub_alternate", + multiple="gsub_multiple", + kern="gpos_pair", + pair="gpos_pair", + single="gpos_single", + 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,nocheck) + local coverage={} + local cover=coveractions[featuretype] + for code,replacement in next,list do + local unicode=tounicode(code) + local description=descriptions[unicode] + if not nocheck and not description then + skip=skip+1 + else + 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 + end + end + return coverage + end + local function prepare_alternate(list,featuretype,nocheck) + local coverage={} + local cover=coveractions[featuretype] + for code,replacement in next,list do + local unicode=tounicode(code) + local description=descriptions[unicode] + if not nocheck and 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]=(nocheck or 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,nocheck) + local coverage={} + local cover=coveractions[featuretype] + for code,replacement in next,list do + local unicode=tounicode(code) + local description=descriptions[unicode] + if not nocheck and 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 nocheck or 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,nocheck) + local coverage={} + local cover=coveractions[featuretype] + for code,ligature in next,list do + local unicode=tounicode(code) + local description=descriptions[unicode] + if not nocheck and not description then + skip=skip+1 + else + 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 nocheck or 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 + end + end + return coverage + end + local function resetspacekerns() + data.properties.hasspacekerns=true + data.resources .spacekerns=nil + end + local function prepare_kern(list,featuretype) + local coverage={} + local cover=coveractions[featuretype] + local isspace=false + 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 + if u==32 then + isspace=true + end + end + end + if next(r) then + cover(coverage,unicode,r) + done=done+1 + if unicode==32 then + isspace=true + end + else + skip=skip+1 + end + else + skip=skip+1 + end + end + if isspace then + resetspacekerns() + 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 + if u==32 then + isspace=true + end + end + end + if next(r) then + cover(coverage,unicode,r) + done=done+1 + if unicode==32 then + isspace=true + end + else + skip=skip+1 + end + else + skip=skip+1 + end + end + if isspace then + resetspacekerns() + end + else + report_otf("unknown cover type %a",featuretype) + end + return coverage + end + local prepare_single=prepare_pair + local function prepare_chain(list,featuretype,sublookups) + local rules=list.rules + local coverage={} + if rules then + local rulehash={} + local rulesize=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 sortedhash(lookups) do + local t=type(v) + if t=="table" then + for i=1,#v do + local vi=v[i] + if type(vi)~="table" then + v[i]={ vi } + end + end + elseif t=="number" then + local lookup=sublookups[v] + if lookup then + lookups[k]={ lookup } + if not subtype then + subtype=lookup.type + end + elseif v==0 then + lookups[k]={ { type="gsub_remove" } } + else + lookups[k]=false + end + else + lookups[k]=false + 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 sortedhash(sequence[start]) do + local cu=coverage[unic] + if not cu then + coverage[unic]=rulehash + end + end + sequence.n=nofsequences + end + end + rulehash.n=rulesize + 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 sortedhash(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 position0 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, + } + checkflags(sequence,resources) + checkmerge(sequence) + checksteps(sequence) + 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 not slot then + slot=#extrafeatures+1 + knownfeatures[name]=slot + elseif specification.overload==false then + slot=#extrafeatures+1 + knownfeatures[name]=slot + else + 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) + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-otc”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-onr” cf93eb4ab34461d2b3797792dbdb035f] --- + +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.513 +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","AZ","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,filename) + local forward=position+tonumber(size) + local stream=decrypt(sub(str,position+1,forward),4330,4) + routines[index]={ byte(stream,1,#stream) } + return forward + end + local setvector=function(str,position,name,size,filename) + local forward=position+tonumber(size) + if n>=m then + return #str + elseif forward<#str then + if n==0 and name~=".notdef" then + report_pfb("reserving .notdef at index 0 in %a",filename) + n=n+1 + end + vector[n]=name + n=n+1 + return forward + else + return #str + end + end + local setshapes=function(str,position,name,size,filename) + local forward=position+tonumber(size) + local stream=sub(str,position+1,forward) + if n>m then + return #str + elseif forward<#str then + if n==0 and name~=".notdef" then + report_pfb("reserving .notdef at index 0 in %a",filename) + n=n+1 + end + 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*Carg(1),setroutine)*p_np+P(1))^1 + local p_filtershapes= + (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal*p_rd*Carg(1),setshapes)*p_nd+P(1))^1 + local p_filternames=Ct ( + (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal*Carg(1),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-",1,true) or find(data,"%!FontType1",1,true)) 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,1,filename) + lpegmatch(p_filtershapes,binary,1,filename) + local data={ + dictionaries={ + { + charstrings=chars, + charset=vector, + subroutines=routines, + } + }, + } + fonts.handlers.otf.readers.parsecharstrings(false,data,glyphs,true,true) + else + lpegmatch(p_filternames,binary,1,filename) + 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=0,#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 + else + if trace_indexing then + report_afm("glyph %a has index %a but no data",name,index) + end + 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 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=resolvers.findfile(file.replacesuffix(file.nameonly(afmname),"pfb")) + end + if pfbname and pfbname~="" then + data.resources.filename=resolvers.unresolve(pfbname) + get_indexes(data,pfbname) + return data + else + report_afm("no pfb file for %a",afmname) + end + 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-2018-09-24.lua scope for “font-onr”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-one” 52588c7c3aab714d95d03e91ee86e1ad] --- + +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,gsub=string.match,string.gsub +local abs=math.abs +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 sortedhash=table.sortedhash +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 privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000 +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.513 +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 and attr.size or 0,attr and 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=data.private or privateoffset + local descriptions=data.descriptions + for name,blob in sortedhash(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 + data.private=private + local resources=data.resources + local filename=resources.filename or file.removesuffix(file.basename(filename)) + resources.filename=resolvers.unresolve(filename) + resources.unicodes=unicodes + resources.marks={} +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 + properties.private=properties.private or data.private or privateoffset + 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-2018-09-24.lua scope for “font-one”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-afk” 9da14e0fb22129c053acc599d1312544] --- + +if not modules then modules={} end modules ['font-afk']={ + 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", + dataonly=true, +} +local allocate=utilities.storage.allocate +fonts.handlers.afm.helpdata={ + ligatures=allocate { + ['f']={ + { 'f','ff' }, + { 'i','fi' }, + { 'l','fl' }, + }, + ['ff']={ + { 'i','ffi' } + }, + ['fi']={ + { 'i','fii' } + }, + ['fl']={ + { 'i','fli' } + }, + ['s']={ + { 't','st' } + }, + ['i']={ + { 'j','ij' } + }, + }, + texligatures=allocate { + ['quoteleft']={ + { 'quoteleft','quotedblleft' } + }, + ['quoteright']={ + { 'quoteright','quotedblright' } + }, + ['hyphen']={ + { 'hyphen','endash' } + }, + ['endash']={ + { 'hyphen','emdash' } + } + }, + leftkerned=allocate { + AEligature="A",aeligature="a", + OEligature="O",oeligature="o", + IJligature="I",ijligature="i", + AE="A",ae="a", + OE="O",oe="o", + IJ="I",ij="i", + Ssharp="S",ssharp="s", + }, + rightkerned=allocate { + AEligature="E",aeligature="e", + OEligature="E",oeligature="e", + IJligature="J",ijligature="j", + AE="E",ae="e", + OE="E",oe="e", + IJ="J",ij="j", + Ssharp="S",ssharp="s", + }, + bothkerned=allocate { + Acircumflex="A",acircumflex="a", + Ccircumflex="C",ccircumflex="c", + Ecircumflex="E",ecircumflex="e", + Gcircumflex="G",gcircumflex="g", + Hcircumflex="H",hcircumflex="h", + Icircumflex="I",icircumflex="i", + Jcircumflex="J",jcircumflex="j", + Ocircumflex="O",ocircumflex="o", + Scircumflex="S",scircumflex="s", + Ucircumflex="U",ucircumflex="u", + Wcircumflex="W",wcircumflex="w", + Ycircumflex="Y",ycircumflex="y", + Agrave="A",agrave="a", + Egrave="E",egrave="e", + Igrave="I",igrave="i", + Ograve="O",ograve="o", + Ugrave="U",ugrave="u", + Ygrave="Y",ygrave="y", + Atilde="A",atilde="a", + Itilde="I",itilde="i", + Otilde="O",otilde="o", + Utilde="U",utilde="u", + Ntilde="N",ntilde="n", + Adiaeresis="A",adiaeresis="a",Adieresis="A",adieresis="a", + Ediaeresis="E",ediaeresis="e",Edieresis="E",edieresis="e", + Idiaeresis="I",idiaeresis="i",Idieresis="I",idieresis="i", + Odiaeresis="O",odiaeresis="o",Odieresis="O",odieresis="o", + Udiaeresis="U",udiaeresis="u",Udieresis="U",udieresis="u", + Ydiaeresis="Y",ydiaeresis="y",Ydieresis="Y",ydieresis="y", + Aacute="A",aacute="a", + Cacute="C",cacute="c", + Eacute="E",eacute="e", + Iacute="I",iacute="i", + Lacute="L",lacute="l", + Nacute="N",nacute="n", + Oacute="O",oacute="o", + Racute="R",racute="r", + Sacute="S",sacute="s", + Uacute="U",uacute="u", + Yacute="Y",yacute="y", + Zacute="Z",zacute="z", + Dstroke="D",dstroke="d", + Hstroke="H",hstroke="h", + Tstroke="T",tstroke="t", + Cdotaccent="C",cdotaccent="c", + Edotaccent="E",edotaccent="e", + Gdotaccent="G",gdotaccent="g", + Idotaccent="I",idotaccent="i", + Zdotaccent="Z",zdotaccent="z", + Amacron="A",amacron="a", + Emacron="E",emacron="e", + Imacron="I",imacron="i", + Omacron="O",omacron="o", + Umacron="U",umacron="u", + Ccedilla="C",ccedilla="c", + Kcedilla="K",kcedilla="k", + Lcedilla="L",lcedilla="l", + Ncedilla="N",ncedilla="n", + Rcedilla="R",rcedilla="r", + Scedilla="S",scedilla="s", + Tcedilla="T",tcedilla="t", + Ohungarumlaut="O",ohungarumlaut="o", + Uhungarumlaut="U",uhungarumlaut="u", + Aogonek="A",aogonek="a", + Eogonek="E",eogonek="e", + Iogonek="I",iogonek="i", + Uogonek="U",uogonek="u", + Aring="A",aring="a", + Uring="U",uring="u", + Abreve="A",abreve="a", + Ebreve="E",ebreve="e", + Gbreve="G",gbreve="g", + Ibreve="I",ibreve="i", + Obreve="O",obreve="o", + Ubreve="U",ubreve="u", + Ccaron="C",ccaron="c", + Dcaron="D",dcaron="d", + Ecaron="E",ecaron="e", + Lcaron="L",lcaron="l", + Ncaron="N",ncaron="n", + Rcaron="R",rcaron="r", + Scaron="S",scaron="s", + Tcaron="T",tcaron="t", + Zcaron="Z",zcaron="z", + dotlessI="I",dotlessi="i", + dotlessJ="J",dotlessj="j", + AEligature="AE",aeligature="ae",AE="AE",ae="ae", + OEligature="OE",oeligature="oe",OE="OE",oe="oe", + IJligature="IJ",ijligature="ij",IJ="IJ",ij="ij", + Lstroke="L",lstroke="l",Lslash="L",lslash="l", + Ostroke="O",ostroke="o",Oslash="O",oslash="o", + Ssharp="SS",ssharp="ss", + Aumlaut="A",aumlaut="a", + Eumlaut="E",eumlaut="e", + Iumlaut="I",iumlaut="i", + Oumlaut="O",oumlaut="o", + Uumlaut="U",uumlaut="u", + } +} + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-afk”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-tfm” c9797c1b4ccb8fdb7f041e19207109a2] --- + +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 helpers=fonts.helpers +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 +local charcommand=helpers.commands.char +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) + properties.private=properties.private or tfmdata.private or privateoffset + 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=tfmdata or 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,charcommand[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 + tfmdata.private=private + 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> + 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 flushstreamobject then + else + 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-2018-09-24.lua scope for “font-tfm”] --- + + +do --- [luaotfload, fontloader-2018-09-24.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-2018-09-24.lua scope for “font-lua”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-def” 8e2cb2552bf02246da2ac43334b91795] --- + +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,basename=file.suffix,file.removesuffix,file.basename +local formatters=string.formatters +local sortedhash,sortedkeys=table.sortedhash,table.sortedkeys +local allocate=utilities.storage.allocate +local trace_defining=false trackers .register("fonts.defining",function(v) trace_defining=v end) +local directive_embedall=false directives.register("fonts.embedall",function(v) directive_embedall=v end) +trackers.register("fonts.loading","fonts.defining","otf.loading","afm.loading","tfm.loading") +local report_defining=logs.reporter("fonts","defining") +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 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 +if context then + local splitter,splitspecifiers=nil,"" + local P,C,S,Cc,Cs=lpeg.P,lpeg.C,lpeg.S,lpeg.Cc,lpeg.Cs + local left=P("(") + local right=P(")") + local colon=P(":") + local space=P(" ") + local lbrace=P("{") + local rbrace=P("}") + definers.defaultlookup="file" + local prefixpattern=P(false) + local function addspecifier(symbol) + splitspecifiers=splitspecifiers..symbol + local method=S(splitspecifiers) + local lookup=C(prefixpattern)*colon + local sub=left*C(P(1-left-right-method)^1)*right + local specification=C(method)*C(P(1)^1) + local name=Cs((lbrace/"")*(1-rbrace)^1*(rbrace/"")+(1-sub-specification)^1) + splitter=P((lookup+Cc(""))*name*(sub+Cc(""))*(specification+Cc(""))) + end + local function addlookup(str) + prefixpattern=prefixpattern+P(str) + end + definers.addlookup=addlookup + addlookup("file") + addlookup("name") + addlookup("spec") + local function getspecification(str) + return lpegmatch(splitter,str or "") + end + definers.getspecification=getspecification + function definers.registersplit(symbol,action,verbosename) + addspecifier(symbol) + variants[symbol]=action + if verbosename then + variants[verbosename]=action + end + end + function definers.analyze(specification,size) + local lookup,name,sub,method,detail=getspecification(specification or "") + return makespecification(specification,lookup,name,sub,method,detail,size) + end +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,instance=resolve(specification.name,specification.sub,specification) + if resolved then + specification.resolved=resolved + specification.sub=sub + specification.subindex=subindex + if instance then + specification.instance=instance + local features=specification.features + if not features then + features={} + specification.features=features + end + local normal=features.normal + if not normal then + normal={} + features.normal=normal + end + normal.instance=instance + if not callbacks.supported.glyph_stream_provider then + normal.variableshapes=true + end + end + local suffix=lower(suffixonly(resolved)) + if fonts.formats[suffix] then + specification.forced=suffix + specification.forcedname=resolved + specification.name=removesuffix(resolved) + else + specification.name=resolved + end + end + else + resolvers.file(specification) + end +end +function resolvers.spec(specification) + local resolvespec=fonts.names.resolvespec + if resolvespec then + local resolved,sub,subindex=resolvespec(specification.name,specification.sub,specification) + 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 +local function checkfeatures(tfmdata) + local resources=tfmdata.resources + local shared=tfmdata.shared + if resources and shared then + local features=resources.features + local usedfeatures=shared.features + if features and usedfeatures then + local usedlanguage=usedfeatures.language or "dflt" + local usedscript=usedfeatures.script or "dflt" + local function check(what) + if what then + local foundlanguages={} + for feature,scripts in next,what do + if usedscript=="auto" or scripts["*"] then + elseif not scripts[usedscript] then + else + for script,languages in next,scripts do + if languages["*"] then + elseif not languages[usedlanguage] then + report_defining("font %!font:name!, feature %a, script %a, no language %a", + tfmdata,feature,script,usedlanguage) + end + end + end + for script,languages in next,scripts do + for language in next,languages do + foundlanguages[language]=true + end + end + end + if false then + foundlanguages["*"]=nil + foundlanguages=sortedkeys(foundlanguages) + for feature,scripts in sortedhash(what) do + for script,languages in next,scripts do + if not languages["*"] then + for i=1,#foundlanguages do + local language=foundlanguages[i] + if not languages[language] then + report_defining("font %!font:name!, feature %a, script %a, no language %a", + tfmdata,feature,script,language) + end + end + end + end + end + end + end + end + check(features.gsub) + check(features.gpos) + end + end +end +function definers.loadfont(specification) + local hash=constructors.hashinstance(specification) + 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 + checkfeatures(tfmdata) + end + end + if not tfmdata then + report_defining("font with asked name %a is not found using lookup %a",specification.name,specification.lookup) + end + return tfmdata +end +function constructors.readanddefine(name,size) + local specification=definers.analyze(name,size) + local method=specification.method + if method and variants[method] then + specification=variants[method](specification) + end + specification=definers.resolve(specification) + local hash=constructors.hashinstance(specification) + local id=definers.registered(hash) + if not id then + local tfmdata=definers.loadfont(specification) + if tfmdata then + tfmdata.properties.hash=hash + id=font.define(tfmdata) + definers.register(tfmdata,id) + else + id=0 + 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 or "-",properties.name,parameters.size,properties.encodingbytes, + properties.encodingname,properties.fullname,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-2018-09-24.lua scope for “font-def”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “fonts-def” f435e0875f203f343157baeff876ec9c] --- + +if not modules then modules={} end modules ['luatex-fonts-def']={ + version=1.001, + comment="companion to luatex-*.tex", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then + os.exit() +end +local fonts=fonts +fonts.constructors.namemode="specification" +function fonts.definers.getspecification(str) + return "",str,"",":",str +end +local list={} +local function issome () list.lookup='name' end +local function isfile () list.lookup='file' end +local function isname () list.lookup='name' end +local function thename(s) list.name=s end +local function issub (v) list.sub=v end +local function iscrap (s) list.crap=string.lower(s) end +local function iskey (k,v) list[k]=v end +local function istrue (s) list[s]=true end +local function isfalse(s) list[s]=false end +local P,S,R,C,Cs=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cs +local spaces=P(" ")^0 +local namespec=Cs((P("{")/"")*(1-S("}"))^0*(P("}")/"")+(1-S("/:("))^0) +local crapspec=spaces*P("/")*(((1-P(":"))^0)/iscrap)*spaces +local filename_1=P("file:")/isfile*(namespec/thename) +local filename_2=P("[")*P(true)/isfile*(((1-P("]"))^0)/thename)*P("]") +local fontname_1=P("name:")/isname*(namespec/thename) +local fontname_2=P(true)/issome*(namespec/thename) +local sometext=R("az","AZ","09")^1 +local somekey=R("az","AZ","09")^1 +local somevalue=(P("{")/"")*(1-P("}"))^0*(P("}")/"")+(1-S(";"))^1 +local truevalue=P("+")*spaces*(sometext/istrue) +local falsevalue=P("-")*spaces*(sometext/isfalse) +local keyvalue=(C(somekey)*spaces*P("=")*spaces*C(somevalue))/iskey +local somevalue=sometext/istrue +local subvalue=P("(")*(C(P(1-S("()"))^1)/issub)*P(")") +local option=spaces*(keyvalue+falsevalue+truevalue+somevalue)*spaces +local options=P(":")*spaces*(P(";")^0*option)^0 +local pattern=(filename_1+filename_2+fontname_1+fontname_2)*subvalue^0*crapspec^0*options^0 +function fonts.definers.analyze(str,size) + local specification=fonts.definers.makespecification(str,nil,nil,nil,":",nil,size) + list={} + lpeg.match(pattern,str) + list.crap=nil + if list.name then + specification.name=list.name + list.name=nil + end + if list.lookup then + specification.lookup=list.lookup + list.lookup=nil + end + if list.sub then + specification.sub=list.sub + list.sub=nil + end + specification.features.normal=fonts.handlers.otf.features.normalize(list) + list=nil + return specification +end +function fonts.definers.applypostprocessors(tfmdata) + local postprocessors=tfmdata.postprocessors + if postprocessors then + for i=1,#postprocessors do + local extrahash=postprocessors[i](tfmdata) + if type(extrahash)=="string" and extrahash~="" then + extrahash=string.gsub(lower(extrahash),"[^a-z]","-") + tfmdata.properties.fullname=format("%s-%s",tfmdata.properties.fullname,extrahash) + end + end + end + return tfmdata +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “fonts-def”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “fonts-ext” 32013cbc5d5d336be8b1d1e5879d86c4] --- + +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 + os.exit() +end +local byte=string.byte +local fonts=fonts +local handlers=fonts.handlers +local otf=handlers.otf +local afm=handlers.afm +local registerotffeature=otf.features.register +local registerafmfeature=afm.features.register +function fonts.loggers.onetimemessage() end +fonts.protrusions=fonts.protrusions or {} +fonts.protrusions.setups=fonts.protrusions.setups or {} +local setups=fonts.protrusions.setups +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 }, +} +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 +local specification={ + name="protrusion", + description="shift characters into the left and or right margin", + initializers={ + base=initializeprotrusion, + node=initializeprotrusion, + } +} +registerotffeature(specification) +registerafmfeature(specification) +fonts.expansions=fonts.expansions or {} +fonts.expansions.setups=fonts.expansions.setups or {} +local setups=fonts.expansions.setups +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, +} +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 +local specification={ + name="expansion", + description="apply hz optimization", + initializers={ + base=initializeexpansion, + node=initializeexpansion, + } +} +registerotffeature(specification) +registerafmfeature(specification) +if not otf.features.normalize then + otf.features.normalize=function(t) + if t.rand then + t.rand="random" + end + return t + end +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 initialize(tfmdata,value) + tfmdata.postprocessors=tfmdata.postprocessors or {} + table.insert(tfmdata.postprocessors, + function(tfmdata) + return specialreencode(tfmdata,value) + end + ) +end +registerotffeature { + name="reencode", + description="reencode characters", + manipulators={ + base=initialize, + node=initialize, + } +} +local function initialize(tfmdata,key,value) + if value then + tfmdata.mathparameters=nil + end +end +registerotffeature { + name="ignoremathconstants", + description="ignore math constants table", + initializers={ + base=initialize, + node=initialize, + } +} + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “fonts-ext”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-imp-tex” ceb087ef6fa2f89aed7179f60ddf8f35] --- + +if not modules then modules={} end modules ['font-imp-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 next=next +local fonts=fonts +local otf=fonts.handlers.otf +local registerotffeature=otf.features.register +local addotffeature=otf.addfeature +local specification={ + type="ligature", + order={ "tlig" }, + prepend=true, + data={ + [0x2013]={ 0x002D,0x002D }, + [0x2014]={ 0x002D,0x002D,0x002D }, + }, +} +addotffeature("tlig",specification) +registerotffeature { + name="tlig", + description="tex ligatures", +} +local specification={ + type="substitution", + order={ "trep" }, + prepend=true, + data={ + [0x0027]=0x2019, + }, +} +addotffeature("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 specification={ + { + type="substitution", + features={ arab={ urd=true,dflt=true } }, + order={ "anum" }, + data=anum_arabic, + valid=valid, + }, + { + type="substitution", + features={ arab={ urd=true } }, + order={ "anum" }, + data=anum_persian, + valid=valid, + }, +} +addotffeature("anum",specification) +registerotffeature { + name="anum", + description="arabic digits", +} + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-imp-tex”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-imp-ligatures” 26ffcf089391445f7af59536c8814364] --- + +if not modules then modules={} end modules ['font-imp-ligatures']={ + 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 lpegmatch=lpeg.match +local utfsplit=utf.split +local settings_to_array=utilities.parsers.settings_to_array +local fonts=fonts +local otf=fonts.handlers.otf +local registerotffeature=otf.features.register +local addotffeature=otf.addfeature +local lookups={} +local protect={} +local revert={} +local zwjchar=0x200C +local zwj={ zwjchar } +addotffeature { + name="blockligatures", + type="chainsubstitution", + nocheck=true, + prepend=true, + future=true, + lookups={ + { + type="multiple", + data=lookups, + }, + }, + data={ + rules=protect, + } +} +addotffeature { + name="blockligatures", + type="chainsubstitution", + nocheck=true, + append=true, + overload=false, + lookups={ + { + type="ligature", + data=lookups, + }, + }, + data={ + rules=revert, + } +} +registerotffeature { + name='blockligatures', + description='block certain ligatures', +} +local splitter=lpeg.splitat(":") +local function blockligatures(str) + local t=settings_to_array(str) + for i=1,#t do + local ti=t[i] + local before,current,after=lpegmatch(splitter,ti) + if current and after then + if before then + before=utfsplit(before) + for i=1,#before do + before[i]={ before[i] } + end + end + if current then + current=utfsplit(current) + end + if after then + after=utfsplit(after) + for i=1,#after do + after[i]={ after[i] } + end + end + else + before=nil + current=utfsplit(ti) + after=nil + end + if #current>1 then + local one=current[1] + local two=current[2] + lookups[one]={ one,zwjchar } + local one={ one } + local two={ two } + local new=#protect+1 + protect[new]={ + before=before, + current={ one,two }, + after=after, + lookups={ 1 }, + } + revert[new]={ + current={ one,zwj }, + after={ two }, + lookups={ 1 }, + } + end + end +end +otf.helpers.blockligatures=blockligatures +if context then + interfaces.implement { + name="blockligatures", + arguments="string", + actions=blockligatures, + } +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-imp-ligatures”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-imp-italics” c2e6d6c4096a2c30d68fbffd3d7d58a7] --- + +if not modules then modules={} end modules ['font-imp-italics']={ + version=1.001, + comment="companion to font-ini.mkiv and hand-ini.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local next=next +local fonts=fonts +local handlers=fonts.handlers +local registerotffeature=handlers.otf.features.register +local registerafmfeature=handlers.afm.features.register +local function initialize(tfmdata,key,value) + for unicode,character in next,tfmdata.characters do + local olditalic=character.italic + if olditalic and olditalic~=0 then + character.width=character.width+olditalic + character.italic=0 + end + end +end +local specification={ + name="italicwidths", + description="add italic to width", + manipulators={ + base=initialize, + node=initialize, + } +} +registerotffeature(specification) +registerafmfeature(specification) +local function initialize(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 +local specification={ + name="itlc", + description="italic correction", + initializers={ + base=initialize, + node=initialize, + } +} +registerotffeature(specification) +registerafmfeature(specification) +if context then + local function initialize(tfmdata,value) + tfmdata.properties.textitalics=toboolean(value) + end + local specification={ + name="textitalics", + description="use alternative text italic correction", + initializers={ + base=initialize, + node=initialize, + } + } + registerotffeature(specification) + registerafmfeature(specification) +end +if context then + local letter=characters.is_letter + local always=true + local function collapseitalics(tfmdata,key,value) + local threshold=value==true and 100 or tonumber(value) + if threshold and threshold>0 then + if threshold>100 then + threshold=100 + end + for unicode,data in next,tfmdata.characters do + if always or letter[unicode] or letter[data.unicode] then + local italic=data.italic + if italic and italic~=0 then + local width=data.width + if width and width~=0 then + local delta=threshold*italic/100 + data.width=width+delta + data.italic=italic-delta + end + end + end + end + end + end + local dimensions_specification={ + name="collapseitalics", + description="collapse italics", + manipulators={ + base=collapseitalics, + node=collapseitalics, + } + } + registerotffeature(dimensions_specification) + registerafmfeature(dimensions_specification) +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-imp-italics”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “font-imp-effects” 3327181ef3db6f44dd234ad66ccc3f38] --- + +if not modules then modules={} end modules ['font-imp-effects']={ + version=1.001, + comment="companion to font-ini.mkiv and hand-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 is_boolean=string.is_boolean +local fonts=fonts +local handlers=fonts.handlers +local registerotffeature=handlers.otf.features.register +local registerafmfeature=handlers.afm.features.register +local settings_to_hash=utilities.parsers.settings_to_hash_colon_too +local helpers=fonts.helpers +local prependcommands=helpers.prependcommands +local charcommand=helpers.commands.char +local leftcommand=helpers.commands.left +local rightcommand=helpers.commands.right +local upcommand=helpers.commands.up +local downcommand=helpers.commands.down +local dummycommand=helpers.commands.dummy +local report_effect=logs.reporter("fonts","effect") +local report_slant=logs.reporter("fonts","slant") +local report_extend=logs.reporter("fonts","extend") +local report_squeeze=logs.reporter("fonts","squeeze") +local trace=false +trackers.register("fonts.effect",function(v) trace=v end) +trackers.register("fonts.slant",function(v) trace=v end) +trackers.register("fonts.extend",function(v) trace=v end) +trackers.register("fonts.squeeze",function(v) trace=v end) +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 + if trace then + report_slant("applying %0.3f",value) + end + tfmdata.parameters.slantfactor=value +end +local specification={ + name="slant", + description="slant glyphs", + initializers={ + base=initializeslant, + node=initializeslant, + } +} +registerotffeature(specification) +registerafmfeature(specification) +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 + if trace then + report_extend("applying %0.3f",value) + end + tfmdata.parameters.extendfactor=value +end +local specification={ + name="extend", + description="scale glyphs horizontally", + initializers={ + base=initializeextend, + node=initializeextend, + } +} +registerotffeature(specification) +registerafmfeature(specification) +local function initializesqueeze(tfmdata,value) + value=tonumber(value) + if not value then + value=0 + elseif value>10 then + value=10 + elseif value<-10 then + value=-10 + end + if trace then + report_squeeze("applying %0.3f",value) + end + tfmdata.parameters.squeezefactor=value +end +local specification={ + name="squeeze", + description="scale glyphs vertically", + initializers={ + base=initializesqueeze, + node=initializesqueeze, + } +} +registerotffeature(specification) +registerafmfeature(specification) +local effects={ + inner=0, + normal=0, + outer=1, + outline=1, + both=2, + hidden=3, +} +local function initializeeffect(tfmdata,value) + local spec + if type(value)=="number" then + spec={ width=value } + else + spec=settings_to_hash(value) + end + local effect=spec.effect or "both" + local width=tonumber(spec.width) or 0 + local mode=effects[effect] + if not mode then + report_effect("invalid effect %a",effect) + elseif width==0 and mode==0 then + report_effect("invalid width %a for effect %a",width,effect) + else + local parameters=tfmdata.parameters + local properties=tfmdata.properties + parameters.mode=mode + parameters.width=width*1000 + if is_boolean(spec.auto)==true then + local squeeze=1-width/20 + local average=(1-squeeze)*width*100 + spec.squeeze=squeeze + spec.extend=1+width/2 + spec.wdelta=average + spec.hdelta=average/2 + spec.ddelta=average/2 + spec.vshift=average/2 + end + local factor=tonumber(spec.factor) or 0 + local hfactor=tonumber(spec.hfactor) or factor + local vfactor=tonumber(spec.vfactor) or factor + local delta=tonumber(spec.delta) or 1 + local wdelta=tonumber(spec.wdelta) or delta + local hdelta=tonumber(spec.hdelta) or delta + local ddelta=tonumber(spec.ddelta) or hdelta + local vshift=tonumber(spec.vshift) or 0 + local slant=spec.slant + local extend=spec.extend + local squeeze=spec.squeeze + if slant then + initializeslant(tfmdata,slant) + end + if extend then + initializeextend(tfmdata,extend) + end + if squeeze then + initializesqueeze(tfmdata,squeeze) + end + properties.effect={ + effect=effect, + width=width, + factor=factor, + hfactor=hfactor, + vfactor=vfactor, + wdelta=wdelta, + hdelta=hdelta, + ddelta=ddelta, + vshift=vshift, + slant=tfmdata.parameters.slantfactor, + extend=tfmdata.parameters.extendfactor, + squeeze=tfmdata.parameters.squeezefactor, + } + end +end +local rules={ + "RadicalRuleThickness", + "OverbarRuleThickness", + "FractionRuleThickness", + "UnderbarRuleThickness", +} +local function setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze) + if delta~=0 then + for i=1,#rules do + local name=rules[i] + local value=mathparameters[name] + if value then + mathparameters[name]=(squeeze or 1)*(value+dx) + end + end + end +end +local function setmathcharacters(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta) + local function wdpatch(char) + if wsnap~=0 then + char.width=char.width+wdelta/2 + end + end + local function htpatch(char) + if hsnap~=0 then + local height=char.height + if height then + char.height=char.height+2*dy + end + end + end + local character=characters[0x221A] + if character and character.next then + local char=character + local next=character.next + wdpatch(char) + htpatch(char) + while next do + char=characters[next] + wdpatch(char) + htpatch(char) + next=char.next + end + if char then + local v=char.vert_variants + if v then + local top=v[#v] + if top then + local char=characters[top.glyph] + htpatch(char) + end + end + end + end +end +local function manipulateeffect(tfmdata) + local effect=tfmdata.properties.effect + if effect then + local characters=tfmdata.characters + local parameters=tfmdata.parameters + local mathparameters=tfmdata.mathparameters + local multiplier=effect.width*100 + local factor=parameters.factor + local hfactor=parameters.hfactor + local vfactor=parameters.vfactor + local wdelta=effect.wdelta*hfactor*multiplier + local hdelta=effect.hdelta*vfactor*multiplier + local ddelta=effect.ddelta*vfactor*multiplier + local vshift=effect.vshift*vfactor*multiplier + local squeeze=effect.squeeze + local hshift=wdelta/2 + local dx=multiplier*vfactor + local dy=vshift + local factor=(1+effect.factor)*factor + local hfactor=(1+effect.hfactor)*hfactor + local vfactor=(1+effect.vfactor)*vfactor + local vshift=vshift~=0 and upcommand[vshift] or false + for unicode,character in next,characters do + local oldwidth=character.width + local oldheight=character.height + local olddepth=character.depth + if oldwidth and oldwidth>0 then + character.width=oldwidth+wdelta + local commands=character.commands + local hshift=rightcommand[hshift] + if vshift then + if commands then + prependcommands (commands, + hshift, + vshift + ) + else + character.commands={ + hshift, + vshift, + charcommand[unicode] + } + end + else + if commands then + prependcommands (commands, + hshift + ) + else + character.commands={ + hshift, + charcommand[unicode] + } + end + end + end + if oldheight and oldheight>0 then + character.height=oldheight+hdelta + end + if olddepth and olddepth>0 then + character.depth=olddepth+ddelta + end + end + if mathparameters then + setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze) + setmathcharacters(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta) + end + parameters.factor=factor + parameters.hfactor=hfactor + parameters.vfactor=vfactor + if trace then + report_effect("applying") + report_effect(" effect : %s",effect.effect) + report_effect(" width : %s => %s",effect.width,multiplier) + report_effect(" factor : %s => %s",effect.factor,factor ) + report_effect(" hfactor : %s => %s",effect.hfactor,hfactor) + report_effect(" vfactor : %s => %s",effect.vfactor,vfactor) + report_effect(" wdelta : %s => %s",effect.wdelta,wdelta) + report_effect(" hdelta : %s => %s",effect.hdelta,hdelta) + report_effect(" ddelta : %s => %s",effect.ddelta,ddelta) + end + end +end +local specification={ + name="effect", + description="apply effects to glyphs", + initializers={ + base=initializeeffect, + node=initializeeffect, + }, + manipulators={ + base=manipulateeffect, + node=manipulateeffect, + }, +} +registerotffeature(specification) +registerafmfeature(specification) +local function initializeoutline(tfmdata,value) + value=tonumber(value) + if not value then + value=0 + else + value=tonumber(value) or 0 + end + local parameters=tfmdata.parameters + local properties=tfmdata.properties + parameters.mode=effects.outline + parameters.width=value*1000 + properties.effect={ + effect=effect, + width=width, + } +end +local specification={ + name="outline", + description="outline glyphs", + initializers={ + base=initializeoutline, + node=initializeoutline, + } +} +registerotffeature(specification) +registerafmfeature(specification) + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “font-imp-effects”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “fonts-lig” fbd251eea3810a43a8d5542319361d68] --- + + +fonts.handlers.otf.addfeature { + ["dataset"]={ + { + ["data"]={ + ["À"]={ "A","̀" }, + ["Á"]={ "A","́" }, + ["Â"]={ "A","̂" }, + ["Ã"]={ "A","̃" }, + ["Ä"]={ "A","̈" }, + ["Å"]={ "A","̊" }, + ["Ç"]={ "C","̧" }, + ["È"]={ "E","̀" }, + ["É"]={ "E","́" }, + ["Ê"]={ "E","̂" }, + ["Ë"]={ "E","̈" }, + ["Ì"]={ "I","̀" }, + ["Í"]={ "I","́" }, + ["Î"]={ "I","̂" }, + ["Ï"]={ "I","̈" }, + ["Ñ"]={ "N","̃" }, + ["Ò"]={ "O","̀" }, + ["Ó"]={ "O","́" }, + ["Ô"]={ "O","̂" }, + ["Õ"]={ "O","̃" }, + ["Ö"]={ "O","̈" }, + ["Ù"]={ "U","̀" }, + ["Ú"]={ "U","́" }, + ["Û"]={ "U","̂" }, + ["Ü"]={ "U","̈" }, + ["Ý"]={ "Y","́" }, + ["à"]={ "a","̀" }, + ["á"]={ "a","́" }, + ["â"]={ "a","̂" }, + ["ã"]={ "a","̃" }, + ["ä"]={ "a","̈" }, + ["å"]={ "a","̊" }, + ["ç"]={ "c","̧" }, + ["è"]={ "e","̀" }, + ["é"]={ "e","́" }, + ["ê"]={ "e","̂" }, + ["ë"]={ "e","̈" }, + ["ì"]={ "i","̀" }, + ["í"]={ "i","́" }, + ["î"]={ "i","̂" }, + ["ï"]={ "i","̈" }, + ["ñ"]={ "n","̃" }, + ["ò"]={ "o","̀" }, + ["ó"]={ "o","́" }, + ["ô"]={ "o","̂" }, + ["õ"]={ "o","̃" }, + ["ö"]={ "o","̈" }, + ["ù"]={ "u","̀" }, + ["ú"]={ "u","́" }, + ["û"]={ "u","̂" }, + ["ü"]={ "u","̈" }, + ["ý"]={ "y","́" }, + ["ÿ"]={ "y","̈" }, + ["Ā"]={ "A","̄" }, + ["ā"]={ "a","̄" }, + ["Ă"]={ "A","̆" }, + ["ă"]={ "a","̆" }, + ["Ą"]={ "A","̨" }, + ["ą"]={ "a","̨" }, + ["Ć"]={ "C","́" }, + ["ć"]={ "c","́" }, + ["Ĉ"]={ "C","̂" }, + ["ĉ"]={ "c","̂" }, + ["Ċ"]={ "C","̇" }, + ["ċ"]={ "c","̇" }, + ["Č"]={ "C","̌" }, + ["č"]={ "c","̌" }, + ["Ď"]={ "D","̌" }, + ["ď"]={ "d","̌" }, + ["Ē"]={ "E","̄" }, + ["ē"]={ "e","̄" }, + ["Ĕ"]={ "E","̆" }, + ["ĕ"]={ "e","̆" }, + ["Ė"]={ "E","̇" }, + ["ė"]={ "e","̇" }, + ["Ę"]={ "E","̨" }, + ["ę"]={ "e","̨" }, + ["Ě"]={ "E","̌" }, + ["ě"]={ "e","̌" }, + ["Ĝ"]={ "G","̂" }, + ["ĝ"]={ "g","̂" }, + ["Ğ"]={ "G","̆" }, + ["ğ"]={ "g","̆" }, + ["Ġ"]={ "G","̇" }, + ["ġ"]={ "g","̇" }, + ["Ģ"]={ "G","̧" }, + ["ģ"]={ "g","̧" }, + ["Ĥ"]={ "H","̂" }, + ["ĥ"]={ "h","̂" }, + ["Ĩ"]={ "I","̃" }, + ["ĩ"]={ "i","̃" }, + ["Ī"]={ "I","̄" }, + ["ī"]={ "i","̄" }, + ["Ĭ"]={ "I","̆" }, + ["ĭ"]={ "i","̆" }, + ["Į"]={ "I","̨" }, + ["į"]={ "i","̨" }, + ["İ"]={ "I","̇" }, + ["Ĵ"]={ "J","̂" }, + ["ĵ"]={ "j","̂" }, + ["Ķ"]={ "K","̧" }, + ["ķ"]={ "k","̧" }, + ["Ĺ"]={ "L","́" }, + ["ĺ"]={ "l","́" }, + ["Ļ"]={ "L","̧" }, + ["ļ"]={ "l","̧" }, + ["Ľ"]={ "L","̌" }, + ["ľ"]={ "l","̌" }, + ["Ń"]={ "N","́" }, + ["ń"]={ "n","́" }, + ["Ņ"]={ "N","̧" }, + ["ņ"]={ "n","̧" }, + ["Ň"]={ "N","̌" }, + ["ň"]={ "n","̌" }, + ["Ō"]={ "O","̄" }, + ["ō"]={ "o","̄" }, + ["Ŏ"]={ "O","̆" }, + ["ŏ"]={ "o","̆" }, + ["Ő"]={ "O","̋" }, + ["ő"]={ "o","̋" }, + ["Ŕ"]={ "R","́" }, + ["ŕ"]={ "r","́" }, + ["Ŗ"]={ "R","̧" }, + ["ŗ"]={ "r","̧" }, + ["Ř"]={ "R","̌" }, + ["ř"]={ "r","̌" }, + ["Ś"]={ "S","́" }, + ["ś"]={ "s","́" }, + ["Ŝ"]={ "S","̂" }, + ["ŝ"]={ "s","̂" }, + ["Ş"]={ "S","̧" }, + ["ş"]={ "s","̧" }, + ["Š"]={ "S","̌" }, + ["š"]={ "s","̌" }, + ["Ţ"]={ "T","̧" }, + ["ţ"]={ "t","̧" }, + ["Ť"]={ "T","̌" }, + ["ť"]={ "t","̌" }, + ["Ũ"]={ "U","̃" }, + ["ũ"]={ "u","̃" }, + ["Ū"]={ "U","̄" }, + ["ū"]={ "u","̄" }, + ["Ŭ"]={ "U","̆" }, + ["ŭ"]={ "u","̆" }, + ["Ů"]={ "U","̊" }, + ["ů"]={ "u","̊" }, + ["Ű"]={ "U","̋" }, + ["ű"]={ "u","̋" }, + ["Ų"]={ "U","̨" }, + ["ų"]={ "u","̨" }, + ["Ŵ"]={ "W","̂" }, + ["ŵ"]={ "w","̂" }, + ["Ŷ"]={ "Y","̂" }, + ["ŷ"]={ "y","̂" }, + ["Ÿ"]={ "Y","̈" }, + ["Ź"]={ "Z","́" }, + ["ź"]={ "z","́" }, + ["Ż"]={ "Z","̇" }, + ["ż"]={ "z","̇" }, + ["Ž"]={ "Z","̌" }, + ["ž"]={ "z","̌" }, + ["Ơ"]={ "O","̛" }, + ["ơ"]={ "o","̛" }, + ["Ư"]={ "U","̛" }, + ["ư"]={ "u","̛" }, + ["Ǎ"]={ "A","̌" }, + ["ǎ"]={ "a","̌" }, + ["Ǐ"]={ "I","̌" }, + ["ǐ"]={ "i","̌" }, + ["Ǒ"]={ "O","̌" }, + ["ǒ"]={ "o","̌" }, + ["Ǔ"]={ "U","̌" }, + ["ǔ"]={ "u","̌" }, + ["Ǖ"]={ "Ü","̄" }, + ["ǖ"]={ "ü","̄" }, + ["Ǘ"]={ "Ü","́" }, + ["ǘ"]={ "ü","́" }, + ["Ǚ"]={ "Ü","̌" }, + ["ǚ"]={ "ü","̌" }, + ["Ǜ"]={ "Ü","̀" }, + ["ǜ"]={ "ü","̀" }, + ["Ǟ"]={ "Ä","̄" }, + ["ǟ"]={ "ä","̄" }, + ["Ǡ"]={ "Ȧ","̄" }, + ["ǡ"]={ "ȧ","̄" }, + ["Ǣ"]={ "Æ","̄" }, + ["ǣ"]={ "æ","̄" }, + ["Ǧ"]={ "G","̌" }, + ["ǧ"]={ "g","̌" }, + ["Ǩ"]={ "K","̌" }, + ["ǩ"]={ "k","̌" }, + ["Ǫ"]={ "O","̨" }, + ["ǫ"]={ "o","̨" }, + ["Ǭ"]={ "Ǫ","̄" }, + ["ǭ"]={ "ǫ","̄" }, + ["Ǯ"]={ "Ʒ","̌" }, + ["ǯ"]={ "ʒ","̌" }, + ["ǰ"]={ "j","̌" }, + ["Ǵ"]={ "G","́" }, + ["ǵ"]={ "g","́" }, + ["Ǹ"]={ "N","̀" }, + ["ǹ"]={ "n","̀" }, + ["Ǻ"]={ "Å","́" }, + ["ǻ"]={ "å","́" }, + ["Ǽ"]={ "Æ","́" }, + ["ǽ"]={ "æ","́" }, + ["Ǿ"]={ "Ø","́" }, + ["ǿ"]={ "ø","́" }, + ["Ȁ"]={ "A","̏" }, + ["ȁ"]={ "a","̏" }, + ["Ȃ"]={ "A","̑" }, + ["ȃ"]={ "a","̑" }, + ["Ȅ"]={ "E","̏" }, + ["ȅ"]={ "e","̏" }, + ["Ȇ"]={ "E","̑" }, + ["ȇ"]={ "e","̑" }, + ["Ȉ"]={ "I","̏" }, + ["ȉ"]={ "i","̏" }, + ["Ȋ"]={ "I","̑" }, + ["ȋ"]={ "i","̑" }, + ["Ȍ"]={ "O","̏" }, + ["ȍ"]={ "o","̏" }, + ["Ȏ"]={ "O","̑" }, + ["ȏ"]={ "o","̑" }, + ["Ȑ"]={ "R","̏" }, + ["ȑ"]={ "r","̏" }, + ["Ȓ"]={ "R","̑" }, + ["ȓ"]={ "r","̑" }, + ["Ȕ"]={ "U","̏" }, + ["ȕ"]={ "u","̏" }, + ["Ȗ"]={ "U","̑" }, + ["ȗ"]={ "u","̑" }, + ["Ș"]={ "S","̦" }, + ["ș"]={ "s","̦" }, + ["Ț"]={ "T","̦" }, + ["ț"]={ "t","̦" }, + ["Ȟ"]={ "H","̌" }, + ["ȟ"]={ "h","̌" }, + ["Ȧ"]={ "A","̇" }, + ["ȧ"]={ "a","̇" }, + ["Ȩ"]={ "E","̧" }, + ["ȩ"]={ "e","̧" }, + ["Ȫ"]={ "Ö","̄" }, + ["ȫ"]={ "ö","̄" }, + ["Ȭ"]={ "Õ","̄" }, + ["ȭ"]={ "õ","̄" }, + ["Ȯ"]={ "O","̇" }, + ["ȯ"]={ "o","̇" }, + ["Ȱ"]={ "Ȯ","̄" }, + ["ȱ"]={ "ȯ","̄" }, + ["Ȳ"]={ "Y","̄" }, + ["ȳ"]={ "y","̄" }, + ["̈́"]={ "̈","́" }, + ["΅"]={ "¨","́" }, + ["Ά"]={ "Α","́" }, + ["Έ"]={ "Ε","́" }, + ["Ή"]={ "Η","́" }, + ["Ί"]={ "Ι","́" }, + ["Ό"]={ "Ο","́" }, + ["Ύ"]={ "Υ","́" }, + ["Ώ"]={ "Ω","́" }, + ["ΐ"]={ "ϊ","́" }, + ["Ϊ"]={ "Ι","̈" }, + ["Ϋ"]={ "Υ","̈" }, + ["ά"]={ "α","́" }, + ["έ"]={ "ε","́" }, + ["ή"]={ "η","́" }, + ["ί"]={ "ι","́" }, + ["ΰ"]={ "ϋ","́" }, + ["ϊ"]={ "ι","̈" }, + ["ϋ"]={ "υ","̈" }, + ["ό"]={ "ο","́" }, + ["ύ"]={ "υ","́" }, + ["ώ"]={ "ω","́" }, + ["ϓ"]={ "ϒ","́" }, + ["ϔ"]={ "ϒ","̈" }, + ["Ѐ"]={ "Е","̀" }, + ["Ё"]={ "Е","̈" }, + ["Ѓ"]={ "Г","́" }, + ["Ї"]={ "І","̈" }, + ["Ќ"]={ "К","́" }, + ["Ѝ"]={ "И","̀" }, + ["Ў"]={ "У","̆" }, + ["Й"]={ "И","̆" }, + ["й"]={ "и","̆" }, + ["ѐ"]={ "е","̀" }, + ["ё"]={ "е","̈" }, + ["ѓ"]={ "г","́" }, + ["ї"]={ "і","̈" }, + ["ќ"]={ "к","́" }, + ["ѝ"]={ "и","̀" }, + ["ў"]={ "у","̆" }, + ["Ѷ"]={ "Ѵ","̏" }, + ["ѷ"]={ "ѵ","̏" }, + ["Ӂ"]={ "Ж","̆" }, + ["ӂ"]={ "ж","̆" }, + ["Ӑ"]={ "А","̆" }, + ["ӑ"]={ "а","̆" }, + ["Ӓ"]={ "А","̈" }, + ["ӓ"]={ "а","̈" }, + ["Ӗ"]={ "Е","̆" }, + ["ӗ"]={ "е","̆" }, + ["Ӛ"]={ "Ә","̈" }, + ["ӛ"]={ "ә","̈" }, + ["Ӝ"]={ "Ж","̈" }, + ["ӝ"]={ "ж","̈" }, + ["Ӟ"]={ "З","̈" }, + ["ӟ"]={ "з","̈" }, + ["Ӣ"]={ "И","̄" }, + ["ӣ"]={ "и","̄" }, + ["Ӥ"]={ "И","̈" }, + ["ӥ"]={ "и","̈" }, + ["Ӧ"]={ "О","̈" }, + ["ӧ"]={ "о","̈" }, + ["Ӫ"]={ "Ө","̈" }, + ["ӫ"]={ "ө","̈" }, + ["Ӭ"]={ "Э","̈" }, + ["ӭ"]={ "э","̈" }, + ["Ӯ"]={ "У","̄" }, + ["ӯ"]={ "у","̄" }, + ["Ӱ"]={ "У","̈" }, + ["ӱ"]={ "у","̈" }, + ["Ӳ"]={ "У","̋" }, + ["ӳ"]={ "у","̋" }, + ["Ӵ"]={ "Ч","̈" }, + ["ӵ"]={ "ч","̈" }, + ["Ӹ"]={ "Ы","̈" }, + ["ӹ"]={ "ы","̈" }, + ["آ"]={ "ا","ٓ" }, + ["أ"]={ "ا","ٔ" }, + ["ؤ"]={ "و","ٔ" }, + ["إ"]={ "ا","ٕ" }, + ["ئ"]={ "ي","ٔ" }, + ["ۀ"]={ "ە","ٔ" }, + ["ۂ"]={ "ہ","ٔ" }, + ["ۓ"]={ "ے","ٔ" }, + ["ऩ"]={ "न","़" }, + ["ऱ"]={ "र","़" }, + ["ऴ"]={ "ळ","़" }, + ["क़"]={ "क","़" }, + ["ख़"]={ "ख","़" }, + ["ग़"]={ "ग","़" }, + ["ज़"]={ "ज","़" }, + ["ड़"]={ "ड","़" }, + ["ढ़"]={ "ढ","़" }, + ["फ़"]={ "फ","़" }, + ["य़"]={ "य","़" }, + ["ো"]={ "ে","া" }, + ["ৌ"]={ "ে","ৗ" }, + ["ড়"]={ "ড","়" }, + ["ঢ়"]={ "ঢ","়" }, + ["য়"]={ "য","়" }, + ["ਲ਼"]={ "ਲ","਼" }, + ["ਸ਼"]={ "ਸ","਼" }, + ["ਖ਼"]={ "ਖ","਼" }, + ["ਗ਼"]={ "ਗ","਼" }, + ["ਜ਼"]={ "ਜ","਼" }, + ["ਫ਼"]={ "ਫ","਼" }, + ["ୈ"]={ "େ","ୖ" }, + ["ୋ"]={ "େ","ା" }, + ["ୌ"]={ "େ","ୗ" }, + ["ଡ଼"]={ "ଡ","଼" }, + ["ଢ଼"]={ "ଢ","଼" }, + ["ஔ"]={ "ஒ","ௗ" }, + ["ொ"]={ "ெ","ா" }, + ["ோ"]={ "ே","ா" }, + ["ௌ"]={ "ெ","ௗ" }, + ["ై"]={ "ె","ౖ" }, + ["ೀ"]={ "ಿ","ೕ" }, + ["ೇ"]={ "ೆ","ೕ" }, + ["ೈ"]={ "ೆ","ೖ" }, + ["ೊ"]={ "ೆ","ೂ" }, + ["ೋ"]={ "ೊ","ೕ" }, + ["ൊ"]={ "െ","ാ" }, + ["ോ"]={ "േ","ാ" }, + ["ൌ"]={ "െ","ൗ" }, + ["ේ"]={ "ෙ","්" }, + ["ො"]={ "ෙ","ා" }, + ["ෝ"]={ "ො","්" }, + ["ෞ"]={ "ෙ","ෟ" }, + ["གྷ"]={ "ག","ྷ" }, + ["ཌྷ"]={ "ཌ","ྷ" }, + ["དྷ"]={ "ད","ྷ" }, + ["བྷ"]={ "བ","ྷ" }, + ["ཛྷ"]={ "ཛ","ྷ" }, + ["ཀྵ"]={ "ཀ","ྵ" }, + ["ཱི"]={ "ཱ","ི" }, + ["ཱུ"]={ "ཱ","ུ" }, + ["ྲྀ"]={ "ྲ","ྀ" }, + ["ླྀ"]={ "ླ","ྀ" }, + ["ཱྀ"]={ "ཱ","ྀ" }, + ["ྒྷ"]={ "ྒ","ྷ" }, + ["ྜྷ"]={ "ྜ","ྷ" }, + ["ྡྷ"]={ "ྡ","ྷ" }, + ["ྦྷ"]={ "ྦ","ྷ" }, + ["ྫྷ"]={ "ྫ","ྷ" }, + ["ྐྵ"]={ "ྐ","ྵ" }, + ["ဦ"]={ "ဥ","ီ" }, + ["ᬆ"]={ "ᬅ","ᬵ" }, + ["ᬈ"]={ "ᬇ","ᬵ" }, + ["ᬊ"]={ "ᬉ","ᬵ" }, + ["ᬌ"]={ "ᬋ","ᬵ" }, + ["ᬎ"]={ "ᬍ","ᬵ" }, + ["ᬒ"]={ "ᬑ","ᬵ" }, + ["ᬻ"]={ "ᬺ","ᬵ" }, + ["ᬽ"]={ "ᬼ","ᬵ" }, + ["ᭀ"]={ "ᬾ","ᬵ" }, + ["ᭁ"]={ "ᬿ","ᬵ" }, + ["ᭃ"]={ "ᭂ","ᬵ" }, + ["Ḁ"]={ "A","̥" }, + ["ḁ"]={ "a","̥" }, + ["Ḃ"]={ "B","̇" }, + ["ḃ"]={ "b","̇" }, + ["Ḅ"]={ "B","̣" }, + ["ḅ"]={ "b","̣" }, + ["Ḇ"]={ "B","̱" }, + ["ḇ"]={ "b","̱" }, + ["Ḉ"]={ "Ç","́" }, + ["ḉ"]={ "ç","́" }, + ["Ḋ"]={ "D","̇" }, + ["ḋ"]={ "d","̇" }, + ["Ḍ"]={ "D","̣" }, + ["ḍ"]={ "d","̣" }, + ["Ḏ"]={ "D","̱" }, + ["ḏ"]={ "d","̱" }, + ["Ḑ"]={ "D","̧" }, + ["ḑ"]={ "d","̧" }, + ["Ḓ"]={ "D","̭" }, + ["ḓ"]={ "d","̭" }, + ["Ḕ"]={ "Ē","̀" }, + ["ḕ"]={ "ē","̀" }, + ["Ḗ"]={ "Ē","́" }, + ["ḗ"]={ "ē","́" }, + ["Ḙ"]={ "E","̭" }, + ["ḙ"]={ "e","̭" }, + ["Ḛ"]={ "E","̰" }, + ["ḛ"]={ "e","̰" }, + ["Ḝ"]={ "Ȩ","̆" }, + ["ḝ"]={ "ȩ","̆" }, + ["Ḟ"]={ "F","̇" }, + ["ḟ"]={ "f","̇" }, + ["Ḡ"]={ "G","̄" }, + ["ḡ"]={ "g","̄" }, + ["Ḣ"]={ "H","̇" }, + ["ḣ"]={ "h","̇" }, + ["Ḥ"]={ "H","̣" }, + ["ḥ"]={ "h","̣" }, + ["Ḧ"]={ "H","̈" }, + ["ḧ"]={ "h","̈" }, + ["Ḩ"]={ "H","̧" }, + ["ḩ"]={ "h","̧" }, + ["Ḫ"]={ "H","̮" }, + ["ḫ"]={ "h","̮" }, + ["Ḭ"]={ "I","̰" }, + ["ḭ"]={ "i","̰" }, + ["Ḯ"]={ "Ï","́" }, + ["ḯ"]={ "ï","́" }, + ["Ḱ"]={ "K","́" }, + ["ḱ"]={ "k","́" }, + ["Ḳ"]={ "K","̣" }, + ["ḳ"]={ "k","̣" }, + ["Ḵ"]={ "K","̱" }, + ["ḵ"]={ "k","̱" }, + ["Ḷ"]={ "L","̣" }, + ["ḷ"]={ "l","̣" }, + ["Ḹ"]={ "Ḷ","̄" }, + ["ḹ"]={ "ḷ","̄" }, + ["Ḻ"]={ "L","̱" }, + ["ḻ"]={ "l","̱" }, + ["Ḽ"]={ "L","̭" }, + ["ḽ"]={ "l","̭" }, + ["Ḿ"]={ "M","́" }, + ["ḿ"]={ "m","́" }, + ["Ṁ"]={ "M","̇" }, + ["ṁ"]={ "m","̇" }, + ["Ṃ"]={ "M","̣" }, + ["ṃ"]={ "m","̣" }, + ["Ṅ"]={ "N","̇" }, + ["ṅ"]={ "n","̇" }, + ["Ṇ"]={ "N","̣" }, + ["ṇ"]={ "n","̣" }, + ["Ṉ"]={ "N","̱" }, + ["ṉ"]={ "n","̱" }, + ["Ṋ"]={ "N","̭" }, + ["ṋ"]={ "n","̭" }, + ["Ṍ"]={ "Õ","́" }, + ["ṍ"]={ "õ","́" }, + ["Ṏ"]={ "Õ","̈" }, + ["ṏ"]={ "õ","̈" }, + ["Ṑ"]={ "Ō","̀" }, + ["ṑ"]={ "ō","̀" }, + ["Ṓ"]={ "Ō","́" }, + ["ṓ"]={ "ō","́" }, + ["Ṕ"]={ "P","́" }, + ["ṕ"]={ "p","́" }, + ["Ṗ"]={ "P","̇" }, + ["ṗ"]={ "p","̇" }, + ["Ṙ"]={ "R","̇" }, + ["ṙ"]={ "r","̇" }, + ["Ṛ"]={ "R","̣" }, + ["ṛ"]={ "r","̣" }, + ["Ṝ"]={ "Ṛ","̄" }, + ["ṝ"]={ "ṛ","̄" }, + ["Ṟ"]={ "R","̱" }, + ["ṟ"]={ "r","̱" }, + ["Ṡ"]={ "S","̇" }, + ["ṡ"]={ "s","̇" }, + ["Ṣ"]={ "S","̣" }, + ["ṣ"]={ "s","̣" }, + ["Ṥ"]={ "Ś","̇" }, + ["ṥ"]={ "ś","̇" }, + ["Ṧ"]={ "Š","̇" }, + ["ṧ"]={ "š","̇" }, + ["Ṩ"]={ "Ṣ","̇" }, + ["ṩ"]={ "ṣ","̇" }, + ["Ṫ"]={ "T","̇" }, + ["ṫ"]={ "t","̇" }, + ["Ṭ"]={ "T","̣" }, + ["ṭ"]={ "t","̣" }, + ["Ṯ"]={ "T","̱" }, + ["ṯ"]={ "t","̱" }, + ["Ṱ"]={ "T","̭" }, + ["ṱ"]={ "t","̭" }, + ["Ṳ"]={ "U","̤" }, + ["ṳ"]={ "u","̤" }, + ["Ṵ"]={ "U","̰" }, + ["ṵ"]={ "u","̰" }, + ["Ṷ"]={ "U","̭" }, + ["ṷ"]={ "u","̭" }, + ["Ṹ"]={ "Ũ","́" }, + ["ṹ"]={ "ũ","́" }, + ["Ṻ"]={ "Ū","̈" }, + ["ṻ"]={ "ū","̈" }, + ["Ṽ"]={ "V","̃" }, + ["ṽ"]={ "v","̃" }, + ["Ṿ"]={ "V","̣" }, + ["ṿ"]={ "v","̣" }, + ["Ẁ"]={ "W","̀" }, + ["ẁ"]={ "w","̀" }, + ["Ẃ"]={ "W","́" }, + ["ẃ"]={ "w","́" }, + ["Ẅ"]={ "W","̈" }, + ["ẅ"]={ "w","̈" }, + ["Ẇ"]={ "W","̇" }, + ["ẇ"]={ "w","̇" }, + ["Ẉ"]={ "W","̣" }, + ["ẉ"]={ "w","̣" }, + ["Ẋ"]={ "X","̇" }, + ["ẋ"]={ "x","̇" }, + ["Ẍ"]={ "X","̈" }, + ["ẍ"]={ "x","̈" }, + ["Ẏ"]={ "Y","̇" }, + ["ẏ"]={ "y","̇" }, + ["Ẑ"]={ "Z","̂" }, + ["ẑ"]={ "z","̂" }, + ["Ẓ"]={ "Z","̣" }, + ["ẓ"]={ "z","̣" }, + ["Ẕ"]={ "Z","̱" }, + ["ẕ"]={ "z","̱" }, + ["ẖ"]={ "h","̱" }, + ["ẗ"]={ "t","̈" }, + ["ẘ"]={ "w","̊" }, + ["ẙ"]={ "y","̊" }, + ["ẛ"]={ "ſ","̇" }, + ["Ạ"]={ "A","̣" }, + ["ạ"]={ "a","̣" }, + ["Ả"]={ "A","̉" }, + ["ả"]={ "a","̉" }, + ["Ấ"]={ "Â","́" }, + ["ấ"]={ "â","́" }, + ["Ầ"]={ "Â","̀" }, + ["ầ"]={ "â","̀" }, + ["Ẩ"]={ "Â","̉" }, + ["ẩ"]={ "â","̉" }, + ["Ẫ"]={ "Â","̃" }, + ["ẫ"]={ "â","̃" }, + ["Ậ"]={ "Ạ","̂" }, + ["ậ"]={ "ạ","̂" }, + ["Ắ"]={ "Ă","́" }, + ["ắ"]={ "ă","́" }, + ["Ằ"]={ "Ă","̀" }, + ["ằ"]={ "ă","̀" }, + ["Ẳ"]={ "Ă","̉" }, + ["ẳ"]={ "ă","̉" }, + ["Ẵ"]={ "Ă","̃" }, + ["ẵ"]={ "ă","̃" }, + ["Ặ"]={ "Ạ","̆" }, + ["ặ"]={ "ạ","̆" }, + ["Ẹ"]={ "E","̣" }, + ["ẹ"]={ "e","̣" }, + ["Ẻ"]={ "E","̉" }, + ["ẻ"]={ "e","̉" }, + ["Ẽ"]={ "E","̃" }, + ["ẽ"]={ "e","̃" }, + ["Ế"]={ "Ê","́" }, + ["ế"]={ "ê","́" }, + ["Ề"]={ "Ê","̀" }, + ["ề"]={ "ê","̀" }, + ["Ể"]={ "Ê","̉" }, + ["ể"]={ "ê","̉" }, + ["Ễ"]={ "Ê","̃" }, + ["ễ"]={ "ê","̃" }, + ["Ệ"]={ "Ẹ","̂" }, + ["ệ"]={ "ẹ","̂" }, + ["Ỉ"]={ "I","̉" }, + ["ỉ"]={ "i","̉" }, + ["Ị"]={ "I","̣" }, + ["ị"]={ "i","̣" }, + ["Ọ"]={ "O","̣" }, + ["ọ"]={ "o","̣" }, + ["Ỏ"]={ "O","̉" }, + ["ỏ"]={ "o","̉" }, + ["Ố"]={ "Ô","́" }, + ["ố"]={ "ô","́" }, + ["Ồ"]={ "Ô","̀" }, + ["ồ"]={ "ô","̀" }, + ["Ổ"]={ "Ô","̉" }, + ["ổ"]={ "ô","̉" }, + ["Ỗ"]={ "Ô","̃" }, + ["ỗ"]={ "ô","̃" }, + ["Ộ"]={ "Ọ","̂" }, + ["ộ"]={ "ọ","̂" }, + ["Ớ"]={ "Ơ","́" }, + ["ớ"]={ "ơ","́" }, + ["Ờ"]={ "Ơ","̀" }, + ["ờ"]={ "ơ","̀" }, + ["Ở"]={ "Ơ","̉" }, + ["ở"]={ "ơ","̉" }, + ["Ỡ"]={ "Ơ","̃" }, + ["ỡ"]={ "ơ","̃" }, + ["Ợ"]={ "Ơ","̣" }, + ["ợ"]={ "ơ","̣" }, + ["Ụ"]={ "U","̣" }, + ["ụ"]={ "u","̣" }, + ["Ủ"]={ "U","̉" }, + ["ủ"]={ "u","̉" }, + ["Ứ"]={ "Ư","́" }, + ["ứ"]={ "ư","́" }, + ["Ừ"]={ "Ư","̀" }, + ["ừ"]={ "ư","̀" }, + ["Ử"]={ "Ư","̉" }, + ["ử"]={ "ư","̉" }, + ["Ữ"]={ "Ư","̃" }, + ["ữ"]={ "ư","̃" }, + ["Ự"]={ "Ư","̣" }, + ["ự"]={ "ư","̣" }, + ["Ỳ"]={ "Y","̀" }, + ["ỳ"]={ "y","̀" }, + ["Ỵ"]={ "Y","̣" }, + ["ỵ"]={ "y","̣" }, + ["Ỷ"]={ "Y","̉" }, + ["ỷ"]={ "y","̉" }, + ["Ỹ"]={ "Y","̃" }, + ["ỹ"]={ "y","̃" }, + ["ἀ"]={ "α","̓" }, + ["ἁ"]={ "α","̔" }, + ["ἂ"]={ "ἀ","̀" }, + ["ἃ"]={ "ἁ","̀" }, + ["ἄ"]={ "ἀ","́" }, + ["ἅ"]={ "ἁ","́" }, + ["ἆ"]={ "ἀ","͂" }, + ["ἇ"]={ "ἁ","͂" }, + ["Ἀ"]={ "Α","̓" }, + ["Ἁ"]={ "Α","̔" }, + ["Ἂ"]={ "Ἀ","̀" }, + ["Ἃ"]={ "Ἁ","̀" }, + ["Ἄ"]={ "Ἀ","́" }, + ["Ἅ"]={ "Ἁ","́" }, + ["Ἆ"]={ "Ἀ","͂" }, + ["Ἇ"]={ "Ἁ","͂" }, + ["ἐ"]={ "ε","̓" }, + ["ἑ"]={ "ε","̔" }, + ["ἒ"]={ "ἐ","̀" }, + ["ἓ"]={ "ἑ","̀" }, + ["ἔ"]={ "ἐ","́" }, + ["ἕ"]={ "ἑ","́" }, + ["Ἐ"]={ "Ε","̓" }, + ["Ἑ"]={ "Ε","̔" }, + ["Ἒ"]={ "Ἐ","̀" }, + ["Ἓ"]={ "Ἑ","̀" }, + ["Ἔ"]={ "Ἐ","́" }, + ["Ἕ"]={ "Ἑ","́" }, + ["ἠ"]={ "η","̓" }, + ["ἡ"]={ "η","̔" }, + ["ἢ"]={ "ἠ","̀" }, + ["ἣ"]={ "ἡ","̀" }, + ["ἤ"]={ "ἠ","́" }, + ["ἥ"]={ "ἡ","́" }, + ["ἦ"]={ "ἠ","͂" }, + ["ἧ"]={ "ἡ","͂" }, + ["Ἠ"]={ "Η","̓" }, + ["Ἡ"]={ "Η","̔" }, + ["Ἢ"]={ "Ἠ","̀" }, + ["Ἣ"]={ "Ἡ","̀" }, + ["Ἤ"]={ "Ἠ","́" }, + ["Ἥ"]={ "Ἡ","́" }, + ["Ἦ"]={ "Ἠ","͂" }, + ["Ἧ"]={ "Ἡ","͂" }, + ["ἰ"]={ "ι","̓" }, + ["ἱ"]={ "ι","̔" }, + ["ἲ"]={ "ἰ","̀" }, + ["ἳ"]={ "ἱ","̀" }, + ["ἴ"]={ "ἰ","́" }, + ["ἵ"]={ "ἱ","́" }, + ["ἶ"]={ "ἰ","͂" }, + ["ἷ"]={ "ἱ","͂" }, + ["Ἰ"]={ "Ι","̓" }, + ["Ἱ"]={ "Ι","̔" }, + ["Ἲ"]={ "Ἰ","̀" }, + ["Ἳ"]={ "Ἱ","̀" }, + ["Ἴ"]={ "Ἰ","́" }, + ["Ἵ"]={ "Ἱ","́" }, + ["Ἶ"]={ "Ἰ","͂" }, + ["Ἷ"]={ "Ἱ","͂" }, + ["ὀ"]={ "ο","̓" }, + ["ὁ"]={ "ο","̔" }, + ["ὂ"]={ "ὀ","̀" }, + ["ὃ"]={ "ὁ","̀" }, + ["ὄ"]={ "ὀ","́" }, + ["ὅ"]={ "ὁ","́" }, + ["Ὀ"]={ "Ο","̓" }, + ["Ὁ"]={ "Ο","̔" }, + ["Ὂ"]={ "Ὀ","̀" }, + ["Ὃ"]={ "Ὁ","̀" }, + ["Ὄ"]={ "Ὀ","́" }, + ["Ὅ"]={ "Ὁ","́" }, + ["ὐ"]={ "υ","̓" }, + ["ὑ"]={ "υ","̔" }, + ["ὒ"]={ "ὐ","̀" }, + ["ὓ"]={ "ὑ","̀" }, + ["ὔ"]={ "ὐ","́" }, + ["ὕ"]={ "ὑ","́" }, + ["ὖ"]={ "ὐ","͂" }, + ["ὗ"]={ "ὑ","͂" }, + ["Ὑ"]={ "Υ","̔" }, + ["Ὓ"]={ "Ὑ","̀" }, + ["Ὕ"]={ "Ὑ","́" }, + ["Ὗ"]={ "Ὑ","͂" }, + ["ὠ"]={ "ω","̓" }, + ["ὡ"]={ "ω","̔" }, + ["ὢ"]={ "ὠ","̀" }, + ["ὣ"]={ "ὡ","̀" }, + ["ὤ"]={ "ὠ","́" }, + ["ὥ"]={ "ὡ","́" }, + ["ὦ"]={ "ὠ","͂" }, + ["ὧ"]={ "ὡ","͂" }, + ["Ὠ"]={ "Ω","̓" }, + ["Ὡ"]={ "Ω","̔" }, + ["Ὢ"]={ "Ὠ","̀" }, + ["Ὣ"]={ "Ὡ","̀" }, + ["Ὤ"]={ "Ὠ","́" }, + ["Ὥ"]={ "Ὡ","́" }, + ["Ὦ"]={ "Ὠ","͂" }, + ["Ὧ"]={ "Ὡ","͂" }, + ["ὰ"]={ "α","̀" }, + ["ὲ"]={ "ε","̀" }, + ["ὴ"]={ "η","̀" }, + ["ὶ"]={ "ι","̀" }, + ["ὸ"]={ "ο","̀" }, + ["ὺ"]={ "υ","̀" }, + ["ὼ"]={ "ω","̀" }, + ["ᾀ"]={ "ἀ","ͅ" }, + ["ᾁ"]={ "ἁ","ͅ" }, + ["ᾂ"]={ "ἂ","ͅ" }, + ["ᾃ"]={ "ἃ","ͅ" }, + ["ᾄ"]={ "ἄ","ͅ" }, + ["ᾅ"]={ "ἅ","ͅ" }, + ["ᾆ"]={ "ἆ","ͅ" }, + ["ᾇ"]={ "ἇ","ͅ" }, + ["ᾈ"]={ "Ἀ","ͅ" }, + ["ᾉ"]={ "Ἁ","ͅ" }, + ["ᾊ"]={ "Ἂ","ͅ" }, + ["ᾋ"]={ "Ἃ","ͅ" }, + ["ᾌ"]={ "Ἄ","ͅ" }, + ["ᾍ"]={ "Ἅ","ͅ" }, + ["ᾎ"]={ "Ἆ","ͅ" }, + ["ᾏ"]={ "Ἇ","ͅ" }, + ["ᾐ"]={ "ἠ","ͅ" }, + ["ᾑ"]={ "ἡ","ͅ" }, + ["ᾒ"]={ "ἢ","ͅ" }, + ["ᾓ"]={ "ἣ","ͅ" }, + ["ᾔ"]={ "ἤ","ͅ" }, + ["ᾕ"]={ "ἥ","ͅ" }, + ["ᾖ"]={ "ἦ","ͅ" }, + ["ᾗ"]={ "ἧ","ͅ" }, + ["ᾘ"]={ "Ἠ","ͅ" }, + ["ᾙ"]={ "Ἡ","ͅ" }, + ["ᾚ"]={ "Ἢ","ͅ" }, + ["ᾛ"]={ "Ἣ","ͅ" }, + ["ᾜ"]={ "Ἤ","ͅ" }, + ["ᾝ"]={ "Ἥ","ͅ" }, + ["ᾞ"]={ "Ἦ","ͅ" }, + ["ᾟ"]={ "Ἧ","ͅ" }, + ["ᾠ"]={ "ὠ","ͅ" }, + ["ᾡ"]={ "ὡ","ͅ" }, + ["ᾢ"]={ "ὢ","ͅ" }, + ["ᾣ"]={ "ὣ","ͅ" }, + ["ᾤ"]={ "ὤ","ͅ" }, + ["ᾥ"]={ "ὥ","ͅ" }, + ["ᾦ"]={ "ὦ","ͅ" }, + ["ᾧ"]={ "ὧ","ͅ" }, + ["ᾨ"]={ "Ὠ","ͅ" }, + ["ᾩ"]={ "Ὡ","ͅ" }, + ["ᾪ"]={ "Ὢ","ͅ" }, + ["ᾫ"]={ "Ὣ","ͅ" }, + ["ᾬ"]={ "Ὤ","ͅ" }, + ["ᾭ"]={ "Ὥ","ͅ" }, + ["ᾮ"]={ "Ὦ","ͅ" }, + ["ᾯ"]={ "Ὧ","ͅ" }, + ["ᾰ"]={ "α","̆" }, + ["ᾱ"]={ "α","̄" }, + ["ᾲ"]={ "ὰ","ͅ" }, + ["ᾳ"]={ "α","ͅ" }, + ["ᾴ"]={ "ά","ͅ" }, + ["ᾶ"]={ "α","͂" }, + ["ᾷ"]={ "ᾶ","ͅ" }, + ["Ᾰ"]={ "Α","̆" }, + ["Ᾱ"]={ "Α","̄" }, + ["Ὰ"]={ "Α","̀" }, + ["ᾼ"]={ "Α","ͅ" }, + ["῁"]={ "¨","͂" }, + ["ῂ"]={ "ὴ","ͅ" }, + ["ῃ"]={ "η","ͅ" }, + ["ῄ"]={ "ή","ͅ" }, + ["ῆ"]={ "η","͂" }, + ["ῇ"]={ "ῆ","ͅ" }, + ["Ὲ"]={ "Ε","̀" }, + ["Ὴ"]={ "Η","̀" }, + ["ῌ"]={ "Η","ͅ" }, + ["῍"]={ "᾿","̀" }, + ["῎"]={ "᾿","́" }, + ["῏"]={ "᾿","͂" }, + ["ῐ"]={ "ι","̆" }, + ["ῑ"]={ "ι","̄" }, + ["ῒ"]={ "ϊ","̀" }, + ["ῖ"]={ "ι","͂" }, + ["ῗ"]={ "ϊ","͂" }, + ["Ῐ"]={ "Ι","̆" }, + ["Ῑ"]={ "Ι","̄" }, + ["Ὶ"]={ "Ι","̀" }, + ["῝"]={ "῾","̀" }, + ["῞"]={ "῾","́" }, + ["῟"]={ "῾","͂" }, + ["ῠ"]={ "υ","̆" }, + ["ῡ"]={ "υ","̄" }, + ["ῢ"]={ "ϋ","̀" }, + ["ῤ"]={ "ρ","̓" }, + ["ῥ"]={ "ρ","̔" }, + ["ῦ"]={ "υ","͂" }, + ["ῧ"]={ "ϋ","͂" }, + ["Ῠ"]={ "Υ","̆" }, + ["Ῡ"]={ "Υ","̄" }, + ["Ὺ"]={ "Υ","̀" }, + ["Ῥ"]={ "Ρ","̔" }, + ["῭"]={ "¨","̀" }, + ["ῲ"]={ "ὼ","ͅ" }, + ["ῳ"]={ "ω","ͅ" }, + ["ῴ"]={ "ώ","ͅ" }, + ["ῶ"]={ "ω","͂" }, + ["ῷ"]={ "ῶ","ͅ" }, + ["Ὸ"]={ "Ο","̀" }, + ["Ὼ"]={ "Ω","̀" }, + ["ῼ"]={ "Ω","ͅ" }, + ["↚"]={ "←","̸" }, + ["↛"]={ "→","̸" }, + ["↮"]={ "↔","̸" }, + ["⇍"]={ "⇐","̸" }, + ["⇎"]={ "⇔","̸" }, + ["⇏"]={ "⇒","̸" }, + ["∄"]={ "∃","̸" }, + ["∉"]={ "∈","̸" }, + ["∌"]={ "∋","̸" }, + ["∤"]={ "∣","̸" }, + ["∦"]={ "∥","̸" }, + ["≁"]={ "∼","̸" }, + ["≄"]={ "≃","̸" }, + ["≇"]={ "≅","̸" }, + ["≉"]={ "≈","̸" }, + ["≠"]={ "=","̸" }, + ["≢"]={ "≡","̸" }, + ["≭"]={ "≍","̸" }, + ["≮"]={ "<","̸" }, + ["≯"]={ ">","̸" }, + ["≰"]={ "≤","̸" }, + ["≱"]={ "≥","̸" }, + ["≴"]={ "≲","̸" }, + ["≵"]={ "≳","̸" }, + ["≸"]={ "≶","̸" }, + ["≹"]={ "≷","̸" }, + ["⊀"]={ "≺","̸" }, + ["⊁"]={ "≻","̸" }, + ["⊄"]={ "⊂","̸" }, + ["⊅"]={ "⊃","̸" }, + ["⊈"]={ "⊆","̸" }, + ["⊉"]={ "⊇","̸" }, + ["⊬"]={ "⊢","̸" }, + ["⊭"]={ "⊨","̸" }, + ["⊮"]={ "⊩","̸" }, + ["⊯"]={ "⊫","̸" }, + ["⋠"]={ "≼","̸" }, + ["⋡"]={ "≽","̸" }, + ["⋢"]={ "⊑","̸" }, + ["⋣"]={ "⊒","̸" }, + ["⋪"]={ "⊲","̸" }, + ["⋫"]={ "⊳","̸" }, + ["⋬"]={ "⊴","̸" }, + ["⋭"]={ "⊵","̸" }, + ["⫝̸"]={ "⫝","̸" }, + ["が"]={ "か","゙" }, + ["ぎ"]={ "き","゙" }, + ["ぐ"]={ "く","゙" }, + ["げ"]={ "け","゙" }, + ["ご"]={ "こ","゙" }, + ["ざ"]={ "さ","゙" }, + ["じ"]={ "し","゙" }, + ["ず"]={ "す","゙" }, + ["ぜ"]={ "せ","゙" }, + ["ぞ"]={ "そ","゙" }, + ["だ"]={ "た","゙" }, + ["ぢ"]={ "ち","゙" }, + ["づ"]={ "つ","゙" }, + ["で"]={ "て","゙" }, + ["ど"]={ "と","゙" }, + ["ば"]={ "は","゙" }, + ["ぱ"]={ "は","゚" }, + ["び"]={ "ひ","゙" }, + ["ぴ"]={ "ひ","゚" }, + ["ぶ"]={ "ふ","゙" }, + ["ぷ"]={ "ふ","゚" }, + ["べ"]={ "へ","゙" }, + ["ぺ"]={ "へ","゚" }, + ["ぼ"]={ "ほ","゙" }, + ["ぽ"]={ "ほ","゚" }, + ["ゔ"]={ "う","゙" }, + ["ゞ"]={ "ゝ","゙" }, + ["ガ"]={ "カ","゙" }, + ["ギ"]={ "キ","゙" }, + ["グ"]={ "ク","゙" }, + ["ゲ"]={ "ケ","゙" }, + ["ゴ"]={ "コ","゙" }, + ["ザ"]={ "サ","゙" }, + ["ジ"]={ "シ","゙" }, + ["ズ"]={ "ス","゙" }, + ["ゼ"]={ "セ","゙" }, + ["ゾ"]={ "ソ","゙" }, + ["ダ"]={ "タ","゙" }, + ["ヂ"]={ "チ","゙" }, + ["ヅ"]={ "ツ","゙" }, + ["デ"]={ "テ","゙" }, + ["ド"]={ "ト","゙" }, + ["バ"]={ "ハ","゙" }, + ["パ"]={ "ハ","゚" }, + ["ビ"]={ "ヒ","゙" }, + ["ピ"]={ "ヒ","゚" }, + ["ブ"]={ "フ","゙" }, + ["プ"]={ "フ","゚" }, + ["ベ"]={ "ヘ","゙" }, + ["ペ"]={ "ヘ","゚" }, + ["ボ"]={ "ホ","゙" }, + ["ポ"]={ "ホ","゚" }, + ["ヴ"]={ "ウ","゙" }, + ["ヷ"]={ "ワ","゙" }, + ["ヸ"]={ "ヰ","゙" }, + ["ヹ"]={ "ヱ","゙" }, + ["ヺ"]={ "ヲ","゙" }, + ["ヾ"]={ "ヽ","゙" }, + ["יִ"]={ "י","ִ" }, + ["ײַ"]={ "ײ","ַ" }, + ["שׁ"]={ "ש","ׁ" }, + ["שׂ"]={ "ש","ׂ" }, + ["שּׁ"]={ "שּ","ׁ" }, + ["שּׂ"]={ "שּ","ׂ" }, + ["אַ"]={ "א","ַ" }, + ["אָ"]={ "א","ָ" }, + ["אּ"]={ "א","ּ" }, + ["בּ"]={ "ב","ּ" }, + ["גּ"]={ "ג","ּ" }, + ["דּ"]={ "ד","ּ" }, + ["הּ"]={ "ה","ּ" }, + ["וּ"]={ "ו","ּ" }, + ["זּ"]={ "ז","ּ" }, + ["טּ"]={ "ט","ּ" }, + ["יּ"]={ "י","ּ" }, + ["ךּ"]={ "ך","ּ" }, + ["כּ"]={ "כ","ּ" }, + ["לּ"]={ "ל","ּ" }, + ["מּ"]={ "מ","ּ" }, + ["נּ"]={ "נ","ּ" }, + ["סּ"]={ "ס","ּ" }, + ["ףּ"]={ "ף","ּ" }, + ["פּ"]={ "פ","ּ" }, + ["צּ"]={ "צ","ּ" }, + ["קּ"]={ "ק","ּ" }, + ["רּ"]={ "ר","ּ" }, + ["שּ"]={ "ש","ּ" }, + ["תּ"]={ "ת","ּ" }, + ["וֹ"]={ "ו","ֹ" }, + ["בֿ"]={ "ב","ֿ" }, + ["כֿ"]={ "כ","ֿ" }, + ["פֿ"]={ "פ","ֿ" }, + ["𑂚"]={ "𑂙","𑂺" }, + ["𑂜"]={ "𑂛","𑂺" }, + ["𑂫"]={ "𑂥","𑂺" }, + ["𑄮"]={ "𑄱","𑄧" }, + ["𑄯"]={ "𑄲","𑄧" }, + ["𑍋"]={ "𑍇","𑌾" }, + ["𑍌"]={ "𑍇","𑍗" }, + ["𑒻"]={ "𑒹","𑒺" }, + ["𑒼"]={ "𑒹","𑒰" }, + ["𑒾"]={ "𑒹","𑒽" }, + ["𑖺"]={ "𑖸","𑖯" }, + ["𑖻"]={ "𑖹","𑖯" }, + ["𝅗𝅥"]={ "𝅗","𝅥" }, + ["𝅘𝅥"]={ "𝅘","𝅥" }, + ["𝅘𝅥𝅮"]={ "𝅘𝅥","𝅮" }, + ["𝅘𝅥𝅯"]={ "𝅘𝅥","𝅯" }, + ["𝅘𝅥𝅰"]={ "𝅘𝅥","𝅰" }, + ["𝅘𝅥𝅱"]={ "𝅘𝅥","𝅱" }, + ["𝅘𝅥𝅲"]={ "𝅘𝅥","𝅲" }, + ["𝆹𝅥"]={ "𝆹","𝅥" }, + ["𝆺𝅥"]={ "𝆺","𝅥" }, + ["𝆹𝅥𝅮"]={ "𝆹𝅥","𝅮" }, + ["𝆺𝅥𝅮"]={ "𝆺𝅥","𝅮" }, + ["𝆹𝅥𝅯"]={ "𝆹𝅥","𝅯" }, + ["𝆺𝅥𝅯"]={ "𝆺𝅥","𝅯" }, + }, + }, + { + ["data"]={ + ["À"]={ "A","̀" }, + ["Á"]={ "A","́" }, + ["Â"]={ "A","̂" }, + ["Ã"]={ "A","̃" }, + ["Ä"]={ "A","̈" }, + ["Å"]={ "A","̊" }, + ["Ç"]={ "C","̧" }, + ["È"]={ "E","̀" }, + ["É"]={ "E","́" }, + ["Ê"]={ "E","̂" }, + ["Ë"]={ "E","̈" }, + ["Ì"]={ "I","̀" }, + ["Í"]={ "I","́" }, + ["Î"]={ "I","̂" }, + ["Ï"]={ "I","̈" }, + ["Ñ"]={ "N","̃" }, + ["Ò"]={ "O","̀" }, + ["Ó"]={ "O","́" }, + ["Ô"]={ "O","̂" }, + ["Õ"]={ "O","̃" }, + ["Ö"]={ "O","̈" }, + ["Ù"]={ "U","̀" }, + ["Ú"]={ "U","́" }, + ["Û"]={ "U","̂" }, + ["Ü"]={ "U","̈" }, + ["Ý"]={ "Y","́" }, + ["à"]={ "a","̀" }, + ["á"]={ "a","́" }, + ["â"]={ "a","̂" }, + ["ã"]={ "a","̃" }, + ["ä"]={ "a","̈" }, + ["å"]={ "a","̊" }, + ["ç"]={ "c","̧" }, + ["è"]={ "e","̀" }, + ["é"]={ "e","́" }, + ["ê"]={ "e","̂" }, + ["ë"]={ "e","̈" }, + ["ì"]={ "i","̀" }, + ["í"]={ "i","́" }, + ["î"]={ "i","̂" }, + ["ï"]={ "i","̈" }, + ["ñ"]={ "n","̃" }, + ["ò"]={ "o","̀" }, + ["ó"]={ "o","́" }, + ["ô"]={ "o","̂" }, + ["õ"]={ "o","̃" }, + ["ö"]={ "o","̈" }, + ["ù"]={ "u","̀" }, + ["ú"]={ "u","́" }, + ["û"]={ "u","̂" }, + ["ü"]={ "u","̈" }, + ["ý"]={ "y","́" }, + ["ÿ"]={ "y","̈" }, + ["Ā"]={ "A","̄" }, + ["ā"]={ "a","̄" }, + ["Ă"]={ "A","̆" }, + ["ă"]={ "a","̆" }, + ["Ą"]={ "A","̨" }, + ["ą"]={ "a","̨" }, + ["Ć"]={ "C","́" }, + ["ć"]={ "c","́" }, + ["Ĉ"]={ "C","̂" }, + ["ĉ"]={ "c","̂" }, + ["Ċ"]={ "C","̇" }, + ["ċ"]={ "c","̇" }, + ["Č"]={ "C","̌" }, + ["č"]={ "c","̌" }, + ["Ď"]={ "D","̌" }, + ["ď"]={ "d","̌" }, + ["Ē"]={ "E","̄" }, + ["ē"]={ "e","̄" }, + ["Ĕ"]={ "E","̆" }, + ["ĕ"]={ "e","̆" }, + ["Ė"]={ "E","̇" }, + ["ė"]={ "e","̇" }, + ["Ę"]={ "E","̨" }, + ["ę"]={ "e","̨" }, + ["Ě"]={ "E","̌" }, + ["ě"]={ "e","̌" }, + ["Ĝ"]={ "G","̂" }, + ["ĝ"]={ "g","̂" }, + ["Ğ"]={ "G","̆" }, + ["ğ"]={ "g","̆" }, + ["Ġ"]={ "G","̇" }, + ["ġ"]={ "g","̇" }, + ["Ģ"]={ "G","̧" }, + ["ģ"]={ "g","̧" }, + ["Ĥ"]={ "H","̂" }, + ["ĥ"]={ "h","̂" }, + ["Ĩ"]={ "I","̃" }, + ["ĩ"]={ "i","̃" }, + ["Ī"]={ "I","̄" }, + ["ī"]={ "i","̄" }, + ["Ĭ"]={ "I","̆" }, + ["ĭ"]={ "i","̆" }, + ["Į"]={ "I","̨" }, + ["į"]={ "i","̨" }, + ["İ"]={ "I","̇" }, + ["Ĵ"]={ "J","̂" }, + ["ĵ"]={ "j","̂" }, + ["Ķ"]={ "K","̧" }, + ["ķ"]={ "k","̧" }, + ["Ĺ"]={ "L","́" }, + ["ĺ"]={ "l","́" }, + ["Ļ"]={ "L","̧" }, + ["ļ"]={ "l","̧" }, + ["Ľ"]={ "L","̌" }, + ["ľ"]={ "l","̌" }, + ["Ń"]={ "N","́" }, + ["ń"]={ "n","́" }, + ["Ņ"]={ "N","̧" }, + ["ņ"]={ "n","̧" }, + ["Ň"]={ "N","̌" }, + ["ň"]={ "n","̌" }, + ["Ō"]={ "O","̄" }, + ["ō"]={ "o","̄" }, + ["Ŏ"]={ "O","̆" }, + ["ŏ"]={ "o","̆" }, + ["Ő"]={ "O","̋" }, + ["ő"]={ "o","̋" }, + ["Ŕ"]={ "R","́" }, + ["ŕ"]={ "r","́" }, + ["Ŗ"]={ "R","̧" }, + ["ŗ"]={ "r","̧" }, + ["Ř"]={ "R","̌" }, + ["ř"]={ "r","̌" }, + ["Ś"]={ "S","́" }, + ["ś"]={ "s","́" }, + ["Ŝ"]={ "S","̂" }, + ["ŝ"]={ "s","̂" }, + ["Ş"]={ "S","̧" }, + ["ş"]={ "s","̧" }, + ["Š"]={ "S","̌" }, + ["š"]={ "s","̌" }, + ["Ţ"]={ "T","̧" }, + ["ţ"]={ "t","̧" }, + ["Ť"]={ "T","̌" }, + ["ť"]={ "t","̌" }, + ["Ũ"]={ "U","̃" }, + ["ũ"]={ "u","̃" }, + ["Ū"]={ "U","̄" }, + ["ū"]={ "u","̄" }, + ["Ŭ"]={ "U","̆" }, + ["ŭ"]={ "u","̆" }, + ["Ů"]={ "U","̊" }, + ["ů"]={ "u","̊" }, + ["Ű"]={ "U","̋" }, + ["ű"]={ "u","̋" }, + ["Ų"]={ "U","̨" }, + ["ų"]={ "u","̨" }, + ["Ŵ"]={ "W","̂" }, + ["ŵ"]={ "w","̂" }, + ["Ŷ"]={ "Y","̂" }, + ["ŷ"]={ "y","̂" }, + ["Ÿ"]={ "Y","̈" }, + ["Ź"]={ "Z","́" }, + ["ź"]={ "z","́" }, + ["Ż"]={ "Z","̇" }, + ["ż"]={ "z","̇" }, + ["Ž"]={ "Z","̌" }, + ["ž"]={ "z","̌" }, + ["Ơ"]={ "O","̛" }, + ["ơ"]={ "o","̛" }, + ["Ư"]={ "U","̛" }, + ["ư"]={ "u","̛" }, + ["Ǎ"]={ "A","̌" }, + ["ǎ"]={ "a","̌" }, + ["Ǐ"]={ "I","̌" }, + ["ǐ"]={ "i","̌" }, + ["Ǒ"]={ "O","̌" }, + ["ǒ"]={ "o","̌" }, + ["Ǔ"]={ "U","̌" }, + ["ǔ"]={ "u","̌" }, + ["Ǖ"]={ "Ü","̄" }, + ["ǖ"]={ "ü","̄" }, + ["Ǘ"]={ "Ü","́" }, + ["ǘ"]={ "ü","́" }, + ["Ǚ"]={ "Ü","̌" }, + ["ǚ"]={ "ü","̌" }, + ["Ǜ"]={ "Ü","̀" }, + ["ǜ"]={ "ü","̀" }, + ["Ǟ"]={ "Ä","̄" }, + ["ǟ"]={ "ä","̄" }, + ["Ǡ"]={ "Ȧ","̄" }, + ["ǡ"]={ "ȧ","̄" }, + ["Ǣ"]={ "Æ","̄" }, + ["ǣ"]={ "æ","̄" }, + ["Ǧ"]={ "G","̌" }, + ["ǧ"]={ "g","̌" }, + ["Ǩ"]={ "K","̌" }, + ["ǩ"]={ "k","̌" }, + ["Ǫ"]={ "O","̨" }, + ["ǫ"]={ "o","̨" }, + ["Ǭ"]={ "Ǫ","̄" }, + ["ǭ"]={ "ǫ","̄" }, + ["Ǯ"]={ "Ʒ","̌" }, + ["ǯ"]={ "ʒ","̌" }, + ["ǰ"]={ "j","̌" }, + ["Ǵ"]={ "G","́" }, + ["ǵ"]={ "g","́" }, + ["Ǹ"]={ "N","̀" }, + ["ǹ"]={ "n","̀" }, + ["Ǻ"]={ "Å","́" }, + ["ǻ"]={ "å","́" }, + ["Ǽ"]={ "Æ","́" }, + ["ǽ"]={ "æ","́" }, + ["Ǿ"]={ "Ø","́" }, + ["ǿ"]={ "ø","́" }, + ["Ȁ"]={ "A","̏" }, + ["ȁ"]={ "a","̏" }, + ["Ȃ"]={ "A","̑" }, + ["ȃ"]={ "a","̑" }, + ["Ȅ"]={ "E","̏" }, + ["ȅ"]={ "e","̏" }, + ["Ȇ"]={ "E","̑" }, + ["ȇ"]={ "e","̑" }, + ["Ȉ"]={ "I","̏" }, + ["ȉ"]={ "i","̏" }, + ["Ȋ"]={ "I","̑" }, + ["ȋ"]={ "i","̑" }, + ["Ȍ"]={ "O","̏" }, + ["ȍ"]={ "o","̏" }, + ["Ȏ"]={ "O","̑" }, + ["ȏ"]={ "o","̑" }, + ["Ȑ"]={ "R","̏" }, + ["ȑ"]={ "r","̏" }, + ["Ȓ"]={ "R","̑" }, + ["ȓ"]={ "r","̑" }, + ["Ȕ"]={ "U","̏" }, + ["ȕ"]={ "u","̏" }, + ["Ȗ"]={ "U","̑" }, + ["ȗ"]={ "u","̑" }, + ["Ș"]={ "S","̦" }, + ["ș"]={ "s","̦" }, + ["Ț"]={ "T","̦" }, + ["ț"]={ "t","̦" }, + ["Ȟ"]={ "H","̌" }, + ["ȟ"]={ "h","̌" }, + ["Ȧ"]={ "A","̇" }, + ["ȧ"]={ "a","̇" }, + ["Ȩ"]={ "E","̧" }, + ["ȩ"]={ "e","̧" }, + ["Ȫ"]={ "Ö","̄" }, + ["ȫ"]={ "ö","̄" }, + ["Ȭ"]={ "Õ","̄" }, + ["ȭ"]={ "õ","̄" }, + ["Ȯ"]={ "O","̇" }, + ["ȯ"]={ "o","̇" }, + ["Ȱ"]={ "Ȯ","̄" }, + ["ȱ"]={ "ȯ","̄" }, + ["Ȳ"]={ "Y","̄" }, + ["ȳ"]={ "y","̄" }, + ["̈́"]={ "̈","́" }, + ["΅"]={ "¨","́" }, + ["Ά"]={ "Α","́" }, + ["Έ"]={ "Ε","́" }, + ["Ή"]={ "Η","́" }, + ["Ί"]={ "Ι","́" }, + ["Ό"]={ "Ο","́" }, + ["Ύ"]={ "Υ","́" }, + ["Ώ"]={ "Ω","́" }, + ["ΐ"]={ "ϊ","́" }, + ["Ϊ"]={ "Ι","̈" }, + ["Ϋ"]={ "Υ","̈" }, + ["ά"]={ "α","́" }, + ["έ"]={ "ε","́" }, + ["ή"]={ "η","́" }, + ["ί"]={ "ι","́" }, + ["ΰ"]={ "ϋ","́" }, + ["ϊ"]={ "ι","̈" }, + ["ϋ"]={ "υ","̈" }, + ["ό"]={ "ο","́" }, + ["ύ"]={ "υ","́" }, + ["ώ"]={ "ω","́" }, + ["ϓ"]={ "ϒ","́" }, + ["ϔ"]={ "ϒ","̈" }, + ["Ѐ"]={ "Е","̀" }, + ["Ё"]={ "Е","̈" }, + ["Ѓ"]={ "Г","́" }, + ["Ї"]={ "І","̈" }, + ["Ќ"]={ "К","́" }, + ["Ѝ"]={ "И","̀" }, + ["Ў"]={ "У","̆" }, + ["Й"]={ "И","̆" }, + ["й"]={ "и","̆" }, + ["ѐ"]={ "е","̀" }, + ["ё"]={ "е","̈" }, + ["ѓ"]={ "г","́" }, + ["ї"]={ "і","̈" }, + ["ќ"]={ "к","́" }, + ["ѝ"]={ "и","̀" }, + ["ў"]={ "у","̆" }, + ["Ѷ"]={ "Ѵ","̏" }, + ["ѷ"]={ "ѵ","̏" }, + ["Ӂ"]={ "Ж","̆" }, + ["ӂ"]={ "ж","̆" }, + ["Ӑ"]={ "А","̆" }, + ["ӑ"]={ "а","̆" }, + ["Ӓ"]={ "А","̈" }, + ["ӓ"]={ "а","̈" }, + ["Ӗ"]={ "Е","̆" }, + ["ӗ"]={ "е","̆" }, + ["Ӛ"]={ "Ә","̈" }, + ["ӛ"]={ "ә","̈" }, + ["Ӝ"]={ "Ж","̈" }, + ["ӝ"]={ "ж","̈" }, + ["Ӟ"]={ "З","̈" }, + ["ӟ"]={ "з","̈" }, + ["Ӣ"]={ "И","̄" }, + ["ӣ"]={ "и","̄" }, + ["Ӥ"]={ "И","̈" }, + ["ӥ"]={ "и","̈" }, + ["Ӧ"]={ "О","̈" }, + ["ӧ"]={ "о","̈" }, + ["Ӫ"]={ "Ө","̈" }, + ["ӫ"]={ "ө","̈" }, + ["Ӭ"]={ "Э","̈" }, + ["ӭ"]={ "э","̈" }, + ["Ӯ"]={ "У","̄" }, + ["ӯ"]={ "у","̄" }, + ["Ӱ"]={ "У","̈" }, + ["ӱ"]={ "у","̈" }, + ["Ӳ"]={ "У","̋" }, + ["ӳ"]={ "у","̋" }, + ["Ӵ"]={ "Ч","̈" }, + ["ӵ"]={ "ч","̈" }, + ["Ӹ"]={ "Ы","̈" }, + ["ӹ"]={ "ы","̈" }, + ["آ"]={ "ا","ٓ" }, + ["أ"]={ "ا","ٔ" }, + ["ؤ"]={ "و","ٔ" }, + ["إ"]={ "ا","ٕ" }, + ["ئ"]={ "ي","ٔ" }, + ["ۀ"]={ "ە","ٔ" }, + ["ۂ"]={ "ہ","ٔ" }, + ["ۓ"]={ "ے","ٔ" }, + ["ऩ"]={ "न","़" }, + ["ऱ"]={ "र","़" }, + ["ऴ"]={ "ळ","़" }, + ["क़"]={ "क","़" }, + ["ख़"]={ "ख","़" }, + ["ग़"]={ "ग","़" }, + ["ज़"]={ "ज","़" }, + ["ड़"]={ "ड","़" }, + ["ढ़"]={ "ढ","़" }, + ["फ़"]={ "फ","़" }, + ["य़"]={ "य","़" }, + ["ো"]={ "ে","া" }, + ["ৌ"]={ "ে","ৗ" }, + ["ড়"]={ "ড","়" }, + ["ঢ়"]={ "ঢ","়" }, + ["য়"]={ "য","়" }, + ["ਲ਼"]={ "ਲ","਼" }, + ["ਸ਼"]={ "ਸ","਼" }, + ["ਖ਼"]={ "ਖ","਼" }, + ["ਗ਼"]={ "ਗ","਼" }, + ["ਜ਼"]={ "ਜ","਼" }, + ["ਫ਼"]={ "ਫ","਼" }, + ["ୈ"]={ "େ","ୖ" }, + ["ୋ"]={ "େ","ା" }, + ["ୌ"]={ "େ","ୗ" }, + ["ଡ଼"]={ "ଡ","଼" }, + ["ଢ଼"]={ "ଢ","଼" }, + ["ஔ"]={ "ஒ","ௗ" }, + ["ொ"]={ "ெ","ா" }, + ["ோ"]={ "ே","ா" }, + ["ௌ"]={ "ெ","ௗ" }, + ["ై"]={ "ె","ౖ" }, + ["ೀ"]={ "ಿ","ೕ" }, + ["ೇ"]={ "ೆ","ೕ" }, + ["ೈ"]={ "ೆ","ೖ" }, + ["ೊ"]={ "ೆ","ೂ" }, + ["ೋ"]={ "ೊ","ೕ" }, + ["ൊ"]={ "െ","ാ" }, + ["ോ"]={ "േ","ാ" }, + ["ൌ"]={ "െ","ൗ" }, + ["ේ"]={ "ෙ","්" }, + ["ො"]={ "ෙ","ා" }, + ["ෝ"]={ "ො","්" }, + ["ෞ"]={ "ෙ","ෟ" }, + ["གྷ"]={ "ག","ྷ" }, + ["ཌྷ"]={ "ཌ","ྷ" }, + ["དྷ"]={ "ད","ྷ" }, + ["བྷ"]={ "བ","ྷ" }, + ["ཛྷ"]={ "ཛ","ྷ" }, + ["ཀྵ"]={ "ཀ","ྵ" }, + ["ཱི"]={ "ཱ","ི" }, + ["ཱུ"]={ "ཱ","ུ" }, + ["ྲྀ"]={ "ྲ","ྀ" }, + ["ླྀ"]={ "ླ","ྀ" }, + ["ཱྀ"]={ "ཱ","ྀ" }, + ["ྒྷ"]={ "ྒ","ྷ" }, + ["ྜྷ"]={ "ྜ","ྷ" }, + ["ྡྷ"]={ "ྡ","ྷ" }, + ["ྦྷ"]={ "ྦ","ྷ" }, + ["ྫྷ"]={ "ྫ","ྷ" }, + ["ྐྵ"]={ "ྐ","ྵ" }, + ["ဦ"]={ "ဥ","ီ" }, + ["ᬆ"]={ "ᬅ","ᬵ" }, + ["ᬈ"]={ "ᬇ","ᬵ" }, + ["ᬊ"]={ "ᬉ","ᬵ" }, + ["ᬌ"]={ "ᬋ","ᬵ" }, + ["ᬎ"]={ "ᬍ","ᬵ" }, + ["ᬒ"]={ "ᬑ","ᬵ" }, + ["ᬻ"]={ "ᬺ","ᬵ" }, + ["ᬽ"]={ "ᬼ","ᬵ" }, + ["ᭀ"]={ "ᬾ","ᬵ" }, + ["ᭁ"]={ "ᬿ","ᬵ" }, + ["ᭃ"]={ "ᭂ","ᬵ" }, + ["Ḁ"]={ "A","̥" }, + ["ḁ"]={ "a","̥" }, + ["Ḃ"]={ "B","̇" }, + ["ḃ"]={ "b","̇" }, + ["Ḅ"]={ "B","̣" }, + ["ḅ"]={ "b","̣" }, + ["Ḇ"]={ "B","̱" }, + ["ḇ"]={ "b","̱" }, + ["Ḉ"]={ "Ç","́" }, + ["ḉ"]={ "ç","́" }, + ["Ḋ"]={ "D","̇" }, + ["ḋ"]={ "d","̇" }, + ["Ḍ"]={ "D","̣" }, + ["ḍ"]={ "d","̣" }, + ["Ḏ"]={ "D","̱" }, + ["ḏ"]={ "d","̱" }, + ["Ḑ"]={ "D","̧" }, + ["ḑ"]={ "d","̧" }, + ["Ḓ"]={ "D","̭" }, + ["ḓ"]={ "d","̭" }, + ["Ḕ"]={ "Ē","̀" }, + ["ḕ"]={ "ē","̀" }, + ["Ḗ"]={ "Ē","́" }, + ["ḗ"]={ "ē","́" }, + ["Ḙ"]={ "E","̭" }, + ["ḙ"]={ "e","̭" }, + ["Ḛ"]={ "E","̰" }, + ["ḛ"]={ "e","̰" }, + ["Ḝ"]={ "Ȩ","̆" }, + ["ḝ"]={ "ȩ","̆" }, + ["Ḟ"]={ "F","̇" }, + ["ḟ"]={ "f","̇" }, + ["Ḡ"]={ "G","̄" }, + ["ḡ"]={ "g","̄" }, + ["Ḣ"]={ "H","̇" }, + ["ḣ"]={ "h","̇" }, + ["Ḥ"]={ "H","̣" }, + ["ḥ"]={ "h","̣" }, + ["Ḧ"]={ "H","̈" }, + ["ḧ"]={ "h","̈" }, + ["Ḩ"]={ "H","̧" }, + ["ḩ"]={ "h","̧" }, + ["Ḫ"]={ "H","̮" }, + ["ḫ"]={ "h","̮" }, + ["Ḭ"]={ "I","̰" }, + ["ḭ"]={ "i","̰" }, + ["Ḯ"]={ "Ï","́" }, + ["ḯ"]={ "ï","́" }, + ["Ḱ"]={ "K","́" }, + ["ḱ"]={ "k","́" }, + ["Ḳ"]={ "K","̣" }, + ["ḳ"]={ "k","̣" }, + ["Ḵ"]={ "K","̱" }, + ["ḵ"]={ "k","̱" }, + ["Ḷ"]={ "L","̣" }, + ["ḷ"]={ "l","̣" }, + ["Ḹ"]={ "Ḷ","̄" }, + ["ḹ"]={ "ḷ","̄" }, + ["Ḻ"]={ "L","̱" }, + ["ḻ"]={ "l","̱" }, + ["Ḽ"]={ "L","̭" }, + ["ḽ"]={ "l","̭" }, + ["Ḿ"]={ "M","́" }, + ["ḿ"]={ "m","́" }, + ["Ṁ"]={ "M","̇" }, + ["ṁ"]={ "m","̇" }, + ["Ṃ"]={ "M","̣" }, + ["ṃ"]={ "m","̣" }, + ["Ṅ"]={ "N","̇" }, + ["ṅ"]={ "n","̇" }, + ["Ṇ"]={ "N","̣" }, + ["ṇ"]={ "n","̣" }, + ["Ṉ"]={ "N","̱" }, + ["ṉ"]={ "n","̱" }, + ["Ṋ"]={ "N","̭" }, + ["ṋ"]={ "n","̭" }, + ["Ṍ"]={ "Õ","́" }, + ["ṍ"]={ "õ","́" }, + ["Ṏ"]={ "Õ","̈" }, + ["ṏ"]={ "õ","̈" }, + ["Ṑ"]={ "Ō","̀" }, + ["ṑ"]={ "ō","̀" }, + ["Ṓ"]={ "Ō","́" }, + ["ṓ"]={ "ō","́" }, + ["Ṕ"]={ "P","́" }, + ["ṕ"]={ "p","́" }, + ["Ṗ"]={ "P","̇" }, + ["ṗ"]={ "p","̇" }, + ["Ṙ"]={ "R","̇" }, + ["ṙ"]={ "r","̇" }, + ["Ṛ"]={ "R","̣" }, + ["ṛ"]={ "r","̣" }, + ["Ṝ"]={ "Ṛ","̄" }, + ["ṝ"]={ "ṛ","̄" }, + ["Ṟ"]={ "R","̱" }, + ["ṟ"]={ "r","̱" }, + ["Ṡ"]={ "S","̇" }, + ["ṡ"]={ "s","̇" }, + ["Ṣ"]={ "S","̣" }, + ["ṣ"]={ "s","̣" }, + ["Ṥ"]={ "Ś","̇" }, + ["ṥ"]={ "ś","̇" }, + ["Ṧ"]={ "Š","̇" }, + ["ṧ"]={ "š","̇" }, + ["Ṩ"]={ "Ṣ","̇" }, + ["ṩ"]={ "ṣ","̇" }, + ["Ṫ"]={ "T","̇" }, + ["ṫ"]={ "t","̇" }, + ["Ṭ"]={ "T","̣" }, + ["ṭ"]={ "t","̣" }, + ["Ṯ"]={ "T","̱" }, + ["ṯ"]={ "t","̱" }, + ["Ṱ"]={ "T","̭" }, + ["ṱ"]={ "t","̭" }, + ["Ṳ"]={ "U","̤" }, + ["ṳ"]={ "u","̤" }, + ["Ṵ"]={ "U","̰" }, + ["ṵ"]={ "u","̰" }, + ["Ṷ"]={ "U","̭" }, + ["ṷ"]={ "u","̭" }, + ["Ṹ"]={ "Ũ","́" }, + ["ṹ"]={ "ũ","́" }, + ["Ṻ"]={ "Ū","̈" }, + ["ṻ"]={ "ū","̈" }, + ["Ṽ"]={ "V","̃" }, + ["ṽ"]={ "v","̃" }, + ["Ṿ"]={ "V","̣" }, + ["ṿ"]={ "v","̣" }, + ["Ẁ"]={ "W","̀" }, + ["ẁ"]={ "w","̀" }, + ["Ẃ"]={ "W","́" }, + ["ẃ"]={ "w","́" }, + ["Ẅ"]={ "W","̈" }, + ["ẅ"]={ "w","̈" }, + ["Ẇ"]={ "W","̇" }, + ["ẇ"]={ "w","̇" }, + ["Ẉ"]={ "W","̣" }, + ["ẉ"]={ "w","̣" }, + ["Ẋ"]={ "X","̇" }, + ["ẋ"]={ "x","̇" }, + ["Ẍ"]={ "X","̈" }, + ["ẍ"]={ "x","̈" }, + ["Ẏ"]={ "Y","̇" }, + ["ẏ"]={ "y","̇" }, + ["Ẑ"]={ "Z","̂" }, + ["ẑ"]={ "z","̂" }, + ["Ẓ"]={ "Z","̣" }, + ["ẓ"]={ "z","̣" }, + ["Ẕ"]={ "Z","̱" }, + ["ẕ"]={ "z","̱" }, + ["ẖ"]={ "h","̱" }, + ["ẗ"]={ "t","̈" }, + ["ẘ"]={ "w","̊" }, + ["ẙ"]={ "y","̊" }, + ["ẛ"]={ "ſ","̇" }, + ["Ạ"]={ "A","̣" }, + ["ạ"]={ "a","̣" }, + ["Ả"]={ "A","̉" }, + ["ả"]={ "a","̉" }, + ["Ấ"]={ "Â","́" }, + ["ấ"]={ "â","́" }, + ["Ầ"]={ "Â","̀" }, + ["ầ"]={ "â","̀" }, + ["Ẩ"]={ "Â","̉" }, + ["ẩ"]={ "â","̉" }, + ["Ẫ"]={ "Â","̃" }, + ["ẫ"]={ "â","̃" }, + ["Ậ"]={ "Ạ","̂" }, + ["ậ"]={ "ạ","̂" }, + ["Ắ"]={ "Ă","́" }, + ["ắ"]={ "ă","́" }, + ["Ằ"]={ "Ă","̀" }, + ["ằ"]={ "ă","̀" }, + ["Ẳ"]={ "Ă","̉" }, + ["ẳ"]={ "ă","̉" }, + ["Ẵ"]={ "Ă","̃" }, + ["ẵ"]={ "ă","̃" }, + ["Ặ"]={ "Ạ","̆" }, + ["ặ"]={ "ạ","̆" }, + ["Ẹ"]={ "E","̣" }, + ["ẹ"]={ "e","̣" }, + ["Ẻ"]={ "E","̉" }, + ["ẻ"]={ "e","̉" }, + ["Ẽ"]={ "E","̃" }, + ["ẽ"]={ "e","̃" }, + ["Ế"]={ "Ê","́" }, + ["ế"]={ "ê","́" }, + ["Ề"]={ "Ê","̀" }, + ["ề"]={ "ê","̀" }, + ["Ể"]={ "Ê","̉" }, + ["ể"]={ "ê","̉" }, + ["Ễ"]={ "Ê","̃" }, + ["ễ"]={ "ê","̃" }, + ["Ệ"]={ "Ẹ","̂" }, + ["ệ"]={ "ẹ","̂" }, + ["Ỉ"]={ "I","̉" }, + ["ỉ"]={ "i","̉" }, + ["Ị"]={ "I","̣" }, + ["ị"]={ "i","̣" }, + ["Ọ"]={ "O","̣" }, + ["ọ"]={ "o","̣" }, + ["Ỏ"]={ "O","̉" }, + ["ỏ"]={ "o","̉" }, + ["Ố"]={ "Ô","́" }, + ["ố"]={ "ô","́" }, + ["Ồ"]={ "Ô","̀" }, + ["ồ"]={ "ô","̀" }, + ["Ổ"]={ "Ô","̉" }, + ["ổ"]={ "ô","̉" }, + ["Ỗ"]={ "Ô","̃" }, + ["ỗ"]={ "ô","̃" }, + ["Ộ"]={ "Ọ","̂" }, + ["ộ"]={ "ọ","̂" }, + ["Ớ"]={ "Ơ","́" }, + ["ớ"]={ "ơ","́" }, + ["Ờ"]={ "Ơ","̀" }, + ["ờ"]={ "ơ","̀" }, + ["Ở"]={ "Ơ","̉" }, + ["ở"]={ "ơ","̉" }, + ["Ỡ"]={ "Ơ","̃" }, + ["ỡ"]={ "ơ","̃" }, + ["Ợ"]={ "Ơ","̣" }, + ["ợ"]={ "ơ","̣" }, + ["Ụ"]={ "U","̣" }, + ["ụ"]={ "u","̣" }, + ["Ủ"]={ "U","̉" }, + ["ủ"]={ "u","̉" }, + ["Ứ"]={ "Ư","́" }, + ["ứ"]={ "ư","́" }, + ["Ừ"]={ "Ư","̀" }, + ["ừ"]={ "ư","̀" }, + ["Ử"]={ "Ư","̉" }, + ["ử"]={ "ư","̉" }, + ["Ữ"]={ "Ư","̃" }, + ["ữ"]={ "ư","̃" }, + ["Ự"]={ "Ư","̣" }, + ["ự"]={ "ư","̣" }, + ["Ỳ"]={ "Y","̀" }, + ["ỳ"]={ "y","̀" }, + ["Ỵ"]={ "Y","̣" }, + ["ỵ"]={ "y","̣" }, + ["Ỷ"]={ "Y","̉" }, + ["ỷ"]={ "y","̉" }, + ["Ỹ"]={ "Y","̃" }, + ["ỹ"]={ "y","̃" }, + ["ἀ"]={ "α","̓" }, + ["ἁ"]={ "α","̔" }, + ["ἂ"]={ "ἀ","̀" }, + ["ἃ"]={ "ἁ","̀" }, + ["ἄ"]={ "ἀ","́" }, + ["ἅ"]={ "ἁ","́" }, + ["ἆ"]={ "ἀ","͂" }, + ["ἇ"]={ "ἁ","͂" }, + ["Ἀ"]={ "Α","̓" }, + ["Ἁ"]={ "Α","̔" }, + ["Ἂ"]={ "Ἀ","̀" }, + ["Ἃ"]={ "Ἁ","̀" }, + ["Ἄ"]={ "Ἀ","́" }, + ["Ἅ"]={ "Ἁ","́" }, + ["Ἆ"]={ "Ἀ","͂" }, + ["Ἇ"]={ "Ἁ","͂" }, + ["ἐ"]={ "ε","̓" }, + ["ἑ"]={ "ε","̔" }, + ["ἒ"]={ "ἐ","̀" }, + ["ἓ"]={ "ἑ","̀" }, + ["ἔ"]={ "ἐ","́" }, + ["ἕ"]={ "ἑ","́" }, + ["Ἐ"]={ "Ε","̓" }, + ["Ἑ"]={ "Ε","̔" }, + ["Ἒ"]={ "Ἐ","̀" }, + ["Ἓ"]={ "Ἑ","̀" }, + ["Ἔ"]={ "Ἐ","́" }, + ["Ἕ"]={ "Ἑ","́" }, + ["ἠ"]={ "η","̓" }, + ["ἡ"]={ "η","̔" }, + ["ἢ"]={ "ἠ","̀" }, + ["ἣ"]={ "ἡ","̀" }, + ["ἤ"]={ "ἠ","́" }, + ["ἥ"]={ "ἡ","́" }, + ["ἦ"]={ "ἠ","͂" }, + ["ἧ"]={ "ἡ","͂" }, + ["Ἠ"]={ "Η","̓" }, + ["Ἡ"]={ "Η","̔" }, + ["Ἢ"]={ "Ἠ","̀" }, + ["Ἣ"]={ "Ἡ","̀" }, + ["Ἤ"]={ "Ἠ","́" }, + ["Ἥ"]={ "Ἡ","́" }, + ["Ἦ"]={ "Ἠ","͂" }, + ["Ἧ"]={ "Ἡ","͂" }, + ["ἰ"]={ "ι","̓" }, + ["ἱ"]={ "ι","̔" }, + ["ἲ"]={ "ἰ","̀" }, + ["ἳ"]={ "ἱ","̀" }, + ["ἴ"]={ "ἰ","́" }, + ["ἵ"]={ "ἱ","́" }, + ["ἶ"]={ "ἰ","͂" }, + ["ἷ"]={ "ἱ","͂" }, + ["Ἰ"]={ "Ι","̓" }, + ["Ἱ"]={ "Ι","̔" }, + ["Ἲ"]={ "Ἰ","̀" }, + ["Ἳ"]={ "Ἱ","̀" }, + ["Ἴ"]={ "Ἰ","́" }, + ["Ἵ"]={ "Ἱ","́" }, + ["Ἶ"]={ "Ἰ","͂" }, + ["Ἷ"]={ "Ἱ","͂" }, + ["ὀ"]={ "ο","̓" }, + ["ὁ"]={ "ο","̔" }, + ["ὂ"]={ "ὀ","̀" }, + ["ὃ"]={ "ὁ","̀" }, + ["ὄ"]={ "ὀ","́" }, + ["ὅ"]={ "ὁ","́" }, + ["Ὀ"]={ "Ο","̓" }, + ["Ὁ"]={ "Ο","̔" }, + ["Ὂ"]={ "Ὀ","̀" }, + ["Ὃ"]={ "Ὁ","̀" }, + ["Ὄ"]={ "Ὀ","́" }, + ["Ὅ"]={ "Ὁ","́" }, + ["ὐ"]={ "υ","̓" }, + ["ὑ"]={ "υ","̔" }, + ["ὒ"]={ "ὐ","̀" }, + ["ὓ"]={ "ὑ","̀" }, + ["ὔ"]={ "ὐ","́" }, + ["ὕ"]={ "ὑ","́" }, + ["ὖ"]={ "ὐ","͂" }, + ["ὗ"]={ "ὑ","͂" }, + ["Ὑ"]={ "Υ","̔" }, + ["Ὓ"]={ "Ὑ","̀" }, + ["Ὕ"]={ "Ὑ","́" }, + ["Ὗ"]={ "Ὑ","͂" }, + ["ὠ"]={ "ω","̓" }, + ["ὡ"]={ "ω","̔" }, + ["ὢ"]={ "ὠ","̀" }, + ["ὣ"]={ "ὡ","̀" }, + ["ὤ"]={ "ὠ","́" }, + ["ὥ"]={ "ὡ","́" }, + ["ὦ"]={ "ὠ","͂" }, + ["ὧ"]={ "ὡ","͂" }, + ["Ὠ"]={ "Ω","̓" }, + ["Ὡ"]={ "Ω","̔" }, + ["Ὢ"]={ "Ὠ","̀" }, + ["Ὣ"]={ "Ὡ","̀" }, + ["Ὤ"]={ "Ὠ","́" }, + ["Ὥ"]={ "Ὡ","́" }, + ["Ὦ"]={ "Ὠ","͂" }, + ["Ὧ"]={ "Ὡ","͂" }, + ["ὰ"]={ "α","̀" }, + ["ὲ"]={ "ε","̀" }, + ["ὴ"]={ "η","̀" }, + ["ὶ"]={ "ι","̀" }, + ["ὸ"]={ "ο","̀" }, + ["ὺ"]={ "υ","̀" }, + ["ὼ"]={ "ω","̀" }, + ["ᾀ"]={ "ἀ","ͅ" }, + ["ᾁ"]={ "ἁ","ͅ" }, + ["ᾂ"]={ "ἂ","ͅ" }, + ["ᾃ"]={ "ἃ","ͅ" }, + ["ᾄ"]={ "ἄ","ͅ" }, + ["ᾅ"]={ "ἅ","ͅ" }, + ["ᾆ"]={ "ἆ","ͅ" }, + ["ᾇ"]={ "ἇ","ͅ" }, + ["ᾈ"]={ "Ἀ","ͅ" }, + ["ᾉ"]={ "Ἁ","ͅ" }, + ["ᾊ"]={ "Ἂ","ͅ" }, + ["ᾋ"]={ "Ἃ","ͅ" }, + ["ᾌ"]={ "Ἄ","ͅ" }, + ["ᾍ"]={ "Ἅ","ͅ" }, + ["ᾎ"]={ "Ἆ","ͅ" }, + ["ᾏ"]={ "Ἇ","ͅ" }, + ["ᾐ"]={ "ἠ","ͅ" }, + ["ᾑ"]={ "ἡ","ͅ" }, + ["ᾒ"]={ "ἢ","ͅ" }, + ["ᾓ"]={ "ἣ","ͅ" }, + ["ᾔ"]={ "ἤ","ͅ" }, + ["ᾕ"]={ "ἥ","ͅ" }, + ["ᾖ"]={ "ἦ","ͅ" }, + ["ᾗ"]={ "ἧ","ͅ" }, + ["ᾘ"]={ "Ἠ","ͅ" }, + ["ᾙ"]={ "Ἡ","ͅ" }, + ["ᾚ"]={ "Ἢ","ͅ" }, + ["ᾛ"]={ "Ἣ","ͅ" }, + ["ᾜ"]={ "Ἤ","ͅ" }, + ["ᾝ"]={ "Ἥ","ͅ" }, + ["ᾞ"]={ "Ἦ","ͅ" }, + ["ᾟ"]={ "Ἧ","ͅ" }, + ["ᾠ"]={ "ὠ","ͅ" }, + ["ᾡ"]={ "ὡ","ͅ" }, + ["ᾢ"]={ "ὢ","ͅ" }, + ["ᾣ"]={ "ὣ","ͅ" }, + ["ᾤ"]={ "ὤ","ͅ" }, + ["ᾥ"]={ "ὥ","ͅ" }, + ["ᾦ"]={ "ὦ","ͅ" }, + ["ᾧ"]={ "ὧ","ͅ" }, + ["ᾨ"]={ "Ὠ","ͅ" }, + ["ᾩ"]={ "Ὡ","ͅ" }, + ["ᾪ"]={ "Ὢ","ͅ" }, + ["ᾫ"]={ "Ὣ","ͅ" }, + ["ᾬ"]={ "Ὤ","ͅ" }, + ["ᾭ"]={ "Ὥ","ͅ" }, + ["ᾮ"]={ "Ὦ","ͅ" }, + ["ᾯ"]={ "Ὧ","ͅ" }, + ["ᾰ"]={ "α","̆" }, + ["ᾱ"]={ "α","̄" }, + ["ᾲ"]={ "ὰ","ͅ" }, + ["ᾳ"]={ "α","ͅ" }, + ["ᾴ"]={ "ά","ͅ" }, + ["ᾶ"]={ "α","͂" }, + ["ᾷ"]={ "ᾶ","ͅ" }, + ["Ᾰ"]={ "Α","̆" }, + ["Ᾱ"]={ "Α","̄" }, + ["Ὰ"]={ "Α","̀" }, + ["ᾼ"]={ "Α","ͅ" }, + ["῁"]={ "¨","͂" }, + ["ῂ"]={ "ὴ","ͅ" }, + ["ῃ"]={ "η","ͅ" }, + ["ῄ"]={ "ή","ͅ" }, + ["ῆ"]={ "η","͂" }, + ["ῇ"]={ "ῆ","ͅ" }, + ["Ὲ"]={ "Ε","̀" }, + ["Ὴ"]={ "Η","̀" }, + ["ῌ"]={ "Η","ͅ" }, + ["῍"]={ "᾿","̀" }, + ["῎"]={ "᾿","́" }, + ["῏"]={ "᾿","͂" }, + ["ῐ"]={ "ι","̆" }, + ["ῑ"]={ "ι","̄" }, + ["ῒ"]={ "ϊ","̀" }, + ["ῖ"]={ "ι","͂" }, + ["ῗ"]={ "ϊ","͂" }, + ["Ῐ"]={ "Ι","̆" }, + ["Ῑ"]={ "Ι","̄" }, + ["Ὶ"]={ "Ι","̀" }, + ["῝"]={ "῾","̀" }, + ["῞"]={ "῾","́" }, + ["῟"]={ "῾","͂" }, + ["ῠ"]={ "υ","̆" }, + ["ῡ"]={ "υ","̄" }, + ["ῢ"]={ "ϋ","̀" }, + ["ῤ"]={ "ρ","̓" }, + ["ῥ"]={ "ρ","̔" }, + ["ῦ"]={ "υ","͂" }, + ["ῧ"]={ "ϋ","͂" }, + ["Ῠ"]={ "Υ","̆" }, + ["Ῡ"]={ "Υ","̄" }, + ["Ὺ"]={ "Υ","̀" }, + ["Ῥ"]={ "Ρ","̔" }, + ["῭"]={ "¨","̀" }, + ["ῲ"]={ "ὼ","ͅ" }, + ["ῳ"]={ "ω","ͅ" }, + ["ῴ"]={ "ώ","ͅ" }, + ["ῶ"]={ "ω","͂" }, + ["ῷ"]={ "ῶ","ͅ" }, + ["Ὸ"]={ "Ο","̀" }, + ["Ὼ"]={ "Ω","̀" }, + ["ῼ"]={ "Ω","ͅ" }, + ["↚"]={ "←","̸" }, + ["↛"]={ "→","̸" }, + ["↮"]={ "↔","̸" }, + ["⇍"]={ "⇐","̸" }, + ["⇎"]={ "⇔","̸" }, + ["⇏"]={ "⇒","̸" }, + ["∄"]={ "∃","̸" }, + ["∉"]={ "∈","̸" }, + ["∌"]={ "∋","̸" }, + ["∤"]={ "∣","̸" }, + ["∦"]={ "∥","̸" }, + ["≁"]={ "∼","̸" }, + ["≄"]={ "≃","̸" }, + ["≇"]={ "≅","̸" }, + ["≉"]={ "≈","̸" }, + ["≠"]={ "=","̸" }, + ["≢"]={ "≡","̸" }, + ["≭"]={ "≍","̸" }, + ["≮"]={ "<","̸" }, + ["≯"]={ ">","̸" }, + ["≰"]={ "≤","̸" }, + ["≱"]={ "≥","̸" }, + ["≴"]={ "≲","̸" }, + ["≵"]={ "≳","̸" }, + ["≸"]={ "≶","̸" }, + ["≹"]={ "≷","̸" }, + ["⊀"]={ "≺","̸" }, + ["⊁"]={ "≻","̸" }, + ["⊄"]={ "⊂","̸" }, + ["⊅"]={ "⊃","̸" }, + ["⊈"]={ "⊆","̸" }, + ["⊉"]={ "⊇","̸" }, + ["⊬"]={ "⊢","̸" }, + ["⊭"]={ "⊨","̸" }, + ["⊮"]={ "⊩","̸" }, + ["⊯"]={ "⊫","̸" }, + ["⋠"]={ "≼","̸" }, + ["⋡"]={ "≽","̸" }, + ["⋢"]={ "⊑","̸" }, + ["⋣"]={ "⊒","̸" }, + ["⋪"]={ "⊲","̸" }, + ["⋫"]={ "⊳","̸" }, + ["⋬"]={ "⊴","̸" }, + ["⋭"]={ "⊵","̸" }, + ["⫝̸"]={ "⫝","̸" }, + ["が"]={ "か","゙" }, + ["ぎ"]={ "き","゙" }, + ["ぐ"]={ "く","゙" }, + ["げ"]={ "け","゙" }, + ["ご"]={ "こ","゙" }, + ["ざ"]={ "さ","゙" }, + ["じ"]={ "し","゙" }, + ["ず"]={ "す","゙" }, + ["ぜ"]={ "せ","゙" }, + ["ぞ"]={ "そ","゙" }, + ["だ"]={ "た","゙" }, + ["ぢ"]={ "ち","゙" }, + ["づ"]={ "つ","゙" }, + ["で"]={ "て","゙" }, + ["ど"]={ "と","゙" }, + ["ば"]={ "は","゙" }, + ["ぱ"]={ "は","゚" }, + ["び"]={ "ひ","゙" }, + ["ぴ"]={ "ひ","゚" }, + ["ぶ"]={ "ふ","゙" }, + ["ぷ"]={ "ふ","゚" }, + ["べ"]={ "へ","゙" }, + ["ぺ"]={ "へ","゚" }, + ["ぼ"]={ "ほ","゙" }, + ["ぽ"]={ "ほ","゚" }, + ["ゔ"]={ "う","゙" }, + ["ゞ"]={ "ゝ","゙" }, + ["ガ"]={ "カ","゙" }, + ["ギ"]={ "キ","゙" }, + ["グ"]={ "ク","゙" }, + ["ゲ"]={ "ケ","゙" }, + ["ゴ"]={ "コ","゙" }, + ["ザ"]={ "サ","゙" }, + ["ジ"]={ "シ","゙" }, + ["ズ"]={ "ス","゙" }, + ["ゼ"]={ "セ","゙" }, + ["ゾ"]={ "ソ","゙" }, + ["ダ"]={ "タ","゙" }, + ["ヂ"]={ "チ","゙" }, + ["ヅ"]={ "ツ","゙" }, + ["デ"]={ "テ","゙" }, + ["ド"]={ "ト","゙" }, + ["バ"]={ "ハ","゙" }, + ["パ"]={ "ハ","゚" }, + ["ビ"]={ "ヒ","゙" }, + ["ピ"]={ "ヒ","゚" }, + ["ブ"]={ "フ","゙" }, + ["プ"]={ "フ","゚" }, + ["ベ"]={ "ヘ","゙" }, + ["ペ"]={ "ヘ","゚" }, + ["ボ"]={ "ホ","゙" }, + ["ポ"]={ "ホ","゚" }, + ["ヴ"]={ "ウ","゙" }, + ["ヷ"]={ "ワ","゙" }, + ["ヸ"]={ "ヰ","゙" }, + ["ヹ"]={ "ヱ","゙" }, + ["ヺ"]={ "ヲ","゙" }, + ["ヾ"]={ "ヽ","゙" }, + ["יִ"]={ "י","ִ" }, + ["ײַ"]={ "ײ","ַ" }, + ["שׁ"]={ "ש","ׁ" }, + ["שׂ"]={ "ש","ׂ" }, + ["שּׁ"]={ "שּ","ׁ" }, + ["שּׂ"]={ "שּ","ׂ" }, + ["אַ"]={ "א","ַ" }, + ["אָ"]={ "א","ָ" }, + ["אּ"]={ "א","ּ" }, + ["בּ"]={ "ב","ּ" }, + ["גּ"]={ "ג","ּ" }, + ["דּ"]={ "ד","ּ" }, + ["הּ"]={ "ה","ּ" }, + ["וּ"]={ "ו","ּ" }, + ["זּ"]={ "ז","ּ" }, + ["טּ"]={ "ט","ּ" }, + ["יּ"]={ "י","ּ" }, + ["ךּ"]={ "ך","ּ" }, + ["כּ"]={ "כ","ּ" }, + ["לּ"]={ "ל","ּ" }, + ["מּ"]={ "מ","ּ" }, + ["נּ"]={ "נ","ּ" }, + ["סּ"]={ "ס","ּ" }, + ["ףּ"]={ "ף","ּ" }, + ["פּ"]={ "פ","ּ" }, + ["צּ"]={ "צ","ּ" }, + ["קּ"]={ "ק","ּ" }, + ["רּ"]={ "ר","ּ" }, + ["שּ"]={ "ש","ּ" }, + ["תּ"]={ "ת","ּ" }, + ["וֹ"]={ "ו","ֹ" }, + ["בֿ"]={ "ב","ֿ" }, + ["כֿ"]={ "כ","ֿ" }, + ["פֿ"]={ "פ","ֿ" }, + ["𑂚"]={ "𑂙","𑂺" }, + ["𑂜"]={ "𑂛","𑂺" }, + ["𑂫"]={ "𑂥","𑂺" }, + ["𑄮"]={ "𑄱","𑄧" }, + ["𑄯"]={ "𑄲","𑄧" }, + ["𑍋"]={ "𑍇","𑌾" }, + ["𑍌"]={ "𑍇","𑍗" }, + ["𑒻"]={ "𑒹","𑒺" }, + ["𑒼"]={ "𑒹","𑒰" }, + ["𑒾"]={ "𑒹","𑒽" }, + ["𑖺"]={ "𑖸","𑖯" }, + ["𑖻"]={ "𑖹","𑖯" }, + ["𝅗𝅥"]={ "𝅗","𝅥" }, + ["𝅘𝅥"]={ "𝅘","𝅥" }, + ["𝅘𝅥𝅮"]={ "𝅘𝅥","𝅮" }, + ["𝅘𝅥𝅯"]={ "𝅘𝅥","𝅯" }, + ["𝅘𝅥𝅰"]={ "𝅘𝅥","𝅰" }, + ["𝅘𝅥𝅱"]={ "𝅘𝅥","𝅱" }, + ["𝅘𝅥𝅲"]={ "𝅘𝅥","𝅲" }, + ["𝆹𝅥"]={ "𝆹","𝅥" }, + ["𝆺𝅥"]={ "𝆺","𝅥" }, + ["𝆹𝅥𝅮"]={ "𝆹𝅥","𝅮" }, + ["𝆺𝅥𝅮"]={ "𝆺𝅥","𝅮" }, + ["𝆹𝅥𝅯"]={ "𝆹𝅥","𝅯" }, + ["𝆺𝅥𝅯"]={ "𝆺𝅥","𝅯" }, + }, + }, + }, + ["name"]="collapse", + ["prepend"]=true, + ["type"]="ligature", +} + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “fonts-lig”] --- + + +do --- [luaotfload, fontloader-2018-09-24.lua scope for “fonts-gbn” 10ecdf01e7c926e5128ad8a9dff4d677] --- + +if not modules then modules={} end modules ['luatex-fonts-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 + os.exit() +end +local next=next +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 n_ligaturing=node.ligaturing +local n_kerning=node.kerning +local d_ligaturing=nuts.ligaturing +local d_kerning=nuts.kerning +local basemodepass=true +local function l_warning() logs.report("fonts","don't call 'node.ligaturing' directly") l_warning=nil end +local function k_warning() logs.report("fonts","don't call 'node.kerning' 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 nuts.ligaturing(...) + if basemodepass and l_warning then + l_warning() + end + return d_ligaturing(...) +end +function nuts.kerning(...) + if basemodepass and k_warning then + k_warning() + end + return d_kerning(...) +end +function nodes.handlers.setbasemodepass(v) + basemodepass=v +end +local function nodepass(head,groupcode,size,packtype,direction) + local fontdata=fonts.hashes.identifiers + if fontdata then + local usedfonts={} + local basefonts={} + local prevfont=nil + local basefont=nil + local variants=nil + local redundant=nil + local nofused=0 + for n in traverse_id(glyph_code,head) 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 + nofused=nofused+1 + 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==head then + head=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,head) 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 + nofused=nofused+1 + 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,direction,nofused) 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=head==start + local prev,next + if stop then + next=getnext(stop) + start,stop=d_ligaturing(start,stop) + start,stop=d_kerning(start,stop) + else + prev=getprev(start) + start=d_ligaturing(start) + start=d_kerning(start) + end + if prev then + setlink(prev,start) + end + if next then + setlink(stop,next) + end + if front and head~=start then + head=start + end + end + end + end + end + return head +end +local function basepass(head) + if basemodepass then + head=d_ligaturing(head) + head=d_kerning(head) + end + return head +end +local protectpass=node.direct.protect_glyphs +local injectpass=nodes.injections.handler +function nodes.handlers.nodepass(head,...) + if head then + return tonode(nodepass(tonut(head),...)) + end +end +function nodes.handlers.basepass(head) + if head then + return tonode(basepass(tonut(head))) + end +end +function nodes.handlers.injectpass(head) + if head then + return tonode(injectpass(tonut(head))) + end +end +function nodes.handlers.protectpass(head) + if head then + protectpass(tonut(head)) + return head + end +end +function nodes.simple_font_handler(head,groupcode,size,packtype,direction) + if head then + head=tonut(head) + head=nodepass(head,groupcode,size,packtype,direction) + head=injectpass(head) + if not basemodepass then + head=basepass(head) + end + protectpass(head) + head=tonode(head) + end + return head +end + +end --- [luaotfload, fontloader-2018-09-24.lua scope for “fonts-gbn”] --- + + +--- vim:ft=lua:sw=2:ts=8:et:tw=79 diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-gen.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-gen.lua index 2be55cceabc..64f3be21832 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-gen.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-gen.lua @@ -7,10 +7,18 @@ if not modules then modules = { } end modules ['luat-basics-gen'] = { } if context then - texio.write_nl("fatal error: this module is not for context") os.exit() end +-- We could load a few more of the general context libraries but it would +-- not make plain / latex users more happy I guess. So, we stick to some +-- placeholders. + +local match, gmatch, gsub, lower = string.match, string.gmatch, string.gsub, string.lower +local formatters, split, format, dump = string.formatters, string.split, string.format, string.dump +local loadfile, type = loadfile, type +local setmetatable, getmetatable, collectgarbage = setmetatable, getmetatable, collectgarbage + local dummyfunction = function() end @@ -18,13 +26,22 @@ local dummyreporter = function(c) return function(f,...) local r = texio.reporter or texio.write_nl if f then - r(c .. " : " .. string.formatters(f,...)) + r(c .. " : " .. (formatters or format)(f,...)) else r("") end end end +local dummyreport = function(c,f,...) + local r = texio.reporter or texio.write_nl + if f then + r(c .. " : " .. (formatters or format)(f,...)) + else + r("") + end +end + statistics = { register = dummyfunction, starttiming = dummyfunction, @@ -59,17 +76,18 @@ logs = { new = dummyreporter, reporter = dummyreporter, messenger = dummyreporter, - report = dummyfunction, + report = dummyreport, } callbacks = { register = function(n,f) return callback.register(n,f) end, - } -utilities = utilities or { } utilities.storage = { +utilities = utilities or { } + +utilities.storage = utilities.storage or { allocate = function(t) return t or { } end, @@ -78,6 +96,28 @@ utilities = utilities or { } utilities.storage = { end, } +utilities.parsers = utilities.parsers or { + -- these are less flexible than in context but ok + -- for generic purpose + settings_to_array = function(s) + return split(s,",") + end, + settings_to_hash = function(s) + local t = { } + for k, v in gmatch(s,"([^%s,=]+)=([^%s,]+)") do + t[k] = v + end + return t + end, + settings_to_hash_colon_too = function(s) + local t = { } + for k, v in gmatch(s,"([^%s,=:]+)[=:]([^%s,]+)") do + t[k] = v + end + return t + end, +} + characters = characters or { data = { } } @@ -98,17 +138,18 @@ local remapper = { pfb = "type1 fonts", -- needed for vector loading afm = "afm", enc = "enc files", + lua = "tex", } function resolvers.findfile(name,fileformat) - name = string.gsub(name,"\\","/") + name = gsub(name,"\\","/") if not fileformat or fileformat == "" then fileformat = file.suffix(name) if fileformat == "" then fileformat = "tex" end end - fileformat = string.lower(fileformat) + fileformat = lower(fileformat) fileformat = remapper[fileformat] or fileformat local found = kpse.find_file(name,fileformat) if not found or found == "" then @@ -117,13 +158,6 @@ function resolvers.findfile(name,fileformat) return found end --- function resolvers.findbinfile(name,fileformat) --- if not fileformat or fileformat == "" then --- fileformat = file.suffix(name) --- end --- return resolvers.findfile(name,(fileformat and remapper[fileformat]) or fileformat) --- end - resolvers.findbinfile = resolvers.findfile function resolvers.loadbinfile(filename,filetype) @@ -191,14 +225,14 @@ do cachepaths = "." end - cachepaths = string.split(cachepaths,os.type == "windows" and ";" or ":") + cachepaths = split(cachepaths,os.type == "windows" and ";" or ":") for i=1,#cachepaths do local cachepath = cachepaths[i] if not lfs.isdir(cachepath) then lfs.mkdirs(cachepath) -- needed for texlive and latex if lfs.isdir(cachepath) then - texio.write(string.format("(created cache path: %s)",cachepath)) + logs.report("system","creating cache path '%s'",cachepath) end end if file.is_writable(cachepath) then @@ -217,16 +251,16 @@ do end if not writable then - texio.write_nl("quiting: fix your writable cache path") + logs.report("system","no writeable cache path, quiting") os.exit() elseif #readables == 0 then - texio.write_nl("quiting: fix your readable cache path") + logs.report("system","no readable cache path, quiting") os.exit() elseif #readables == 1 and readables[1] == writable then - texio.write(string.format("(using cache: %s)",writable)) + logs.report("system","using cache '%s'",writable) else - texio.write(string.format("(using write cache: %s)",writable)) - texio.write(string.format("(using read cache: %s)",table.concat(readables, " "))) + logs.report("system","using write cache '%s'",writable) + logs.report("system","using read cache '%s'",table.concat(readables," ")) end end @@ -258,75 +292,34 @@ function caches.is_writable(path,name) return fullname and file.is_writable(fullname) end --- function caches.loaddata(paths,name) --- for i=1,#paths do --- local data = false --- local luaname, lucname = makefullname(paths[i],name) --- if lucname and not lfs.isfile(lucname) and type(caches.compile) == "function" then --- -- in case we used luatex and luajittex mixed ... lub or luc file --- texio.write(string.format("(compiling luc: %s)",lucname)) --- data = loadfile(luaname) --- if data then --- data = data() --- end --- if data then --- caches.compile(data,luaname,lucname) --- return data --- end --- end --- if lucname and lfs.isfile(lucname) then -- maybe also check for size --- texio.write(string.format("(load luc: %s)",lucname)) --- data = loadfile(lucname) --- if data then --- data = data() --- end --- if data then --- return data --- else --- texio.write(string.format("(loading failed: %s)",lucname)) --- end --- end --- if luaname and lfs.isfile(luaname) then --- texio.write(string.format("(load lua: %s)",luaname)) --- data = loadfile(luaname) --- if data then --- data = data() --- end --- if data then --- return data --- end --- end --- end --- end - function caches.loaddata(readables,name,writable) for i=1,#readables do local path = readables[i] local loader = false local luaname, lucname = makefullname(path,name) if lfs.isfile(lucname) then - texio.write(string.format("(load luc: %s)",lucname)) + logs.report("system","loading luc file '%s'",lucname) loader = loadfile(lucname) end if not loader and lfs.isfile(luaname) then -- can be different paths when we read a file database from disk local luacrap, lucname = makefullname(writable,name) - texio.write(string.format("(compiling luc: %s)",lucname)) + logs.report("system","compiling luc file '%s'",lucname) if lfs.isfile(lucname) then loader = loadfile(lucname) end caches.compile(data,luaname,lucname) if lfs.isfile(lucname) then - texio.write(string.format("(load luc: %s)",lucname)) + logs.report("system","loading luc file '%s'",lucname) loader = loadfile(lucname) else - texio.write(string.format("(loading failed: %s)",lucname)) + logs.report("system","error in loading luc file '%s'",lucname) end if not loader then - texio.write(string.format("(load lua: %s)",luaname)) + logs.report("system","loading lua file '%s'",luaname) loader = loadfile(luaname) else - texio.write(string.format("(loading failed: %s)",luaname)) + logs.report("system","error in loading lua file '%s'",luaname) end end if loader then @@ -341,42 +334,19 @@ end function caches.savedata(path,name,data) local luaname, lucname = makefullname(path,name) if luaname then - texio.write(string.format("(save: %s)",luaname)) + logs.report("system","saving lua file '%s'",luaname) table.tofile(luaname,data,true) if lucname and type(caches.compile) == "function" then os.remove(lucname) -- better be safe - texio.write(string.format("(save: %s)",lucname)) + logs.report("system","saving luc file '%s'",lucname) caches.compile(data,luaname,lucname) end end end --- According to KH os.execute is not permitted in plain/latex so there is --- no reason to use the normal context way. So the method here is slightly --- different from the one we have in context. We also use different suffixes --- as we don't want any clashes (sharing cache files is not that handy as --- context moves on faster.) --- --- Beware: serialization might fail on large files (so maybe we should pcall --- this) in which case one should limit the method to luac and enable support --- for execution. - --- function caches.compile(data,luaname,lucname) --- local d = io.loaddata(luaname) --- if not d or d == "" then --- d = table.serialize(data,true) -- slow --- end --- if d and d ~= "" then --- local f = io.open(lucname,'w') --- if f then --- local s = loadstring(d) --- if s then --- f:write(string.dump(s,true)) --- end --- f:close() --- end --- end --- end +-- The method here is slightly different from the one we have in context. We +-- also use different suffixes as we don't want any clashes (sharing cache +-- files is not that handy as context moves on faster.) function caches.compile(data,luaname,lucname) local d = io.loaddata(luaname) @@ -388,23 +358,14 @@ function caches.compile(data,luaname,lucname) if f then local s = loadstring(d) if s then - f:write(string.dump(s,true)) + f:write(dump(s,true)) end f:close() end end end --- - --- function table.setmetatableindex(t,f) --- if type(t) ~= "table" then --- f = f or t --- t = { } --- end --- setmetatable(t,{ __index = f }) --- return t --- end +-- simplfied version: function table.setmetatableindex(t,f) if type(t) ~= "table" then @@ -422,13 +383,23 @@ function table.setmetatableindex(t,f) return t end +function table.makeweak(t) + local m = getmetatable(t) + if m then + m.__mode = "v" + else + setmetatable(t,{ __mode = "v" }) + end + return t +end + -- helper for plain: arguments = { } if arg then for i=1,#arg do - local k, v = string.match(arg[i],"^%-%-([^=]+)=?(.-)$") + local k, v = match(arg[i],"^%-%-([^=]+)=?(.-)$") if k and v then arguments[k] = v end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-nod.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-nod.lua index 42a7a2e29eb..c3b467a11b3 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-nod.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics-nod.lua @@ -7,12 +7,12 @@ if not modules then modules = { } end modules ['luatex-fonts-nod'] = { } if context then - texio.write_nl("fatal error: this module is not for context") os.exit() end --- Don't depend on code here as it is only needed to complement the --- font handler code. +-- Don't depend on code here as it is only needed to complement the font handler +-- code. I will move some to another namespace as I don't see other macro packages +-- use the context logic. It's a subset anyway. -- Attributes: @@ -48,7 +48,6 @@ end -- Nodes (a subset of context so that we don't get too much unused code): nodes = { } -nodes.pool = { } nodes.handlers = { } local nodecodes = { } @@ -60,11 +59,11 @@ for k, v in next, node.types() do nodecodes[k] = v nodecodes[v] = k end -for i=0,#glyphcodes do - glyphcodes[glyphcodes[i]] = i +for k, v in next, glyphcodes do + glyphcodes[v] = k end -for i=0,#disccodes do - disccodes[disccodes[i]] = i +for k, v in next, glyphcodes do + disccodes[v] = k end nodes.nodecodes = nodecodes @@ -73,11 +72,10 @@ 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 +nodes.handlers.protectglyphs = node.protect_glyphs -- beware: nodes! +nodes.handlers.unprotectglyphs = node.unprotect_glyphs -- beware: nodes! local math_code = nodecodes.math local end_of_math = node.end_of_math @@ -108,12 +106,6 @@ 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 @@ -171,6 +163,8 @@ nodes.mlist_to_hlist = node.mlist_to_hlist -- we can go nuts (e.g. experimental); this split permits us us keep code -- used elsewhere stable but at the same time play around in context +-- much of this will go away + local direct = node.direct local nuts = { } nodes.nuts = nuts @@ -201,16 +195,40 @@ 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.setsubtype = direct.setsubtype nuts.getchar = direct.getchar nuts.setchar = direct.setchar nuts.getdisc = direct.getdisc nuts.setdisc = direct.setdisc nuts.setlink = direct.setlink +nuts.setsplit = direct.setsplit 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 +nuts.setlist = direct.setlist + +nuts.getoffsets = direct.getoffsets or + function(n) + return getfield(n,"xoffset"), getfield(n,"yoffset") + end +nuts.setoffsets = direct.setoffsets or + function(n,x,y) + if x then setfield(n,"xoffset",x) end + if y then setfield(n,"xoffset",y) end + end + +nuts.getleader = direct.getleader or function(n) return getfield(n,"leader") end +nuts.setleader = direct.setleader or function(n,l) setfield(n,"leader",l) end +nuts.getcomponents = direct.getcomponents or function(n) return getfield(n,"components") end +nuts.setcomponents = direct.setcomponents or function(n,c) setfield(n,"components",c) end +nuts.getkern = direct.getkern or function(n) return getfield(n,"kern") end +nuts.setkern = direct.setkern or function(n,k) setfield(n,"kern",k) end +nuts.getdir = direct.getdir or function(n) return getfield(n,"dir") end +nuts.setdir = direct.setdir or function(n,d) setfield(n,"dir",d) end +nuts.getwidth = direct.getwidth or function(n) return getfield(n,"width") end +nuts.setwidth = direct.setwidth or function(n,w) return setfield(n,"width",w) end +nuts.getheight = direct.getheight or function(n) return getfield(n,"height") end +nuts.setheight = direct.setheight or function(n,h) return setfield(n,"height",h) end +nuts.getdepth = direct.getdepth or function(n) return getfield(n,"depth") end +nuts.setdepth = direct.setdepth or function(n,d) return setfield(n,"depth",d) end if not direct.is_glyph then local getchar = direct.getchar @@ -267,22 +285,14 @@ 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.traverse_glyph = direct.traverse_glyph nuts.ligaturing = direct.ligaturing nuts.kerning = direct.kerning +nuts.new = direct.new 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 - -- properties as used in the (new) injector: local propertydata = direct.get_properties_table() @@ -312,3 +322,182 @@ end nodes.setprop = nodes.setproperty nodes.getprop = nodes.getproperty + +-- a few helpers (we need to keep 'm in sync with context) .. some day components +-- might go so here we isolate them + +local setprev = nuts.setprev +local setnext = nuts.setnext +local getnext = nuts.getnext +local setlink = nuts.setlink +local getfield = nuts.getfield +local setfield = nuts.setfield +local getcomponents = nuts.getcomponents +local setcomponents = nuts.setcomponents + +local find_tail = nuts.tail +local flush_list = nuts.flush_list +local flush_node = nuts.flush_node +local traverse_id = nuts.traverse_id +local copy_node = nuts.copy_node + +local glyph_code = nodes.nodecodes.glyph + +function nuts.set_components(target,start,stop) + local head = getcomponents(target) + if head then + flush_list(head) + head = nil + end + if start then + setprev(start) + else + return nil + end + if stop then + setnext(stop) + end + local tail = nil + while start do + local c = getcomponents(start) + local n = getnext(start) + if c then + if head then + setlink(tail,c) + else + head = c + end + tail = find_tail(c) + setcomponents(start) + flush_node(start) + else + if head then + setlink(tail,start) + else + head = start + end + tail = start + end + start = n + end + setcomponents(target,head) + -- maybe also upgrade the subtype but we don't use it anyway + return head +end + +nuts.get_components = nuts.getcomponents + +function nuts.take_components(target) + local c = getcomponents(target) + setcomponents(target) + -- maybe also upgrade the subtype but we don't use it anyway + return c +end + +function nuts.count_components(n,marks) + local components = getcomponents(n) + if components then + if marks then + local i = 0 + for g in traverse_id(glyph_code,components) do + if not marks[getchar(g)] then + i = i + 1 + end + end + return i + else + return count(glyph_code,components) + end + else + return 0 + end +end + +function nuts.copy_no_components(g,copyinjection) + local components = getcomponents(g) + if components then + setcomponents(g) + local n = copy_node(g) + if copyinjection then + copyinjection(n,g) + end + setcomponents(g,components) + -- maybe also upgrade the subtype but we don't use it anyway + return n + else + local n = copy_node(g) + if copyinjection then + copyinjection(n,g) + end + return n + end +end + +function nuts.copy_only_glyphs(current) + local head = nil + local previous = nil + for n in traverse_id(glyph_code,current) do + n = copy_node(n) + if head then + setlink(previous,n) + else + head = n + end + previous = n + end + return head +end + +nuts.uses_font = direct.uses_font + +if not nuts.uses_font then + local getdisc = nuts.getdisc + local getfont = nuts.getfont + function nuts.uses_font(n,font) + local pre, post, replace = getdisc(n) + if pre then + -- traverse_char + for n in traverse_id(glyph_code,pre) do + if getfont(n) == font then + return true + end + end + end + if post then + for n in traverse_id(glyph_code,post) do + if getfont(n) == font then + return true + end + end + end + if replace then + for n in traverse_id(glyph_code,replace) do + if getfont(n) == font then + return true + end + end + end + return false + end +end + +-- +do + + -- another poor mans substitute ... i will move these to a more protected + -- namespace .. experimental hack + + local dummy = tonut(node.new("glyph")) + + nuts.traversers = { + glyph = nuts.traverse_id(nodecodes.glyph,dummy), + glue = nuts.traverse_id(nodecodes.glue,dummy), + disc = nuts.traverse_id(nodecodes.disc,dummy), + boundary = nuts.traverse_id(nodecodes.boundary,dummy), + + char = nuts.traverse_char(dummy), + + node = nuts.traverse(dummy), + } + +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics.tex b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics.tex deleted file mode 100644 index 1180c68e6c5..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-basics.tex +++ /dev/null @@ -1,93 +0,0 @@ -%D \module -%D [ file=luatex-basics, -%D version=2009.12.01, -%D title=\LUATEX\ Support Macros, -%D subtitle=Attribute Allocation, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] - -%D As soon as we feel the need this file will file will contain an extension -%D to the standard plain register allocation. For the moment we stick to a -%D rather dumb attribute allocator. We start at 256 because we don't want -%D any interference with the attributes used in the font handler. - -\ifx\newattribute\undefined \else \endinput \fi - -\newcount \lastallocatedattribute \lastallocatedattribute=255 - -\def\newattribute#1% - {\global\advance\lastallocatedattribute 1 - \attributedef#1\lastallocatedattribute} - -% maybe we will have luatex-basics.lua some day for instance when more -% (pdf) primitives have moved to macros) - -\directlua { - - gadgets = gadgets or { } % reserved namespace - - gadgets.functions = { } - local registered = {} - - function gadgets.functions.reverve() - local numb = newtoken.scan_int() - local name = newtoken.scan_string() - local okay = string.gsub(name,"[\string\\ ]","") - registered[okay] = numb - texio.write_nl("reserving lua function '"..okay.."' with number "..numb) - end - - function gadgets.functions.register(name,f) - local okay = string.gsub(name,"[\string\\ ]","") - local numb = registered[okay] - if numb then - texio.write_nl("registering lua function '"..okay.."' with number "..numb) - lua.get_functions_table()[numb] = f - else - texio.write_nl("lua function '"..okay.."' is not reserved") - end - end - -} - -\newcount\lastallocatedluafunction - -\def\newluafunction#1% - {\ifdefined#1\else - \global\advance\lastallocatedluafunction 1 - \global\chardef#1\lastallocatedluafunction - \directlua{gadgets.functions.reserve()}#1{\detokenize{#1}}% - \fi} - -% an example of usage (if we ever support it it will go to the plain gadgets module): -% -% \directlua { -% -% local cct = nil -% local chr = nil -% -% gadgets.functions.register("UcharcatLuaOne",function() -% chr = newtoken.scan_int() -% cct = tex.getcatcode(chr) -% tex.setcatcode(chr,newtoken.scan_int()) -% tex.sprint(unicode.utf8.char(chr)) -% end) -% -% gadgets.functions.register("UcharcatLuaTwo",function() -% tex.setcatcode(chr,cct) -% end) -% -% } -% -% \def\Ucharcat -% {\expandafter\expandafter\expandafter\luafunction -% \expandafter\expandafter\expandafter\UcharcatLuaTwo -% \luafunction\UcharcatLuaOne} -% -% A:\the\catcode65:\Ucharcat 65 11:A:\the\catcode65\par -% A:\the\catcode65:\Ucharcat 65 5:A:\the\catcode65\par -% A:\the\catcode65:\Ucharcat 65 11:A:\the\catcode65\par - - -\endinput diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-afk.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-afk.lua index 8b65b063184..761016d3485 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-afk.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-afk.lua @@ -1,6 +1,6 @@ if not modules then modules = { } end modules ['font-afk'] = { version = 1.001, - comment = "companion to font-afm.lua", + 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", diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-cff.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-cff.lua index 1c6bd56fd3d..87234dd460f 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-cff.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-cff.lua @@ -13,32 +13,54 @@ if not modules then modules = { } end modules ['font-cff'] = { -- This is a heavy one as it is a rather packed format. We don't need al the information -- now but we might need it later (who know what magic we can do with metapost). So at -- some point this might become a module. We just follow Adobe Technical Notes #5176 and --- #5177. In case of doubt I looked in the fontforge code that comes with LuaTeX. +-- #5177. In case of doubt I looked in the fontforge code that comes with LuaTeX but +-- it's not the easiest source to read (and doesn't cover cff2). -- For now we save the segments in a list of segments with the operator last in an entry -- because that reflects the original. But it might make more sense to use a single array -- per segment. For pdf a simple concat works ok, but for other purposes a operator first -- flush is nicer. +-- +-- In retrospect I could have looked into the backend code of LuaTeX but it never +-- occurred to me that parsing charstrings was needed there (which has to to +-- with merging subroutines and flattening, not so much with calculations.) On +-- the other hand, we can now feed back cff2 stuff. local next, type, tonumber = next, type, tonumber -local byte = string.byte +local byte, char, gmatch = string.byte, string.char, string.gmatch local concat, remove = table.concat, table.remove -local floor, abs, round, ceil = math.floor, math.abs, math.round, math.ceil +local floor, abs, round, ceil, min, max = math.floor, math.abs, math.round, math.ceil, math.min, math.max 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 bytetable = string.bytetable local readers = fonts.handlers.otf.readers local streamreader = readers.streamreader -local readbytes = streamreader.readbytes local readstring = streamreader.readstring local readbyte = streamreader.readcardinal1 -- 8-bit unsigned integer local readushort = streamreader.readcardinal2 -- 16-bit unsigned integer local readuint = streamreader.readcardinal3 -- 24-bit unsigned integer -local readulong = streamreader.readcardinal4 -- 24-bit unsigned integer +local readulong = streamreader.readcardinal4 -- 32-bit unsigned integer local setposition = streamreader.setposition local getposition = streamreader.getposition +local readbytetable = streamreader.readbytetable + +directives.register("fonts.streamreader",function() + + streamreader = utilities.streams + + readstring = streamreader.readstring + readbyte = streamreader.readcardinal1 + readushort = streamreader.readcardinal2 + readuint = streamreader.readcardinal3 + readulong = streamreader.readcardinal4 + setposition = streamreader.setposition + getposition = streamreader.getposition + readbytetable = streamreader.readbytetable + +end) local setmetatableindex = table.setmetatableindex @@ -50,6 +72,8 @@ local parsecharstring local parsecharstrings local resetcharstrings local parseprivates +local startparsing +local stopparsing local defaultstrings = { [0] = -- taken from ff ".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", @@ -130,13 +154,21 @@ local cffreaders = { local function readheader(f) local offset = getposition(f) + local major = readbyte(f) local header = { offset = offset, - major = readbyte(f), + major = major, minor = readbyte(f), size = readbyte(f), -- headersize - osize = readbyte(f), -- for offsets to start } + if major == 1 then + header.dsize = readbyte(f) -- list of dict offsets + elseif major == 2 then + header.dsize = readushort(f) -- topdict size + else + -- I'm probably no longer around by then and we use AI's to + -- handle this kind of stuff, if we typeset documents at all. + end setposition(f,offset+header.size) return header end @@ -144,8 +176,8 @@ end -- The indexes all look the same, so we share a loader. We could pass a handler -- and run over the array but why bother, we only have a few uses. -local function readlengths(f) - local count = readushort(f) +local function readlengths(f,longcount) + local count = longcount and readulong(f) or readushort(f) if count == 0 then return { } end @@ -159,7 +191,12 @@ local function readlengths(f) local previous = read(f) for i=1,count do local offset = read(f) - lengths[i] = offset - previous + local length = offset - previous + if length < 0 then + report("bad offset: %i",length) + length = 0 + end + lengths[i] = length previous = offset end return lengths @@ -217,7 +254,8 @@ end do - -- We use a closure so that we don't need to pass too much around. + -- We use a closure so that we don't need to pass too much around. For cff2 we can + -- at some point use a simple version as there is less. local stack = { } local top = 0 @@ -271,7 +309,7 @@ do result.encoding = stack[top] top = 0 end - + P("\17") / function() + + P("\17") / function() -- valid cff2 result.charstrings = stack[top] top = 0 end @@ -284,19 +322,32 @@ do end + P("\19") / function() result.subroutines = stack[top] + top = 0 -- new, forgotten ? end + P("\20") / function() result.defaultwidthx = stack[top] + top = 0 -- new, forgotten ? end + P("\21") / function() result.nominalwidthx = stack[top] + top = 0 -- new, forgotten ? + end + -- + P("\22") / function() -- reserved + -- end + -- + P("\23") / function() -- reserved + -- end + + P("\24") / function() -- new in cff2 + result.vstore = stack[top] + top = 0 end - -- + P("\22") / function() end -- reserved - -- + P("\23") / function() end -- reserved - -- + P("\24") / function() end -- reserved - -- + P("\25") / function() end -- reserved - -- + P("\26") / function() end -- reserved - -- + P("\27") / function() end -- reserved + + P("\25") / function() -- new in cff2 + result.maxstack = stack[top] + top = 0 + end + -- + P("\26") / function() -- reserved + -- end + -- + P("\27") / function() -- reserved + -- end local p_double = P("\12") * ( P("\00") / function() @@ -327,7 +378,7 @@ do result.charstringtype = stack[top] top = 0 end - + P("\07") / function() + + P("\07") / function() -- valid cff2 result.fontmatrix = { unpack(stack,1,6) } top = 0 end @@ -377,11 +428,11 @@ do result.cid.uidbase = stack[top] top = 0 end - + P("\36") / function() + + P("\36") / function() -- valid cff2 result.cid.fdarray = stack[top] top = 0 end - + P("\37") / function() + + P("\37") / function() -- valid cff2 result.cid.fdselect = stack[top] top = 0 end @@ -502,12 +553,12 @@ do + p_unsupported )^1 - parsedictionaries = function(data,dictionaries) + parsedictionaries = function(data,dictionaries,what) stack = { } strings = data.strings for i=1,#dictionaries do top = 0 - result = { + result = what == "cff" and { monospaced = false, italicangle = 0, underlineposition = -100, @@ -525,6 +576,13 @@ do fonttype = 0, count = 8720, } + } or { + charstringtype = 2, + charset = 0, + vstore = 0, + cid = { + -- nothing yet + }, } lpegmatch(p_dictionary,dictionaries[i]) dictionaries[i] = result @@ -579,23 +637,32 @@ do -- because there quite some variants are done in one helper with a lot of -- testing for states. - 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 x = 0 + local y = 0 + local width = false + local r = 0 + local stems = 0 + local globalbias = 0 + local localbias = 0 + local nominalwidth = 0 + local defaultwidth = 0 + local charset = false + 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 regions = false + local nofregions = 0 + local region = false + local factors = false + local axis = false + local vsindex = 0 local function showstate(where) report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top) @@ -611,16 +678,17 @@ do -- All these indirect calls make this run slower but it's cleaner this way -- and we cache the result. As we moved the boundingbox code inline we gain - -- some back. + -- some back. I inlined some of then and a bit speed can be gained by more + -- inlining but not that much. - local function moveto(x,y) + local function xymoveto() 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 + if x > xmax then xmax = x elseif x < xmin then xmin = x end + if y > ymax then ymax = y elseif y < ymin then ymin = y end else xmin = x ymin = y @@ -630,14 +698,58 @@ do end end - local function lineto(x,y) + local function xmoveto() -- slight speedup + if keepcurve then + r = r + 1 + result[r] = { x, y, "m" } + end + if not checked then + xmin = x + ymin = y + xmax = x + ymax = y + checked = true + elseif x > xmax then + xmax = x + elseif x < xmin then + xmin = x + end + end + + local function ymoveto() -- slight speedup + if keepcurve then + r = r + 1 + result[r] = { x, y, "m" } + end + if not checked then + xmin = x + ymin = y + xmax = x + ymax = y + checked = true + elseif y > ymax then + ymax = y + elseif y < ymin then + ymin = y + end + end + + local function moveto() + if trace_charstrings then + showstate("moveto") + end + top = 0 -- forgotten + xymoveto() + end + + local function xylineto() -- we could inline, no blend 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 + if x > xmax then xmax = x elseif x < xmin then xmin = x end + if y > ymax then ymax = y elseif y < ymin then ymin = y end else xmin = x ymin = y @@ -647,14 +759,53 @@ do end end - local function curveto(x1,y1,x2,y2,x3,y3) + local function xlineto() -- slight speedup + if keepcurve then + r = r + 1 + result[r] = { x, y, "l" } + end + if not checked then + xmin = x + ymin = y + xmax = x + ymax = y + checked = true + elseif x > xmax then + xmax = x + elseif x < xmin then + xmin = x + end + end + + local function ylineto() -- slight speedup + if keepcurve then + r = r + 1 + result[r] = { x, y, "l" } + end + if not checked then + xmin = x + ymin = y + xmax = x + ymax = y + checked = true + elseif y > ymax then + ymax = y + elseif y < ymin then + ymin = y + end + end + + local function xycurveto(x1,y1,x2,y2,x3,y3) -- called local so no blend here + if trace_charstrings then + showstate("curveto") + end 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 + if x1 > xmax then xmax = x1 elseif x1 < xmin then xmin = x1 end + if y1 > ymax then ymax = y1 elseif y1 < ymin then ymin = y1 end else xmin = x1 ymin = y1 @@ -662,22 +813,22 @@ do 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 + if x2 > xmax then xmax = x2 elseif x2 < xmin then xmin = x2 end + if y2 > ymax then ymax = y2 elseif y2 < ymin then ymin = y2 end + if x3 > xmax then xmax = x3 elseif x3 < xmin then xmin = x3 end + if y3 > ymax then ymax = y3 elseif y3 < ymin then ymin = y3 end end local function rmoveto() - if top > 2 then - if not width then + if not width then + if top > 2 then width = stack[1] if trace_charstrings then - showvalue("width",width) + showvalue("backtrack width",width) end + else + width = true end - elseif not width then - width = true end if trace_charstrings then showstate("rmoveto") @@ -685,45 +836,45 @@ do x = x + stack[top-1] -- dx1 y = y + stack[top] -- dy1 top = 0 - moveto(x,y) + xymoveto() end local function hmoveto() - if top > 1 then - if not width then + if not width then + if top > 1 then width = stack[1] if trace_charstrings then - showvalue("width",width) + showvalue("backtrack width",width) end + else + width = true end - elseif not width then - width = true end if trace_charstrings then showstate("hmoveto") end x = x + stack[top] -- dx1 top = 0 - moveto(x,y) + xmoveto() end local function vmoveto() - if top > 1 then - if not width then + if not width then + if top > 1 then width = stack[1] if trace_charstrings then - showvalue("width",width) + showvalue("backtrack width",width) end + else + width = true end - elseif not width then - width = true end if trace_charstrings then showstate("vmoveto") end y = y + stack[top] -- dy1 top = 0 - moveto(x,y) + ymoveto() end local function rlineto() @@ -733,21 +884,7 @@ do for i=1,top,2 do x = x + stack[i] -- dxa y = y + stack[i+1] -- dya - lineto(x,y) - end - top = 0 - end - - local function xlineto(swap) -- x (y,x)+ | (x,y)+ - 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) + xylineto() end top = 0 end @@ -756,14 +893,48 @@ do if trace_charstrings then showstate("hlineto") end - xlineto(true) + if top == 1 then + x = x + stack[1] + xlineto() + else + local swap = true + for i=1,top do + if swap then + x = x + stack[i] + xlineto() + swap = false + else + y = y + stack[i] + ylineto() + swap = true + end + end + end + top = 0 end local function vlineto() -- y (x,y)+ | (y,x)+ if trace_charstrings then showstate("vlineto") end - xlineto(false) + if top == 1 then + y = y + stack[1] + ylineto() + else + local swap = false + for i=1,top do + if swap then + x = x + stack[i] + xlineto() + swap = false + else + y = y + stack[i] + ylineto() + swap = true + end + end + end + top = 0 end local function rrcurveto() @@ -775,9 +946,9 @@ do local ay = y + stack[i+1] -- dya local bx = ax + stack[i+2] -- dxb local by = ay + stack[i+3] -- dyb - x = bx + stack[i+4] -- dxc - y = by + stack[i+5] -- dyc - curveto(ax,ay,bx,by,x,y) + x = bx + stack[i+4] -- dxc + y = by + stack[i+5] -- dyc + xycurveto(ax,ay,bx,by,x,y) end top = 0 end @@ -788,17 +959,17 @@ do end local s = 1 if top % 2 ~= 0 then - y = y + stack[1] -- dy1 + y = y + stack[1] -- dy1 s = 2 end for i=s,top,4 do - local ax = x + stack[i] -- dxa + local ax = x + stack[i] -- dxa local ay = y local bx = ax + stack[i+1] -- dxb local by = ay + stack[i+2] -- dyb - x = bx + stack[i+3] -- dxc + x = bx + stack[i+3] -- dxc y = by - curveto(ax,ay,bx,by,x,y) + xycurveto(ax,ay,bx,by,x,y) end top = 0 end @@ -810,28 +981,64 @@ do local s = 1 local d = 0 if top % 2 ~= 0 then - d = stack[1] -- dx1 + d = stack[1] -- dx1 s = 2 end for i=s,top,4 do local ax = x + d - local ay = y + stack[i] -- dya + local ay = y + stack[i] -- dya local bx = ax + stack[i+1] -- dxb local by = ay + stack[i+2] -- dyb x = bx - y = by + stack[i+3] -- dyc - curveto(ax,ay,bx,by,x,y) + y = by + stack[i+3] -- dyc + xycurveto(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 +-- 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 +-- xycurveto(ax,ay,bx,by,x,y) +-- 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 @@ -859,11 +1066,12 @@ do end swap = true end - curveto(ax,ay,bx,by,x,y) + xycurveto(ax,ay,bx,by,x,y) end top = 0 end + local function hvcurveto() if trace_charstrings then showstate("hvcurveto") @@ -889,11 +1097,11 @@ do local by = ay + stack[i+3] -- dyb x = bx + stack[i+4] -- dxc y = by + stack[i+5] -- dyc - curveto(ax,ay,bx,by,x,y) + xycurveto(ax,ay,bx,by,x,y) end x = x + stack[top-1] -- dxc y = y + stack[top] -- dyc - lineto(x,y) + xylineto() top = 0 end @@ -905,7 +1113,7 @@ do for i=1,top-6,2 do x = x + stack[i] y = y + stack[i+1] - lineto(x,y) + xylineto() end end local ax = x + stack[top-5] @@ -914,7 +1122,7 @@ do local by = ay + stack[top-2] x = bx + stack[top-1] y = by + stack[top] - curveto(ax,ay,bx,by,x,y) + xycurveto(ax,ay,bx,by,x,y) top = 0 end @@ -930,14 +1138,14 @@ do local by = ay + stack[4] -- dy2 local cx = bx + stack[5] -- dx3 local cy = by + stack[6] -- dy3 - curveto(ax,ay,bx,by,cx,cy) + xycurveto(ax,ay,bx,by,cx,cy) local dx = cx + stack[7] -- dx4 local dy = cy + stack[8] -- dy4 local ex = dx + stack[9] -- dx5 local ey = dy + stack[10] -- dy5 x = ex + stack[11] -- dx6 y = ey + stack[12] -- dy6 - curveto(dx,dy,ex,ey,x,y) + xycurveto(dx,dy,ex,ey,x,y) top = 0 end @@ -945,19 +1153,19 @@ do if trace_charstrings then showstate("hflex") end - local ax = x + stack[1] -- dx1 + local ax = x + stack[1] -- dx1 local ay = y local bx = ax + stack[2] -- dx2 local by = ay + stack[3] -- dy2 local cx = bx + stack[4] -- dx3 local cy = by - curveto(ax,ay,bx,by,cx,cy) + xycurveto(ax,ay,bx,by,cx,cy) local dx = cx + stack[5] -- dx4 local dy = by local ex = dx + stack[6] -- dx5 local ey = y - x = ex + stack[7] -- dx6 - curveto(dx,dy,ex,ey,x,y) + x = ex + stack[7] -- dx6 + xycurveto(dx,dy,ex,ey,x,y) top = 0 end @@ -971,13 +1179,13 @@ do local by = ay + stack[4] -- dy2 local cx = bx + stack[5] -- dx3 local cy = by - curveto(ax,ay,bx,by,cx,cy) + xycurveto(ax,ay,bx,by,cx,cy) local dx = cx + stack[6] -- dx4 local dy = by local ex = dx + stack[7] -- dx5 local ey = dy + stack[8] -- dy5 - x = ex + stack[9] -- dx6 - curveto(dx,dy,ex,ey,x,y) + x = ex + stack[9] -- dx6 + xycurveto(dx,dy,ex,ey,x,y) top = 0 end @@ -991,7 +1199,7 @@ do local by = ay + stack[4] --dy2 local cx = bx + stack[5] --dx3 local cy = by + stack[6] --dy3 - curveto(ax,ay,bx,by,cx,cy) + xycurveto(ax,ay,bx,by,cx,cy) local dx = cx + stack[7] --dx4 local dy = cy + stack[8] --dy4 local ex = dx + stack[9] --dx5 @@ -1001,7 +1209,7 @@ do else y = ey + stack[11] end - curveto(dx,dy,ex,ey,x,y) + xycurveto(dx,dy,ex,ey,x,y) top = 0 end @@ -1160,7 +1368,109 @@ do top = 0 end - -- so far for unsupported postscript + -- So far for unsupported postscript. Now some cff2 magic. As I still need + -- to wrap my head around the rather complex variable font specification + -- with regions and axis, the following approach kind of works but is more + -- some trial and error trick. It's still not clear how much of the complex + -- truetype description applies to cff. + + local reginit = false + + local function updateregions(n) -- n + 1 + if regions then + local current = regions[n] or regions[1] + nofregions = #current + if axis and n ~= reginit then + factors = { } + for i=1,nofregions do + local region = current[i] + local s = 1 + for j=1,#axis do + local f = axis[j] + local r = region[j] + local start = r.start + local peak = r.peak + local stop = r.stop + if start > peak or peak > stop then + -- * 1 + elseif start < 0 and stop > 0 and peak ~= 0 then + -- * 1 + elseif peak == 0 then + -- * 1 + elseif f < start or f > stop then + -- * 0 + s = 0 + break + elseif f < peak then + s = s * (f - start) / (peak - start) + elseif f > peak then + s = s * (stop - f) / (stop - peak) + else + -- * 1 + end + end + factors[i] = s + end + end + end + reginit = n + end + + local function setvsindex() + local vsindex = stack[top] + if trace_charstrings then + showstate(formatters["vsindex %i"](vsindex)) + end + updateregions(vsindex) + top = top - 1 + end + + local function blend() + local n = stack[top] + top = top - 1 + if axis then + -- x (r1x,r2x,r3x) + -- (x,y) (r1x,r2x,r3x) (r1y,r2y,r3y) + if trace_charstrings then + local t = top - nofregions * n + local m = t - n + for i=1,n do + local k = m + i + local d = m + n + (i-1)*nofregions + local old = stack[k] + local new = old + for r=1,nofregions do + new = new + stack[d+r] * factors[r] + end + stack[k] = new + showstate(formatters["blend %i of %i: %s -> %s"](i,n,old,new)) + end + top = t + elseif n == 1 then + top = top - nofregions + local v = stack[top] + for r=1,nofregions do + v = v + stack[top+r] * factors[r] + end + stack[top] = v + else + top = top - nofregions * n + local d = top + local k = top - n + for i=1,n do + k = k + 1 + local v = stack[k] + for r=1,nofregions do + v = v + stack[d+r] * factors[r] + end + stack[k] = v + d = d + nofregions + end + end + else + -- error + end + end -- Bah, we cannot use a fast lpeg because a hint has an unknown size and a -- runtime capture cannot handle that well. @@ -1181,8 +1491,8 @@ do unsupported, -- 12 -- elsewhere hsbw, -- 13 -- hsbw (type 1 cff) unsupported, -- 14 -- endchar, - unsupported, -- 15 - unsupported, -- 16 + setvsindex, -- 15 -- cff2 + blend, -- 16 -- cff2 unsupported, -- 17 getstem, -- 18 -- hstemhm getmask, -- 19 -- hintmask @@ -1218,9 +1528,95 @@ do [037] = flex1, } - local p_bytes = Ct((P(1)/byte)^0) + local c_endchar = char(14) + + local passon do + + -- todo: round in blend + -- todo: delay this hash + + local rshift = bit32.rshift + local band = bit32.band + local round = math.round - local function call(scope,list,bias,process) + local encode = table.setmetatableindex(function(t,i) + for i=-2048,-1130 do + t[i] = char(28,band(rshift(i,8),0xFF),band(i,0xFF)) + end + for i=-1131,-108 do + local v = 0xFB00 - i - 108 + t[i] = char(band(rshift(v,8),0xFF),band(v,0xFF)) + end + for i=-107,107 do + t[i] = char(i + 139) + end + for i=108,1131 do + local v = 0xF700 + i - 108 + t[i] = char(band(rshift(v,8),0xFF),band(v,0xFF)) + end + for i=1132,2048 do + t[i] = char(28,band(rshift(i,8),0xFF),band(i,0xFF)) + end + return t[i] + end) + + local function setvsindex() + local vsindex = stack[top] + updateregions(vsindex) + top = top - 1 + end + + local function blend() + local n = stack[top] + top = top - 1 + if not axis then + -- fatal error + elseif n == 1 then + top = top - nofregions + local v = stack[top] + for r=1,nofregions do + v = v + stack[top+r] * factors[r] + end + stack[top] = round(v) + else + top = top - nofregions * n + local d = top + local k = top - n + for i=1,n do + k = k + 1 + local v = stack[k] + for r=1,nofregions do + v = v + stack[d+r] * factors[r] + end + stack[k] = round(v) + d = d + nofregions + end + end + end + + passon = function(operation) + if operation == 15 then + setvsindex() + elseif operation == 16 then + blend() + else + for i=1,top do + r = r + 1 + result[r] = encode[stack[i]] + end + r = r + 1 + result[r] = char(operation) -- maybe use a hash + top = 0 + end + end + + end + + -- end of experiment + + local process + + local function call(scope,list,bias) -- ,process) depth = depth + 1 if top == 0 then showstate(formatters["unknown %s call"](scope)) @@ -1233,10 +1629,6 @@ do 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)) @@ -1246,54 +1638,63 @@ do depth = depth - 1 end - local function process(tab) + -- precompiling and reuse is much slower than redoing the calls + + local justpass = false + + process = function(tab) local i = 1 local n = #tab while i <= n do local t = tab[i] - if t >= 32 and t <= 246 then - -- -107 .. +107 + if t >= 32 then top = top + 1 - stack[top] = t - 139 - i = i + 1 - elseif t >= 247 and t <= 250 then - -- +108 .. +1131 - top = top + 1 - stack[top] = (t-247)*256 + tab[i+1] + 108 - i = i + 2 - elseif t >= 251 and t <= 254 then - -- -1131 .. -108 - top = top + 1 - stack[top] = -(t-251)*256 - tab[i+1] - 108 - i = i + 2 + if t <= 246 then + -- -107 .. +107 + stack[top] = t - 139 + i = i + 1 + elseif t <= 250 then + -- +108 .. +1131 + -- stack[top] = (t-247)*256 + tab[i+1] + 108 + -- stack[top] = t*256 - 247*256 + tab[i+1] + 108 + stack[top] = t*256 - 63124 + tab[i+1] + i = i + 2 + elseif t <= 254 then + -- -1131 .. -108 + -- stack[top] = -(t-251)*256 - tab[i+1] - 108 + -- stack[top] = -t*256 + 251*256 - tab[i+1] - 108 + stack[top] = -t*256 + 64148 - tab[i+1] + i = i + 2 + else + local n = 0x100 * tab[i+1] + tab[i+2] + if n >= 0x8000 then + -- stack[top] = n - 0xFFFF - 1 + (0x100 * tab[i+3] + tab[i+4])/0xFFFF + stack[top] = n - 0x10000 + (0x100 * tab[i+3] + tab[i+4])/0xFFFF + else + stack[top] = n + (0x100 * tab[i+3] + tab[i+4])/0xFFFF + end + i = i + 5 + end elseif t == 28 then -- -32768 .. +32767 : b1<<8 | b2 top = top + 1 local n = 0x100 * tab[i+1] + tab[i+2] if n >= 0x8000 then - stack[top] = n - 0xFFFF - 1 + -- stack[top] = n - 0xFFFF - 1 + stack[top] = n - 0x10000 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 + elseif t == 11 then -- not in cff2 if trace_charstrings then showstate("return") end return elseif t == 10 then - call("local",locals,localbias,process) + call("local",locals,localbias) -- ,process) i = i + 1 - elseif t == 14 then -- endchar + elseif t == 14 then -- not in cff2 if width then -- okay elseif top > 0 then @@ -1309,7 +1710,7 @@ do end return elseif t == 29 then - call("global",globals,globalbias,process) + call("global",globals,globalbias) -- ,process) i = i + 1 elseif t == 12 then i = i + 1 @@ -1324,20 +1725,25 @@ do top = 0 end i = i + 1 + elseif justpass then + passon(t) + i = i + 1 else local a = actions[t] if a then local s = a(t) if s then - i = i + s + i = i + s + 1 + else + i = i + 1 end else if trace_charstrings then showvalue("",t) end top = 0 + i = i + 1 end - i = i + 1 end end end @@ -1356,7 +1762,7 @@ do -- if y < ymin then ymin = y end -- if y > ymax then ymax = y end -- -- we now have a reasonable start so we could - -- -- simplyfy the next checks + -- -- simplify the next checks -- for i=1,nofsegments do -- local s = segments[i] -- local x = s[1] @@ -1393,150 +1799,63 @@ do end end - parsecharstrings = function(data,glyphs,doshapes,tversion) - -- for all charstrings - 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 function processshape(tab,index) - 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 - -- - -- trace_charstrings = index == 3078 -- todo: make tracker - local glyph = glyphs[index] -- can be autodefined in otr - if not glyph then - glyphs[index] = { - segments = doshapes ~= false and result or nil, -- optional - boundingbox = boundingbox, - width = width, - name = charset[index], - -- sidebearing = 0, - } - 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 - -- glyph.sidebearing = 0 -- todo - end - if trace_charstrings then - report("width: %s",tostring(width)) - report("boundingbox: % t",boundingbox) - end - charstrings[i] = nil -- free memory - end - return glyphs - end + tab = bytetable(tab) - parsecharstring = function(data,dictionary,tab,glyphs,index,doshapes,tversion) - local private = dictionary.private - keepcurve = doshapes - version = tversion - strings = data.strings -- or in dict? - locals = dictionary.subroutines or { } - globals = data.routines or { } + x = 0 + y = 0 + width = false + r = 0 + top = 0 + stems = 0 + result = { } -- we could reuse it when only boundingbox calculations are needed - globalbias, localbias = setbias(globals,locals) + xmin = 0 + xmax = 0 + ymin = 0 + ymax = 0 + checked = false - 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) + report("data : % t",tab) end - -- + + if regions then + updateregions(vsindex) + end + process(tab) - -- - local boundingbox = { xmin, ymin, xmax, ymax } - -- + + local boundingbox = { + round(xmin), + round(ymin), + round(xmax), + round(ymax), + } + if width == true or width == false then width = defaultwidth else width = nominalwidth + width end - -- - index = index - 1 - -- + local glyph = glyphs[index] -- can be autodefined in otr - if not glyph then - glyphs[index] = { - segments = doshapes ~= false and result or nil, -- optional - boundingbox = boundingbox, - width = width, - name = charset[index], - -- sidebearing = 0, - } - else - glyph.segments = doshapes ~= false and result or nil + if justpass then + r = r + 1 + result[r] = c_endchar + local stream = concat(result) + -- if trace_charstrings then + -- report("vdata: %s",stream) + -- end + if glyph then + glyph.stream = stream + else + glyphs[index] = { stream = stream } + end + elseif glyph then + glyph.segments = keepcurve ~= false and result or nil glyph.boundingbox = boundingbox if not glyph.width then glyph.width = width @@ -1545,18 +1864,107 @@ do glyph.name = charset[index] end -- glyph.sidebearing = 0 -- todo + elseif keepcurve then + glyphs[index] = { + segments = result, + boundingbox = boundingbox, + width = width, + name = charset and charset[index] or nil, + -- sidebearing = 0, + } + else + glyphs[index] = { + boundingbox = boundingbox, + width = width, + name = charset and charset[index] or nil, + } end - -- + if trace_charstrings then - report("width: %s",tostring(width)) + report("width : %s",tostring(width)) report("boundingbox: % t",boundingbox) end + end - resetcharstrings = function() - result = { } - top = 0 - stack = { } + startparsing = function(fontdata,data,streams) + reginit = false + axis = false + regions = data.regions + justpass = streams == true + if regions then + regions = { regions } -- needs checking + axis = data.factors or false + end + end + + stopparsing = function(fontdata,data) + stack = { } + glyphs = false + result = { } + top = 0 + locals = false + globals = false + strings = false + end + + local function setwidths(private) + if not private then + return 0, 0 + end + local privatedata = private.data + if not privatedata then + return 0, 0 + end + return privatedata.nominalwidthx or 0, privatedata.defaultwidthx or 0 + end + + parsecharstrings = function(fontdata,data,glphs,doshapes,tversion,streams) + + local dictionary = data.dictionaries[1] + local charstrings = dictionary.charstrings + + keepcurve = doshapes + version = tversion + strings = data.strings + globals = data.routines or { } + locals = dictionary.subroutines or { } + charset = dictionary.charset + vsindex = dictionary.vsindex or 0 + glyphs = glphs or { } + + globalbias, localbias = setbias(globals,locals) + nominalwidth, defaultwidth = setwidths(dictionary.private) + + startparsing(fontdata,data,streams) + + for index=1,#charstrings do + processshape(charstrings[index],index-1) + charstrings[index] = nil -- free memory (what if used more often?) + end + + stopparsing(fontdata,data) + + return glyphs + end + + parsecharstring = function(fontdata,data,dictionary,tab,glphs,index,doshapes,tversion) + + keepcurve = doshapes + version = tversion + strings = data.strings + globals = data.routines or { } + locals = dictionary.subroutines or { } + charset = false + vsindex = dictionary.vsindex or 0 + glyphs = glphs or { } + + globalbias, localbias = setbias(globals,locals) + nominalwidth, defaultwidth = setwidths(dictionary.private) + + processshape(tab,index-1) + + -- return glyphs[index] end end @@ -1564,7 +1972,7 @@ end local function readglobals(f,data) local routines = readlengths(f) for i=1,#routines do - routines[i] = readstring(f,routines[i]) + routines[i] = readbytetable(f,routines[i]) end data.routines = routines end @@ -1578,8 +1986,7 @@ local function readcharsets(f,data,dictionary) local strings = data.strings local nofglyphs = data.nofglyphs local charsetoffset = dictionary.charset - - if charsetoffset ~= 0 then + if charsetoffset and charsetoffset ~= 0 then setposition(f,header.offset+charsetoffset) local format = readbyte(f) local charset = { [0] = ".notdef" } @@ -1605,6 +2012,9 @@ local function readcharsets(f,data,dictionary) else report("cff parser: unsupported charset format %a",format) end + else + dictionary.nocharset = true + dictionary.charset = nil end end @@ -1627,7 +2037,7 @@ local function readlocals(f,data,dictionary) setposition(f,header.offset+private.offset+subroutineoffset) local subroutines = readlengths(f) for i=1,#subroutines do - subroutines[i] = readstring(f,subroutines[i]) + subroutines[i] = readbytetable(f,subroutines[i]) end dictionary.subroutines = subroutines private.data.subroutines = nil @@ -1642,16 +2052,18 @@ end -- These charstrings are little programs and described in: Technical Note #5177. A truetype -- font has only one dictionary. -local function readcharstrings(f,data) +local function readcharstrings(f,data,what) local header = data.header local dictionaries = data.dictionaries local dictionary = dictionaries[1] - local type = dictionary.charstringtype + local stringtype = dictionary.charstringtype local offset = dictionary.charstrings - if type == 2 then + if type(offset) ~= "number" then + -- weird + elseif stringtype == 2 then setposition(f,header.offset+offset) -- could be a metatable .. delayed loading - local charstrings = readlengths(f) + local charstrings = readlengths(f,what=="cff2") local nofglyphs = #charstrings for i=1,nofglyphs do charstrings[i] = readstring(f,charstrings[i]) @@ -1659,7 +2071,7 @@ local function readcharstrings(f,data) data.nofglyphs = nofglyphs dictionary.charstrings = charstrings else - report("unsupported charstr type %i",type) + report("unsupported charstr type %i",stringtype) data.nofglyphs = 0 dictionary.charstrings = { } end @@ -1681,31 +2093,38 @@ local function readcidprivates(f,data) parseprivates(data,dictionaries) end -local function readnoselect(f,data,glyphs,doshapes,version) +readers.parsecharstrings = parsecharstrings -- used in font-onr.lua (type 1) + +local function readnoselect(f,fontdata,data,glyphs,doshapes,version,streams) local dictionaries = data.dictionaries local dictionary = dictionaries[1] readglobals(f,data) - readcharstrings(f,data) - readencodings(f,data) - readcharsets(f,data,dictionary) + readcharstrings(f,data,version) + if version == "cff2" then + dictionary.charset = nil + else + readencodings(f,data) + readcharsets(f,data,dictionary) + end readprivates(f,data) parseprivates(data,data.dictionaries) readlocals(f,data,dictionary) - parsecharstrings(data,glyphs,doshapes,version) - resetcharstrings() + startparsing(fontdata,data,streams) + parsecharstrings(fontdata,data,glyphs,doshapes,version,streams) + stopparsing(fontdata,data) end -readers.parsecharstrings = parsecharstrings - -local function readfdselect(f,data,glyphs,doshapes,version) +local function readfdselect(f,fontdata,data,glyphs,doshapes,version,streams) 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) + readcharstrings(f,data,version) + if version ~= "cff2" then + readencodings(f,data) + end local charstrings = dictionary.charstrings local fdindex = { } local nofglyphs = data.nofglyphs @@ -1742,6 +2161,7 @@ local function readfdselect(f,data,glyphs,doshapes,version) else -- unsupported format end + -- hm, always if maxindex >= 0 then local cidarray = cid.fdarray setposition(f,header.offset+cidarray) @@ -1755,85 +2175,157 @@ local function readfdselect(f,data,glyphs,doshapes,version) for i=1,#dictionaries do readlocals(f,data,dictionaries[i]) end + startparsing(fontdata,data,streams) for i=1,#charstrings do - parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version) + parsecharstring(fontdata,data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version) + charstrings[i] = nil end - resetcharstrings() + stopparsing(fontdata,data) end end +local gotodatatable = readers.helpers.gotodatatable + +local function cleanup(data,dictionaries) + -- for i=1,#dictionaries do + -- local d = dictionaries[i] + -- d.subroutines = nil + -- end + -- data.strings = nil + -- if data then + -- data.charstrings = nil + -- data.routines = nil + -- end +end + function readers.cff(f,fontdata,specification) --- if specification.glyphs then - 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 - -- we only want some metadata + local tableoffset = gotodatatable(f,fontdata,"cff",specification.details) + if tableoffset then + local header = readheader(f) + if header.major ~= 1 then + report("only version %s is supported for table %a",1,"cff") + return + end + local glyphs = fontdata.glyphs + 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,dictionaries,"cff") + -- + local dic = dictionaries[1] + local cid = dic.cid + fontdata.cffinfo = { + familynamename = dic.familyname, + fullname = dic.fullname, + boundingbox = dic.boundingbox, + weight = dic.weight, + italicangle = dic.italicangle, + underlineposition = dic.underlineposition, + underlinethickness = dic.underlinethickness, + monospaced = dic.monospaced, + } + fontdata.cidinfo = cid and { + registry = cid.registry, + ordering = cid.ordering, + supplement = cid.supplement, + } + -- + if specification.glyphs then + local all = specification.shapes or false + if cid and cid.fdselect then + readfdselect(f,fontdata,data,glyphs,all,"cff") 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 + readnoselect(f,fontdata,data,glyphs,all,"cff") end + end + cleanup(data,dictionaries) + end +end + +function readers.cff2(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"cff2",specification.glyphs) + if tableoffset then + local header = readheader(f) + if header.major ~= 2 then + report("only version %s is supported for table %a",2,"cff2") + return + end + local glyphs = fontdata.glyphs + local dictionaries = { readstring(f,header.dsize) } + local data = { + header = header, + dictionaries = dictionaries, + nofglyphs = fontdata.nofglyphs, + } + -- + parsedictionaries(data,dictionaries,"cff2") + -- + local offset = dictionaries[1].vstore + if offset > 0 then + local storeoffset = dictionaries[1].vstore + data.header.offset + 2 -- cff has a preceding size field + local regions, deltas = readers.helpers.readvariationdata(f,storeoffset,factors) -- - -- cleanup (probably more can go) - -- - -- for i=1,#dictionaries do - -- local d = dictionaries[i] - -- d.subroutines = nil - -- end - -- data.strings = nil - -- if data then - -- data.charstrings = nil - -- data.routines = nil - -- end + data.regions = regions + data.deltas = deltas + else + data.regions = { } + data.deltas = { } + end + data.factors = specification.factors + -- + local cid = data.dictionaries[1].cid + local all = specification.shapes or false + if cid and cid.fdselect then + readfdselect(f,fontdata,data,glyphs,all,"cff2",specification.streams) + else + readnoselect(f,fontdata,data,glyphs,all,"cff2",specification.streams) + end + cleanup(data,dictionaries) + end +end + +-- temporary helper needed for checking backend patches + +function readers.cffcheck(filename) + local f = io.open(filename,"rb") + if f then + local fontdata = { + glyphs = { }, + } + local header = readheader(f) + if header.major ~= 1 then + report("only version %s is supported for table %a",1,"cff") + return + end + local names = readfontnames(f) + local dictionaries = readtopdictionaries(f) + local strings = readstrings(f) + local glyphs = { } + local data = { + header = header, + names = names, + dictionaries = dictionaries, + strings = strings, + glyphs = glyphs, + nofglyphs = 4, + } + -- + parsedictionaries(data,dictionaries,"cff") + -- + local cid = data.dictionaries[1].cid + if cid and cid.fdselect then + readfdselect(f,fontdata,data,glyphs,false) + else + readnoselect(f,fontdata,data,glyphs,false) end + return data end end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-cid.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-cid.lua index 0eaacdfbd54..781bc9fec97 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-cid.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-cid.lua @@ -1,6 +1,6 @@ if not modules then modules = { } end modules ['font-cid'] = { version = 1.001, - comment = "companion to font-otf.lua (cidmaps)", + 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" diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-con.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-con.lua index 18f2217108a..e8bcd56a263 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-con.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-con.lua @@ -8,11 +8,14 @@ if not modules then modules = { } end modules ['font-con'] = { -- some names of table entries will be changed (no _) -local next, tostring, rawget = next, tostring, rawget +local next, tostring, tonumber, rawget = next, tostring, tonumber, rawget local format, match, lower, gsub, find = string.format, string.match, string.lower, string.gsub, string.find -local sort, insert, concat, sortedkeys, serialize, fastcopy = table.sort, table.insert, table.concat, table.sortedkeys, table.serialize, table.fastcopy +local sort, insert, concat = table.sort, table.insert, table.concat +local sortedkeys, sortedhash, serialize, fastcopy = table.sortedkeys, table.sortedhash, table.serialize, table.fastcopy local derivetable = table.derive local ioflush = io.flush +local round = math.round +local setmetatable, getmetatable, rawget, rawset = setmetatable, getmetatable, rawget, rawset local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) local trace_scaling = false trackers.register("fonts.scaling", function(v) trace_scaling = v end) @@ -43,7 +46,7 @@ constructors.namemode = "fullpath" -- will be a function constructors.version = 1.01 constructors.cache = containers.define("fonts", "constructors", constructors.version, false) -constructors.privateoffset = 0xF0000 -- 0x10FFFF +constructors.privateoffset = 0xF0000 -- 0x10FFFF | context also uses privates: 0xE000-0xEFFF constructors.cacheintex = true -- so we see the original table in fonts.font @@ -88,6 +91,31 @@ function constructors.scaled(scaledpoints, designsize) -- handles designsize in end end +function constructors.getprivate(tfmdata) + local properties = tfmdata.properties + local private = properties.private + properties.private = private + 1 + return private +end + +function constructors.setmathparameter(tfmdata,name,value) + local m = tfmdata.mathparameters + local c = tfmdata.MathConstants + if m then + m[name] = value + end + if c and c ~= m then + c[name] = value + end +end + +function constructors.getmathparameter(tfmdata,name) + local p = tfmdata.mathparameters or tfmdata.MathConstants + if p then + return p[name] + end +end + --[[ldx--

Beware, the boundingbox is passed as reference so we may not overwrite it in the process; numbers are of course copies. Here 65536 equals 1pt. (Due to @@ -221,27 +249,87 @@ function constructors.trytosharefont(target,tfmdata) 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 +-- -- synonyms +-- 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 synonyms = { + exheight = "x_height", + xheight = "x_height", + ex = "x_height", + emwidth = "quad", + em = "quad", + spacestretch = "space_stretch", + stretch = "space_stretch", + spaceshrink = "space_shrink", + shrink = "space_shrink", + extraspace = "extra_space", + xspace = "extra_space", + slantperpoint = "slant", +} + function constructors.enhanceparameters(parameters) - local 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, - } + local mt = getmetatable(parameters) + local getter = function(t,k) + if not k then + return nil + end + local s = synonyms[k] + if s then + return rawget(t,s) or (mt and mt[s]) or nil + end + if k == "spacing" then + return { + width = t.space, + stretch = t.space_stretch, + shrink = t.space_shrink, + extra = t.extra_space, + } + end + return mt and mt[k] or nil + end + local setter = function(t,k,v) + if not k then + return 0 + end + local s = synonyms[k] + if s then + rawset(t,s,v) + elseif k == "spacing" then + if type(v) == "table" then + rawset(t,"space",v.width or 0) + rawset(t,"space_stretch",v.stretch or 0) + rawset(t,"space_shrink",v.shrink or 0) + rawset(t,"extra_space",v.extra or 0) + end + else + rawset(t,k,v) + end + end + setmetatable(parameters, { + __index = getter, + __newindex = setter, + }) end local function mathkerns(v,vdelta) @@ -263,7 +351,7 @@ local psfake = 0 local function fixedpsname(psname,fallback) local usedname = psname if psname and psname ~= "" then - if find(psname," ") then + if find(psname," ",1,true) then usedname = gsub(psname,"[%s]+","-") else -- we assume that the name is sane enough (we might sanitize completely some day) @@ -321,7 +409,7 @@ function constructors.scale(tfmdata,specification) -- local mathsize = tonumber(specification.mathsize) or 0 local textsize = tonumber(specification.textsize) or scaledpoints - local forcedsize = tonumber(parameters.mathsize ) or 0 + local forcedsize = tonumber(parameters.mathsize ) or 0 -- can be set by the feature "mathsize" local extrafactor = tonumber(specification.factor ) or 1 if (mathsize == 2 or forcedsize == 2) and parameters.scriptpercentage then scaledpoints = parameters.scriptpercentage * textsize / 100 @@ -329,6 +417,8 @@ function constructors.scale(tfmdata,specification) scaledpoints = parameters.scriptscriptpercentage * textsize / 100 elseif forcedsize > 1000 then -- safeguard scaledpoints = forcedsize + else + -- in context x and xx also use mathsize end targetparameters.mathsize = mathsize -- context specific targetparameters.textsize = textsize -- context specific @@ -342,10 +432,6 @@ function constructors.scale(tfmdata,specification) local defaultdepth = resources.defaultdepth or 0 local units = parameters.units or 1000 -- - if target.fonts then - target.fonts = fastcopy(target.fonts) -- maybe we virtualize more afterwards - end - -- -- boundary keys are no longer needed as we now have a string 'right_boundary' -- that can be used in relevant tables (kerns and ligatures) ... not that I ever -- used them @@ -387,7 +473,8 @@ function constructors.scale(tfmdata,specification) local psname = properties.psname or tfmdata.psname local name = properties.name or tfmdata.name -- - -- the psname used in pdf file as well as for selecting subfont in ttc + -- The psname used in pdf file as well as for selecting subfont in ttc although + -- we don't need that subfont look up here (mapfile stuff). -- local psname, psfixed = fixedpsname(psname,fontname or fullname or file.nameonly(filename)) -- @@ -405,30 +492,41 @@ function constructors.scale(tfmdata,specification) -- expansion (hz) local expansion = parameters.expansion if expansion then - target.stretch = expansion.stretch - target.shrink = expansion.shrink - target.step = expansion.step - target.auto_expand = expansion.auto + target.stretch = expansion.stretch + target.shrink = expansion.shrink + target.step = expansion.step end - -- protrusion - local protrusion = parameters.protrusion - if protrusion then - target.auto_protrude = protrusion.auto + -- slanting + local slantfactor = parameters.slantfactor or 0 + if slantfactor ~= 0 then + target.slant = slantfactor * 1000 + else + target.slant = 0 end -- widening local extendfactor = parameters.extendfactor or 0 if extendfactor ~= 0 and extendfactor ~= 1 then hdelta = hdelta * extendfactor - target.extend = extendfactor * 1000 -- extent ? + target.extend = extendfactor * 1000 else target.extend = 1000 -- extent ? end - -- slanting - local slantfactor = parameters.slantfactor or 0 - if slantfactor ~= 0 then - target.slant = slantfactor * 1000 + -- squeezing + local squeezefactor = parameters.squeezefactor or 0 + if squeezefactor ~= 0 and squeezefactor ~= 1 then + vdelta = vdelta * squeezefactor + target.squeeze = squeezefactor * 1000 else - target.slant = 0 + target.squeeze = 1000 -- extent ? + end + -- effects + local mode = parameters.mode or 0 + if mode ~= 0 then + target.mode = mode + end + local width = parameters.width or 0 + if width ~= 0 then + target.width = width * delta * 1000 / 655360 end -- targetparameters.factor = delta @@ -437,9 +535,11 @@ function constructors.scale(tfmdata,specification) targetparameters.size = scaledpoints targetparameters.units = units targetparameters.scaledpoints = askedscaledpoints + targetparameters.mode = mode + targetparameters.width = width -- local isvirtual = properties.virtualized or tfmdata.type == "virtual" - local hasquality = target.auto_expand or target.auto_protrude + local hasquality = parameters.expansion or parameters.protrusion local hasitalics = properties.hasitalics local autoitalicamount = properties.autoitalicamount local stackmath = not properties.nostackmath @@ -450,6 +550,13 @@ function constructors.scale(tfmdata,specification) local writingmode = properties.writingmode or "horizontal" local identity = properties.identity or "horizontal" -- + local vfonts = target.fonts + if vfonts and #vfonts > 0 then + target.fonts = fastcopy(vfonts) -- maybe we virtualize more afterwards + elseif isvirtual then + target.fonts = { { id = 0 } } -- catch error + end + -- if changed and not next(changed) then changed = false end @@ -485,7 +592,7 @@ function constructors.scale(tfmdata,specification) targetparameters.descender = delta * descender end -- - constructors.enhanceparameters(targetparameters) -- official copies for us + constructors.enhanceparameters(targetparameters) -- official copies for us, now virtual -- local protrusionfactor = (targetquad ~= 0 and 1000/targetquad) or 0 local scaledwidth = defaultwidth * hdelta @@ -548,10 +655,15 @@ function constructors.scale(tfmdata,specification) 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 + if c and c ~= unicode then + if c then + description = descriptions[c] or descriptions[unicode] or character + character = characters[c] or character + index = description.index or c + else + description = descriptions[unicode] or character + index = description.index or unicode + end else description = descriptions[unicode] or character index = description.index or unicode @@ -657,12 +769,15 @@ function constructors.scale(tfmdata,specification) 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"], + local s = vvi["start"] or 0 + local e = vvi["end"] or 0 + local a = vvi["advance"] or 0 + t[i] = { -- zero check nicer for 5.3 + ["start"] = s == 0 and 0 or s * vdelta, + ["end"] = e == 0 and 0 or e * vdelta, + ["advance"] = a == 0 and 0 or a * vdelta, + ["extender"] = vvi["extender"], + ["glyph"] = vvi["glyph"], } end chr.vert_variants = t @@ -672,12 +787,15 @@ function constructors.scale(tfmdata,specification) 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"], + local s = hvi["start"] or 0 + local e = hvi["end"] or 0 + local a = hvi["advance"] or 0 + t[i] = { -- zero check nicer for 5.3 + ["start"] = s == 0 and 0 or s * hdelta, + ["end"] = e == 0 and 0 or e * hdelta, + ["advance"] = a == 0 and 0 or a * hdelta, + ["extender"] = hvi["extender"], + ["glyph"] = hvi["glyph"], } end chr.horiz_variants = t @@ -767,7 +885,7 @@ function constructors.scale(tfmdata,specification) local ok = false for i=1,#vc do local key = vc[i][1] - if key == "right" or key == "down" then + if key == "right" or key == "down" or key == "rule" then ok = true break end @@ -791,7 +909,7 @@ function constructors.scale(tfmdata,specification) else chr.commands = vc end - chr.index = nil + -- chr.index = nil end end targetcharacters[unicode] = chr @@ -803,6 +921,24 @@ function constructors.scale(tfmdata,specification) -- constructors.trytosharefont(target,tfmdata) -- + -- catch inconsistencies + -- + local vfonts = target.fonts +-- if isvirtual then +if isvirtual or target.type == "virtual" or properties.virtualized then + properties.virtualized = true +target.type = "virtual" + if not vfonts or #vfonts == 0 then + target.fonts = { { id = 0 } } + end + elseif vfonts then + properties.virtualized = true + target.type = "virtual" + if #vfonts == 0 then + target.fonts = { { id = 0 } } + end + end + -- return target end @@ -826,16 +962,9 @@ function constructors.finalize(tfmdata) -- 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 + stretch = tfmdata.stretch or 0, + shrink = tfmdata.shrink or 0, + step = tfmdata.step or 0, } end -- @@ -843,14 +972,26 @@ function constructors.finalize(tfmdata) parameters.size = tfmdata.size end -- - if not parameters.extendfactor then - parameters.extendfactor = tfmdata.extend or 0 + if not parameters.mode then + parameters.mode = 0 + end + -- + if not parameters.width then + parameters.width = 0 end -- if not parameters.slantfactor then parameters.slantfactor = tfmdata.slant or 0 end -- + if not parameters.extendfactor then + parameters.extendfactor = tfmdata.extend or 0 + end + -- + if not parameters.squeezefactor then + parameters.squeezefactor = tfmdata.squeeze or 0 + end + -- local designsize = parameters.designsize if designsize then parameters.minsize = tfmdata.minsize or designsize @@ -939,10 +1080,11 @@ function constructors.finalize(tfmdata) tfmdata.stretch = nil tfmdata.shrink = nil tfmdata.step = nil - tfmdata.auto_expand = nil - tfmdata.auto_protrude = nil - tfmdata.extend = nil tfmdata.slant = nil + tfmdata.extend = nil + tfmdata.squeeze = nil + tfmdata.mode = nil + tfmdata.width = nil tfmdata.units = nil tfmdata.units_per_em = nil -- @@ -963,49 +1105,26 @@ constructors.hashmethods = hashmethods function constructors.hashfeatures(specification) -- will be overloaded local features = specification.features if features then - local t, tn = { }, 0 - for category, list in next, features do + local t, n = { }, 0 + for category, list in sortedhash(features) do if next(list) then local hasher = hashmethods[category] if hasher then local hash = hasher(list) if hash then - tn = tn + 1 - t[tn] = category .. ":" .. hash + n = n + 1 + t[n] = category .. ":" .. hash end end end end - if tn > 0 then + if n > 0 then return concat(t," & ") end end return "unknown" end --- hashmethods.normal = function(list) --- local s = { } --- local n = 0 --- for k, v in next, list do --- if not k then --- -- no need to add to hash --- elseif k == "number" or k == "features" then --- -- no need to add to hash (maybe we need a skip list) --- else --- n = n + 1 --- s[n] = k --- end --- end --- if n > 0 then --- sort(s) --- for i=1,n do --- local k = s[i] --- s[i] = k .. '=' .. tostring(list[k]) --- end --- return concat(s,"+") --- end --- end - hashmethods.normal = function(list) local s = { } local n = 0 @@ -1016,7 +1135,18 @@ hashmethods.normal = function(list) -- no need to add to hash (maybe we need a skip list) else n = n + 1 - s[n] = k .. '=' .. tostring(v) + if type(v) == "table" then + -- table.sequenced + local t = { } + local m = 0 + for k, v in next, v do + m = m + 1 + t[m] = k .. '=' .. tostring(v) + end + s[n] = k .. '={' .. concat(t,",") .. "}" + else + s[n] = k .. '=' .. tostring(v) + end end end if n > 0 then @@ -1026,7 +1156,7 @@ hashmethods.normal = function(list) end --[[ldx-- -

In principle we can share tfm tables when we are in node for a font, but then +

In principle we can share tfm tables when we are in need for a font, but then we need to define a font switch as an id/attr switch which is no fun, so in that case users can best use dynamic features ... so, we will not use that speedup. Okay, when we get rid of base mode we can optimize even further by sharing, but then we @@ -1040,13 +1170,15 @@ function constructors.hashinstance(specification,force) specification.hash = hash end if size < 1000 and designsizes[hash] then - size = math.round(constructors.scaled(size,designsizes[hash])) - specification.size = size + size = round(constructors.scaled(size,designsizes[hash])) + else + size = round(size) end + specification.size = size if fallbacks then - return hash .. ' @ ' .. tostring(size) .. ' @ ' .. fallbacks + return hash .. ' @ ' .. size .. ' @ ' .. fallbacks else - return hash .. ' @ ' .. tostring(size) + return hash .. ' @ ' .. size end end @@ -1255,11 +1387,13 @@ do if not enhancers then - local actions = allocate() - local before = allocate() - local after = allocate() - local order = allocate() - local patches = { before = before, after = after } + local actions = allocate() -- no need to allocate thee + local before = allocate() + local after = allocate() + local order = allocate() + local known = { } + local nofsteps = 0 + local patches = { before = before, after = after } local trace = false local report = logs.reporter("fonts",format .. " enhancing") @@ -1285,7 +1419,7 @@ do report("%s enhancing file %a","start",filename) end ioflush() -- we want instant messages - for e=1,#order do + for e=1,nofsteps do local enhancer = order[e] local b = before[enhancer] if b then @@ -1295,7 +1429,7 @@ do end end end - enhance(enhancer,data,filename,raw) + enhance(enhancer,data,filename,raw) -- we have one installed: check extra features local a = after[enhancer] if a then for pattern, action in next, a do @@ -1317,7 +1451,9 @@ do if actions[what] then -- overloading, e.g."check extra features" else - order[#order+1] = what + nofsteps = nofsteps + 1 + order[nofsteps] = what + known[what] = nofsteps end actions[what] = action else @@ -1325,7 +1461,19 @@ do end end - -- fonts.constructors.otf.enhancers.patch("before","migrate metadata","cambria",function() end) + -- We used to have a lot of enhancers but no longer with the new font loader. The order of enhancers + -- is the order of definition. The before/after patches are there for old times sake and happen + -- before or after a (named) enhancer. An example of a set enhancer is "check extra features" so one + -- one set patches before or after that is applied. Unknown enhancers are auto-registered. It's a bit + -- messy but we keep it for compatibility reasons. + -- + -- fonts.handlers.otf.enhancers.patches.register("before","some patches","somefont",function(data,filename) + -- print("!!!!!!!") -- before | after + -- end) + -- + -- fonts.handlers.otf.enhancers.register("more patches",function(data,filename) + -- print("???????") -- enhance + -- end) local function patch(what,where,pattern,action) local pw = patches[what] @@ -1334,7 +1482,12 @@ do if ww then ww[pattern] = action else - pw[where] = { [pattern] = action} + pw[where] = { [pattern] = action } + if not known[where] then + nofsteps = nofsteps + 1 + order[nofsteps] = where + known[where] = nofsteps + end end end end @@ -1343,7 +1496,11 @@ do register = register, apply = apply, patch = patch, - patches = { register = patch }, -- for old times sake + report = report, + patches = { + register = patch, + report = report, + }, -- for old times sake } handler.enhancers = enhancers diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-def.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-def.lua index 88d6145665b..e5101e7f656 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-def.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-def.lua @@ -11,8 +11,9 @@ if not modules then modules = { } end modules ['font-def'] = { local lower, gsub = string.lower, string.gsub local tostring, next = tostring, next local lpegmatch = lpeg.match -local suffixonly, removesuffix = file.suffix, file.removesuffix +local suffixonly, removesuffix, basename = file.suffix, file.removesuffix, file.basename local formatters = string.formatters +local sortedhash, sortedkeys = table.sortedhash, table.sortedkeys local allocate = utilities.storage.allocate @@ -20,7 +21,6 @@ local trace_defining = false trackers .register("fonts.defining", function 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") @@ -80,53 +80,6 @@ and prepares a table that will move along as we proceed.

-- name name(sub) name(sub)*spec name*spec -- name@spec*oeps -local splitter, splitspecifiers = nil, "" -- not so nice - -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 "") -- weird catch -end - -definers.getspecification = getspecification - -function definers.registersplit(symbol,action,verbosename) - addspecifier(symbol) - variants[symbol] = action - if verbosename then - variants[verbosename] = action - end -end - local function makespecification(specification,lookup,name,sub,method,detail,size) size = size or 655360 if not lookup or lookup == "" then @@ -151,13 +104,65 @@ local function makespecification(specification,lookup,name,sub,method,detail,siz return t end - definers.makespecification = makespecification -function definers.analyze(specification, size) - -- can be optimized with locals - local lookup, name, sub, method, detail = getspecification(specification or "") - return makespecification(specification, lookup, name, sub, method, detail, size) +if context then + + local splitter, splitspecifiers = nil, "" -- not so nice + + local P, C, S, Cc, Cs = lpeg.P, lpeg.C, lpeg.S, lpeg.Cc, lpeg.Cs + + local left = P("(") + local right = P(")") + local colon = P(":") + local space = P(" ") + local lbrace = P("{") + local rbrace = P("}") + + definers.defaultlookup = "file" + + local prefixpattern = P(false) + + local function addspecifier(symbol) + splitspecifiers = splitspecifiers .. symbol + local method = S(splitspecifiers) + local lookup = C(prefixpattern) * colon + local sub = left * C(P(1-left-right-method)^1) * right + local specification = C(method) * C(P(1)^1) + local name = Cs((lbrace/"") * (1-rbrace)^1 * (rbrace/"") + (1-sub-specification)^1) + splitter = P((lookup + Cc("")) * name * (sub + Cc("")) * (specification + Cc(""))) + end + + local function addlookup(str) + prefixpattern = prefixpattern + P(str) + end + + definers.addlookup = addlookup + + addlookup("file") + addlookup("name") + addlookup("spec") + + local function getspecification(str) + return lpegmatch(splitter,str or "") -- weird catch + end + + definers.getspecification = getspecification + + function definers.registersplit(symbol,action,verbosename) + addspecifier(symbol) + variants[symbol] = action + if verbosename then + variants[verbosename] = action + end + end + + function definers.analyze(specification, size) + -- can be optimized with locals + local lookup, name, sub, method, detail = getspecification(specification or "") + return makespecification(specification, lookup, name, sub, method, detail, size) + end + end --[[ldx-- @@ -184,11 +189,30 @@ end function resolvers.name(specification) local resolve = fonts.names.resolve if resolve then - local resolved, sub, subindex = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions + local resolved, sub, subindex, instance = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions if resolved then specification.resolved = resolved specification.sub = sub specification.subindex = subindex + -- new, needed for experiments + if instance then + specification.instance = instance + local features = specification.features + if not features then + features = { } + specification.features = features + end + local normal = features.normal + if not normal then + normal = { } + features.normal = normal + end + normal.instance = instance + if not callbacks.supported.glyph_stream_provider then + normal.variableshapes = true -- for the moment + end + end + -- local suffix = lower(suffixonly(resolved)) if fonts.formats[suffix] then specification.forced = suffix @@ -294,10 +318,71 @@ local function checkembedding(tfmdata) tfmdata.embedding = embedding end +local function checkfeatures(tfmdata) + local resources = tfmdata.resources + local shared = tfmdata.shared + if resources and shared then + local features = resources.features + local usedfeatures = shared.features + if features and usedfeatures then + local usedlanguage = usedfeatures.language or "dflt" + local usedscript = usedfeatures.script or "dflt" + local function check(what) + if what then + local foundlanguages = { } + for feature, scripts in next, what do + if usedscript == "auto" or scripts["*"] then + -- ok + elseif not scripts[usedscript] then + -- report_defining("font %!font:name!, feature %a, no script %a", + -- tfmdata,feature,usedscript) + else + for script, languages in next, scripts do + if languages["*"] then + -- ok + elseif not languages[usedlanguage] then + report_defining("font %!font:name!, feature %a, script %a, no language %a", + tfmdata,feature,script,usedlanguage) + end + end + end + for script, languages in next, scripts do + for language in next, languages do + foundlanguages[language] = true + end + end + end + if false then + foundlanguages["*"] = nil + foundlanguages = sortedkeys(foundlanguages) + for feature, scripts in sortedhash(what) do + for script, languages in next, scripts do + if not languages["*"] then + for i=1,#foundlanguages do + local language = foundlanguages[i] + if not languages[language] then + report_defining("font %!font:name!, feature %a, script %a, no language %a", + tfmdata,feature,script,language) + end + end + end + end + end + end + end + end + check(features.gsub) + check(features.gpos) + end + end +end + function definers.loadfont(specification) local hash = constructors.hashinstance(specification) + -- todo: also hash by instance / factors local tfmdata = loadedfonts[hash] -- hashes by size ! if not tfmdata then + -- normally context will not end up here often (if so there is an issue somewhere) local forced = specification.forced or "" if forced ~= "" then local reader = readers[lower(forced)] -- normally forced is already lowered @@ -327,6 +412,7 @@ function definers.loadfont(specification) checkembedding(tfmdata) -- todo: general postprocessor loadedfonts[hash] = tfmdata designsizes[specification.hash] = tfmdata.parameters.designsize + checkfeatures(tfmdata) end end if not tfmdata then @@ -335,10 +421,6 @@ function definers.loadfont(specification) return tfmdata end -function constructors.checkvirtualids() - -- dummy in plain version -end - function constructors.readanddefine(name,size) -- no id -- maybe a dummy first local specification = definers.analyze(name,size) local method = specification.method @@ -352,7 +434,6 @@ function constructors.readanddefine(name,size) -- no id -- maybe a dummy first local tfmdata = definers.loadfont(specification) if tfmdata then tfmdata.properties.hash = hash - constructors.checkvirtualids(tfmdata) -- experiment, will become obsolete when slots can selfreference id = font.define(tfmdata) definers.register(tfmdata,id) else @@ -437,8 +518,8 @@ function definers.read(specification,size,id) -- id can be optional, name can al 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)) + properties.format or "unknown", id or "-", properties.name, parameters.size, properties.encodingbytes, + properties.encodingname, properties.fullname, basename(properties.filename)) end statistics.stoptiming(fonts) return tfmdata diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua index 13095f7e699..1fe4289b2f7 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-dsp.lua @@ -48,21 +48,36 @@ if not modules then modules = { } end modules ['font-dsp'] = { -- of node lists is not noticeable faster for latin texts, but for arabic we gain some 10% -- (and could probably gain a bit more). +-- All this packing in the otf format is somewhat obsessive as nowadays 4K resolution +-- multi-gig videos pass through our networks and storage and memory is abundant. + +-- Although we use a few table readers there i sno real gain in there (apart from having +-- less code. After all there are often not that many demanding features. + local next, type = next, type -local bittest = bit32.btest +local band = bit32.band +local extract = bit32.extract +local bor = bit32.bor +local lshift = bit32.lshift local rshift = bit32.rshift -local concat = table.concat +local gsub = string.gsub local lower = string.lower -local copy = table.copy local sub = string.sub local strip = string.strip local tohash = table.tohash +local concat = table.concat +local copy = table.copy local reversed = table.reversed +local sort = table.sort +local insert = table.insert +local round = math.round +local settings_to_hash = utilities.parsers.settings_to_hash_colon_too local setmetatableindex = table.setmetatableindex local formatters = string.formatters local sortedkeys = table.sortedkeys local sortedhash = table.sortedhash +local sequenced = table.sequenced local report = logs.reporter("otf reader") @@ -71,18 +86,62 @@ local streamreader = readers.streamreader local setposition = streamreader.setposition local getposition = streamreader.getposition -local skipshort = streamreader.skipshort local readushort = streamreader.readcardinal2 -- 16-bit unsigned integer local readulong = streamreader.readcardinal4 -- 24-bit unsigned integer +local readinteger = streamreader.readinteger1 local readshort = streamreader.readinteger2 -- 16-bit signed integer -local readfword = readshort local readstring = streamreader.readstring local readtag = streamreader.readtag local readbytes = streamreader.readbytes +local readfixed = streamreader.readfixed4 +local read2dot14 = streamreader.read2dot14 +local skipshort = streamreader.skipshort +local skipbytes = streamreader.skip +local readbytetable = streamreader.readbytetable +local readbyte = streamreader.readbyte +local readcardinaltable = streamreader.readcardinaltable +local readintegertable = streamreader.readintegertable +local readfword = readshort + +local short = 2 +local ushort = 2 +local ulong = 4 + +directives.register("fonts.streamreader",function() + + streamreader = utilities.streams + + setposition = streamreader.setposition + getposition = streamreader.getposition + readushort = streamreader.readcardinal2 + readulong = streamreader.readcardinal4 + readinteger = streamreader.readinteger1 + readshort = streamreader.readinteger2 + readstring = streamreader.readstring + readtag = streamreader.readtag + readbytes = streamreader.readbytes + readfixed = streamreader.readfixed4 + read2dot14 = streamreader.read2dot14 + skipshort = streamreader.skipshort + skipbytes = streamreader.skip + readbytetable = streamreader.readbytetable + readbyte = streamreader.readbyte + readcardinaltable = streamreader.readcardinaltable + readintegertable = streamreader.readintegertable + readfword = readshort + +end) local gsubhandlers = { } local gposhandlers = { } +readers.gsubhandlers = gsubhandlers +readers.gposhandlers = gposhandlers + +local helpers = readers.helpers +local gotodatatable = helpers.gotodatatable +local setvariabledata = helpers.setvariabledata + local lookupidoffset = -1 -- will become 1 when we migrate (only -1 for comparign with old) local classes = { @@ -121,6 +180,57 @@ local chaindirections = { reversechainedcontextsingle = -1, } +local function setmetrics(data,where,tag,d) + local w = data[where] + if w then + local v = w[tag] + if v then + -- it looks like some fonts set the value and not the delta + -- report("adding %s to %s.%s value %s",d,where,tag,v) + w[tag] = v + d + end + end +end + +local variabletags = { + hasc = function(data,d) setmetrics(data,"windowsmetrics","typoascender",d) end, + hdsc = function(data,d) setmetrics(data,"windowsmetrics","typodescender",d) end, + hlgp = function(data,d) setmetrics(data,"windowsmetrics","typolinegap",d) end, + hcla = function(data,d) setmetrics(data,"windowsmetrics","winascent",d) end, + hcld = function(data,d) setmetrics(data,"windowsmetrics","windescent",d) end, + vasc = function(data,d) setmetrics(data,"vhea not done","ascent",d) end, + vdsc = function(data,d) setmetrics(data,"vhea not done","descent",d) end, + vlgp = function(data,d) setmetrics(data,"vhea not done","linegap",d) end, + xhgt = function(data,d) setmetrics(data,"windowsmetrics","xheight",d) end, + cpht = function(data,d) setmetrics(data,"windowsmetrics","capheight",d) end, + sbxs = function(data,d) setmetrics(data,"windowsmetrics","subscriptxsize",d) end, + sbys = function(data,d) setmetrics(data,"windowsmetrics","subscriptysize",d) end, + sbxo = function(data,d) setmetrics(data,"windowsmetrics","subscriptxoffset",d) end, + sbyo = function(data,d) setmetrics(data,"windowsmetrics","subscriptyoffset",d) end, + spxs = function(data,d) setmetrics(data,"windowsmetrics","superscriptxsize",d) end, + spys = function(data,d) setmetrics(data,"windowsmetrics","superscriptysize",d) end, + spxo = function(data,d) setmetrics(data,"windowsmetrics","superscriptxoffset",d) end, + spyo = function(data,d) setmetrics(data,"windowsmetrics","superscriptyoffset",d) end, + strs = function(data,d) setmetrics(data,"windowsmetrics","strikeoutsize",d) end, + stro = function(data,d) setmetrics(data,"windowsmetrics","strikeoutpos",d) end, + unds = function(data,d) setmetrics(data,"postscript","underlineposition",d) end, + undo = function(data,d) setmetrics(data,"postscript","underlinethickness",d) end, +} + +local read_cardinal = { + streamreader.readcardinal1, + streamreader.readcardinal2, + streamreader.readcardinal3, + streamreader.readcardinal4, +} + +local read_integer = { + streamreader.readinteger1, + streamreader.readinteger2, + streamreader.readinteger3, + streamreader.readinteger4, +} + -- Traditionally we use these unique names (so that we can flatten the lookup list -- (we create subsets runtime) but I will adapt the old code to newer names. @@ -164,7 +274,7 @@ local lookupnames = { -- local lookupstate = setmetatableindex(function(t,k) -- local v = { } -- for kk, vv in next, lookupbits do --- if bittest(k,kk) then +-- if band(k,kk) ~= 0 then -- v[vv] = true -- end -- end @@ -174,36 +284,286 @@ local lookupnames = { local lookupflags = setmetatableindex(function(t,k) local v = { - bittest(k,0x0008) and true or false, -- ignoremarks - bittest(k,0x0004) and true or false, -- ignoreligatures - bittest(k,0x0002) and true or false, -- ignorebaseglyphs - bittest(k,0x0001) and true or false, -- r2l + band(k,0x0008) ~= 0 and true or false, -- ignoremarks + band(k,0x0004) ~= 0 and true or false, -- ignoreligatures + band(k,0x0002) ~= 0 and true or false, -- ignorebaseglyphs + band(k,0x0001) ~= 0 and true or false, -- r2l } t[k] = v return v end) +-- Variation stores: it's not entirely clear if the regions are a shared +-- resource (it looks like they are). Anyway, we play safe and use a +-- share. + +-- values can be anything the min/max permits so we can either think of +-- real values of a fraction along the axis (probably easier) + +-- wght:400,wdth:100,ital:1 + +local function axistofactors(str) + local t = settings_to_hash(str) + for k, v in next, t do + t[k] = tonumber(v) or v -- this also normalizes numbers itself + end + return t +end + +local hash = table.setmetatableindex(function(t,k) + local v = sequenced(axistofactors(k),",") + t[k] = v + return v +end) + +helpers.normalizedaxishash = hash + +local cleanname = fonts.names and fonts.names.cleanname or function(name) + return name and (gsub(lower(name),"[^%a%d]","")) or nil +end + +helpers.cleanname = cleanname + +function helpers.normalizedaxis(str) + return hash[str] or str +end + +-- contradicting spec ... (signs) so i'll check it and fix it once we have +-- proper fonts + +local function getaxisscale(segments,minimum,default,maximum,user) + -- + -- returns the right values cf example in standard + -- + if not minimum or not default or not maximum then + return false + end + if user < minimum then + user = minimum + elseif user > maximum then + user = maximum + end + if user < default then + default = - (default - user) / (default - minimum) + elseif user > default then + default = (user - default) / (maximum - default) + else + default = 0 + end + if not segments then + return default + end + local e + for i=1,#segments do + local s = segments[i] + if type(s) ~= "number" then + report("using default axis scale") + return default + elseif s[1] >= default then + if s[2] == default then + return default + else + e = i + break + end + end + end + if e then + local b = segments[e-1] + local e = segments[e] + return b[2] + (e[2] - b[2]) * (default - b[1]) / (e[1] - b[1]) + else + return false + end +end + +local function getfactors(data,instancespec) + if instancespec == true then + -- take default + elseif type(instancespec) ~= "string" or instancespec == "" then + return + end + local variabledata = data.variabledata + if not variabledata then + return + end + local instances = variabledata.instances + local axis = variabledata.axis + local segments = variabledata.segments + if instances and axis then + local values + if instancespec == true then + -- first instance: + -- values = instances[1].values + -- axis defaults: + values = { } + for i=1,#axis do + values[i] = { + -- axis = axis[i].tag, + value = axis[i].default, + } + end + + else + for i=1,#instances do + local instance = instances[i] + if cleanname(instance.subfamily) == instancespec then + values = instance.values + break + end + end + end + if values then + local factors = { } + for i=1,#axis do + local a = axis[i] + factors[i] = getaxisscale(segments,a.minimum,a.default,a.maximum,values[i].value) + end + return factors + end + local values = axistofactors(hash[instancespec] or instancespec) + if values then + local factors = { } + for i=1,#axis do + local a = axis[i] + local d = a.default + factors[i] = getaxisscale(segments,a.minimum,d,a.maximum,values[a.name or a.tag] or d) + end + return factors + end + end +end + +local function getscales(regions,factors) + local scales = { } + for i=1,#regions do + local region = regions[i] + local s = 1 + for j=1,#region do + local axis = region[j] + local f = factors[j] + local start = axis.start + local peak = axis.peak + local stop = axis.stop + -- get rid of these tests, false flag + if start > peak or peak > stop then + -- * 1 + elseif start < 0 and stop > 0 and peak ~= 0 then + -- * 1 + elseif peak == 0 then + -- * 1 + elseif f < start or f > stop then + -- * 0 + s = 0 + break + elseif f < peak then + -- s = - s * (f - start) / (peak - start) + s = s * (f - start) / (peak - start) + elseif f > peak then + s = s * (stop - f) / (stop - peak) + else + -- * 1 + end + end + scales[i] = s + end + return scales +end + +helpers.getaxisscale = getaxisscale +helpers.getfactors = getfactors +helpers.getscales = getscales +helpers.axistofactors = axistofactors + +local function readvariationdata(f,storeoffset,factors) -- store + local position = getposition(f) + setposition(f,storeoffset) + -- header + local format = readushort(f) + local regionoffset = storeoffset + readulong(f) + local nofdeltadata = readushort(f) + local deltadata = readcardinaltable(f,nofdeltadata,ulong) + -- regions + setposition(f,regionoffset) + local nofaxis = readushort(f) + local nofregions = readushort(f) + local regions = { } + for i=1,nofregions do -- 0 + local t = { } + for i=1,nofaxis do + t[i] = { -- maybe no keys, just 1..3 + start = read2dot14(f), + peak = read2dot14(f), + stop = read2dot14(f), + } + end + regions[i] = t + end + -- deltas + if factors then + for i=1,nofdeltadata do + setposition(f,storeoffset+deltadata[i]) + local nofdeltasets = readushort(f) + local nofshorts = readushort(f) + local nofregions = readushort(f) + local usedregions = { } + local deltas = { } + for i=1,nofregions do + usedregions[i] = regions[readushort(f)+1] + end + -- we could test before and save a for + for i=1,nofdeltasets do + local t = readintegertable(f,nofshorts,short) + for i=nofshorts+1,nofregions do + t[i] = readinteger(f) + end + deltas[i] = t + end + deltadata[i] = { + regions = usedregions, + deltas = deltas, + scales = factors and getscales(usedregions,factors) or nil, + } + end + end + setposition(f,position) + return regions, deltadata +end + +helpers.readvariationdata = readvariationdata + -- Beware: only use the simple variant if we don't set keys/values (otherwise too many entries). We -- could also have a variant that applies a function but there is no real benefit in this. 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) + -- often 1 or 2 + if nofcoverage == 1 then + return { readushort(f) } + elseif nofcoverage == 2 then + return { readushort(f), readushort(f) } + else + return readcardinaltable(f,nofcoverage,ushort) end + elseif nofcoverage == 1 then + return { [readushort(f)] = 0 } + elseif nofcoverage == 2 then + return { [readushort(f)] = 0, [readushort(f)] = 1 } else + local coverage = { } for i=0,nofcoverage-1 do coverage[readushort(f)] = i -- index in record end + return coverage end elseif coverageformat == 2 then local nofranges = readushort(f) - local n = simple and 1 or 0 -- needs checking + local coverage = { } + local n = simple and 1 or 0 -- needs checking for i=1,nofranges do local firstindex = readushort(f) local lastindex = readushort(f) @@ -220,10 +580,11 @@ local function readcoverage(f,offset,simple) end end end + return coverage else report("unknown coverage format %a ",coverageformat) + return { } end - return coverage end local function readclassdef(f,offset,preset) @@ -283,36 +644,168 @@ end -- extra readers -local function readposition(f,format) +local skips = { [0] = + 0, -- ---- + 1, -- ---x + 1, -- --y- + 2, -- --yx + 1, -- -h-- + 2, -- -h-x + 2, -- -hy- + 3, -- -hyx + 2, -- v--x + 2, -- v-y- + 3, -- v-yx + 2, -- vh-- + 3, -- vh-x + 3, -- vhy- + 4, -- vhyx +} + +-- We can assume that 0 is nothing and in fact we can start at 1 as +-- usual in Lua to make sure of that. + +local function readvariation(f,offset) + local p = getposition(f) + setposition(f,offset) + local outer = readushort(f) + local inner = readushort(f) + local format = readushort(f) + setposition(f,p) + if format == 0x8000 then + return outer, inner + end +end + +local function readposition(f,format,mainoffset,getdelta) if format == 0 then - return nil + return false end - -- maybe fast test on 0x0001 + 0x0002 + 0x0004 + 0x0008 (profile first) - local x = bittest(format,0x0001) and readshort(f) or 0 -- placement - local y = bittest(format,0x0002) and readshort(f) or 0 -- placement - local h = bittest(format,0x0004) and readshort(f) or 0 -- advance - local v = bittest(format,0x0008) and readshort(f) or 0 -- advance - if x == 0 and y == 0 and h == 0 and v == 0 then - return nil + -- a few happen often + if format == 0x04 then + local h = readshort(f) + if h == 0 then + return true -- all zero + else + return { 0, 0, h, 0 } + end + end + if format == 0x05 then + local x = readshort(f) + local h = readshort(f) + if x == 0 and h == 0 then + return true -- all zero + else + return { x, 0, h, 0 } + end + end + if format == 0x44 then + local h = readshort(f) + if getdelta then + local d = readshort(f) -- short or ushort + if d > 0 then + local outer, inner = readvariation(f,mainoffset+d) + if outer then + h = h + getdelta(outer,inner) + end + end + else + skipshort(f,1) + end + if h == 0 then + return true -- all zero + else + return { 0, 0, h, 0 } + end + end + -- + -- todo: + -- + -- if format == 0x55 then + -- local x = readshort(f) + -- local h = readshort(f) + -- .... + -- end + -- + local x = band(format,0x1) ~= 0 and readshort(f) or 0 -- x placement + local y = band(format,0x2) ~= 0 and readshort(f) or 0 -- y placement + local h = band(format,0x4) ~= 0 and readshort(f) or 0 -- h advance + local v = band(format,0x8) ~= 0 and readshort(f) or 0 -- v advance + if format >= 0x10 then + local X = band(format,0x10) ~= 0 and skipshort(f) or 0 + local Y = band(format,0x20) ~= 0 and skipshort(f) or 0 + local H = band(format,0x40) ~= 0 and skipshort(f) or 0 + local V = band(format,0x80) ~= 0 and skipshort(f) or 0 + local s = skips[extract(format,4,4)] + if s > 0 then + skipshort(f,s) + end + if getdelta then + if X > 0 then + local outer, inner = readvariation(f,mainoffset+X) + if outer then + x = x + getdelta(outer,inner) + end + end + if Y > 0 then + local outer, inner = readvariation(f,mainoffset+Y) + if outer then + y = y + getdelta(outer,inner) + end + end + if H > 0 then + local outer, inner = readvariation(f,mainoffset+H) + if outer then + h = h + getdelta(outer,inner) + end + end + if V > 0 then + local outer, inner = readvariation(f,mainoffset+V) + if outer then + v = v + getdelta(outer,inner) + end + end + end + return { x, y, h, v } + elseif x == 0 and y == 0 and h == 0 and v == 0 then + return true -- all zero else return { x, y, h, v } end end -local function readanchor(f,offset) +local function readanchor(f,offset,getdelta) -- maybe also ignore 0's as in pos if not offset or offset == 0 then return nil -- false 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 + -- no need to skip as we position each + local format = readshort(f) -- 1: x y 2: x y index 3 x y X Y + local x = readshort(f) + local y = readshort(f) + if format == 3 then + if getdelta then + local X = readshort(f) + local Y = readshort(f) + if X > 0 then + local outer, inner = readvariation(f,offset+X) + if outer then + x = x + getdelta(outer,inner) + end + end + if Y > 0 then + local outer, inner = readvariation(f,offset+Y) + if outer then + y = y + getdelta(outer,inner) + end + end + else + skipshort(f,2) + end + return { x, y } -- , { xindex, yindex } + else + return { x, y } end - return { readshort(f), readshort(f) } end -- common handlers: inlining can be faster but we cache anyway @@ -325,23 +818,17 @@ local function readfirst(f,offset) return { readushort(f) } end -local function readarray(f,offset,first) +-- quite often 0, 1, 2 + +function readarray(f,offset) 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 + if n == 1 then + return { readushort(f) }, 1 elseif n > 0 then - local t = { } - for i=1,n do - t[i] = readushort(f) - end - return t, n + return readcardinaltable(f,n,ushort), n end end @@ -390,7 +877,13 @@ local function readlookuparray(f,noflookups,nofcurrent) if index > length then length = index end - lookups[index] = readushort(f) + 1 + local lookup = readushort(f) + 1 + local list = lookups[index] + if list then + list[#list+1] = lookup + else + lookups[index] = { lookup } + end end for index=1,length do if not lookups[index] then @@ -736,10 +1229,7 @@ function gsubhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofg elseif subtype == 2 then -- in streamreader a seek and fetch is faster than a temp table local coverage = readushort(f) local nofreplacements = readushort(f) - local replacements = { } - for i=1,nofreplacements do - replacements[i] = readushort(f) - end + local replacements = readcardinaltable(f,nofreplacements,ushort) local coverage = readcoverage(f,tableoffset + coverage) -- not simple as we need to set key/value anyway for index, newindex in next, coverage do newindex = newindex + 1 @@ -767,18 +1257,10 @@ local function sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp if subtype == 1 then local coverage = readushort(f) local nofsequence = readushort(f) - local sequences = { } - for i=1,nofsequence do - sequences[i] = readushort(f) - end + local sequences = readcardinaltable(f,nofsequence,ushort) 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 + sequences[i] = readcardinaltable(f,readushort(f),ushort) end local coverage = readcoverage(f,tableoffset + coverage) for index, newindex in next, coverage do @@ -813,19 +1295,20 @@ function gsubhandlers.ligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,no if subtype == 1 then local coverage = readushort(f) local nofsets = readushort(f) - local ligatures = { } - for i=1,nofsets do - ligatures[i] = readushort(f) - end + local ligatures = readcardinaltable(f,nofsets,ushort) 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) + if n == 1 then + ligatures[i] = { offset + readushort(f) } + else + local l = { } + for i=1,n do + l[i] = offset + readushort(f) + end + ligatures[i] = l end - ligatures[i] = l end local coverage = readcoverage(f,tableoffset + coverage) for index, newindex in next, coverage do @@ -883,12 +1366,14 @@ function gsubhandlers.reversechainedcontextsingle(f,fontdata,lookupid,lookupoffs before = readcoveragearray(f,tableoffset,before,true) after = readcoveragearray(f,tableoffset,after,true) return { - coverage = { - format = "reversecoverage", -- reversesub - before = before, - current = current, - after = after, - replacements = replacements, + format = "reversecoverage", -- reversesub + rules = { + { + before = before, + current = current, + after = after, + replacements = replacements, + } } }, "reversechainedcontextsingle" else @@ -898,20 +1383,21 @@ end -- gpos handlers -local function readpairsets(f,tableoffset,sets,format1,format2) +local function readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta) local done = { } for i=1,#sets do local offset = sets[i] local reused = done[offset] if not reused then - setposition(f,tableoffset + offset) + offset = tableoffset + offset + setposition(f,offset) local n = readushort(f) reused = { } for i=1,n do reused[i] = { readushort(f), -- second glyph id - readposition(f,format1), - readposition(f,format2) + readposition(f,format1,offset,getdelta), + readposition(f,format2,offset,getdelta), } end done[offset] = reused @@ -921,14 +1407,14 @@ local function readpairsets(f,tableoffset,sets,format1,format2) return sets end -local function readpairclasssets(f,nofclasses1,nofclasses2,format1,format2) +local function readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,mainoffset,getdelta) 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) + local one = readposition(f,format1,mainoffset,getdelta) + local two = readposition(f,format2,mainoffset,getdelta) if one or two then classlist2[j] = { one, two } else @@ -944,34 +1430,35 @@ end function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs) local tableoffset = lookupoffset + offset setposition(f,tableoffset) - local subtype = readushort(f) + local subtype = readushort(f) + local getdelta = fontdata.temporary.getdelta if subtype == 1 then local coverage = readushort(f) local format = readushort(f) - local value = readposition(f,format) + local value = readposition(f,format,tableoffset,getdelta) local coverage = readcoverage(f,tableoffset+coverage) for index, newindex in next, coverage do - coverage[index] = value + coverage[index] = value -- will be packed and shared anyway end return { - format = "pair", - coverage = coverage + format = "single", + coverage = coverage, } elseif subtype == 2 then local coverage = readushort(f) local format = readushort(f) - local values = { } local nofvalues = readushort(f) + local values = { } for i=1,nofvalues do - values[i] = readposition(f,format) + values[i] = readposition(f,format,tableoffset,getdelta) 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 + format = "single", + coverage = coverage, } else report("unsupported subtype %a in %a positioning",subtype,"single") @@ -983,18 +1470,17 @@ end -- ValueFormat1 applies to the ValueRecord of the first glyph in each pair. ValueRecords for all first glyphs must use ValueFormat1. If ValueFormat1 is set to zero (0), the corresponding glyph has no ValueRecord and, therefore, should not be repositioned. -- ValueFormat2 applies to the ValueRecord of the second glyph in each pair. ValueRecords for all second glyphs must use ValueFormat2. If ValueFormat2 is set to null, then the second glyph of the pair is the “next” glyph for which a lookup should be performed. --- !!!!! this needs checking: when both false, we have no hit so then we might need to fall through - function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs) local tableoffset = lookupoffset + offset setposition(f,tableoffset) - local subtype = readushort(f) + local subtype = readushort(f) + local getdelta = fontdata.temporary.getdelta 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) + sets = readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta) coverage = readcoverage(f,tableoffset + coverage) for index, newindex in next, coverage do local set = sets[newindex+1] @@ -1006,9 +1492,9 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly local first = value[2] local second = value[3] if first or second then - hash[other] = { first, second } -- needs checking + hash[other] = { first, second or nil } -- needs checking else - hash[other] = nil + hash[other] = nil -- what if set, maybe warning end end end @@ -1016,7 +1502,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly end return { format = "pair", - coverage = coverage + coverage = coverage, } elseif subtype == 2 then local coverage = readushort(f) @@ -1026,7 +1512,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly local classdef2 = readushort(f) local nofclasses1 = readushort(f) -- incl class 0 local nofclasses2 = readushort(f) -- incl class 0 - local classlist = readpairclasssets(f,nofclasses1,nofclasses2,format1,format2) + local classlist = readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,tableoffset,getdelta) coverage = readcoverage(f,tableoffset+coverage) classdef1 = readclassdef(f,tableoffset+classdef1,coverage) classdef2 = readclassdef(f,tableoffset+classdef2,nofglyphs) @@ -1042,7 +1528,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly local first = offsets[1] local second = offsets[2] if first or second then - hash[paired] = { first, second } + hash[paired] = { first, second or nil } else -- upto the next lookup for this combination end @@ -1054,7 +1540,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly end return { format = "pair", - coverage = usedcoverage + coverage = usedcoverage, } elseif subtype == 3 then report("yet unsupported subtype %a in %a positioning",subtype,"pair") @@ -1066,7 +1552,8 @@ end function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs) local tableoffset = lookupoffset + offset setposition(f,tableoffset) - local subtype = readushort(f) + local subtype = readushort(f) + local getdelta = fontdata.temporary.getdelta if subtype == 1 then local coverage = tableoffset + readushort(f) local nofrecords = readushort(f) @@ -1075,24 +1562,34 @@ function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nof 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, + -- entry = entry ~= 0 and (tableoffset + entry) or false, + -- exit = exit ~= 0 and (tableoffset + exit ) or nil, + entry ~= 0 and (tableoffset + entry) or false, + exit ~= 0 and (tableoffset + exit ) or nil, } end + -- slot 1 will become hash after loading and it must be unique because we + -- pack the tables (packed we turn the cc-* into a zero) + local cc = (fontdata.temporary.cursivecount or 0) + 1 + fontdata.temporary.cursivecount = cc + cc = "cc-" .. cc coverage = readcoverage(f,coverage) for i=1,nofrecords do local r = records[i] records[i] = { - 1, -- will become hash after loading (must be unique per lookup when packed) - readanchor(f,r.entry) or nil, - readanchor(f,r.exit ) or nil, + -- 1, + cc, + -- readanchor(f,r.entry,getdelta) or false, + -- readanchor(f,r.exit, getdelta) or nil, + readanchor(f,r[1],getdelta) or false, + readanchor(f,r[2],getdelta) or nil, } end for index, newindex in next, coverage do coverage[index] = records[newindex+1] end return { - coverage = coverage + coverage = coverage, } else report("unsupported subtype %a in %a positioning",subtype,"cursive") @@ -1102,7 +1599,8 @@ end local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,ligature) local tableoffset = lookupoffset + offset setposition(f,tableoffset) - local subtype = readushort(f) + local subtype = readushort(f) + local getdelta = fontdata.temporary.getdelta if subtype == 1 then -- we are one based, not zero local markcoverage = tableoffset + readushort(f) @@ -1121,17 +1619,12 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp local lastanchor = fontdata.lastanchor or 0 local usedanchors = { } -- --- local placeholder = (fontdata.markcount or 0) + 1 --- fontdata.markcount = placeholder --- placeholder = "m" .. placeholder - -- for i=1,nofmarkclasses do local class = readushort(f) + 1 local offset = readushort(f) if offset == 0 then markclasses[i] = false else --- markclasses[i] = { placeholder, class, markoffset + offset } markclasses[i] = { class, markoffset + offset } end usedanchors[class] = true @@ -1139,8 +1632,7 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp for i=1,nofmarkclasses do local mc = markclasses[i] if mc then --- mc[3] = readanchor(f,mc[3]) - mc[2] = readanchor(f,mc[2]) + mc[2] = readanchor(f,mc[2],getdelta) end end -- @@ -1194,7 +1686,7 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp local classes = components[c] if classes then for i=1,nofclasses do - local anchor = readanchor(f,classes[i]) + local anchor = readanchor(f,classes[i],getdelta) local bclass = baseclasses[i] local bentry = bclass[b] if bentry then @@ -1204,7 +1696,6 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp end end end --- components[i] = classes end end end @@ -1237,7 +1728,7 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp local r = baserecords[i] local b = basecoverage[i] for j=1,nofclasses do - baseclasses[j][b] = readanchor(f,r[j]) + baseclasses[j][b] = readanchor(f,r[j],getdelta) end end for index, newindex in next, markcoverage do @@ -1295,10 +1786,10 @@ do setposition(f,offset) local designsize = readushort(f) if designsize > 0 then -- we could also have a threshold - local fontstyle = readushort(f) - local guimenuid = readushort(f) - local minsize = readushort(f) - local maxsize = readushort(f) + local fontstyleid = 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 @@ -1328,6 +1819,10 @@ do end end + -- function plugins.rvrn(f,fontdata,tableoffset,feature) + -- -- todo, at least a message + -- end + -- feature order needs checking ... as we loop over a hash ... however, in the file -- they are sorted so order is not that relevant @@ -1461,10 +1956,15 @@ do local parameters = readushort(f) -- feature.parameters local noflookups = readushort(f) if noflookups > 0 then - local lookups = { } +-- local lookups = { } +-- feature.lookups = lookups +-- for j=1,noflookups do +-- lookups[j] = readushort(f) + 1 +-- end + local lookups = readcardinaltable(f,noflookups,ushort) feature.lookups = lookups for j=1,noflookups do - lookups[j] = readushort(f) + 1 + lookups[j] = lookups[j] + 1 end end if parameters > 0 then @@ -1480,14 +1980,11 @@ do 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 + local lookups = readcardinaltable(f,noflookups,ushort) for lookupid=1,noflookups do - local index = lookups[lookupid] - setposition(f,lookupoffset+index) + local offset = lookups[lookupid] + setposition(f,lookupoffset+offset) local subtables = { } local typebits = readushort(f) local flagbits = readushort(f) @@ -1495,11 +1992,10 @@ do local lookupflags = lookupflags[flagbits] local nofsubtables = readushort(f) for j=1,nofsubtables do - local offset = readushort(f) - subtables[j] = offset + index -- we can probably put lookupoffset here + subtables[j] = offset + readushort(f) -- we can probably put lookupoffset here end -- which one wins? - local markclass = bittest(flagbits,0x0010) -- usemarkfilteringset + local markclass = band(flagbits,0x0010) ~= 0 -- usemarkfilteringset if markclass then markclass = readushort(f) -- + 1 end @@ -1521,26 +2017,12 @@ do 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 function resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset) - local sequences = fontdata.sequences or { } - local sublookuplist = fontdata.sublookups or { } + local sequences = fontdata.sequences or { } + local sublookuplist = fontdata.sublookups or { } fontdata.sequences = sequences fontdata.sublookups = sublookuplist local nofsublookups = #sublookuplist @@ -1555,6 +2037,8 @@ do local noflookups = #lookups local lookupprefix = sub(what,2,2) -- g[s|p][ub|os] -- + local usedlookups = false -- setmetatableindex("number") + -- for lookupid=1,noflookups do local lookup = lookups[lookupid] local lookuptype = lookup.type @@ -1565,7 +2049,7 @@ do local nofsubtables = #subtables local order = lookup.order local flags = lookup.flags - -- this is expected in th efont handler (faster checking) + -- this is expected in the font handler (faster checking) if flags[1] then flags[1] = "mark" end if flags[2] then flags[2] = "ligature" end if flags[3] then flags[3] = "base" end @@ -1593,10 +2077,11 @@ do 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 + local rule = rules[i] + local before = rule.before + local current = rule.current + local after = rule.after + local replacements = rule.replacements if before then for i=1,#before do before[i] = tohash(before[i]) @@ -1605,15 +2090,44 @@ do rule.before = reversed(before) end if current then - for i=1,#current do - current[i] = tohash(current[i]) + if replacements then + -- We have a reverse lookup and therefore only one current entry. We might need + -- to reverse the order in the before and after lists so that needs checking. + local first = current[1] + local hash = { } + local repl = { } + for i=1,#first do + local c = first[i] + hash[c] = true + repl[c] = replacements[i] + end + rule.current = { hash } + rule.replacements = repl + else + for i=1,#current do + current[i] = tohash(current[i]) + end end + else + -- weird lookup end if after then for i=1,#after do after[i] = tohash(after[i]) end end + if usedlookups then + local lookups = rule.lookups + if lookups then + for k, v in next, lookups do + if v then + for k, v in next, v do + usedlookups[v] = usedlookups[v] + 1 + end + end + end + end + end end end end @@ -1665,6 +2179,10 @@ do end end + if usedlookups then + report("used %s lookups: % t",what,sortedkeys(usedlookups)) + end + -- When we have a context, we have sublookups that resolve into lookups for which we need to -- know the type. We split the main lookuptable in two parts: sequences (the main lookups) -- and subtable lookups (simple specs with no features). We could keep them merged and might do @@ -1698,56 +2216,63 @@ do report_issue(i,what,sequence,"empty") rule.lookups = nil else - -- we can have holes in rlookups - -- for index, lookupid in sortedhash(rlookups) do + -- we can have holes in rlookups flagged false and we can have multiple lookups + -- applied (first time seen in seguemj) local length = #rlookups --- for index in next, rlookups do --- if index > length then --- length = index --- end --- end for index=1,length do - local lookupid = rlookups[index] - if lookupid then - local h = sublookuphash[lookupid] - if not h then - -- here we have a lookup that is used independent as well - -- as in another one - local lookup = lookups[lookupid] - if lookup then - local d = lookup.done - if d then - nofsublookups = nofsublookups + 1 - -- report("registering %i as sublookup %i",lookupid,nofsublookups) - h = { - index = nofsublookups, -- handy for tracing - name = f_lookupname(lookupprefix,"d",lookupid+lookupidoffset), - derived = true, -- handy for tracing - steps = d.steps, - nofsteps = d.nofsteps, - type = d.lookuptype, - markclass = d.markclass or nil, - flags = d.flags, - -- chain = d.chain, - } - sublookuplist[nofsublookups] = copy(h) -- we repack later - sublookuphash[lookupid] = nofsublookups - sublookupcheck[lookupid] = 1 - h = nofsublookups --- patch by Hans + local lookuplist = rlookups[index] + if lookuplist then + local length = #lookuplist + local found = { } + local noffound = 0 + for index=1,length do + local lookupid = lookuplist[index] + if lookupid then + local h = sublookuphash[lookupid] + if not h then + -- here we have a lookup that is used independent as well + -- as in another one + local lookup = lookups[lookupid] + if lookup then + local d = lookup.done + if d then + nofsublookups = nofsublookups + 1 + -- report("registering %i as sublookup %i",lookupid,nofsublookups) + local l = { + index = nofsublookups, -- handy for tracing + name = f_lookupname(lookupprefix,"d",lookupid+lookupidoffset), + derived = true, -- handy for tracing + steps = d.steps, + nofsteps = d.nofsteps, + type = d.lookuptype or "gsub_single", -- todo: check type + markclass = d.markclass or nil, + flags = d.flags, + -- chain = d.chain, + } + sublookuplist[nofsublookups] = copy(l) -- we repack later + 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 - report_issue(i,what,sequence,"missing") - rule.lookups = nil - break + sublookupcheck[lookupid] = sublookupcheck[lookupid] + 1 + end + if h then + noffound = noffound + 1 + found[noffound] = h 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 + rlookups[index] = noffound > 0 and found or false else rlookups[index] = false end @@ -1764,45 +2289,122 @@ do 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) - -- inspect(l) 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 + local function loadvariations(f,fontdata,variationsoffset,lookuptypes,featurehash,featureorder) + setposition(f,variationsoffset) + local version = readulong(f) + local nofrecords = readulong(f) + local records = { } + for i=1,nofrecords do + records[i] = { + conditions = readulong(f), + substitutions = readulong(f), + } end - local scriptoffset, featureoffset, lookupoffset = readscriptoffsets(f,fontdata,tableoffset) - if not scriptoffset then - return + for i=1,nofrecords do + local record = records[i] + local offset = record.conditions + if offset == 0 then + record.condition = nil + record.matchtype = "always" + else + local offset = variationsoffset+offset + setposition(f,offset) + local nofconditions = readushort(f) + local conditions = { } + for i=1,nofconditions do + conditions[i] = offset + readulong(f) + end + record.conditions = conditions + record.matchtype = "condition" + end 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 } + for i=1,nofrecords do + local record = records[i] + if record.matchtype == "condition" then + local conditions = record.conditions + for i=1,#conditions do + setposition(f,conditions[i]) + conditions[i] = { + format = readushort(f), + axis = readushort(f), + minvalue = read2dot14(f), + maxvalue = read2dot14(f), + } + end + end end - -- - if not lookupstoo then - return + + for i=1,nofrecords do + local record = records[i] + local offset = record.substitutions + if offset == 0 then + record.substitutions = { } + else + setposition(f,variationsoffset + offset) + local version = readulong(f) + local nofsubstitutions = readushort(f) + local substitutions = { } + for i=1,nofsubstitutions do + substitutions[readushort(f)] = readulong(f) + end + for index, alternates in sortedhash(substitutions) do + if index == 0 then + record.substitutions = false + else + local tableoffset = variationsoffset + offset + alternates + setposition(f,tableoffset) + local parameters = readulong(f) -- feature parameters + local noflookups = readushort(f) + local lookups = readcardinaltable(f,noflookups,ushort) -- not sure what to do with these + -- todo : resolve to proper lookups + record.substitutions = lookups + end + end + end end - -- - local lookups = readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder) - -- - if lookups then - resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what) + setvariabledata(fontdata,"features",records) + end + + local function readscripts(f,fontdata,what,lookuptypes,lookuphandlers,lookupstoo) + local tableoffset = gotodatatable(f,fontdata,what,true) + if tableoffset then + local version = readulong(f) + local scriptoffset = tableoffset + readushort(f) + local featureoffset = tableoffset + readushort(f) + local lookupoffset = tableoffset + readushort(f) + local variationsoffset = version > 0x00010000 and (tableoffset + readulong(f)) or 0 + 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,tableoffset) + end + -- + if variationsoffset > 0 then + loadvariations(f,fontdata,variationsoffset,lookuptypes,featurehash,featureorder) + end end end @@ -1822,61 +2424,64 @@ do 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) - -- bit 8-15 of coverage: format 0 or 2 - local format = bit32.rshift(coverage,8) -- is this ok? - 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) + if noftables > 1 then + report("adding global kern table as gpos feature %a",name) + local kerns = setmetatableindex("table") + for i=1,noftables do + local version = readushort(f) + local length = readushort(f) + local coverage = readushort(f) + -- bit 8-15 of coverage: format 0 or 2 + local format = rshift(coverage,8) -- is this ok + 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 + -- apple specific so let's ignore it + else + -- not supported by ms end - elseif format == 2 then - -- apple specific so let's ignore it - else - -- not supported by ms 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", + 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", - -- type = "gpos_single", -- maybe better - flags = { false, false, false, false }, - order = { name }, - features = { [name] = feature }, - } + nofsteps = 1, + type = "gpos_pair", + flags = { false, false, false, false }, + order = { name }, + features = { [name] = feature }, + } + else + report("ignoring empty kern table of feature %a",name) + end end function readers.gsub(f,fontdata,specification) @@ -1897,26 +2502,30 @@ do 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) -- used for bitmaps - local ligaturecarets = tableoffset + readushort(f) -- used in editors (maybe nice for tracing) - 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 - -- class definitions - setposition(f,classoffset) + if not specification.glyphs then + return + end + local datatable = fontdata.tables.gdef + if datatable then + local tableoffset = datatable.offset + setposition(f,tableoffset) + local version = readulong(f) + local classoffset = readushort(f) + local attachmentoffset = readushort(f) -- used for bitmaps + local ligaturecarets = readushort(f) -- used in editors (maybe nice for tracing) + local markclassoffset = readushort(f) + local marksetsoffset = version >= 0x00010002 and readushort(f) or 0 + local varsetsoffset = version >= 0x00010003 and readulong(f) or 0 + local glyphs = fontdata.glyphs + local marks = { } + local markclasses = setmetatableindex("table") + local marksets = setmetatableindex("table") + fontdata.marks = marks + fontdata.markclasses = markclasses + fontdata.marksets = marksets + -- class definitions + if classoffset ~= 0 then + setposition(f,tableoffset + classoffset) local classformat = readushort(f) if classformat == 1 then local firstindex = readushort(f) @@ -1944,8 +2553,10 @@ function readers.gdef(f,fontdata,specification) end end end - -- mark classes - setposition(f,markclassoffset) + end + -- mark classes + if markclassoffset ~= 0 then + setposition(f,tableoffset + markclassoffset) local classformat = readushort(f) if classformat == 1 then local firstindex = readushort(f) @@ -1964,24 +2575,55 @@ function readers.gdef(f,fontdata,specification) end end end - -- mark sets : todo: just make the same as class sets above - if marksetsoffset and marksetsoffset > tableoffset then -- zero offset means no table - 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 + -- mark sets : todo: just make the same as class sets above + if marksetsoffset ~= 0 then + marksetsoffset = tableoffset + marksetsoffset + setposition(f,marksetsoffset) + local format = readushort(f) + if format == 1 then + local nofsets = readushort(f) + local sets = readcardinaltable(f,nofsets,ulong) + for i=1,nofsets do + local offset = sets[i] + if offset ~= 0 then + marksets[i] = readcoverage(f,marksetsoffset+offset) end - for i=1,nofsets do - local offset = sets[i] - if offset ~= 0 then - marksets[i] = readcoverage(f,marksetsoffset+offset) + end + end + end + + local factors = specification.factors + + if (specification.variable or factors) and varsetsoffset ~= 0 then + + local regions, deltas = readvariationdata(f,tableoffset+varsetsoffset,factors) + + -- setvariabledata(fontdata,"gregions",regions) + + if factors then + fontdata.temporary.getdelta = function(outer,inner) + local delta = deltas[outer+1] + if delta then + local d = delta.deltas[inner+1] + if d then + local scales = delta.scales + local dd = 0 + for i=1,#scales do + local di = d[i] + if di then + dd = dd + scales[i] * di + else + break + end + end + return round(dd) end end + return 0 end end + end end end @@ -2178,14 +2820,8 @@ local function readmathvariants(f,fontdata,offset) 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 + local vconstruction = readcardinaltable(f,vnofglyphs,ushort) + local hconstruction = readcardinaltable(f,hnofglyphs,ushort) fontdata.mathconstants.MinConnectorOverlap = minoverlap @@ -2243,7 +2879,7 @@ local function readmathvariants(f,fontdata,offset) advance = readushort(f), } local flags = readushort(f) - if bittest(flags,0x0001) then + if band(flags,0x0001) ~= 0 then p.extender = 1 -- true end parts[i] = p @@ -2270,173 +2906,719 @@ local function readmathvariants(f,fontdata,offset) 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) + local tableoffset = gotodatatable(f,fontdata,"math",specification.glyphs) + if tableoffset then + 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 + +function readers.colr(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"colr",specification.glyphs) + if tableoffset then + 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 + -- The special value 0xFFFF is foreground (but we index from 1). It + -- more looks like indices into a palette so 'class' is a better name + -- than 'palette'. + 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 - if glyphinfo ~= 0 then - readmathglyphinfo(f,fontdata,tableoffset+glyphinfo) + 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 - if variants ~= 0 then - readmathvariants(f,fontdata,tableoffset+variants) + glyphs[glyphindex].colors = t + end + end + fontdata.hascolor = true +end + +function readers.cpal(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"cpal",specification.glyphs) + if tableoffset then + 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 = readcardinaltable(f,nofpalettes,ushort) + if version == 1 then + -- used for guis + 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 -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 +function readers.svg(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"svg",specification.glyphs) + if tableoffset then + 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 + +function readers.sbix(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"sbix",specification.glyphs) + if tableoffset then + local version = readushort(f) + local flags = readushort(f) + local nofstrikes = readulong(f) + local strikes = { } + local nofglyphs = fontdata.nofglyphs + for i=1,nofstrikes do + strikes[i] = readulong(f) + end + -- if true then + local shapes = { } + local done = 0 + for i=1,nofstrikes do + local strikeoffset = strikes[i] + tableoffset + setposition(f,strikeoffset) + strikes[i] = { + ppem = readushort(f), + ppi = readushort(f), + offset = strikeoffset + } 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 - -- The special value 0xFFFF is foreground (but we index from 1). It - -- more looks like indices into a palette so 'class' is a better name - -- than 'palette'. - 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 + -- highest first + sort(strikes,function(a,b) + if b.ppem == a.ppem then + return b.ppi < a.ppi + else + return b.ppem < a.ppem + end + end) + local glyphs = { } + for i=1,nofstrikes do + local strike = strikes[i] + local strikeppem = strike.ppem + local strikeppi = strike.ppi + local strikeoffset = strike.offset + setposition(f,strikeoffset) + for i=0,nofglyphs do + glyphs[i] = readulong(f) + end + local glyphoffset = glyphs[0] + for i=0,nofglyphs-1 do + local nextoffset = glyphs[i+1] + if not shapes[i] then + local datasize = nextoffset - glyphoffset + if datasize > 0 then + setposition(f,strikeoffset + glyphoffset) + shapes[i] = { + x = readshort(f), + y = readshort(f), + tag = readtag(f), -- maybe for tracing + data = readstring(f,datasize-8), + ppem = strikeppem, -- not used, for tracing + ppi = strikeppi, -- not used, for tracing + } + done = done + 1 + if done == nofglyphs then + break + end + end end + glyphoffset = nextoffset end - layerrecords[i] = { - slot = slot, - class = class, + end + fontdata.sbixshapes = shapes + -- else + -- for i=1,nofstrikes do + -- local strikeoffset = strikes[i] + tableoffset + -- setposition(f,strikeoffset) + -- local glyphs = { } + -- strikes[i] = { + -- ppem = readushort(f), + -- ppi = readushort(f), + -- glyphs = glyphs, + -- } + -- for i=0,nofglyphs do + -- glyphs[i] = readulong(f) + -- end + -- local glyphoffset = glyphs[0] + -- for i=0,nofglyphs-1 do + -- local nextoffset = glyphs[i+1] + -- local datasize = nextoffset - glyphoffset + -- if datasize > 0 then + -- setposition(f,strikeoffset + glyphoffset) + -- glyphs[i] = { + -- x = readshort(f), + -- y = readshort(f), + -- tag = readtag(f), + -- data = readstring(f,datasize-8) + -- } + -- glyphoffset = nextoffset + -- end + -- end + -- end + -- fontdata.sbixshapes = strikes + -- end + end +end + +-- function readers.cblc(f,fontdata,specification) +-- local tableoffset = gotodatatable(f,fontdata,"cblc",specification.glyphs) +-- if tableoffset then +-- end +-- end +-- +-- function readers.cbdt(f,fontdata,specification) +-- local tableoffset = gotodatatable(f,fontdata,"ctdt",specification.glyphs) +-- if tableoffset then +-- +-- local function getmetrics(f) +-- return { +-- ascender = readinteger(f), +-- descender = readinteger(f), +-- widthmax = readcardinal(f), +-- caretslopedumerator = readinteger(f), +-- caretslopedenominator = readinteger(f), +-- caretoffset = readinteger(f), +-- minorigin = readinteger(f), +-- minadvance = readinteger(f), +-- maxbefore = readinteger(f), +-- minafter = readinteger(f), +-- pad1 = readinteger(f), +-- pad2 = readinteger(f), +-- } +-- end +-- +-- local majorversion = readushort(f) +-- local minorversion = readushort(f) +-- local nofsizetables = readulong(f) +-- local sizetable = { } +-- for i=1,nofsizetables do +-- sizetable[i] = { +-- subtables = readulong(f), +-- indexsize = readulong(f), +-- nofsubtables = readulong(f), +-- colorref = readulong(f), +-- hormetrics = getmetrics(f), +-- vermetrics = getmetrics(f), +-- firstindex = readushort(f), +-- lastindex = readushort(f), +-- ppemx = readbyte(f), +-- ppemy = readbyte(f), +-- bitdepth = readbyte(f), +-- flags = readbyte(f), +-- } +-- end +-- +-- sort(sizetable,function(a,b) +-- if b.ppemx == a.ppemx then +-- return b.bitdepth < a.bitdepth +-- else +-- return b.ppemx < a.ppemx +-- end +-- end) +-- +-- local shapes = { } +-- +-- for i=1,nofsizetables do +-- local s = sizetables[i] +-- for j=firstindex,lastindex do +-- if not shapes[j] then +-- shapes[j] = { +-- i +-- } +-- end +-- end +-- end +-- +-- inspect(shapes) +-- +-- end +-- end + +-- function readers.ebdt(f,fontdata,specification) +-- if specification.glyphs then +-- end +-- end + +-- function readers.ebsc(f,fontdata,specification) +-- if specification.glyphs then +-- end +-- end + +-- function readers.eblc(f,fontdata,specification) +-- if specification.glyphs then +-- end +-- end + +-- + AVAR : optional +-- + CFF2 : otf outlines +-- - CVAR : ttf hinting, not needed +-- + FVAR : the variations +-- + GVAR : ttf outline changes +-- + HVAR : horizontal changes +-- + MVAR : metric changes +-- + STAT : relations within fonts +-- * VVAR : vertical changes +-- +-- * BASE : extra baseline adjustments +-- - GASP : not needed +-- + GDEF : not needed (carets) +-- + GPOS : adapted device tables (needed?) +-- + GSUB : new table +-- + NAME : 25 added + +function readers.stat(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"stat",true) -- specification.variable + if tableoffset then + local extras = fontdata.extras + local version = readulong(f) -- 0x00010000 + local axissize = readushort(f) + local nofaxis = readushort(f) + local axisoffset = readulong(f) + local nofvalues = readushort(f) + local valuesoffset = readulong(f) + local fallbackname = extras[readushort(f)] -- beta fonts mess up + local axis = { } + local values = { } + setposition(f,tableoffset+axisoffset) + for i=1,nofaxis do + local tag = readtag(f) + axis[i] = { + tag = tag, + name = lower(extras[readushort(f)] or tag), + ordering = readushort(f), -- maybe gaps + variants = { } + } + end + -- flags: + -- + -- 0x0001 : OlderSiblingFontAttribute + -- 0x0002 : ElidableAxisValueName + -- 0xFFFC : reservedFlags + -- + setposition(f,tableoffset+valuesoffset) + for i=1,nofvalues do + values[i] = readushort(f) + end + for i=1,nofvalues do + setposition(f,tableoffset + valuesoffset + values[i]) + local format = readushort(f) + local index = readushort(f) + 1 + local flags = readushort(f) + local name = lower(extras[readushort(f)] or "no name") + local value = readfixed(f) + local variant + if format == 1 then + variant = { + flags = flags, + name = name, + value = value, + } + elseif format == 2 then + variant = { + flags = flags, + name = name, + value = value, + minimum = readfixed(f), + maximum = readfixed(f), + } + elseif format == 3 then + variant = { + flags = flags, + name = name, + value = value, + link = readfixed(f), } 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] + insert(axis[index].variants,variant) + end + sort(axis,function(a,b) + return a.ordering < b.ordering + end) + for i=1,#axis do + local a = axis[i] + sort(a.variants,function(a,b) + return a.name < b.name + end) + a.ordering = nil + end + setvariabledata(fontdata,"designaxis",axis) + setvariabledata(fontdata,"fallbackname",fallbackname) + end +end + +-- The avar table is optional and used in combination with fvar. Given the +-- detailed explanation about bad values we expect the worst and do some +-- checking. + +function readers.avar(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"avar",true) -- specification.variable + if tableoffset then + + local function collect() + local nofvalues = readushort(f) + local values = { } + local lastfrom = false + local lastto = false + for i=1,nofvalues do + local f, t = read2dot14(f), read2dot14(f) + if lastfrom and f <= lastfrom then + -- ignore + elseif lastto and t >= lastto then + -- ignore + else + values[#values+1] = { f, t } + lastfrom, lastto = f, t + end + end + nofvalues = #values + if nofvalues > 2 then + local some = values[1] + if some[1] == -1 and some[2] == -1 then + some = values[nofvalues] + if some[1] == 1 and some[2] == 1 then + for i=2,nofvalues-1 do + some = values[i] + if some[1] == 0 and some[2] == 0 then + return values + end + end + end end - glyphs[glyphindex].colors = t end + return false end - fontdata.hascolor = true + + local majorversion = readushort(f) -- 1 + local minorversion = readushort(f) -- 0 + local reserved = readushort(f) + local nofaxis = readushort(f) + local segments = { } + for i=1,nofaxis do + segments[i] = collect() + end + setvariabledata(fontdata,"segments",segments) 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 +function readers.fvar(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"fvar",true) -- specification.variable or specification.instancenames + if tableoffset then + local version = readulong(f) -- 1.0 + local offsettoaxis = tableoffset + readushort(f) + local reserved = skipshort(f) + -- pair 1 + local nofaxis = readushort(f) + local sizeofaxis = readushort(f) + -- pair 2 + local nofinstances = readushort(f) + local sizeofinstances = readushort(f) + -- + local extras = fontdata.extras + local axis = { } + local instances = { } + -- + setposition(f,offsettoaxis) + -- + for i=1,nofaxis do + axis[i] = { + tag = readtag(f), -- ital opsz slnt wdth wght + minimum = readfixed(f), + default = readfixed(f), + maximum = readfixed(f), + flags = readushort(f), + name = lower(extras[readushort(f)] or "bad name"), + } + local n = sizeofaxis - 20 + if n > 0 then + skipbytes(f,n) + elseif n < 0 then + -- error 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 - -- used for guis - 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 + -- + local nofbytes = 2 + 2 + 2 + nofaxis * 4 + local readpsname = nofbytes <= sizeofinstances + local skippable = sizeofinstances - nofbytes + for i=1,nofinstances do + local subfamid = readushort(f) + local flags = readushort(f) -- 0, not used yet + local values = { } + for i=1,nofaxis do + values[i] = { + axis = axis[i].tag, + value = readfixed(f), } 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 + local psnameid = readpsname and readushort(f) or 0xFFFF + if subfamid == 2 or subfamid == 17 then + -- okay + elseif subfamid == 0xFFFF then + subfamid = nil + elseif subfamid <= 256 or subfamid >= 32768 then + subfamid = nil -- actually an error + end + if psnameid == 6 then + -- okay + elseif psnameid == 0xFFFF then + psnameid = nil + elseif psnameid <= 256 or psnameid >= 32768 then + psnameid = nil -- actually an error + end + instances[i] = { + -- flags = flags, + subfamily = extras[subfamid], + psname = psnameid and extras[psnameid] or nil, + values = values, + } + if skippable > 0 then + skipbytes(f,skippable) end - fontdata.colorpalettes = palettes end + setvariabledata(fontdata,"axis",axis) + setvariabledata(fontdata,"instances",instances) 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), - } +function readers.hvar(f,fontdata,specification) + local factors = specification.factors + if not factors then + return + end + local tableoffset = gotodatatable(f,fontdata,"hvar",specification.variable) + if not tableoffset then + return + end + + local version = readulong(f) -- 1.0 + local variationoffset = tableoffset + readulong(f) -- the store + local advanceoffset = tableoffset + readulong(f) + local lsboffset = tableoffset + readulong(f) + local rsboffset = tableoffset + readulong(f) + + local regions = { } + local variations = { } + local innerindex = { } -- size is mapcount + local outerindex = { } -- size is mapcount + + if variationoffset > 0 then + regions, deltas = readvariationdata(f,variationoffset,factors) + end + + if not regions then + -- for now .. what to do ? + return + end + + if advanceoffset > 0 then + -- + -- innerIndexBitCountMask = 0x000F + -- mapEntrySizeMask = 0x0030 + -- reservedFlags = 0xFFC0 + -- + -- outerIndex = entry >> ((entryFormat & innerIndexBitCountMask) + 1) + -- innerIndex = entry & ((1 << ((entryFormat & innerIndexBitCountMask) + 1)) - 1) + -- + setposition(f,advanceoffset) + local format = readushort(f) -- todo: check + local mapcount = readushort(f) + local entrysize = rshift(band(format,0x0030),4) + 1 + local nofinnerbits = band(format,0x000F) + 1 -- n of inner bits + local innermask = lshift(1,nofinnerbits) - 1 + local readcardinal = read_cardinal[entrysize] -- 1 upto 4 bytes + for i=0,mapcount-1 do + local mapdata = readcardinal(f) + outerindex[i] = rshift(mapdata,nofinnerbits) + innerindex[i] = band(mapdata,innermask) + end + -- use last entry when no match i + setvariabledata(fontdata,"hvarwidths",true) + local glyphs = fontdata.glyphs + for i=0,fontdata.nofglyphs-1 do + local glyph = glyphs[i] + local width = glyph.width + if width then + local outer = outerindex[i] or 0 + local inner = innerindex[i] or i + if outer and inner then -- not needed + local delta = deltas[outer+1] + if delta then + local d = delta.deltas[inner+1] + if d then + local scales = delta.scales + local deltaw = 0 + for i=1,#scales do + local di = d[i] + if di then + deltaw = deltaw + scales[i] * di + else + break -- can't happen + end + end +-- report("index: %i, outer: %i, inner: %i, deltas: %|t, scales: %|t, width: %i, delta %i", +-- i,outer,inner,d,scales,width,round(deltaw)) + glyph.width = width + round(deltaw) + end + end + end 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 + + end + + -- if lsboffset > 0 then + -- -- we don't use left side bearings + -- end + + -- if rsboffset > 0 then + -- -- we don't use right side bearings + -- end + + -- setvariabledata(fontdata,"hregions",regions) + +end + +function readers.vvar(f,fontdata,specification) + if not specification.variable then + return + end +end + +function readers.mvar(f,fontdata,specification) + local tableoffset = gotodatatable(f,fontdata,"mvar",specification.variable) + if tableoffset then + local version = readulong(f) -- 1.0 + local reserved = skipshort(f,1) + local recordsize = readushort(f) + local nofrecords = readushort(f) + local offsettostore = tableoffset + readushort(f) + local dimensions = { } + local factors = specification.factors + if factors then + local regions, deltas = readvariationdata(f,offsettostore,factors) + for i=1,nofrecords do + local tag = readtag(f) + local var = variabletags[tag] + if var then + local outer = readushort(f) + local inner = readushort(f) + local delta = deltas[outer+1] + if delta then + local d = delta.deltas[inner+1] + if d then + local scales = delta.scales + local dd = 0 + for i=1,#scales do + dd = dd + scales[i] * d[i] + end + var(fontdata,round(dd)) + end + end + else + skipshort(f,2) + end + if recordsize > 8 then -- 4 + 2 + 2 + skipbytes(recordsize-8) + end end - fontdata.svgshapes = entries end - fontdata.hascolor = true + -- setvariabledata(fontdata,"mregions",regions) end end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-gbn.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-gbn.lua deleted file mode 100644 index 21c6061542d..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-gbn.lua +++ /dev/null @@ -1,251 +0,0 @@ -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" -} - --- generic [base|node] mode handler - -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 -- context abstraction of direct nodes - -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 - --- from now on we apply ligaturing and kerning here because it might interfere with complex --- opentype discretionary handling where the base ligature pass expect some weird extra --- pointers (which then confuse the tail slider that has some checking built in) - -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 -- we need to check shared, only when same features - if shared then - local processors = shared.processes - if processors and #processors > 0 then - usedfonts[font] = processors - 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 - -- per generic user request we always remove selectors - 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 -- we need to check shared, only when same features - if shared then - local processors = shared.processes - if processors and #processors > 0 then - usedfonts[font] = processors - 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 -- safeguard - 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 diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-effects.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-effects.lua new file mode 100644 index 00000000000..578c8bc1027 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-effects.lua @@ -0,0 +1,414 @@ +if not modules then modules = { } end modules ['font-imp-effects'] = { + version = 1.001, + comment = "companion to font-ini.mkiv and hand-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- todo: pickup from goodies: if type(effect) then ... + +local next, type, tonumber = next, type, tonumber +local is_boolean = string.is_boolean + +local fonts = fonts + +local handlers = fonts.handlers +local registerotffeature = handlers.otf.features.register +local registerafmfeature = handlers.afm.features.register + +local settings_to_hash = utilities.parsers.settings_to_hash_colon_too + +local helpers = fonts.helpers +local prependcommands = helpers.prependcommands +local charcommand = helpers.commands.char +local leftcommand = helpers.commands.left +local rightcommand = helpers.commands.right +local upcommand = helpers.commands.up +local downcommand = helpers.commands.down +local dummycommand = helpers.commands.dummy + +----- constructors = fonts.constructors +----- getmathparameter = constructors.getmathparameter +----- setmathparameter = constructors.setmathparameter + +local report_effect = logs.reporter("fonts","effect") +local report_slant = logs.reporter("fonts","slant") +local report_extend = logs.reporter("fonts","extend") +local report_squeeze = logs.reporter("fonts","squeeze") + +local trace = false + +trackers.register("fonts.effect", function(v) trace = v end) +trackers.register("fonts.slant", function(v) trace = v end) +trackers.register("fonts.extend", function(v) trace = v end) +trackers.register("fonts.squeeze",function(v) trace = v end) + +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 + if trace then + report_slant("applying %0.3f",value) + end + tfmdata.parameters.slantfactor = value +end + +local specification = { + name = "slant", + description = "slant glyphs", + initializers = { + base = initializeslant, + node = initializeslant, + } +} + +registerotffeature(specification) +registerafmfeature(specification) + +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 + if trace then + report_extend("applying %0.3f",value) + end + tfmdata.parameters.extendfactor = value +end + +local specification = { + name = "extend", + description = "scale glyphs horizontally", + initializers = { + base = initializeextend, + node = initializeextend, + } +} + +registerotffeature(specification) +registerafmfeature(specification) + +local function initializesqueeze(tfmdata,value) + value = tonumber(value) + if not value then + value = 0 + elseif value > 10 then + value = 10 + elseif value < -10 then + value = -10 + end + if trace then + report_squeeze("applying %0.3f",value) + end + tfmdata.parameters.squeezefactor = value +end + +local specification = { + name = "squeeze", + description = "scale glyphs vertically", + initializers = { + base = initializesqueeze, + node = initializesqueeze, + } +} + +registerotffeature(specification) +registerafmfeature(specification) + +local effects = { + inner = 0, + normal = 0, + outer = 1, + outline = 1, + both = 2, + hidden = 3, +} + +local function initializeeffect(tfmdata,value) + local spec + if type(value) == "number" then + spec = { width = value } + else + spec = settings_to_hash(value) + end + local effect = spec.effect or "both" + local width = tonumber(spec.width) or 0 + local mode = effects[effect] + if not mode then + report_effect("invalid effect %a",effect) + elseif width == 0 and mode == 0 then + report_effect("invalid width %a for effect %a",width,effect) + else + local parameters = tfmdata.parameters + local properties = tfmdata.properties + parameters.mode = mode + parameters.width = width * 1000 + if is_boolean(spec.auto) == true then + local squeeze = 1 - width/20 + local average = (1 - squeeze) * width * 100 + spec.squeeze = squeeze + spec.extend = 1 + width/2 + spec.wdelta = average + spec.hdelta = average/2 + spec.ddelta = average/2 + spec.vshift = average/2 + end + local factor = tonumber(spec.factor) or 0 + local hfactor = tonumber(spec.hfactor) or factor + local vfactor = tonumber(spec.vfactor) or factor + local delta = tonumber(spec.delta) or 1 + local wdelta = tonumber(spec.wdelta) or delta + local hdelta = tonumber(spec.hdelta) or delta + local ddelta = tonumber(spec.ddelta) or hdelta + local vshift = tonumber(spec.vshift) or 0 + local slant = spec.slant + local extend = spec.extend + local squeeze = spec.squeeze + if slant then + initializeslant(tfmdata,slant) + end + if extend then + initializeextend(tfmdata,extend) + end + if squeeze then + initializesqueeze(tfmdata,squeeze) + end + properties.effect = { + effect = effect, + width = width, + factor = factor, + hfactor = hfactor, + vfactor = vfactor, + wdelta = wdelta, + hdelta = hdelta, + ddelta = ddelta, + vshift = vshift, + slant = tfmdata.parameters.slantfactor, + extend = tfmdata.parameters.extendfactor, + squeeze = tfmdata.parameters.squeezefactor, + } + end +end + +local rules = { + "RadicalRuleThickness", + "OverbarRuleThickness", + "FractionRuleThickness", + "UnderbarRuleThickness", +} + +-- local commands = char.commands +-- if commands then +-- local command = commands[1] +-- if command and command[1] == "right" then +-- commands[1] = rightcommand[command[2]-snap] +-- end +-- end + +local function setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze) + if delta ~= 0 then + for i=1,#rules do + local name = rules[i] + local value = mathparameters[name] + if value then + mathparameters[name] = (squeeze or 1) * (value + dx) + end + end + end +end + +local function setmathcharacters(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta) + + local function wdpatch(char) + if wsnap ~= 0 then + char.width = char.width + wdelta/2 + end + end + + local function htpatch(char) + if hsnap ~= 0 then + local height = char.height + if height then + char.height = char.height + 2 * dy + end + end + end + + local character = characters[0x221A] + + if character and character.next then +-- print("base char",0x221A,table.sequenced(character)) + local char = character + local next = character.next + wdpatch(char) + htpatch(char) + while next do + char = characters[next] + wdpatch(char) + htpatch(char) +-- print("next char",next,table.sequenced(char)) + next = char.next + end + if char then + local v = char.vert_variants + if v then + local top = v[#v] + if top then + local char = characters[top.glyph] +-- print("top char",top.glyph,table.sequenced(char)) + htpatch(char) + end + end + end + end +end + +-- local show_effect = { "lua", function(f,c) +-- report_effect("font id %i, char %C",f,c) +-- inspect(fonts.hashes.characters[f][c]) +-- end } + +-- local show_effect = { "lua", "print('!')" } + +local function manipulateeffect(tfmdata) + local effect = tfmdata.properties.effect + if effect then + local characters = tfmdata.characters + local parameters = tfmdata.parameters + local mathparameters = tfmdata.mathparameters + local multiplier = effect.width * 100 + local factor = parameters.factor + local hfactor = parameters.hfactor + local vfactor = parameters.vfactor + local wdelta = effect.wdelta * hfactor * multiplier + local hdelta = effect.hdelta * vfactor * multiplier + local ddelta = effect.ddelta * vfactor * multiplier + local vshift = effect.vshift * vfactor * multiplier + local squeeze = effect.squeeze + local hshift = wdelta / 2 + local dx = multiplier * vfactor + local dy = vshift + local factor = (1 + effect.factor) * factor + local hfactor = (1 + effect.hfactor) * hfactor + local vfactor = (1 + effect.vfactor) * vfactor + local vshift = vshift ~= 0 and upcommand[vshift] or false + for unicode, character in next, characters do + local oldwidth = character.width + local oldheight = character.height + local olddepth = character.depth + if oldwidth and oldwidth > 0 then + character.width = oldwidth + wdelta + local commands = character.commands + local hshift = rightcommand[hshift] + if vshift then + if commands then + prependcommands ( commands, +-- show_effect, + hshift, + vshift + ) + else + character.commands = { +-- show_effect, + hshift, + vshift, + charcommand[unicode] + } + end + else + if commands then + prependcommands ( commands, +-- show_effect, + hshift + ) + else + character.commands = { +-- show_effect, + hshift, + charcommand[unicode] + } + end + end + end + if oldheight and oldheight > 0 then + character.height = oldheight + hdelta + end + if olddepth and olddepth > 0 then + character.depth = olddepth + ddelta + end + end + if mathparameters then + setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze) + setmathcharacters(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta) + end + parameters.factor = factor + parameters.hfactor = hfactor + parameters.vfactor = vfactor + if trace then + report_effect("applying") + report_effect(" effect : %s", effect.effect) + report_effect(" width : %s => %s", effect.width, multiplier) + report_effect(" factor : %s => %s", effect.factor, factor ) + report_effect(" hfactor : %s => %s", effect.hfactor,hfactor) + report_effect(" vfactor : %s => %s", effect.vfactor,vfactor) + report_effect(" wdelta : %s => %s", effect.wdelta, wdelta) + report_effect(" hdelta : %s => %s", effect.hdelta, hdelta) + report_effect(" ddelta : %s => %s", effect.ddelta, ddelta) + end + end +end + +local specification = { + name = "effect", + description = "apply effects to glyphs", + initializers = { + base = initializeeffect, + node = initializeeffect, + }, + manipulators = { + base = manipulateeffect, + node = manipulateeffect, + }, +} + +registerotffeature(specification) +registerafmfeature(specification) + +local function initializeoutline(tfmdata,value) + value = tonumber(value) + if not value then + value = 0 + else + value = tonumber(value) or 0 + end + local parameters = tfmdata.parameters + local properties = tfmdata.properties + parameters.mode = effects.outline + parameters.width = value * 1000 + properties.effect = { + effect = effect, + width = width, + } +end + +local specification = { + name = "outline", + description = "outline glyphs", + initializers = { + base = initializeoutline, + node = initializeoutline, + } +} + +registerotffeature(specification) +registerafmfeature(specification) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-italics.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-italics.lua new file mode 100644 index 00000000000..83c785d3876 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-italics.lua @@ -0,0 +1,147 @@ +if not modules then modules = { } end modules ['font-imp-italics'] = { + version = 1.001, + comment = "companion to font-ini.mkiv and hand-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local next = next + +local fonts = fonts +local handlers = fonts.handlers +local registerotffeature = handlers.otf.features.register +local registerafmfeature = handlers.afm.features.register + +local function initialize(tfmdata,key,value) + for unicode, character in next, tfmdata.characters do + local olditalic = character.italic + if olditalic and olditalic ~= 0 then + character.width = character.width + olditalic + character.italic = 0 + end + end +end + +local specification = { + name = "italicwidths", + description = "add italic to width", + manipulators = { + base = initialize, + node = initialize, -- only makes sense for math + } +} + +registerotffeature(specification) +registerafmfeature(specification) + +local function initialize(tfmdata,value) -- hm, always value + if value then + -- the magic 40 and it formula come from Dohyun Kim but we might need another guess + 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 + +local specification = { + name = "itlc", + description = "italic correction", + initializers = { + base = initialize, + node = initialize, + } +} + +registerotffeature(specification) +registerafmfeature(specification) + +if context then + + local function initialize(tfmdata,value) -- yes no delay + tfmdata.properties.textitalics = toboolean(value) + end + + local specification = { + name = "textitalics", + description = "use alternative text italic correction", + initializers = { + base = initialize, + node = initialize, + } + } + + registerotffeature(specification) + registerafmfeature(specification) + +end + +-- no longer used + +-- if context then +-- +-- -- local function initializemathitalics(tfmdata,value) -- yes no delay +-- -- tfmdata.properties.mathitalics = toboolean(value) +-- -- end +-- -- +-- -- local specification = { +-- -- name = "mathitalics", +-- -- description = "use alternative math italic correction", +-- -- initializers = { +-- -- base = initializemathitalics, +-- -- node = initializemathitalics, +-- -- } +-- -- } +-- -- +-- -- registerotffeature(specification) +-- -- registerafmfeature(specification) +-- +-- end + +-- -- also not used, only when testing + +if context then + + local letter = characters.is_letter + local always = true + + local function collapseitalics(tfmdata,key,value) + local threshold = value == true and 100 or tonumber(value) + if threshold and threshold > 0 then + if threshold > 100 then + threshold = 100 + end + for unicode, data in next, tfmdata.characters do + if always or letter[unicode] or letter[data.unicode] then + local italic = data.italic + if italic and italic ~= 0 then + local width = data.width + if width and width ~= 0 then + local delta = threshold * italic / 100 + data.width = width + delta + data.italic = italic - delta + end + end + end + end + end + end + + local dimensions_specification = { + name = "collapseitalics", + description = "collapse italics", + manipulators = { + base = collapseitalics, + node = collapseitalics, + } + } + + registerotffeature(dimensions_specification) + registerafmfeature(dimensions_specification) + +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-ligatures.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-ligatures.lua new file mode 100644 index 00000000000..091eb5d4b45 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-ligatures.lua @@ -0,0 +1,136 @@ +if not modules then modules = { } end modules ['font-imp-ligatures'] = { + 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 lpegmatch = lpeg.match +local utfsplit = utf.split +local settings_to_array = utilities.parsers.settings_to_array + +local fonts = fonts +local otf = fonts.handlers.otf +local registerotffeature = otf.features.register +local addotffeature = otf.addfeature + +-- This is a quick and dirty hack. + +local lookups = { } +local protect = { } +local revert = { } +local zwjchar = 0x200C +local zwj = { zwjchar } + +addotffeature { + name = "blockligatures", + type = "chainsubstitution", + nocheck = true, -- because there is no 0x200C in the font + prepend = true, -- make sure we do it early + future = true, -- avoid nilling due to no steps yet + lookups = { + { + type = "multiple", + data = lookups, + }, + }, + data = { + rules = protect, + } +} + +addotffeature { + name = "blockligatures", + type = "chainsubstitution", + nocheck = true, -- because there is no 0x200C in the font + append = true, -- this is done late + overload = false, -- we don't want to overload the previous definition + lookups = { + { + type = "ligature", + data = lookups, + }, + }, + data = { + rules = revert, + } +} + +registerotffeature { + name = 'blockligatures', + description = 'block certain ligatures', +} + +local splitter = lpeg.splitat(":") + +local function blockligatures(str) + + local t = settings_to_array(str) + + for i=1,#t do + local ti = t[i] + local before, current, after = lpegmatch(splitter,ti) + if current and after then -- before is returned when no match + -- experimental joke + if before then + before = utfsplit(before) + for i=1,#before do + before[i] = { before[i] } + end + end + if current then + current = utfsplit(current) + end + if after then + after = utfsplit(after) + for i=1,#after do + after[i] = { after[i] } + end + end + else + before = nil + current = utfsplit(ti) + after = nil + end + if #current > 1 then + local one = current[1] + local two = current[2] + lookups[one] = { one, zwjchar } + local one = { one } + local two = { two } + local new = #protect + 1 + protect[new] = { + before = before, + current = { one, two }, + after = after, + lookups = { 1 }, -- not shared ! + } + revert[new] = { + -- before = before, + current = { one, zwj }, + -- after = { two, unpack(after) }, + after = { two }, + lookups = { 1 }, -- not shared ! + } + end + end +end + +-- blockligatures("\0\0") + +otf.helpers.blockligatures = blockligatures + +-- blockligatures("fi,ff") +-- blockligatures("fl") +-- blockligatures("u:fl:age") + +if context then + + interfaces.implement { + name = "blockligatures", + arguments = "string", + actions = blockligatures, + } + +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-tex.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-tex.lua new file mode 100644 index 00000000000..b4b9a7b6943 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-tex.lua @@ -0,0 +1,144 @@ +if not modules then modules = { } end modules ['font-imp-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 next = next + +local fonts = fonts +local otf = fonts.handlers.otf +local registerotffeature = otf.features.register +local addotffeature = otf.addfeature + +-- tlig (we need numbers for some fonts so ...) + +local specification = { + type = "ligature", + order = { "tlig" }, + prepend = true, + data = { + -- endash = "hyphen hyphen", + -- emdash = "hyphen hyphen hyphen", + [0x2013] = { 0x002D, 0x002D }, + [0x2014] = { 0x002D, 0x002D, 0x002D }, + -- quotedblleft = "quoteleft quoteleft", + -- quotedblright = "quoteright quoteright", + -- quotedblleft = "grave grave", + -- quotedblright = "quotesingle quotesingle", + -- quotedblbase = "comma comma", + }, +} + +addotffeature("tlig",specification) + +registerotffeature { + -- this makes it a known feature (in tables) + name = "tlig", + description = "tex ligatures", +} + +-- trep + +local specification = { + type = "substitution", + order = { "trep" }, + prepend = true, + data = { + -- [0x0022] = 0x201D, + [0x0027] = 0x2019, + -- [0x0060] = 0x2018, + }, +} + +addotffeature("trep",specification) + +registerotffeature { + -- this makes it a known feature (in tables) + name = "trep", + description = "tex replacements", +} + +-- some day this will be moved to font-imp-scripts.lua + +-- anum + +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 specification = { + { + type = "substitution", + features = { arab = { urd = true, dflt = true } }, + order = { "anum" }, + data = anum_arabic, + valid = valid, + }, + { + type = "substitution", + features = { arab = { urd = true } }, + order = { "anum" }, + data = anum_persian, + valid = valid, + }, +} + +addotffeature("anum",specification) + +registerotffeature { + -- this makes it a known feature (in tables) + name = "anum", + description = "arabic digits", +} + +-- -- example: +-- +-- fonts.handlers.otf.addfeature { +-- name = "hangulfix", +-- type = "substitution", +-- features = { ["hang"] = { ["*"] = true } }, +-- data = { +-- [0x1160] = 0x119E, +-- }, +-- order = { "hangulfix" }, +-- flags = { }, +-- prepend = true, +-- }) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ini.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ini.lua index abc319484d2..3d5dd27a2d5 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ini.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ini.lua @@ -10,21 +10,22 @@ if not modules then modules = { } end modules ['font-ini'] = {

Not much is happening here.

--ldx]]-- -local allocate = utilities.storage.allocate +local allocate = utilities.storage.allocate -fonts = fonts or { } -local fonts = fonts +fonts = fonts or { } +local fonts = fonts -fonts.hashes = { identifiers = allocate() } +fonts.hashes = fonts.hashes or { identifiers = allocate() } +fonts.tables = fonts.tables or { } +fonts.helpers = fonts.helpers or { } +fonts.tracers = fonts.tracers or { } -- for the moment till we have move to moduledata +fonts.specifiers = fonts.specifiers or { } -- in format ! -fonts.tables = fonts.tables or { } -fonts.helpers = fonts.helpers or { } -fonts.tracers = fonts.tracers or { } -- for the moment till we have move to moduledata -fonts.specifiers = fonts.specifiers or { } -- in format ! +fonts.analyzers = { } -- not needed here +fonts.readers = { } +fonts.definers = { methods = { } } +fonts.loggers = { register = function() end } -fonts.analyzers = { } -- not needed here -fonts.readers = { } -fonts.definers = { methods = { } } -fonts.loggers = { register = function() end } - -fontloader.totable = fontloader.to_table -- not used +if context then + fontloader = nil +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-map.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-map.lua index e2254f8ca0e..a7fbfe49e89 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-map.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-map.lua @@ -10,15 +10,17 @@ 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 rshift = bit32.rshift 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") -- not otf only +-- force_ligatures was true for a while so that these emoji's with bad names work too + local force_ligatures = false directives.register("fonts.mapping.forceligatures",function(v) force_ligatures = v end) local fonts = fonts or { } @@ -76,6 +78,9 @@ end local f_single = formatters["%04X"] local f_double = formatters["%04X%04X"] +-- floor(x/256) => rshift(x, 8) +-- floor(x/1024) => rshift(x,10) + -- 0.684 0.661 0,672 0.650 : cache at lua end (more mem) -- 0.682 0,672 0.698 0.657 : no cache (moderate mem i.e. lua strings) -- 0.644 0.647 0.655 0.645 : convert in c (less mem in theory) @@ -86,7 +91,7 @@ local f_double = formatters["%04X%04X"] -- s = f_single(unicode) -- else -- unicode = unicode - 0x10000 --- s = f_double(floor(unicode/1024)+0xD800,unicode%1024+0xDC00) +-- s = f_double(rshift(unicode,10)+0xD800,unicode%1024+0xDC00) -- end -- t[unicode] = s -- return s @@ -145,7 +150,7 @@ local function tounicode16(unicode) return f_single(unicode) else unicode = unicode - 0x10000 - return f_double(floor(unicode/1024)+0xD800,unicode%1024+0xDC00) + return f_double(rshift(unicode,10)+0xD800,unicode%1024+0xDC00) end end @@ -157,33 +162,122 @@ local function tounicode16sequence(unicodes) t[l] = f_single(u) else u = u - 0x10000 - t[l] = f_double(floor(u/1024)+0xD800,u%1024+0xDC00) + t[l] = f_double(rshift(u,10)+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 +-- local function tounicode(unicode) +-- 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(rshift(u,10)+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(rshift(unicode,10)+0xD800,unicode%1024+0xDC00) +-- end +-- end +-- end + +local unknown = f_single(0xFFFD) + +-- local function tounicode(unicode) +-- if type(unicode) == "table" then +-- local t = { } +-- for l=1,#unicode do +-- t[l] = tounicode(unicode[l]) +-- end +-- return concat(t) +-- elseif unicode >= 0x00E000 and unicode <= 0x00F8FF then +-- return unknown +-- elseif unicode >= 0x0F0000 and unicode <= 0x0FFFFF then +-- return unknown +-- elseif unicode >= 0x100000 and unicode <= 0x10FFFF then +-- return unknown +-- elseif unicode < 0xD7FF or (unicode > 0xDFFF and unicode <= 0xFFFF) then +-- return f_single(unicode) +-- else +-- unicode = unicode - 0x10000 +-- return f_double(rshift(unicode,10)+0xD800,unicode%1024+0xDC00) +-- end +-- end + +-- local hash = table.setmetatableindex(function(t,k) +-- local v +-- if k >= 0x00E000 and k <= 0x00F8FF then +-- v = unknown +-- elseif k >= 0x0F0000 and k <= 0x0FFFFF then +-- v = unknown +-- elseif k >= 0x100000 and k <= 0x10FFFF then +-- v = unknown +-- elseif k < 0xD7FF or (k > 0xDFFF and k <= 0xFFFF) then +-- v = f_single(k) +-- else +-- k = k - 0x10000 +-- v = f_double(rshift(k,10)+0xD800,k%1024+0xDC00) +-- end +-- t[k] = v +-- return v +-- end) +-- +-- table.makeweak(hash) +-- +-- local function tounicode(unicode) +-- if type(unicode) == "table" then +-- local t = { } +-- for l=1,#unicode do +-- t[l] = hash[unicode[l]] +-- end +-- return concat(t) +-- else +-- return hash[unicode] +-- end +-- end + +local hash = { } +local conc = { } + +-- table.makeweak(hash) + +table.setmetatableindex(hash,function(t,k) + if type(k) == "table" then + local n = #k + for l=1,n do + conc[l] = hash[k[l]] end - return concat(t) + return concat(conc,"",1,n) + end + local v + if k >= 0x00E000 and k <= 0x00F8FF then + v = unknown + elseif k >= 0x0F0000 and k <= 0x0FFFFF then + v = unknown + elseif k >= 0x100000 and k <= 0x10FFFF then + v = unknown + elseif k < 0xD7FF or (k > 0xDFFF and k <= 0xFFFF) then + v = f_single(k) 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 + k = k - 0x10000 + v = f_double(rshift(k,10)+0xD800,k%1024+0xDC00) end + t[k] = v + return v +end) + +local function tounicode(unicode) + return hash[unicode] end local function fromunicode16(str) @@ -216,6 +310,8 @@ mappings.tounicode16 = tounicode16 mappings.tounicode16sequence = tounicode16sequence mappings.fromunicode16 = fromunicode16 +-- mozilla emoji has bad lig names: name = gsub(name,"(u[a-f0-9_]+)%-([a-f0-9_]+)","%1_%2") + local ligseparator = P("_") local varseparator = P(".") local namesplitter = Ct(C((1 - ligseparator - varseparator)^1) * (ligseparator * C((1 - ligseparator - varseparator)^1))^0) @@ -239,7 +335,7 @@ local namesplitter = Ct(C((1 - ligseparator - varseparator)^1) * (ligseparator * do - local overloads = allocate { + local overloads = { 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 }, @@ -249,9 +345,12 @@ do 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 } }, + + -- endash = { name = "endash", unicode = 0x2013, mess = 0x2013 }, + -- emdash = { name = "emdash", unicode = 0x2014, mess = 0x2014 }, } - local o = { } + local o = allocate { } for k, v in next, overloads do local name = v.name @@ -269,7 +368,7 @@ do end -function mappings.addtounicode(data,filename,checklookups) +function mappings.addtounicode(data,filename,checklookups,forceligatures) local resources = data.resources local unicodes = resources.unicodes if not unicodes then @@ -464,41 +563,54 @@ function mappings.addtounicode(data,filename,checklookups) checklookups(data,missing,nofmissing) end - -- todo: go lowercase - - local collected = false local unicoded = 0 - -- for du, glyph in next, descriptions do - 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 + local collected = fonts.handlers.otf.readers.getcomponents(data) -- neglectable overhead + + local function resolve(glyph,u) + local n = #u + for i=1,n do + if u[i] > private then + n = 0 + break end - local u = collected[du] -- always tables - 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 + + if not collected then + -- move on + elseif forceligatures or force_ligatures then + for i=1,#dlist do + local du = dlist[i] + if du >= private or (du >= 0xE000 and du <= 0xF8FF) then + local u = collected[du] -- always tables + if u then + resolve(descriptions[du],u) end - if n > 0 then - if n > 1 then - glyph.unicode = u - else - glyph.unicode = u[1] + end + end + else + for i=1,#dlist do + local du = dlist[i] + if du >= private or (du >= 0xE000 and du <= 0xF8FF) then + local glyph = descriptions[du] + if glyph.class == "ligature" and not glyph.unicode then + local u = collected[du] -- always tables + if u then + resolve(glyph,u) 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 diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ocl.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ocl.lua index 65a1c63427c..f97487ecd42 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ocl.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ocl.lua @@ -1,6 +1,6 @@ if not modules then modules = { } end modules ['font-ocl'] = { version = 1.001, - comment = "companion to font-otf.lua (context)", + 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" @@ -8,17 +8,25 @@ if not modules then modules = { } end modules ['font-ocl'] = { -- todo : user list of colors -local tostring, next, format = tostring, next, string.format +local tostring, tonumber, next = tostring, tonumber, next local round, max = math.round, math.round +local sortedkeys, sortedhash = table.sortedkeys, table.sortedhash +local setmetatableindex = table.setmetatableindex -local formatters = string.formatters -local tounicode = fonts.mappings.tounicode +local formatters = string.formatters +local tounicode = fonts.mappings.tounicode -local otf = fonts.handlers.otf +local helpers = fonts.helpers -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" +local charcommand = helpers.commands.char +local rightcommand = helpers.commands.right +local leftcommand = helpers.commands.left +local downcommand = helpers.commands.down + +local otf = fonts.handlers.otf + +local f_color = formatters["%.3f %.3f %.3f rg"] +local f_gray = formatters["%.3f g"] if context then @@ -39,7 +47,7 @@ else function otf.getactualtext(s) return - "/Span << /ActualText >> BDC", + "/Span << /ActualText >> BDC", "EMC" end @@ -47,32 +55,40 @@ end local sharedpalettes = { } +local hash = setmetatableindex(function(t,k) + local v = { "pdf", "direct", k } + t[k] = v + return v +end) + if context then - local graytorgb = attributes.colors.graytorgb - local cmyktorgb = attributes.colors.cmyktorgb + local colors = attributes.list[attributes.private('color')] or { } + local transparencies = attributes.list[attributes.private('transparency')] or { } 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) + local c = nil + local t = nil + if type(v) == "table" then + c = colors.register(name,"rgb", + max(round((v.r or 0)*255),255)/255, + max(round((v.g or 0)*255),255)/255, + max(round((v.b or 0)*255),255)/255 + ) 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 + c = colors[v] + t = transparencies[v] + end + if c and t then + values[i] = hash[lpdf.color(1,c) .. " " .. lpdf.transparency(t)] + elseif c then + values[i] = hash[lpdf.color(1,c)] + elseif t then + values[i] = hash[lpdf.color(1,t)] 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 @@ -82,23 +98,138 @@ else -- for generic 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) - } + values[i] = hash[f_color( + max(round((v.r or 0)*255),255)/255, + max(round((v.g or 0)*255),255)/255, + max(round((v.b or 0)*255),255)/255 + )] end end end -local function initializecolr(tfmdata,kind,value) -- hm, always value +-- We need to force page first because otherwise the q's get outside the font switch and +-- as a consequence the next character has no font set (well, it has: the preceding one). As +-- a consequence these fonts are somewhat inefficient as each glyph gets the font set. It's +-- a side effect of the fact that a font is handled when a character gets flushed. Okay, from +-- now on we can use text as literal mode. + +local function convert(t,k) + local v = { } + for i=1,#k do + local p = k[i] + local r, g, b = p[1], p[2], p[3] + if r == g and g == b then + v[i] = hash[f_gray(r/255)] + else + v[i] = hash[f_color(r/255,g/255,b/255)] + end + end + t[k] = v + return v +end + +local start = { "pdf", "mode", "font" } -- force text mode (so get q Q right) +----- stop = { "pdf", "mode", "page" } -- force page mode (else overlap) +local push = { "pdf", "page", "q" } +local pop = { "pdf", "page", "Q" } + +if not LUATEXFUNCTIONALITY or LUATEXFUNCTIONALITY < 6472 then -- for outside context (till tl 2019) + start = { "nop" } + ----- = stop +end + +-- -- This one results in color directives inside BT ET but has less q Q pairs. It +-- -- only shows the first glyph in acrobat and nothing more. No problem with other +-- -- renderers. +-- +-- local function initialize(tfmdata,kind,value) -- hm, always value +-- if value then +-- local resources = tfmdata.resources +-- local palettes = resources.colorpalettes +-- if palettes then +-- -- +-- local converted = resources.converted +-- if not converted then +-- converted = setmetatableindex(convert) +-- resources.converted = converted +-- end +-- local colorvalues = sharedpalettes[value] or converted[palettes[tonumber(value) or 1] or palettes[1]] or { } +-- local classes = #colorvalues +-- if classes == 0 then +-- return +-- end +-- -- +-- local characters = tfmdata.characters +-- local descriptions = tfmdata.descriptions +-- local properties = tfmdata.properties +-- -- +-- properties.virtualized = true +-- tfmdata.fonts = { +-- { id = 0 } +-- } +-- -- +-- local getactualtext = otf.getactualtext +-- local default = colorvalues[#colorvalues] +-- local b, e = getactualtext(tounicode(0xFFFD)) +-- local actualb = { "pdf", "page", b } -- saves tables +-- local actuale = { "pdf", "page", e } -- saves tables +-- -- +-- for unicode, character in next, characters do +-- local description = descriptions[unicode] +-- if description then +-- local colorlist = description.colors +-- if colorlist then +-- local u = description.unicode or characters[unicode].unicode +-- local w = character.width or 0 +-- local s = #colorlist +-- local goback = w ~= 0 and leftcommand[w] or nil -- needs checking: are widths the same +-- local t = { +-- start, +-- not u and actualb or { "pdf", "page", (getactualtext(tounicode(u))) } +-- } +-- local n = 2 +-- local l = nil +-- n = n + 1 t[n] = push +-- for i=1,s do +-- local entry = colorlist[i] +-- local v = colorvalues[entry.class] or default +-- if v and l ~= v then +-- n = n + 1 t[n] = v +-- l = v +-- end +-- n = n + 1 t[n] = charcommand[entry.slot] +-- if s > 1 and i < s and goback then +-- n = n + 1 t[n] = goback +-- end +-- end +-- n = n + 1 t[n] = pop +-- n = n + 1 t[n] = actuale +-- n = n + 1 t[n] = stop +-- character.commands = t +-- end +-- end +-- end +-- end +-- end +-- end +-- +-- -- Here we have no color change in BT .. ET and more q Q pairs but even then acrobat +-- -- fails displaying the overlays correctly. Other renderers do it right. + +local function initialize(tfmdata,kind,value) -- hm, always value if value then - local palettes = tfmdata.resources.colorpalettes + local resources = tfmdata.resources + local palettes = resources.colorpalettes if palettes then -- - local palette = sharedpalettes[value] or palettes[tonumber(value) or 1] or palettes[1] or { } - local classes = #palette + local converted = resources.converted + if not converted then + converted = setmetatableindex(convert) + resources.converted = converted + end + local colorvalues = sharedpalettes[value] or converted[palettes[tonumber(value) or 1] or palettes[1]] or { } + local classes = #colorvalues if classes == 0 then return end @@ -106,54 +237,56 @@ local function initializecolr(tfmdata,kind,value) -- hm, always value 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 + local default = colorvalues[#colorvalues] + local b, e = getactualtext(tounicode(0xFFFD)) + local actualb = { "pdf", "page", b } -- saves tables + local actuale = { "pdf", "page", e } -- saves tables -- 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 u = description.unicode or characters[unicode].unicode local w = character.width or 0 local s = #colorlist + local goback = w ~= 0 and leftcommand[w] or nil -- needs checking: are widths the same local t = { - -- We need to force page first because otherwise the q's get outside - -- the font switch and as a consequence the next character has no font - -- set (well, it has: the preceding one). As a consequence these fonts - -- are somewhat inefficient as each glyph gets the font set. It's a - -- side effect of the fact that a font is handled when a character gets - -- flushed. - { "special", "pdf:page:q" }, - { "special", "pdf:raw:" .. b } + start, -- really needed + not u and actualb or { "pdf", "page", (getactualtext(tounicode(u))) } } - local n = #t + local n = 2 + local l = nil + local f = false 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 } + local v = colorvalues[entry.class] or default + if v and l ~= v then + if f then + n = n + 1 t[n] = pop + end + n = n + 1 t[n] = push + f = true + n = n + 1 t[n] = v + l = v + end + n = n + 1 t[n] = charcommand[entry.slot] + if s > 1 and i < s and goback then + n = n + 1 t[n] = goback end end - n = n + 1 t[n] = { "special", "pdf:page:" .. e } - n = n + 1 t[n] = { "special", "pdf:raw:Q" } + if f then + n = n + 1 t[n] = pop + end + n = n + 1 t[n] = actuale + -- n = n + 1 t[n] = stop -- not needed character.commands = t end end @@ -166,19 +299,13 @@ fonts.handlers.otf.features.register { name = "colr", description = "color glyphs", manipulators = { - base = initializecolr, - node = initializecolr, + base = initialize, + node = initialize, } } -local otfsvg = otf.svg or { } -otf.svg = otfsvg -otf.svgenabled = true - do - local nofstreams = 0 - -- local f_setstream = formatters[ [[io.savedata("svg-glyph-%05i",%q)]] ] -- local f_getstream = formatters[ [[svg-glyph-%05i]] ] @@ -190,47 +317,152 @@ do -- end -- end - local f_name = formatters[ [[svg-glyph-%05i]] ] - local f_used = context and formatters[ [[original:///%s]] ] or formatters[ [[%s]] ] + local nofstreams = 0 + local f_name = formatters[ [[pdf-glyph-%05i]] ] + local f_used = context and formatters[ [[original:///%s]] ] or formatters[ [[%s]] ] + local hashed = { } + local cache = { } + + if epdf then + + local openpdf = epdf.openMemStream + + function otf.storepdfdata(pdf) + local done = hashed[pdf] + if not done then + nofstreams = nofstreams + 1 + local o, n = openpdf(pdf,#pdf,f_name(nofstreams)) + cache[n] = o -- we need to keep in mem + done = f_used(n) + hashed[pdf] = done + end + return nil, done, nil + end + + else - local cache = { } + local openpdf = pdfe.new + ----- prefix = "data:application/pdf," + + function otf.storepdfdata(pdf) + local done = hashed[pdf] + if not done then + nofstreams = nofstreams + 1 + local f = f_name(nofstreams) + local n = openpdf(pdf,#pdf,f) + done = f_used(n) + hashed[pdf] = done + end + return nil, done, nil + end - function otfsvg.storepdfdata(pdf) - nofstreams = nofstreams + 1 - local o, n = epdf.openMemStream(pdf,#pdf,f_name(nofstreams)) - cache[n] = o -- we need to keep in mem - 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 + -- maybe more efficient but much slower (and we hash already) + -- + -- if context then + -- + -- local storepdfdata = otf.storepdfdata + -- local initialized = false + -- + -- function otf.storepdfdata(pdf) + -- if not initialized then + -- if resolvers.setmemstream then + -- local f_setstream = formatters[ [[resolvers.setmemstream("pdf-glyph-%05i",%q,true)]] ] + -- local f_getstream = formatters[ [[memstream:///pdf-glyph-%05i]] ] + -- local f_nilstream = formatters[ [[resolvers.resetmemstream("pdf-glyph-%05i",true)]] ] + -- storepdfdata = function(pdf) + -- local done = hashed[pdf] + -- local set = nil + -- local reset = nil + -- if not done then + -- nofstreams = nofstreams + 1 + -- set = f_setstream(nofstreams,pdf) + -- done = f_getstream(nofstreams) + -- reset = f_nilstream(nofstreams) + -- hashed[pdf] = done + -- end + -- return set, done, reset + -- end + -- otf.storepdfdata = storepdfdata + -- end + -- initialized = true + -- end + -- return storepdfdata(pdf) + -- end + -- + -- end + +end + +local function pdftovirtual(tfmdata,pdfshapes,kind) -- kind = sbix|svg + if not tfmdata or not pdfshapes or not kind then + return + end + -- + local characters = tfmdata.characters + local properties = tfmdata.properties + local parameters = tfmdata.parameters + local hfactor = parameters.hfactor + -- + properties.virtualized = true + -- + tfmdata.fonts = { + { id = 0 } -- not really needed + } + -- + local getactualtext = otf.getactualtext + local storepdfdata = otf.storepdfdata + -- + local b, e = getactualtext(tounicode(0xFFFD)) + local actualb = { "pdf", "page", b } -- saves tables + local actuale = { "pdf", "page", e } -- saves tables + -- + for unicode, character in sortedhash(characters) do -- sort is nicer for svg + local index = character.index + if index then + local pdf = pdfshapes[index] + local typ = type(pdf) + local data = nil + local dx = nil + local dy = nil + if typ == "table" then + data = pdf.data + dx = pdf.dx or 0 + dy = pdf.dy or 0 + elseif typ == "string" then + data = pdf + dx = 0 + dy = 0 + end + if data then + -- We can delay storage by a lua function in commands: but then we need to + -- be able to provide our own mem stream name (so that we can reserve it). + local setcode, name, nilcode = storepdfdata(data) + if name then + local bt = unicode and getactualtext(unicode) + local wd = character.width or 0 + local ht = character.height or 0 + local dp = character.depth or 0 + character.commands = { + not unicode and actualb or { "pdf", "page", (getactualtext(unicode)) }, + downcommand[dp + dy * hfactor], + rightcommand[dx * hfactor], + -- setcode and { "lua", setcode } or nop, + { "image", { filename = name, width = wd, height = ht, depth = dp } }, + -- nilcode and { "lua", nilcode } or nop, + actuale, + } + character[kind] = true end - initialized = true end - return storepdfdata(pdf) end - end - end +local otfsvg = otf.svg or { } +otf.svg = otfsvg +otf.svgenabled = true do @@ -261,77 +493,66 @@ do end - -- function otfsvg.topdf(svgshapes) - -- local svgfile = "temp-otf-svg-shape.svg" - -- local pdffile = "temp-otf-svg-shape.pdf" - -- local command = "inkscape " .. svgfile .. " --export-pdf=" .. pdffile - -- local testrun = false - -- local pdfshapes = { } - -- local nofshapes = #svgshapes - -- 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) - -- savedata(svgfile,tostring(data)) - -- if data and data ~= "" then - -- report_svg("processing svg shape of glyph %i in container %i",index,i) - -- os.execute(command) - -- pdfshapes[index] = loaddata(pdffile) - -- end - -- end - -- if testrun and i > testrun then - -- report_svg("quiting test run") - -- break - -- end - -- end - -- remove(svgfile) - -- statistics.stoptiming() - -- report_svg("conversion time: %0.3f",statistics.elapsedtime()) - -- return pdfshapes - -- end + local runner = sandbox and sandbox.registerrunner { + name = "otfsvg", + program = "inkscape", + method = "pipeto", + template = "--shell > temp-otf-svg-shape.log", + reporter = report_svg, + } + + if not runner then + -- + -- poor mans variant for generic: + -- + runner = function() + return io.open("inkscape --shell > temp-otf-svg-shape.log","w") + 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 + local pdfshapes = { } + local inkscape = runner() + if inkscape then + 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 + local nofdone = 0 + 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 + nofdone = nofdone + 1 + if nofdone % 100 == 0 then + report_svg("%i shapes processed",nofdone) + end + end end end - end - inkscape:write("quit\n") - -- while inkscape:read("*a") do - -- os.sleep(0.1) - -- end - 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()) + 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() or "-") + end end return pdfshapes end @@ -340,17 +561,12 @@ end local function initializesvg(tfmdata,kind,value) -- hm, always value if value and otf.svgenabled then - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local properties = tfmdata.properties - -- - local svg = properties.svg + local svg = tfmdata.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 @@ -362,44 +578,7 @@ local function initializesvg(tfmdata,kind,value) -- hm, always value 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 + pdftovirtual(tfmdata,pdfshapes,"svg") end end @@ -411,3 +590,114 @@ fonts.handlers.otf.features.register { node = initializesvg, } } + +-- This can be done differently e.g. with ffi and gm and we can share code anway. Using +-- batchmode in gm is not faster and as it accumulates we would need to flush all +-- individual shapes. + +local otfsbix = otf.sbix or { } +otf.sbix = otfsbix +otf.sbixenabled = true + +do + + -- for now png but also other bitmap formats + + local report_sbix = logs.reporter("fonts","sbix conversion") + + local loaddata = io.loaddata + local savedata = io.savedata + local remove = os.remove + + local runner = sandbox and sandbox.registerrunner { + name = "otfsbix", + program = "gm", + template = "convert -quality 100 temp-otf-sbix-shape.sbix temp-otf-sbix-shape.pdf > temp-otf-svg-shape.log", + -- reporter = report_sbix, + } + + if not runner then + -- + -- poor mans variant for generic: + -- + runner = function() + return os.execute("gm convert -quality 100 temp-otf-sbix-shape.sbix temp-otf-sbix-shape.pdf > temp-otf-svg-shape.log") + end + end + + -- Alternatively we can create a single pdf file with -adjoin and then pick up pages from + -- that file but creating thousands of small files is no fun either. + + function otfsbix.topdf(sbixshapes) + local pdfshapes = { } + local sbixfile = "temp-otf-sbix-shape.sbix" + local pdffile = "temp-otf-sbix-shape.pdf" + local nofdone = 0 + local indices = sortedkeys(sbixshapes) -- can be sparse + local nofindices = #indices + report_sbix("processing %i sbix containers",nofindices) + statistics.starttiming() + for i=1,nofindices do + local index = indices[i] + local entry = sbixshapes[index] + local data = entry.data + local x = entry.x + local y = entry.y + savedata(sbixfile,data) + runner() + pdfshapes[index] = { + x = x ~= 0 and x or nil, + y = y ~= 0 and y or nil, + data = loaddata(pdffile), + } + nofdone = nofdone + 1 + if nofdone % 100 == 0 then + report_sbix("%i shapes processed",nofdone) + end + end + report_sbix("processing %i pdf results",nofindices) + remove(sbixfile) + remove(pdffile) + statistics.stoptiming() + if statistics.elapsedseconds then + report_sbix("sbix conversion time %s",statistics.elapsedseconds() or "-") + end + return pdfshapes + -- end + end + +end + +local function initializesbix(tfmdata,kind,value) -- hm, always value + if value and otf.sbixenabled then + local sbix = tfmdata.properties.sbix + local hash = sbix and sbix.hash + local timestamp = sbix and sbix.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 sbixfile = containers.read(otf.sbixcache,hash) + local sbixshapes = sbixfile and sbixfile.sbixshapes + pdfshapes = sbixshapes and otfsbix.topdf(sbixshapes) or { } + containers.write(otf.pdfcache, hash, { + pdfshapes = pdfshapes, + timestamp = timestamp, + }) + end + -- + pdftovirtual(tfmdata,pdfshapes,"sbix") + end +end + +fonts.handlers.otf.features.register { + name = "sbix", + description = "sbix glyphs", + manipulators = { + base = initializesbix, + node = initializesbix, + } +} + diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-one.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-one.lua index d9b9c65dff4..85b9d406e18 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-one.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-one.lua @@ -22,12 +22,11 @@ add features.

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 match, gsub = string.match, string.gsub 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 sortedhash = table.sortedhash 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) @@ -41,6 +40,8 @@ local derivetable = table.derive local findbinfile = resolvers.findbinfile +local privateoffset = fonts.constructors and fonts.constructors.privateoffset or 0xF0000 -- 0x10FFFF + local definers = fonts.definers local readers = fonts.readers local constructors = fonts.constructors @@ -58,7 +59,7 @@ local registerafmfeature = afmfeatures.register local afmenhancers = constructors.enhancers.afm local registerafmenhancer = afmenhancers.register -afm.version = 1.512 -- incrementing this number one up will force a re-cache +afm.version = 1.513 -- incrementing this number one up will force a re-cache afm.cache = containers.define("fonts", "one", afm.version, true) afm.autoprefixed = true -- this will become false some day (catches texnansi-blabla.*) @@ -85,7 +86,7 @@ function afm.load(filename) 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 size, time = attr and attr.size or 0, attr and attr.modification or 0 -- local pfbfile = file.replacesuffix(name,"pfb") local pfbname = resolvers.findfile(pfbfile,"pfb") or "" @@ -139,9 +140,9 @@ local function enhance_unify_names(data, filename) local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context local unicodes = { } local names = { } - local private = constructors.privateoffset + local private = data.private or privateoffset local descriptions = data.descriptions - for name, blob in next, data.characters do + for name, blob in sortedhash(data.characters) do -- sorting is nicer for privates local code = unicodevector[name] -- or characters.name_to_unicode[name] if not code then code = lpegmatch(uparser,name) @@ -179,13 +180,13 @@ local function enhance_unify_names(data, filename) end end data.characters = nil + data.private = private local resources = data.resources - local filename = resources.filename or file.removesuffix(file.basename(filename)) + local filename = resources.filename or file.removesuffix(file.basename(filename)) resources.filename = resolvers.unresolve(filename) -- no shortcut resources.unicodes = unicodes -- name to unicode - resources.marks = { } -- todo - -- resources.names = names -- name to index - resources.private = private + resources.marks = { } -- todo + -- resources.names = names -- name to index end local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } } @@ -587,6 +588,7 @@ local function copytotfm(data) properties.fullname = fullname properties.psname = fullname properties.name = filename or fullname or fontname + properties.private = properties.private or data.private or privateoffset -- if next(characters) then return { diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-onr.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-onr.lua index 6c33b24c644..7c806c69206 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-onr.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-onr.lua @@ -41,7 +41,7 @@ handlers.afm = afm local readers = afm.readers or { } afm.readers = readers -afm.version = 1.512 -- incrementing this number one up will force a re-cache +afm.version = 1.513 -- incrementing this number one up will force a re-cache --[[ldx--

We start with the basic reader which we give a name similar to the built in @@ -89,7 +89,7 @@ do 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 name = P("/") * C((R("az","AZ","09")+S("-_."))^1) local digits = R("09")^1 local cardinal = digits / tonumber local spaces = P(" ")^1 @@ -103,32 +103,40 @@ do return position + 1 end - local setroutine = function(str,position,index,size) + local setroutine = function(str,position,index,size,filename) local forward = position + tonumber(size) - local stream = sub(str,position+1,forward) - routines[index] = decrypt(stream,4330,4) + local stream = decrypt(sub(str,position+1,forward),4330,4) + routines[index] = { byte(stream,1,#stream) } return forward end - local setvector = function(str,position,name,size) + local setvector = function(str,position,name,size,filename) local forward = position + tonumber(size) if n >= m then return #str elseif forward < #str then + if n == 0 and name ~= ".notdef" then + report_pfb("reserving .notdef at index 0 in %a",filename) -- luatex needs that + n = n + 1 + end vector[n] = name - n = n + 1 -- we compensate for notdef at the cff loader end + n = n + 1 return forward else return #str end end - local setshapes = function(str,position,name,size) + local setshapes = function(str,position,name,size,filename) local forward = position + tonumber(size) local stream = sub(str,position+1,forward) if n > m then return #str elseif forward < #str then + if n == 0 and name ~= ".notdef" then + report_pfb("reserving .notdef at index 0 in %a",filename) -- luatex needs that + n = n + 1 + end vector[n] = name n = n + 1 chars [n] = decrypt(stream,4330,4) @@ -144,15 +152,15 @@ do local p_filterroutines = -- dup RD or -| NP or | (1-subroutines)^0 * subroutines * spaces * Cmt(cardinal,initialize) - * (Cmt(cardinal * spaces * cardinal * p_rd, setroutine) * p_np + P(1))^1 + * (Cmt(cardinal * spaces * cardinal * p_rd * Carg(1), setroutine) * p_np + P(1))^1 local p_filtershapes = -- /foo RD ND (1-charstrings)^0 * charstrings * spaces * Cmt(cardinal,initialize) - * (Cmt(name * spaces * cardinal * p_rd, setshapes) * p_nd + P(1))^1 + * (Cmt(name * spaces * cardinal * p_rd * Carg(1) , 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 + * (Cmt(name * spaces * cardinal * Carg(1), setvector) + P(1))^1 ) -- /Encoding 256 array @@ -177,7 +185,7 @@ do return end - if not (find(data,"!PS%-AdobeFont%-") or find(data,"%%!FontType1")) then + if not (find(data,"!PS-AdobeFont-",1,true) or find(data,"%!FontType1",1,true)) then report_pfb("no font in %a",filename) return end @@ -192,14 +200,14 @@ do 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) + lpegmatch(p_filterroutines,binary,1,filename) + lpegmatch(p_filtershapes,binary,1,filename) local data = { dictionaries = { { @@ -209,9 +217,9 @@ do } }, } - fonts.handlers.otf.readers.parsecharstrings(data,glyphs,true,true) + fonts.handlers.otf.readers.parsecharstrings(false,data,glyphs,true,true) else - lpegmatch(p_filternames,binary) + lpegmatch(p_filternames,binary,1,filename) end names = vector @@ -233,7 +241,7 @@ do if trace_loading then report_afm("getting index data from %a",pfbname) end - for index=1,#vector do + for index=0,#vector do -- hm, zero, often space or notdef local name = vector[index] local char = characters[name] if char then @@ -241,6 +249,10 @@ do report_afm("glyph %a has index %a",name,index) end char.index = index + else + if trace_indexing then + report_afm("glyph %a has index %a but no data",name,index) + end end end end @@ -410,10 +422,6 @@ 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 ) @@ -463,17 +471,17 @@ 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) + pfbname = resolvers.findfile(file.replacesuffix(file.nameonly(afmname),"pfb")) end if pfbname and pfbname ~= "" then data.resources.filename = resolvers.unresolve(pfbname) get_indexes(data,pfbname) - elseif trace_loading then + return data + else -- if trace_loading then report_afm("no pfb file for %a",afmname) - -- data.resources.filename = "unset" -- better than loading the afm file + -- better than loading the afm file: data.resources.filename = rawname + -- but that will still crash the backend so we just return nothing now end - return data end end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-osd.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-osd.lua index b67cc924113..32d791b48e9 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-osd.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-osd.lua @@ -6,72 +6,85 @@ if not modules then modules = { } end modules ['font-osd'] = { -- script devanag license = "see context related readme files" } --- I'll optimize this one with ischar (much faster) when I see a reason (read: I need a --- proper test case first). --- This is a version of font-odv.lua adapted to the new font loader and more --- direct hashing. The initialization code has been adapted (more efficient). One day --- I'll speed this up ... char swapping and properties. +-- we need to check nbsphash (context only) -- A few remarks: -- --- This code is a partial rewrite of the code that deals with devanagari. The data and logic --- is by Kai Eigner and based based on Microsoft's OpenType specifications for specific --- scripts, but with a few improvements. More information can be found at: +-- This code is a partial rewrite of the code that deals with devanagari. The data +-- and logic is by Kai Eigner and based based on Microsoft's OpenType specifications +-- for specific scripts, but with a few improvements. More information can be found +-- at: -- -- deva: http://www.microsoft.com/typography/OpenType%20Dev/devanagari/introO.mspx -- dev2: http://www.microsoft.com/typography/OpenType%20Dev/devanagari/intro.mspx -- --- Rajeesh Nambiar provided patches for the malayalam variant. Thanks to feedback from --- the mailing list some aspects could be improved. +-- Rajeesh Nambiar provided patches for the malayalam variant. Thanks to feedback +-- from the mailing list some aspects could be improved. -- --- As I touched nearly all code, reshuffled it, optimized a lot, etc. etc. (imagine how --- much can get messed up in over a week work) it could be that I introduced bugs. There --- is more to gain (esp in the functions applied to a range) but I'll do that when --- everything works as expected. Kai's original code is kept in font-odk.lua as a reference --- so blame me (HH) for bugs. +-- As I touched nearly all code, reshuffled it, optimized a lot, etc. etc. (imagine +-- how much can get messed up in over a week work) it could be that I introduced +-- bugs. There is more to gain (esp in the functions applied to a range) but I'll do +-- that when everything works as expected. Kai's original code is kept in +-- font-odk.lua as a reference so blame me (HH) for bugs. (We no longer ship that +-- file as the code below has diverted too much and in the meantime has more than +-- doubled in size.) -- --- Interesting is that Kai managed to write this on top of the existing otf handler. Only a --- few extensions were needed, like a few more analyzing states and dealing with changed --- head nodes in the core scanner as that only happens here. There's a lot going on here --- and it's only because I touched nearly all code that I got a bit of a picture of what --- happens. For in-depth knowledge one needs to consult Kai. +-- Interesting is that Kai managed to write this on top of the existing otf handler. +-- Only a few extensions were needed, like a few more analyzing states and dealing +-- with changed head nodes in the core scanner as that only happens here. There's a +-- lot going on here and it's only because I touched nearly all code that I got a +-- bit of a picture of what happens. For in-depth knowledge one needs to consult +-- Kai. -- --- The rewrite mostly deals with efficiency, both in terms of speed and code. We also made --- sure that it suits generic use as well as use in ConTeXt. I removed some buglets but can --- as well have messed up the logic by doing this. For this we keep the original around --- as that serves as reference. Due to the lots of reshuffling glyphs quite some leaks --- occur(red) but once I'm satisfied with the rewrite I'll weed them. I also integrated --- initialization etc into the regular mechanisms. +-- The rewrite mostly deals with efficiency, both in terms of speed and code. We +-- also made sure that it suits generic use as well as use in ConTeXt. I removed +-- some buglets but can as well have messed up the logic by doing this. For this we +-- keep the original around as that serves as reference. Due to the lots of +-- reshuffling glyphs quite some leaks occur(red) but once I'm satisfied with the +-- rewrite I'll weed them. I also integrated initialization etc into the regular +-- mechanisms. -- --- In the meantime, we're down from 25.5-3.5=22 seconds to 17.7-3.5=14.2 seconds for a 100 --- page sample (mid 2012) with both variants so it's worth the effort. Some more speedup is --- to be expected. Due to the method chosen it will never be real fast. If I ever become a --- power user I'll have a go at some further speed up. I will rename some functions (and --- features) once we don't need to check the original code. We now use a special subset --- sequence for use inside the analyzer (after all we could can store this in the dataset --- and save redundant analysis). +-- In the meantime, we're down from 25.5-3.5=22 seconds to 17.7-3.5=14.2 seconds for +-- a 100 page sample (mid 2012) with both variants so it's worth the effort. Some +-- more speedup is to be expected. Due to the method chosen it will never be real +-- fast. If I ever become a power user I'll have a go at some further speed up. I +-- will rename some functions (and features) once we don't need to check the +-- original code. We now use a special subset sequence for use inside the analyzer +-- (after all we could can store this in the dataset and save redundant analysis). -- --- I might go for an array approach with respect to attributes (and reshuffling). Easier. +-- By now we have yet another incremental improved version. In the end I might +-- rewrite the code. -- --- Some data will move to char-def.lua (some day). --- --- By now we have yet another incremental improved version. In the end I might rewrite the --- code. - -- Hans Hagen, PRAGMA-ADE, Hasselt NL --- --- We could have c_nukta, c_halant, c_ra is we know that they are never used mixed within --- one script .. yes or no? + +-- Todo: -- -- Matras: according to Microsoft typography specifications "up to one of each type: -- pre-, above-, below- or post- base", but that does not seem to be right. It could -- become an option. +-- +-- Resources: +-- +-- The tables that we had here are now generated from char-def.lua or in the case of +-- generic usage loaded from luatex-basics-chr.lua. Still a couple of entries need +-- to be added to char-def.lua but finally I moved the indic specific tables there. +-- For generic usage one can create the relevant resources by running: +-- +-- context luatex-basics-prepare.tex +-- +-- and an overview with: +-- +-- context --global s-fonts-basics.mkiv +-- +-- For now we have defined: bengali, devanagari, gujarati, gurmukhi, kannada, +-- malayalam, oriya, tamil and tolugu but not all are checked. Also, some of the +-- code below might need to be adapted to the extra scripts. local insert, imerge, copy = table.insert, table.imerge, table.copy local next, type = next, type -local report_devanagari = logs.reporter("otf","devanagari") +local report = logs.reporter("otf","devanagari") fonts = fonts or { } fonts.analyzers = fonts.analyzers or { } @@ -86,8 +99,6 @@ 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 @@ -121,6 +132,9 @@ local a_state = attributes.private('state') local a_syllabe = attributes.private('syllabe') local dotted_circle = 0x25CC +local c_nbsp = 0x00A0 +local c_zwnj = 0x200C +local c_zwj = 0x200D local states = fonts.analyzers.states -- not features @@ -139,327 +153,193 @@ replace_all_nbsp = function(head) -- delayed definition return replace_all_nbsp(head) end -local xprocesscharacters = nil +local processcharacters = nil if context then - xprocesscharacters = function(head,font) - xprocesscharacters = nodes.handlers.characters - return xprocesscharacters(head,font) + local fontprocesses = fonts.hashes.processes + function processcharacters(head,font) + local processors = fontprocesses[font] + for i=1,#processors do + head = processors[i](head,font,0) + end + return head end else - xprocesscharacters = function(head,font) - xprocesscharacters = nodes.handlers.nodepass -- generic - return xprocesscharacters(head,font) + function processcharacters(head,font) + local processors = fontdata[font].shared.processes + for i=1,#processors do + head = processors[i](head,font,0) + end + return head end end -local function processcharacters(head,font) - return tonut(xprocesscharacters(tonode(head))) -- can be more efficient in context, just direct call -end - --- local fontprocesses = fonts.hashes.processes --- --- function processcharacters(head,font) --- local processors = fontprocesses[font] --- for i=1,#processors do --- head = processors[i](head,font,0) --- end --- return head, true --- end - --- In due time there will be entries here for scripts like Bengali, Gujarati, --- Gurmukhi, Kannada, Malayalam, Oriya, Tamil, Telugu. Feel free to provide the --- code points. - -- We can assume that script are not mixed in the source but if that is the case -- we might need to have consonants etc per script and initialize a local table --- pointing to the right one. - --- new, to be checked: --- --- U+00978 : DEVANAGARI LETTER MARWARI DDA --- U+00980 : BENGALI ANJI --- U+00C00 : TELUGU SIGN COMBINING CANDRABINDU ABOVE --- U+00C34 : TELUGU LETTER LLLA --- U+00C81 : KANNADA SIGN CANDRABINDU --- U+00D01 : MALAYALAM SIGN CANDRABINDU --- U+00DE6 : SINHALA LITH DIGIT ZERO --- U+00DE7 : SINHALA LITH DIGIT ONE --- U+00DE8 : SINHALA LITH DIGIT TWO --- U+00DE9 : SINHALA LITH DIGIT THREE --- U+00DEA : SINHALA LITH DIGIT FOUR --- U+00DEB : SINHALA LITH DIGIT FIVE --- U+00DEC : SINHALA LITH DIGIT SIX --- U+00DED : SINHALA LITH DIGIT SEVEN --- U+00DEE : SINHALA LITH DIGIT EIGHT --- U+00DEF : SINHALA LITH DIGIT NINE - -local consonant = { - -- devanagari - [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, - -- kannada - [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, -- obsolete - -- malayalam - [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, -} +-- pointing to the right one. But not now. -local independent_vowel = { - -- devanagari - [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, - -- kannada - [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, - -- malayalam - [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 = { -- matra - -- devanagari - [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, - -- kannada - [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, - -- malayalam - [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 indicgroups = characters and characters.indicgroups -local vowel_modifier = { - -- devanagari - [0x0900] = true, [0x0901] = true, [0x0902] = true, [0x0903] = true, - -- A8E0 - A8F1 are cantillation marks for the Samaveda and may not belong here. - [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, - -- malayalam - [0x0D02] = true, [0x0D03] = true, -} +if not indicgroups and characters then -local stress_tone_mark = { - [0x0951] = true, [0x0952] = true, [0x0953] = true, [0x0954] = true, - -- kannada - [0x0CCD] = true, - -- malayalam - [0x0D4D] = true, -} + local indic = { + c = { }, -- consonant + i = { }, -- independent vowel + d = { }, -- dependent vowel + m = { }, -- vowel modifier + s = { }, -- stress tone mark + o = { }, -- other + } -local nukta = { - -- devanagari - [0x093C] = true, - -- kannada: - [0x0CBC] = true, -} + local indicmarks = { + l = { }, -- left | pre_mark + t = { }, -- top | above_mark + b = { }, -- bottom | below_mark + r = { }, -- right | post_mark + s = { }, -- split | twopart_mark + } -local halant = { - -- devanagari - [0x094D] = true, - -- kannada - [0x0CCD] = true, - -- malayalam - [0x0D4D] = true, -} + local indicclasses = { + nukta = { }, + halant = { }, + ra = { }, + anudatta = { }, + } -local ra = { - -- devanagari - [0x0930] = true, - -- kannada - [0x0CB0] = true, - -- malayalam - [0x0D30] = true, -} + local indicorders = { + bp = { }, -- before_postscript + ap = { }, -- after_postscript + bs = { }, -- before_half + as = { }, -- after_half + bh = { }, -- before_subscript + ah = { }, -- after_subscript + bm = { }, -- before_main + am = { }, -- after_main + } -local c_anudatta = 0x0952 -- used to be tables -local c_nbsp = 0x00A0 -- used to be tables -local c_zwnj = 0x200C -- used to be tables -local c_zwj = 0x200D -- used to be tables + for k, v in next, characters.data do + local i = v.indic + if i then + indic[i][k] = true + i = v.indicmark + if i then + if i == "s" then + local s = v.specials + indicmarks[i][k] = { s[2], s[3] } + else + indicmarks[i][k] = true + end + end + i = v.indicclass + if i then + indicclasses[i][k] = true + end + i = v.indicorder + if i then + indicorders[i][k] = true + end + end + end -local zw_char = { -- could also be inlined - [0x200C] = true, - [0x200D] = true, -} + indicgroups = { + consonant = indic.c, + independent_vowel = indic.i, + dependent_vowel = indic.d, + vowel_modifier = indic.m, + stress_tone_mark = indic.s, + -- other = indic.o, + pre_mark = indicmarks.l, + above_mark = indicmarks.t, + below_mark = indicmarks.b, + post_mark = indicmarks.r, + twopart_mark = indicmarks.s, + nukta = indicclasses.nukta, + halant = indicclasses.halant, + ra = indicclasses.ra, + anudatta = indicclasses.anudatta, + before_postscript = indicorders.bp, + after_postscript = indicorders.ap, + before_half = indicorders.bh, + after_half = indicorders.ah, + before_subscript = indicorders.bs, + after_subscript = indicorders.as, + before_main = indicorders.bm, + after_main = indicorders.am, + } --- 0C82 anusvara --- 0C83 visarga --- 0CBD avagraha --- 0CD5 length mark --- 0CD6 ai length mark --- 0CE0 letter ll --- 0CE1 letter rr --- 0CE2 vowel sign l --- 0CE2 vowel sign ll --- 0CF1 sign --- 0CF2 sign --- OCE6 - OCEF digits - -local pre_mark = { - [0x093F] = true, [0x094E] = true, - -- malayalam - [0x0D46] = true, [0x0D47] = true, [0x0D48] = true, -} + indic = nil + indicmarks = nil + indicclasses = nil + indicorders = nil -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, - -- malayalam - [0x0D4E] = true, -} + characters.indicgroups = indicgroups -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, -} +end -local post_mark = { - [0x0903] = true, [0x093B] = true, [0x093E] = true, [0x0940] = true, - [0x0949] = true, [0x094A] = true, [0x094B] = true, [0x094C] = true, - [0x094F] = true, -} +local consonant = indicgroups.consonant +local independent_vowel = indicgroups.independent_vowel +local dependent_vowel = indicgroups.dependent_vowel +local vowel_modifier = indicgroups.vowel_modifier +local stress_tone_mark = indicgroups.stress_tone_mark +local pre_mark = indicgroups.pre_mark +local above_mark = indicgroups.above_mark +local below_mark = indicgroups.below_mark +local post_mark = indicgroups.post_mark +local twopart_mark = indicgroups.twopart_mark +local nukta = indicgroups.nukta +local halant = indicgroups.halant +local ra = indicgroups.ra +local anudatta = indicgroups.anudatta + +local before_postscript = indicgroups.before_postscript +local after_postscript = indicgroups.after_postscript +local before_half = indicgroups.before_half +local after_half = indicgroups.after_half +local before_subscript = indicgroups.before_subscript +local after_subscript = indicgroups.after_subscript +local before_main = indicgroups.before_main +local after_main = indicgroups.after_main + +local mark_four = table.merged ( + pre_mark, + above_mark, + below_mark, + post_mark +) + +local mark_above_below_post = table.merged ( + above_mark, + below_mark, + post_mark +) -local twopart_mark = { - -- malayalam - [0x0D4A] = { 0x0D46, 0x0D3E, }, -- ൊ - [0x0D4B] = { 0x0D47, 0x0D3E, }, -- ോ - [0x0D4C] = { 0x0D46, 0x0D57, }, -- ൌ -} +-- We use some pseudo features as we need to manipulate the nodelist based +-- on information in the font as well as already applied features. We can +-- probably replace some of the code below by injecting 'real' features +-- using the extension mechanism. -local mark_four = { } -- As we access these frequently an extra hash is used. - -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 - --- Again, this table can be extended for other scripts than devanagari. Actually, --- for ConTeXt this kind of data is kept elsewhere so eventually we might move --- tables to someplace else. - -local reorder_class = { - -- devanagari - [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", - -- kannada: - [0x0CB0] = "after postscript", -- todo in code below - [0x0CBF] = "before subscript", -- todo in code below - [0x0CC6] = "before subscript", -- todo in code below - [0x0CCC] = "before subscript", -- todo in code below - [0x0CBE] = "before subscript", -- todo in code below - [0x0CE2] = "before subscript", -- todo in code below - [0x0CE3] = "before subscript", -- todo in code below - [0x0CC1] = "before subscript", -- todo in code below - [0x0CC2] = "before subscript", -- todo in code below - [0x0CC3] = "after subscript", - [0x0CC4] = "after subscript", - [0x0CD5] = "after subscript", - [0x0CD6] = "after subscript", - -- malayalam +local zw_char = { -- both_joiners_true + [c_zwnj] = true, + [c_zwj ] = true, } --- We use some pseudo features as we need to manipulate the nodelist based --- on information in the font as well as already applied features. - local dflt_true = { dflt = true } -local dev2_defaults = { +local two_defaults = { dev2 = dflt_true, } -local deva_defaults = { +local one_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 }, + features = { dv01 = two_defaults }, flags = false_flags, name = "dv01_reorder_matras", order = { "dv01" }, @@ -467,14 +347,13 @@ local sequence_reorder_matras = { nofsteps = 1, steps = { { - osdstep = true, coverage = pre_mark, } } } local sequence_reorder_reph = { - features = { dv02 = dev2_defaults }, + features = { dv02 = two_defaults }, flags = false_flags, name = "dv02_reorder_reph", order = { "dv02" }, @@ -482,14 +361,13 @@ local sequence_reorder_reph = { nofsteps = 1, steps = { { - osdstep = true, coverage = { }, } } } local sequence_reorder_pre_base_reordering_consonants = { - features = { dv03 = dev2_defaults }, + features = { dv03 = two_defaults }, flags = false_flags, name = "dv03_reorder_pre_base_reordering_consonants", order = { "dv03" }, @@ -497,22 +375,21 @@ local sequence_reorder_pre_base_reordering_consonants = { nofsteps = 1, steps = { { - osdstep = true, coverage = { }, } } } local sequence_remove_joiners = { - features = { dv04 = deva_defaults }, + features = { dv04 = one_defaults }, flags = false_flags, name = "dv04_remove_joiners", order = { "dv04" }, type = "devanagari_remove_joiners", nofsteps = 1, steps = { - { osdstep = true, - coverage = both_joiners_true, + { + coverage = zw_char, -- both_joiners_true }, } } @@ -522,38 +399,85 @@ local sequence_remove_joiners = { -- as it might depends on the font. Not that it's a bottleneck. local basic_shaping_forms = { - nukt = true, + -- init = true, -- new + -- abvs = true, -- new akhn = true, - rphf = true, - pref = true, - rkrf = true, blwf = true, + -- calt = true, -- new + cjct = true, half = true, + -- haln = true, -- new + nukt = true, + pref = true, + -- pres = true, -- new pstf = true, + -- psts = true, -- new + rkrf = true, + rphf = true, vatu = true, - cjct = true, } local valid = { - akhn = true, -- malayalam - rphf = true, - pref = true, - half = true, + -- akhn = true, -- malayalam + -- rphf = true, + -- pref = true, + -- half = true, + -- blwf = true, + -- pstf = true, + -- pres = true, -- malayalam + -- blws = true, -- malayalam + -- psts = true, -- malayalam + abvs = true, + akhn = true, blwf = true, + calt = true, + cjct = true, + half = true, + haln = true, + nukt = true, + pref = true, + pres = true, pstf = true, - pres = true, -- malayalam - blws = true, -- malayalam - psts = true, -- malayalam + psts = true, + rkrf = true, + rphf = true, + vatu = true, + pres = true, + abvs = true, + blws = true, + psts = true, + haln = true, + calt = true, } +local scripts = { } + +local scripts_one = { "deva", "mlym", "beng", "gujr", "guru", "knda", "orya", "taml", "telu" } +local scripts_two = { "dev2", "mlm2", "bng2", "gjr2", "gur2", "knd2", "ory2", "tml2", "tel2" } + +local nofscripts = #scripts_one + +for i=1,nofscripts do + local one = scripts_one[i] + local two = scripts_two[i] + scripts[one] = true + scripts[two] = true + two_defaults[one] = dflt_true + one_defaults[one] = dflt_true + one_defaults[two] = dflt_true +end + +local function valid_one(s) for i=1,nofscripts do if s[scripts_one[i]] then return true end end end +local function valid_two(s) for i=1,nofscripts do if s[scripts_two[i]] then return true end end end + local function initializedevanagi(tfmdata) local script, language = otf.scriptandlanguage(tfmdata,attr) -- todo: take fast variant - if script == "deva" or script == "dev2" or script =="mlym" or script == "mlm2" then + if scripts[script] then local resources = tfmdata.resources local devanagari = resources.devanagari if not devanagari then -- - report_devanagari("adding devanagari features to font") + report("adding devanagari features to font") -- local gsubfeatures = resources.features.gsub local sequences = resources.sequences @@ -572,10 +496,10 @@ local function initializedevanagi(tfmdata) end local insertindex = lastmatch + 1 -- - gsubfeatures["dv01"] = dev2_defaults -- reorder matras - gsubfeatures["dv02"] = dev2_defaults -- reorder reph - gsubfeatures["dv03"] = dev2_defaults -- reorder pre base reordering consonants - gsubfeatures["dv04"] = deva_defaults -- remove joiners + gsubfeatures["dv01"] = two_defaults -- reorder matras + gsubfeatures["dv02"] = two_defaults -- reorder reph + gsubfeatures["dv03"] = two_defaults -- reorder pre base reordering consonants + gsubfeatures["dv04"] = one_defaults -- remove joiners -- local reorder_pre_base_reordering_consonants = copy(sequence_reorder_pre_base_reordering_consonants) local reorder_reph = copy(sequence_reorder_reph) @@ -631,43 +555,31 @@ local function initializedevanagi(tfmdata) end end end - for kind, spec in next, features do -- beware, this is - if spec.dev2 and valid[kind] then + for kind, spec in next, features do + -- if spec.dev2 and valid[kind] then + if valid[kind] and valid_two(spec)then for i=1,nofsteps do local step = steps[i] local coverage = step.coverage if coverage then local reph = false if kind == "rphf" then - -- - -- KE: I don't understand the rationale behind osdstep. The original if - -- statement checked whether coverage is contextual chaining. - -- - -- HH: The osdstep signals that we deal with our own feature here, not - -- one in the font itself so it was just a safeguard against us overloading - -- something driven by the font. - -- - -- if step.osdstep then -- selective - if true then -- always - -- rphf acts on consonant + halant - 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 + -- rphf acts on consonant + halant + 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 - else - -- rphf might be result of other handler/chainproc end end seqsubset[#seqsubset+1] = { kind, coverage, reph } @@ -716,8 +628,30 @@ local function initializedevanagi(tfmdata) sharedfeatures["pstf"] = true sharedfeatures["pref"] = true sharedfeatures["dv03"] = true -- dv03_reorder_pre_base_reordering_consonants - gsubfeatures ["dv03"] = dev2_defaults -- reorder pre base reordering consonants + gsubfeatures ["dv03"] = two_defaults -- reorder pre base reordering consonants insert(sequences,insertindex,sequence_reorder_pre_base_reordering_consonants) + -- elseif script == "beng" then + -- elseif script == "bng2" then + -- elseif script == "gujr" then + -- elseif script == "gjr2" then + -- elseif script == "guru" then + -- elseif script == "gur2" then + -- elseif script == "knda" then + -- elseif script == "knd2" then + elseif script == "taml" then + sharedfeatures["dv04"] = true -- dv04_remove_joiners +sharedfeatures["pstf"] = true + elseif script == "tml2" then +-- sharedfeatures["pstf"] = true +-- sharedfeatures["pref"] = true +-- sharedfeatures["dv01"] = true -- dv01_reorder_matras +-- sharedfeatures["dv02"] = true -- dv02_reorder_reph +-- sharedfeatures["dv03"] = true -- dv03_reorder_pre_base_reordering_consonants +-- sharedfeatures["dv04"] = true -- dv04_remove_joiners + -- elseif script == "telu" then + -- elseif script == "tel2" then + else + report("todo: enable the right features for script %a",script) end end end @@ -732,9 +666,22 @@ registerotffeature { }, } +local show_syntax_errors = false + +local function inject_syntax_error(head,current,char) + local signal = copy_node(current) + copyinjection(signal,current) + if pre_mark[char] then + setchar(signal,dotted_circle) + else + setchar(current,dotted_circle) + end + return insert_node_after(head,current,signal) +end + -- hm, this is applied to one character: -local function deva_initialize(font,attr) -- we need a proper hook into the dataset initializer +local function initialize_one(font,attr) -- we need a proper hook into the dataset initializer local tfmdata = fontdata[font] local datasets = otf.dataset(tfmdata,font,attr) -- don't we know this one? @@ -773,9 +720,9 @@ local function deva_initialize(font,attr) -- we need a proper hook into the data end -local function deva_reorder(head,start,stop,font,attr,nbspaces) +local function reorder_one(head,start,stop,font,attr,nbspaces) - local reph, vattu, blwfcache = deva_initialize(font,attr) -- todo: a hash[font] + local reph, vattu, blwfcache = initialize_one(font,attr) -- todo: a hash[font] local current = start local n = getnext(start) @@ -812,40 +759,39 @@ local function deva_reorder(head,start,stop,font,attr,nbspaces) lastcons = current current = getnext(current) if current ~= stop then - if nukta[getchar(current)] then + local char = getchar(current) + if nukta[char] then current = getnext(current) + char = getchar(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 -- needs checking - local changestop = next == stop - local tempcurrent = copy_node(next) - copyinjection(tempcurrent,next) - local nextcurrent = copy_node(current) - copyinjection(nextcurrent,current) -- KE: necessary? HH: probably not as positioning comes later and we rawget/set - 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) -- KE: necessary? HH: probably not as positioning comes later and we rawget/set - setchar(current,dotted_circle) - head = insert_node_after(head, current, n) - else - setchar(current,getchar(tempcurrent)) -- we assumes that the result of blwf consists of one node - local freenode = getnext(current) - setlink(current,tmp) - flush_node(freenode) - flush_list(tempcurrent) - if changestop then - stop = current - end + if char == c_zwj and 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 -- needs checking + local changestop = next == stop + local tempcurrent = copy_node(next) + copyinjection(tempcurrent,next) + local nextcurrent = copy_node(current) + copyinjection(nextcurrent,current) -- KE: necessary? HH: probably not as positioning comes later and we rawget/set + 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) + if show_syntax_errors then + head, current = inject_syntax_error(head,current,char) + end + else + setchar(current,getchar(tempcurrent)) -- we assumes that the result of blwf consists of one node + local freenode = getnext(current) + setlink(current,tmp) + flush_node(freenode) + flush_list(tempcurrent) + if changestop then + stop = current end end end @@ -1140,6 +1086,7 @@ function handlers.devanagari_reorder_matras(head,start) -- no leak head = remove_node(head,start) setlink(start,next) setlink(current,start) + -- setlink(current,start,next) -- maybe start = startnext break end @@ -1151,41 +1098,39 @@ function handlers.devanagari_reorder_matras(head,start) -- no leak return head, start, true end --- todo: way more caching of attributes and font - --- Reph’s original position is always at the beginning of the syllable, (i.e. it is not reordered at the character reordering stage). --- However, it will be reordered according to the basic-forms shaping results. --- Possible positions for reph, depending on the script, are; after main, before post-base consonant forms, --- and after post-base consonant forms. - --- 1 If reph should be positioned after post-base consonant forms, proceed to step 5. --- 2 If the reph repositioning class is not after post-base: target position is after the first explicit halant glyph between --- the first post-reph consonant and last main consonant. If ZWJ or ZWNJ are following this halant, position is moved after it. --- If such position is found, this is the target position. Otherwise, proceed to the next step. --- Note: in old-implementation fonts, where classifications were fixed in shaping engine, --- there was no case where reph position will be found on this step. --- 3 If reph should be repositioned after the main consonant: from the first consonant not ligated with main, --- or find the first consonant that is not a potential pre-base reordering Ra. --- 4 If reph should be positioned before post-base consonant, find first post-base classified consonant not ligated with main. --- If no consonant is found, the target position should be before the first matra, syllable modifier sign or vedic sign. --- 5 If no consonant is found in steps 3 or 4, move reph to a position immediately before the first post-base matra, --- syllable modifier sign or vedic sign that has a reordering class after the intended reph position. --- For example, if the reordering position for reph is post-main, it will skip above-base matras that also have a post-main position. --- 6 Otherwise, reorder reph to the end of the syllable. - --- hm, this only looks at the start of a nodelist ... is this supposed to be line based? +-- Reph’s original position is always at the beginning of the syllable, (i.e. it is +-- not reordered at the character reordering stage). However, it will be reordered +-- according to the basic-forms shaping results. Possible positions for reph, +-- depending on the script, are; after main, before post-base consonant forms, and +-- after post-base consonant forms. + +-- In Devanagari reph has reordering position 'before postscript' and dev2 only +-- follows step 2, 4, and 6. function handlers.devanagari_reorder_reph(head,start) - -- since in Devanagari reph has reordering position 'before postscript' dev2 only follows step 2, 4, and 6, - -- the other steps are still ToDo (required for scripts other than dev2) local current = getnext(start) local startnext = nil local startprev = nil local startfont = getfont(start) local startattr = getprop(start,a_syllabe) + -- + ::step_1:: + -- + -- If reph should be positioned after post-base consonant forms, proceed to step 5. + -- + ::step_2:: + -- + -- If the reph repositioning class is not after post-base: target position is after + -- the first explicit halant glyph between the first post-reph consonant and last + -- main consonant. If ZWJ or ZWNJ are following this halant, position is moved after + -- it. If such position is found, this is the target position. Otherwise, proceed to + -- the next step. Note: in old-implementation fonts, where classifications were + -- fixed in shaping engine, there was no case where reph position will be found on + -- this step. + -- while current do local char = ischar(current,startfont) - if char and getprop(current,a_syllabe) == startattr then -- step 2 + 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 @@ -1199,6 +1144,7 @@ function handlers.devanagari_reorder_reph(head,start) head = remove_node(head,start) setlink(start,next) setlink(current,start) + -- setlink(current,start,next) -- maybe start = startnext startattr = getprop(start,a_syllabe) break @@ -1208,17 +1154,31 @@ function handlers.devanagari_reorder_reph(head,start) break end end + ::step_3:: + -- + -- If reph should be repositioned after the main consonant: from the first consonant + -- not ligated with main, or find the first consonant that is not a potential + -- pre-base reordering Ra. + -- + -- Kai: todo + -- + ::step_4:: + -- + -- If reph should be positioned before post-base consonant, find first post-base + -- classified consonant not ligated with main. If no consonant is found, the target + -- position should be before the first matra, syllable modifier sign or vedic sign. + -- if not startnext then current = getnext(start) while current do local char = ischar(current,startfont) - if char and getprop(current,a_syllabe) == startattr then -- step 4 + if char and getprop(current,a_syllabe) == startattr then if getprop(current,a_state) == s_pstf then -- post-base startnext = getnext(start) head = remove_node(head,start) - local prev = getprev(current) - setlink(prev,start) + setlink(getprev(current),start) setlink(start,current) + -- setlink(getprev(current),start,current) -- maybe start = startnext startattr = getprop(start,a_syllabe) break @@ -1229,16 +1189,22 @@ function handlers.devanagari_reorder_reph(head,start) end end end - -- todo: determine position for reph with reordering position other than 'before postscript' - -- (required for scripts other than dev2) - -- leaks + -- + ::step_5:: + -- + -- If no consonant is found in steps 3 or 4, move reph to a position immediately + -- before the first post-base matra, syllable modifier sign or vedic sign that has a + -- reordering class after the intended reph position. For example, if the reordering + -- position for reph is post-main, it will skip above-base matras that also have a + -- post-main position. + -- 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 -- step 5 - if not c and mark_above_below_post[char] and reorder_class[char] ~= "after subscript" then + if char and getprop(current,a_syllabe) == startattr then + if not c and mark_above_below_post[char] and not after_subscript[char] then c = current end current = getnext(current) @@ -1250,21 +1216,25 @@ function handlers.devanagari_reorder_reph(head,start) if c then startnext = getnext(start) head = remove_node(head,start) - local prev = getprev(c) - setlink(prev,start) + setlink(getprev(c),start) setlink(start,c) + -- setlink(getprev(c),start,c) -- maybe -- end start = startnext startattr = getprop(start,a_syllabe) end end - -- leaks + -- + ::step_6:: + -- + -- Otherwise, reorder reph to the end of the syllable. + -- 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 --step 6 + if nextchar and getprop(next,a_syllabe) == startattr then current = next next = getnext(current) else @@ -1274,9 +1244,9 @@ function handlers.devanagari_reorder_reph(head,start) if start ~= current then startnext = getnext(start) head = remove_node(head,start) - local next = getnext(current) - setlink(start,next) + setlink(start,getnext(current)) setlink(current,start) + -- setlink(current,start,getnext(current)) -- maybe start = startnext end end @@ -1284,16 +1254,20 @@ function handlers.devanagari_reorder_reph(head,start) return head, start, true end --- we can cache some checking (v) - -- If a pre-base reordering consonant is found, reorder it according to the following rules: -- --- 1 Only reorder a glyph produced by substitution during application of the feature. --- (Note that a font may shape a Ra consonant with the feature generally but block it in certain contexts.) --- 2 Try to find a target position the same way as for pre-base matra. If it is found, reorder pre-base consonant glyph. +-- 1 Only reorder a glyph produced by substitution during application of the feature. (Note +-- that a font may shape a Ra consonant with the feature generally but block it in certain +-- contexts.) +-- 2 Try to find a target position the same way as for pre-base matra. If it is found, reorder +-- pre-base consonant glyph. -- 3 If position is not found, reorder immediately before main consonant. --- UNTESTED: NOT CALLED IN EXAMPLE +-- Here we implement a few handlers: +-- +-- function(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) +-- return head, start, done +-- end function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) local current = start @@ -1320,6 +1294,7 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) removenode(start,start) setlink(start,next) setlink(current,start) + -- setlink(current,start,next) -- maybe start = startnext break end @@ -1337,9 +1312,9 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) if not consonant[char] and getprop(current,a_state) then -- main startnext = getnext(start) removenode(start,start) - local prev = getprev(current) - setlink(prev,start) + setlink(getprev(current),start) setlink(start,current) + -- setlink(getprev(current),start,current) -- maybe start = startnext break end @@ -1352,32 +1327,6 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) return head, start, true end --- function handlers.devanagari_remove_joiners(head,start,kind,lookupname,replacement) --- local stop = getnext(start) --- local font = getfont(start) --- while stop do --- local char = ischar(stop) --- if char and (char == c_zwnj or char == c_zwj) then --- stop = getnext(stop) --- else --- break --- end --- end --- if stop then --- setnext(getprev(stop)) --- setprev(stop,getprev(start)) --- end --- local prev = getprev(start) --- if prev then --- setnext(prev,stop) --- end --- if head == start then --- head = stop --- end --- flush_list(start) --- return head, stop, true --- end - function handlers.devanagari_remove_joiners(head,start,kind,lookupname,replacement) local stop = getnext(start) local font = getfont(start) @@ -1399,13 +1348,13 @@ function handlers.devanagari_remove_joiners(head,start,kind,lookupname,replaceme setnext(prev) end if head == start then - head = stop + head = stop end flush_list(start) return head, stop, true end -local function dev2_initialize(font,attr) +local function initialize_two(font,attr) local devanagari = fontdata[font].resources.devanagari @@ -1420,9 +1369,9 @@ end -- this one will be merged into the caller: it saves a call, but we will then make function -- of the actions -local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pass over (determine stop in sweep) +local function reorder_two(head,start,stop,font,attr,nbspaces) -- maybe do a pass over (determine stop in sweep) - local seqsubset, reorderreph = dev2_initialize(font,attr) + local seqsubset, reorderreph = initialize_two(font,attr) local reph = false -- was nil ... probably went unnoticed because never assigned local halfpos = nil @@ -1433,7 +1382,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa for i=1,#seqsubset do - -- maybe quit if start == stop + -- this can be done more efficient, the last test and less getnext local subset = seqsubset[i] local kind = subset[1] @@ -1452,8 +1401,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa if found[n] then --above-base: rphf Consonant + Halant local afternext = next ~= stop and getnext(next) if afternext and zw_char[getchar(afternext)] then -- ZWJ and ZWNJ prevent creation of reph - current = next - current = getnext(current) + current = afternext -- getnext(next) elseif current == start then setprop(current,a_state,s_rphf) current = next @@ -1589,7 +1537,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa next = getnext(current) local tmp = getnext(next) local changestop = next == stop - setnext(next,nil) + setnext(next) setprop(current,a_state,s_pref) current = processcharacters(current,font) setprop(current,a_state,s_blwf) @@ -1599,10 +1547,9 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa 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) + if show_syntax_errors then + head, current = inject_syntax_error(head,current,char) + end else setnext(current,tmp) -- assumes that result of pref, blwf, or pstf consists of one node if changestop then @@ -1690,9 +1637,9 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa end start = current end - local prev = getprev(halfpos) - setlink(prev,current) + setlink(getprev(halfpos),current) setlink(current,halfpos) + -- setlink(getprev(halfpos),current,halfpos) -- maybe halfpos = current elseif above_mark[char] then -- After main consonant target = basepos @@ -1721,9 +1668,9 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa if current == stop then stop = prev end - local next = getnext(target) - setlink(current,next) + setlink(current,getnext(target)) setlink(target,current) + -- setlink(target,current,getnext(target)) -- maybe end end end @@ -1750,8 +1697,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa if stop == next then stop = current end - local prev = getprev(c) - setlink(prev,next) + setlink(getprev(c),next) local nextnext = getnext(next) setnext(current,nextnext) local nextnextnext = getnext(nextnext) @@ -1999,7 +1945,7 @@ local function analyze_next_chars_two(c,font) if not v then return c end - if v == c_anudatta then + if anudatta[v] then c = n n = getnext(c) if not n then @@ -2098,35 +2044,23 @@ local function analyze_next_chars_two(c,font) end end -local function inject_syntax_error(head,current,mark) - local signal = copy_node(current) - copyinjection(signal,current) - if mark == pre_mark then -- THIS IS WRONG: pre_mark is a table - setchar(signal,dotted_circle) - else - setchar(current,dotted_circle) - end - return insert_node_after(head,current,signal) -end - -- It looks like these two analyzers were written independently but they share -- a lot. Common code has been synced. -function methods.deva(head,font,attr) - head = tonut(head) +local function method_one(head,font,attr) local current = head local start = true local done = false local nbspaces = 0 while current do - local char = ischar(current,font) + 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 + local first = char if n and ra[first] then local second = ischar(n,font) if second and halant[second] then @@ -2158,10 +2092,10 @@ function methods.deva(head,font,attr) end if standalone then -- stand alone cluster (at the start of the word only): #[Ra+H]+NBSP+[N]+[<[]+H+C>]+[{M}+[N]+[H]]+[SM]+[(VD)] - local syllableend = analyze_next_chars_one(c,font,2) - current = getnext(syllableend) + 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) + head, current, nbspaces = reorder_one(head,syllablestart,syllableend,font,attr,nbspaces) current = getnext(current) end else @@ -2267,7 +2201,7 @@ function methods.deva(head,font,attr) end end if syllablestart ~= syllableend then - head, current, nbspaces = deva_reorder(head,syllablestart,syllableend,font,attr,nbspaces) + head, current, nbspaces = reorder_one(head,syllablestart,syllableend,font,attr,nbspaces) current = getnext(current) end elseif independent_vowel[char] then @@ -2289,9 +2223,11 @@ function methods.deva(head,font,attr) end end else - local mark = mark_four[char] - if mark then - head, current = inject_syntax_error(head,current,mark) + if show_syntax_errors then + local mark = mark_four[char] + if mark then + head, current = inject_syntax_error(head,current,char) + end end current = getnext(current) end @@ -2306,16 +2242,13 @@ function methods.deva(head,font,attr) head = replace_all_nbsp(head) end - head = tonode(head) - return head, done end -- there is a good change that when we run into one with subtype < 256 that the rest is also done -- so maybe we can omit this check (it's pretty hard to get glyphs in the stream out of the blue) -function methods.dev2(head,font,attr) - head = tonut(head) +local function method_two(head,font,attr) local current = head local start = true local done = false @@ -2338,7 +2271,7 @@ function methods.dev2(head,font,attr) local nextnextchar = ischar(n,font) if nextnextchar then c = n - char = nextnextchar + char = nextnextchar end end end @@ -2385,14 +2318,14 @@ function methods.dev2(head,font,attr) end end if syllableend and syllablestart ~= syllableend then - head, current, nbspaces = dev2_reorder(head,syllablestart,syllableend,font,attr,nbspaces) + head, current, nbspaces = reorder_two(head,syllablestart,syllableend,font,attr,nbspaces) end - if not syllableend then + if not syllableend and show_syntax_errors 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) + head, current = inject_syntax_error(head,current,char) end end end @@ -2404,10 +2337,10 @@ function methods.dev2(head,font,attr) head = replace_all_nbsp(head) end - head = tonode(head) - return head, done end -methods.mlym = methods.deva -methods.mlm2 = methods.dev2 +for i=1,nofscripts do + methods[scripts_one[i]] = method_one + methods[scripts_two[i]] = method_two +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ota.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ota.lua index b8944e0c0c9..48ec6b976de 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ota.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ota.lua @@ -1,6 +1,6 @@ if not modules then modules = { } end modules ['font-ota'] = { version = 1.001, - comment = "companion to font-otf.lua (analysing)", + 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" @@ -32,7 +32,6 @@ 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 @@ -43,7 +42,6 @@ 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 @@ -72,7 +70,7 @@ local s_isol = 4 local s_blwf = 10 local s_mark = 5 local s_pstf = 11 local s_rest = 6 -local states = { +local states = allocate { init = s_init, medi = s_medi, med2 = s_medi, @@ -89,7 +87,7 @@ local states = { pstf = s_pstf, } -local features = { +local features = allocate { init = s_init, medi = s_medi, med2 = s_medi, @@ -113,6 +111,8 @@ analyzers.useunicodemarks = false -- todo: analyzers per script/lang, cross font, so we need an font id hash -> script -- e.g. latin -> hyphenate, arab -> 1/2/3 analyze -- its own namespace +-- done can go away as can tonut + function analyzers.setstate(head,font) local useunicodemarks = analyzers.useunicodemarks local tfmdata = fontdata[font] @@ -248,12 +248,12 @@ local function warning(current,what) end end -local mappers = { - l = s_init, -- left - d = s_medi, -- double - c = s_medi, -- joiner - r = s_fina, -- right - u = s_isol, -- nonjoiner +local mappers = allocate { + l = s_init, -- left + d = s_medi, -- double + c = s_medi, -- joiner + r = s_fina, -- right + u = s_isol, -- nonjoiner } -- we can also use this trick for devanagari diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otc.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otc.lua index a55320903fb..c2e89599a92 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otc.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otc.lua @@ -1,15 +1,16 @@ if not modules then modules = { } end modules ['font-otc'] = { version = 1.001, - comment = "companion to font-otf.lua (context)", + comment = "companion to font-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -local format, insert, sortedkeys, tohash = string.format, table.insert, table.sortedkeys, table.tohash +local insert, sortedkeys, sortedhash, tohash = table.insert, table.sortedkeys, table.sortedhash, table.tohash local type, next = type, next local lpegmatch = lpeg.match local utfbyte, utflen = utf.byte, utf.len +local sortedhash = table.sortedhash -- we assume that the other otf stuff is loaded already @@ -21,6 +22,10 @@ local otf = fonts.handlers.otf local registerotffeature = otf.features.register local setmetatableindex = table.setmetatableindex +local checkmerge = fonts.helpers.checkmerge +local checkflags = fonts.helpers.checkflags +local checksteps = fonts.helpers.checksteps + local normalized = { substitution = "substitution", single = "substitution", @@ -29,6 +34,7 @@ local normalized = { multiple = "multiple", kern = "kern", pair = "pair", + single = "single", chainsubstitution = "chainsubstitution", chainposition = "chainposition", } @@ -40,6 +46,7 @@ local types = { multiple = "gsub_multiple", kern = "gpos_pair", pair = "gpos_pair", + single = "gpos_single", chainsubstitution = "gsub_contextchain", chainposition = "gpos_contextchain", } @@ -195,13 +202,15 @@ local function addfeature(data,feature,specifications) local stepkey = coverup.stepkey local register = coverup.register - local function prepare_substitution(list,featuretype) + local function prepare_substitution(list,featuretype,nocheck) 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 not nocheck and not description then + skip = skip + 1 + else if type(replacement) == "table" then replacement = replacement[1] end @@ -212,26 +221,24 @@ local function addfeature(data,feature,specifications) else skip = skip + 1 end - else - skip = skip + 1 end end return coverage end - local function prepare_alternate(list,featuretype) + local function prepare_alternate(list,featuretype,nocheck) 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 + if not nocheck and 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 + r[i] = (nocheck or descriptions[u]) and u or unicode end cover(coverage,unicode,r) done = done + 1 @@ -248,19 +255,19 @@ local function addfeature(data,feature,specifications) return coverage end - local function prepare_multiple(list,featuretype) + local function prepare_multiple(list,featuretype,nocheck) 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 + if not nocheck and 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 + if nocheck or descriptions[u] then n = n + 1 r[n] = u end @@ -284,13 +291,15 @@ local function addfeature(data,feature,specifications) return coverage end - local function prepare_ligature(list,featuretype) + local function prepare_ligature(list,featuretype,nocheck) 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 not nocheck and not description then + skip = skip + 1 + else if type(ligature) == "string" then ligature = { lpegmatch(splitter,ligature) } end @@ -298,7 +307,7 @@ local function addfeature(data,feature,specifications) for i=1,#ligature do local l = ligature[i] local u = tounicode(l) - if descriptions[u] then + if nocheck or descriptions[u] then ligature[i] = u else present = false @@ -311,16 +320,23 @@ local function addfeature(data,feature,specifications) else skip = skip + 1 end - else - skip = skip + 1 end end return coverage end + local function resetspacekerns() + -- a bit of a hack, this nil setting but it forces a + -- rehash of the resources needed .. the feature itself + -- should be a kern (at least for now) + data.properties.hasspacekerns = true + data.resources .spacekerns = nil + end + local function prepare_kern(list,featuretype) local coverage = { } local cover = coveractions[featuretype] + local isspace = false for code, replacement in next, list do local unicode = tounicode(code) local description = descriptions[unicode] @@ -330,11 +346,17 @@ local function addfeature(data,feature,specifications) local u = tounicode(k) if u then r[u] = v + if u == 32 then + isspace = true + end end end if next(r) then cover(coverage,unicode,r) done = done + 1 + if unicode == 32 then + isspace = true + end else skip = skip + 1 end @@ -342,6 +364,9 @@ local function addfeature(data,feature,specifications) skip = skip + 1 end end + if isspace then + resetspacekerns() + end return coverage end @@ -358,11 +383,17 @@ local function addfeature(data,feature,specifications) local u = tounicode(k) if u then r[u] = v + if u == 32 then + isspace = true + end end end if next(r) then cover(coverage,unicode,r) done = done + 1 + if unicode == 32 then + isspace = true + end else skip = skip + 1 end @@ -370,22 +401,25 @@ local function addfeature(data,feature,specifications) skip = skip + 1 end end + if isspace then + resetspacekerns() + end else report_otf("unknown cover type %a",featuretype) end return coverage end + local prepare_single = prepare_pair -- we could have a better test on the spec + local function prepare_chain(list,featuretype,sublookups) -- todo: coveractions local rules = list.rules local coverage = { } if rules then - local rulehash = { } - local rulesize = 0 - local sequence = { } - local nofsequences = 0 - local lookuptype = types[featuretype] + local rulehash = { } + local rulesize = 0 + local lookuptype = types[featuretype] for nofrules=1,#rules do local rule = rules[nofrules] local current = rule.current @@ -415,15 +449,30 @@ local function addfeature(data,feature,specifications) 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 + for k, v in sortedhash(lookups) do + local t = type(v) + if t == "table" then + -- already ok + for i=1,#v do + local vi = v[i] + if type(vi) ~= "table" then + v[i] = { vi } + end + end + elseif t == "number" then + local lookup = sublookups[v] + if lookup then + lookups[k] = { lookup } + if not subtype then + subtype = lookup.type + end + elseif v == 0 then + lookups[k] = { { type = "gsub_remove" } } + else + lookups[k] = false -- { false } -- new end else - -- already expanded + lookups[k] = false -- { false } -- new end end end @@ -454,14 +503,17 @@ local function addfeature(data,feature,specifications) replacements, -- 7 subtype, -- 8 } - for unic in next, sequence[start] do +-- for unic in next, sequence[start] do + for unic in sortedhash(sequence[start]) do local cu = coverage[unic] if not cu then coverage[unic] = rulehash -- can now be done cleaner i think end end + sequence.n = nofsequences end end + rulehash.n = rulesize end return coverage end @@ -502,7 +554,7 @@ local function addfeature(data,feature,specifications) local s = sequences[i] local f = s.features if f then - for k in next, f do + for k in sortedhash(f) do -- next, f do if k == position then index = i break @@ -552,12 +604,17 @@ local function addfeature(data,feature,specifications) 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 nocheck = specification.nocheck + local futuresteps = specification.futuresteps 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 + -- + checkflags(specification,resources) + -- if sublookups then local s = { } for i=1,#sublookups do @@ -572,28 +629,36 @@ local function addfeature(data,feature,specifications) local coverage = nil local format = nil if featuretype == "substitution" then - coverage = prepare_substitution(list,featuretype) + coverage = prepare_substitution(list,featuretype,nocheck) elseif featuretype == "ligature" then - coverage = prepare_ligature(list,featuretype) + coverage = prepare_ligature(list,featuretype,nocheck) elseif featuretype == "alternate" then - coverage = prepare_alternate(list,featuretype) + coverage = prepare_alternate(list,featuretype,nocheck) elseif featuretype == "multiple" then - coverage = prepare_multiple(list,featuretype) - elseif featuretype == "kern" then - format = "kern" + coverage = prepare_multiple(list,featuretype,nocheck) + elseif featuretype == "kern" or featuretype == "move" then + format = featuretype coverage = prepare_kern(list,featuretype) elseif featuretype == "pair" then format = "pair" coverage = prepare_pair(list,featuretype) + elseif featuretype == "single" then + format = "single" + coverage = prepare_single(list,featuretype) end if coverage and next(coverage) then nofsteps = nofsteps + 1 steps[nofsteps] = register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) end end + -- + checkmerge(specification) + checksteps(specification) + -- s[i] = { [stepkey] = steps, nofsteps = nofsteps, + flags = featureflags, type = types[featuretype], } end @@ -605,24 +670,28 @@ local function addfeature(data,feature,specifications) local format = nil if featuretype == "substitution" then category = "gsub" - coverage = prepare_substitution(list,featuretype) + coverage = prepare_substitution(list,featuretype,nocheck) elseif featuretype == "ligature" then category = "gsub" - coverage = prepare_ligature(list,featuretype) + coverage = prepare_ligature(list,featuretype,nocheck) elseif featuretype == "alternate" then category = "gsub" - coverage = prepare_alternate(list,featuretype) + coverage = prepare_alternate(list,featuretype,nocheck) elseif featuretype == "multiple" then category = "gsub" - coverage = prepare_multiple(list,featuretype) - elseif featuretype == "kern" then + coverage = prepare_multiple(list,featuretype,nocheck) + elseif featuretype == "kern" or featuretype == "move" then category = "gpos" - format = "kern" + format = featuretype coverage = prepare_kern(list,featuretype) elseif featuretype == "pair" then category = "gpos" format = "pair" coverage = prepare_pair(list,featuretype) + elseif featuretype == "single" then + category = "gpos" + format = "single" + coverage = prepare_single(list,featuretype) elseif featuretype == "chainsubstitution" then category = "gsub" coverage = prepare_chain(list,featuretype,sublookups) @@ -645,6 +714,7 @@ local function addfeature(data,feature,specifications) 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 @@ -659,6 +729,10 @@ local function addfeature(data,feature,specifications) nofsteps = nofsteps, type = steptype, } + -- + checkflags(sequence,resources) + checkmerge(sequence) + checksteps(sequence) -- position | prepend | append local first, last = getrange(sequences,category) inject(specification,sequences,sequence,first,last,category,feature) @@ -708,11 +782,16 @@ function otf.addfeature(name,specification) specification, name = validspecification(specification,name) if name and specification then local slot = knownfeatures[name] - if slot then - -- we overload one .. should be option - else + if not slot then + -- we have a new one slot = #extrafeatures + 1 knownfeatures[name] = slot + elseif specification.overload == false then + -- we add an extre one + slot = #extrafeatures + 1 + knownfeatures[name] = slot + else + -- we overload a previous one end specification.name = name -- to be sure extrafeatures[slot] = specification @@ -735,177 +814,6 @@ otf.enhancers.enhance = enhance otf.enhancers.register("check extra features",enhance) --- tlig -- - -local tlig = { -- we need numbers for some fonts so ... - -- endash = "hyphen hyphen", - -- emdash = "hyphen hyphen hyphen", - [0x2013] = { 0x002D, 0x002D }, - [0x2014] = { 0x002D, 0x002D, 0x002D }, - -- quotedblleft = "quoteleft quoteleft", - -- quotedblright = "quoteright quoteright", - -- quotedblleft = "grave grave", - -- quotedblright = "quotesingle quotesingle", - -- quotedblbase = "comma comma", -} - -local tlig_specification = { - type = "ligature", - features = everywhere, - data = tlig, - order = { "tlig" }, - flags = noflags, - prepend = true, -} - -otf.addfeature("tlig",tlig_specification) - -registerotffeature { - -- this makes it a known feature (in tables) - name = 'tlig', - description = 'tex ligatures', -} - --- trep - -local trep = { - -- [0x0022] = 0x201D, - [0x0027] = 0x2019, - -- [0x0060] = 0x2018, -} - -local trep_specification = { - type = "substitution", - features = everywhere, - data = trep, - order = { "trep" }, - flags = noflags, - prepend = true, -} - -otf.addfeature("trep",trep_specification) - -registerotffeature { - -- this makes it a known feature (in tables) - name = 'trep', - description = 'tex replacements', -} - --- -- tcom (obsolete, was already not set for a while) - --- if characters.combined then --- --- local tcom = { } --- --- local function initialize() --- characters.initialize() --- for first, seconds in next, characters.combined do --- for second, combination in next, seconds do --- tcom[combination] = { first, second } --- end --- end --- -- return false --- end --- --- local tcom_specification = { --- type = "ligature", --- features = everywhere, --- data = tcom, --- order = { "tcom" }, --- flags = noflags, --- initialize = initialize, --- } --- --- otf.addfeature("tcom",tcom_specification) --- --- registerotffeature { --- name = 'tcom', --- description = 'tex combinations', --- } --- --- end - --- anum - -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) -- todo: only when there is already an arab script feature - -registerotffeature { - -- this makes it a known feature (in tables) - name = 'anum', - description = 'arabic digits', -} - --- maybe: - --- fonts.handlers.otf.addfeature("hangulfix",{ --- type = "substitution", --- features = { ["hang"] = { ["*"] = true } }, --- data = { --- [0x1160] = 0x119E, --- }, --- order = { "hangulfix" }, --- flags = { }, --- prepend = true, --- }) - -- fonts.handlers.otf.features.register { -- name = 'hangulfix', -- description = 'fixes for hangul', diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oti.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oti.lua index e10a261cd2b..4c6053be073 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oti.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oti.lua @@ -160,3 +160,103 @@ function otffeatures.checkeddefaultlanguage(featuretype,autolanguage,languages) end end end + +-- the following might become available generic in due time but for now +-- this is some context playground (development code) + +-- if not context then +-- return +-- end + +-- local helpers = otf.readers.helpers +-- local axistofactors = helpers.axistofactors +-- local normalizedaxis = helpers.normalizedaxis +-- local getaxisscale = helpers.getaxisscale +-- local cleanname = containers.cleanname + +-- local function validvariable(tfmdata) +-- if tfmdata.properties.factors then +-- return +-- end +-- local resources = tfmdata.resources +-- local variabledata = resources and resources.variabledata +-- if not variabledata then +-- return +-- end +-- local instances = variabledata.instances +-- local axis = variabledata.axis +-- local segments = variabledata.segments +-- if instances and axis then +-- return instances, axis, segments +-- end +-- end + +-- local function initializeinstance(tfmdata,value) +-- if type(value) == "string" then +-- local instances, axis, segments = validvariable(tfmdata) +-- if instances then +-- local values +-- for i=1,#instances do +-- local instance = instances[i] +-- if cleanname(instance.subfamily) == value then +-- values = instance.values +-- break +-- end +-- end +-- if values then +-- local factors = { } +-- for i=1,#axis do +-- local a = axis[i] +-- factors[i] = getaxisscale(segments,a.minimum,a.default,a.maximum,values[i].value) +-- end +-- tfmdata.properties.instance = { +-- hash = instance, +-- factors = factors, +-- } +-- end +-- else +-- report("incomplete variable data") +-- end +-- end +-- end + +-- local function initializeaxis(tfmdata,value) +-- if type(value) == "string" then +-- local instances, axis, segments = validvariable(tfmdata) +-- if instances then +-- local values = axistofactors(value) +-- if values then +-- local factors = { } +-- for i=1,#axis do +-- local a = axis[i] +-- local d = a.default +-- factors[i] = getaxisscale(segments,a.minimum,d,a.maximum,values[a.name or a.tag] or d) +-- end +-- tfmdata.properties.instance = { +-- hash = cleanname(value), +-- factors = factors, +-- } +-- end +-- else +-- report("incomplete variable data") +-- end +-- end +-- end + +-- registerotffeature { +-- name = "instance", +-- description = "variation instance", +-- initializers = { +-- node = initializeinstance, +-- base = initializeinstance, +-- } +-- } + +-- registerotffeature { +-- name = "axis", +-- description = "variation axis", +-- initializers = { +-- node = initializeaxis, +-- base = initializeaxis, +-- } +-- } diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otj.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otj.lua index 68cf608ec94..8d1b540be0f 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otj.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otj.lua @@ -7,7 +7,7 @@ if not modules then modules = { } end modules ['font-otj'] = { } -- This property based variant is not faster but looks nicer than the attribute one. We --- need to use rawget (which is apbout 4 times slower than a direct access but we cannot +-- need to use rawget (which is about 4 times slower than a direct access but we cannot -- get/set that one for our purpose! This version does a bit more with discretionaries -- (and Kai has tested it with his collection of weird fonts.) @@ -16,31 +16,37 @@ if not modules then modules = { } end modules ['font-otj'] = { -- cleaner to have an identification pass here. Also, I need to keep tracing in mind so -- being too clever here is dangerous. --- The subtype test is not needed as there will be no (new) properties set, given that we --- reset the properties. - -- As we have a rawget on properties we don't need one on injections. --- The use_advance code is just a test and is meant for testing and manuals. There is no +-- The use_advance code was just a test and is meant for testing and manuals. There is no -- performance (or whatever) gain and using kerns is somewhat cleaner (at least for now). --- Maybe: subtype fontkern when pure kerns. +-- An alternative is to have a list per base of all marks and then do a run over the node +-- list that resolves the accumulated l/r/x/y and then do an inject pass. + +-- if needed we can flag a kern node as immutable + +-- The thing with these positioning options is that it is not clear what Uniscribe does with +-- the 2rl flag and we keep oscillating a between experiments. if not nodes.properties then return end -local next, rawget = next, rawget +local next, rawget, tonumber = next, rawget, tonumber local fastcopy = table.fastcopy -local registertracker = trackers.register +local registertracker = trackers.register +local registerdirective = directives.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) --- use_advance is just an experiment: it makes copying glyphs (instead of new_glyph) dangerous - -local use_advance = false directives.register("fonts.injections.advance", function(v) use_advance = v end) +-- local fix_cursive_marks = false +-- +-- registerdirective("fonts.injections.fixcursivemarks", function(v) +-- fix_cursive_marks = v +-- end) local report_injections = logs.reporter("fonts","injections") local report_spaces = logs.reporter("fonts","spaces") @@ -50,6 +56,7 @@ local attributes, nodes, node = attributes, nodes, node fonts = fonts local hashes = fonts.hashes local fontdata = hashes.identifiers +local fontmarks = hashes.marks ----- parameters = fonts.hashes.parameters -- not in generic ----- resources = fonts.hashes.resources -- not in generic @@ -69,92 +76,105 @@ 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 getoffsets = nuts.getoffsets local getboth = nuts.getboth - -local ischar = nuts.is_char - local getdisc = nuts.getdisc local setdisc = nuts.setdisc +local setoffsets = nuts.setoffsets +local ischar = nuts.is_char +local getkern = nuts.getkern +local setkern = nuts.setkern +local setlink = nuts.setlink +local setwidth = nuts.setwidth +local getwidth = nuts.getwidth + +----- traverse_id = nuts.traverse_id +----- traverse_char = nuts.traverse_char +local nextchar = nuts.traversers.char +local nextglue = nuts.traversers.glue -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 fontkern = nuts.pool and nuts.pool.fontkern -- context +local italickern = nuts.pool and nuts.pool.italickern -- context -local nofregisteredkerns = 0 -local nofregisteredpairs = 0 -local nofregisteredmarks = 0 -local nofregisteredcursives = 0 -local keepregisteredcounts = false +local useitalickerns = false + +directives.register("fonts.injections.useitalics", function(v) + if v then + report_injections("using italics for space kerns (tracing only)") + end + useitalickerns = v +end) + +do if not fontkern then -- generic + + local thekern = nuts.new("kern",0) -- fontkern + local setkern = nuts.setkern + local copy_node = nuts.copy_node + + fontkern = function(k) + local n = copy_node(thekern) + setkern(n,k) + return n + end + +end end + +do if not italickern then -- generic + + local thekern = nuts.new("kern",3) -- italiccorrection + local setkern = nuts.setkern + local copy_node = nuts.copy_node + + italickern = function(k) + local n = copy_node(thekern) + setkern(n,k) + return n + end + +end end + +function injections.installnewkern() end -- obsolete + +local nofregisteredkerns = 0 +local nofregisteredpositions = 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 + nofregisteredkerns = 0 + nofregisteredpositions = 0 + nofregisteredmarks = 0 + nofregisteredcursives = 0 + keepregisteredcounts = false end -- We need to make sure that a possible metatable will not kick in unexpectedly. --- function injections.reset(n) --- local p = rawget(properties,n) --- if p and rawget(p,"injections") then --- p.injections = nil --- end --- end - --- function injections.copy(target,source) --- local sp = rawget(properties,source) --- if sp then --- local tp = rawget(properties,target) --- local si = rawget(sp,"injections") --- if si then --- si = fastcopy(si) --- if tp then --- tp.injections = si --- else --- propertydata[target] = { --- injections = si, --- } --- end --- else --- if tp then --- tp.injections = nil --- end --- end --- end --- end - function injections.reset(n) local p = rawget(properties,n) if p then - p.injections = false -- { } + p.injections = false -- { } -- nil should work too as we use rawget else - properties[n] = false -- { injections = { } } + properties[n] = false -- { injections = { } } -- nil should work too as we use rawget end end @@ -162,14 +182,13 @@ function injections.copy(target,source) local sp = rawget(properties,source) if sp then local tp = rawget(properties,target) - -- local si = rawget(sp,"injections") local si = sp.injections if si then si = fastcopy(si) if tp then tp.injections = si else - propertydata[target] = { + properties[target] = { injections = si, } end @@ -188,10 +207,9 @@ function injections.copy(target,source) end end -function injections.setligaindex(n,index) +function injections.setligaindex(n,index) -- todo: don't set when 0 local p = rawget(properties,n) if p then - -- local i = rawget(p,"injections") local i = p.injections if i then i.ligaindex = index @@ -212,7 +230,6 @@ end function injections.getligaindex(n,default) local p = rawget(properties,n) if p then - -- local i = rawget(p,"injections") local i = p.injections if i then return i.ligaindex or default @@ -221,7 +238,15 @@ function injections.getligaindex(n,default) return default end -function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) -- hm: nuts or nodes +function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext,r2lflag) + + -- The standard says something about the r2lflag related to the first in a series: + -- + -- When this bit is set, the last glyph in a given sequence to which the cursive + -- attachment lookup is applied, will be positioned on the baseline. + -- + -- But it looks like we don't need to consider it. + local dx = factor*(exit[1]-entry[1]) local dy = -factor*(exit[2]-entry[2]) local ws = tfmstart.width @@ -239,7 +264,6 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne -- local p = rawget(properties,start) if p then - -- local i = rawget(p,"injections") local i = p.injections if i then i.cursiveanchor = true @@ -257,7 +281,6 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne end local p = rawget(properties,nxt) if p then - -- local i = rawget(p,"injections") local i = p.injections if i then i.cursivex = dx @@ -279,17 +302,19 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne return dx, dy, nofregisteredcursives end -function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2lflag & tfmchr not used - local x = factor*spec[1] - local y = factor*spec[2] - local w = factor*spec[3] - local h = factor*spec[4] +-- kind: 0=single 1=first of pair, 2=second of pair + +function injections.setposition(kind,current,factor,rlmode,spec,injection) + local x = factor * (spec[1] or 0) + local y = factor * (spec[2] or 0) + local w = factor * (spec[3] or 0) + local h = factor * (spec[4] or 0) if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay? local yoffset = y - h local leftkern = x -- both kerns are set in a pair kern compared local rightkern = w - x -- to normal kerns where we set only leftkern if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then - nofregisteredpairs = nofregisteredpairs + 1 + nofregisteredpositions = nofregisteredpositions + 1 if rlmode and rlmode < 0 then leftkern, rightkern = rightkern, leftkern end @@ -298,8 +323,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l end local p = rawget(properties,current) if p then - -- local i = p[injection] - local i = rawget(p,injection) + local i = p[injection] if i then if leftkern ~= 0 then i.leftkern = (i.leftkern or 0) + leftkern @@ -336,15 +360,15 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l }, } end - return x, y, w, h, nofregisteredpairs + return x, y, w, h, nofregisteredpositions end end return x, y, w, h -- no bound end --- This needs checking for rl < 0 but it is unlikely that a r2l script uses kernclasses between --- glyphs so we're probably safe (KE has a problematic font where marks interfere with rl < 0 in --- the previous case) +-- The next one is used for simple kerns coming from a truetype kern table. The r2l +-- variant variant needs checking but it is unlikely that a r2l script uses thsi +-- feature. function injections.setkern(current,factor,rlmode,x,injection) local dx = factor * x @@ -355,8 +379,7 @@ function injections.setkern(current,factor,rlmode,x,injection) injection = "injections" end if p then - -- local i = rawget(p,injection) - local i = rawget(p,injection) + local i = p[injection] if i then i.leftkern = dx + (i.leftkern or 0) else @@ -377,8 +400,62 @@ function injections.setkern(current,factor,rlmode,x,injection) end end -function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark) -- ba=baseanchor, ma=markanchor +-- This one is an optimization of pairs where we have only a "w" entry. This one is +-- potentially different from the previous one wrt r2l. It needs checking. The +-- optimization relates to smaller tma files. + +function injections.setmove(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 rlmode and rlmode < 0 then + -- we need to swap with a single so then we also need to to it here + -- as move is just a simple single + if p then + local i = p[injection] + if i then + i.rightkern = dx + (i.rightkern or 0) + else + p[injection] = { + rightkern = dx, + } + end + else + properties[current] = { + [injection] = { + rightkern = dx, + }, + } + end + else + if p then + local i = 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 + end + return dx, nofregisteredkerns + else + return 0, 0 + end +end +function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark) -- ba=baseanchor, ma=markanchor local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) nofregisteredmarks = nofregisteredmarks + 1 if rlmode >= 0 then @@ -387,15 +464,20 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmar local p = rawget(properties,start) -- hm, dejavu serif does a sloppy mark2mark before mark2base if p then - -- local i = rawget(p,"injections") local i = p.injections if i then if i.markmark then -- out of order mkmk: yes or no or option else - i.markx = dx - i.marky = dy - i.markdir = rlmode or 0 + if dx ~= 0 then + i.markx = dx + end + if y ~= 0 then + i.marky = dy + end + if rlmode then + i.markdir = rlmode + end i.markbase = nofregisteredmarks i.markbasenode = base i.markmark = mkmk @@ -441,7 +523,7 @@ local function show(n,what,nested,symbol) if n then local p = rawget(properties,n) if p then - local i = rawget(p,what) + local i = p[what] if i then local leftkern = i.leftkern or 0 local rightkern = i.rightkern or 0 @@ -483,7 +565,7 @@ end local function showsub(n,what,where) report_injections("begin subrun: %s",where) - for n in traverse_id(glyph_code,n) do + for n in nextchar, n do showchar(n,where) show(n,what,where," ") end @@ -491,8 +573,9 @@ local function showsub(n,what,where) 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) + report_injections() + report_injections("begin run %s: %s kerns, %s positions, %s marks and %s cursives registered", + where or "",nofregisteredkerns,nofregisteredpositions,nofregisteredmarks,nofregisteredcursives) local n = head while n do local id = getid(n) @@ -527,11 +610,12 @@ local function show_result(head) 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")) + local w = getwidth(current) + local x, y = getoffsets(current) + report_injections("char: %C, width %p, xoffset %p, yoffset %p",getchar(current),w,x,y) skipping = false elseif id == kern_code then - report_injections("kern: %p",getfield(current,"kern")) + report_injections("kern: %p",getkern(current)) skipping = false elseif not skipping then report_injections() @@ -539,6 +623,7 @@ local function show_result(head) end current = getnext(current) end + report_injections() end -- G +D-pre G @@ -550,7 +635,6 @@ end -- +D-replace +D-replace local function inject_kerns_only(head,where) - head = tonut(head) if trace_injections then trace(head,"kerns") end @@ -558,7 +642,7 @@ local function inject_kerns_only(head,where) local prev = nil local next = nil local prevdisc = nil - local prevglyph = nil + -- local prevglyph = nil local pre = nil -- saves a lookup local post = nil -- saves a lookup local replace = nil -- saves a lookup @@ -566,96 +650,75 @@ local function inject_kerns_only(head,where) local posttail = nil -- saves a lookup local replacetail = nil -- saves a lookup 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 = rawget(p,"injections") - local i = p.injections - if i then - -- left|glyph|right - 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)) + local char, id = ischar(current) + if char then + local p = rawget(properties,current) + if p then + local i = p.injections + if i then + -- left|glyph|right + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + head = insert_node_before(head,current,fontkern(leftkern)) + 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 + setlink(posttail,fontkern(leftkern)) + done = true end end end - if prevdisc then - local done = false - if post then - -- local i = rawget(p,"postinjections") - 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 + if replace then + local i = p.replaceinjections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + setlink(replacetail,fontkern(leftkern)) + done = true end end - if replace then - -- local i = rawget(p,"replaceinjections") - 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 = rawget(p,"emptyinjections") - local i = p.emptyinjections - if i then - -- glyph|disc|glyph (special case) - local leftkern = i.leftkern - if leftkern and leftkern ~= 0 then - setfield(prev,"replace",newkern(leftkern)) -- maybe also leftkern - end + else + local i = p.emptyinjections + if i then + -- glyph|disc|glyph (special case) + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + setfield(prev,"replace",fontkern(leftkern)) -- maybe also leftkern end end - if done then - setdisc(prevdisc,pre,post,replace) - end + end + if done then + setdisc(prevdisc,pre,post,replace) end end end prevdisc = nil - prevglyph = current + -- prevglyph = current + elseif char == false then + -- other font + prevdisc = nil + -- prevglyph = current elseif id == disc_code then pre, post, replace, pretail, posttail, replacetail = getdisc(current,true) local done = false if pre then -- left|pre glyphs|right - for n in traverse_char(pre) do + for n in nextchar, pre do local p = rawget(properties,n) if p then - -- local i = rawget(p,"injections") or rawget(p,"preinjections") 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 + pre = insert_node_before(pre,n,fontkern(leftkern)) + done = true end end end @@ -663,21 +726,15 @@ local function inject_kerns_only(head,where) end if post then -- left|post glyphs|right - for n in traverse_char(post) do + for n in nextchar, post do local p = rawget(properties,n) if p then - -- local i = rawget(p,"injections") or rawget(p,"postinjections") 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 + post = insert_node_before(post,n,fontkern(leftkern)) + done = true end end end @@ -685,21 +742,15 @@ local function inject_kerns_only(head,where) end if replace then -- left|replace glyphs|right - for n in traverse_char(replace) do + for n in nextchar, replace do local p = rawget(properties,n) if p then - -- local i = rawget(p,"injections") or rawget(p,"replaceinjections") 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 + replace = insert_node_before(replace,n,fontkern(leftkern)) + done = true end end end @@ -708,10 +759,10 @@ local function inject_kerns_only(head,where) if done then setdisc(current,pre,post,replace) end - prevglyph = nil + -- prevglyph = nil prevdisc = current else - prevglyph = nil + -- prevglyph = nil prevdisc = nil end prev = current @@ -723,13 +774,15 @@ local function inject_kerns_only(head,where) else nofregisteredkerns = 0 end - return tonode(head), true + if trace_injections then + show_result(head) + end + return head end -local function inject_pairs_only(head,where) - head = tonut(head) +local function inject_positions_only(head,where) if trace_injections then - trace(head,"pairs") + trace(head,"positions") end local current = head local prev = nil @@ -743,110 +796,110 @@ local function inject_pairs_only(head,where) local posttail = nil -- saves a lookup local replacetail = nil -- saves a lookup 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 = rawget(p,"injections") - local i = p.injections - if i then - -- left|glyph|right - 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)) + local char, id = ischar(current) + if char then + local p = rawget(properties,current) + if p then + local i = p.injections + if i then + -- left|glyph|right + local yoffset = i.yoffset + if yoffset and yoffset ~= 0 then + setoffsets(current,false,yoffset) + end + local leftkern = i.leftkern + local rightkern = i.rightkern + if leftkern and leftkern ~= 0 then + if rightkern and leftkern == -rightkern then + setoffsets(current,leftkern,false) + rightkern = 0 + else + head = insert_node_before(head,current,fontkern(leftkern)) end + end + if rightkern and rightkern ~= 0 then + insert_node_after(head,current,fontkern(rightkern)) + end + else + local i = p.emptyinjections + if i then + -- glyph|disc|glyph (special case) local rightkern = i.rightkern if rightkern and rightkern ~= 0 then - insert_node_after(head,current,newkern(rightkern)) - end - else - -- local i = rawget(p,"emptyinjections") - local i = p.emptyinjections - if i then - -- glyph|disc|glyph (special case) --- is this okay? - local rightkern = i.rightkern - if rightkern and rightkern ~= 0 then - if next and getid(next) == disc_code then - if replace then - -- error, we expect an empty one - else - setfield(next,"replace",newkern(rightkern)) -- maybe also leftkern - end + if next and getid(next) == disc_code then + if replace then + -- error, we expect an empty one + else + setfield(next,"replace",fontkern(rightkern)) -- maybe also leftkern end end end end - if prevdisc then - local done = false - if post then - -- local i = rawget(p,"postinjections") - 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 + 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 + setlink(posttail,fontkern(leftkern)) + done = true end end - if replace then - -- local i = rawget(p,"replaceinjections") - 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 --- new .. okay? - local leftkern = i.leftkern - if leftkern and leftkern ~= 0 then - setfield(prev,"replace",newkern(leftkern)) -- maybe also leftkern - end + end + if replace then + local i = p.replaceinjections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + setlink(replacetail,fontkern(leftkern)) + done = true end end - if done then - setdisc(prevdisc,pre,post,replace) + else + local i = p.emptyinjections + if i then + -- new .. okay? + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + setfield(prev,"replace",fontkern(leftkern)) -- maybe also leftkern + end end end + if done then + setdisc(prevdisc,pre,post,replace) + end end end prevdisc = nil prevglyph = current + elseif char == false then + prevdisc = nil + prevglyph = current elseif id == disc_code then pre, post, replace, pretail, posttail, replacetail = getdisc(current,true) local done = false if pre then -- left|pre glyphs|right - for n in traverse_char(pre) do + for n in nextchar, pre do local p = rawget(properties,n) if p then - -- local i = rawget(p,"injections") or rawget(p,"preinjections") local i = p.injections or p.preinjections if i then local yoffset = i.yoffset if yoffset and yoffset ~= 0 then - setfield(n,"yoffset",yoffset) + setoffsets(n,false,yoffset) end local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - pre = insert_node_before(pre,n,newkern(leftkern)) + pre = insert_node_before(pre,n,fontkern(leftkern)) done = true end local rightkern = i.rightkern if rightkern and rightkern ~= 0 then - insert_node_after(pre,n,newkern(rightkern)) + insert_node_after(pre,n,fontkern(rightkern)) done = true end end @@ -855,24 +908,23 @@ local function inject_pairs_only(head,where) end if post then -- left|post glyphs|right - for n in traverse_char(post) do + for n in nextchar, post do local p = rawget(properties,n) if p then - -- local i = rawget(p,"injections") or rawget(p,"postinjections") local i = p.injections or p.postinjections if i then local yoffset = i.yoffset if yoffset and yoffset ~= 0 then - setfield(n,"yoffset",yoffset) + setoffsets(n,false,yoffset) end local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - post = insert_node_before(post,n,newkern(leftkern)) + post = insert_node_before(post,n,fontkern(leftkern)) done = true end local rightkern = i.rightkern if rightkern and rightkern ~= 0 then - insert_node_after(post,n,newkern(rightkern)) + insert_node_after(post,n,fontkern(rightkern)) done = true end end @@ -881,24 +933,23 @@ local function inject_pairs_only(head,where) end if replace then -- left|replace glyphs|right - for n in traverse_char(replace) do + for n in nextchar, replace do local p = rawget(properties,n) if p then - -- local i = rawget(p,"injections") or rawget(p,"replaceinjections") local i = p.injections or p.replaceinjections if i then local yoffset = i.yoffset if yoffset and yoffset ~= 0 then - setfield(n,"yoffset",yoffset) + setoffsets(n,false,yoffset) end local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - replace = insert_node_before(replace,n,newkern(leftkern)) + replace = insert_node_before(replace,n,fontkern(leftkern)) done = true end local rightkern = i.rightkern if rightkern and rightkern ~= 0 then - insert_node_after(replace,n,newkern(rightkern)) + insert_node_after(replace,n,fontkern(rightkern)) done = true end end @@ -909,13 +960,12 @@ local function inject_pairs_only(head,where) if pre then local p = rawget(properties,prevglyph) if p then - -- local i = rawget(p,"preinjections") local i = p.preinjections if i then -- glyph|pre glyphs local rightkern = i.rightkern if rightkern and rightkern ~= 0 then - pre = insert_node_before(pre,pre,newkern(rightkern)) + pre = insert_node_before(pre,pre,fontkern(rightkern)) done = true end end @@ -924,13 +974,12 @@ local function inject_pairs_only(head,where) if replace then local p = rawget(properties,prevglyph) if p then - -- local i = rawget(p,"replaceinjections") local i = p.replaceinjections if i then -- glyph|replace glyphs local rightkern = i.rightkern if rightkern and rightkern ~= 0 then - replace = insert_node_before(replace,replace,newkern(rightkern)) + replace = insert_node_before(replace,replace,fontkern(rightkern)) done = true end end @@ -953,43 +1002,22 @@ local function inject_pairs_only(head,where) if keepregisteredcounts then keepregisteredcounts = false else - nofregisteredkerns = 0 + nofregisteredpositions = 0 + end + if trace_injections then + show_result(head) end - return tonode(head), true + return head end --- local function showoffset(n,flag) --- local ox = getfield(n,"xoffset") --- local oy = getfield(n,"yoffset") --- if flag then --- if ox == 0 then --- setcolor(n,oy == 0 and "darkgray" or "darkgreen") --- else --- setcolor(n,oy == 0 and "darkblue" or "darkred") --- end --- else --- if ox == 0 then --- setcolor(n,oy == 0 and "gray" or "green") --- else --- setcolor(n,oy == 0 and "blue" or "red") --- end --- end --- 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) + local x, y = getoffsets(n) + if x ~= 0 or y ~= 0 then + setcolor(n,"darkgray") end end local function inject_everything(head,where) - head = tonut(head) if trace_injections then trace(head,"everything") end @@ -998,8 +1026,6 @@ local function inject_everything(head,where) -- local current = head local last = nil - local font = font - local markdata = nil local prev = nil local next = nil local prevdisc = nil @@ -1018,262 +1044,333 @@ local function inject_everything(head,where) local marks = { } local nofmarks = 0 -- + -- local applyfix = hascursives and fix_cursive_marks + -- -- move out -- local function processmark(p,n,pn) -- p = basenode - local px = getfield(p,"xoffset") + local px, py = getoffsets(p) + local nx, ny = getoffsets(n) local ox = 0 local rightkern = nil local pp = rawget(properties,p) if pp then - -- pp = rawget(pp,"injections") pp = pp.injections if pp then rightkern = pp.rightkern end end + local markdir = pn.markdir if rightkern then -- x and w ~= 0 - if pn.markdir < 0 then + ox = px - (pn.markx or 0) - rightkern + if markdir and markdir < 0 then -- kern(w-x) glyph(p) kern(x) mark(n) - ox = px - pn.markx - rightkern - -- report_injections("r2l case 1: %p",ox) + if not pn.markmark then + ox = ox + (pn.leftkern or 0) + end else -- kern(x) glyph(p) kern(w-x) mark(n) - -- ox = px - getfield(p,"width") + pn.markx - pp.leftkern -- -- According to Kai we don't need to handle leftkern here but I'm -- pretty sure I've run into a case where it was needed so maybe -- some day we need something more clever here. -- + -- maybe we need to apply both then + -- if false then - -- a mark with kerning + -- a mark with kerning (maybe husayni needs it ) local leftkern = pp.leftkern if leftkern then - ox = px - pn.markx - leftkern - else - ox = px - pn.markx + ox = ox - leftkern end - else - ox = px - pn.markx end end else - -- if pn.markdir < 0 then - -- ox = px - pn.markx - -- -- report_injections("r2l case 3: %p",ox) - -- else - -- -- ox = px - getfield(p,"width") + pn.markx - ox = px - pn.markx - -- report_injections("l2r case 3: %p",ox) - -- end + ox = px - (pn.markx or 0) + if markdir and markdir < 0 then + if not pn.markmark then + local leftkern = pn.leftkern + if leftkern then + ox = ox + leftkern -- husayni needs it + end + end + end if pn.checkmark then - local wn = getfield(n,"width") -- in arial marks have widths - if wn ~= 0 then + local wn = getwidth(n) -- in arial marks have widths + if wn and wn ~= 0 then wn = wn/2 if trace_injections then report_injections("correcting non zero width mark %C",getchar(n)) end -- -- bad: we should center + -- -- pn.leftkern = -wn -- pn.rightkern = -wn - -- -- we're too late anyway as kerns are already injected so - -- -- we do it the ugly way (no checking if the previous is - -- -- already a kern) .. maybe we should fix the font instead - insert_node_before(n,n,newkern(-wn)) - insert_node_after(n,n,newkern(-wn)) + -- + -- -- we're too late anyway as kerns are already injected so we do it the + -- -- ugly way (no checking if the previous is already a kern) .. maybe we + -- -- should fix the font instead + -- + -- todo: head and check for prev / next kern + -- + insert_node_before(n,n,fontkern(-wn)) + insert_node_after(n,n,fontkern(-wn)) end end end - local oy = getfield(n,"yoffset") + getfield(p,"yoffset") + pn.marky - setfield(n,"xoffset",ox) - setfield(n,"yoffset",oy) + local oy = ny + py + (pn.marky or 0) + if not pn.markmark then + local yoffset = pn.yoffset + if yoffset then + oy = oy + yoffset -- husayni needs it + end + end + setoffsets(n,ox,oy) if trace_marks then showoffset(n,true) end end - -- todo: marks in disc + -- begin of temp fix -- + -- local base = nil -- bah, some arabic fonts have no mark anchoring + -- end of temp fix -- 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 = rawget(p,"injections") - 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 -- cursiveprops - last = current + local char, id = ischar(current) + if char then + local p = rawget(properties,current) + -- begin of temp fix -- + -- if applyfix then + -- if not p then + -- local m = fontmarks[getfont(current)] + -- if m and m[char] then + -- if base then + -- p = { injections = { markbasenode = base } } + -- nofmarks = nofmarks + 1 + -- marks[nofmarks] = current + -- properties[current] = p + -- hasmarks = true + -- end + -- else + -- base = current + -- end + -- end + -- end + -- end of temp fix + if p then + local i = p.injections + -- begin of temp fix -- + -- if applyfix then + -- if not i then + -- local m = fontmarks[getfont(current)] + -- if m and m[char] then + -- if base then + -- i = { markbasenode = base } + -- nofmarks = nofmarks + 1 + -- marks[nofmarks] = current + -- p.injections = i + -- hasmarks = true + -- end + -- else + -- base = current + -- end + -- end + -- end + -- end of temp fix -- + if i then + local pm = i.markbasenode + -- begin of temp fix -- + -- if applyfix then + -- if not pm then + -- local m = fontmarks[getfont(current)] + -- if m and m[char] then + -- if base then + -- pm = base + -- i.markbasenode = pm + -- hasmarks = true + -- end + -- else + -- base = current + -- end + -- else + -- base = current + -- end + -- end + -- end of temp fix -- + if pm then + nofmarks = nofmarks + 1 + marks[nofmarks] = current + else + local yoffset = i.yoffset + if yoffset and yoffset ~= 0 then + setoffsets(current,false,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 = 0 + maxc = maxc + 1 + glyphs[maxc] = cursiveanchor end - elseif maxc > 0 then - local ny = getfield(current,"yoffset") + properties[cursiveanchor].cursivedy = i.cursivey -- cursiveprops + last = current + else + maxc = 0 + end + elseif maxc > 0 then + local nx, ny = getoffsets(current) + for i=maxc,minc,-1 do + local ti = glyphs[i] + ny = ny + properties[ti].cursivedy + setoffsets(ti,false,ny) -- why not add ? + if trace_cursive then + showoffset(ti) + end + end + maxc = 0 + cursiveanchor = nil + end + if i.cursiveanchor then + cursiveanchor = current -- no need for both now + else + if maxc > 0 then + local nx, ny = getoffsets(current) for i=maxc,minc,-1 do local ti = glyphs[i] ny = ny + properties[ti].cursivedy - setfield(ti,"yoffset",ny) -- why not add ? + setoffsets(ti,false,ny) -- why not add ? if trace_cursive then showoffset(ti) end end maxc = 0 - cursiveanchor = nil - end - if i.cursiveanchor then - cursiveanchor = current -- no need for both now - 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) -- why not add ? - if trace_cursive then - showoffset(ti) - end - end - maxc = 0 - end - cursiveanchor = nil end + cursiveanchor = nil end - -- left|glyph|right - 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 + -- left|glyph|right + local leftkern = i.leftkern + local rightkern = i.rightkern + if leftkern and leftkern ~= 0 then + if rightkern and leftkern == -rightkern then + setoffsets(current,leftkern,false) + rightkern = 0 + else + head = insert_node_before(head,current,fontkern(leftkern)) end end - else - -- local i = rawget(p,"emptyinjections") - local i = p.emptyinjections - if i then - -- glyph|disc|glyph (special case) - -- okay? - local rightkern = i.rightkern - if rightkern and rightkern ~= 0 then - if next and getid(next) == disc_code then - if replace then - -- error, we expect an empty one - else - setfield(next,"replace",newkern(rightkern)) -- maybe also leftkern - end + if rightkern and rightkern ~= 0 then + insert_node_after(head,current,fontkern(rightkern)) + end + end + else + local i = p.emptyinjections + if i then + -- glyph|disc|glyph (special case) + local rightkern = i.rightkern + if rightkern and rightkern ~= 0 then + if next and getid(next) == disc_code then + if replace then + -- error, we expect an empty one + else + setfield(next,"replace",fontkern(rightkern)) -- maybe also leftkern end end end end - if prevdisc then - if p then - local done = false - if post then - -- local i = rawget(p,"postinjections") - 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 + 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 + setlink(posttail,fontkern(leftkern)) + done = true end end - if replace then - -- local i = rawget(p,"replaceinjections") - 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 = rawget(p,"emptyinjections") - local i = p.emptyinjections - if i then - local leftkern = i.leftkern - if leftkern and leftkern ~= 0 then - setfield(prev,"replace",newkern(leftkern)) -- maybe also leftkern - end + end + if replace then + local i = p.replaceinjections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + setlink(replacetail,fontkern(leftkern)) + done = true end end - if done then - setdisc(prevdisc,pre,post,replace) + else + local i = p.emptyinjections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + setfield(prev,"replace",fontkern(leftkern)) -- maybe also leftkern + end end end - end - else - -- cursive - 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) -- can be mark + if done then + setdisc(prevdisc,pre,post,replace) end - maxc = 0 - cursiveanchor = nil end end + else + -- cursive + if hascursives and maxc > 0 then + local nx, ny = getoffsets(current) + for i=maxc,minc,-1 do + local ti = glyphs[i] + ny = ny + properties[ti].cursivedy + local xi, yi = getoffsets(ti) + setoffsets(ti,xi,yi + ny) -- can be mark, we could use properties + end + maxc = 0 + cursiveanchor = nil + end end prevdisc = nil prevglyph = current + elseif char == false then + -- base = nil + prevdisc = nil + prevglyph = current elseif id == disc_code then + -- base = nil pre, post, replace, pretail, posttail, replacetail = getdisc(current,true) local done = false if pre then -- left|pre glyphs|right - for n in traverse_char(pre) do + for n in nextchar, pre do local p = rawget(properties,n) if p then - -- local i = rawget(p,"injections") or rawget(p,"preinjections") local i = p.injections or p.preinjections if i then local yoffset = i.yoffset if yoffset and yoffset ~= 0 then - setfield(n,"yoffset",yoffset) + setoffsets(n,false,yoffset) end local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - pre = insert_node_before(pre,n,newkern(leftkern)) + pre = insert_node_before(pre,n,fontkern(leftkern)) done = true end local rightkern = i.rightkern if rightkern and rightkern ~= 0 then - insert_node_after(pre,n,newkern(rightkern)) + insert_node_after(pre,n,fontkern(rightkern)) done = true end if hasmarks then local pm = i.markbasenode if pm then - processmark(pm,current,i) + processmark(pm,n,i) end end end @@ -1282,30 +1379,29 @@ local function inject_everything(head,where) end if post then -- left|post glyphs|right - for n in traverse_char(post) do + for n in nextchar, post do local p = rawget(properties,n) if p then - -- local i = rawget(p,"injections") or rawget(p,"postinjections") local i = p.injections or p.postinjections if i then local yoffset = i.yoffset if yoffset and yoffset ~= 0 then - setfield(n,"yoffset",yoffset) + setoffsets(n,false,yoffset) end local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - post = insert_node_before(post,n,newkern(leftkern)) + post = insert_node_before(post,n,fontkern(leftkern)) done = true end local rightkern = i.rightkern if rightkern and rightkern ~= 0 then - insert_node_after(post,n,newkern(rightkern)) + insert_node_after(post,n,fontkern(rightkern)) done = true end if hasmarks then local pm = i.markbasenode if pm then - processmark(pm,current,i) + processmark(pm,n,i) end end end @@ -1314,30 +1410,29 @@ local function inject_everything(head,where) end if replace then -- left|replace glyphs|right - for n in traverse_char(replace) do + for n in nextchar, replace do local p = rawget(properties,n) if p then - -- local i = rawget(p,"injections") or rawget(p,"replaceinjections") local i = p.injections or p.replaceinjections if i then local yoffset = i.yoffset if yoffset and yoffset ~= 0 then - setfield(n,"yoffset",yoffset) + setoffsets(n,false,yoffset) end local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - replace = insert_node_before(replace,n,newkern(leftkern)) + replace = insert_node_before(replace,n,fontkern(leftkern)) done = true end local rightkern = i.rightkern if rightkern and rightkern ~= 0 then - insert_node_after(replace,n,newkern(rightkern)) + insert_node_after(replace,n,fontkern(rightkern)) done = true end if hasmarks then local pm = i.markbasenode if pm then - processmark(pm,current,i) + processmark(pm,n,i) end end end @@ -1348,13 +1443,12 @@ local function inject_everything(head,where) if pre then local p = rawget(properties,prevglyph) if p then - -- local i = rawget(p,"preinjections") local i = p.preinjections if i then -- glyph|pre glyphs local rightkern = i.rightkern if rightkern and rightkern ~= 0 then - pre = insert_node_before(pre,pre,newkern(rightkern)) + pre = insert_node_before(pre,pre,fontkern(rightkern)) done = true end end @@ -1363,13 +1457,12 @@ local function inject_everything(head,where) if replace then local p = rawget(properties,prevglyph) if p then - -- local i = rawget(p,"replaceinjections") local i = p.replaceinjections if i then -- glyph|replace glyphs local rightkern = i.rightkern if rightkern and rightkern ~= 0 then - replace = insert_node_before(replace,replace,newkern(rightkern)) + replace = insert_node_before(replace,replace,fontkern(rightkern)) done = true end end @@ -1382,6 +1475,7 @@ local function inject_everything(head,where) prevglyph = nil prevdisc = current else + -- base = nil prevglyph = nil prevdisc = nil end @@ -1390,11 +1484,11 @@ local function inject_everything(head,where) end -- cursive if hascursives and maxc > 0 then - local ny = getfield(last,"yoffset") + local nx, ny = getoffsets(last) for i=maxc,minc,-1 do local ti = glyphs[i] ny = ny + properties[ti].cursivedy - setfield(ti,"yoffset",ny) -- why not add ? + setoffsets(ti,false,ny) -- why not add ? if trace_cursive then showoffset(ti) end @@ -1405,7 +1499,6 @@ local function inject_everything(head,where) for i=1,nofmarks do local m = marks[i] local p = rawget(properties,m) - -- local i = rawget(p,"injections") local i = p.injections local b = i.markbasenode processmark(b,m,i) @@ -1417,12 +1510,15 @@ local function inject_everything(head,where) if keepregisteredcounts then keepregisteredcounts = false else - nofregisteredkerns = 0 - nofregisteredpairs = 0 - nofregisteredmarks = 0 - nofregisteredcursives = 0 + nofregisteredkerns = 0 + nofregisteredpositions = 0 + nofregisteredmarks = 0 + nofregisteredcursives = 0 end - return tonode(head), true + if trace_injections then + show_result(head) + end + return head end -- space triggers @@ -1470,21 +1566,49 @@ end injections.getthreshold = getthreshold -function injections.isspace(n,threshold) - if getid(n) == glue_code then - local w = getfield(n,"width") +function injections.isspace(n,threshold,id) + if (id or getid(n)) == glue_code then + local w = getwidth(n) if threshold and w > threshold then -- was >= return 32 end end end +-- We have a plugin so that Kai can use the next in plain. Such a plugin is rather application +-- specific. +-- +-- local getboth = nodes.direct.getboth +-- local getid = nodes.direct.getid +-- local getprev = nodes.direct.getprev +-- local getnext = nodes.direct.getnext +-- +-- local whatsit_code = nodes.nodecodes.whatsit +-- local glyph_code = nodes.nodecodes.glyph +-- +-- local function getspaceboth(n) -- fragile: what it prev/next has no width field +-- local prev, next = getboth(n) +-- while prev and (getid(prev) == whatsit_code or (getwidth(prev) == 0 and getid(prev) ~= glyph_code)) do +-- prev = getprev(prev) +-- end +-- while next and (getid(next) == whatsit_code or (getwidth(next) == 0 and getid(next) ~= glyph_code)) do +-- next = getnext(next) +-- end +-- end +-- +-- injections.installgetspaceboth(getspaceboth) + +local getspaceboth = getboth + +function injections.installgetspaceboth(gb) + getspaceboth = gb or getboth +end + local function injectspaces(head) if not triggers then - return head, false + return head end - local lastfont = nil local spacekerns = nil local leftkerns = nil @@ -1502,10 +1626,10 @@ local function injectspaces(head) 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) + for n in nextglue, head do + local prev, next = getspaceboth(n) + local prevchar = prev and ischar(prev) + local nextchar = next and ischar(next) if nextchar then local font = getfont(next) local trig = triggers[font] @@ -1531,39 +1655,66 @@ local function injectspaces(head) end end if leftkern then - local old = getfield(n,"width") + local old = getwidth(n) 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) + if useitalickerns then + local lnew = leftkern * factor + local rnew = rightkern * factor + if trace_spaces then + report_spaces("%C [%p + %p + %p] %C",prevchar,lnew,old,rnew,nextchar) + end + head = insert_node_before(head,n,italickern(lnew)) + insert_node_after(head,n,italickern(rnew)) + else + local new = old + (leftkern + rightkern) * factor + if trace_spaces then + report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar) + end + setwidth(n,new) end - setfield(n,"width",new) - leftkern = false + rightkern = false else - local new = old + leftkern * factor - if trace_spaces then - report_spaces("%C [%p -> %p]",prevchar,old,new) + if useitalickerns then + local new = leftkern * factor + if trace_spaces then + report_spaces("%C [%p + %p]",prevchar,old,new) + end + insert_node_after(head,n,italickern(new)) -- tricky with traverse but ok + else + local new = old + leftkern * factor + if trace_spaces then + report_spaces("%C [%p -> %p]",prevchar,old,new) + end + setwidth(n,new) end - setfield(n,"width",new) end end leftkern = false elseif rightkern then - local old = getfield(n,"width") + local old = getwidth(n) if old > threshold then - local new = old + rightkern * factor - if trace_spaces then - report_spaces("[%p -> %p] %C",nextchar,old,new) + if useitalickerns then + local new = rightkern * factor + if trace_spaces then + report_spaces("%C [%p + %p]",nextchar,old,new) + end + insert_node_after(head,n,italickern(new)) + else + local new = old + rightkern * factor + if trace_spaces then + report_spaces("[%p -> %p] %C",nextchar,old,new) + end + setwidth(n,new) end - setfield(n,"width",new) end rightkern = false end end triggers = false - return head, true + + return head end -- @@ -1578,17 +1729,17 @@ function injections.handler(head,where) report_injections("injection variant %a","everything") end return inject_everything(head,where) - elseif nofregisteredpairs > 0 then + elseif nofregisteredpositions > 0 then if trace_injections then - report_injections("injection variant %a","pairs") + report_injections("injection variant %a","positions") end - return inject_pairs_only(head,where) + return inject_positions_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 + return head end end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otl.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otl.lua index d4eaed763ba..abb0ebd148c 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otl.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otl.lua @@ -23,7 +23,7 @@ if not modules then modules = { } end modules ['font-otl'] = { -- todo: less tounicodes -local gmatch, find, match, lower, strip = string.gmatch, string.find, string.match, string.lower, string.strip +local lower = string.lower local type, next, tonumber, tostring, unpack = type, next, tonumber, tostring, unpack local abs = math.abs local derivetable = table.derive @@ -52,12 +52,14 @@ local report_otf = logs.reporter("fonts","otf loading") local fonts = fonts local otf = fonts.handlers.otf -otf.version = 3.027 -- beware: also sync font-mis.lua and in mtx-fonts -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.version = 3.104 -- beware: also sync font-mis.lua and in mtx-fonts +otf.cache = containers.define("fonts", "otl", otf.version, true) +otf.svgcache = containers.define("fonts", "svg", otf.version, true) +otf.sbixcache = containers.define("fonts", "sbix", otf.version, true) +otf.pdfcache = containers.define("fonts", "pdf", otf.version, true) otf.svgenabled = false +otf.sbixenabled = false local otfreaders = otf.readers @@ -77,6 +79,8 @@ local cleanup = 0 -- mk: 0=885M 1=765M 2=735M (regular run 730M) local syncspace = true local forcenotdef = false +local privateoffset = fonts.constructors and fonts.constructors.privateoffset or 0xF0000 -- 0x10FFFF + local applyruntimefixes = fonts.treatments and fonts.treatments.applyfixes local wildcard = "*" @@ -93,31 +97,43 @@ registerdirective("fonts.otf.loader.force", function(v) forceload = registerdirective("fonts.otf.loader.syncspace", function(v) syncspace = v end) registerdirective("fonts.otf.loader.forcenotdef", function(v) forcenotdef = v end) --- local function load_featurefile(raw,featurefile) --- if featurefile and featurefile ~= "" then --- if trace_loading then --- report_otf("using featurefile %a", featurefile) --- end --- -- TODO: apply_featurefile(raw, featurefile) --- end --- end - -- otfenhancers.patch("before","migrate metadata","cambria",function() end) registerotfenhancer("check extra features", function() end) -- placeholder -function otf.load(filename,sub,featurefile) -- second argument (format) is gone ! - -- - local featurefile = nil -- not supported (yet) - -- +-- Kai has memory problems on osx so here is an experiment (I only tested on windows as +-- my test mac is old and gets no updates and is therefore rather useless.): + +local checkmemory = utilities.lua and utilities.lua.checkmemory +local threshold = 100 -- MB +local tracememory = false + +registertracker("fonts.otf.loader.memory",function(v) tracememory = v end) + +if not checkmemory then -- we need a generic plug (this code might move): + + local collectgarbage = collectgarbage + + checkmemory = function(previous,threshold) -- threshold in MB + local current = collectgarbage("count") + if previous then + local checked = (threshold or 64)*1024 + if current - previous > checked then + collectgarbage("collect") + current = collectgarbage("count") + end + end + return current + end + +end + +function otf.load(filename,sub,instance) local base = file.basename(file.removesuffix(filename)) - local name = file.removesuffix(base) + local name = file.removesuffix(base) -- already no suffix 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 -- sub can be number of string if sub == "" then sub = false @@ -126,71 +142,30 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone 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 + if instance then + hash = hash .. "-" .. instance end + hash = containers.cleanname(hash) 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 not reload then - -- local featuredata = data.featuredata - -- if featurefiles then - -- if not featuredata or #featuredata ~= #featurefiles then - -- reload = true - -- else - -- for i=1,#featurefiles do - -- local fi, fd = featurefiles[i], featuredata[i] - -- if fi.name ~= fd.name or fi.size ~= fd.size or fi.time ~= fd.time then - -- reload = true - -- break - -- end - -- end - -- end - -- elseif featuredata then - -- reload = true - -- end - -- if reload then - -- report_otf("loading: forced reload due to changed featurefile specification %a",featurefile) - -- end - -- end if reload then report_otf("loading %a, hash %a",filename,hash) -- starttiming(otfreaders) - data = otfreaders.loadfont(filename,sub or 1) -- we can pass the number instead (if it comes from a name search) - -- - -- if featurefiles then - -- for i=1,#featurefiles do - -- load_featurefile(data,featurefiles[i].name) - -- end - -- end - -- - -- + data = otfreaders.loadfont(filename,sub or 1,instance) -- we can pass the number instead (if it comes from a name search) if data then - -- - local resources = data.resources - local svgshapes = resources.svgshapes + -- todo: make this a plugin + local used = checkmemory() + local resources = data.resources + local svgshapes = resources.svgshapes + local sbixshapes = resources.sbixshapes + if cleanup == 0 then + checkmemory(used,threshold,tracememory) + end if svgshapes then resources.svgshapes = nil if otf.svgenabled then @@ -205,18 +180,51 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone timestamp = timestamp, } end + if cleanup > 1 then + collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) + end + end + if sbixshapes then + resources.sbixshapes = nil + if otf.sbixenabled then + local timestamp = os.date() + -- work in progress ... a bit boring to do + containers.write(otf.sbixcache,hash, { + sbixshapes = sbixshapes, + timestamp = timestamp, + }) + data.properties.sbix = { + hash = hash, + timestamp = timestamp, + } + end + if cleanup > 1 then + collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) + end end -- otfreaders.compact(data) + if cleanup == 0 then + checkmemory(used,threshold,tracememory) + end otfreaders.rehash(data,"unicodes") otfreaders.addunicodetable(data) otfreaders.extend(data) + if cleanup == 0 then + checkmemory(used,threshold,tracememory) + end 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") + else + checkmemory(used,threshold,tracememory) end stoptiming(otfreaders) if elapsedtime then @@ -224,10 +232,14 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone end if cleanup > 3 then collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) end data = containers.read(otf.cache,hash) -- this frees the old table and load the sparse one if cleanup > 2 then collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) end else data = nil @@ -243,15 +255,30 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone otfreaders.expand(data) -- inline tables otfreaders.addunicodetable(data) -- only when not done yet -- - otfenhancers.apply(data,filename,data) + otfenhancers.apply(data,filename,data) -- in context one can also use treatments -- -- constructors.addcoreunicodes(data.resources.unicodes) -- still needed ? -- if applyruntimefixes then - applyruntimefixes(filename,data) + applyruntimefixes(filename,data) -- e.g. see treatments.lfg end -- data.metadata.math = data.resources.mathconstants + -- + -- delayed tables (experiment) + -- + local classes = data.resources.classes + if not classes then + local descriptions = data.descriptions + classes = setmetatableindex(function(t,k) + local d = descriptions[k] + local v = (d and d.class or "base") or false + t[k] = v + return v + end) + data.resources.classes = classes + end + -- end return data @@ -313,7 +340,7 @@ local function copytotfm(data,cache_id) end if mathspecs then for unicode, character in next, characters do - local d = descriptions[unicode] + local d = descriptions[unicode] -- we could use parent table here local m = d.math if m then -- watch out: luatex uses horiz_variants for the parts @@ -378,6 +405,7 @@ local function copytotfm(data,cache_id) local fontname = metadata.fontname local fullname = metadata.fullname or fontname local psname = fontname or fullname + local subfont = metadata.subfontindex local units = metadata.units or 1000 -- if units == 0 then -- catch bugs in fonts @@ -472,9 +500,12 @@ local function copytotfm(data,cache_id) properties.fullname = fullname properties.psname = psname properties.name = filename or fullname + properties.subfont = subfont -- -- properties.name = specification.name -- properties.sub = specification.sub + -- + properties.private = properties.private or data.private or privateoffset -- return { characters = characters, @@ -535,7 +566,8 @@ local function otftotfm(specification) local subindex = specification.subindex local filename = specification.filename local features = specification.features.normal - local rawdata = otf.load(filename,sub,features and features.featurefile) + local instance = specification.instance or (features and features.axis) + local rawdata = otf.load(filename,sub,instance) if rawdata and next(rawdata) then local descriptions = rawdata.descriptions rawdata.lookuphash = { } -- to be done @@ -585,7 +617,7 @@ local function checkmathsize(tfmdata,mathsize) local parameters = tfmdata.parameters parameters.scriptpercentage = mathdata.ScriptPercentScaleDown parameters.scriptscriptpercentage = mathdata.ScriptScriptPercentScaleDown - parameters.mathsize = mathsize + parameters.mathsize = mathsize -- only when a number ! end end @@ -795,6 +827,7 @@ otf.coverup = { multiple = justset, kern = justset, pair = justset, + single = justset, ligature = function(coverage,unicode,ligature) local first = ligature[1] local tree = coverage[first] diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oto.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oto.lua index 13568799b75..4b986bd3b66 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oto.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oto.lua @@ -6,10 +6,6 @@ if not modules then modules = { } end modules ['font-oto'] = { -- original tex license = "see context related readme files" } --- This is a version of font-otb adapted to the new fontloader code. We used to have two --- base initialization methods but now we have only one. This means that instead of the --- old default (independent) we now use the one more similar to node node (shared). - 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 @@ -34,6 +30,8 @@ local registerotffeature = otffeatures.register otf.defaultbasealternate = "none" -- first last +local getprivate = fonts.constructors.getprivate + local wildcard = "*" local default = "dflt" @@ -71,21 +69,32 @@ local function cref(feature,sequence) return formatters["feature %a, type %a, chain lookup %a"](feature,sequence.type,sequence.name) end - -local function report_alternate(feature,sequence,descriptions,unicode,replacement,value,comment) - report_prepare("%s: base alternate %s => %s (%S => %S)", - cref(feature,sequence), - gref(descriptions,unicode), - replacement and gref(descriptions,replacement), - value, - comment) +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_substitution(feature,sequence,descriptions,unicode,substitution) - report_prepare("%s: base substitution %s => %S", - cref(feature,sequence), - gref(descriptions,unicode), - gref(descriptions,substitution)) +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) @@ -158,13 +167,11 @@ end -- messy if we need to take that into account. local function makefake(tfmdata,name,present) - local resources = tfmdata.resources - local private = resources.private + local private = getprivate(tfmdata) 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 @@ -225,6 +232,9 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis 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 @@ -237,39 +247,44 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis if kind == "gsub_single" then for i=1,#steps do for unicode, data in next, steps[i].coverage do - -- if not changed[unicode] then -- fails for multiple subs in some math fonts - if trace_singles then - report_substitution(feature,sequence,descriptions,unicode,data) - end + if unicode ~= data then changed[unicode] = data - -- end + 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 - if not changed[unicode] then - local replacement = data[alternate] - if replacement then + local replacement = data[alternate] + if replacement then + if unicode ~= replacement then changed[unicode] = replacement - if trace_alternatives then - report_alternate(feature,sequence,descriptions,unicode,replacement,value,"normal") - end - elseif defaultalt == "first" then - replacement = data[1] + 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 - if trace_alternatives then - report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt) - end - elseif defaultalt == "last" then - replacement = data[#data] - 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 + 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 @@ -289,7 +304,6 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis local nofligatures = #ligatures if nofligatures > 0 then - local characters = tfmdata.characters local present = { } local done = trace_baseinit and trace_ligatures and { } @@ -324,8 +338,9 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist local format = sequence.format if kind == "gpos_pair" then for i=1,#steps do - local step = steps[i] - if step.format == "kern" then + local step = steps[i] + local format = step.format + if format == "kern" or format == "move" then for unicode, data in next, steps[i].coverage do local character = characters[unicode] local kerns = character.kerns @@ -353,9 +368,13 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist 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 + -- kern[2] is true (all zero) or a table + local other = kern[2] + if other == true or (not other and not (kerns and kerns[otherunicode])) then local kern = kern[1] - if kern[1] ~= 0 or kern[2] ~= 0 or kern[4] ~= 0 then + if kern == true then + -- all zero + elseif kern[1] ~= 0 or kern[2] ~= 0 or kern[4] ~= 0 then -- a complex pair not suitable for basemode else kern = kern[3] @@ -385,6 +404,53 @@ local function initializehashes(tfmdata) -- already done end +local function checkmathreplacements(tfmdata,fullname,fixitalics) + 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 fixitalics then + -- quite some warnings on stix ... + local ui = u.italic + if ui and not r.italic then + if trace_preparing then + report_prepare("using %i units of italic correction from %C for %U",ui,unicode,replacement) + end + r.italic = ui -- print(ui,ri) + end + end + 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() @@ -401,6 +467,8 @@ local function featuresinitializer(tfmdata,value) 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 @@ -423,12 +491,14 @@ local function featuresinitializer(tfmdata,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 @@ -437,6 +507,10 @@ local function featuresinitializer(tfmdata,value) end end -- + if substitutionsdone then + checkmathreplacements(tfmdata,fullname,features.fixitalics) + end + -- registerbasehash(tfmdata) end if trace_preparing then diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otr.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otr.lua index a9d3a8b2931..c7438dd4305 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otr.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-otr.lua @@ -65,24 +65,27 @@ if not modules then modules = { } end modules ['font-otr'] = { -- require("char-ini") -- end -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 next, type, tonumber = next, type, tonumber +local byte, lower, char, gsub = string.byte, string.lower, string.char, string.gsub +local floor, round = math.floor, 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 rshift = bit32.rshift local setmetatableindex = table.setmetatableindex local formatters = string.formatters local sortedkeys = table.sortedkeys local sortedhash = table.sortedhash -local stripstring = string.strip +local stripstring = string.nospaces local utf16_to_utf8_be = utf.utf16_to_utf8_be local report = logs.reporter("otf reader") local trace_cmap = false -- only for checking issues +local trace_cmap_detail = false -- only for checking issues + +-- local trace_cmap = true +-- local trace_cmap_detail = true fonts = fonts or { } local handlers = fonts.handlers or { } @@ -92,8 +95,8 @@ handlers.otf = otf local readers = otf.readers or { } otf.readers = readers ------ streamreader = utilities.streams -- faster on big files -local streamreader = utilities.files -- faster on identify +----- streamreader = utilities.streams -- faster on big files (not true any longer) +local streamreader = utilities.files -- faster on identify (also uses less memory) local streamwriter = utilities.files readers.streamreader = streamreader @@ -109,20 +112,56 @@ local readstring = streamreader.readstring local readbyte = streamreader.readcardinal1 -- 8-bit unsigned integer local readushort = streamreader.readcardinal2 -- 16-bit unsigned integer local readuint = streamreader.readcardinal3 -- 24-bit unsigned integer -local readulong = streamreader.readcardinal4 -- 24-bit unsigned integer +local readulong = streamreader.readcardinal4 -- 32-bit unsigned integer ----- readchar = streamreader.readinteger1 -- 8-bit signed integer local readshort = streamreader.readinteger2 -- 16-bit signed integer -local readlong = streamreader.readinteger4 -- 24-bit unsigned integer +local readlong = streamreader.readinteger4 -- 32-bit unsigned integer local readfixed = streamreader.readfixed4 +local read2dot14 = streamreader.read2dot14 -- 16-bit signed fixed number with the low 14 bits of fraction (2.14) (F2DOT14) local readfword = readshort -- 16-bit signed integer that describes a quantity in FUnits local readufword = readushort -- 16-bit unsigned integer that describes a quantity in FUnits local readoffset = readushort -local read2dot14 = streamreader.read2dot14 -- 16-bit signed fixed number with the low 14 bits of fraction (2.14) (F2DOT14) +local readcardinaltable = streamreader.readcardinaltable +local readintegertable = streamreader.readintegertable function streamreader.readtag(f) - return lower(strip(readstring(f,4))) + return lower(stripstring(readstring(f,4))) end +local short = 2 +local ushort = 2 +local ulong = 4 + +directives.register("fonts.streamreader",function() + + streamreader = utilities.streams + + openfile = streamreader.open + closefile = streamreader.close + setposition = streamreader.setposition + skipshort = streamreader.skipshort + readbytes = streamreader.readbytes + readstring = streamreader.readstring + readbyte = streamreader.readcardinal1 + readushort = streamreader.readcardinal2 + readuint = streamreader.readcardinal3 + readulong = streamreader.readcardinal4 + readshort = streamreader.readinteger2 + readlong = streamreader.readinteger4 + readfixed = streamreader.readfixed4 + read2dot14 = streamreader.read2dot14 + readfword = readshort + readufword = readushort + readoffset = readushort + readcardinaltable = streamreader.readcardinaltable + readintegertable = streamreader.readintegertable + + function streamreader.readtag(f) + return lower(stripstring(readstring(f,4))) + end + +end) + -- date represented in number of seconds since 12:00 midnight, January 1, 1904. The value is represented as a -- signed 64-bit integer @@ -134,15 +173,6 @@ end local tableversion = 0.004 readers.tableversion = tableversion local privateoffset = fonts.constructors and fonts.constructors.privateoffset or 0xF0000 -- 0x10FFFF -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 -- We have quite some data tables. We are somewhat ff compatible with names but as I used -- the information from the microsoft site there can be differences. Eventually I might end @@ -174,6 +204,7 @@ local reservednames = { [0] = "wwssubfamily", "lightbackgroundpalette", "darkbackgroundpalette", + "variationspostscriptnameprefix", } -- more at: https://www.microsoft.com/typography/otspec/name.htm @@ -666,12 +697,10 @@ local widths = { setmetatableindex(weights, function(t,k) local r = floor((k + 50) / 100) * 100 local v = (r > 900 and "black") or rawget(t,r) or "normal" --- print("weight:",k,r,v) return v end) setmetatableindex(widths,function(t,k) --- print("width:",k) return "normal" end) @@ -704,6 +733,44 @@ local panosewidths = { -- We implement a reader per table. +-- helper + +local helpers = { } +readers.helpers = helpers + +local function gotodatatable(f,fontdata,tag,criterium) + if criterium and f then + local datatable = fontdata.tables[tag] + if datatable then + local tableoffset = datatable.offset + setposition(f,tableoffset) + return tableoffset + end + end +end + +local function reportskippedtable(f,fontdata,tag,criterium) + if criterium and f then + local datatable = fontdata.tables[tag] + if datatable then + report("loading of table %a skipped",tag) + end + end +end + +local function setvariabledata(fontdata,tag,data) + local variabledata = fontdata.variabledata + if variabledata then + variabledata[tag] = data + else + fontdata.variabledata = { [tag] = data } + end +end + +helpers.gotodatatable = gotodatatable +helpers.setvariabledata = setvariabledata +helpers.reportskippedtable = reportskippedtable + -- The name table is probably the first one to load. After all this one provides -- useful information about what we deal with. The complication is that we need -- to filter the best one available. @@ -719,14 +786,13 @@ local platformnames = { } function readers.name(f,fontdata,specification) - local datatable = fontdata.tables.name - if datatable then - setposition(f,datatable.offset) + local tableoffset = gotodatatable(f,fontdata,"name",true) + if tableoffset then local format = readushort(f) local nofnames = readushort(f) local offset = readushort(f) -- we can also provide a raw list as extra, todo as option - local start = datatable.offset + offset + local start = tableoffset + offset local namelists = { unicode = { }, windows = { }, @@ -747,19 +813,17 @@ function readers.name(f,fontdata,specification) 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 + local index = readushort(f) + local name = reservednames[index] + namelist[#namelist+1] = { + platform = platform, + encoding = encoding, + language = language, + name = name, + index = index, + length = readushort(f), + offset = start + readushort(f), + } else skipshort(f,3) end @@ -783,8 +847,9 @@ function readers.name(f,fontdata,specification) -- -- we need to choose one we like, for instance an unicode one -- - local names = { } - local done = { } + local names = { } + local done = { } + local extras = { } -- -- there is quite some logic in ff ... hard to follow so we start simple -- and extend when we run into it (todo: proper reverse hash) .. we're only @@ -795,7 +860,8 @@ function readers.name(f,fontdata,specification) for i=1,#namelist do local name = namelist[i] local nametag = name.name - if not done[nametag] then + local index = name.index + if not done[nametag or i] then local encoding = name.encoding local language = name.language if (not e or encoding == e) and (not l or language == l) then @@ -808,13 +874,16 @@ function readers.name(f,fontdata,specification) if decoder then content = decoder(content) end - names[nametag] = { - content = content, - platform = platform, - encoding = encoding, - language = language, - } - done[nametag] = true + if nametag then + names[nametag] = { + content = content, + platform = platform, + encoding = encoding, + language = language, + } + end + extras[index] = content + done[nametag or i] = true end end end @@ -827,7 +896,8 @@ function readers.name(f,fontdata,specification) filter("macintosh") filter("unicode") -- - fontdata.names = names + fontdata.names = names + fontdata.extras = extras -- if specification.platformnames then local collected = { } @@ -884,9 +954,8 @@ end -- properties table afterwards. readers["os/2"] = function(f,fontdata) - local datatable = fontdata.tables["os/2"] - if datatable then - setposition(f,datatable.offset) + local tableoffset = gotodatatable(f,fontdata,"os/2",true) + if tableoffset then local version = readushort(f) local windowsmetrics = { version = version, @@ -945,9 +1014,8 @@ readers["os/2"] = function(f,fontdata) end readers.head = function(f,fontdata) - local datatable = fontdata.tables.head - if datatable then - setposition(f,datatable.offset) + local tableoffset = gotodatatable(f,fontdata,"head",true) + if tableoffset then local fontheader = { version = readfixed(f), revision = readfixed(f), @@ -978,68 +1046,60 @@ end -- variables are not used but nofmetrics is quite important. 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), -- two ushorts: major minor - 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 + local tableoffset = gotodatatable(f,fontdata,"hhea",specification.details) + if tableoffset then + fontdata.horizontalheader = { + version = readfixed(f), -- two ushorts: major minor + 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 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), - } --- inspect(fontdata.verticalheader) - else - fontdata.verticalheader = { - nofmetrics = 0, - } - end + local tableoffset = gotodatatable(f,fontdata,"vhea",specification.details) + if tableoffset then + fontdata.verticalheader = { + version = readfixed(f), + 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 @@ -1049,44 +1109,40 @@ end -- fontdata.maximumprofile can be bad 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 + local tableoffset = gotodatatable(f,fontdata,"maxp",specification.details) + if tableoffset then + local version = readfixed(f) + local nofglyphs = readushort(f) + fontdata.nofglyphs = nofglyphs + if version == 0.5 then + fontdata.maximumprofile = { + version = version, + nofglyphs = nofglyphs, + } + 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), + } + else + fontdata.maximumprofile = { + version = version, + nofglyphs = 0, + } end - fontdata.maximumprofile = { - version = version, - nofglyphs = 0, - } end end @@ -1094,87 +1150,78 @@ end -- course). 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 -- advance - 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 - -- if leftsidebearing ~= 0 then - -- glyph.lsb = leftsidebearing - -- end + local tableoffset = gotodatatable(f,fontdata,"hmtx",specification.glyphs) + if tableoffset then + local horizontalheader = fontdata.horizontalheader + local nofmetrics = horizontalheader.nofmetrics + local glyphs = fontdata.glyphs + local nofglyphs = fontdata.nofglyphs + local width = 0 -- advance + 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 - -- The next can happen in for instance a monospace font or in a cjk font - -- with fixed widths. - for i=nofmetrics,nofglyphs-1 do - local glyph = glyphs[i] - if width ~= 0 then - glyph.width = width - end - -- if leftsidebearing ~= 0 then - -- glyph.lsb = leftsidebearing - -- end +-- for now +-- if leftsidebearing ~= 0 then +-- glyph.lsb = leftsidebearing +-- end + end + -- The next can happen in for instance a monospace font or in a cjk font + -- with fixed widths. + for i=nofmetrics,nofglyphs-1 do + local glyph = glyphs[i] + if width ~= 0 then + glyph.width = width end + -- if leftsidebearing ~= 0 then + -- glyph.lsb = leftsidebearing + -- end end + -- hm, there can be a lsb here 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 - -- if topsidebearing ~= 0 then - -- glyph.tsb = topsidebearing - -- end + local tableoffset = gotodatatable(f,fontdata,"vmtx",specification.glyphs) + if tableoffset then + 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 - -- The next can happen in for instance a monospace font or in a cjk font - -- with fixed heights. - for i=nofmetrics,nofglyphs-1 do - local glyph = glyphs[i] - if vheight ~= 0 and vheight ~= vdefault then - glyph.vheight = vheight - end - -- if topsidebearing ~= 0 then - -- glyph.tsb = topsidebearing - -- end + -- if topsidebearing ~= 0 then + -- glyph.tsb = topsidebearing + -- end + end + -- The next can happen in for instance a monospace font or in a cjk font + -- with fixed heights. + for i=nofmetrics,nofglyphs-1 do + local glyph = glyphs[i] + if vheight ~= 0 and vheight ~= vdefault then + glyph.vheight = vheight end + -- if topsidebearing ~= 0 then + -- glyph.tsb = topsidebearing + -- 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 + reportskippedtable(f,fontdata,"vorg",specification.glyphs) end -- The post table relates to postscript (printing) but has some relevant properties for other @@ -1182,9 +1229,8 @@ end -- description is somewhat fuzzy but it is a hybrid with overloads. readers.post = function(f,fontdata,specification) - local datatable = fontdata.tables.post - if datatable then - setposition(f,datatable.offset) + local tableoffset = gotodatatable(f,fontdata,"post",true) + if tableoffset then local version = readfixed(f) fontdata.postscript = { version = version, @@ -1213,7 +1259,7 @@ readers.post = function(f,fontdata,specification) for i=0,nofglyphs-1 do local nameindex = readushort(f) if nameindex >= 258 then - maxnames = maxnames + 1 + maxnames = maxnames + 1 nameindex = nameindex - 257 indices[nameindex] = i else @@ -1226,7 +1272,7 @@ readers.post = function(f,fontdata,specification) report("quit post name fetching at %a of %a: %s",i,maxnames,"no index") break else - local length = readbyte(f) + local length = readbyte(f) if length > 0 then glyphs[mapping].name = readstring(f,length) else @@ -1246,9 +1292,7 @@ readers.post = function(f,fontdata,specification) end readers.cff = function(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("cff") - end + reportskippedtable(f,fontdata,"cff",specification.glyphs) end -- Not all cmaps make sense .. e.g. dfont is obsolete and probably more are not relevant. Let's see @@ -1270,6 +1314,7 @@ local sequence = { -- variants { 0, 5, 14 }, -- last resort ranges +{ 0, 4, 12 }, { 3, 10, 13 }, } @@ -1288,7 +1333,8 @@ local sequence = { local supported = { } for i=1,#sequence do - local sp, se, sf = unpack(sequence[i]) + local si = sequence[i] + local sp, se, sf = si[1], si[2], si[3] local p = supported[sp] if not p then p = { } @@ -1311,34 +1357,18 @@ formatreaders[4] = function(f,fontdata,offset) -- skipshort(f,3) -- searchrange entryselector rangeshift -- - 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) -- 0 - 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 endchars = readcardinaltable(f,nofsegments,ushort) + local reserved = readushort(f) -- 0 + local startchars = readcardinaltable(f,nofsegments,ushort) + local deltas = readcardinaltable(f,nofsegments,ushort) + local offsets = readcardinaltable(f,nofsegments,ushort) -- format length language nofsegments searchrange entryselector rangeshift 4-tables - local size = (length - 2 * 2 - 5 * 2 - 4 * nofsegments * 2) / 2 - for i=1,size-1 do - indices[i] = readushort(f) - end + local size = (length - 2 * 2 - 5 * 2 - 4 * 2 * nofsegments) / 2 + local indices = readcardinaltable(f,size-1,ushort) -- for segment=1,nofsegments do local startchar = startchars[segment] @@ -1352,7 +1382,7 @@ formatreaders[4] = function(f,fontdata,offset) elseif offset == 0xFFFF then -- bad encoding elseif offset == 0 then - if trace_cmap then + if trace_cmap_detail 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 @@ -1385,7 +1415,7 @@ formatreaders[4] = function(f,fontdata,offset) end else local shift = (segment-nofsegments+offset/2) - startchar - if trace_cmap then + if trace_cmap_detail 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 @@ -1435,7 +1465,7 @@ formatreaders[6] = function(f,fontdata,offset) local count = readushort(f) local stop = start+count-1 local nofdone = 0 - if trace_cmap then + if trace_cmap_detail then report("format 6 from %C to %C",2,start,stop) end for unicode=start,stop do @@ -1472,7 +1502,7 @@ formatreaders[12] = function(f,fontdata,offset) local first = readulong(f) local last = readulong(f) local index = readulong(f) - if trace_cmap then + if trace_cmap_detail then report("format 12 from %C to %C starts at index %i",first,last,index) end for unicode=first,last do @@ -1503,7 +1533,7 @@ end formatreaders[13] = function(f,fontdata,offset) -- - -- this fector is only used for simple fallback fonts + -- this vector is only used for simple fallback fonts -- setposition(f,offset+2+2+4+4) -- skip format reserved length language local mapping = fontdata.mapping @@ -1516,7 +1546,7 @@ formatreaders[13] = function(f,fontdata,offset) local last = readulong(f) local index = readulong(f) if first < privateoffset then - if trace_cmap then + if trace_cmap_detail then report("format 13 from %C to %C get index %i",first,last,index) end local glyph = glyphs[index] @@ -1622,76 +1652,82 @@ local function checkcmap(f,fontdata,records,platform,encoding,format) 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) + if trace_cmap then + report("cmap checked: platform %i (%s), encoding %i (%s), format %i, new unicodes %i",platform,p,encoding,e and e[encoding] or "?",format,n) + end 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 = { }, - } + local tableoffset = gotodatatable(f,fontdata,"cmap",specification.glyphs) + if tableoffset then + 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 subtables = record[encoding] - if not subtables then - record[encoding] = { - offsets = { offset }, - formats = { }, - } - else - local offsets = subtables.offsets - offsets[#offsets+1] = offset - end + local offsets = subtables.offsets + offsets[#offsets+1] = offset end end + end + if trace_cmap then 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 "?" + end + for platform, record in sortedhash(records) do + local p = platforms[platform] + local e = encodings[p] + local sp = supported[platform] + local ps = p or "?" + if trace_cmap then 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 "?" + end + for encoding, subtables in sortedhash(record) do + local se = sp and sp[encoding] + local es = e and e[encoding] or "?" + if trace_cmap then 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 + 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 + if trace_cmap then local list = sortedkeys(formats) for i=1,#list do if not (se and se[list[i]]) then @@ -1701,26 +1737,27 @@ function readers.cmap(f,fontdata,specification) 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 + -- + local ok = false + for i=1,#sequence do + local si = sequence[i] + local sp, se, sf = si[1], si[2], si[3] + if checkcmap(f,fontdata,records,sp,se,sf) > 0 then + ok = true end - -- - fontdata.cidmaps = { - version = version, - noftables = noftables, - records = records, - } - else - fontdata.cidmaps = { } 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 @@ -1729,111 +1766,120 @@ end -- although we not need it in our usage (yet). We can remove the locations table when we're done. function readers.loca(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("loca") - end + reportskippedtable(f,fontdata,"loca",specification.glyphs) end function readers.glyf(f,fontdata,specification) -- part goes to cff module - if specification.glyphs then - reportskippedtable("glyf") - end + reportskippedtable(f,fontdata,"glyf",specification.glyphs) end --- Experimental (we need fonts). +-- The MicroSoft variant is pretty clean and is supported (implemented elsewhere) +-- just because I wanted to see how such a font looks like. function readers.colr(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("colr") - end + reportskippedtable(f,fontdata,"colr",specification.glyphs) end - function readers.cpal(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("cpal") - end + reportskippedtable(f,fontdata,"cpal",specification.glyphs) end +-- This one is also supported, if only because I could locate a proper font for +-- testing. + function readers.svg(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("svg") - end + reportskippedtable(f,fontdata,"svg",specification.glyphs) +end + +-- There is a font from apple to test the next one. Will there be more? Anyhow, +-- it's relatively easy to support, so I did it. + +function readers.sbix(f,fontdata,specification) + reportskippedtable(f,fontdata,"sbix",specification.glyphs) +end + +-- I'm only willing to look into the next variant if I see a decent and complete (!) +-- font and more can show up. It makes no sense to waste time on ideas. Okay, the +-- apple font also has these tables. + +function readers.cbdt(f,fontdata,specification) + reportskippedtable(f,fontdata,"cbdt",specification.glyphs) +end +function readers.cblc(f,fontdata,specification) + reportskippedtable(f,fontdata,"cblc",specification.glyphs) +end +function readers.ebdt(f,fontdata,specification) + reportskippedtable(f,fontdata,"ebdt",specification.glyphs) +end +function readers.ebsc(f,fontdata,specification) + reportskippedtable(f,fontdata,"ebsc",specification.glyphs) +end +function readers.eblc(f,fontdata,specification) + reportskippedtable(f,fontdata,"eblc",specification.glyphs) end -- Here we have a table that we really need for later processing although a more advanced gpos table -- can also be available. Todo: we need a 'fake' lookup for this (analogue to ff). 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) - -- bit 8-15 of coverage: format 0 or 2 - local format = bit32.rshift(coverage,8) -- is this ok? - 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 + local tableoffset = gotodatatable(f,fontdata,"kern",specification.kerns) + if tableoffset then + 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) + -- bit 8-15 of coverage: format 0 or 2 + local format = rshift(coverage,8) -- is this ok? + 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 - elseif format == 2 then - report("todo: kern classes") - else - report("todo: kerns") end + elseif format == 2 then + report("todo: kern classes") + else + report("todo: kerns") end end end end function readers.gdef(f,fontdata,specification) - if specification.details then - reportskippedtable("gdef") - end + reportskippedtable(f,fontdata,"gdef",specification.details) end function readers.gsub(f,fontdata,specification) - if specification.details then - reportskippedtable("gsub") - end + reportskippedtable(f,fontdata,"gsub",specification.details) end function readers.gpos(f,fontdata,specification) - if specification.details then - reportskippedtable("gpos") - end + reportskippedtable(f,fontdata,"gpos",specification.details) end function readers.math(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("math") - end + reportskippedtable(f,fontdata,"math",specification.details) end -- Now comes the loader. The order of reading these matters as we need to know -- some properties in order to read following tables. When details is true we also -- initialize the glyphs data. -local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo) +local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo,instancenames) local fontdata = sub and maindata.subfonts and maindata.subfonts[sub] or maindata local names = fontdata.names local info = nil @@ -1858,6 +1904,25 @@ local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo) if not familyname then familyname = family end if not subfamilyname then subfamilyname = subfamily end end + if platformnames then + platformnames = fontdata.platformnames + end + if instancenames then + local variabledata = fontdata.variabledata + if variabledata then + local instances = variabledata and variabledata.instances + if instances then + instancenames = { } + for i=1,#instances do + instancenames[i] = lower(stripstring(instances[i].subfamily)) + end + else + instancenames = nil + end + else + instancenames = nil + end + end info = { -- we inherit some inconsistencies/choices from ff subfontindex = fontdata.subfontindex or sub or 0, -- filename = filename, @@ -1888,7 +1953,8 @@ local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo) capheight = metrics.capheight, -- not always present and probably crap ascender = metrics.typoascender, descender = metrics.typodescender, - platformnames = platformnames and fontdata.platformnames or nil, + platformnames = platformnames or nil, + instancenames = instancenames or nil, } if metricstoo then local keys = { @@ -1946,6 +2012,7 @@ local function loadtables(f,specification,offset) entryselector = readushort(f), -- not needed rangeshift = readushort(f), -- not needed tables = tables, + foundtables = false, } for i=1,fontdata.noftables do local tag = lower(stripstring(readstring(f,4))) @@ -1961,7 +2028,8 @@ local function loadtables(f,specification,offset) length = length, } end - if tables.cff then + fontdata.foundtables = sortedkeys(tables) + if tables.cff or tables.cff2 then fontdata.format = "opentype" else fontdata.format = "truetype" @@ -1982,14 +2050,35 @@ local function prepareglyps(fontdata) fontdata.mapping = { } end +local function readtable(tag,f,fontdata,specification,...) + local reader = readers[tag] + if reader then + -- local t = os.clock() + reader(f,fontdata,specification,...) + -- report("reading table %a took %0.4f seconds",tag,os.clock()-t) + end +end + +local variablefonts_supported = (context and true) or (logs and logs.application and true) or false + local function readdata(f,offset,specification) + local fontdata = loadtables(f,specification,offset) + if specification.glyphs then prepareglyps(fontdata) end - -- - readers["name"](f,fontdata,specification) - -- + + if not variablefonts_supported then + specification.instance = nil + specification.variable = nil + specification.factors = nil + end + + fontdata.temporary = { } + + readtable("name",f,fontdata,specification) + local askedname = specification.askedname if askedname then local fullname = getname(fontdata,"fullname") or "" @@ -1999,35 +2088,124 @@ local function readdata(f,offset,specification) return -- keep searching 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) - -- + + readtable("stat",f,fontdata,specification) + readtable("avar",f,fontdata,specification) + readtable("fvar",f,fontdata,specification) + + if variablefonts_supported then + + local variabledata = fontdata.variabledata + + if variabledata then + local instances = variabledata.instances + local axis = variabledata.axis + if axis and (not instances or #instances == 0) then + instances = { } + variabledata.instances = instances + local function add(n,subfamily,value) + local values = { } + for i=1,#axis do + local a = axis[i] + values[i] = { + axis = a.tag, + value = i == n and value or a.default, + } + end + instances[#instances+1] = { + subfamily = subfamily, + values = values, + } + end + for i=1,#axis do + local a = axis[i] + local tag = a.tag + add(i,"default"..tag,a.default) + add(i,"minimum"..tag,a.minimum) + add(i,"maximum"..tag,a.maximum) + end + -- report("%i fake instances added",#instances) + end + end + + if not specification.factors then + local instance = specification.instance + if type(instance) == "string" then + local factors = helpers.getfactors(fontdata,instance) + if factors then + specification.factors = factors + fontdata.factors = factors + fontdata.instance = instance + report("user instance: %s, factors: % t",instance,factors) + else + report("user instance: %s, bad factors",instance) + end + end + end + + if not fontdata.factors then + if fontdata.variabledata then + local factors = helpers.getfactors(fontdata,true) + if factors then + specification.factors = factors + fontdata.factors = factors + -- report("factors: % t",factors) + -- else + -- report("bad factors") + end + else + -- report("unknown instance") + end + end + + end + + readtable("os/2",f,fontdata,specification) + readtable("head",f,fontdata,specification) + readtable("maxp",f,fontdata,specification) + readtable("hhea",f,fontdata,specification) + readtable("vhea",f,fontdata,specification) + readtable("hmtx",f,fontdata,specification) + readtable("vmtx",f,fontdata,specification) + readtable("vorg",f,fontdata,specification) + readtable("post",f,fontdata,specification) + + readtable("mvar",f,fontdata,specification) + readtable("hvar",f,fontdata,specification) + readtable("vvar",f,fontdata,specification) + + readtable("gdef",f,fontdata,specification) + + readtable("cff" ,f,fontdata,specification) + readtable("cff2",f,fontdata,specification) + + readtable("cmap",f,fontdata,specification) + readtable("loca",f,fontdata,specification) -- maybe load it in glyf + readtable("glyf",f,fontdata,specification) -- loads gvar + + readtable("colr",f,fontdata,specification) + readtable("cpal",f,fontdata,specification) + + readtable("svg" ,f,fontdata,specification) + + readtable("sbix",f,fontdata,specification) + + readtable("cbdt",f,fontdata,specification) + readtable("cblc",f,fontdata,specification) + readtable("ebdt",f,fontdata,specification) + readtable("eblc",f,fontdata,specification) + + readtable("kern",f,fontdata,specification) + readtable("gsub",f,fontdata,specification) + readtable("gpos",f,fontdata,specification) + + readtable("math",f,fontdata,specification) + fontdata.locations = nil fontdata.tables = nil fontdata.cidmaps = nil fontdata.dictionaries = nil - -- fontdata.cff = nil + -- fontdata.cff = nil return fontdata end @@ -2051,12 +2229,9 @@ local function loadfontdata(specification) 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 + local offsets = readcardinaltable(f,nofsubfonts,ulong) if subfont then -- a number of not if subfont >= 1 and subfont <= nofsubfonts then fontdata = readdata(f,offsets[subfont],specification) @@ -2102,7 +2277,7 @@ local function loadfontdata(specification) end end -local function loadfont(specification,n) +local function loadfont(specification,n,instance) if type(specification) == "string" then specification = { filename = specification, @@ -2111,11 +2286,13 @@ local function loadfont(specification,n) glyphs = true, shapes = true, kerns = true, + variable = true, globalkerns = true, lookups = true, -- true or number: subfont = n or true, tounicode = false, + instance = instance } end -- if shapes only then @@ -2131,6 +2308,10 @@ local function loadfont(specification,n) if specification.platformnames then specification.platformnames = true -- not really used any more end + if specification.instance or instance then + specification.variable = true + specification.instance = specification.instance or instance + end local function message(str) report("fatal error in file %a: %s\n%s",specification.filename,str,debug.traceback()) end @@ -2142,11 +2323,14 @@ end -- we need even less, but we can have a 'detail' variant -function readers.loadshapes(filename,n) +function readers.loadshapes(filename,n,instance,streams) local fontdata = loadfont { filename = filename, shapes = true, + streams = streams, + variable = true, subfont = n, + instance = instance, } if fontdata then -- easier on luajit but still we can hit the 64 K stack constants issue @@ -2171,18 +2355,19 @@ function readers.loadshapes(filename,n) } end -function readers.loadfont(filename,n) +function readers.loadfont(filename,n,instance) local fontdata = loadfont { filename = filename, glyphs = true, shapes = false, lookups = true, + variable = true, -- kerns = true, -- globalkerns = true, -- only for testing, e.g. cambria has different gpos and kern subfont = n, + instance = instance, } if fontdata then - -- return { tableversion = tableversion, creator = "context mkiv", @@ -2192,11 +2377,13 @@ function readers.loadfont(filename,n) descriptions = fontdata.descriptions, format = fontdata.format, goodies = { }, - metadata = getinfo(fontdata,n,false,false,true), -- no platformnames here ! + metadata = getinfo(fontdata,n,false,false,true,true), -- no platformnames here ! properties = { hasitalics = fontdata.hasitalics or false, maxcolorclass = fontdata.maxcolorclass, hascolor = fontdata.hascolor or false, + instance = fontdata.instance, + factors = fontdata.factors, }, resources = { -- filename = fontdata.filename, @@ -2215,6 +2402,9 @@ function readers.loadfont(filename,n) mathconstants = fontdata.mathconstants, colorpalettes = fontdata.colorpalettes, svgshapes = fontdata.svgshapes, + sbixshapes = fontdata.sbixshapes, + variabledata = fontdata.variabledata, + foundtables = fontdata.foundtables, }, } end @@ -2226,6 +2416,7 @@ function readers.getinfo(filename,specification) -- string, nil|number|table local subfont = nil local platformnames = false local rawfamilynames = false + local instancenames = true if type(specification) == "table" then subfont = tonumber(specification.subfont) platformnames = specification.platformnames @@ -2237,20 +2428,21 @@ function readers.getinfo(filename,specification) -- string, nil|number|table filename = filename, details = true, platformnames = platformnames, + instancenames = true, -- rawfamilynames = rawfamilynames, } if fontdata then local subfonts = fontdata.subfonts if not subfonts then - return getinfo(fontdata,nil,platformnames,rawfamilynames) + return getinfo(fontdata,nil,platformnames,rawfamilynames,false,instancenames) elseif not subfont then local info = { } for i=1,#subfonts do - info[i] = getinfo(fontdata,i,platformnames,rawfamilynames) + info[i] = getinfo(fontdata,i,platformnames,rawfamilynames,false,instancenames) end return info elseif subfont >= 1 and subfont <= #subfonts then - return getinfo(fontdata,subfont,platformnames,rawfamilynames) + return getinfo(fontdata,subfont,platformnames,rawfamilynames,false,instancenames) else return { filename = filename, diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ots.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ots.lua index 1f84214812f..e3389b9daf7 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ots.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ots.lua @@ -6,73 +6,45 @@ if not modules then modules = { } end modules ['font-ots'] = { -- sequences license = "see context related readme files", } --- to be checked: discrun doesn't seem to do something useful now (except run the --- check again) so if we need it again we'll do a zwnjrun or so - --- components will go away and be replaced by a property table which simplifies --- code (also more efficient) - --- beware, on my development machine we test a slightly a more optimized version - --- assumptions: --- --- cursives don't cross discretionaries --- marks precede bases --- --- pitfalls: --- --- when we append to a dics field we need to set the field in order to update tail --- --- This is a version of font-otn.lua adapted to the new font loader code. It --- is a context version which can contain experimental code, but when we --- have serious patches we will backport to the font-otn files. The plain --- loader that ships with context also uses this now. --- --- todo: looks like we have a leak somewhere (probably in ligatures) --- todo: copy attributes to disc --- todo: get rid of components, better use the tounicode entry if needed (at all) --- --- we do some disc juggling where we need to keep in mind that the --- pre, post and replace fields can have prev pointers to a nesting --- node ... i wonder if that is still needed --- --- not possible: --- --- \discretionary {alpha-} {betagammadelta} --- {\discretionary {alphabeta-} {gammadelta} --- {\discretionary {alphabetagamma-} {delta} --- {alphabetagammadelta}}} - --[[ldx--

This module is a bit more split up that I'd like but since we also want to test with plain it has to be so. This module is part of and discussion about improvements and functionality mostly happens on the mailing list.

-

The specification of OpenType is kind of vague. Apart from a lack of a proper -free specifications there's also the problem that Microsoft and Adobe -may have their own interpretation of how and in what order to apply features. -In general the Microsoft website has more detailed specifications and is a -better reference. There is also some information in the FontForge help files.

+

The specification of OpenType is (or at least a decade ago was) kind of vague. +Apart from a lack of a proper free specifications there's also the problem that +Microsoft and Adobe may have their own interpretation of how and in what order to +apply features. In general the Microsoft website has more detailed specifications +and is a better reference. There is also some information in the FontForge help +files. In the end we rely most on the Microsoft specification.

Because there is so much possible, fonts might contain bugs and/or be made to work with certain rederers. These may evolve over time which may have the side -effect that suddenly fonts behave differently.

+effect that suddenly fonts behave differently. We don't want to catch all font +issues.

-

After a lot of experiments (mostly by Taco, me and Idris) we're now at yet another -implementation. Of course all errors are mine and of course the code can be -improved. There are quite some optimizations going on here and processing speed -is currently acceptable. Not all functions are implemented yet, often because I -lack the fonts for testing. Many scripts are not yet supported either, but I will -look into them as soon as users ask for it.

+

After a lot of experiments (mostly by Taco, me and Idris) the first implementation +becaus quite useful. When it did most of what we wanted, a more optimized version +evolved. Of course all errors are mine and of course the code can be improved. There +are quite some optimizations going on here and processing speed is currently quite +acceptable and has been improved over time. Many complex scripts are not yet supported +yet, but I will look into them as soon as users ask for it.

-

The specification leaves room for interpretation. In case of doubt the microsoft +

The specification leaves room for interpretation. In case of doubt the Microsoft implementation is the reference as it is the most complete one. As they deal with lots of scripts and fonts, Kai and Ivo did a lot of testing of the generic code and their suggestions help improve the code. I'm aware that not all border cases can be taken care of, unless we accept excessive runtime, and even then the interference with other mechanisms (like hyphenation) are not trivial.

+

Especially discretionary handling has been improved much by Kai Eigner who uses complex +(latin) fonts. The current implementation is a compromis between his patches and my code +and in the meantime performance is quite ok. We cannot check all border cases without +compromising speed but so far we're okay. Given good test cases we can probably improve +it here and there. Especially chain lookups are non trivial with discretionaries but +things got much better over time thanks to Kai.

+

Glyphs are indexed not by unicode but in their own way. This is because there is no relationship with unicode at all, apart from the fact that a font might cover certain ranges of characters. One character can have multiple shapes. However, at the @@ -84,81 +56,128 @@ then in the output eventually.

that different from the one produced by but we uses hashes instead. In that table is packed (similar tables are shared) and cached on disk so that successive runs can use the optimized table (after loading the table is -unpacked). The flattening code used later is a prelude to an even more compact table -format (and as such it keeps evolving).

- -

This module is sparsely documented because it is a moving target. The table format -of the reader changes and we experiment a lot with different methods for supporting -features.

+unpacked).

-

As with the code, we may decide to store more information in the - table.

+

This module is sparsely documented because it is has been a moving target. The +table format of the reader changed a bit over time and we experiment a lot with +different methods for supporting features. By now the structures are quite stable

Incrementing the version number will force a re-cache. We jump the number by one -when there's a fix in the library or code that -results in different tables.

+when there's a fix in the reader or processing code that can result in different +results.

+ +

This code is also used outside context but in context it has to work with other +mechanisms. Both put some constraints on the code here.

+ --ldx]]-- +-- Remark: We assume that cursives don't cross discretionaries which is okay because it +-- is only used in semitic scripts. +-- +-- Remark: We assume that marks precede base characters. +-- +-- Remark: When complex ligatures extend into discs nodes we can get side effects. Normally +-- this doesn't happen; ff\d{l}{l}{l} in lm works but ff\d{f}{f}{f}. +-- +-- Todo: check if we copy attributes to disc nodes if needed. +-- +-- Todo: it would be nice if we could get rid of components. In other places we can use +-- the unicode properties. +-- +-- Remark: We do some disc juggling where we need to keep in mind that the pre, post and +-- replace fields can have prev pointers to a nesting node ... I wonder if that is still +-- needed. +-- +-- Remark: This is not possible: +-- +-- \discretionary {alpha-} {betagammadelta} +-- {\discretionary {alphabeta-} {gammadelta} +-- {\discretionary {alphabetagamma-} {delta} +-- {alphabetagammadelta}}} +-- +-- Remark: Something is messed up: we have two mark / ligature indices, one at the +-- injection end and one here ... this is based on KE's patches but there is something +-- fishy there as I'm pretty sure that for husayni we need some connection (as it's much +-- more complex than an average font) but I need proper examples of all cases, not of +-- only some. +-- +-- Remark: I wonder if indexed would be faster than unicoded. It would be a major +-- rewrite to have char being unicode + an index field in glyph nodes. Also more +-- assignments have to be made in order to keep things in sync. So, it's a no-go. +-- +-- Remark: We can provide a fast loop when there are no disc nodes (tests show a 1% +-- gain). Smaller functions might perform better cache-wise. But ... memory becomes +-- faster anyway, so ... +-- +-- Remark: Some optimizations made sense for 5.2 but seem less important for 5.3 but +-- anyway served their purpose. +-- +-- Todo: just (0=l2r and 1=r2l) or maybe (r2l = true) + 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) ------ trace_applied = false registertracker("otf.applied", function(v) trace_applied = v end) -local trace_steps = false registertracker("otf.steps", function(v) trace_steps = v end) -local trace_skips = false registertracker("otf.skips", function(v) trace_skips = v end) -local trace_directions = false registertracker("otf.directions", function(v) trace_directions = v end) -local 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) - ------ zwnjruns = true registerdirective("otf.zwnjruns", function(v) zwnjruns = 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 registertracker = trackers.register + +local logs = logs +local trackers = trackers +local nodes = nodes +local attributes = attributes +local fonts = fonts + +local otf = fonts.handlers.otf +local tracers = nodes.tracers + +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_plugins = false registertracker("otf.plugins", function(v) trace_plugins = v end) +local trace_chains = false registertracker("otf.chains", function(v) trace_chains = v end) + +local trace_kernruns = false registertracker("otf.kernruns", function(v) trace_kernruns = v end) +----- 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 forcediscretionaries = false +local forcepairadvance = false -- for testing + +directives.register("otf.forcediscretionaries",function(v) + forcediscretionaries = v +end) + +directives.register("otf.forcepairadvance",function(v) + forcepairadvance = 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.substitutions", "otf.singles","otf.multiples","otf.alternatives","otf.ligatures") +registertracker("otf.positions", "otf.marks","otf.kerns","otf.cursive") +registertracker("otf.actions", "otf.substitutions","otf.positions") +registertracker("otf.sample", "otf.steps","otf.substitutions","otf.positions","otf.analyzing") +registertracker("otf.sample.silent", "otf.steps=silent","otf.substitutions","otf.positions","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 @@ -178,26 +197,37 @@ local setchar = nuts.setchar local getdisc = nuts.getdisc local setdisc = nuts.setdisc local setlink = nuts.setlink +local getcomponents = nuts.getcomponents -- the original one, not yet node-aux +local setcomponents = nuts.setcomponents -- the original one, not yet node-aux +local getdir = nuts.getdir +local getwidth = nuts.getwidth local ischar = nuts.is_char +local usesfont = nuts.uses_font local insert_node_after = nuts.insert_after local copy_node = nuts.copy local copy_node_list = nuts.copy_list +local remove_node = nuts.remove 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 - +----- traverse_nodes = nuts.traverse +----- traverse_id = nuts.traverse_id +local set_components = nuts.set_components +local take_components = nuts.take_components +local count_components = nuts.count_components +local copy_no_components = nuts.copy_no_components +local copy_only_glyphs = nuts.copy_only_glyphs + +local setmetatable = setmetatable local setmetatableindex = table.setmetatableindex -local zwnj = 0x200C -local zwj = 0x200D -local wildcard = "*" -local default = "dflt" +local nextnode = nuts.traversers.node + +----- zwnj = 0x200C +----- zwj = 0x200D local nodecodes = nodes.nodecodes local glyphcodes = nodes.glyphcodes @@ -213,31 +243,22 @@ local localpar_code = nodecodes.localpar local discretionary_code = disccodes.discretionary local ligature_code = glyphcodes.ligature -local privateattribute = attributes.private - --- Something is messed up: we have two mark / ligature indices, one at the injection --- end and one here ... this is based on KE's patches but there is something fishy --- there as I'm pretty sure that for husayni we need some connection (as it's much --- more complex than an average font) but I need proper examples of all cases, not --- of only some. - -local a_state = privateattribute('state') +local a_state = attributes.private('state') +local a_noligature = attributes.private("noligature") local injections = nodes.injections local setmark = injections.setmark local setcursive = injections.setcursive local setkern = injections.setkern -local setpair = injections.setpair +local setmove = injections.setmove +local setposition = injections.setposition 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 fontdata = fonts.hashes.identifiers +local fontfeatures = fonts.hashes.features local otffeatures = fonts.constructors.features.otf local registerotffeature = otffeatures.register @@ -248,48 +269,51 @@ local getrandom = utilities and utilities.randomizer and utilities.rand otf.defaultnodealternate = "none" -- first last --- We use a few global variables. The handler can be called nested but this assumes that the --- same font is used. Nested calls are normally not needed (only for devanagari). +-- We use a few semi-global variables. The handler can be called nested but this assumes +-- that the same font is used. -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 tfmdata = false +local characters = false +local descriptions = false +local marks = false +local classes = 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 discs = false +local spaces = false -local notmatchpre = { } -local notmatchpost = { } -local notmatchreplace = { } +local sweepnode = nil +local sweephead = { } -- we don't nil entries but false them (no collection and such) -local handlers = { } +local notmatchpre = { } -- to be checked: can we use false instead of nil / what if a == b tests +local notmatchpost = { } -- to be checked: can we use false instead of nil / what if a == b tests +local notmatchreplace = { } -- to be checked: can we use false instead of nil / what if a == b tests -local isspace = injections.isspace -local getthreshold = injections.getthreshold +local handlers = { } --- we use this for special testing and documentation +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 checkstep = (tracers and tracers.steppers.check) or function() end +local registerstep = (tracers and tracers.steppers.register) or function() end +local registermessage = (tracers and 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 checkdisccontent(d) +-- local pre, post, replace = getdisc(d) +-- if pre then for n in traverse_id(glue_code,pre) do report("pre: %s",nodes.idstostring(pre)) break end end +-- if post then for n in traverse_id(glue_code,post) do report("pos: %s",nodes.idstostring(post)) break end end +-- if replace then for n in traverse_id(glue_code,replace) do report("rep: %s",nodes.idstostring(replace)) break end end +-- end local function logprocess(...) if trace_steps then registermessage(...) + if trace_steps == "silent" then + return + end end report_direct(...) end @@ -298,59 +322,77 @@ local function logwarning(...) report_direct(...) end -local f_unicode = formatters["%U"] -local f_uniname = formatters["%U (%s)"] -local f_unilist = formatters["% t (% t)"] +local gref do -local function gref(n) -- currently the same as in font-otb - 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 -- later we will start at 2 - local di = descriptions[ni] - num[i] = f_unicode(ni) - nam[i] = di and di.name or "-" + local f_unicode = formatters["U+%X"] -- was ["%U"] + local f_uniname = formatters["U+%X (%s)"] -- was ["%U (%s)"] + local f_unilist = formatters["% t"] + + gref = function(n) -- currently the same as in font-otb + 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 t = { } + for i=1,#n do + local ni = n[i] + if tonumber(ni) then -- later we will start at 2 + local di = descriptions[ni] + local nn = di and di.name + if nn then + t[#t+1] = f_uniname(ni,nn) + else + t[#t+1] = f_unicode(ni) + end + end + end + return f_unilist(t) + else + return "" end - return f_unilist(num,nam) - else - return "" 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) + end + local merged = sequence.merged and "merged " or "" + if index then + return formatters["feature %a, type %a, %schain lookup %a, index %a"]( + dataset[4],sequence.type,merged,sequence.name,index) else - return formatters["feature %a, type %a, chain lookup %a"](dataset[4],sequence.type,sequence.name) + return formatters["feature %a, type %a, %schain lookup %a"]( + dataset[4],sequence.type,merged,sequence.name) end end local function pref(dataset,sequence) - return formatters["feature %a, type %a, lookup %a"](dataset[4],sequence.type,sequence.name) + return formatters["feature %a, type %a, %slookup %a"]( + dataset[4],sequence.type,sequence.merged and "merged " or "",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 + if not rlmode or rlmode >= 0 then return "l2r" + else + return "r2l" end end +-- The next code is somewhat complicated by the fact that some fonts can have ligatures made +-- from ligatures that themselves have marks. This was identified by Kai in for instance +-- arabtype: KAF LAM SHADDA ALEF FATHA (0x0643 0x0644 0x0651 0x0627 0x064E). This becomes +-- KAF LAM-ALEF with a SHADDA on the first and a FATHA op de second component. In a next +-- iteration this becomes a KAF-LAM-ALEF with a SHADDA on the second and a FATHA on the +-- third component. + -- We can assume that languages that use marks are not hyphenated. We can also assume -- that at most one discretionary is present. @@ -360,21 +402,6 @@ end -- However, for arabic we need to keep them around for the sake of mark placement -- and indices. -local function copy_glyph(g) -- next and prev are untouched ! - local components = getfield(g,"components") - if components then - setfield(g,"components") - local n = copy_node(g) - copyinjection(n,g) -- we need to preserve the lig indices - setfield(g,"components",components) - return n - else - local n = copy_node(g) - copyinjection(n,g) -- we need to preserve the lig indices - return n - end -end - local function flattendisk(head,disc) local pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true) local prev, next = getboth(disc) @@ -396,7 +423,7 @@ local function flattendisk(head,disc) elseif next then return next, next else - return -- maybe warning + -- return -- maybe warning end else if replace then @@ -419,18 +446,40 @@ local function appenddisc(disc,list) if post then setlink(posttail,posthead) else - post = phead + post = posthead end if replace then setlink(replacetail,replacehead) else - replace = rhead + replace = replacehead end setdisc(disc,pre,post,replace) end -- start is a mark and we need to keep that one +local take_components = getcomponents -- we overload here (for now) +local set_components = setcomponents -- we overload here (for now) +----- get_components = getcomponents -- we overload here (for now) + +local function count_components(start,marks) + if getid(start) ~= glyph_code then + return 0 + elseif getsubtype(start) == ligature_code then + local i = 0 + local components = getcomponents(start) + while components do + i = i + count_components(components,marks) + components = getnext(components) + end + return i + elseif not marks[getchar(start)] then + return 1 + else + return 0 + end +end + local function markstoligature(head,start,stop,char) if start == stop and getchar(start) == char then return head, start @@ -439,48 +488,28 @@ local function markstoligature(head,start,stop,char) local next = getnext(stop) setprev(start) setnext(stop) - local base = copy_glyph(start) + local base = copy_no_components(start,copyinjection) 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) + set_components(base,start) + setlink(prev,base,next) return head, base end end --- The next code is somewhat complicated by the fact that some fonts can have ligatures made --- from ligatures that themselves have marks. This was identified by Kai in for instance --- arabtype: KAF LAM SHADDA ALEF FATHA (0x0643 0x0644 0x0651 0x0627 0x064E). This becomes --- KAF LAM-ALEF with a SHADDA on the first and a FATHA op de second component. In a next --- iteration this becomes a KAF-LAM-ALEF with a SHADDA on the second and a FATHA on the --- third component. - -local function getcomponentindex(start) -- we could store this offset in the glyph (nofcomponents) - if getid(start) ~= glyph_code then -- and then get rid of all components - 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") +-- Remark for Kai: (some arabic fonts do mark + mark = other mark and such) +-- +-- The hasmarks is needed for ligatures of marks that are part of a ligature in +-- which case we assume that we can delete the marks anyway (we can always become +-- more clever if needed) .. in fact the whole logic here should be redone. We're +-- in the not discfound branch then. We now have skiphash too so we can be more +-- selective if needed (todo). -local function toligature(head,start,stop,char,dataset,sequence,markflag,discfound) -- brr head +local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfound,hasmarks) -- brr head if getattr(start,a_noligature) == 1 then -- so we can do: e\noligature{ff}e e\noligature{f}fie (we only look at the first) return head, start @@ -490,53 +519,43 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou setchar(start,char) return head, start end - -- needs testing (side effects): - local components = getfield(start,"components") - if components then - -- we get a double free .. needs checking - -- flush_node_list(components) - end - -- local prev = getprev(start) local next = getnext(stop) local comp = start setprev(start) setnext(stop) - local base = copy_glyph(start) + local base = copy_no_components(start,copyinjection) if start == head then head = base end resetinjection(base) setchar(base,char) setsubtype(base,ligature_code) - setfield(base,"components",comp) -- start can have components ... do we need to flush? - if prev then - setnext(prev,base) - end - if next then - setprev(next,base) - end - setboth(base,prev,next) + set_components(base,comp) + setlink(prev,base,next) if not discfound then - local deletemarks = markflag ~= "mark" + local deletemarks = not skiphash or hasmarks local components = start local baseindex = 0 local componentindex = 0 local head = base local current = base - -- first we loop over the glyphs in start .. stop + -- first we loop over the glyphs in start ... stop while start do local char = getchar(start) if not marks[char] then baseindex = baseindex + componentindex - componentindex = getcomponentindex(start) - elseif not deletemarks then -- quite fishy + componentindex = count_components(start,marks) + -- we can be more clever here: "not deletemarks or (skiphash and not skiphash[char])" + -- and such: + elseif not deletemarks then + -- we can get a loop when the font expects otherwise (i.e. unexpected deletemarks) 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) + copyinjection(n,start) -- is this ok ? we position later anyway head, current = insert_node_after(head,current,n) -- unlikely that mark has components elseif trace_marks then logwarning("%s: delete mark %s",pref(dataset,sequence),gref(char)) @@ -544,11 +563,11 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou start = getnext(start) end -- we can have one accent as part of a lookup and another following - -- local start = components -- was wrong (component scanning was introduced when more complex ligs in devanagari was added) local start = getnext(current) while start do local char = ischar(start) if char then + -- also something skiphash here? if marks[char] then setligaindex(start,baseindex + getligaindex(start,componentindex)) if trace_marks then @@ -570,27 +589,14 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou -- \- can give problems as there we can have a negative char but that won't match -- anyway local pre, post, replace, pretail, posttail, replacetail = getdisc(discfound,true) - if not replace then -- todo: signal simple hyphen - 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) -- also blocks funny assignments - setnext(discprev) -- also blocks funny assignments + if not replace then + local prev = getprev(base) + local comp = take_components(base) + local copied = copy_only_glyphs(comp) if pre then setlink(discprev,pre) + else + setnext(discprev) -- also blocks funny assignments end pre = comp if post then @@ -598,20 +604,26 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou setprev(post) else post = discnext + setprev(discnext) -- also blocks funny assignments end - setlink(prev,discfound) - setlink(discfound,next) + setlink(prev,discfound,next) setboth(base) - setfield(base,"components",copied) - setdisc(discfound,pre,post,base) -- was discretionary_code - base = prev -- restart + -- here components have a pointer so we can't free it! + set_components(base,copied) + replace = base + if forcediscretionaries then + setdisc(discfound,pre,post,replace,discretionary_code) + else + setdisc(discfound,pre,post,replace) + end + base = prev end end end return head, base end -local function multiple_glyphs(head,start,multiple,ignoremarks,what) +local function multiple_glyphs(head,start,multiple,skiphash,what,stop) -- what to do with skiphash matches here local nofmultiples = #multiple if nofmultiples > 0 then resetinjection(start) @@ -654,7 +666,11 @@ end local function get_alternative_glyph(start,alternatives,value) local n = #alternatives - if value == "random" then + if n == 1 then + -- we could actually change that into a gsub and save some memory in the + -- font loader but it makes tracing more messy + return alternatives[1], trace_alternatives and "1 (only one present)" + elseif 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 @@ -719,21 +735,25 @@ function handlers.gsub_alternate(head,start,dataset,sequence,alternative) return head, start, true end -function handlers.gsub_multiple(head,start,dataset,sequence,multiple) +function handlers.gsub_multiple(head,start,dataset,sequence,multiple,rlmode,skiphash) 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]) + return multiple_glyphs(head,start,multiple,skiphash,dataset[1]) end -function handlers.gsub_ligature(head,start,dataset,sequence,ligature) +-- Don't we deal with disc otherwise now? I need to check if the next one can be +-- simplified. Anyway, it can be way messier: marks that get removed as well as +-- marks that are kept. + +function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skiphash) 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 + if skiphash and skiphash[startchar] then while current do local char = ischar(current,currentfont) if char then @@ -764,15 +784,16 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) -- ok, goto next lookup end end - else - local skipmark = sequence.flags[1] + else -- is the check for disc still valid here ? and why only replace then local discfound = false local lastdisc = nil + local hasmarks = marks[startchar] while current do local char, id = ischar(current,currentfont) if char then - if skipmark and marks[char] then + if skiphash and skiphash[char] then current = getnext(current) + -- if stop then stop = current end -- ? else -- ligature is a tree local lg = ligature[char] -- can there be multiple in a row? maybe in a bad font if lg then @@ -780,6 +801,9 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) discfound = lastdisc lastdisc = nil end + if marks[char] then + hasmarks = true + end stop = current -- needed for fake so outside then ligature = lg current = getnext(current) @@ -791,8 +815,20 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) -- kind of weird break elseif id == disc_code then - -- tricky .. we also need to do pre here - local replace = getfield(current,"replace") + -- + -- Kai: see chainprocs, we probably could do the same here or was there a reason + -- why we kept the replace check here. + -- + -- if not discfound then + -- discfound = current + -- end + -- if current == stop then + -- break -- okay? or before the disc + -- else + -- current = getnext(current) + -- end + -- + local replace = getfield(current,"replace") -- hm: pre and post if replace then -- of{f-}{}{f}e o{f-}{}{f}fe o{-}{}{ff}e (oe and ff ligature) -- we can end up here when we have a start run .. testruns start at a disc but @@ -802,6 +838,9 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) if char then local lg = ligature[char] -- can there be multiple in a row? maybe in a bad font if lg then + if marks[char] then + hasmarks = true -- very unlikely + end ligature = lg replace = getnext(replace) else @@ -824,10 +863,10 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) if stop then if trace_ligatures then local stopchar = getchar(stop) - head, start = toligature(head,start,stop,lig,dataset,sequence,skipmark,discfound) + head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) 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) + head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) end else -- weird but happens (in some arabic font) @@ -845,84 +884,81 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) return head, start, false, discfound end -function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,step,i,injection) +function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection) local startchar = getchar(start) - if step.format == "pair" then - local dx, dy, w, h = setpair(start,factor,rlmode,sequence.flags[4],kerns,injection) + local format = step.format + if format == "single" or type(kerns) == "table" then -- the table check can go + local dx, dy, w, h = setposition(0,start,factor,rlmode,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) + logprocess("%s: shifting single %s by %s xy (%p,%p) and wh (%p,%p)",pref(dataset,sequence),gref(startchar),format,dx,dy,w,h) end else - -- needs checking .. maybe no kerns format for single - local k = setkern(start,factor,rlmode,kerns,injection) + local k = (format == "move" and setmove or setkern)(start,factor,rlmode,kerns,injection) if trace_kerns then - logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(startchar),k) + logprocess("%s: shifting single %s by %s %p",pref(dataset,sequence),gref(startchar),format,k) end end - return head, start, false + return head, start, true end -function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,injection) +function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,skiphash,step,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 + if skiphash and skiphash[nextchar] then -- includes marks too when flag + 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 - -- this permits a mixed table, but we could also decide to optimize this - -- in the loader and use format 'kern' - 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) + else + local krn = kerns[nextchar] + if not krn then + break + end + local format = step.format + if format == "pair" then + local a, b = krn[1], krn[2] + if a == true then + -- zero + elseif a then -- #a > 0 + local x, y, w, h = setposition(1,start,factor,rlmode,a,injection) if trace_kerns then - logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(nextchar),k) + local startchar = getchar(start) + logprocess("%s: shifting first of pair %s and %s by xy (%p,%p) and wh (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections") 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") + if b == true then + -- zero + start = snext -- cf spec + elseif b then -- #b > 0 + local x, y, w, h = setposition(2,snext,factor,rlmode,b,injection) + if trace_kerns then + local startchar = getchar(start) + logprocess("%s: shifting second of pair %s and %s by xy (%p,%p) and wh (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections") + end + start = snext -- cf spec + elseif forcepairadvance then + start = snext -- for testing, not cf spec end - end - if b and #b > 0 then - local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,injection) + return head, start, true + elseif krn ~= 0 then + local k = (format == "move" and setmove or setkern)(snext,factor,rlmode,krn,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") + logprocess("%s: inserting %s %p between %s and %s as %s",pref(dataset,sequence),format,k,gref(getchar(prev)),gref(nextchar),injection or "injections") end + return head, start, true + else -- can't happen + break 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 -- can't happen - break end else break end end - return head, start, done + return head, start, false end end @@ -931,7 +967,7 @@ end we need to explicitly test for basechar, baselig and basemark entries.

--ldx]]-- -function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode) +function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode,skiphash) local markchar = getchar(start) if marks[markchar] then local base = getprev(start) -- [glyph] [start=mark] @@ -966,10 +1002,13 @@ function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode) 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) + logprocess("%s, bound %s, anchoring mark %s to basechar %s => (%p,%p)", + pref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) end return head, start, true + elseif trace_bugs then + -- onetimemessage(currentfont,basechar,"no base anchors",report_fonts) + logwarning("%s: mark %s is not anchored to %s",pref(dataset,sequence),gref(markchar),gref(basechar)) end elseif trace_bugs then logwarning("%s: nothing preceding, case %i",pref(dataset,sequence),1) @@ -983,7 +1022,7 @@ function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode) return head, start, false end -function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlmode) +function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlmode,skiphash) local markchar = getchar(start) if marks[markchar] then local base = getprev(start) -- [glyph] [optional marks] [start=mark] @@ -1022,8 +1061,8 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm 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) + logprocess("%s, index %s, bound %s, anchoring mark %s to baselig %s at index %s => (%p,%p)", + pref(dataset,sequence),index,bound,gref(markchar),gref(basechar),index,dx,dy) end return head, start, true else @@ -1048,7 +1087,7 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm return head, start, false end -function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode) +function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode,skiphash) local markchar = getchar(start) if marks[markchar] then local base = getprev(start) -- [glyph] [basemark] [start=mark] @@ -1071,8 +1110,8 @@ function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode) 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) + logprocess("%s, bound %s, anchoring mark %s to basemark %s => (%p,%p)", + pref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) end return head, start, true end @@ -1084,8 +1123,7 @@ function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode) return head, start, false end -function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,step,i) -- to be checked - local done = false +function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,skiphash,step) -- to be checked local startchar = getchar(start) if marks[startchar] then if trace_cursive then @@ -1093,12 +1131,11 @@ function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,st end else local nxt = getnext(start) - while not done and nxt do + while nxt do local nextchar = ischar(nxt,currentfont) if not nextchar then break - elseif marks[nextchar] then - -- should not happen (maybe warning) + elseif marks[nextchar] then -- always sequence.flags[1] nxt = getnext(nxt) else local exit = exitanchors[3] @@ -1107,11 +1144,12 @@ function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,st if entry then entry = entry[2] if entry then - local dx, dy, bound = setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + local r2lflag = sequence.flags[4] -- mentioned in the standard + local dx, dy, bound = setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar],r2lflag) 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)) + logprocess("%s: moving %s to %s cursive (%p,%p) using bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,bound,mref(rlmode)) end - done = true + return head, start, true end end end @@ -1119,7 +1157,7 @@ function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,st end end end - return head, start, done + return head, start, false end --[[ldx-- @@ -1132,6 +1170,9 @@ local chainprocs = { } local function logprocess(...) if trace_steps then registermessage(...) + if trace_steps == "silent" then + return + end end report_subchain(...) end @@ -1141,6 +1182,9 @@ local logwarning = report_subchain local function logprocess(...) if trace_steps then registermessage(...) + if trace_steps == "silent" then + return + end end report_chain(...) end @@ -1154,7 +1198,7 @@ local logwarning = report_chain -- in a bit weird way. There is no lookup and the replacement comes from the lookup -- itself. It is meant mostly for dealing with Urdu. -local function reversesub(head,start,stop,dataset,sequence,replacements,rlmode) +local function reversesub(head,start,stop,dataset,sequence,replacements,rlmode,skiphash) local char = getchar(start) local replacement = replacements[char] if replacement then @@ -1196,66 +1240,76 @@ as less as needed but that would also make the code even more messy.

-- this is messy: do we need this disc checking also in alternaties? +local function reportzerosteps(dataset,sequence) + logwarning("%s: no steps",cref(dataset,sequence)) +end + 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 function reportbadsteps(dataset,sequence) +-- logwarning("%s: bad step, no proper return values",cref(dataset,sequence)) +-- end + +local function getmapping(dataset,sequence,currentlookup) 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 - -- can't happen - break - elseif current == stop then - break - else - current = getnext(current) + if nofsteps == 0 then + reportzerosteps(dataset,sequence) + currentlookup.mapping = false + return false + else + if nofsteps > 1 then + reportmoresteps(dataset,sequence) end + local mapping = steps[1].coverage + currentlookup.mapping = mapping + currentlookup.format = steps[1].format + return mapping end - return head, start, false end ---[[ldx-- -

Here we replace start by a sequence of new glyphs.

---ldx]]-- +function chainprocs.gsub_remove(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + if trace_chains then + logprocess("%s: removing character %s",cref(dataset,sequence,chainindex),gref(getchar(start))) + end + head, start = remove_node(head,start,true) + return head, getprev(start), true +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) +function chainprocs.gsub_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping = currentlookup.mapping + if mapping == nil then + mapping = getmapping(dataset,sequence,currentlookup) 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)) + if mapping then + local current = start + while current do + local currentchar = ischar(current) + if currentchar then + local replacement = mapping[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 + -- can't happen + break + elseif current == stop then + break + else + current = getnext(current) + end end - return multiple_glyphs(head,start,replacement,sequence.flags[1],dataset[1]) end return head, start, false end @@ -1272,42 +1326,69 @@ end -- marks come last anyway -- are there cases where we need to delete the mark -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) +function chainprocs.gsub_alternate(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping = currentlookup.mapping + if mapping == nil then + mapping = getmapping(dataset,sequence,currentlookup) end - local kind = dataset[4] - local what = dataset[1] - local value = what == true and tfmdata.shared.features[kind] or what -- todo: optimize in ctx - 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) + if mapping then + local kind = dataset[4] + local what = dataset[1] + local value = what == true and tfmdata.shared.features[kind] or what -- todo: optimize in ctx + local current = start + while current do + local currentchar = ischar(current) + if currentchar then + local alternatives = mapping[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(currentchar),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(currentchar),comment) + end end end + return head, start, true + elseif currentchar == false then + -- can't happen + break + elseif current == stop then + break + else + current = getnext(current) + end + end + end + return head, start, false +end + +--[[ldx-- +

Here we replace start by a sequence of new glyphs.

+--ldx]]-- + +function chainprocs.gsub_multiple(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping = currentlookup.mapping + if mapping == nil then + mapping = getmapping(dataset,sequence,currentlookup) + end + if mapping then + local startchar = getchar(start) + local replacement = mapping[startchar] + if not replacement or replacement == "" then + if trace_bugs then + logwarning("%s: no multiple for %s",cref(dataset,sequence),gref(startchar)) end - return head, start, true - elseif currentchar == false then - -- can't happen - break - elseif current == stop then - break else - current = getnext(current) + 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,skiphash,dataset[1],stop) end end return head, start, false @@ -1319,419 +1400,423 @@ this function (move code inline and handle the marks by a separate function). We assume rather stupid ligatures (no complex disc nodes).

--ldx]]-- -function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup,chainindex) - local steps = currentlookup.steps - local nofsteps = currentlookup.nofsteps - if nofsteps > 1 then - reportmoresteps(dataset,sequence) +-- compare to handlers.gsub_ligature which is more complex ... why + +function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping = currentlookup.mapping + if mapping == nil then + mapping = getmapping(dataset,sequence,currentlookup) 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] -- sequence.flags? - while current do - -- todo: ischar ... can there really be disc nodes here? - local id = getid(current) - if id == disc_code then - if not discfound then - discfound = current - end - if current == stop then - break -- okay? or before the disc + if mapping then + local startchar = getchar(start) + local ligatures = mapping[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 hasmarks = marks[startchar] + local current = getnext(start) + local discfound = false + local last = stop + local nofreplacements = 1 + while current do + -- todo: ischar ... can there really be disc nodes here? + local id = getid(current) + if id == disc_code then + if not discfound then + discfound = current + end + if current == stop then + break -- okay? or before the disc + else + current = getnext(current) + end else - current = getnext(current) - end - else - local schar = getchar(current) - if skipmark and marks[schar] then -- marks - -- if current == stop then -- maybe add this - -- break - -- else - current = getnext(current) - -- end - else - local lg = ligatures[schar] - if lg then - ligatures = lg - last = current - nofreplacements = nofreplacements + 1 - if current == stop then - break - else + local schar = getchar(current) + if skiphash and skiphash[schar] then -- marks + -- if current == stop then -- maybe add this + -- break + -- else current = getnext(current) - end + -- end else - break + local lg = ligatures[schar] + if lg then + ligatures = lg + last = current + nofreplacements = nofreplacements + 1 + if marks[char] then + hasmarks = true + end + if current == stop then + break + else + current = getnext(current) + end + else + break + end end end end - end - local ligature = ligatures.ligature - if ligature then - if chainindex then - stop = last - end - if trace_ligatures then + 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,skiphash,discfound,hasmarks) + return head, start, true, nofreplacements, discfound + elseif trace_bugs then if start == stop then - logprocess("%s: replacing character %s by ligature %s case 3",cref(dataset,sequence,chainindex),gref(startchar),gref(ligature)) + logwarning("%s: replacing character %s by ligature fails",cref(dataset,sequence,chainindex),gref(startchar)) else - logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(dataset,sequence,chainindex),gref(startchar),gref(getchar(stop)),gref(ligature)) + logwarning("%s: replacing character %s upto %s by ligature fails",cref(dataset,sequence,chainindex),gref(startchar),gref(getchar(stop))) 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) +function chainprocs.gpos_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping = currentlookup.mapping + if mapping == nil then + mapping = getmapping(dataset,sequence,currentlookup) end - local startchar = getchar(start) - local step = steps[1] - local kerns = step.coverage[startchar] - if not kerns then - -- skip - elseif step.format == "pair" then - local dx, dy, w, h = setpair(start,factor,rlmode,sequence.flags[4],kerns) -- currentlookup.flags ? - 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 -- needs checking .. maybe no kerns format for single - 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) + if mapping then + local startchar = getchar(start) + local kerns = mapping[startchar] + if kerns then + local format = currentlookup.format + if format == "single" then + local dx, dy, w, h = setposition(0,start,factor,rlmode,kerns) -- currentlookup.flags ? + if trace_kerns then + logprocess("%s: shifting single %s by %s (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),format,dx,dy,w,h) + end + else -- needs checking .. maybe no kerns format for single + local k = (format == "move" and setmove or setkern)(start,factor,rlmode,kerns,injection) + if trace_kerns then + logprocess("%s: shifting single %s by %s %p",cref(dataset,sequence),gref(startchar),format,k) + end + end + return head, start, true end end return head, start, false end -function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,chainindex) -- todo: injections ? - local steps = currentlookup.steps - local nofsteps = currentlookup.nofsteps - if nofsteps > 1 then - reportmoresteps(dataset,sequence) +function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) -- todo: injections ? + local mapping = currentlookup.mapping + if mapping == nil then + mapping = getmapping(dataset,sequence,currentlookup) end - local snext = getnext(start) - if snext then - local startchar = getchar(start) - local step = steps[1] - local kerns = step.coverage[startchar] -- always 1 step - 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 - -- this permits a mixed table, but we could also decide to optimize this - -- in the loader and use format 'kern' - 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 mapping then + local snext = getnext(start) + if snext then + local startchar = getchar(start) + local kerns = mapping[startchar] -- always 1 step + if kerns then + local prev = start + while snext do + local nextchar = ischar(snext,currentfont) + if not nextchar then + break + end + if skiphash and skiphash[nextchar] then + prev = snext + snext = getnext(snext) + else + local krn = kerns[nextchar] + if not krn then + break + end + local format = currentlookup.format + if format == "pair" then + local a, b = krn[1], krn[2] + if a == true then + -- zero + elseif a then + local x, y, w, h = setposition(1,start,factor,rlmode,a,"injections") -- currentlookups flags? + if trace_kerns then + local startchar = getchar(start) + 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 == true then + -- zero + start = snext -- cf spec + elseif b then -- #b > 0 + local x, y, w, h = setposition(2,snext,factor,rlmode,b,"injections") + if trace_kerns then + local startchar = getchar(start) + 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 + start = snext -- cf spec + elseif forcepairadvance then + start = snext -- for testing, not cf spec + end + return head, start, true + elseif krn ~= 0 then + local k = (format == "move" and setmove or setkern)(snext,factor,rlmode,krn) if trace_kerns then - logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k) + logprocess("%s: inserting %s %p between %s and %s",cref(dataset,sequence),format,k,gref(getchar(prev)),gref(nextchar)) end - done = true + return head, start, true + else 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") -- currentlookups flags? - 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) +function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping = currentlookup.mapping + if mapping == nil then + mapping = getmapping(dataset,sequence,currentlookup) end - local markchar = getchar(start) - if marks[markchar] then - local markanchors = steps[1].coverage[markchar] -- always 1 step - if markanchors then - local base = getprev(start) -- [glyph] [start=mark] - 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 + if mapping then + local markchar = getchar(start) + if marks[markchar] then + local markanchors = mapping[markchar] -- always 1 step + if markanchors then + local base = getprev(start) -- [glyph] [start=mark] + 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),1) + logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2) 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) + 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, bound %s, anchoring mark %s to basechar %s => (%p,%p)", + cref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) + end + return head, start, true end - return head, start, true 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),1) + logwarning("%s: prev node is no char, case %i",cref(dataset,sequence),2) end elseif trace_bugs then - logwarning("%s: prev node is no char, case %i",cref(dataset,sequence),2) + logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar)) end elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar)) + logwarning("%s: mark %s is no mark",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) +function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping = currentlookup.mapping + if mapping == nil then + mapping = getmapping(dataset,sequence,currentlookup) end - local markchar = getchar(start) - if marks[markchar] then - local markanchors = steps[1].coverage[markchar] -- always 1 step - if markanchors then - local base = getprev(start) -- [glyph] [optional marks] [start=mark] - 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 + if mapping then + local markchar = getchar(start) + if marks[markchar] then + local markanchors = mapping[markchar] -- always 1 step + if markanchors then + local base = getprev(start) -- [glyph] [optional marks] [start=mark] + 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,1) + logwarning("%s: no base for mark %s, case %i",cref(dataset,sequence),markchar,2) 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) + 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, bound %s, anchoring mark %s to baselig %s at index %s => (%p,%p)", + cref(dataset,sequence),a or bound,gref(markchar),gref(basechar),index,dx,dy) + end + return head, start, true 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),1) + logwarning("%s, prev node is no char, case %i",cref(dataset,sequence),2) end elseif trace_bugs then - logwarning("%s, prev node is no char, case %i",cref(dataset,sequence),2) + logwarning("%s, mark %s has no anchors",cref(dataset,sequence),gref(markchar)) end elseif trace_bugs then - logwarning("%s, mark %s has no anchors",cref(dataset,sequence),gref(markchar)) + logwarning("%s, mark %s is no mark",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) +function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping = currentlookup.mapping + if mapping == nil then + mapping = getmapping(dataset,sequence,currentlookup) end - local markchar = getchar(start) - if marks[markchar] then - local markanchors = steps[1].coverage[markchar] -- always 1 step - if markanchors then - local base = getprev(start) -- [glyph] [basemark] [start=mark] - local slc = getligaindex(start) - if slc then -- a rather messy loop ... needs checking with husayni - while base do - local blc = getligaindex(base) - if blc and blc ~= slc then - base = getprev(base) - else - break + if mapping then + local markchar = getchar(start) + if marks[markchar] then + local markanchors = mapping[markchar] -- always 1 step + if markanchors then + local base = getprev(start) -- [glyph] [basemark] [start=mark] + local slc = getligaindex(start) + if slc then -- a rather messy loop ... needs checking with husayni + while base do + local blc = getligaindex(base) + if blc and blc ~= slc then + base = getprev(base) + else + break + end end end - end - if base then -- subtype test can go - 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) + if base then -- subtype test can go + 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, bound %s, anchoring mark %s to basemark %s => (%p,%p)", + cref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) + end + return head, start, true end - return head, start, true 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),1) + logwarning("%s: prev node is no mark, case %i",cref(dataset,sequence),2) end elseif trace_bugs then - logwarning("%s: prev node is no mark, case %i",cref(dataset,sequence),2) + logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar)) end elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar)) + logwarning("%s: mark %s is no mark",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) +function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping = currentlookup.mapping + if mapping == nil then + mapping = getmapping(dataset,sequence,currentlookup) end - local startchar = getchar(start) - local exitanchors = steps[1].coverage[startchar] -- always 1 step - 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 - -- should not happen (maybe warning) - nxt = getnext(nxt) - else - local exit = exitanchors[3] - if exit then - local entry = exitanchors[1][nextchar] - if entry then - entry = entry[2] + if mapping then + local startchar = getchar(start) + local exitanchors = mapping[startchar] -- always 1 step + if exitanchors then + 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 nxt do + local nextchar = ischar(nxt,currentfont) + if not nextchar then + break + elseif marks[nextchar] then + -- should not happen (maybe warning) + nxt = getnext(nxt) + else + local exit = exitanchors[3] + if exit then + local entry = exitanchors[1][nextchar] 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)) + entry = entry[2] + if entry then + local r2lflag = sequence.flags[4] -- mentioned in the standard + local dx, dy, bound = setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar],r2lflag) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%p,%p) using bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,bound,mref(rlmode)) + end + return head, start, true end - done = true - break end + elseif trace_bugs then + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) end - elseif trace_bugs then - onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + break end - break end end - end - return head, start, done - else - if trace_cursive and trace_details then + elseif 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 + return head, start, false end -- what pointer to return, spec says stop @@ -1745,36 +1830,50 @@ end -- A previous version had disc collapsing code in the (single sub) handler plus some -- checking in the main loop, but that left the pre/post sequences undone. The best -- solution is to add some checking there and backtrack when a replace/post matches --- but it takes a bit of work to figure out an efficient way (this is what the sweep* --- names refer to). I might look into that variant one day again as it can replace --- some other code too. In that approach we can have a special version for gub and pos --- which gains some speed. This method does the test and passes info to the handlers --- (sweepnode, sweepmode, sweepprev, sweepnext, etc). Here collapsing is handled in the --- main loop which also makes code elsewhere simpler (i.e. no need for the other special --- runners and disc code in ligature building). I also experimented with pushing preceding --- glyphs sequences in the replace/pre fields beforehand which saves checking afterwards --- but at the cost of duplicate glyphs (memory) but it's too much overhead (runtime). +-- but it takes a bit of work to figure out an efficient way (this is what the +-- sweep* names refer to). I might look into that variant one day again as it can +-- replace some other code too. In that approach we can have a special version for +-- gub and pos which gains some speed. This method does the test and passes info to +-- the handlers. Here collapsing is handled in the main loop which also makes code +-- elsewhere simpler (i.e. no need for the other special runners and disc code in +-- ligature building). I also experimented with pushing preceding glyphs sequences +-- in the replace/pre fields beforehand which saves checking afterwards but at the +-- cost of duplicate glyphs (memory) but it's too much overhead (runtime). -- --- In the meantime Kai had moved the code from the single chain into a more general handler --- and this one (renamed to chaindisk) is used now. I optimized the code a bit and brought --- it in sycn with the other code. Hopefully I didn't introduce errors. Note: this somewhat --- complex approach is meant for fonts that implement (for instance) ligatures by character --- replacement which to some extend is not that suitable for hyphenation. I also use some --- helpers. This method passes some states but reparses the list. There is room for a bit of --- speed up but that will be done in the context version. (In fact a partial rewrite of all --- code can bring some more efficientry.) +-- In the meantime Kai had moved the code from the single chain into a more general +-- handler and this one (renamed to chaindisk) is used now. I optimized the code a +-- bit and brought it in sycn with the other code. Hopefully I didn't introduce +-- errors. Note: this somewhat complex approach is meant for fonts that implement +-- (for instance) ligatures by character replacement which to some extend is not +-- that suitable for hyphenation. I also use some helpers. This method passes some +-- states but reparses the list. There is room for a bit of speed up but that will +-- be done in the context version. (In fact a partial rewrite of all code can bring +-- some more efficiency.) -- --- I didn't test it with extremes but successive disc nodes still can give issues but in --- order to handle that we need more complex code which also slows down even more. The main --- loop variant could deal with that: test, collapse, backtrack. +-- I didn't test it with extremes but successive disc nodes still can give issues +-- but in order to handle that we need more complex code which also slows down even +-- more. The main loop variant could deal with that: test, collapse, backtrack. + +local userkern = nuts.pool and nuts.pool.newkern -- context -local new_kern = nuts.pool.kern +do if not userkern then -- generic + + local thekern = nuts.new("kern",1) -- userkern + local setkern = nuts.setkern -- not injections.setkern + + userkern = function(k) + local n = copy_node(thekern) + setkern(n,k) + return n + end + +end end local function checked(head) local current = head while current do if getid(current) == glue_code then - local kern = new_kern(getfield(current,"width")) + local kern = userkern(getwidth(current)) if head == current then local next = getnext(current) if next then @@ -1785,8 +1884,7 @@ local function checked(head) current = next else local prev, next = getboth(current) - setlink(prev,kern) - setlink(kern,next) + setlink(prev,kern,next) flush_node(current) current = next end @@ -1804,7 +1902,131 @@ local function setdiscchecked(d,pre,post,replace) setdisc(d,pre,post,replace) end -local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,ck,chainproc) +local noflags = { false, false, false, false } + +local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) + + local size = ck[5] - ck[4] + 1 + local chainlookups = ck[6] + local done = false + + -- current match + if chainlookups then + -- Lookups can be like { 1, false, 3 } or { false, 2 } or basically anything and + -- #lookups can be less than #current + + if size == 1 then + + -- if nofchainlookups > size then + -- -- bad rules + -- end + + local chainlookup = chainlookups[1] + for j=1,#chainlookup do + local chainstep = chainlookup[j] + local chainkind = chainstep.type + local chainproc = chainprocs[chainkind] + if chainproc then + local ok + head, start, ok = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash) + if ok then + done = true + end + else + logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind) + end + end + + else + + -- See LookupType 5: Contextual Substitution Subtable. Now it becomes messy. The + -- easiest case is where #current maps on #lookups i.e. one-to-one. But what if + -- we have a ligature. Cf the spec we then need to advance one character but we + -- really need to test it as there are fonts out there that are fuzzy and have + -- too many lookups: + -- + -- U+1105 U+119E U+1105 U+119E : sourcehansansklight: script=hang ccmp=yes + -- + -- Even worse are these family emoji shapes as they can have multiple lookups + -- per slot (probably only for gpos). + + -- It's very unlikely that we will have skip classes here but still ... we seldom + -- enter this branch anyway. + + local i = 1 + local laststart = start + local nofchainlookups = #chainlookups -- useful? + while start do + if skiphash then -- hm, so we know we skip some + while start do + local char = ischar(start,currentfont) + if char then + if skiphash and skiphash[char] then + start = getnext(start) + else + break + end + else + break + end + end + end + local chainlookup = chainlookups[i] + if chainlookup then + for j=1,#chainlookup do + local chainstep = chainlookup[j] + local chainkind = chainstep.type + local chainproc = chainprocs[chainkind] + if chainproc then + local ok, n + head, start, ok, n = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,i) + -- messy since last can be changed ! + if ok then + done = true + if n and n > 1 and i + n > nofchainlookups then + -- this is a safeguard, we just ignore the rest of the lookups + i = size -- prevents an advance + break + end + end + else + -- actually an error + logprocess("%s: %s is not yet supported (2)",cref(dataset,sequence),chainkind) + end + end + else + -- we skip but we could also delete as option .. what does an empty lookup actually mean + -- in opentype ... anyway, we could map it onto gsub_remove if needed + end + i = i + 1 + if i > size or not start then + break + elseif start then + laststart = start + start = getnext(start) + end + end + if not start then + start = laststart + end + + end + else + -- todo: needs checking for holes in the replacements + local replacements = ck[7] + if replacements then + head, start, done = reversesub(head,start,last,dataset,sequence,replacements,rlmode,skiphash) + else + done = true + if trace_contexts then + logprocess("%s: skipping match",cref(dataset,sequence)) + end + end + end + return head, start, done +end + +local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) if not start then return head, start, false @@ -1819,7 +2041,6 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c local sweepnode = sweepnode local sweeptype = sweeptype local sweepoverflow = false - local checkdisc = getprev(head) -- hm bad name head local keepdisc = not sweepnode local lookaheaddisc = nil local backtrackdisc = nil @@ -1845,19 +2066,23 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c hasglue = true elseif id == disc_code then if keepdisc then - keepdisc = false - if notmatchpre[current] ~= notmatchreplace[current] then - lookaheaddisc = current - end + keepdisc = false + lookaheaddisc = current local replace = getfield(current,"replace") - while replace and i <= l do - if getid(replace) == glyph_code then - i = i + 1 + if not replace then + sweepoverflow = true + sweepnode = current + current = getnext(current) + else + while replace and i <= l do + if getid(replace) == glyph_code then + i = i + 1 + end + replace = getnext(replace) end - replace = getnext(replace) + current = getnext(replace) end - last = current - current = getnext(c) + last = current else head, current = flattendisk(head,current) end @@ -1868,7 +2093,7 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c if current then -- go on elseif sweepoverflow then - -- we already are folling up on sweepnode + -- we already are following up on sweepnode break elseif sweeptype == "post" or sweeptype == "replace" then current = getnext(sweepnode) @@ -1919,7 +2144,8 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c if notmatchpre[current] ~= notmatchreplace[current] then lookaheaddisc = current end - local replace = getfield(c,"replace") + -- we assume a simple text only replace (we could use nuts.count) + local replace = getfield(current,"replace") while replace and i < s do if getid(replace) == glyph_code then i = i + 1 @@ -1964,6 +2190,7 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c if notmatchpost[current] ~= notmatchreplace[current] then backtrackdisc = current end + -- we assume a simple text only replace (we could use nuts.count) local replace = getfield(current,"replace") while replace and i > 1 do if getid(replace) == glyph_code then @@ -1981,8 +2208,8 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c end end end + local done = false - local ok = false if lookaheaddisc then local cf = start @@ -2001,38 +2228,44 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c break end end - - setprev(lookaheaddisc,cprev) - if cprev then - setnext(cprev,lookaheaddisc) - end + setlink(cprev,lookaheaddisc) setprev(cf) setnext(cl) if startishead then head = lookaheaddisc end local pre, post, replace = getdisc(lookaheaddisc) - local new = copy_node_list(cf) + local new = copy_node_list(cf) -- br, how often does that happen local cnew = new + if pre then + setlink(find_node_tail(cf),pre) + end + if replace then + local tail = find_node_tail(new) + setlink(tail,replace) + end for i=1,insertedmarks do cnew = getnext(cnew) end + cl = start local clast = cnew for i=f,l do + cl = getnext(cl) clast = getnext(clast) end if not notmatchpre[lookaheaddisc] then - cf, start, ok = chainproc(cf,start,last,dataset,sequence,chainlookup,rlmode,k) + local ok = false + cf, start, ok = chainrun(cf,start,cl,dataset,sequence,rlmode,skiphash,ck) + if ok then + done = true + end 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) + local ok = false + new, cnew, ok = chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck) + if ok then + done = true + end end if hasglue then setdiscchecked(lookaheaddisc,cf,post,new) @@ -2040,8 +2273,8 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c setdisc(lookaheaddisc,cf,post,new) end start = getprev(lookaheaddisc) - sweephead[cf] = getnext(clast) - sweephead[new] = getnext(last) + sweephead[cf] = getnext(clast) or false + sweephead[new] = getnext(cl) or false elseif backtrackdisc then @@ -2060,10 +2293,7 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c break end end - if cnext then - setprev(cnext,backtrackdisc) - end - setnext(backtrackdisc,cnext) + setlink(backtrackdisc,cnext) setprev(cf) setnext(cl) local pre, post, replace, pretail, posttail, replacetail = getdisc(backtrackdisc,true) @@ -2077,10 +2307,18 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c clast = getnext(clast) end if not notmatchpost[backtrackdisc] then - cf, start, ok = chainproc(cf,start,last,dataset,sequence,chainlookup,rlmode,k) + local ok = false + cf, start, ok = chainrun(cf,start,last,dataset,sequence,rlmode,skiphash,ck) + if ok then + done = true + end end if not notmatchreplace[backtrackdisc] then - new, cnew, ok = chainproc(new,cnew,clast,dataset,sequence,chainlookup,rlmode,k) + local ok = false + new, cnew, ok = chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck) + if ok then + done = true + end end if post then setlink(posttail,cf) @@ -2098,539 +2336,486 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c setdisc(backtrackdisc,pre,post,replace) end start = getprev(backtrackdisc) - sweephead[post] = getnext(clast) - sweephead[replace] = getnext(last) + sweephead[post] = getnext(clast) or false + sweephead[replace] = getnext(last) or false else - head, start, ok = chainproc(head,start,last,dataset,sequence,chainlookup,rlmode,k) + local ok = false + head, start, ok = chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) + if ok then + done = true + end end - return head, start, ok + return head, start, done end -local noflags = { false, false, false, false } +local function chaintrac(head,start,dataset,sequence,rlmode,skiphash,ck,match,discseen,sweepnode) + local rule = ck[1] + local lookuptype = ck[8] or ck[2] + local nofseq = #ck[3] + local first = ck[4] + local last = ck[5] + local char = getchar(start) + logwarning("%s: rule %s %s at char %s for (%s,%s,%s) chars, lookuptype %a, %sdisc seen, %ssweeping", + cref(dataset,sequence),rule,match and "matches" or "nomatch", + gref(char),first-1,last-first+1,nofseq-last,lookuptype, + discseen and "" or "no ", sweepnode and "" or "not ") +end + +-- The next one is quite optimized but still somewhat slow, fonts like ebgaramond +-- are real torture tests because they have many steps with one context (having +-- multiple contexts makes more sense) also because we (can) reduce them. Instead of +-- a match boolean variable and check for that I decided to use a goto with labels +-- instead. This is one of the cases where it makes the code more readable and we +-- might even gain a bit performance. -local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode) +-- when we have less replacements (lookups) then current matches we can push too much into +-- the previous disc .. such be it ( with only f done) + +local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,skiphash) + -- optimizing for rlmode gains nothing local sweepnode = sweepnode local sweeptype = sweeptype + local postreplace + local prereplace + local checkdisc + local discseen -- = false + if sweeptype then + if sweeptype == "replace" then + postreplace = true + prereplace = true + else + postreplace = sweeptype == "post" + prereplace = sweeptype == "pre" + end + checkdisc = getprev(head) + end 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 -- i've only seen ccmp having > 1 (e.g. dejavu) - local match = true + + local skipped -- = false + + local startprev, + startnext = getboth(start) + local done -- = false + + -- we can have multiple hits and as we scan (currently) all we need to check + -- if we have a match ... contextchains have no real coverage table (with + -- unique entries) + + -- fonts can have many steps (each doing one check) or many contexts + + -- todo: make a per-char cache so that we have small contexts (when we have a context + -- n == 1 and otherwise it can be more so we can even distingish n == 1 or more) + + local nofcontexts = contexts.n -- #contexts + + local startchar = nofcontext == 1 or ischar(start,currentfont) -- only needed in a chain + + for k=1,nofcontexts do -- does this disc mess work well with n > 1 + + local ck = contexts[k] + local seq = ck[3] + local f = ck[4] -- first current + if not startchar or not seq[f][startchar] then + -- report("no hit in %a at %i of %i contexts",sequence.type,k,nofcontexts) + goto next + end + local s = seq.n -- or #seq + local l = ck[5] -- last current local current = start local last = start - local ck = contexts[k] - local seq = ck[3] - local s = #seq - local size = 1 - -- f..l = mid string - if s == 1 then - -- this seldom happens as it makes no sense (bril, ebgaramond, husayni, minion) - local char = ischar(current,currentfont) - if char then - match = seq[1][char] - end - else - -- maybe we need a better space check (maybe check for glue or category or combination) - -- we cannot optimize for n=2 because there can be disc nodes - local f = ck[4] - local l = ck[5] - -- current match - size = l - f + 1 - if size > 1 then - -- before/current/after | before/current | current/after - local discfound = nil - local n = f + 1 - last = getnext(last) -- the second in current (first already matched) - 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 + + -- current match + + if l > f then + -- before/current/after | before/current | current/after + local discfound -- = nil + local n = f + 1 + last = startnext -- the second in current (first already matched) + while n <= l do + if postreplace and not last then + last = getnext(sweepnode) + sweeptype = nil + end + if last then + local char, id = ischar(last,currentfont) + if char then + if skiphash and skiphash[char] then + skipped = true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + last = getnext(last) + elseif seq[n][char] then + if n < l then + last = getnext(last) + end + n = n + 1 + elseif discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next 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 + goto next + end + elseif char == false then + if discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next else - match = false + break 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 + else + goto next + end + elseif id == disc_code then + -- elseif id == disc_code and (not discs or discs[last]) then + discseen = 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 + if n > l then break end - end - if n <= l then + pre = getnext(pre) + else notmatchpre[last] = true + break end - else - notmatchpre[last] = true end - if replace then - -- so far we never entered this branch - while replace do - if seq[n][getchar(replace)] then - n = n + 1 - replace = getnext(replace) - if n > l then - break - end + -- commented, for Kai to check + -- if n <= l then + -- notmatchpre[last] = true + -- end + else + notmatchpre[last] = true + end + if replace then + -- so far we never entered this branch + while replace do + if seq[n][getchar(replace)] then + n = n + 1 + if n > l then + break + end + replace = getnext(replace) + else + notmatchreplace[last] = true + if notmatchpre[last] then + goto next else - notmatchreplace[last] = true - match = not notmatchpre[last] break end end - match = not notmatchpre[last] end - -- maybe only if match - last = getnext(last) - else - match = false - break + -- why here again + if notmatchpre[last] then + goto next + end end + -- maybe only if match + last = getnext(last) else - match = false - break + goto next end + else + goto next end end - -- before - if match and f > 1 then - local prev = getprev(start) + end + + -- before + + if f > 1 then + if startprev then + local prev = startprev + if prereplace and prev == checkdisc then + prev = getprev(sweepnode) + end if prev then - if prev == checkdisc and (sweeptype == "pre" or sweeptype == "replace") then - prev = getprev(sweepnode) - -- sweeptype = nil - 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) -- moved here - elseif seq[n][char] then - if n > 1 then -- new test - prev = getprev(prev) -- moved here - end - n = n - 1 - else - if discfound then - notmatchreplace[discfound] = true - match = not notmatchpost[discfound] - else - match = false - end - break - end + local discfound -- = nil + local n = f - 1 + while n >= 1 do + if prev then + local char, id = ischar(prev,currentfont) + if char then + if skiphash and skiphash[char] then + skipped = true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + prev = getprev(prev) + elseif seq[n][char] then + if n > 1 then + prev = getprev(prev) + end + n = n - 1 + elseif discfound then + notmatchreplace[discfound] = true + if notmatchpost[discfound] then + goto next else - if discfound then - notmatchreplace[discfound] = true - match = not notmatchpost[discfound] - else - match = false - end break end - -- prev = getprev(prev) -- moved up - elseif char == false then - if discfound then - notmatchreplace[discfound] = true - match = not notmatchpost[discfound] - else - match = false + else + goto next + end + elseif char == false then + if discfound then + notmatchreplace[discfound] = true + if notmatchpost[discfound] then + goto next end - break - elseif id == disc_code then - -- the special case: f i where i becomes dottless i .. - 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 + else + goto next + end + break + elseif id == disc_code then + -- elseif id == disc_code and (not discs or discs[prev]) then + -- the special case: f i where i becomes dottless i .. + discseen = true + discfound = prev + notmatchpre[prev] = true + notmatchpost[prev] = nil + notmatchreplace[prev] = nil + local pre, post, replace, pretail, posttail, replacetail = getdisc(prev,true) + -- weird test: needs checking + 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 or n < 1 then break + else + posttail = getprev(posttail) end - end - if n >= 1 then + else notmatchpost[prev] = true + break end - else + end + if n >= 1 then notmatchpost[prev] = true end - if replace then - -- we seldom enter this branch (e.g. on brill efficient) - 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 + notmatchpost[prev] = true + end + if replace then + -- we seldom enter this branch (e.g. on brill efficient) + while replacetail do + if seq[n][getchar(replacetail)] then + n = n - 1 + if replacetail == replace or n < 1 then + break + else + replacetail = getprev(replacetail) + end + else + notmatchreplace[prev] = true + if notmatchpost[prev] then + goto next else - notmatchreplace[prev] = true - match = not notmatchpost[prev] break end end - if not match then - break - end end + else + notmatchreplace[prev] = true -- new, for Kai to check end - -- maybe only if match - prev = getprev(prev) - elseif seq[n][32] and isspace(prev,threshold) then + end + prev = getprev(prev) + -- elseif id == glue_code and seq[n][32] and isspace(prev,threshold,id) then + -- elseif seq[n][32] and spaces[prev] then + -- n = n - 1 + -- prev = getprev(prev) + elseif id == glue_code then + local sn = seq[n] + if (sn[32] and spaces[prev]) or sn[0xFFFC] then n = n - 1 prev = getprev(prev) else - match = false - break + goto next end + elseif seq[n][0xFFFC] then + n = n - 1 + prev = getprev(prev) else - match = false - break + goto next end + else + goto next end - else - match = false end else - match = false + goto next end + else + goto next end - -- after - 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) - -- sweeptype = nil - end - end - if current then - local discfound = nil - -- removed optimization for s-l == 1, we have to deal with marks anyway - local n = l + 1 - while n <= s do - if current then - local 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) -- was absent - elseif seq[n][char] then - if n < s then -- new test - current = getnext(current) -- was absent - end - n = n + 1 - else - if discfound then - notmatchreplace[discfound] = true - match = not notmatchpre[discfound] - else - match = false - end - break - end + end + + -- after + + if s > l then + local current = last and getnext(last) + if not current and postreplace then + current = getnext(sweepnode) + 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 + if skiphash and skiphash[char] then + skipped = true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + current = getnext(current) -- was absent + elseif seq[n][char] then + if n < s then -- new test + current = getnext(current) -- was absent + end + n = n + 1 + elseif discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next 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 + goto next + end + elseif char == false then + if discfound then + notmatchreplace[discfound] = true + if notmatchpre[discfound] then + goto next else - match = false + break 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 + else + goto next + end + elseif id == disc_code then + -- elseif id == disc_code and (not discs or discs[current]) then + discseen = 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 + if n > s then break + else + pre = getnext(pre) end - end - if n <= s then + else notmatchpre[current] = true + break end - else + end + if n <= s then notmatchpre[current] = true end - if replace then - -- so far we never entered this branch - while replace do - if seq[n][getchar(replace)] then - n = n + 1 + else + notmatchpre[current] = true + end + if replace then + -- so far we never entered this branch + while replace do + if seq[n][getchar(replace)] then + n = n + 1 + if n > s then + break + else replace = getnext(replace) - if n > s then - break - end + end + else + notmatchreplace[current] = true + if notmatchpre[current] then + goto next else - notmatchreplace[current] = true - match = notmatchpre[current] break end end - if not match then - break - end - else - -- skip 'm end - -- maybe only if match - current = getnext(current) - elseif seq[n][32] and isspace(current,threshold) then + else + notmatchreplace[current] = true -- new, for Kai to check + end + current = getnext(current) + elseif id == glue_code then + local sn = seq[n] + if (sn[32] and spaces[current]) or sn[0xFFFC] then n = n + 1 current = getnext(current) else - match = false - break + goto next end + elseif seq[n][0xFFFC] then + n = n + 1 + current = getnext(current) else - match = false - break - end - end - else - match = false - end - end - end - if match then - -- Can lookups be of a different type? - 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 - -- Lookups can be like { 1, false, 3 } or { false, 2 } or basically anything and - -- #lookups can be less than #current - if size == 1 then - -- if nofchainlookups > size then - -- -- bad rules - -- end - 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 + goto next end else - logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind) - end - else - -- See LookupType 5: Contextual Substitution Subtable. Now it becomes messy. The - -- easiest case is where #current maps on #lookups i.e. one-to-one. But what if - -- we have a ligature. Cf the spec we then need to advance one character but we - -- really need to test it as there are fonts out there that are fuzzy and have - -- too many lookups: - -- - -- U+1105 U+119E U+1105 U+119E : sourcehansansklight: script=hang ccmp=yes - -- - local i = 1 - while start do - if skipped then - while start do -- todo: use properties - 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 - -- messy since last can be changed ! - if ok then - done = true - if n and n > 1 and i + n > nofchainlookups then - -- this is a safeguard, we just ignore the rest of the lookups - break - end - end - else - -- actually an error - 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 + goto next end end else - -- todo: needs checking for holes in the replacements - 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 -- out of contexts (new, needs checking) + goto next end end + + if trace_contexts then + chaintrac(head,start,dataset,sequence,rlmode,skipped and skiphash,ck,true,discseen,sweepnode) + end + if discseen or sweepnode then + head, start, done = chaindisk(head,start,dataset,sequence,rlmode,skipped and skiphash,ck) + else + head, start, done = chainrun(head,start,last,dataset,sequence,rlmode,skipped and skiphash,ck) + end + if done then + break + -- else + -- next context + end + ::next:: + -- if trace_chains then + -- chaintrac(head,start,dataset,sequence,rlmode,skipped and skiphash,ck,false,discseen,sweepnode) + -- end end - if diskseen then + if discseen then notmatchpre = { } notmatchpost = { } notmatchreplace = { } + -- notmatchpre = { a = 1, b = 1 } notmatchpre .a = nil notmatchpre .b = nil + -- notmatchpost = { a = 1, b = 1 } notmatchpost .a = nil notmatchpost .b = nil + -- notmatchreplace = { a = 1, b = 1 } notmatchreplace.a = nil notmatchreplace.b = nil end return head, start, done end @@ -2643,13 +2828,19 @@ handlers.gpos_context = handle_contextchain -- this needs testing -local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode) +local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash) 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) + -- probably wrong + local l = steps[1].coverage[getchar(start)] + if l then + return handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash) + else + return head, start, false + end end chainprocs.gsub_context = chained_contextchain @@ -2658,28 +2849,40 @@ chainprocs.gsub_reversecontextchain = chained_contextchain chainprocs.gpos_contextchain = chained_contextchain chainprocs.gpos_context = chained_contextchain -local missing = setmetatableindex("table") +------------------------------ + +-- experiment (needs no handler in font-otc so not now): +-- +-- function otf.registerchainproc(name,f) +-- -- chainprocs[name] = f +-- chainprocs[name] = function(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash) +-- local done = currentlookup.nofsteps > 0 +-- if not done then +-- reportzerosteps(dataset,sequence) +-- else +-- head, start, done = f(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash) +-- if not head or not start then +-- reportbadsteps(dataset,sequence) +-- end +-- end +-- return head, start, done +-- end +-- end + +local missing = setmetatableindex("table") +local logwarning = report_process +local resolved = { } -- we only resolve a font,script,language pair once local function logprocess(...) if trace_steps then registermessage(...) + if trace_steps == "silent" then + return + end 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 = { } -- we only resolve a font,script,language pair once - -- todo: pass all these 'locals' in a table local sequencelists = setmetatableindex(function(t,font) @@ -2693,81 +2896,96 @@ end) -- fonts.hashes.sequences = sequencelists -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 } +do -- overcome local limit + + local autofeatures = fonts.analyzers.features + local featuretypes = otf.tables.featuretypes + local defaultscript = otf.features.checkeddefaultscript + local defaultlanguage = otf.features.checkeddefaultlanguage + + local wildcard = "*" + local default = "dflt" + + 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 + -- can't happen end - else - -- can't happen end + return false end - return false -end -function otf.dataset(tfmdata,font) -- generic variant, overloaded in context - 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 = { - -- indexed but we can also add specific data by key - } - 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 + function otf.dataset(tfmdata,font) -- generic variant, overloaded in context + 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 = { + -- indexed but we can also add specific data by key + } + 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 - return rl + end +-- Functions like kernrun, comprun etc evolved over time and in the end look rather +-- complex. It's a bit of a compromis between extensive copying and creating subruns. +-- The logic has been improved a lot by Kai and Ivo who use complex fonts which +-- really helped to identify border cases on the one hand and get insight in the diverse +-- ways fonts implement features (not always that consistent and efficient). At the same +-- time I tried to keep the code relatively efficient so that the overhead in runtime +-- stays acceptable. + local function report_disc(what,n) report_run("%s: %s > %s",what,n,languages.serializediscretionary(n)) end @@ -2815,14 +3033,12 @@ local function kernrun(disc,k_run,font,attr,...) done = true end if prev then - local nest = getprev(pre) setlink(prev,pre) if k_run(prevmarks,"preinjections",pre,font,attr,...) then -- or prev? done = true end - setprev(pre,nest) --- setprev(pre) - setnext(prev,disc) + setprev(pre) + setlink(prev,disc) end end -- @@ -2836,7 +3052,7 @@ local function kernrun(disc,k_run,font,attr,...) done = true end setnext(posttail) - setprev(next,disc) + setlink(disc,next) end end -- @@ -2845,14 +3061,12 @@ local function kernrun(disc,k_run,font,attr,...) done = true end if prev then - local nest = getprev(replace) setlink(prev,replace) if k_run(prevmarks,"replaceinjections",replace,font,attr,...) then -- getnext(replace)) done = true end - setprev(replace,nest) --- setprev(replace) - setnext(prev,disc) + setprev(replace) + setlink(prev,disc) end if next then setlink(replacetail,next) @@ -2860,20 +3074,25 @@ local function kernrun(disc,k_run,font,attr,...) done = true end setnext(replacetail) - setprev(next,disc) + setlink(disc,next) 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) + setlink(prev,disc,next) + end + if done and trace_testruns then + report_disc("done",disc) end return nextstart, done end -local function comprun(disc,c_run,...) +-- fonts like ebgaramond do ligatures this way (less efficient than e.g. dejavu which +-- will do the testrun variant) + +local function comprun(disc,c_run,...) -- vararg faster than the whole list if trace_compruns then report_disc("comp",disc) end @@ -2883,7 +3102,7 @@ local function comprun(disc,c_run,...) -- if pre then sweepnode = disc - sweeptype = "pre" -- in alternative code preinjections is uc_c_sed (also used then for proeprties, saves a variable) + sweeptype = "pre" -- in alternative code preinjections is used (also used then for properties, saves a variable) local new, done = c_run(pre,...) if done then pre = new @@ -2914,12 +3133,18 @@ local function comprun(disc,c_run,...) sweepnode = nil sweeptype = nil if renewed then + if trace_testruns then + report_disc("done",disc) + end setdisc(disc,pre,post,replace) end -- return getnext(disc), renewed end +-- if we can hyphenate in a lig then unlikely a lig so we +-- could have a option here to ignore lig + local function testrun(disc,t_run,c_run,...) if trace_testruns then report_disc("test",disc) @@ -2930,32 +3155,72 @@ local function testrun(disc,t_run,c_run,...) return end local pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true) - local done = false - if replace and prev then - -- this is a bit strange as we only do replace here and not post - -- anyway, we only look ahead ... the idea is that we discard a - -- disc when there is a ligature crossing the replace boundary - setlink(replacetail,next) - local ok, overflow = t_run(replace,next,...) - if ok and overflow then - -- so, we can have crossed the boundary - setfield(disc,"replace") - setlink(prev,replace) - -- setlink(replacetail,next) - setboth(disc) - flush_node_list(disc) - return replace, true -- restart .. tricky ! + local renewed = false + if (post or replace) and prev then + if post then + setlink(posttail,next) + else + post = next + end + if replace then + setlink(replacetail,next) + else + replace = next + end + local d_post = t_run(post,next,...) + local d_replace = t_run(replace,next,...) + if d_post > 0 or d_replace > 0 then + local d = d_replace > d_post and d_replace or d_post + local head = getnext(disc) -- is: next + local tail = head + for i=1,d do + local nx = getnext(tail) + local id = getid(nx) + if id == disc_code then + head, tail = flattendisk(head,nx) + elseif id == glyph_code then + tail = nx + else + -- we can have overrun into a glue + break + end + end + next = getnext(tail) + setnext(tail) + setprev(head) + local new = copy_node_list(head) + if posttail then + setlink(posttail,head) + else + post = head + end + if replacetail then + setlink(replacetail,new) + else + replace = new + end else -- we stay inside the disc - setnext(replacetail) - setprev(next,disc) + if posttail then + setnext(posttail) + else + post = nil + end + if replacetail then + setnext(replacetail) + else + replace = nil + end end + setlink(disc,next) -- pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true) end -- -- like comprun -- - local renewed = false + if trace_testruns then + report_disc("more",disc) + end -- if pre then sweepnode = disc @@ -2991,47 +3256,14 @@ local function testrun(disc,t_run,c_run,...) sweeptype = nil if renewed then setdisc(disc,pre,post,replace) - return next, true - else - return next, done + if trace_testruns then + report_disc("done",disc) + end end + -- next can have changed (copied list) + return getnext(disc), renewed end --- A discrun happens when we have a zwnj. We're gpossing so it is unlikely that --- there has been a match changing the character. Now, as we check again here --- the question is: why do we do this ... needs checking as drun seems useless --- ... maybe that code can go away - --- local function discrun(disc,drun,krun) --- local prev, next = getboth(disc) --- if trace_discruns then --- report_disc("disc",disc) --- end --- if next and prev then --- setnext(prev,next) --- -- setprev(next,prev) --- drun(prev) --- setnext(prev,disc) --- -- setprev(next,disc) --- end --- -- --- if krun then -- currently always false --- local pre = getfield(disc,"pre") --- if not pre then --- -- go on --- elseif prev then --- local nest = getprev(pre) --- setlink(prev,pre) --- krun(prev,"preinjections") --- setprev(pre,nest) --- setnext(prev,disc) --- else --- krun(pre,"preinjections") --- end --- end --- return next --- end - -- We can make some assumptions with respect to discretionaries. First of all it is very -- unlikely that some of the analysis related attributes applies. Then we can also assume -- that the ConTeXt specific dynamic attribute is different, although we do use explicit @@ -3059,24 +3291,28 @@ end local nesting = 0 -local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) +local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) local done = false local sweep = sweephead[head] if sweep then start = sweep - sweephead[head] = nil + -- sweephead[head] = nil + sweephead[head] = false else start = head end while start do - local char = ischar(start,font) + local char, id = ischar(start,font) if char then - local a = attr and getattr(start,0) + local a -- happens often so no assignment is faster + if attr then + a = getattr(start,0) + end 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) + head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) if ok then done = true end @@ -3101,67 +3337,103 @@ local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlm return head, done end +-- only replace? + local function t_run_single(start,stop,font,attr,lookupcache) + local lastd = nil while start ~= stop do local char = ischar(start,font) if char then - local a = attr and getattr(start,0) + local a -- happens often so no assignment is faster + if attr then + a = getattr(start,0) + end + local startnext = getnext(start) if not a or (a == attr) then local lookupmatch = lookupcache[char] if lookupmatch then -- hm, hyphens can match (tlig) so we need to really check -- if we need more than ligatures we can outline the code and use functions - local s = getnext(start) + local s = startnext + local ss = nil + local sstop = s == stop + if not s then + s = ss + ss = nil + end + -- a bit weird: why multiple ... anyway we can't have a disc in a disc + -- how about post ... we can probably merge this into the while + while getid(s) == disc_code do + ss = getnext(s) + s = getfield(s,"replace") + if not s then + s = ss + ss = nil + end + end 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) + local char = ischar(s,font) + if char then + local lg = lookupmatch[char] + if lg then + if sstop then + d = 1 + elseif d > 0 then + d = d + 1 + end + l = lg + s = getnext(s) + sstop = s == stop + if not s then + s = ss + ss = nil + end + while getid(s) == disc_code do + ss = getnext(s) + s = getfield(s,"replace") + if not s then + s = ss + ss = nil + end + end + else + break + end else break end end - if l and l.ligature then - return true, d > 1 + if l and l.ligature then -- so we test for ligature + lastd = d end +-- why not: if not l then break elseif l.ligature then return d end + else +-- why not: break + -- no match (yet) end else -- go on can be a mixed one +-- why not: break end - start = getnext(start) + if lastd then + return lastd + end + start = startnext else break end end + return 0 end --- local function d_run_single(prev,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) --- local a = attr and getattr(prev,0) --- if not a or (a == attr) then --- local char = ischar(prev) -- can be disc --- if char then --- local lookupmatch = lookupcache[char] --- if lookupmatch then --- local h, d, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1) --- if ok then --- done = true --- success = true --- end --- end --- 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) +local function k_run_single(sub,injection,last,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + local a -- happens often so no assignment is faster + if attr then + a = getattr(sub,0) + end if not a or (a == attr) then - for n in traverse_nodes(sub) do -- only gpos + for n in nextnode, sub do -- only gpos if n == last then break end @@ -3169,7 +3441,7 @@ local function k_run_single(sub,injection,last,font,attr,lookupcache,step,datase if char then local lookupmatch = lookupcache[char] if lookupmatch then - local h, d, ok = handler(sub,n,dataset,sequence,lookupmatch,rlmode,step,1,injection) + local h, d, ok = handler(sub,n,dataset,sequence,lookupmatch,rlmode,skiphash,step,injection) if ok then return true end @@ -3179,39 +3451,39 @@ local function k_run_single(sub,injection,last,font,attr,lookupcache,step,datase end end -local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler) +local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) local done = false local sweep = sweephead[head] if sweep then start = sweep - sweephead[head] = nil + -- sweephead[head] = nil + sweephead[head] = false else start = head end while start do local char = ischar(start,font) if char then - local a = attr and getattr(start,0) + local a -- happens often so no assignment is faster + if attr then + a = getattr(start,0) + end 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 - -- we could move all code inline but that makes things even more unreadable - local ok - head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - if ok then - done = true - break - elseif not start then - -- don't ask why ... shouldn't happen - break - end + local lookupmatch = lookupcache[char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local ok + head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + done = true + break + elseif not start then + -- don't ask why ... shouldn't happen + break end - else - report_missing_coverage(dataset,sequence) end end if start then @@ -3236,83 +3508,97 @@ local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlm end local function t_run_multiple(start,stop,font,attr,steps,nofsteps) + local lastd = nil while start ~= stop do local char = ischar(start,font) if char then - local a = attr and getattr(start,0) + local a -- happens often so no assignment is faster + if attr then + a = getattr(start,0) + end + local startnext = getnext(start) 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 - -- if we need more than ligatures we can outline the code and use functions - 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)] + local lookupmatch = lookupcache[char] + if lookupmatch then + -- if we need more than ligatures we can outline the code and use functions + local s = startnext + local ss = nil + local sstop = s == stop + if not s then + s = ss + ss = nil + end + while getid(s) == disc_code do + ss = getnext(s) + s = getfield(s,"replace") + if not s then + s = ss + ss = nil + end + end + local l = nil + local d = 0 + while s do + local char = ischar(s) + if char then + local lg = lookupmatch[char] if lg then + if sstop then + d = 1 + elseif d > 0 then + d = d + 1 + end l = lg s = getnext(s) + sstop = s == stop + if not s then + s = ss + ss = nil + end + while getid(s) == disc_code do + ss = getnext(s) + s = getfield(s,"replace") + if not s then + s = ss + ss = nil + end + end else break end - end - if l and l.ligature then - return true, d > 1 + else + break end end - else - report_missing_coverage(dataset,sequence) + if l and l.ligature then + lastd = d + end end end else -- go on can be a mixed one end - start = getnext(start) + if lastd then + return lastd + end + start = startnext else break end end + return 0 end --- local function d_run_multiple(prev,attr,steps,nofsteps,dataset,sequence,rlmode,handler) --- local a = attr and getattr(prev,0) --- if not a or (a == attr) then --- local char = ischar(prev) -- can be disc --- 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 --- -- we could move all code inline but that makes things even more unreadable --- local h, d, ok = handler(head,prev,dataset,sequence,lookupmatch,rlmode,step,i) --- if ok then --- done = true --- break --- end --- end --- else --- report_missing_coverage(dataset,sequence) --- end --- end --- 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) +local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + local a -- happens often so no assignment is faster + if attr then + a = getattr(sub,0) + end if not a or (a == attr) then - for n in traverse_nodes(sub) do -- only gpos + for n in nextnode, sub do -- only gpos if n == last then break end @@ -3321,16 +3607,12 @@ local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,datase 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 + local lookupmatch = lookupcache[char] + if lookupmatch then + local h, d, ok = handler(sub,n,dataset,sequence,lookupmatch,rlmode,skiphash,step,injection) -- sub was head + if ok then + return true end - else - report_missing_coverage(dataset,sequence) end end end @@ -3341,230 +3623,374 @@ end -- to be checked, nowadays we probably can assume properly matched directions -- so maybe we no longer need a stack +-- local function txtdirstate(start,stack,top,rlparmode) +-- local dir = getdir(start) +-- 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 +-- if top == 1 then +-- top = 0 +-- new = rlparmode +-- else +-- top = top - 1 +-- if stack[top] == "+TRT" then +-- new = -1 +-- end +-- end +-- else +-- new = rlparmode +-- end +-- return getnext(start), top, new +-- end +-- +-- local function pardirstate(start) +-- local dir = getdir(start) +-- local new = 0 +-- if dir == "TLT" then +-- new = 1 +-- elseif dir == "TRT" then +-- new = -1 +-- end +-- return getnext(start), new, new +-- end + local function txtdirstate(start,stack,top,rlparmode) - local dir = getfield(start,"dir") - local new = 1 + local nxt = getnext(start) + local dir = getdir(start) if dir == "+TRT" then top = top + 1 stack[top] = dir - new = -1 + return nxt, top, -1 elseif dir == "+TLT" then top = top + 1 stack[top] = dir + return nxt, top, 1 elseif dir == "-TRT" or dir == "-TLT" then - top = top - 1 - if stack[top] == "+TRT" then - new = -1 + if top == 1 then + return nxt, 0, rlparmode + else + top = top - 1 + if stack[top] == "+TRT" then + return nxt, top, -1 + else + return nxt, top, 1 + end 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) + return nxt, top, rlparmode end - return getnext(start), top, new end local function pardirstate(start) - local dir = getfield(start,"dir") - local new = 0 + local nxt = getnext(start) + local dir = getdir(start) if dir == "TLT" then - new = 1 + return nxt, 1, 1 elseif dir == "TRT" then - new = -1 - end - if trace_directions then - report_process("directions after pardir %a: parmode %a",dir,mref(new)) + return nxt, -1, -1 + else + return nxt, 0, 0 end - return getnext(start), new, new end +-- -- this will become: +-- +-- local getdirection = nuts.getdirection +-- +-- local function txtdirstate1(start,stack,top,rlparmode) +-- local nxt = getnext(start) +-- local dir, sub = getdirection(start) +-- if sub then +-- if top == 1 then +-- return nxt, 0, rlparmode +-- elseif dir < 2 then +-- top = top - 1 +-- if stack[top] == 1 then +-- return nxt, top, -1 +-- else +-- return nxt, top, 1 +-- end +-- else +-- return nxt, top, rlparmode +-- end +-- elseif dir == 1 then +-- top = top + 1 +-- stack[top] = 1 +-- return nxt, top, -1 +-- elseif dir == 0 then +-- top = top + 1 +-- stack[top] = 0 +-- return nxt, top, 1 +-- else +-- return nxt, top, rlparmode +-- end +-- end +-- +-- local function pardirstate1(start) +-- local nxt = getnext(start) +-- local dir = getdirection(start) +-- if dir == 0 then +-- return nxt, 1, 1 +-- elseif dir == 1 then +-- return nxt, -1, -1 +-- else +-- return nxt, 0, 0 +-- end +-- end + otf.helpers = otf.helpers or { } otf.helpers.txtdirstate = txtdirstate otf.helpers.pardirstate = pardirstate -local function featuresprocessor(head,font,attr) - - local sequences = sequencelists[font] -- temp hack +-- This is the main loop. We run over the node list dealing with a specific font. The +-- attribute is a context specific thing. We could work on sub start-stop ranges instead +-- but I wonder if there is that much speed gain (experiments showed that it made not +-- much sense) and we need to keep track of directions anyway. Also at some point I +-- want to play with font interactions and then we do need the full sweeps. Apart from +-- optimizations the principles of processing the features hasn't changed much since +-- the beginning. - if not sequencelists then - return head, false - end - - nesting = nesting + 1 - - if nesting == 1 then +do - 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 + -- reference: + -- + -- 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 + -- + -- used: + -- + -- local a -- happens often so no assignment is faster + -- if attr then + -- if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then + -- a = true + -- end + -- elseif not attribute or getprop(start,a_state) == attribute then + -- a = true + -- end + + -- This is a measurable experimental speedup (only with hyphenated text and multiple + -- fonts per processor call), especially for fonts with lots of contextual lookups. + + local fastdisc = true + local testdics = false + + directives.register("otf.fastdisc",function(v) fastdisc = v end) -- normally enabled + + -- using a merged combined hash as first test saves some 30% on ebgaramond and + -- about 15% on arabtype .. then moving the a test also saves a bit (even when + -- often a is not set at all so that one is a bit debatable + + local otfdataset = nil -- todo: make an installer + + local getfastdisc = { __index = function(t,k) + local v = usesfont(k,currentfont) + t[k] = v + return v + end } + + local getfastspace = { __index = function(t,k) + -- we don't pass the id so that one can overload isspace + local v = isspace(k,threshold) or false + t[k] = v + return v + end } + + function otf.featuresprocessor(head,font,attr,direction,n) + + local sequences = sequencelists[font] -- temp hack + + nesting = nesting + 1 + + if nesting == 1 then + currentfont = font + tfmdata = fontdata[font] + descriptions = tfmdata.descriptions -- only needed in gref so we could pass node there instead + characters = tfmdata.characters -- but this branch is not entered that often anyway + local resources = tfmdata.resources + marks = resources.marks + classes = resources.classes + threshold, + factor = getthreshold(font) + checkmarks = tfmdata.properties.checkmarks + + if not otfdataset then + otfdataset = otf.dataset + end - report_warning("nested call with a different font, level %s, quitting",nesting) - nesting = nesting - 1 - return head, false + discs = fastdisc and n and n > 1 and setmetatable({},getfastdisc) -- maybe inline + spaces = setmetatable({},getfastspace) - end + elseif currentfont ~= font then - -- some 10% faster when no dynamics but hardly measureable on real runs .. but: it only - -- works when we have no other dynamics as otherwise the zero run will be applied to the - -- whole stream for which we then need to pass another variable which we won't + report_warning("nested call with a different font, level %s, quitting",nesting) + nesting = nesting - 1 + return head, false - -- if attr == 0 then - -- attr = false - -- end + end - head = tonut(head) + -- some 10% faster when no dynamics but hardly measureable on real runs .. but: it only + -- works when we have no other dynamics as otherwise the zero run will be applied to the + -- whole stream for which we then need to pass another variable which we won't - if trace_steps then - checkstep(head) - end + -- if attr == 0 then + -- attr = false + -- end - local rlmode = 0 + if trace_steps then + checkstep(head) + end - local done = false - local datasets = otf.dataset(tfmdata,font,attr) - - local forcedisc = alwaysdisc or not attr - - local dirstack = { } -- could move outside function but we can have local runs - - sweephead = { } - - -- We could work on sub start-stop ranges instead but I wonder if there is that - -- much speed gain (experiments showed that it made not much sense) and we need - -- to keep track of directions anyway. Also at some point I want to play with - -- font interactions and then we do need the full sweeps. - - -- Keeping track of the headnode is needed for devanagari (I generalized it a bit - -- so that multiple cases are also covered.) - - -- We don't goto the next node when a disc node is created so that we can then treat - -- the pre, post and replace. It's a bit of a hack but works out ok for most cases. - - for s=1,#datasets do - local dataset = datasets[s] - ----- featurevalue = dataset[1] -- todo: pass to function instead of using a global - local attribute = dataset[2] - local sequence = dataset[3] -- sequences[s] -- also dataset[5] - local rlparmode = 0 - local topstack = 0 - local success = false - local typ = sequence.type - local gpossing = typ == "gpos_single" or typ == "gpos_pair" -- store in dataset - local handler = handlers[typ] - local steps = sequence.steps - local nofsteps = sequence.nofsteps - if not steps then - -- this permits injection, watch the different arguments - local h, d, ok = handler(head,head,dataset,sequence,nil,nil,nil,0,font,attr) - if ok then - success = true - if h then + local initialrl = direction == "TRT" and -1 or 0 + -- local initialrl = (direction == 1 or direction == "TRT") and -1 or 0 + + -- local done = false + local datasets = otfdataset(tfmdata,font,attr) + local dirstack = { nil } -- could move outside function but we can have local runs + sweephead = { } + -- sweephead = { a = 1, b = 1 } sweephead.a = nil sweephead.b = nil + + -- Keeping track of the headnode is needed for devanagari. (I generalized it a bit + -- so that multiple cases are also covered.) We could prepend a temp node. + + -- We don't goto the next node when a disc node is created so that we can then treat + -- the pre, post and replace. It's a bit of a hack but works out ok for most cases. + + for s=1,#datasets do + local dataset = datasets[s] + local attribute = dataset[2] + local sequence = dataset[3] -- sequences[s] -- also dataset[5] + local rlparmode = initialrl + local topstack = 0 + local typ = sequence.type + local gpossing = typ == "gpos_single" or typ == "gpos_pair" -- store in dataset + local forcetestrun = typ == "gsub_ligature" -- testrun is only for ligatures + local handler = handlers[typ] -- store in dataset + local steps = sequence.steps + local nofsteps = sequence.nofsteps + local skiphash = sequence.skiphash + + if not steps then + -- This permits injection, watch the different arguments. Watch out, the arguments passed + -- are not frozen as we might extend or change this. Is this used at all apart from some + -- experiments? + local h, ok = handler(head,dataset,sequence,initialrl,font,attr) -- less arguments now + -- if ok then + -- done = true + -- end + if h and h ~= head then head = h end - end - elseif typ == "gsub_reversecontextchain" then - -- this is a limited case, no special treatments like 'init' etc - 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 - -- todo: disc? - local ok - head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - if ok then - success = true - break + elseif typ == "gsub_reversecontextchain" then + -- + -- This might need a check: if we have #before or #after > 0 then we might need to reverse + -- the before and after lists in the loader. But first I need to see a font that uses multiple + -- matches. + -- + local start = find_node_tail(head) + local rlmode = 0 -- how important is this .. do we need to check for dir? + local merged = steps.merged + while start do + local char = ischar(start,font) + if char then + local m = merged[char] + if m then + local a -- happens often so no assignment is faster + if attr then + a = getattr(start,0) + end + if not a or (a == attr) then + for i=m[1],m[2] do + local step = steps[i] + -- for i=1,#m do + -- local step = m[i] + local lookupcache = step.coverage + local lookupmatch = lookupcache[char] + if lookupmatch then + local ok + head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + -- done = true + break + end end end + if start then + start = getprev(start) + end else - report_missing_coverage(dataset,sequence) + start = getprev(start) end - end - if start then + else start = getprev(start) end else start = getprev(start) end - else - start = getprev(start) end - end - else - local start = head -- local ? - rlmode = 0 -- to be checked ? - if nofsteps == 1 then -- happens often - local step = steps[1] - local lookupcache = step.coverage - if not lookupcache then - report_missing_coverage(dataset,sequence) - else + else + local start = head + local rlmode = initialrl + if nofsteps == 1 then -- happens often + local step = steps[1] + local lookupcache = step.coverage 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) + if skiphash and skiphash[char] then -- we never needed it here but let's try + start = getnext(start) 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 - -- elseif gpossing and zwnjruns and char == zwnj then - -- discrun(start,d_run,font,attr,lookupcache) + local a -- happens often so no assignment is faster + if attr then + if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then + a = true + end + elseif not attribute or getprop(start,a_state) == attribute then + a = true end - -- elseif gpossing and zwnjruns and char == zwnj then - -- discrun(start,d_run,font,attr,lookupcache) - end - if start then - start = getnext(start) + if a then + local ok + head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + -- if ok then + -- done = true + -- end + if start then + start = getnext(start) + end + else + start = getnext(start) + end + else + start = getnext(start) end - else - start = getnext(start) end - elseif char == false then - -- whatever glyph - start = getnext(start) + elseif char == false or id == glue_code then + -- a different font|state or glue (happens often) + start = getnext(start) elseif id == disc_code then - -- ctx: we could assume the same attr as the surrounding glyphs but then we loose - -- the option to have interesting disc nodes (we gain upto 10% on extreme tests); - -- if a disc would have a font field we could even be more strict (see oldstyle test - -- case) - local a = forcedisc or getsubtype(start) == discretionary_code or getattr(start,0) == attr - if a then - -- local attr = false + if not discs or discs[start] == true 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) + start, ok = kernrun(start,k_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + elseif forcetestrun then + start, ok = testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) else - start, ok = comprun(start,c_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,handler) - end - if ok then - success = true + start, ok = comprun(start,c_run_single, font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) end + -- if ok then + -- done = true + -- end else start = getnext(start) end @@ -3578,97 +4004,199 @@ local function featuresprocessor(head,font,attr) 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 - -- we could move all code inline but that makes things even more unreadable - local ok - head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - if ok then - success = true - break - elseif not start then - -- don't ask why ... shouldn't happen - break - -- elseif gpossing and zwnjruns and char == zwnj then - -- discrun(start,d_run,font,attr,steps,nofsteps) + else + local merged = steps.merged + while start do + local char, id = ischar(start,font) + if char then + if skiphash and skiphash[char] then -- we never needed it here but let's try + start = getnext(start) + else + local m = merged[char] + if m then + local a -- happens often so no assignment is faster + if attr then + if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then + a = true + end + elseif not attribute or getprop(start,a_state) == attribute then + a = true + end + if a then + for i=m[1],m[2] do + local step = steps[i] + -- for i=1,#m do + -- local step = m[i] + local lookupcache = step.coverage + local lookupmatch = lookupcache[char] + if lookupmatch then + -- we could move all code inline but that makes things even more unreadable + local ok + head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + -- done = true + break + elseif not start then + -- don't ask why ... shouldn't happen + break + end + end end - -- elseif gpossing and zwnjruns and char == zwnj then - -- discrun(start,d_run,font,attr,steps,nofsteps) + if start then + start = getnext(start) + end + else + start = getnext(start) end else - report_missing_coverage(dataset,sequence) + start = getnext(start) end end - if start then - start = getnext(start) - end - else + elseif char == false or id == glue_code then + -- a different font|state or glue (happens often) start = getnext(start) - end - elseif char == false then - start = getnext(start) - elseif id == disc_code then - -- see comment above - 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) + elseif id == disc_code then + if not discs or discs[start] == true then + local ok + if gpossing then + start, ok = kernrun(start,k_run_multiple, font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + elseif forcetestrun then + start, ok = testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + else + start, ok = comprun(start,c_run_multiple, font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + end + -- if ok then + -- done = true + -- end else - start, ok = comprun(start,c_run_multiple, font,attr,steps,nofsteps,dataset,sequence,rlmode,handler) - end - if ok then - success = true + 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 - 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 + + if trace_steps then -- ? + registerstep(head) + end + end - if success then - done = true + nesting = nesting - 1 + + -- return head, done + return head + end + + -- This is not an official helper and used for tracing experiments. It can be changed as I like + -- at any moment. At some point it might be used in a module that can help font development. + + function otf.datasetpositionprocessor(head,font,direction,dataset) + + currentfont = font + tfmdata = fontdata[font] + descriptions = tfmdata.descriptions -- only needed in gref so we could pass node there instead + characters = tfmdata.characters -- but this branch is not entered that often anyway + local resources = tfmdata.resources + marks = resources.marks + classes = resources.classes + threshold, + factor = getthreshold(font) + checkmarks = tfmdata.properties.checkmarks + + if type(dataset) == "number" then + dataset = otfdataset(tfmdata,font,0)[dataset] end - if trace_steps then -- ? - registerstep(head) + + local sequence = dataset[3] -- sequences[s] -- also dataset[5] + local typ = sequence.type + -- local gpossing = typ == "gpos_single" or typ == "gpos_pair" -- store in dataset + + -- gpos_contextchain gpos_context + + -- if not gpossing then + -- return head, false + -- end + + local handler = handlers[typ] -- store in dataset + local steps = sequence.steps + local nofsteps = sequence.nofsteps + + local done = false + local dirstack = { nil } -- could move outside function but we can have local runs (maybe a few more nils) + local start = head + local initialrl = direction == "TRT" and -1 or 0 + -- local initialrl = (direction == 1 or direction == "TRT") and -1 or 0 + local rlmode = initialrl + local rlparmode = initialrl + local topstack = 0 + local merged = steps.merged + + -- local matches = false + local position = 0 + + while start do + local char, id = ischar(start,font) + if char then + position = position + 1 + local m = merged[char] + if m then + if skiphash and skiphash[char] then -- we never needed it here but let's try + start = getnext(start) + else + for i=m[1],m[2] do + local step = steps[i] + local lookupcache = step.coverage + local lookupmatch = lookupcache[char] + if lookupmatch then + local ok + head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + -- if matches then + -- matches[position] = i + -- else + -- matches = { [position] = i } + -- end + break + elseif not start then + break + end + end + end + if start then + start = getnext(start) + end + end + else + start = getnext(start) + end + elseif char == false or id == glue_code then + -- a different font|state or glue (happens often) + start = getnext(start) + 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 + return head end - nesting = nesting - 1 - head = tonode(head) + -- end of experiment - return head, done end -- so far @@ -3676,32 +4204,49 @@ end local plugins = { } otf.plugins = plugins +local report = logs.reporter("fonts") + function otf.registerplugin(name,f) if type(name) == "string" and type(f) == "function" then plugins[name] = { name, f } + report() + report("plugin %a has been loaded, please be aware of possible side effects",name) + report() + if logs.pushtarget then + logs.pushtarget("log") + end + report("Plugins are not officially supported unless stated otherwise. This is because") + report("they bypass the regular font handling and therefore some features in ConTeXt") + report("(especially those related to fonts) might not work as expected or might not work") + report("at all. Some plugins are for testing and development only and might change") + report("whenever we feel the need for it.") + report() + if logs.poptarget then + logs.poptarget() + end end end -local function plugininitializer(tfmdata,value) +function otf.plugininitializer(tfmdata,value) if type(value) == "string" then tfmdata.shared.plugin = plugins[value] end end -local function pluginprocessor(head,font) +function otf.pluginprocessor(head,font,attr,direction) -- n 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) + return p[2](head,font,attr,direction) else return head, false end end -local function featuresinitializer(tfmdata,value) +function otf.featuresinitializer(tfmdata,value) -- nothing done here any more end @@ -3711,67 +4256,113 @@ registerotffeature { default = true, initializers = { position = 1, - node = featuresinitializer, - plug = plugininitializer, + node = otf.featuresinitializer, + plug = otf.plugininitializer, }, processors = { - node = featuresprocessor, - plug = pluginprocessor, + node = otf.featuresprocessor, + plug = otf.pluginprocessor, } } -otf.nodemodeinitializer = featuresinitializer -otf.featuresprocessor = featuresprocessor - --- This can be used for extra handlers, but should be used with care! - -otf.handlers = handlers -- used in devanagari +-- This can be used for extra handlers, but should be used with care! We implement one +-- here but some more can be found in the osd (script devanagary) file. Now watch out: +-- when a handlers has steps, it is called as the other ones, but when we have no steps, +-- we use a different call: +-- +-- function(head,dataset,sequence,initialrl,font,attr) +-- return head, done +-- end +-- +-- Also see (!!). --- We implement one here: +otf.handlers = handlers local setspacekerns = nodes.injections.setspacekerns if not setspacekerns then os.exit() end +local tag = "kern" -- maybe some day a merge + if fontfeatures then - function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr) + function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,attr) local features = fontfeatures[font] - local enabled = features and features.spacekern and features.kern + local enabled = features and features.spacekern and features[tag] if enabled then setspacekerns(font,sequence) end - return head, start, enabled + return head, enabled end else -- generic (no hashes) - function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr) + function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,attr) local shared = fontdata[font].shared local features = shared and shared.features - local enabled = features and features.spacekern and features.kern + local enabled = features and features.spacekern and features[tag] if enabled then setspacekerns(font,sequence) end - return head, start, enabled + return head, enabled end end +-- There are fonts out there that change the space but we don't do that kind of +-- things in TeX. + 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 - -- maybe an issue - elseif coverage[32] then - return true - else - for k, v in next, coverage do - if v[32] then - return true + local resources = data.resources + local sequences = resources.sequences + local validgpos = resources.features.gpos + if validgpos and sequences then + for i=1,#sequences do + local sequence = sequences[i] + local steps = sequence.steps + if steps and sequence.features[tag] then + local kind = sequence.type + if kind == "gpos_pair" or kind == "gpos_single" then + for i=1,#steps do + local step = steps[i] + local coverage = step.coverage + local rules = step.rules + if rules then + -- not now: analyze (simple) rules + elseif not coverage then + -- nothing to do + elseif kind == "gpos_single" then + -- maybe a message that we ignore + elseif kind == "gpos_pair" then + local format = step.format + if format == "move" or format == "kern" then + local kerns = coverage[32] + if kerns then + return true + end + for k, v in next, coverage do + if v[32] then + return true + end + end + elseif format == "pair" then + local kerns = coverage[32] + if kerns then + for k, v in next, kerns do + local one = v[1] + if one and one ~= true then + return true + end + end + end + for k, v in next, coverage do + local kern = v[32] + if kern then + local one = kern[1] + if one and one ~= true then + return true + end + end + end + end end end end @@ -3788,112 +4379,120 @@ otf.readers.registerextender { end } --- we merge the lookups but we still honor the language / script - local function spaceinitializer(tfmdata,value) -- attr local resources = tfmdata.resources local spacekerns = resources and resources.spacekerns - local properties = tfmdata.properties if value and spacekerns == nil then + local rawdata = tfmdata.shared and tfmdata.shared.rawdata + local properties = rawdata.properties 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 - -- not now: analyze (simple) rules - elseif coverage then - -- what to do if we have no [1] but only [2] - 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] + local validgpos = resources.features.gpos + if validgpos and sequences then + 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 + -- we don't support space kerns in other features + local kern = sequence.features[tag] + if kern then + local kind = sequence.type + if kind == "gpos_pair" or kind == "gpos_single" 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 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] + for i=1,#steps do + local step = steps[i] + local coverage = step.coverage + local rules = step.rules + if rules then + -- not now: analyze (simple) rules + elseif not coverage then + -- nothng to do + elseif kind == "gpos_single" then + -- makes no sense in TeX + elseif kind == "gpos_pair" then + local format = step.format + if format == "move" or format == "kern" then + local kerns = coverage[32] + if kerns then + for k, v in next, kerns do + right[k] = v + end + end + for k, v in next, coverage do + local kern = v[32] + if kern then + left[k] = kern + end + end + elseif format == "pair" then + local kerns = coverage[32] + if kerns then + for k, v in next, kerns do + local one = v[1] + if one and one ~= true then + right[k] = one[3] + end + end + end + for k, v in next, coverage do + local kern = v[32] + if kern then + local one = kern[1] + if one and one ~= true then + left[k] = one[3] + end + end end end end end + last = i end + else + -- no steps ... needed for old one ... we could use the basekerns + -- instead end - last = i end - else - -- no steps ... needed for old one ... we could use the basekerns - -- instead - 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, + 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, } - insert(sequences,last,triggersequence) + if last > 0 then + local triggersequence = { + -- no steps, see (!!) + features = { [tag] = feat or { dflt = { dflt = true, } } }, + flags = noflags, + name = "trigger_space_kerns", + order = { tag }, + type = "trigger_space_kerns", + left = left, + right = right, + } + insert(sequences,last,triggersequence) + end end - else - spacekerns = false end - else - spacekerns = false end resources.spacekerns = spacekerns end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ott.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ott.lua new file mode 100644 index 00000000000..88da136ac70 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ott.lua @@ -0,0 +1,1159 @@ +if not modules then modules = { } end modules ["font-ott"] = { + 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", + -- dataonly = true, +} + +local type, next, tonumber, tostring, rawget, rawset = type, next, tonumber, tostring, rawget, rawset +local gsub, lower, format, match, gmatch, find = string.gsub, string.lower, string.format, string.match, string.gmatch, string.find +local sequenced = table.sequenced +local is_boolean = string.is_boolean + +local setmetatableindex = table.setmetatableindex +local setmetatablenewindex = table.setmetatablenewindex +local allocate = utilities.storage.allocate + +local fonts = fonts +local otf = fonts.handlers.otf +local otffeatures = otf.features + +local tables = otf.tables or { } +otf.tables = tables + +local statistics = otf.statistics or { } +otf.statistics = statistics + +local scripts = allocate { + ["arab"] = "arabic", + ["armi"] = "imperial aramaic", + ["armn"] = "armenian", + ["avst"] = "avestan", + ["bali"] = "balinese", + ["bamu"] = "bamum", + ["batk"] = "batak", + ["beng"] = "bengali", + ["bng2"] = "bengali variant 2", + ["bopo"] = "bopomofo", + ["brah"] = "brahmi", + ["brai"] = "braille", + ["bugi"] = "buginese", + ["buhd"] = "buhid", + ["byzm"] = "byzantine music", + ["cakm"] = "chakma", + ["cans"] = "canadian syllabics", + ["cari"] = "carian", + ["cham"] = "cham", + ["cher"] = "cherokee", + ["copt"] = "coptic", + ["cprt"] = "cypriot syllabary", + ["cyrl"] = "cyrillic", + ["deva"] = "devanagari", + ["dev2"] = "devanagari variant 2", + ["dsrt"] = "deseret", + ["egyp"] = "egyptian heiroglyphs", + ["ethi"] = "ethiopic", + ["geor"] = "georgian", + ["glag"] = "glagolitic", + ["goth"] = "gothic", + ["grek"] = "greek", + ["gujr"] = "gujarati", + ["gjr2"] = "gujarati variant 2", + ["guru"] = "gurmukhi", + ["gur2"] = "gurmukhi variant 2", + ["hang"] = "hangul", + ["hani"] = "cjk ideographic", + ["hano"] = "hanunoo", + ["hebr"] = "hebrew", + ["ital"] = "old italic", + ["jamo"] = "hangul jamo", + ["java"] = "javanese", + ["kali"] = "kayah li", + ["kana"] = "hiragana and katakana", + ["khar"] = "kharosthi", + ["khmr"] = "khmer", + ["knda"] = "kannada", + ["knd2"] = "kannada variant 2", + ["kthi"] = "kaithi", + ["lana"] = "tai tham", + ["lao" ] = "lao", + ["latn"] = "latin", + ["lepc"] = "lepcha", + ["limb"] = "limbu", + ["linb"] = "linear b", + ["lisu"] = "lisu", + ["lyci"] = "lycian", + ["lydi"] = "lydian", + ["mand"] = "mandaic and mandaean", + ["math"] = "mathematical alphanumeric symbols", + ["merc"] = "meroitic cursive", + ["mero"] = "meroitic hieroglyphs", + ["mlym"] = "malayalam", + ["mlm2"] = "malayalam variant 2", + ["mong"] = "mongolian", + ["mtei"] = "meitei Mayek", + ["musc"] = "musical symbols", + ["mym2"] = "myanmar variant 2", + ["mymr"] = "myanmar", + ["nko" ] = 'n"ko', + ["ogam"] = "ogham", + ["olck"] = "ol chiki", + ["orkh"] = "old turkic and orkhon runic", + ["orya"] = "oriya", + ["ory2"] = "odia variant 2", + ["osma"] = "osmanya", + ["phag"] = "phags-pa", + ["phli"] = "inscriptional pahlavi", + ["phnx"] = "phoenician", + ["prti"] = "inscriptional parthian", + ["rjng"] = "rejang", + ["runr"] = "runic", + ["samr"] = "samaritan", + ["sarb"] = "old south arabian", + ["saur"] = "saurashtra", + ["shaw"] = "shavian", + ["shrd"] = "sharada", + ["sinh"] = "sinhala", + ["sora"] = "sora sompeng", + ["sund"] = "sundanese", + ["sylo"] = "syloti nagri", + ["syrc"] = "syriac", + ["tagb"] = "tagbanwa", + ["takr"] = "takri", + ["tale"] = "tai le", + ["talu"] = "tai lu", + ["taml"] = "tamil", + ["tavt"] = "tai viet", + ["telu"] = "telugu", + ["tel2"] = "telugu variant 2", + ["tfng"] = "tifinagh", + ["tglg"] = "tagalog", + ["thaa"] = "thaana", + ["thai"] = "thai", + ["tibt"] = "tibetan", + ["tml2"] = "tamil variant 2", + ["ugar"] = "ugaritic cuneiform", + ["vai" ] = "vai", + ["xpeo"] = "old persian cuneiform", + ["xsux"] = "sumero-akkadian cuneiform", + ["yi" ] = "yi", +} + +local languages = allocate { + ["aba" ] = "abaza", + ["abk" ] = "abkhazian", + ["ach" ] = "acholi", + ["acr" ] = "achi", + ["ady" ] = "adyghe", + ["afk" ] = "afrikaans", + ["afr" ] = "afar", + ["agw" ] = "agaw", + ["aio" ] = "aiton", + ["aka" ] = "akan", + ["als" ] = "alsatian", + ["alt" ] = "altai", + ["amh" ] = "amharic", + ["ang" ] = "anglo-saxon", + ["apph"] = "phonetic transcription—americanist conventions", + ["ara" ] = "arabic", + ["arg" ] = "aragonese", + ["ari" ] = "aari", + ["ark" ] = "rakhine", + ["asm" ] = "assamese", + ["ast" ] = "asturian", + ["ath" ] = "athapaskan", + ["avr" ] = "avar", + ["awa" ] = "awadhi", + ["aym" ] = "aymara", + ["azb" ] = "torki", + ["aze" ] = "azerbaijani", + ["bad" ] = "badaga", + ["bad0"] = "banda", + ["bag" ] = "baghelkhandi", + ["bal" ] = "balkar", + ["ban" ] = "balinese", + ["bar" ] = "bavarian", + ["bau" ] = "baulé", + ["bbc" ] = "batak toba", + ["bbr" ] = "berber", + ["bch" ] = "bench", + ["bcr" ] = "bible cree", + ["bdy" ] = "bandjalang", + ["bel" ] = "belarussian", + ["bem" ] = "bemba", + ["ben" ] = "bengali", + ["bgc" ] = "haryanvi", + ["bgq" ] = "bagri", + ["bgr" ] = "bulgarian", + ["bhi" ] = "bhili", + ["bho" ] = "bhojpuri", + ["bik" ] = "bikol", + ["bil" ] = "bilen", + ["bis" ] = "bislama", + ["bjj" ] = "kanauji", + ["bkf" ] = "blackfoot", + ["bli" ] = "baluchi", + ["blk" ] = "pa'o karen", + ["bln" ] = "balante", + ["blt" ] = "balti", + ["bmb" ] = "bambara (bamanankan)", + ["bml" ] = "bamileke", + ["bos" ] = "bosnian", + ["bpy" ] = "bishnupriya manipuri", + ["bre" ] = "breton", + ["brh" ] = "brahui", + ["bri" ] = "braj bhasha", + ["brm" ] = "burmese", + ["brx" ] = "bodo", + ["bsh" ] = "bashkir", + ["bti" ] = "beti", + ["bts" ] = "batak simalungun", + ["bug" ] = "bugis", + ["cak" ] = "kaqchikel", + ["cat" ] = "catalan", + ["cbk" ] = "zamboanga chavacano", + ["ceb" ] = "cebuano", + ["cgg" ] = "chiga", + ["cha" ] = "chamorro", + ["che" ] = "chechen", + ["chg" ] = "chaha gurage", + ["chh" ] = "chattisgarhi", + ["chi" ] = "chichewa (chewa, nyanja)", + ["chk" ] = "chukchi", + ["chk0"] = "chuukese", + ["cho" ] = "choctaw", + ["chp" ] = "chipewyan", + ["chr" ] = "cherokee", + ["chu" ] = "chuvash", + ["chy" ] = "cheyenne", + ["cmr" ] = "comorian", + ["cop" ] = "coptic", + ["cor" ] = "cornish", + ["cos" ] = "corsican", + ["cpp" ] = "creoles", + ["cre" ] = "cree", + ["crr" ] = "carrier", + ["crt" ] = "crimean tatar", + ["csb" ] = "kashubian", + ["csl" ] = "church slavonic", + ["csy" ] = "czech", + ["ctg" ] = "chittagonian", + ["cuk" ] = "san blas kuna", + ["dan" ] = "danish", + ["dar" ] = "dargwa", + ["dax" ] = "dayi", + ["dcr" ] = "woods cree", + ["deu" ] = "german", + ["dgo" ] = "dogri", + ["dgr" ] = "dogri", + ["dhg" ] = "dhangu", + ["dhv" ] = "divehi (dhivehi, maldivian)", + ["diq" ] = "dimli", + ["div" ] = "divehi (dhivehi, maldivian)", + ["djr" ] = "zarma", + ["djr0"] = "djambarrpuyngu", + ["dng" ] = "dangme", + ["dnj" ] = "dan", + ["dnk" ] = "dinka", + ["dri" ] = "dari", + ["duj" ] = "dhuwal", + ["dun" ] = "dungan", + ["dzn" ] = "dzongkha", + ["ebi" ] = "ebira", + ["ecr" ] = "eastern cree", + ["edo" ] = "edo", + ["efi" ] = "efik", + ["ell" ] = "greek", + ["emk" ] = "eastern maninkakan", + ["eng" ] = "english", + ["erz" ] = "erzya", + ["esp" ] = "spanish", + ["esu" ] = "central yupik", + ["eti" ] = "estonian", + ["euq" ] = "basque", + ["evk" ] = "evenki", + ["evn" ] = "even", + ["ewe" ] = "ewe", + ["fan" ] = "french antillean", + ["fan0"] = " fang", + ["far" ] = "persian", + ["fat" ] = "fanti", + ["fin" ] = "finnish", + ["fji" ] = "fijian", + ["fle" ] = "dutch (flemish)", + ["fne" ] = "forest nenets", + ["fon" ] = "fon", + ["fos" ] = "faroese", + ["fra" ] = "french", + ["frc" ] = "cajun french", + ["fri" ] = "frisian", + ["frl" ] = "friulian", + ["frp" ] = "arpitan", + ["fta" ] = "futa", + ["ful" ] = "fulah", + ["fuv" ] = "nigerian fulfulde", + ["gad" ] = "ga", + ["gae" ] = "scottish gaelic (gaelic)", + ["gag" ] = "gagauz", + ["gal" ] = "galician", + ["gar" ] = "garshuni", + ["gaw" ] = "garhwali", + ["gez" ] = "ge'ez", + ["gih" ] = "githabul", + ["gil" ] = "gilyak", + ["gil0"] = "kiribati (gilbertese)", + ["gkp" ] = "kpelle (guinea)", + ["glk" ] = "gilaki", + ["gmz" ] = "gumuz", + ["gnn" ] = "gumatj", + ["gog" ] = "gogo", + ["gon" ] = "gondi", + ["grn" ] = "greenlandic", + ["gro" ] = "garo", + ["gua" ] = "guarani", + ["guc" ] = "wayuu", + ["guf" ] = "gupapuyngu", + ["guj" ] = "gujarati", + ["guz" ] = "gusii", + ["hai" ] = "haitian (haitian creole)", + ["hal" ] = "halam", + ["har" ] = "harauti", + ["hau" ] = "hausa", + ["haw" ] = "hawaiian", + ["hay" ] = "haya", + ["haz" ] = "hazaragi", + ["hbn" ] = "hammer-banna", + ["her" ] = "herero", + ["hil" ] = "hiligaynon", + ["hin" ] = "hindi", + ["hma" ] = "high mari", + ["hmn" ] = "hmong", + ["hmo" ] = "hiri motu", + ["hnd" ] = "hindko", + ["ho" ] = "ho", + ["hri" ] = "harari", + ["hrv" ] = "croatian", + ["hun" ] = "hungarian", + ["hye" ] = "armenian", + ["hye0"] = "armenian east", + ["iba" ] = "iban", + ["ibb" ] = "ibibio", + ["ibo" ] = "igbo", + ["ido" ] = "ido", + ["ijo" ] = "ijo languages", + ["ile" ] = "interlingue", + ["ilo" ] = "ilokano", + ["ina" ] = "interlingua", + ["ind" ] = "indonesian", + ["ing" ] = "ingush", + ["inu" ] = "inuktitut", + ["ipk" ] = "inupiat", + ["ipph"] = "phonetic transcription—ipa conventions", + ["iri" ] = "irish", + ["irt" ] = "irish traditional", + ["isl" ] = "icelandic", + ["ism" ] = "inari sami", + ["ita" ] = "italian", + ["iwr" ] = "hebrew", + ["jam" ] = "jamaican creole", + ["jan" ] = "japanese", + ["jav" ] = "javanese", + ["jbo" ] = "lojban", + ["jii" ] = "yiddish", + ["jud" ] = "ladino", + ["jul" ] = "jula", + ["kab" ] = "kabardian", + ["kab0"] = "kabyle", + ["kac" ] = "kachchi", + ["kal" ] = "kalenjin", + ["kan" ] = "kannada", + ["kar" ] = "karachay", + ["kat" ] = "georgian", + ["kaz" ] = "kazakh", + ["kde" ] = "makonde", + ["kea" ] = "kabuverdianu (crioulo)", + ["keb" ] = "kebena", + ["kek" ] = "kekchi", + ["kge" ] = "khutsuri georgian", + ["kha" ] = "khakass", + ["khk" ] = "khanty-kazim", + ["khm" ] = "khmer", + ["khs" ] = "khanty-shurishkar", + ["kht" ] = "khamti shan", + ["khv" ] = "khanty-vakhi", + ["khw" ] = "khowar", + ["kik" ] = "kikuyu (gikuyu)", + ["kir" ] = "kirghiz (kyrgyz)", + ["kis" ] = "kisii", + ["kiu" ] = "kirmanjki", + ["kjd" ] = "southern kiwai", + ["kjp" ] = "eastern pwo karen", + ["kkn" ] = "kokni", + ["klm" ] = "kalmyk", + ["kmb" ] = "kamba", + ["kmn" ] = "kumaoni", + ["kmo" ] = "komo", + ["kms" ] = "komso", + ["knr" ] = "kanuri", + ["kod" ] = "kodagu", + ["koh" ] = "korean old hangul", + ["kok" ] = "konkani", + ["kom" ] = "komi", + ["kon" ] = "kikongo", + ["kon0"] = "kongo", + ["kop" ] = "komi-permyak", + ["kor" ] = "korean", + ["kos" ] = "kosraean", + ["koz" ] = "komi-zyrian", + ["kpl" ] = "kpelle", + ["kri" ] = "krio", + ["krk" ] = "karakalpak", + ["krl" ] = "karelian", + ["krm" ] = "karaim", + ["krn" ] = "karen", + ["krt" ] = "koorete", + ["ksh" ] = "kashmiri", + ["ksh0"] = "ripuarian", + ["ksi" ] = "khasi", + ["ksm" ] = "kildin sami", + ["ksw" ] = "s’gaw karen", + ["kua" ] = "kuanyama", + ["kui" ] = "kui", + ["kul" ] = "kulvi", + ["kum" ] = "kumyk", + ["kur" ] = "kurdish", + ["kuu" ] = "kurukh", + ["kuy" ] = "kuy", + ["kyk" ] = "koryak", + ["kyu" ] = "western kayah", + ["lad" ] = "ladin", + ["lah" ] = "lahuli", + ["lak" ] = "lak", + ["lam" ] = "lambani", + ["lao" ] = "lao", + ["lat" ] = "latin", + ["laz" ] = "laz", + ["lcr" ] = "l-cree", + ["ldk" ] = "ladakhi", + ["lez" ] = "lezgi", + ["lij" ] = "ligurian", + ["lim" ] = "limburgish", + ["lin" ] = "lingala", + ["lis" ] = "lisu", + ["ljp" ] = "lampung", + ["lki" ] = "laki", + ["lma" ] = "low mari", + ["lmb" ] = "limbu", + ["lmo" ] = "lombard", + ["lmw" ] = "lomwe", + ["lom" ] = "loma", + ["lrc" ] = "luri", + ["lsb" ] = "lower sorbian", + ["lsm" ] = "lule sami", + ["lth" ] = "lithuanian", + ["ltz" ] = "luxembourgish", + ["lua" ] = "luba-lulua", + ["lub" ] = "luba-katanga", + ["lug" ] = "ganda", + ["luh" ] = "luyia", + ["luo" ] = "luo", + ["lvi" ] = "latvian", + ["mad" ] = "madura", + ["mag" ] = "magahi", + ["mah" ] = "marshallese", + ["maj" ] = "majang", + ["mak" ] = "makhuwa", + ["mal" ] = "malayalam reformed", + ["mam" ] = "mam", + ["man" ] = "mansi", + ["map" ] = "mapudungun", + ["mar" ] = "marathi", + ["maw" ] = "marwari", + ["mbn" ] = "mbundu", + ["mch" ] = "manchu", + ["mcr" ] = "moose cree", + ["mde" ] = "mende", + ["mdr" ] = "mandar", + ["men" ] = "me'en", + ["mer" ] = "meru", + ["mfe" ] = "morisyen", + ["min" ] = "minangkabau", + ["miz" ] = "mizo", + ["mkd" ] = "macedonian", + ["mkr" ] = "makasar", + ["mkw" ] = "kituba", + ["mle" ] = "male", + ["mlg" ] = "malagasy", + ["mln" ] = "malinke", + ["mly" ] = "malay", + ["mnd" ] = "mandinka", + ["mng" ] = "mongolian", + ["mni" ] = "manipuri", + ["mnk" ] = "maninka", + ["mnx" ] = "manx", + ["moh" ] = "mohawk", + ["mok" ] = "moksha", + ["mol" ] = "moldavian", + ["mon" ] = "mon", + ["mor" ] = "moroccan", + ["mos" ] = "mossi", + ["mri" ] = "maori", + ["mth" ] = "maithili", + ["mts" ] = "maltese", + ["mun" ] = "mundari", + ["mus" ] = "muscogee", + ["mwl" ] = "mirandese", + ["mww" ] = "hmong daw", + ["myn" ] = "mayan", + ["mzn" ] = "mazanderani", + ["nag" ] = "naga-assamese", + ["nah" ] = "nahuatl", + ["nan" ] = "nanai", + ["nap" ] = "neapolitan", + ["nas" ] = "naskapi", + ["nau" ] = "nauruan", + ["nav" ] = "navajo", + ["ncr" ] = "n-cree", + ["ndb" ] = "ndebele", + ["ndc" ] = "ndau", + ["ndg" ] = "ndonga", + ["nds" ] = "low saxon", + ["nep" ] = "nepali", + ["new" ] = "newari", + ["nga" ] = "ngbaka", + ["ngr" ] = "nagari", + ["nhc" ] = "norway house cree", + ["nis" ] = "nisi", + ["niu" ] = "niuean", + ["nkl" ] = "nyankole", + ["nko" ] = "n'ko", + ["nld" ] = "dutch", + ["noe" ] = "nimadi", + ["nog" ] = "nogai", + ["nor" ] = "norwegian", + ["nov" ] = "novial", + ["nsm" ] = "northern sami", + ["nso" ] = "sotho, northern", + ["nta" ] = "northern tai", + ["nto" ] = "esperanto", + ["nym" ] = "nyamwezi", + ["nyn" ] = "norwegian nynorsk", + ["oci" ] = "occitan", + ["ocr" ] = "oji-cree", + ["ojb" ] = "ojibway", + ["ori" ] = "odia", + ["oro" ] = "oromo", + ["oss" ] = "ossetian", + ["paa" ] = "palestinian aramaic", + ["pag" ] = "pangasinan", + ["pal" ] = "pali", + ["pam" ] = "pampangan", + ["pan" ] = "punjabi", + ["pap" ] = "palpa", + ["pap0"] = "papiamentu", + ["pas" ] = "pashto", + ["pau" ] = "palauan", + ["pcc" ] = "bouyei", + ["pcd" ] = "picard", + ["pdc" ] = "pennsylvania german", + ["pgr" ] = "polytonic greek", + ["phk" ] = "phake", + ["pih" ] = "norfolk", + ["pil" ] = "filipino", + ["plg" ] = "palaung", + ["plk" ] = "polish", + ["pms" ] = "piemontese", + ["pnb" ] = "western panjabi", + ["poh" ] = "pocomchi", + ["pon" ] = "pohnpeian", + ["pro" ] = "provencal", + ["ptg" ] = "portuguese", + ["pwo" ] = "western pwo karen", + ["qin" ] = "chin", + ["quc" ] = "k’iche’", + ["quh" ] = "quechua (bolivia)", + ["quz" ] = "quechua", + ["qvi" ] = "quechua (ecuador)", + ["qwh" ] = "quechua (peru)", + ["raj" ] = "rajasthani", + ["rar" ] = "rarotongan", + ["rbu" ] = "russian buriat", + ["rcr" ] = "r-cree", + ["rej" ] = "rejang", + ["ria" ] = "riang", + ["rif" ] = "tarifit", + ["rit" ] = "ritarungo", + ["rkw" ] = "arakwal", + ["rms" ] = "romansh", + ["rmy" ] = "vlax romani", + ["rom" ] = "romanian", + ["roy" ] = "romany", + ["rsy" ] = "rusyn", + ["rtm" ] = "rotuman", + ["rua" ] = "kinyarwanda", + ["run" ] = "rundi", + ["rup" ] = "aromanian", + ["rus" ] = "russian", + ["sad" ] = "sadri", + ["san" ] = "sanskrit", + ["sas" ] = "sasak", + ["sat" ] = "santali", + ["say" ] = "sayisi", + ["scn" ] = "sicilian", + ["sco" ] = "scots", + ["sek" ] = "sekota", + ["sel" ] = "selkup", + ["sga" ] = "old irish", + ["sgo" ] = "sango", + ["sgs" ] = "samogitian", + ["shi" ] = "tachelhit", + ["shn" ] = "shan", + ["sib" ] = "sibe", + ["sid" ] = "sidamo", + ["sig" ] = "silte gurage", + ["sks" ] = "skolt sami", + ["sky" ] = "slovak", + ["sla" ] = "slavey", + ["slv" ] = "slovenian", + ["sml" ] = "somali", + ["smo" ] = "samoan", + ["sna" ] = "sena", + ["sna0"] = "shona", + ["snd" ] = "sindhi", + ["snh" ] = "sinhala (sinhalese)", + ["snk" ] = "soninke", + ["sog" ] = "sodo gurage", + ["sop" ] = "songe", + ["sot" ] = "sotho, southern", + ["sqi" ] = "albanian", + ["srb" ] = "serbian", + ["srd" ] = "sardinian", + ["srk" ] = "saraiki", + ["srr" ] = "serer", + ["ssl" ] = "south slavey", + ["ssm" ] = "southern sami", + ["stq" ] = "saterland frisian", + ["suk" ] = "sukuma", + ["sun" ] = "sundanese", + ["sur" ] = "suri", + ["sva" ] = "svan", + ["sve" ] = "swedish", + ["swa" ] = "swadaya aramaic", + ["swk" ] = "swahili", + ["swz" ] = "swati", + ["sxt" ] = "sutu", + ["sxu" ] = "upper saxon", + ["syl" ] = "sylheti", + ["syr" ] = "syriac", + ["szl" ] = "silesian", + ["tab" ] = "tabasaran", + ["taj" ] = "tajiki", + ["tam" ] = "tamil", + ["tat" ] = "tatar", + ["tcr" ] = "th-cree", + ["tdd" ] = "dehong dai", + ["tel" ] = "telugu", + ["tet" ] = "tetum", + ["tgl" ] = "tagalog", + ["tgn" ] = "tongan", + ["tgr" ] = "tigre", + ["tgy" ] = "tigrinya", + ["tha" ] = "thai", + ["tht" ] = "tahitian", + ["tib" ] = "tibetan", + ["tiv" ] = "tiv", + ["tkm" ] = "turkmen", + ["tmh" ] = "tamashek", + ["tmn" ] = "temne", + ["tna" ] = "tswana", + ["tne" ] = "tundra nenets", + ["tng" ] = "tonga", + ["tod" ] = "todo", + ["tod0"] = "toma", + ["tpi" ] = "tok pisin", + ["trk" ] = "turkish", + ["tsg" ] = "tsonga", + ["tua" ] = "turoyo aramaic", + ["tul" ] = "tulu", + ["tuv" ] = "tuvin", + ["tvl" ] = "tuvalu", + ["twi" ] = "twi", + ["tyz" ] = "tày", + ["tzm" ] = "tamazight", + ["tzo" ] = "tzotzil", + ["udm" ] = "udmurt", + ["ukr" ] = "ukrainian", + ["umb" ] = "umbundu", + ["urd" ] = "urdu", + ["usb" ] = "upper sorbian", + ["uyg" ] = "uyghur", + ["uzb" ] = "uzbek", + ["vec" ] = "venetian", + ["ven" ] = "venda", + ["vit" ] = "vietnamese", + ["vol" ] = "volapük", + ["vro" ] = "võro", + ["wa" ] = "wa", + ["wag" ] = "wagdi", + ["war" ] = "waray-waray", + ["wcr" ] = "west-cree", + ["wel" ] = "welsh", + ["wlf" ] = "wolof", + ["wln" ] = "walloon", + ["xbd" ] = "lü", + ["xhs" ] = "xhosa", + ["xjb" ] = "minjangbal", + ["xog" ] = "soga", + ["xpe" ] = "kpelle (liberia)", + ["yak" ] = "sakha", + ["yao" ] = "yao", + ["yap" ] = "yapese", + ["yba" ] = "yoruba", + ["ycr" ] = "y-cree", + ["yic" ] = "yi classic", + ["yim" ] = "yi modern", + ["zea" ] = "zealandic", + ["zgh" ] = "standard morrocan tamazigh", + ["zha" ] = "zhuang", + ["zhh" ] = "chinese, hong kong sar", + ["zhp" ] = "chinese phonetic", + ["zhs" ] = "chinese simplified", + ["zht" ] = "chinese traditional", + ["znd" ] = "zande", + ["zul" ] = "zulu", + ["zza" ] = "zazaki", +} + +local features = allocate { + ["aalt"] = "access all alternates", + ["abvf"] = "above-base forms", + ["abvm"] = "above-base mark positioning", + ["abvs"] = "above-base substitutions", + ["afrc"] = "alternative fractions", + ["akhn"] = "akhands", + ["blwf"] = "below-base forms", + ["blwm"] = "below-base mark positioning", + ["blws"] = "below-base substitutions", + ["c2pc"] = "petite capitals from capitals", + ["c2sc"] = "small capitals from capitals", + ["calt"] = "contextual alternates", + ["case"] = "case-sensitive forms", + ["ccmp"] = "glyph composition/decomposition", + ["cfar"] = "conjunct form after ro", + ["cjct"] = "conjunct forms", + ["clig"] = "contextual ligatures", + ["cpct"] = "centered cjk punctuation", + ["cpsp"] = "capital spacing", + ["cswh"] = "contextual swash", + ["curs"] = "cursive positioning", + ["dflt"] = "default processing", + ["dist"] = "distances", + ["dlig"] = "discretionary ligatures", + ["dnom"] = "denominators", + ["dtls"] = "dotless forms", -- math + ["expt"] = "expert forms", + ["falt"] = "final glyph alternates", + ["fin2"] = "terminal forms #2", + ["fin3"] = "terminal forms #3", + ["fina"] = "terminal forms", + ["flac"] = "flattened accents over capitals", -- math + ["frac"] = "fractions", + ["fwid"] = "full width", + ["half"] = "half forms", + ["haln"] = "halant forms", + ["halt"] = "alternate half width", + ["hist"] = "historical forms", + ["hkna"] = "horizontal kana alternates", + ["hlig"] = "historical ligatures", + ["hngl"] = "hangul", + ["hojo"] = "hojo kanji forms", + ["hwid"] = "half width", + ["init"] = "initial forms", + ["isol"] = "isolated forms", + ["ital"] = "italics", + ["jalt"] = "justification alternatives", + ["jp04"] = "jis2004 forms", + ["jp78"] = "jis78 forms", + ["jp83"] = "jis83 forms", + ["jp90"] = "jis90 forms", + ["kern"] = "kerning", + ["lfbd"] = "left bounds", + ["liga"] = "standard ligatures", + ["ljmo"] = "leading jamo forms", + ["lnum"] = "lining figures", + ["locl"] = "localized forms", + ["ltra"] = "left-to-right alternates", + ["ltrm"] = "left-to-right mirrored forms", + ["mark"] = "mark positioning", + ["med2"] = "medial forms #2", + ["medi"] = "medial forms", + ["mgrk"] = "mathematical greek", + ["mkmk"] = "mark to mark positioning", + ["mset"] = "mark positioning via substitution", + ["nalt"] = "alternate annotation forms", + ["nlck"] = "nlc kanji forms", + ["nukt"] = "nukta forms", + ["numr"] = "numerators", + ["onum"] = "old style figures", + ["opbd"] = "optical bounds", + ["ordn"] = "ordinals", + ["ornm"] = "ornaments", + ["palt"] = "proportional alternate width", + ["pcap"] = "petite capitals", + ["pkna"] = "proportional kana", + ["pnum"] = "proportional figures", + ["pref"] = "pre-base forms", + ["pres"] = "pre-base substitutions", + ["pstf"] = "post-base forms", + ["psts"] = "post-base substitutions", + ["pwid"] = "proportional widths", + ["qwid"] = "quarter widths", + ["rand"] = "randomize", + ["rclt"] = "required contextual alternates", + ["rkrf"] = "rakar forms", + ["rlig"] = "required ligatures", + ["rphf"] = "reph form", + ["rtbd"] = "right bounds", + ["rtla"] = "right-to-left alternates", + ["rtlm"] = "right to left mirrored forms", + ["rvrn"] = "required variation alternates", + ["ruby"] = "ruby notation forms", + ["salt"] = "stylistic alternates", + ["sinf"] = "scientific inferiors", + ["size"] = "optical size", -- now stat table + ["smcp"] = "small capitals", + ["smpl"] = "simplified forms", + -- ["ss01"] = "stylistic set 1", + -- ["ss02"] = "stylistic set 2", + -- ["ss03"] = "stylistic set 3", + -- ["ss04"] = "stylistic set 4", + -- ["ss05"] = "stylistic set 5", + -- ["ss06"] = "stylistic set 6", + -- ["ss07"] = "stylistic set 7", + -- ["ss08"] = "stylistic set 8", + -- ["ss09"] = "stylistic set 9", + -- ["ss10"] = "stylistic set 10", + -- ["ss11"] = "stylistic set 11", + -- ["ss12"] = "stylistic set 12", + -- ["ss13"] = "stylistic set 13", + -- ["ss14"] = "stylistic set 14", + -- ["ss15"] = "stylistic set 15", + -- ["ss16"] = "stylistic set 16", + -- ["ss17"] = "stylistic set 17", + -- ["ss18"] = "stylistic set 18", + -- ["ss19"] = "stylistic set 19", + -- ["ss20"] = "stylistic set 20", + ["ssty"] = "script style", -- math + ["stch"] = "stretching glyph decomposition", + ["subs"] = "subscript", + ["sups"] = "superscript", + ["swsh"] = "swash", + ["titl"] = "titling", + ["tjmo"] = "trailing jamo forms", + ["tnam"] = "traditional name forms", + ["tnum"] = "tabular figures", + ["trad"] = "traditional forms", + ["twid"] = "third widths", + ["unic"] = "unicase", + ["valt"] = "alternate vertical metrics", + ["vatu"] = "vattu variants", + ["vert"] = "vertical writing", + ["vhal"] = "alternate vertical half metrics", + ["vjmo"] = "vowel jamo forms", + ["vkna"] = "vertical kana alternates", + ["vkrn"] = "vertical kerning", + ["vpal"] = "proportional alternate vertical metrics", + ["vrt2"] = "vertical rotation", + ["zero"] = "slashed zero", + + ["trep"] = "traditional tex replacements", + ["tlig"] = "traditional tex ligatures", + + ["ss.."] = "stylistic set ..", + ["cv.."] = "character variant ..", + ["js.."] = "justification ..", + + ["dv.."] = "devanagari ..", + ["ml.."] = "malayalam ..", +} + +local baselines = allocate { + ["hang"] = "hanging baseline", + ["icfb"] = "ideographic character face bottom edge baseline", + ["icft"] = "ideographic character face tope edige baseline", + ["ideo"] = "ideographic em-box bottom edge baseline", + ["idtp"] = "ideographic em-box top edge baseline", + ["math"] = "mathematical centered baseline", + ["romn"] = "roman baseline" +} + +tables.scripts = scripts +tables.languages = languages +tables.features = features +tables.baselines = baselines + +local acceptscripts = true directives.register("otf.acceptscripts", function(v) acceptscripts = v end) +local acceptlanguages = true directives.register("otf.acceptlanguages", function(v) acceptlanguages = v end) + +local report_checks = logs.reporter("fonts","checks") + +-- hm, we overload the metatables + +if otffeatures.features then + for k, v in next, otffeatures.features do + features[k] = v + end + otffeatures.features = features +end + +local function swapped(h) + local r = { } + for k, v in next, h do + r[gsub(v,"[^a-z0-9]","")] = k -- is already lower + end + return r +end + +local verbosescripts = allocate(swapped(scripts )) +local verboselanguages = allocate(swapped(languages)) +local verbosefeatures = allocate(swapped(features )) +local verbosebaselines = allocate(swapped(baselines)) + +-- lets forget about trailing spaces + +local function resolve(t,k) + if k then + k = gsub(lower(k),"[^a-z0-9]","") + local v = rawget(t,k) + if v then + return v + end + end +end + +setmetatableindex(verbosescripts, resolve) +setmetatableindex(verboselanguages, resolve) +setmetatableindex(verbosefeatures, resolve) +setmetatableindex(verbosebaselines, resolve) + +-- We could optimize the next lookups by using an extra metatable and storing +-- already found values but in practice there are not that many lookups so +-- it's never a bottleneck. + +setmetatableindex(scripts, function(t,k) + if k then + k = lower(k) + if k == "dflt" then + return k + end + local v = rawget(t,k) + if v then + return v + end + k = gsub(k," ","") + v = rawget(t,v) + if v then + return v + elseif acceptscripts then + report_checks("registering extra script %a",k) + rawset(t,k,k) + return k + end + end + return "dflt" +end) + +setmetatableindex(languages, function(t,k) + if k then + k = lower(k) + if k == "dflt" then + return k + end + local v = rawget(t,k) + if v then + return v + end + k = gsub(k," ","") + v = rawget(t,v) + if v then + return v + elseif acceptlanguages then + report_checks("registering extra language %a",k) + rawset(t,k,k) + return k + end + end + return "dflt" +end) + +if setmetatablenewindex then + + setmetatablenewindex(languages, "ignore") + setmetatablenewindex(scripts, "ignore") + setmetatablenewindex(baselines, "ignore") + +end + +local function resolve(t,k) + if k then + k = lower(k) + local v = rawget(t,k) + if v then + return v + end + k = gsub(k," ","") + local v = rawget(t,k) + if v then + return v + end + local tag, dd = match(k,"(..)(%d+)") + if tag and dd then + local v = rawget(t,tag) + if v then + return v -- return format(v,tonumber(dd)) -- old way + else + local v = rawget(t,tag.."..") -- nicer in overview + if v then + return (gsub(v,"%.%.",tonumber(dd))) -- new way + end + end + end + end + return k -- "dflt" +end + +setmetatableindex(features, resolve) + +local function assign(t,k,v) + if k and v then + v = lower(v) + rawset(t,k,v) -- rawset ? + -- rawset(features,gsub(v,"[^a-z0-9]",""),k) -- why ? old code + end +end + +if setmetatablenewindex then + + setmetatablenewindex(features, assign) + +end + +local checkers = { + rand = function(v) + return v == true and "random" or v + end +} + +-- Keep this: +-- +-- function otf.features.normalize(features) +-- if features then +-- local h = { } +-- for k, v in next, features do +-- k = lower(k) +-- if k == "language" then +-- v = gsub(lower(v),"[^a-z0-9]","") +-- h.language = rawget(verboselanguages,v) or (languages[v] and v) or "dflt" -- auto adds +-- elseif k == "script" then +-- v = gsub(lower(v),"[^a-z0-9]","") +-- h.script = rawget(verbosescripts,v) or (scripts[v] and v) or "dflt" -- auto adds +-- else +-- if type(v) == "string" then +-- local b = is_boolean(v) +-- if type(b) == "nil" then +-- v = tonumber(v) or lower(v) +-- else +-- v = b +-- end +-- end +-- if not rawget(features,k) then +-- k = rawget(verbosefeatures,k) or k +-- end +-- local c = checkers[k] +-- h[k] = c and c(v) or v +-- end +-- end +-- return h +-- end +-- end + +-- inspect(fonts.handlers.otf.statistics.usedfeatures) + +if not storage then + return +end + +local usedfeatures = statistics.usedfeatures or { } +statistics.usedfeatures = usedfeatures + +table.setmetatableindex(usedfeatures, function(t,k) if k then local v = { } t[k] = v return v end end) -- table.autotable + +storage.register("fonts/otf/usedfeatures", usedfeatures, "fonts.handlers.otf.statistics.usedfeatures" ) + +local normalizedaxis = otf.readers.helpers.normalizedaxis or function(s) return s end + +function otffeatures.normalize(features) + if features then + local h = { } + for key, value in next, features do + local k = lower(key) + if k == "language" then + local v = gsub(lower(value),"[^a-z0-9]","") + h.language = rawget(verboselanguages,v) or (languages[v] and v) or "dflt" -- auto adds + elseif k == "script" then + local v = gsub(lower(value),"[^a-z0-9]","") + h.script = rawget(verbosescripts,v) or (scripts[v] and v) or "dflt" -- auto adds + elseif k == "axis" then + h[k] = normalizedaxis(value) + if not callbacks.supported.glyph_stream_provider then + h.variableshapes = true -- for the moment + end + else + local uk = usedfeatures[key] + local uv = uk[value] + if uv then + -- report_checks("feature value %a first seen at %a",value,key) + else + uv = tonumber(value) -- before boolean as there we also handle 0/1 + if uv then + -- we're okay + elseif type(value) == "string" then + local b = is_boolean(value) + if type(b) == "nil" then + -- we do this elsewhere + -- + -- if find(value,"=") then + -- local t = { } + -- for k, v in gmatch(value,"([^%s,=]+)%s*=%s*([^%s,=]+)") do + -- t[k] = tonumber(v) or v + -- end + -- if next(t) then + -- value = sequenced(t,",") + -- end + -- end + uv = lower(value) + else + uv = b + end + elseif type(value) == "table" then + uv = sequenced(t,",") + else + uv = value + end + if not rawget(features,k) then + k = rawget(verbosefeatures,k) or k + end + local c = checkers[k] + if c then + uv = c(uv) or vc + end + uk[value] = uv + end + h[k] = uv + end + end + return h + end +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oup.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oup.lua index cfa90c7944a..562d7561d94 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oup.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-oup.lua @@ -15,6 +15,7 @@ local formatters = string.formatters local sortedkeys = table.sortedkeys local sortedhash = table.sortedhash local tohash = table.tohash +local setmetatableindex = table.setmetatableindex local report = logs.reporter("otf reader") @@ -29,8 +30,8 @@ local f_index = formatters["I%05X"] local f_character_y = formatters["%C"] local f_character_n = formatters["[ %C ]"] -local check_duplicates = true -- can become an option (pseudo feature) / aways needed anyway -local check_soft_hyphen = false -- can become an option (pseudo feature) / needed for tagging +local check_duplicates = true -- can become an option (pseudo feature) / aways needed anyway +local check_soft_hyphen = true -- can become an option (pseudo feature) / needed for tagging directives.register("otf.checksofthyphen",function(v) check_soft_hyphen = v @@ -370,6 +371,7 @@ local function copyduplicates(fontdata) local duplicates = resources.duplicates if check_soft_hyphen then -- ebgaramond has a zero width empty soft hyphen + -- antykwatorunsks lacks a soft hyphen local ds = descriptions[0xAD] if not ds or ds.width == 0 then if ds then @@ -621,7 +623,7 @@ local function checklookups(fontdata,missing,nofmissing) end end if next(done) then - report("not unicoded: % t",table.sortedkeys(done)) + report("not unicoded: % t",sortedkeys(done)) end end end @@ -632,7 +634,6 @@ local function unifymissing(fontdata) 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 @@ -673,7 +674,8 @@ local function unifyglyphs(fontdata,usenames) for index=1,#glyphs do local glyph = glyphs[index] local unicode = glyph.unicode -- this is the primary one - if not unicode then + -- if not unicode then -- some fonts use the private space + if not unicode or unicode >= private or (unicode >= 0xE000 and unicode <= 0xF8FF) or unicode == 0xFFFE or unicode == 0xFFFF then -- report("assigning private unicode %U to glyph indexed %05X (%s)",private,index,"unset") unicode = private -- glyph.unicode = -1 @@ -812,9 +814,9 @@ function readers.getcomponents(fontdata) -- handy for resolving ligatures when n end for i=1,#steps do -- we actually had/have this in base mode - local coverage = steps[i].coverage - if coverage then - for k, v in next, coverage do + local c = steps[i].coverage + if c then + for k, v in next, c do traverse(k,k,v) end end @@ -891,7 +893,7 @@ function readers.rehash(fontdata,hashmethod) -- TODO: combine loops in one unifymissing(fontdata) -- stripredundant(fontdata) else - fontdata.hashmethod = "unicode" + fontdata.hashmethod = "unicodes" local indices = unifyglyphs(fontdata) unifyresources(fontdata,indices) copyduplicates(fontdata) @@ -908,10 +910,10 @@ function readers.checkhash(fontdata) elseif hashmethod == "names" and fontdata.names then unifyresources(fontdata,fontdata.names) copyduplicates(fontdata) - fontdata.hashmethod = "unicode" + fontdata.hashmethod = "unicodes" fontdata.names = nil -- no need for it else - readers.rehash(fontdata,"unicode") + readers.rehash(fontdata,"unicodes") end end @@ -1066,13 +1068,14 @@ function readers.pack(data) end end - local function pack_flat(v) - local tag = tabstr_flat(v) + local function pack_normal_cc(v) + local tag = tabstr_normal(v) local ht = h[tag] if ht then c[ht] = c[ht] + 1 return ht else + v[1] = 0 nt = nt + 1 t[nt] = v h[tag] = nt @@ -1081,8 +1084,8 @@ function readers.pack(data) end end - local function pack_boolean(v) - local tag = tabstr_boolean(v) + local function pack_flat(v) + local tag = tabstr_flat(v) local ht = h[tag] if ht then c[ht] = c[ht] + 1 @@ -1126,6 +1129,84 @@ function readers.pack(data) end end + -- saves a lot on noto sans + + -- can be made more clever + + 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 + + -- -- This was an experiment to see if we can bypass the luajit limits but loading is + -- -- still an issue due to other limits so we don't use this ... actually it can + -- -- prevent a luajittex crash but i don't care too much about that as we can't use + -- -- that engine anyway then. + -- + -- local function check(t) + -- if type(t) == "table" then + -- local s = sortedkeys(t) + -- local n = #s + -- if n <= 10 then + -- return + -- end + -- local ranges = { } + -- local first, last + -- for i=1,#s do + -- local ti = s[i] + -- if not first then + -- first = ti + -- last = ti + -- elseif ti == last + 1 then + -- last = ti + -- elseif last - first < 10 then + -- -- we could permits a few exceptions + -- return + -- else + -- ranges[#ranges+1] = { first, last } + -- first, last = nil, nil + -- end + -- end + -- if #ranges > 0 then + -- return { + -- ranges = ranges + -- } + -- end + -- end + -- end + -- + -- local function pack_boolean(v) + -- local tag + -- local r = check(v) + -- if r then + -- v = r + -- tag = tabstr_normal(v) + -- else + -- tag = tabstr_boolean(v) + -- end + -- 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) -- v == number if c[v] <= criterium then @@ -1145,6 +1226,25 @@ function readers.pack(data) end end + local function pack_final_cc(v) + -- v == number + if c[v] <= criterium then + return t[v] + else + -- compact hash + 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 @@ -1191,9 +1291,9 @@ function readers.pack(data) local function packers(pass) if pass == 1 then - return pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed + return pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed, pack_normal_cc else - return pack_final, pack_final, pack_final, pack_final, pack_final + return pack_final, pack_final, pack_final, pack_final, pack_final, pack_final_cc end end @@ -1202,6 +1302,7 @@ function readers.pack(data) local sublookups = resources.sublookups local features = resources.features local palettes = resources.colorpalettes + local variable = resources.variabledata local chardata = characters and characters.data local descriptions = data.descriptions or data.glyphs @@ -1210,15 +1311,13 @@ function readers.pack(data) 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) + local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed, pack_normal_cc = packers(pass) for unicode, description in next, descriptions do local boundingbox = description.boundingbox @@ -1258,28 +1357,30 @@ function readers.pack(data) if kind == "gpos_pair" then local c = step.coverage if c then - if step.format == "kern" then + if step.format == "pair" then for g1, d1 in next, c do - c[g1] = pack_normal(d1) + for g2, d2 in next, d1 do + local f = d2[1] if f and f ~= true then d2[1] = pack_indexed(f) end + local s = d2[2] if s and s ~= true then d2[2] = pack_indexed(s) end + end 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 + c[g1] = pack_normal(d1) 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 + if step.format == "single" then for g1, d1 in next, c do - c[g1] = pack_indexed(d1) + if d1 and d1 ~= true then + c[g1] = pack_indexed(d1) + end end + else + step.coverage = pack_normal(c) end end elseif kind == "gpos_cursive" then @@ -1331,7 +1432,7 @@ function readers.pack(data) 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.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 @@ -1377,6 +1478,53 @@ function readers.pack(data) end + if variable then + + -- todo: segments + + local instances = variable.instances + if instances then + for i=1,#instances do + local v = instances[i].values + for j=1,#v do + v[j] = pack_normal(v[j]) + end + end + end + + local function packdeltas(main) + if main then + local deltas = main.deltas + if deltas then + for i=1,#deltas do + local di = deltas[i] + local d = di.deltas + -- local r = di.regions + for j=1,#d do + d[j] = pack_indexed(d[j]) + end + di.regions = pack_indexed(di.regions) + end + end + local regions = main.regions + if regions then + for i=1,#regions do + local r = regions[i] + for j=1,#r do + r[j] = pack_normal(r[j]) + end + end + end + end + end + + packdeltas(variable.global) + packdeltas(variable.horizontal) + packdeltas(variable.vertical) + packdeltas(variable.metrics) + + end + if not success(1,pass) then return end @@ -1391,7 +1539,7 @@ function readers.pack(data) report_otf("start packing: stage 2, pass %s",pass) end - local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed = packers(pass) + local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed, pack_normal_cc = packers(pass) for unicode, description in next, descriptions do local math = description.math @@ -1415,9 +1563,7 @@ function readers.pack(data) if kind == "gpos_pair" then local c = step.coverage if c then - if step.format == "kern" then - -- todo ! - else + if step.format == "pair" then for g1, d1 in next, c do for g2, d2 in next, d1 do d1[g2] = pack_normal(d2) @@ -1425,11 +1571,22 @@ function readers.pack(data) end end end --- elseif kind == "gpos_mark2base" or kind == "gpos_mark2mark" or kind == "gpos_mark2ligature" then --- local c = step.baseclasses --- for k, v in next, c do --- c[k] = pack_normal(v) --- end + -- elseif kind == "gpos_cursive" then + -- local c = step.coverage -- new + -- if c then + -- for g1, d1 in next, c do + -- c[g1] = pack_normal_cc(d1) + -- end + -- end + elseif kind == "gpos_mark2ligature" then + local c = step.baseclasses -- new + if c then + for g1, d1 in next, c do + for g2, d2 in next, d1 do + d1[g2] = pack_normal(d2) + end + end + end end local rules = step.rules if rules then @@ -1453,10 +1610,23 @@ function readers.pack(data) if sublookups then packthem(sublookups) end - -- features - if not success(2,pass) then - -- return + if variable then + local function unpackdeltas(main) + if main then + local regions = main.regions + if regions then + main.regions = pack_normal(regions) + end + end + end + unpackdeltas(variable.global) + unpackdeltas(variable.horizontal) + unpackdeltas(variable.vertical) + unpackdeltas(variable.metrics) end + -- if not success(2,pass) then + -- -- return + -- end end for pass=1,2 do @@ -1464,7 +1634,7 @@ function readers.pack(data) report_otf("start packing: stage 3, pass %s",pass) end - local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed = packers(pass) + local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed, pack_normal_cc = packers(pass) local function packthem(sequences) for i=1,#sequences do @@ -1478,18 +1648,23 @@ function readers.pack(data) if kind == "gpos_pair" then local c = step.coverage if c then - if step.format == "kern" then - -- todo ! - else + if step.format == "pair" then for g1, d1 in next, c do c[g1] = pack_normal(d1) end end end + elseif kind == "gpos_cursive" then + local c = step.coverage + if c then + for g1, d1 in next, c do + c[g1] = pack_normal_cc(d1) + end + end end end end - end + end end if sequences then @@ -1525,6 +1700,7 @@ function readers.unpack(data) local sublookups = resources.sublookups local features = resources.features local palettes = resources.colorpalettes + local variable = resources.variabledata local unpacked = { } setmetatable(unpacked,unpacked_mt) for unicode, description in next, descriptions do @@ -1564,6 +1740,15 @@ function readers.unpack(data) -- end end + -- local function expandranges(t,ranges) + -- for i=1,#ranges do + -- local r = ranges[i] + -- for k=r[1],r[2] do + -- t[k] = true + -- end + -- end + -- end + local function unpackthem(sequences) for i=1,#sequences do local sequence = sequences[i] @@ -1573,20 +1758,26 @@ function readers.unpack(data) local features = sequence.features local flags = sequence.flags local markclass = sequence.markclass + 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 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 + if step.format == "pair" then for g1, d1 in next, c do local tv = tables[d1] if tv then @@ -1603,29 +1794,41 @@ function readers.unpack(data) local s = tables[d2[2]] if s then d2[2] = s end end 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_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 + if step.format == "single" then for g1, d1 in next, c do local tv = tables[d1] if tv then c[g1] = tv end end + else + local tv = tables[c] + if tv then + step.coverage = tv + end end end elseif kind == "gpos_cursive" then local c = step.coverage if c then for g1, d1 in next, c do + local tv = tables[d1] + if tv then + d1 = tv + c[g1] = d1 + end 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 @@ -1633,12 +1836,6 @@ function readers.unpack(data) elseif kind == "gpos_mark2base" or kind == "gpos_mark2mark" then local c = step.baseclasses if c then --- for k, v in next, c do --- local tv = tables[v] --- if tv then --- c[k] = tv --- end --- end for g1, d1 in next, c do for g2, d2 in next, d1 do local tv = tables[d2] @@ -1660,14 +1857,13 @@ function readers.unpack(data) elseif kind == "gpos_mark2ligature" then local c = step.baseclasses if c then --- for k, v in next, c do --- local tv = tables[v] --- if tv then --- c[k] = tv --- end --- end for g1, d1 in next, c do for g2, d2 in next, d1 do + local tv = tables[d2] -- new + if tv then + d2 = tv + d1[g2] = d2 + end for g3, d3 in next, d2 do local tv = tables[d2[g3]] if tv then @@ -1704,6 +1900,18 @@ function readers.unpack(data) before[i] = tv end end + -- for i=1,#before do + -- local bi = before[i] + -- local tv = tables[bi] + -- if tv then + -- bi = tv + -- before[i] = bi + -- end + -- local ranges = bi.ranges + -- if ranges then + -- expandranges(bi,ranges) + -- end + -- end end local after = rule.after if after then @@ -1718,6 +1926,18 @@ function readers.unpack(data) after[i] = tv end end + -- for i=1,#after do + -- local ai = after[i] + -- local tv = tables[ai] + -- if tv then + -- ai = tv + -- after[i] = ai + -- end + -- local ranges = ai.ranges + -- if ranges then + -- expandranges(ai,ranges) + -- end + -- end end local current = rule.current if current then @@ -1732,14 +1952,26 @@ function readers.unpack(data) current[i] = tv end end + -- for i=1,#current do + -- local ci = current[i] + -- local tv = tables[ci] + -- if tv then + -- ci = tv + -- current[i] = ci + -- end + -- local ranges = ci.ranges + -- if ranges then + -- expandranges(ci,ranges) + -- end + -- end end - local lookups = rule.lookups - if lookups then - local tv = tables[lookups] - if tv then - rule.lookups = tv - 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] @@ -1751,19 +1983,6 @@ function readers.unpack(data) 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 @@ -1810,6 +2029,70 @@ function readers.unpack(data) end end + if variable then + + -- todo: segments + + local instances = variable.instances + if instances then + for i=1,#instances do + local v = instances[i].values + for j=1,#v do + local tv = tables[v[j]] + if tv then + v[j] = tv + end + end + end + end + + local function unpackdeltas(main) + if main then + local deltas = main.deltas + if deltas then + for i=1,#deltas do + local di = deltas[i] + local d = di.deltas + local r = di.regions + for j=1,#d do + local tv = tables[d[j]] + if tv then + d[j] = tv + end + end + local tv = di.regions + if tv then + di.regions = tv + end + end + end + local regions = main.regions + if regions then + local tv = tables[regions] + if tv then + main.regions = tv + regions = tv + end + for i=1,#regions do + local r = regions[i] + for j=1,#r do + local tv = tables[r[j]] + if tv then + r[j] = tv + end + end + end + end + end + end + + unpackdeltas(variable.global) + unpackdeltas(variable.horizontal) + unpackdeltas(variable.vertical) + unpackdeltas(variable.metrics) + + end + data.tables = nil end end @@ -1851,9 +2134,12 @@ local function mergesteps_1(lookup,strict) 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 + local c = steps[i].coverage + if c then + for k, v in next, c do + if not target[k] then + target[k] = v + end end end end @@ -1863,8 +2149,10 @@ local function mergesteps_1(lookup,strict) return nofsteps - 1 end - -local function mergesteps_2(lookup,strict) -- pairs +local function mergesteps_2(lookup) -- pairs + -- this can be tricky as we can have a match on a mark with no marks skip flag + -- in which case with multiple steps a hit can prevent a next step while in the + -- merged case we can hit differently (a messy font then anyway) local steps = lookup.steps local nofsteps = lookup.nofsteps local first = steps[1] @@ -1880,71 +2168,68 @@ local function mergesteps_2(lookup,strict) -- pairs 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 + local c = steps[i].coverage + if c then + for k, v in next, c do + local tk = target[k] + if tk then + for kk, vv in next, v do + if tk[kk] == nil then + tk[kk] = vv + end end + else + target[k] = v end - else - target[k] = v end end end lookup.nofsteps = 1 - lookup.steps = { first } + lookup.merged = true + lookup.steps = { first } return nofsteps - 1 end +-- we could have a coverage[first][second] = { } already here (because eventually +-- we also have something like that after loading) local function mergesteps_3(lookup,strict) -- marks 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 = { } + -- check first + local coverage = { } for i=1,nofsteps do - local offset = i*10 + local c = steps[i].coverage + if c then + for k, v in next, c do + local tk = coverage[k] -- { class, { x, y } } + if tk then + report("quitting merge due to multiple checks") + return nofsteps + else + coverage[k] = v + end + end + end + end + -- merge indeed + local first = steps[1] + local baseclasses = { } -- let's assume sparse step.baseclasses + for i=1,nofsteps do + local offset = i*10 -- we assume max 10 classes per step 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) + v[1] = offset + v[1] end end first.baseclasses = baseclasses first.coverage = coverage lookup.nofsteps = 1 + lookup.merged = true lookup.steps = { first } return nofsteps - 1 end @@ -1973,12 +2258,15 @@ local function mergesteps_4(lookup) -- ligatures 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 + local c = steps[i].coverage + if c then + for k, v in next, c do + local tk = target[k] + if tk then + nested(v,tk) + else + target[k] = v + end end end end @@ -1987,62 +2275,140 @@ local function mergesteps_4(lookup) -- ligatures return nofsteps - 1 end +-- so we assume only one cursive entry and exit and even then the first one seems +-- to win anyway: no exit or entry quite the lookup match and then we take the +-- next step; this means that we can as well merge them + +local function mergesteps_5(lookup) -- cursive + 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 + local hash = nil + for k, v in next, target do + hash = v[1] + break + end + for i=2,nofsteps do + local c = steps[i].coverage + if c then + for k, v in next, c do + local tk = target[k] + if tk then + if not tk[2] then + tk[2] = v[2] + end + if not tk[3] then + tk[3] = v[3] + end + else + target[k] = v + v[1] = hash + end + end + end + end + lookup.nofsteps = 1 + lookup.merged = true + lookup.steps = { first } + return nofsteps - 1 +end + local function checkkerns(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 - if d1[1] ~= 0 or d1[2] ~= 0 or d1[4] ~= 0 then + if d1 == true then + -- all zero + elseif not d1 then + -- null + elseif 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) + local c = { } for g1, d1 in next, coverage do - coverage[g1] = d1[3] + if d1 and d1 ~= true then + c[g1] = d1[3] + end end - step.format = "kern" + step.coverage = c + step.format = "move" + kerned = kerned + 1 end end end + return kerned end +-- There are several options to optimize but we have this somewhat fuzzy aspect of +-- advancing (depending on the second of a pair) so we need to retain that information. +-- +-- We can have: +-- +-- true, nil|false +-- +-- which effectively means: nothing to be done and advance to next (so not next of +-- next) and because coverage should be not overlapping we can wipe these. However, +-- checking for (true,nil) (false,nil) and omitting them doesn't gain much. + +-- Because we pack we cannot mix tables and numbers so we can only turn a whole set in +-- format kern instead of pair. + 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 + + local function onlykerns(step) + local coverage = step.coverage + for g1, d1 in next, coverage do + for g2, d2 in next, d1 do + if d2[2] then + --- true or { a, b, c, d } + return false + else + local v = d2[1] + if v == true then + -- all zero + elseif v and (v[1] ~= 0 or v[2] ~= 0 or v[4] ~= 0) then + return false end end end - if kerns then + end + return coverage + end + + for i=1,nofsteps do + local step = steps[i] + if step.format == "pair" then + local coverage = onlykerns(step) + if coverage 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 + local d = { } for g2, d2 in next, d1 do - d1[g2] = d2[1][3] + local v = d2[1] + if v == true then + -- ignore -- d1[g2] = nil + elseif v then + d[g2] = v[3] -- d1[g2] = v[3] + end end + coverage[g1] = d end - step.format = "kern" + step.format = "move" kerned = kerned + 1 end end @@ -2050,6 +2416,30 @@ local function checkpairs(lookup) return kerned end +local compact_pairs = true +local compact_singles = true + +local merge_pairs = true +local merge_singles = true +local merge_substitutions = true +local merge_alternates = true +local merge_multiples = true +local merge_ligatures = true +local merge_cursives = true +local merge_marks = true + +directives.register("otf.compact.pairs", function(v) compact_pairs = v end) +directives.register("otf.compact.singles", function(v) compact_singles = v end) + +directives.register("otf.merge.pairs", function(v) merge_pairs = v end) +directives.register("otf.merge.singles", function(v) merge_singles = v end) +directives.register("otf.merge.substitutions", function(v) merge_substitutions = v end) +directives.register("otf.merge.alternates", function(v) merge_alternates = v end) +directives.register("otf.merge.multiples", function(v) merge_multiples = v end) +directives.register("otf.merge.ligatures", function(v) merge_ligatures = v end) +directives.register("otf.merge.cursives", function(v) merge_cursives = v end) +directives.register("otf.merge.marks", function(v) merge_marks = v end) + function readers.compact(data) if not data or data.compacted then return @@ -2066,23 +2456,65 @@ function readers.compact(data) for i=1,#lookups do local lookup = lookups[i] local nofsteps = lookup.nofsteps + local kind = lookup.type 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) + local merg = merged + if kind == "gsub_single" then + if merge_substitutions then + merged = merged + mergesteps_1(lookup) + end + elseif kind == "gsub_alternate" then + if merge_alternates then + merged = merged + mergesteps_1(lookup) + end + elseif kind == "gsub_multiple" then + if merge_multiples then + merged = merged + mergesteps_1(lookup) + end elseif kind == "gsub_ligature" then - merged = merged + mergesteps_4(lookup) + if merge_ligatures then + merged = merged + mergesteps_4(lookup) + end elseif kind == "gpos_single" then - merged = merged + mergesteps_1(lookup,true) - checkkerns(lookup) + if merge_singles then + merged = merged + mergesteps_1(lookup,true) + end + if compact_singles then + kerned = kerned + checkkerns(lookup) + end elseif kind == "gpos_pair" then - merged = merged + mergesteps_2(lookup,true) - kerned = kerned + checkpairs(lookup) + if merge_pairs then + merged = merged + mergesteps_2(lookup) + end + if compact_pairs then + kerned = kerned + checkpairs(lookup) + end elseif kind == "gpos_cursive" then - merged = merged + mergesteps_2(lookup) + if merge_cursives then + merged = merged + mergesteps_5(lookup) + end elseif kind == "gpos_mark2mark" or kind == "gpos_mark2base" or kind == "gpos_mark2ligature" then - merged = merged + mergesteps_3(lookup) + if merge_marks then + merged = merged + mergesteps_3(lookup) + end + end + if merg ~= merged then + lookup.merged = true + end + elseif nofsteps == 1 then + local kern = kerned + if kind == "gpos_single" then + if compact_singles then + kerned = kerned + checkkerns(lookup) + end + elseif kind == "gpos_pair" then + if compact_pairs then + kerned = kerned + checkpairs(lookup) + end + end + if kern ~= kerned then + -- lookup.kerned = true end end end @@ -2100,6 +2532,79 @@ function readers.compact(data) end end +local function mergesteps(t,k) + if k == "merged" then + local merged = { } + for i=1,#t do + local step = t[i] + local coverage = step.coverage + for k in next, coverage do + local m = merged[k] + if m then + m[2] = i + -- m[#m+1] = step + else + merged[k] = { i, i } + -- merged[k] = { step } + end + end + end + t.merged = merged + return merged + end +end + +local function checkmerge(sequence) + local steps = sequence.steps + if steps then + setmetatableindex(steps,mergesteps) + end +end + +local function checkflags(sequence,resources) + if not sequence.skiphash then + local flags = sequence.flags + if flags then + local skipmark = flags[1] + local skipligature = flags[2] + local skipbase = flags[3] + local markclass = sequence.markclass + local skipsome = skipmark or skipligature or skipbase or markclass or false + if skipsome then + sequence.skiphash = setmetatableindex(function(t,k) + local c = resources.classes[k] -- delayed table + local v = c == skipmark + or (markclass and c == "mark" and not markclass[k]) + or c == skipligature + or c == skipbase + or false + t[k] = v + return v + end) + else + sequence.skiphash = false + end + else + sequence.skiphash = false + end + end +end + +local function checksteps(sequence) + local steps = sequence.steps + if steps then + for i=1,#steps do + steps[i].index = i + end + end +end + +if fonts.helpers then + fonts.helpers.checkmerge = checkmerge + fonts.helpers.checkflags = checkflags + fonts.helpers.checksteps = checksteps -- has to happen last +end + function readers.expand(data) if not data or data.expanded then return @@ -2141,6 +2646,11 @@ function readers.expand(data) end end end + + -- using a merged combined hash as first test saves some 30% on ebgaramond and + -- about 15% on arabtype .. then moving the a test also saves a bit (even when + -- often a is not set at all so that one is a bit debatable + local function expandlookups(sequences) if sequences then -- we also need to do sublookups @@ -2148,6 +2658,8 @@ function readers.expand(data) local sequence = sequences[i] local steps = sequence.steps if steps then + local nofsteps = sequence.nofsteps + local kind = sequence.type local markclass = sequence.markclass if markclass then @@ -2158,7 +2670,8 @@ function readers.expand(data) sequence.markclass = markclasses[markclass] end end - for i=1,sequence.nofsteps do + + for i=1,nofsteps do local step = steps[i] local baseclasses = step.baseclasses if baseclasses then @@ -2174,13 +2687,14 @@ function readers.expand(data) end local rules = step.rules if rules then - local rulehash = { } + local rulehash = { n = 0 } -- is contexts in font-ots local rulesize = 0 local coverage = { } local lookuptype = sequence.type + local nofrules = #rules step.coverage = coverage -- combined hits - for nofrules=1,#rules do - local rule = rules[nofrules] + for currentrule=1,nofrules do + local rule = rules[currentrule] local current = rule.current local before = rule.before local after = rule.after @@ -2208,23 +2722,27 @@ function readers.expand(data) local lookups = rule.lookups or false local subtype = nil if lookups then - -- is now indexed - for k, v in next, lookups do - local lookup = sublookups[v] - if lookup then - lookups[k] = lookup - if not subtype then - subtype = lookup.type + for i=1,#lookups do + local lookups = lookups[i] + if lookups then + for k, v in next, lookups do -- actually this one is indexed + local lookup = sublookups[v] + if lookup then + lookups[k] = lookup + if not subtype then + subtype = lookup.type + end + else + -- already expanded + end end - else - -- already expanded end end end if sequence[1] then -- we merge coverage into one - rulesize = rulesize + 1 - rulehash[rulesize] = { - nofrules, -- 1 + sequence.n = #sequence -- tiny speedup + local ruledata = { + currentrule, -- 1 -- original rule number, only use this for tracing! lookuptype, -- 2 sequence, -- 3 start, -- 4 @@ -2233,20 +2751,61 @@ function readers.expand(data) replacements, -- 7 subtype, -- 8 } - for unic in next, sequence[start] do - local cu = coverage[unic] - if not cu then - coverage[unic] = rulehash -- can now be done cleaner i think + -- + -- possible optimization: per [unic] a rulehash, but beware: + -- contexts have unique coverage and chains can have multiple + -- hits (rules) per coverage entry + -- + -- so: we can combine multiple steps as well as multiple rules + -- but that takes careful checking, in which case we can go the + -- step list approach and turn contexts into steps .. in fact, + -- if we turn multiple contexts into steps we're already ok as + -- steps gets a coverage hash by metatable + -- + rulesize = rulesize + 1 + rulehash[rulesize] = ruledata + rulehash.n = rulesize -- tiny speedup + -- + if true then -- nofrules > 1 + + for unic in next, sequence[start] do + local cu = coverage[unic] + if cu then + local n = #cu+1 + cu[n] = ruledata + cu.n = n + else + coverage[unic] = { ruledata, n = 1 } + end end + + else + + for unic in next, sequence[start] do + local cu = coverage[unic] + if cu then + -- we can have a contextchains with many matches which we + -- can actually optimize + else + coverage[unic] = rulehash + end + end + end end end end end + + checkmerge(sequence) + checkflags(sequence,resources) + checksteps(sequence) + end end end end + expandlookups(sequences) expandlookups(sublookups) end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-tfm.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-tfm.lua index 6584190ce38..9f78f2c0a3d 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-tfm.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-tfm.lua @@ -21,6 +21,7 @@ local setmetatableindex = table.setmetatableindex local fonts = fonts local handlers = fonts.handlers +local helpers = fonts.helpers local readers = fonts.readers local constructors = fonts.constructors local encodings = fonts.encodings @@ -39,6 +40,8 @@ local registertfmfeature = tfmfeatures.register local tfmenhancers = constructors.enhancers.tfm local registertfmenhancer = tfmenhancers.register +local charcommand = helpers.commands.char + constructors.resolvevirtualtoo = false -- wil be set in font-ctx.lua fonts.formats.tfm = "type1" -- we need to have at least a value here @@ -244,6 +247,8 @@ local function read_from_tfm(specification) -- constructors.enhanceparameters(parameters) -- official copies for us -- + properties.private = properties.private or tfmdata.private or privateoffset + -- if newtfmdata then -- -- We do nothing as we assume flat tfm files. It would become real messy @@ -436,7 +441,7 @@ do local originals = tfmdata.characters local indices = { } local parentfont = { "font", 1 } - local private = fonts.constructors.privateoffset + local private = tfmdata or fonts.constructors.privateoffset local reported = encdone[tfmfile][encfile] -- create characters table @@ -463,7 +468,7 @@ do if backmap then original.index = backmap[name] else -- probably bitmap - original.commands = { parentfont, { "char", index } } + original.commands = { parentfont, charcommand[index] } -- or "slot" original.oindex = index end done[name] = true @@ -514,6 +519,7 @@ do tfmdata.tounicode = 1 tfmdata.embedding = "subset" tfmdata.usedbitmap = bitmap and virtualid + tfmdata.private = private return tfmdata end @@ -548,7 +554,9 @@ end local flushstreamobject = lpdf and lpdf.flushstreamobject local setfontattributes = pdf.setfontattributes - if not flushstreamobject then + if flushstreamobject then + -- we're in context + else flushstreamobject = function(data) return pdf.obj { immediate = true, diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ttf.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ttf.lua index 6df339214b2..bdfa3e85906 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ttf.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-ttf.lua @@ -6,42 +6,108 @@ if not modules then modules = { } end modules ['font-ttf'] = { license = "see context related readme files" } +-- This version is different from previous in the sense that we no longer store +-- contours but keep points and contours (endpoints) separate for a while +-- because later on we need to apply deltas and that is easier on a list of +-- points. + +-- The code is a bit messy. I looked at the ff code but it's messy too. It has +-- to do with the fact that we need to look at points on the curve and control +-- points in between. This also means that we start at point 2 and have to look +-- at point 1 when we're at the end. We still use a ps like storage with the +-- operator last in an entry. It's typical code that evolves stepwise till a +-- point of no comprehension. + +-- For deltas we need a rather complex loop over points that can have holes and +-- be less than nofpoints and even can have duplicates and also the x and y value +-- lists can be shorter than etc. I need fonts in order to complete this simply +-- because I need to visualize in order to understand (what the standard tries +-- to explain). + +-- 0 point then none applied +-- 1 points then applied to all +-- otherwise inferred deltas using nearest +-- if no lower point then use highest referenced point +-- if no higher point then use lowest referenced point +-- factor = (target-left)/(right-left) +-- delta = (1-factor)*left + factor * right + local next, type, unpack = next, type, unpack -local bittest = bit32.btest -local sqrt = math.sqrt +local band, rshift = bit32.band, bit32.rshift +local sqrt, round = math.sqrt, math.round +local char = string.char +local concat = table.concat + +local report = logs.reporter("otf reader","ttf") + +local trace_deltas = false + +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 -- 8-bit unsigned integer +local readushort = streamreader.readcardinal2 -- 16-bit unsigned integer +local readulong = streamreader.readcardinal4 -- 24-bit unsigned integer +local readchar = streamreader.readinteger1 -- 8-bit signed integer +local readshort = streamreader.readinteger2 -- 16-bit signed integer +local read2dot14 = streamreader.read2dot14 -- 16-bit signed fixed number with the low 14 bits of fraction (2.14) (F2DOT14) +local readinteger = streamreader.readinteger1 +local readcardinaltable = streamreader.readcardinaltable +local readintegertable = streamreader.readintegertable + +directives.register("fonts.streamreader",function() + + streamreader = utilities.streams + + setposition = streamreader.setposition + getposition = streamreader.getposition + skipbytes = streamreader.skip + readbyte = streamreader.readcardinal1 + readushort = streamreader.readcardinal2 + readulong = streamreader.readcardinal4 + readchar = streamreader.readinteger1 + readshort = streamreader.readinteger2 + read2dot14 = streamreader.read2dot14 + readinteger = streamreader.readinteger1 + readcardinaltable = streamreader.readcardinaltable + readintegertable = streamreader.readintegertable -local report = logs.reporter("otf reader","ttf") +end) -local readers = fonts.handlers.otf.readers -local streamreader = readers.streamreader +local short = 2 +local ushort = 2 +local ulong = 4 -local setposition = streamreader.setposition -local getposition = streamreader.getposition -local skipbytes = streamreader.skip -local readbyte = streamreader.readcardinal1 -- 8-bit unsigned integer -local readushort = streamreader.readcardinal2 -- 16-bit unsigned integer -local readulong = streamreader.readcardinal4 -- 24-bit unsigned integer -local readchar = streamreader.readinteger1 -- 8-bit signed integer -local readshort = streamreader.readinteger2 -- 16-bit signed integer -local read2dot14 = streamreader.read2dot14 -- 16-bit signed fixed number with the low 14 bits of fraction (2.14) (F2DOT14) +local helpers = readers.helpers +local gotodatatable = helpers.gotodatatable local function mergecomposites(glyphs,shapes) + -- todo : deltas + local function merge(index,shape,components) local contours = { } + local points = { } local nofcontours = 0 + local nofpoints = 0 + local offset = 0 + local deltas = shape.deltas for i=1,#components do local component = components[i] local subindex = component.index local subshape = shapes[subindex] local subcontours = subshape.contours + local subpoints = subshape.points if not subcontours then local subcomponents = subshape.components if subcomponents then - subcontours = merge(subindex,subshape,subcomponents) + subcontours, subpoints = merge(subindex,subshape,subcomponents) end end - if subcontours then + if subpoints then local matrix = component.matrix local xscale = matrix[1] local xrotate = matrix[2] @@ -49,36 +115,39 @@ local function mergecomposites(glyphs,shapes) local yscale = matrix[4] local xoffset = matrix[5] local yoffset = matrix[6] + for i=1,#subpoints do + local p = subpoints[i] + local x = p[1] + local y = p[2] + nofpoints = nofpoints + 1 + points[nofpoints] = { + xscale * x + xrotate * y + xoffset, + yscale * y + yrotate * x + yoffset, + p[3] + } + end 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 + contours[nofcontours] = offset + subcontours[i] end + offset = offset + #subpoints else report("missing contours composite %s, component %s of %s, glyph %s",index,i,#components,subindex) end end + shape.points = points -- todo : phantom points shape.contours = contours shape.components = nil - return contours + return contours, points end for index=1,#glyphs do - local shape = shapes[index] - local components = shape.components - if components then - merge(index,shape,components) + local shape = shapes[index] + if shape then + local components = shape.components + if components then + merge(index,shape,components) + end end end @@ -92,145 +161,561 @@ end -- begin of converter --- make paths: the ff code is quite complex but it looks like we need to deal --- with all kind of on curve border cases - local function curveto(m_x,m_y,l_x,l_y,r_x,r_y) -- todo: inline this - return { + 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" -- "curveto" - } + r_x, r_y, "c" end -- We could omit the operator which saves some 10%: -- --- #2=lineto #4=quadratic #6=cubic #3=moveto (with "m") +-- #2=lineto #4=quadratic #6=cubic #3=moveto (with "m") -- --- For the moment we keep the original outlines but that default might change --- in the future. In any case, a backend should support both. +-- This is tricky ... something to do with phantom points .. however, the hvar +-- and vvar tables should take care of the width .. the test font doesn't have +-- those so here we go then (we need a flag for hvar). -- --- The code is a bit messy. I looked at the ff code but it's messy too. It has --- to do with the fact that we need to look at points on the curve and control --- points in between. This also means that we start at point 2 and have to look at --- point 1 when we're at the end. We still use a ps like storage with the operator --- last in an entry. It's typical code that evolves stepwise till a point of no --- comprehension. - -local function contours2outlines(glyphs,shapes) - local quadratic = true - -- local quadratic = false +-- h-advance left-side-bearing v-advance top-side-bearing +-- +-- We had two loops (going backward) but can do it in one loop .. but maybe we +-- should only accept fonts with proper hvar tables. + +local function applyaxis(glyph,shape,deltas,dowidth) + local points = shape.points + if points then + local nofpoints = #points + local h = nofpoints + 2 -- weird, the example font seems to have left first + local l = nofpoints + 1 + ----- v = nofpoints + 3 + ----- t = nofpoints + 4 + local dw = 0 + local dl = 0 + for i=1,#deltas do + local deltaset = deltas[i] + local xvalues = deltaset.xvalues + local yvalues = deltaset.yvalues + local dpoints = deltaset.points + local factor = deltaset.factor + if dpoints then + -- todo: interpolate + local nofdpoints = #dpoints + for i=1,nofdpoints do + local d = dpoints[i] + local p = points[d] + if p then + if xvalues then + local x = xvalues[i] + if x and x ~= 0 then + p[1] = p[1] + factor * x + end + end + if yvalues then + local y = yvalues[i] + if y and y ~= 0 then + p[2] = p[2] + factor * y + end + end + elseif dowidth then + -- we've now ran into phantom points which is a bit fuzzy because: + -- are there gaps in there? + -- + -- todo: move this outside the loop (when we can be sure of all 4 being there) + if d == h then + -- we have a phantom point hadvance + local x = xvalues[i] + if x then + dw = dw + factor * x + end + elseif d == l then + local x = xvalues[i] + if x then + dl = dl + factor * x + end + end + end + end + else + for i=1,nofpoints do + local p = points[i] + if xvalues then + local x = xvalues[i] + if x and x ~= 0 then + p[1] = p[1] + factor * x + end + end + if yvalues then + local y = yvalues[i] + if y and y ~= 0 then + p[2] = p[2] + factor * y + end + end + end + if dowidth then + local x = xvalues[h] + if x then + dw = dw + factor * x + end + local x = xvalues[l] + if x then + dl = dl + factor * x + end + end + end + end + -- for i=1,nofpoints do + -- local p = points[i] + -- p[1] = round(p[1]) + -- p[2] = round(p[2]) + -- end + if dowidth then + local width = glyph.width or 0 + -- local lsb = glyph.lsb or 0 + glyph.width = width + dw - dl + end + else + report("no points for glyph %a",glyph.name) + end +end + +-- round or not ? + +-- local quadratic = true -- both methods work, todo: install a directive +local quadratic = false + +local function contours2outlines_normal(glyphs,shapes) -- maybe accept the bbox overhead 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] - -- todo no new tables but reuse lineto and quadratic - if nofcontour == 1 then - -- this can influence the boundingbox - first_pt[3] = "m" -- "moveto" - nofsegments = nofsegments + 1 - segments[nofsegments] = first_pt - else -- maybe also treat n == 2 special - 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 + local shape = shapes[index] + if shape then + local glyph = glyphs[index] + local contours = shape.contours + local points = shape.points + if contours then + local nofcontours = #contours + local segments = { } + local nofsegments = 0 + glyph.segments = segments + if nofcontours > 0 then + local px, py = 0, 0 -- we could use these in calculations which saves a copy + local first = 1 + for i=1,nofcontours do + local last = contours[i] + if last >= first then + local first_pt = points[first] + local first_on = first_pt[3] + -- todo no new tables but reuse lineto and quadratic + if first == last then + first_pt[3] = "m" -- "moveto" + nofsegments = nofsegments + 1 + segments[nofsegments] = first_pt + else -- maybe also treat n == 2 special + local first_on = first_pt[3] + local last_pt = points[last] + local last_on = last_pt[3] + local start = 1 + local control_pt = false + if first_on then + start = 2 else - first_pt = { (first_pt[1]+last_pt[1])/2, (first_pt[2]+last_pt[2])/2, false } + 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 - control_pt = first_pt - end - nofsegments = nofsegments + 1 - segments[nofsegments] = { first_pt[1], first_pt[2], "m" } -- "moveto" - 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 - -- both normal points + local x, y = first_pt[1], first_pt[2] + if not done then + xmin, ymin, xmax, ymax = x, y, x, y + done = true + end + nofsegments = nofsegments + 1 + segments[nofsegments] = { x, y, "m" } -- "moveto" + if not quadratic then + px, py = x, y + end + local previous_pt = first_pt + for i=first,last do + local current_pt = points[i] + local current_on = current_pt[3] + local previous_on = previous_pt[3] + if previous_on then + if current_on then + -- both normal points + local x, y = current_pt[1], current_pt[2] + nofsegments = nofsegments + 1 + segments[nofsegments] = { x, y, "l" } -- "lineto" + if not quadratic then + px, py = x, y + end + else + control_pt = current_pt + end + elseif current_on then + local x1, y1 = control_pt[1], control_pt[2] + local x2, y2 = current_pt[1], current_pt[2] nofsegments = nofsegments + 1 - segments[nofsegments] = { current_pt[1], current_pt[2], "l" } -- "lineto" + if quadratic then + segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto" + else + x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2) + segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" } -- "curveto" + end + control_pt = false else + local x2, y2 = (previous_pt[1]+current_pt[1])/2, (previous_pt[2]+current_pt[2])/2 + local x1, y1 = control_pt[1], control_pt[2] + nofsegments = nofsegments + 1 + if quadratic then + segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto" + else + x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2) + segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" } -- "curveto" + end control_pt = current_pt end - elseif current_on then - local ps = segments[nofsegments] + previous_pt = current_pt + end + if first_pt == last_pt then + -- we're already done, probably a simple curve + else nofsegments = nofsegments + 1 - if quadratic then - segments[nofsegments] = { control_pt[1], control_pt[2], current_pt[1], current_pt[2], "q" } -- "quadraticto" + local x2, y2 = first_pt[1], first_pt[2] + if not control_pt then + segments[nofsegments] = { x2, y2, "l" } -- "lineto" + elseif quadratic then + local x1, y1 = control_pt[1], control_pt[2] + segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto" + else + local x1, y1 = control_pt[1], control_pt[2] + x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2) + segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" } -- "curveto" + -- px, py = x2, y2 + end + end + end + end + first = last + 1 + end + end + end + end + end +end + +local function contours2outlines_shaped(glyphs,shapes,keepcurve) + for index=1,#glyphs do + local shape = shapes[index] + if shape then + local glyph = glyphs[index] + local contours = shape.contours + local points = shape.points + if contours then + local nofcontours = #contours + local segments = keepcurve and { } or nil + local nofsegments = 0 + if keepcurve then + glyph.segments = segments + end + if nofcontours > 0 then + local xmin, ymin, xmax, ymax, done = 0, 0, 0, 0, false + local px, py = 0, 0 -- we could use these in calculations which saves a copy + local first = 1 + for i=1,nofcontours do + local last = contours[i] + if last >= first then + local first_pt = points[first] + local first_on = first_pt[3] + -- todo no new tables but reuse lineto and quadratic + if first == last then + -- this can influence the boundingbox + if keepcurve then + first_pt[3] = "m" -- "moveto" + nofsegments = nofsegments + 1 + segments[nofsegments] = first_pt + end + else -- maybe also treat n == 2 special + local first_on = first_pt[3] + local last_pt = points[last] + 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 - 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]) + first_pt = { (first_pt[1]+last_pt[1])/2, (first_pt[2]+last_pt[2])/2, false } end - control_pt = false + control_pt = first_pt + end + local x, y = first_pt[1], first_pt[2] + if not done then + xmin, ymin, xmax, ymax = x, y, x, y + done = true else + 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 + end + if keepcurve then nofsegments = nofsegments + 1 - local halfway_x = (previous_pt[1]+current_pt[1])/2 - local halfway_y = (previous_pt[2]+current_pt[2])/2 + segments[nofsegments] = { x, y, "m" } -- "moveto" + end + if not quadratic then + px, py = x, y + end + local previous_pt = first_pt + for i=first,last do + local current_pt = points[i] + local current_on = current_pt[3] + local previous_on = previous_pt[3] + if previous_on then + if current_on then + -- both normal points + local x, y = current_pt[1], current_pt[2] + 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 + if keepcurve then + nofsegments = nofsegments + 1 + segments[nofsegments] = { x, y, "l" } -- "lineto" + end + if not quadratic then + px, py = x, y + end + else + control_pt = current_pt + end + elseif current_on then + local x1, y1 = control_pt[1], control_pt[2] + local x2, y2 = current_pt[1], current_pt[2] + if quadratic 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 + if keepcurve then + nofsegments = nofsegments + 1 + segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto" + end + else + x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2) + 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 + 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 px < xmin then xmin = px elseif px > xmax then xmax = px end + if py < ymin then ymin = py elseif py > ymax then ymax = py end + if keepcurve then + nofsegments = nofsegments + 1 + segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" } -- "curveto" + end + end + control_pt = false + else + local x2, y2 = (previous_pt[1]+current_pt[1])/2, (previous_pt[2]+current_pt[2])/2 + local x1, y1 = control_pt[1], control_pt[2] + if quadratic 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 + if keepcurve then + nofsegments = nofsegments + 1 + segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto" + end + else + x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2) + 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 + 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 px < xmin then xmin = px elseif px > xmax then xmax = px end + if py < ymin then ymin = py elseif py > ymax then ymax = py end + if keepcurve then + nofsegments = nofsegments + 1 + segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" } -- "curveto" + end + end + control_pt = current_pt + end + previous_pt = current_pt + end + if first_pt == last_pt then + -- we're already done, probably a simple curve + elseif not control_pt then + if keepcurve then + nofsegments = nofsegments + 1 + segments[nofsegments] = { first_pt[1], first_pt[2], "l" } -- "lineto" + end + else + local x1, y1 = control_pt[1], control_pt[2] + local x2, y2 = first_pt[1], first_pt[2] + 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 if quadratic then - segments[nofsegments] = { control_pt[1], control_pt[2], halfway_x, halfway_y, "q" } -- "quadraticto" + if keepcurve then + nofsegments = nofsegments + 1 + segments[nofsegments] = { x1, y1, x2, y2, "q" } -- "quadraticto" + end 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) + x1, y1, x2, y2, px, py = curveto(x1, y1, px, py, x2, y2) + 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 px < xmin then xmin = px elseif px > xmax then xmax = px end + if py < ymin then ymin = py elseif py > ymax then ymax = py end + if keepcurve then + nofsegments = nofsegments + 1 + segments[nofsegments] = { x1, y1, x2, y2, px, py, "c" } -- "curveto" + end + -- px, py = x2, y2 end - control_pt = current_pt end - previous_pt = current_pt end - if first_pt == last_pt then - -- we're already done, probably a simple curve + end + first = last + 1 + end + glyph.boundingbox = { round(xmin), round(ymin), round(xmax), round(ymax) } + end + end + end + end +end + +-- optimize for zero + +local c_zero = char(0) +local s_zero = char(0,0) + +local function toushort(n) + return char(band(rshift(n,8),0xFF),band(n,0xFF)) +end + +local function toshort(n) + if n < 0 then + n = n + 0x10000 + end + return char(band(rshift(n,8),0xFF),band(n,0xFF)) +end + +-- todo: we can reuse result, xpoints and ypoints + +local function repackpoints(glyphs,shapes) + local noboundingbox = { 0, 0, 0, 0 } + local result = { } -- reused + for index=1,#glyphs do + local shape = shapes[index] + if shape then + local r = 0 + local glyph = glyphs[index] + if false then -- shape.type == "composite" + -- we merged them + else + local contours = shape.contours + local nofcontours = contours and #contours or 0 + local boundingbox = glyph.boundingbox or noboundingbox + r = r + 1 result[r] = toshort(nofcontours) + r = r + 1 result[r] = toshort(boundingbox[1]) -- xmin + r = r + 1 result[r] = toshort(boundingbox[2]) -- ymin + r = r + 1 result[r] = toshort(boundingbox[3]) -- xmax + r = r + 1 result[r] = toshort(boundingbox[4]) -- ymax + if nofcontours > 0 then + for i=1,nofcontours do + r = r + 1 result[r] = toshort(contours[i]-1) + end + r = r + 1 result[r] = s_zero -- no instructions + local points = shape.points + local currentx = 0 + local currenty = 0 + local xpoints = { } + local ypoints = { } + local x = 0 + local y = 0 + local lastflag = nil + local nofflags = 0 + for i=1,#points do + local pt = points[i] + local px = pt[1] + local py = pt[2] + local fl = pt[3] and 0x01 or 0x00 + if px == currentx then + fl = fl + 0x10 + else + local dx = round(px - currentx) + if dx < -255 or dx > 255 then + x = x + 1 xpoints[x] = toshort(dx) + elseif dx < 0 then + fl = fl + 0x02 + x = x + 1 xpoints[x] = char(-dx) + elseif dx > 0 then + fl = fl + 0x12 + x = x + 1 xpoints[x] = char(dx) + else + fl = fl + 0x02 + x = x + 1 xpoints[x] = c_zero + end + end + if py == currenty then + fl = fl + 0x20 + else + local dy = round(py - currenty) + if dy < -255 or dy > 255 then + y = y + 1 ypoints[y] = toshort(dy) + elseif dy < 0 then + fl = fl + 0x04 + y = y + 1 ypoints[y] = char(-dy) + elseif dy > 0 then + fl = fl + 0x24 + y = y + 1 ypoints[y] = char(dy) else - nofsegments = nofsegments + 1 - if not control_pt then - segments[nofsegments] = { first_pt[1], first_pt[2], "l" } -- "lineto" - elseif quadratic then - segments[nofsegments] = { control_pt[1], control_pt[2], first_pt[1], first_pt[2], "q" } -- "quadraticto" - 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 + fl = fl + 0x04 + y = y + 1 ypoints[y] = c_zero end end + currentx = px + currenty = py + if lastflag == fl then + nofflags = nofflags + 1 + else -- if > 255 + if nofflags == 1 then + r = r + 1 result[r] = char(lastflag) + elseif nofflags == 2 then + r = r + 1 result[r] = char(lastflag,lastflag) + elseif nofflags > 2 then + lastflag = lastflag + 0x08 + r = r + 1 result[r] = char(lastflag,nofflags-1) + end + nofflags = 1 + lastflag = fl + end + end + if nofflags == 1 then + r = r + 1 result[r] = char(lastflag) + elseif nofflags == 2 then + r = r + 1 result[r] = char(lastflag,lastflag) + elseif nofflags > 2 then + lastflag = lastflag + 0x08 + r = r + 1 result[r] = char(lastflag,nofflags-1) end + r = r + 1 result[r] = concat(xpoints) + r = r + 1 result[r] = concat(ypoints) end end + glyph.stream = concat(result,"",1,r) + else + -- fatal end end end -- end of converter -local function readglyph(f,nofcontours) - local points = { } - local endpoints = { } - local instructions = { } - local flags = { } +local function readglyph(f,nofcontours) -- read deltas here, saves space + local points = { } + local instructions = { } + local flags = { } + local contours = { } -- readintegertable(f,nofcontours,short) for i=1,nofcontours do - endpoints[i] = readshort(f) + 1 + contours[i] = readshort(f) + 1 end - local nofpoints = endpoints[nofcontours] + local nofpoints = contours[nofcontours] local nofinstructions = readushort(f) --- f:seek("set",f:seek()+nofinstructions) skipbytes(f,nofinstructions) -- because flags can repeat we don't know the amount ... in fact this is -- not that efficient (small files but more mem) @@ -238,7 +723,7 @@ local function readglyph(f,nofcontours) while i <= nofpoints do local flag = readbyte(f) flags[i] = flag - if bittest(flag,0x0008) then + if band(flag,0x08) ~= 0 then for j=1,readbyte(f) do i = i + 1 flags[i] = flag @@ -251,8 +736,8 @@ local function readglyph(f,nofcontours) local x = 0 for i=1,nofpoints do local flag = flags[i] - local short = bittest(flag,0x0002) - local same = bittest(flag,0x0010) + local short = band(flag,0x02) ~= 0 + local same = band(flag,0x10) ~= 0 if short then if same then x = x + readbyte(f) @@ -264,13 +749,13 @@ local function readglyph(f,nofcontours) else x = x + readshort(f) end - points[i] = { x, y, bittest(flag,0x0001) } + points[i] = { x, 0, band(flag,0x01) ~= 0 } end local y = 0 for i=1,nofpoints do local flag = flags[i] - local short = bittest(flag,0x0004) - local same = bittest(flag,0x0020) + local short = band(flag,0x04) ~= 0 + local same = band(flag,0x20) ~= 0 if short then if same then y = y + readbyte(f) @@ -284,17 +769,11 @@ local function readglyph(f,nofcontours) end points[i][2] = y end - -- we could integrate this if needed - 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", - -- points = points, - contours = endpoints, + type = "glyph", + points = points, + contours = contours, + nofpoints = nofpoints, } end @@ -305,19 +784,19 @@ local function readcomposite(f) while true do local flags = readushort(f) local index = readushort(f) - ----- f_words = bittest(flags,0x0001) - local f_xyarg = bittest(flags,0x0002) - ----- f_round = bittest(flags,0x0004+0x0002) - ----- f_scale = bittest(flags,0x0008) - ----- f_reserved = bittest(flags,0x0010) - ----- f_more = bittest(flags,0x0020) - ----- f_xyscale = bittest(flags,0x0040) - ----- f_matrix = bittest(flags,0x0080) - ----- f_instruct = bittest(flags,0x0100) - ----- f_usemine = bittest(flags,0x0200) - ----- f_overlap = bittest(flags,0x0400) - local f_offset = bittest(flags,0x0800) - ----- f_uoffset = bittest(flags,0x1000) + ----- f_words = band(flags,0x0001) ~= 0 + local f_xyarg = band(flags,0x0002) ~= 0 + ----- f_round = band(flags,0x0006) ~= 0 -- 2 + 4 + ----- f_scale = band(flags,0x0008) ~= 0 + ----- f_reserved = band(flags,0x0010) ~= 0 + ----- f_more = band(flags,0x0020) ~= 0 + ----- f_xyscale = band(flags,0x0040) ~= 0 + ----- f_matrix = band(flags,0x0080) ~= 0 + ----- f_instruct = band(flags,0x0100) ~= 0 + ----- f_usemine = band(flags,0x0200) ~= 0 + ----- f_overlap = band(flags,0x0400) ~= 0 + local f_offset = band(flags,0x0800) ~= 0 + ----- f_uoffset = band(flags,0x1000) ~= 0 local xscale = 1 local xrotate = 0 local yrotate = 0 @@ -327,7 +806,7 @@ local function readcomposite(f) local base = false local reference = false if f_xyarg then - if bittest(flags,0x0001) then -- f_words + if band(flags,0x0001) ~= 0 then -- f_words xoffset = readshort(f) yoffset = readshort(f) else @@ -335,7 +814,7 @@ local function readcomposite(f) yoffset = readchar(f) -- signed byte, stupid name end else - if bittest(flags,0x0001) then -- f_words + if band(flags,0x0001) ~= 0 then -- f_words base = readshort(f) reference = readshort(f) else @@ -343,21 +822,21 @@ local function readcomposite(f) reference = readchar(f) -- signed byte, stupid name end end - if bittest(flags,0x0008) then -- f_scale + if band(flags,0x0008) ~= 0 then -- f_scale xscale = read2dot14(f) yscale = xscale if f_xyarg and f_offset then xoffset = xoffset * xscale yoffset = yoffset * yscale end - elseif bittest(flags,0x0040) then -- f_xyscale + elseif band(flags,0x0040) ~= 0 then -- f_xyscale 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 -- f_matrix + elseif band(flags,0x0080) ~= 0 then -- f_matrix xscale = read2dot14(f) xrotate = read2dot14(f) yrotate = read2dot14(f) @@ -370,22 +849,22 @@ local function readcomposite(f) nofcomponents = nofcomponents + 1 components[nofcomponents] = { index = index, - usemine = bittest(flags,0x0200), -- f_usemine - round = bittest(flags,0x0006), -- f_round, + usemine = band(flags,0x0200) ~= 0, -- f_usemine + round = band(flags,0x0006) ~= 0, -- f_round, base = base, reference = reference, matrix = { xscale, xrotate, yrotate, yscale, xoffset, yoffset }, } - if bittest(flags,0x0100) then + if band(flags,0x0100) ~= 0 then instructions = true end - if not bittest(flags,0x0020) then -- f_more + if not band(flags,0x0020) ~= 0 then -- f_more break end end return { - type = "composite", - components = components, + type = "composite", + components = components, } end @@ -407,15 +886,13 @@ function readers.loca(f,fontdata,specification) local locations = { } setposition(f,datatable.offset) if format == 1 then - local nofglyphs = datatable.length/4 - 1 - -1 + local nofglyphs = datatable.length/4 - 2 for i=0,nofglyphs do locations[i] = offset + readulong(f) end fontdata.nofglyphs = nofglyphs else - local nofglyphs = datatable.length/2 - 1 - -1 + local nofglyphs = datatable.length/2 - 2 for i=0,nofglyphs do locations[i] = offset + readushort(f) * 2 end @@ -427,54 +904,374 @@ function readers.loca(f,fontdata,specification) end function readers.glyf(f,fontdata,specification) -- part goes to cff module - 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), -- xmin - readshort(f), -- ymin - readshort(f), -- xmax - readshort(f), -- ymax - } - if not loadshapes then - -- save space - elseif nofcontours == 0 then - shapes[index] = readnothing(f,nofcontours) - elseif nofcontours > 0 then - shapes[index] = readglyph(f,nofcontours) + local tableoffset = gotodatatable(f,fontdata,"glyf",specification.glyphs) + if tableoffset 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 or specification.instance + 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), -- xmin + readshort(f), -- ymin + readshort(f), -- xmax + readshort(f), -- ymax + } + if not loadshapes then + -- save space + 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 + if readers.gvar then + readers.gvar(f,fontdata,specification,glyphs,shapes) + end + mergecomposites(glyphs,shapes) + if specification.instance then + if specification.streams then + repackpoints(glyphs,shapes) + else + contours2outlines_shaped(glyphs,shapes,specification.shapes) + end + elseif specification.shapes then + contours2outlines_normal(glyphs,shapes) + end + end + end + end +end + +-- gvar is a bit crazy format and one can really wonder if the bit-jugling obscurity +-- is still needed in these days .. cff is much nicer with these blends while the ttf +-- coding variant looks quite horrible + +local function readtuplerecord(f,nofaxis) + local record = { } + for i=1,nofaxis do + record[i] = read2dot14(f) + end + return record +end + +-- (1) the first is a real point the rest deltas +-- (2) points can be present more than once (multiple deltas then) + +local function readpoints(f) + local count = readbyte(f) + if count == 0 then + -- second byte not used, deltas for all point numbers + return nil, 0 -- todo + else + if count < 128 then + -- no second byte, use count + elseif band(count,0x80) ~= 0 then + count = band(count,0x7F) * 256 + readbyte(f) + else + -- bad news + end + local points = { } + local p = 0 + local n = 1 -- indices + while p < count do + local control = readbyte(f) + local runreader = band(control,0x80) ~= 0 and readushort or readbyte + local runlength = band(control,0x7F) + for i=1,runlength+1 do + n = n + runreader(f) + p = p + 1 + points[p] = n + end + end + return points, p + end +end + +local function readdeltas(f,nofpoints) + local deltas = { } + local p = 0 + local z = 0 + while nofpoints > 0 do + local control = readbyte(f) +if not control then + break +end + local allzero = band(control,0x80) ~= 0 + local runlength = band(control,0x3F) + 1 + if allzero then + z = z + runlength + else + local runreader = band(control,0x40) ~= 0 and readshort or readinteger + if z > 0 then + for i=1,z do + p = p + 1 + deltas[p] = 0 + end + z = 0 + end + for i=1,runlength do + p = p + 1 + deltas[p] = runreader(f) + end + end + nofpoints = nofpoints - runlength + end + -- saves space +-- if z > 0 then +-- for i=1,z do +-- p = p + 1 +-- deltas[p] = 0 +-- end +-- end + if p > 0 then + -- forget about trailing zeros + return deltas + else + -- forget about all zeros + end +end + +local function readdeltas(f,nofpoints) + local deltas = { } + local p = 0 + while nofpoints > 0 do + local control = readbyte(f) + if control then + local allzero = band(control,0x80) ~= 0 + local runlength = band(control,0x3F) + 1 + if allzero then + for i=1,runlength do + p = p + 1 + deltas[p] = 0 + end + else + local runreader = band(control,0x40) ~= 0 and readshort or readinteger + for i=1,runlength do + p = p + 1 + deltas[p] = runreader(f) + end + end + nofpoints = nofpoints - runlength + else + -- it happens + break + end + end + -- saves space + if p > 0 then + return deltas + else + -- forget about all zeros + end +end + +function readers.gvar(f,fontdata,specification,glyphdata,shapedata) + -- this is one of the messiest tables + local instance = specification.instance + if not instance then + return + end + local factors = specification.factors + if not factors then + return + end + local tableoffset = gotodatatable(f,fontdata,"gvar",specification.variable or specification.shapes) + if tableoffset then + local version = readulong(f) -- 1.0 + local nofaxis = readushort(f) + local noftuples = readushort(f) + local tupleoffset = tableoffset + readulong(f) + local nofglyphs = readushort(f) + local flags = readushort(f) + local dataoffset = tableoffset + readulong(f) + local data = { } + local tuples = { } + local glyphdata = fontdata.glyphs + local dowidth = not fontdata.variabledata.hvarwidths + -- there is one more offset (so that one can calculate the size i suppose) + -- so we could test for overflows but we simply assume sane font files + if band(flags,0x0001) ~= 0 then + for i=1,nofglyphs+1 do + data[i] = dataoffset + readulong(f) + end + else + for i=1,nofglyphs+1 do + data[i] = dataoffset + 2*readushort(f) + end + end + -- + if noftuples > 0 then + setposition(f,tupleoffset) + for i=1,noftuples do + tuples[i] = readtuplerecord(f,nofaxis) + end + end + local nextoffset = false + local startoffset = data[1] + for i=1,nofglyphs do -- hm one more cf spec + nextoffset = data[i+1] + local glyph = glyphdata[i-1] + local name = trace_deltas and glyph.name + if startoffset == nextoffset then + if name then + report("no deltas for glyph %a",name) + end + else + local shape = shapedata[i-1] -- todo 0 + if not shape then + if name then + report("no shape for glyph %a",name) + end + else + lastoffset = startoffset + setposition(f,startoffset) + local flags = readushort(f) + local count = band(flags,0x0FFF) + local offset = startoffset + readushort(f) -- to serialized + local deltas = { } + local allpoints = (shape.nofpoints or 0) -- + 1 + local shared = false + local nofshared = 0 + if band(flags,0x8000) ~= 0 then -- has shared points + -- go to the packed stream (get them once) + local current = getposition(f) + setposition(f,offset) + shared, nofshared = readpoints(f) + offset = getposition(f) + setposition(f,current) + -- and back to the table + end + for j=1,count do + local size = readushort(f) -- check + local flags = readushort(f) + local index = band(flags,0x0FFF) + local haspeak = band(flags,0x8000) ~= 0 + local intermediate = band(flags,0x4000) ~= 0 + local private = band(flags,0x2000) ~= 0 + local peak = nil + local start = nil + local stop = nil + local xvalues = nil + local yvalues = nil + local points = shared -- we default to shared + local nofpoints = nofshared -- we default to shared + -- local advance = 4 + if haspeak then + peak = readtuplerecord(f,nofaxis) + -- advance = advance + 2*nofaxis else - shapes[index] = readcomposite(f,nofcontours) + if index+1 > #tuples then + report("error, bad tuple index",index) + end + peak = tuples[index+1] -- hm, needs checking, only peak? end - else - if loadshapes then - shapes[index] = { } + if intermediate then + start = readtuplerecord(f,nofaxis) + stop = readtuplerecord(f,nofaxis) + -- advance = advance + 4*nofaxis + end + -- get the deltas + if size > 0 then + local current = getposition(f) + -- goto the packed stream + setposition(f,offset) + if private then + points, nofpoints = readpoints(f) + end -- else + if nofpoints == 0 then + nofpoints = allpoints + 4 + end + if nofpoints > 0 then + -- a nice test is to do only one + xvalues = readdeltas(f,nofpoints) + yvalues = readdeltas(f,nofpoints) + end + -- resync offset + offset = offset + size + -- back to the table + setposition(f,current) + end + if not xvalues and not yvalues then + points = nil + end + local s = 1 + for i=1,nofaxis do + local f = factors[i] + local peak = peak and peak [i] or 0 + -- local start = start and start[i] or 0 + -- local stop = stop and stop [i] or 0 + local start = start and start[i] or (peak < 0 and peak or 0) + local stop = stop and stop [i] or (peak > 0 and peak or 0) + -- do we really need these tests ... can't we assume sane values + if start > peak or peak > stop then + -- * 1 + elseif start < 0 and stop > 0 and peak ~= 0 then + -- * 1 + elseif peak == 0 then + -- * 1 + elseif f < start or f > stop then + -- * 0 + s = 0 + break + elseif f < peak then +-- s = - s * (f - start) / (peak - start) + s = s * (f - start) / (peak - start) + elseif f > peak then + s = s * (stop - f) / (stop - peak) + else + -- * 1 + end + end + if s == 0 then + if name then + report("no deltas applied for glyph %a",name) + end + else + deltas[#deltas+1] = { + factor = s, + points = points, + xvalues = xvalues, + yvalues = yvalues, + } end - glyphs[index].boundingbox = nothing end - end - if loadshapes then - mergecomposites(glyphs,shapes) - contours2outlines(glyphs,shapes) + if shape.type == "glyph" then +-- if glyph.name == "u1f31d" then +-- if glyph.unicode == 127773 then +-- inspect(deltas) +-- end + applyaxis(glyph,shape,deltas,dowidth) + else + -- todo: args_are_xy_values mess .. i have to be really bored + -- and motivated to deal with it + shape.deltas = deltas + end end end + startoffset = nextoffset end end end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-vfc.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-vfc.lua new file mode 100644 index 00000000000..3e48422d621 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-font-vfc.lua @@ -0,0 +1,99 @@ +if not modules then modules = { } end modules ['font-vfc'] = { + version = 1.001, + comment = "companion to font-ini.mkiv and hand-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local select = select +local insert = table.insert + +local fonts = fonts +local helpers = fonts.helpers + +local setmetatableindex = table.setmetatableindex +local makeweak = table.makeweak + +-- Helpers dealing with virtual fonts: beware, these are final values so +-- don't change the content of tables gotten this way! + +local push = { "push" } +local pop = { "pop" } +local dummy = { "comment" } + +function helpers.prependcommands(commands,...) + insert(commands,1,push) + for i=select("#",...),1,-1 do + local s = select(i,...) + if s then + insert(commands,1,s) + end + end + insert(commands,pop) + return commands +end + +function helpers.appendcommands(commands,...) + insert(commands,1,push) + insert(commands,pop) + for i=1,select("#",...) do + local s = select(i,...) + if s then + insert(commands,s) + end + end + return commands +end + +-- todo: maybe weak +-- todo: maybe indirect so that we can't change them + +local char = setmetatableindex(function(t,k) + -- local v = { "char", k } + local v = { "slot", 0, k } + t[k] = v + return v +end) + +local right = setmetatableindex(function(t,k) + local v = { "right", k } + t[k] = v + return v +end) + +local left = setmetatableindex(function(t,k) + local v = { "right", -k } + t[k] = v + return v +end) + +local down = setmetatableindex(function(t,k) + local v = { "down", k } + t[k] = v + return v +end) + +local up = setmetatableindex(function(t,k) + local v = { "down", -k } + t[k] = v + return v +end) + +-- makeweak(char) +-- makeweak(right) +-- makeweak(left) +-- makeweak(up) +-- makeweak(down) + +helpers.commands = utilities.storage.allocate { + char = char, + right = right, + left = left, + down = down, + up = up, + push = push, + pop = pop, + dummy = dummy, +} + diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-def.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-def.lua new file mode 100644 index 00000000000..883451fb5e6 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-def.lua @@ -0,0 +1,98 @@ +if not modules then modules = { } end modules ['luatex-fonts-def'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + os.exit() +end + +local fonts = fonts + +-- A bit of tuning for definitions. + +fonts.constructors.namemode = "specification" -- somehow latex needs this (changed name!) => will change into an overload + +-- tricky: we sort of bypass the parser and directly feed all into +-- the sub parser + +function fonts.definers.getspecification(str) + return "", str, "", ":", str +end + +-- the generic name parser (different from context!) + +local list = { } -- we could pass Carg but let's keep the old one + +local function issome () list.lookup = 'name' end -- xetex mode prefers name (not in context!) +local function isfile () list.lookup = 'file' end +local function isname () list.lookup = 'name' end +local function thename(s) list.name = s end +local function issub (v) list.sub = v end +local function iscrap (s) list.crap = string.lower(s) end +local function iskey (k,v) list[k] = v end +local function istrue (s) list[s] = true end +local function isfalse(s) list[s] = false end + +local P, S, R, C, Cs = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cs + +local spaces = P(" ")^0 +local namespec = Cs((P("{")/"") * (1-S("}"))^0 * (P("}")/"") + (1-S("/:("))^0) +local crapspec = spaces * P("/") * (((1-P(":"))^0)/iscrap) * spaces +local filename_1 = P("file:")/isfile * (namespec/thename) +local filename_2 = P("[") * P(true)/isfile * (((1-P("]"))^0)/thename) * P("]") +local fontname_1 = P("name:")/isname * (namespec/thename) +local fontname_2 = P(true)/issome * (namespec/thename) +local sometext = R("az","AZ","09")^1 +local somekey = R("az","AZ","09")^1 +local somevalue = (P("{")/"")*(1-P("}"))^0*(P("}")/"") + (1-S(";"))^1 +local truevalue = P("+") * spaces * (sometext/istrue) +local falsevalue = P("-") * spaces * (sometext/isfalse) +local keyvalue = (C(somekey) * spaces * P("=") * spaces * C(somevalue))/iskey +local somevalue = sometext/istrue +local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim +local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces +local options = P(":") * spaces * (P(";")^0 * option)^0 + +local pattern = (filename_1 + filename_2 + fontname_1 + fontname_2) + * subvalue^0 * crapspec^0 * options^0 + +function fonts.definers.analyze(str,size) + local specification = fonts.definers.makespecification(str,nil,nil,nil,":",nil,size) + list = { } + lpeg.match(pattern,str) + list.crap = nil + if list.name then + specification.name = list.name + list.name = nil + end + if list.lookup then + specification.lookup = list.lookup + list.lookup = nil + end + if list.sub then + specification.sub = list.sub + list.sub = nil + end + specification.features.normal = fonts.handlers.otf.features.normalize(list) + list = nil + return specification +end + +function fonts.definers.applypostprocessors(tfmdata) + local postprocessors = tfmdata.postprocessors + if postprocessors then + for i=1,#postprocessors do + local extrahash = postprocessors[i](tfmdata) -- after scaling etc + if type(extrahash) == "string" and extrahash ~= "" then + -- e.g. a reencoding needs this + extrahash = string.gsub(lower(extrahash),"[^a-z]","-") + tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) + end + end + end + return tfmdata +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-demo-vf-1.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-demo-vf-1.lua deleted file mode 100644 index 793526f7be3..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-demo-vf-1.lua +++ /dev/null @@ -1,52 +0,0 @@ -if not modules then modules = { } end modules ['luatex-fonts-demo-vf-1'] = { - 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" -} - -local identifiers = fonts.hashes.identifiers - -return function(specification) - local f1, id1 = fonts.constructors.readanddefine('lmroman10-regular', specification.size) - local f2, id2 = fonts.constructors.readanddefine('lmsans10-regular', specification.size) - local f3, id3 = fonts.constructors.readanddefine('lmtypewriter10-regular',specification.size) - if f1 and f2 and f3 then - f1.properties.name = specification.name - f1.properties.virtualized = true - f1.fonts = { - { id = id1 }, - { id = id2 }, - { id = id3 }, - } - local color = { [0] = - { "special", "pdf:0 g" }, - { "special", "pdf:1 0 0 rg" }, - { "special", "pdf:0 1 0 rg" }, - { "special", "pdf:0 0 1 rg" }, - { "special", "pdf:0 0 1 rg" }, - } - local chars = { - identifiers[id1].characters, - identifiers[id2].characters, - identifiers[id3].characters, - } - for u, v in next, f1.characters do - local n = math.floor(math.random(1,3)+0.5) - local c = chars[n][u] or v - v.commands = { - color[n], - { 'slot', n, u }, - color[0], - { 'nop' } - } - v.kerns = nil - v.width = c.width - v.height = c.height - v.depth = c.depth - v.italic = nil - end - end - return f1 -end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-enc.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-enc.lua index c076d59477d..2bc6b71bf49 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-enc.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-enc.lua @@ -7,7 +7,6 @@ if not modules then modules = { } end modules ['luatex-font-enc'] = { } if context then - texio.write_nl("fatal error: this module is not for context") os.exit() end @@ -19,7 +18,7 @@ encodings.known = { } setmetatable(encodings.agl, { __index = function(t,k) if k == "unicodes" then - texio.write(" ") + logs.report("fonts","loading (extended) adobe glyph list") local unicodes = dofile(resolvers.findfile("font-age.lua")) encodings.agl = { unicodes = unicodes } return unicodes diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-ext.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-ext.lua index 0b61e27fd67..aee43ec4b30 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-ext.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-ext.lua @@ -7,88 +7,54 @@ if not modules then modules = { } end modules ['luatex-fonts-ext'] = { } 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 - --- A few generic extensions. +local byte = string.byte -local function initializeitlc(tfmdata,value) - if value then - -- the magic 40 and it formula come from Dohyun Kim but we might need another guess - 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 +local fonts = fonts +local handlers = fonts.handlers +local otf = handlers.otf +local afm = handlers.afm +local registerotffeature = otf.features.register +local registerafmfeature = afm.features.register -otffeatures.register { - name = "itlc", - description = "italic correction", - initializers = { - base = initializeitlc, - node = initializeitlc, - } -} +-- extra generic stuff --- slant and extend +function fonts.loggers.onetimemessage() end -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 +-- done elsewhere +-- +-- loadmodule('font-ext-imp-italic.lua') +-- loadmodule('font-ext-imp-effect.lua') +-- loadmodule('luatex-fonts-lig.lua') -otffeatures.register { - name = "slant", - description = "slant glyphs", - initializers = { - base = initializeslant, - node = initializeslant, - } -} +-- protrusion (simplified version) -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 +fonts.protrusions = fonts.protrusions or { } +fonts.protrusions.setups = fonts.protrusions.setups or { } +local setups = fonts.protrusions.setups -otffeatures.register { - name = "extend", - description = "scale glyphs horizontally", - initializers = { - base = initializeextend, - node = initializeextend, - } -} +setups['default'] = { -- demo vector --- expansion and protrusion + factor = 1, + left = 1, + right = 1, -fonts.protrusions = fonts.protrusions or { } -fonts.protrusions.setups = fonts.protrusions.setups or { } + [0x002C] = { 0, 1 }, -- comma + [0x002E] = { 0, 1 }, -- period + [0x003A] = { 0, 1 }, -- colon + [0x003B] = { 0, 1 }, -- semicolon + [0x002D] = { 0, 1 }, -- hyphen + [0x2013] = { 0, 0.50 }, -- endash + [0x2014] = { 0, 0.33 }, -- emdash + [0x3001] = { 0, 1 }, -- ideographic comma 、 + [0x3002] = { 0, 1 }, -- ideographic full stop 。 + [0x060C] = { 0, 1 }, -- arabic comma ، + [0x061B] = { 0, 1 }, -- arabic semicolon ؛ + [0x06D4] = { 0, 1 }, -- arabic full stop ۔ -local setups = fonts.protrusions.setups +} local function initializeprotrusion(tfmdata,value) if value then @@ -111,7 +77,7 @@ local function initializeprotrusion(tfmdata,value) end end -otffeatures.register { +local specification = { name = "protrusion", description = "shift characters into the left and or right margin", initializers = { @@ -120,10 +86,32 @@ otffeatures.register { } } -fonts.expansions = fonts.expansions or { } -fonts.expansions.setups = fonts.expansions.setups or { } +registerotffeature(specification) +registerafmfeature(specification) + +-- expansion (simplified version) + +fonts.expansions = fonts.expansions or { } +fonts.expansions.setups = fonts.expansions.setups or { } +local setups = fonts.expansions.setups -local setups = fonts.expansions.setups +setups['default'] = { -- demo vector + + 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, +} local function initializeexpansion(tfmdata,value) if value then @@ -148,7 +136,7 @@ local function initializeexpansion(tfmdata,value) end end -otffeatures.register { +local specification = { name = "expansion", description = "apply hz optimization", initializers = { @@ -157,55 +145,20 @@ otffeatures.register { } } --- left over - -function fonts.loggers.onetimemessage() end - --- example vectors - -local byte = string.byte +registerotffeature(specification) +registerafmfeature(specification) -fonts.expansions.setups['default'] = { +-- normalizer (generic only) - 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, -} +if not otf.features.normalize then -fonts.protrusions.setups['default'] = { - - factor = 1, left = 1, right = 1, - - [0x002C] = { 0, 1 }, -- comma - [0x002E] = { 0, 1 }, -- period - [0x003A] = { 0, 1 }, -- colon - [0x003B] = { 0, 1 }, -- semicolon - [0x002D] = { 0, 1 }, -- hyphen - [0x2013] = { 0, 0.50 }, -- endash - [0x2014] = { 0, 0.33 }, -- emdash - [0x3001] = { 0, 1 }, -- ideographic comma 、 - [0x3002] = { 0, 1 }, -- ideographic full stop 。 - [0x060C] = { 0, 1 }, -- arabic comma ، - [0x061B] = { 0, 1 }, -- arabic semicolon ؛ - [0x06D4] = { 0, 1 }, -- arabic full stop ۔ - -} - --- normalizer - -fonts.handlers.otf.features.normalize = function(t) - if t.rand then - t.rand = "random" + otf.features.normalize = function(t) + if t.rand then + t.rand = "random" + end + return t end - return t + end -- bonus @@ -229,6 +182,8 @@ end -- [110] = 109, -- n -- } +-- reencoding (generic only) + fonts.encodings = fonts.encodings or { } local reencodings = { } fonts.encodings.reencodings = reencodings @@ -253,7 +208,7 @@ local function specialreencode(tfmdata,value) end end -local function reencode(tfmdata,value) +local function initialize(tfmdata,value) tfmdata.postprocessors = tfmdata.postprocessors or { } table.insert(tfmdata.postprocessors, function(tfmdata) @@ -262,26 +217,28 @@ local function reencode(tfmdata,value) ) end -otffeatures.register { +registerotffeature { name = "reencode", description = "reencode characters", manipulators = { - base = reencode, - node = reencode, + base = initialize, + node = initialize, } } -local function ignore(tfmdata,key,value) +-- math stuff (generic only) + +local function initialize(tfmdata,key,value) if value then tfmdata.mathparameters = nil end end -otffeatures.register { +registerotffeature { name = "ignoremathconstants", description = "ignore math constants table", initializers = { - base = ignore, - node = ignore, + base = initialize, + node = initialize, } } diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-gbn.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-gbn.lua new file mode 100644 index 00000000000..53be41c7ed4 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-gbn.lua @@ -0,0 +1,300 @@ +if not modules then modules = { } end modules ['luatex-fonts-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" +} + +-- generic [base|node] mode handler + +if context then + os.exit() +end + +local next = next + +local fonts = fonts +local nodes = nodes + +local nuts = nodes.nuts -- context abstraction of direct nodes + +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 + +-- from now on we apply ligaturing and kerning here because it might interfere with complex +-- opentype discretionary handling where the base ligature pass expect some weird extra +-- pointers (which then confuse the tail slider that has some checking built in) + +local n_ligaturing = node.ligaturing +local n_kerning = node.kerning + +local d_ligaturing = nuts.ligaturing +local d_kerning = nuts.kerning + +local basemodepass = true + +local function l_warning() logs.report("fonts","don't call 'node.ligaturing' directly") l_warning = nil end +local function k_warning() logs.report("fonts","don't call 'node.kerning' 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 nuts.ligaturing(...) + if basemodepass and l_warning then + l_warning() + end + return d_ligaturing(...) +end + +function nuts.kerning(...) + if basemodepass and k_warning then + k_warning() + end + return d_kerning(...) +end + +-- direct.ligaturing = nuts.ligaturing +-- direct.kerning = nuts.kerning + +function nodes.handlers.setbasemodepass(v) + basemodepass = v +end + +local function nodepass(head,groupcode,size,packtype,direction) + local fontdata = fonts.hashes.identifiers + if fontdata then + local usedfonts = { } + local basefonts = { } + local prevfont = nil + local basefont = nil + local variants = nil + local redundant = nil + local nofused = 0 + for n in traverse_id(glyph_code,head) 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 -- we need to check shared, only when same features + if shared then + local processors = shared.processes + if processors and #processors > 0 then + usedfonts[font] = processors + nofused = nofused + 1 + 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 + -- per generic user request we always remove selectors + 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 == head then + head = 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,head) 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 -- we need to check shared, only when same features + if shared then + local processors = shared.processes + if processors and #processors > 0 then + usedfonts[font] = processors + nofused = nofused + 1 + 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,direction,nofused) 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 = head == start + local prev, next + if stop then + next = getnext(stop) + start, stop = d_ligaturing(start,stop) + start, stop = d_kerning(start,stop) + else + prev = getprev(start) + start = d_ligaturing(start) + start = d_kerning(start) + end + if prev then + setlink(prev,start) + end + if next then + setlink(stop,next) + end + if front and head ~= start then + head = start + end + end + end + end + end + return head +end + +local function basepass(head) + if basemodepass then + head = d_ligaturing(head) + head = d_kerning(head) + end + return head +end + +local protectpass = node.direct.protect_glyphs +local injectpass = nodes.injections.handler + +-- This is the only official public interface and this one can be hooked into a callback (chain) and +-- everything else can change!@ Functione being visibel doesn't mean that it's part of the api. + +function nodes.handlers.nodepass(head,...) + if head then + return tonode(nodepass(tonut(head),...)) + end +end + +function nodes.handlers.basepass(head) + if head then + return tonode(basepass(tonut(head))) + end +end + +function nodes.handlers.injectpass(head) + if head then + return tonode(injectpass(tonut(head))) + end +end + +function nodes.handlers.protectpass(head) + if head then + protectpass(tonut(head)) + return head + end +end + +function nodes.simple_font_handler(head,groupcode,size,packtype,direction) + if head then + head = tonut(head) + head = nodepass(head,groupcode,size,packtype,direction) + head = injectpass(head) + if not basemodepass then + head = basepass(head) + end + protectpass(head) + head = tonode(head) + end + return head +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-lig.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-lig.lua new file mode 100644 index 00000000000..4ce1265338e --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-lig.lua @@ -0,0 +1,2067 @@ +-- this file is generated by context + +fonts.handlers.otf.addfeature { + ["dataset"]={ + { + ["data"]={ + ["À"]={ "A", "̀" }, + ["Á"]={ "A", "́" }, + ["Â"]={ "A", "̂" }, + ["Ã"]={ "A", "̃" }, + ["Ä"]={ "A", "̈" }, + ["Å"]={ "A", "̊" }, + ["Ç"]={ "C", "̧" }, + ["È"]={ "E", "̀" }, + ["É"]={ "E", "́" }, + ["Ê"]={ "E", "̂" }, + ["Ë"]={ "E", "̈" }, + ["Ì"]={ "I", "̀" }, + ["Í"]={ "I", "́" }, + ["Î"]={ "I", "̂" }, + ["Ï"]={ "I", "̈" }, + ["Ñ"]={ "N", "̃" }, + ["Ò"]={ "O", "̀" }, + ["Ó"]={ "O", "́" }, + ["Ô"]={ "O", "̂" }, + ["Õ"]={ "O", "̃" }, + ["Ö"]={ "O", "̈" }, + ["Ù"]={ "U", "̀" }, + ["Ú"]={ "U", "́" }, + ["Û"]={ "U", "̂" }, + ["Ü"]={ "U", "̈" }, + ["Ý"]={ "Y", "́" }, + ["à"]={ "a", "̀" }, + ["á"]={ "a", "́" }, + ["â"]={ "a", "̂" }, + ["ã"]={ "a", "̃" }, + ["ä"]={ "a", "̈" }, + ["å"]={ "a", "̊" }, + ["ç"]={ "c", "̧" }, + ["è"]={ "e", "̀" }, + ["é"]={ "e", "́" }, + ["ê"]={ "e", "̂" }, + ["ë"]={ "e", "̈" }, + ["ì"]={ "i", "̀" }, + ["í"]={ "i", "́" }, + ["î"]={ "i", "̂" }, + ["ï"]={ "i", "̈" }, + ["ñ"]={ "n", "̃" }, + ["ò"]={ "o", "̀" }, + ["ó"]={ "o", "́" }, + ["ô"]={ "o", "̂" }, + ["õ"]={ "o", "̃" }, + ["ö"]={ "o", "̈" }, + ["ù"]={ "u", "̀" }, + ["ú"]={ "u", "́" }, + ["û"]={ "u", "̂" }, + ["ü"]={ "u", "̈" }, + ["ý"]={ "y", "́" }, + ["ÿ"]={ "y", "̈" }, + ["Ā"]={ "A", "̄" }, + ["ā"]={ "a", "̄" }, + ["Ă"]={ "A", "̆" }, + ["ă"]={ "a", "̆" }, + ["Ą"]={ "A", "̨" }, + ["ą"]={ "a", "̨" }, + ["Ć"]={ "C", "́" }, + ["ć"]={ "c", "́" }, + ["Ĉ"]={ "C", "̂" }, + ["ĉ"]={ "c", "̂" }, + ["Ċ"]={ "C", "̇" }, + ["ċ"]={ "c", "̇" }, + ["Č"]={ "C", "̌" }, + ["č"]={ "c", "̌" }, + ["Ď"]={ "D", "̌" }, + ["ď"]={ "d", "̌" }, + ["Ē"]={ "E", "̄" }, + ["ē"]={ "e", "̄" }, + ["Ĕ"]={ "E", "̆" }, + ["ĕ"]={ "e", "̆" }, + ["Ė"]={ "E", "̇" }, + ["ė"]={ "e", "̇" }, + ["Ę"]={ "E", "̨" }, + ["ę"]={ "e", "̨" }, + ["Ě"]={ "E", "̌" }, + ["ě"]={ "e", "̌" }, + ["Ĝ"]={ "G", "̂" }, + ["ĝ"]={ "g", "̂" }, + ["Ğ"]={ "G", "̆" }, + ["ğ"]={ "g", "̆" }, + ["Ġ"]={ "G", "̇" }, + ["ġ"]={ "g", "̇" }, + ["Ģ"]={ "G", "̧" }, + ["ģ"]={ "g", "̧" }, + ["Ĥ"]={ "H", "̂" }, + ["ĥ"]={ "h", "̂" }, + ["Ĩ"]={ "I", "̃" }, + ["ĩ"]={ "i", "̃" }, + ["Ī"]={ "I", "̄" }, + ["ī"]={ "i", "̄" }, + ["Ĭ"]={ "I", "̆" }, + ["ĭ"]={ "i", "̆" }, + ["Į"]={ "I", "̨" }, + ["į"]={ "i", "̨" }, + ["İ"]={ "I", "̇" }, + ["Ĵ"]={ "J", "̂" }, + ["ĵ"]={ "j", "̂" }, + ["Ķ"]={ "K", "̧" }, + ["ķ"]={ "k", "̧" }, + ["Ĺ"]={ "L", "́" }, + ["ĺ"]={ "l", "́" }, + ["Ļ"]={ "L", "̧" }, + ["ļ"]={ "l", "̧" }, + ["Ľ"]={ "L", "̌" }, + ["ľ"]={ "l", "̌" }, + ["Ń"]={ "N", "́" }, + ["ń"]={ "n", "́" }, + ["Ņ"]={ "N", "̧" }, + ["ņ"]={ "n", "̧" }, + ["Ň"]={ "N", "̌" }, + ["ň"]={ "n", "̌" }, + ["Ō"]={ "O", "̄" }, + ["ō"]={ "o", "̄" }, + ["Ŏ"]={ "O", "̆" }, + ["ŏ"]={ "o", "̆" }, + ["Ő"]={ "O", "̋" }, + ["ő"]={ "o", "̋" }, + ["Ŕ"]={ "R", "́" }, + ["ŕ"]={ "r", "́" }, + ["Ŗ"]={ "R", "̧" }, + ["ŗ"]={ "r", "̧" }, + ["Ř"]={ "R", "̌" }, + ["ř"]={ "r", "̌" }, + ["Ś"]={ "S", "́" }, + ["ś"]={ "s", "́" }, + ["Ŝ"]={ "S", "̂" }, + ["ŝ"]={ "s", "̂" }, + ["Ş"]={ "S", "̧" }, + ["ş"]={ "s", "̧" }, + ["Š"]={ "S", "̌" }, + ["š"]={ "s", "̌" }, + ["Ţ"]={ "T", "̧" }, + ["ţ"]={ "t", "̧" }, + ["Ť"]={ "T", "̌" }, + ["ť"]={ "t", "̌" }, + ["Ũ"]={ "U", "̃" }, + ["ũ"]={ "u", "̃" }, + ["Ū"]={ "U", "̄" }, + ["ū"]={ "u", "̄" }, + ["Ŭ"]={ "U", "̆" }, + ["ŭ"]={ "u", "̆" }, + ["Ů"]={ "U", "̊" }, + ["ů"]={ "u", "̊" }, + ["Ű"]={ "U", "̋" }, + ["ű"]={ "u", "̋" }, + ["Ų"]={ "U", "̨" }, + ["ų"]={ "u", "̨" }, + ["Ŵ"]={ "W", "̂" }, + ["ŵ"]={ "w", "̂" }, + ["Ŷ"]={ "Y", "̂" }, + ["ŷ"]={ "y", "̂" }, + ["Ÿ"]={ "Y", "̈" }, + ["Ź"]={ "Z", "́" }, + ["ź"]={ "z", "́" }, + ["Ż"]={ "Z", "̇" }, + ["ż"]={ "z", "̇" }, + ["Ž"]={ "Z", "̌" }, + ["ž"]={ "z", "̌" }, + ["Ơ"]={ "O", "̛" }, + ["ơ"]={ "o", "̛" }, + ["Ư"]={ "U", "̛" }, + ["ư"]={ "u", "̛" }, + ["Ǎ"]={ "A", "̌" }, + ["ǎ"]={ "a", "̌" }, + ["Ǐ"]={ "I", "̌" }, + ["ǐ"]={ "i", "̌" }, + ["Ǒ"]={ "O", "̌" }, + ["ǒ"]={ "o", "̌" }, + ["Ǔ"]={ "U", "̌" }, + ["ǔ"]={ "u", "̌" }, + ["Ǖ"]={ "Ü", "̄" }, + ["ǖ"]={ "ü", "̄" }, + ["Ǘ"]={ "Ü", "́" }, + ["ǘ"]={ "ü", "́" }, + ["Ǚ"]={ "Ü", "̌" }, + ["ǚ"]={ "ü", "̌" }, + ["Ǜ"]={ "Ü", "̀" }, + ["ǜ"]={ "ü", "̀" }, + ["Ǟ"]={ "Ä", "̄" }, + ["ǟ"]={ "ä", "̄" }, + ["Ǡ"]={ "Ȧ", "̄" }, + ["ǡ"]={ "ȧ", "̄" }, + ["Ǣ"]={ "Æ", "̄" }, + ["ǣ"]={ "æ", "̄" }, + ["Ǧ"]={ "G", "̌" }, + ["ǧ"]={ "g", "̌" }, + ["Ǩ"]={ "K", "̌" }, + ["ǩ"]={ "k", "̌" }, + ["Ǫ"]={ "O", "̨" }, + ["ǫ"]={ "o", "̨" }, + ["Ǭ"]={ "Ǫ", "̄" }, + ["ǭ"]={ "ǫ", "̄" }, + ["Ǯ"]={ "Ʒ", "̌" }, + ["ǯ"]={ "ʒ", "̌" }, + ["ǰ"]={ "j", "̌" }, + ["Ǵ"]={ "G", "́" }, + ["ǵ"]={ "g", "́" }, + ["Ǹ"]={ "N", "̀" }, + ["ǹ"]={ "n", "̀" }, + ["Ǻ"]={ "Å", "́" }, + ["ǻ"]={ "å", "́" }, + ["Ǽ"]={ "Æ", "́" }, + ["ǽ"]={ "æ", "́" }, + ["Ǿ"]={ "Ø", "́" }, + ["ǿ"]={ "ø", "́" }, + ["Ȁ"]={ "A", "̏" }, + ["ȁ"]={ "a", "̏" }, + ["Ȃ"]={ "A", "̑" }, + ["ȃ"]={ "a", "̑" }, + ["Ȅ"]={ "E", "̏" }, + ["ȅ"]={ "e", "̏" }, + ["Ȇ"]={ "E", "̑" }, + ["ȇ"]={ "e", "̑" }, + ["Ȉ"]={ "I", "̏" }, + ["ȉ"]={ "i", "̏" }, + ["Ȋ"]={ "I", "̑" }, + ["ȋ"]={ "i", "̑" }, + ["Ȍ"]={ "O", "̏" }, + ["ȍ"]={ "o", "̏" }, + ["Ȏ"]={ "O", "̑" }, + ["ȏ"]={ "o", "̑" }, + ["Ȑ"]={ "R", "̏" }, + ["ȑ"]={ "r", "̏" }, + ["Ȓ"]={ "R", "̑" }, + ["ȓ"]={ "r", "̑" }, + ["Ȕ"]={ "U", "̏" }, + ["ȕ"]={ "u", "̏" }, + ["Ȗ"]={ "U", "̑" }, + ["ȗ"]={ "u", "̑" }, + ["Ș"]={ "S", "̦" }, + ["ș"]={ "s", "̦" }, + ["Ț"]={ "T", "̦" }, + ["ț"]={ "t", "̦" }, + ["Ȟ"]={ "H", "̌" }, + ["ȟ"]={ "h", "̌" }, + ["Ȧ"]={ "A", "̇" }, + ["ȧ"]={ "a", "̇" }, + ["Ȩ"]={ "E", "̧" }, + ["ȩ"]={ "e", "̧" }, + ["Ȫ"]={ "Ö", "̄" }, + ["ȫ"]={ "ö", "̄" }, + ["Ȭ"]={ "Õ", "̄" }, + ["ȭ"]={ "õ", "̄" }, + ["Ȯ"]={ "O", "̇" }, + ["ȯ"]={ "o", "̇" }, + ["Ȱ"]={ "Ȯ", "̄" }, + ["ȱ"]={ "ȯ", "̄" }, + ["Ȳ"]={ "Y", "̄" }, + ["ȳ"]={ "y", "̄" }, + ["̈́"]={ "̈", "́" }, + ["΅"]={ "¨", "́" }, + ["Ά"]={ "Α", "́" }, + ["Έ"]={ "Ε", "́" }, + ["Ή"]={ "Η", "́" }, + ["Ί"]={ "Ι", "́" }, + ["Ό"]={ "Ο", "́" }, + ["Ύ"]={ "Υ", "́" }, + ["Ώ"]={ "Ω", "́" }, + ["ΐ"]={ "ϊ", "́" }, + ["Ϊ"]={ "Ι", "̈" }, + ["Ϋ"]={ "Υ", "̈" }, + ["ά"]={ "α", "́" }, + ["έ"]={ "ε", "́" }, + ["ή"]={ "η", "́" }, + ["ί"]={ "ι", "́" }, + ["ΰ"]={ "ϋ", "́" }, + ["ϊ"]={ "ι", "̈" }, + ["ϋ"]={ "υ", "̈" }, + ["ό"]={ "ο", "́" }, + ["ύ"]={ "υ", "́" }, + ["ώ"]={ "ω", "́" }, + ["ϓ"]={ "ϒ", "́" }, + ["ϔ"]={ "ϒ", "̈" }, + ["Ѐ"]={ "Е", "̀" }, + ["Ё"]={ "Е", "̈" }, + ["Ѓ"]={ "Г", "́" }, + ["Ї"]={ "І", "̈" }, + ["Ќ"]={ "К", "́" }, + ["Ѝ"]={ "И", "̀" }, + ["Ў"]={ "У", "̆" }, + ["Й"]={ "И", "̆" }, + ["й"]={ "и", "̆" }, + ["ѐ"]={ "е", "̀" }, + ["ё"]={ "е", "̈" }, + ["ѓ"]={ "г", "́" }, + ["ї"]={ "і", "̈" }, + ["ќ"]={ "к", "́" }, + ["ѝ"]={ "и", "̀" }, + ["ў"]={ "у", "̆" }, + ["Ѷ"]={ "Ѵ", "̏" }, + ["ѷ"]={ "ѵ", "̏" }, + ["Ӂ"]={ "Ж", "̆" }, + ["ӂ"]={ "ж", "̆" }, + ["Ӑ"]={ "А", "̆" }, + ["ӑ"]={ "а", "̆" }, + ["Ӓ"]={ "А", "̈" }, + ["ӓ"]={ "а", "̈" }, + ["Ӗ"]={ "Е", "̆" }, + ["ӗ"]={ "е", "̆" }, + ["Ӛ"]={ "Ә", "̈" }, + ["ӛ"]={ "ә", "̈" }, + ["Ӝ"]={ "Ж", "̈" }, + ["ӝ"]={ "ж", "̈" }, + ["Ӟ"]={ "З", "̈" }, + ["ӟ"]={ "з", "̈" }, + ["Ӣ"]={ "И", "̄" }, + ["ӣ"]={ "и", "̄" }, + ["Ӥ"]={ "И", "̈" }, + ["ӥ"]={ "и", "̈" }, + ["Ӧ"]={ "О", "̈" }, + ["ӧ"]={ "о", "̈" }, + ["Ӫ"]={ "Ө", "̈" }, + ["ӫ"]={ "ө", "̈" }, + ["Ӭ"]={ "Э", "̈" }, + ["ӭ"]={ "э", "̈" }, + ["Ӯ"]={ "У", "̄" }, + ["ӯ"]={ "у", "̄" }, + ["Ӱ"]={ "У", "̈" }, + ["ӱ"]={ "у", "̈" }, + ["Ӳ"]={ "У", "̋" }, + ["ӳ"]={ "у", "̋" }, + ["Ӵ"]={ "Ч", "̈" }, + ["ӵ"]={ "ч", "̈" }, + ["Ӹ"]={ "Ы", "̈" }, + ["ӹ"]={ "ы", "̈" }, + ["آ"]={ "ا", "ٓ" }, + ["أ"]={ "ا", "ٔ" }, + ["ؤ"]={ "و", "ٔ" }, + ["إ"]={ "ا", "ٕ" }, + ["ئ"]={ "ي", "ٔ" }, + ["ۀ"]={ "ە", "ٔ" }, + ["ۂ"]={ "ہ", "ٔ" }, + ["ۓ"]={ "ے", "ٔ" }, + ["ऩ"]={ "न", "़" }, + ["ऱ"]={ "र", "़" }, + ["ऴ"]={ "ळ", "़" }, + ["क़"]={ "क", "़" }, + ["ख़"]={ "ख", "़" }, + ["ग़"]={ "ग", "़" }, + ["ज़"]={ "ज", "़" }, + ["ड़"]={ "ड", "़" }, + ["ढ़"]={ "ढ", "़" }, + ["फ़"]={ "फ", "़" }, + ["य़"]={ "य", "़" }, + ["ো"]={ "ে", "া" }, + ["ৌ"]={ "ে", "ৗ" }, + ["ড়"]={ "ড", "়" }, + ["ঢ়"]={ "ঢ", "়" }, + ["য়"]={ "য", "়" }, + ["ਲ਼"]={ "ਲ", "਼" }, + ["ਸ਼"]={ "ਸ", "਼" }, + ["ਖ਼"]={ "ਖ", "਼" }, + ["ਗ਼"]={ "ਗ", "਼" }, + ["ਜ਼"]={ "ਜ", "਼" }, + ["ਫ਼"]={ "ਫ", "਼" }, + ["ୈ"]={ "େ", "ୖ" }, + ["ୋ"]={ "େ", "ା" }, + ["ୌ"]={ "େ", "ୗ" }, + ["ଡ଼"]={ "ଡ", "଼" }, + ["ଢ଼"]={ "ଢ", "଼" }, + ["ஔ"]={ "ஒ", "ௗ" }, + ["ொ"]={ "ெ", "ா" }, + ["ோ"]={ "ே", "ா" }, + ["ௌ"]={ "ெ", "ௗ" }, + ["ై"]={ "ె", "ౖ" }, + ["ೀ"]={ "ಿ", "ೕ" }, + ["ೇ"]={ "ೆ", "ೕ" }, + ["ೈ"]={ "ೆ", "ೖ" }, + ["ೊ"]={ "ೆ", "ೂ" }, + ["ೋ"]={ "ೊ", "ೕ" }, + ["ൊ"]={ "െ", "ാ" }, + ["ോ"]={ "േ", "ാ" }, + ["ൌ"]={ "െ", "ൗ" }, + ["ේ"]={ "ෙ", "්" }, + ["ො"]={ "ෙ", "ා" }, + ["ෝ"]={ "ො", "්" }, + ["ෞ"]={ "ෙ", "ෟ" }, + ["གྷ"]={ "ག", "ྷ" }, + ["ཌྷ"]={ "ཌ", "ྷ" }, + ["དྷ"]={ "ད", "ྷ" }, + ["བྷ"]={ "བ", "ྷ" }, + ["ཛྷ"]={ "ཛ", "ྷ" }, + ["ཀྵ"]={ "ཀ", "ྵ" }, + ["ཱི"]={ "ཱ", "ི" }, + ["ཱུ"]={ "ཱ", "ུ" }, + ["ྲྀ"]={ "ྲ", "ྀ" }, + ["ླྀ"]={ "ླ", "ྀ" }, + ["ཱྀ"]={ "ཱ", "ྀ" }, + ["ྒྷ"]={ "ྒ", "ྷ" }, + ["ྜྷ"]={ "ྜ", "ྷ" }, + ["ྡྷ"]={ "ྡ", "ྷ" }, + ["ྦྷ"]={ "ྦ", "ྷ" }, + ["ྫྷ"]={ "ྫ", "ྷ" }, + ["ྐྵ"]={ "ྐ", "ྵ" }, + ["ဦ"]={ "ဥ", "ီ" }, + ["ᬆ"]={ "ᬅ", "ᬵ" }, + ["ᬈ"]={ "ᬇ", "ᬵ" }, + ["ᬊ"]={ "ᬉ", "ᬵ" }, + ["ᬌ"]={ "ᬋ", "ᬵ" }, + ["ᬎ"]={ "ᬍ", "ᬵ" }, + ["ᬒ"]={ "ᬑ", "ᬵ" }, + ["ᬻ"]={ "ᬺ", "ᬵ" }, + ["ᬽ"]={ "ᬼ", "ᬵ" }, + ["ᭀ"]={ "ᬾ", "ᬵ" }, + ["ᭁ"]={ "ᬿ", "ᬵ" }, + ["ᭃ"]={ "ᭂ", "ᬵ" }, + ["Ḁ"]={ "A", "̥" }, + ["ḁ"]={ "a", "̥" }, + ["Ḃ"]={ "B", "̇" }, + ["ḃ"]={ "b", "̇" }, + ["Ḅ"]={ "B", "̣" }, + ["ḅ"]={ "b", "̣" }, + ["Ḇ"]={ "B", "̱" }, + ["ḇ"]={ "b", "̱" }, + ["Ḉ"]={ "Ç", "́" }, + ["ḉ"]={ "ç", "́" }, + ["Ḋ"]={ "D", "̇" }, + ["ḋ"]={ "d", "̇" }, + ["Ḍ"]={ "D", "̣" }, + ["ḍ"]={ "d", "̣" }, + ["Ḏ"]={ "D", "̱" }, + ["ḏ"]={ "d", "̱" }, + ["Ḑ"]={ "D", "̧" }, + ["ḑ"]={ "d", "̧" }, + ["Ḓ"]={ "D", "̭" }, + ["ḓ"]={ "d", "̭" }, + ["Ḕ"]={ "Ē", "̀" }, + ["ḕ"]={ "ē", "̀" }, + ["Ḗ"]={ "Ē", "́" }, + ["ḗ"]={ "ē", "́" }, + ["Ḙ"]={ "E", "̭" }, + ["ḙ"]={ "e", "̭" }, + ["Ḛ"]={ "E", "̰" }, + ["ḛ"]={ "e", "̰" }, + ["Ḝ"]={ "Ȩ", "̆" }, + ["ḝ"]={ "ȩ", "̆" }, + ["Ḟ"]={ "F", "̇" }, + ["ḟ"]={ "f", "̇" }, + ["Ḡ"]={ "G", "̄" }, + ["ḡ"]={ "g", "̄" }, + ["Ḣ"]={ "H", "̇" }, + ["ḣ"]={ "h", "̇" }, + ["Ḥ"]={ "H", "̣" }, + ["ḥ"]={ "h", "̣" }, + ["Ḧ"]={ "H", "̈" }, + ["ḧ"]={ "h", "̈" }, + ["Ḩ"]={ "H", "̧" }, + ["ḩ"]={ "h", "̧" }, + ["Ḫ"]={ "H", "̮" }, + ["ḫ"]={ "h", "̮" }, + ["Ḭ"]={ "I", "̰" }, + ["ḭ"]={ "i", "̰" }, + ["Ḯ"]={ "Ï", "́" }, + ["ḯ"]={ "ï", "́" }, + ["Ḱ"]={ "K", "́" }, + ["ḱ"]={ "k", "́" }, + ["Ḳ"]={ "K", "̣" }, + ["ḳ"]={ "k", "̣" }, + ["Ḵ"]={ "K", "̱" }, + ["ḵ"]={ "k", "̱" }, + ["Ḷ"]={ "L", "̣" }, + ["ḷ"]={ "l", "̣" }, + ["Ḹ"]={ "Ḷ", "̄" }, + ["ḹ"]={ "ḷ", "̄" }, + ["Ḻ"]={ "L", "̱" }, + ["ḻ"]={ "l", "̱" }, + ["Ḽ"]={ "L", "̭" }, + ["ḽ"]={ "l", "̭" }, + ["Ḿ"]={ "M", "́" }, + ["ḿ"]={ "m", "́" }, + ["Ṁ"]={ "M", "̇" }, + ["ṁ"]={ "m", "̇" }, + ["Ṃ"]={ "M", "̣" }, + ["ṃ"]={ "m", "̣" }, + ["Ṅ"]={ "N", "̇" }, + ["ṅ"]={ "n", "̇" }, + ["Ṇ"]={ "N", "̣" }, + ["ṇ"]={ "n", "̣" }, + ["Ṉ"]={ "N", "̱" }, + ["ṉ"]={ "n", "̱" }, + ["Ṋ"]={ "N", "̭" }, + ["ṋ"]={ "n", "̭" }, + ["Ṍ"]={ "Õ", "́" }, + ["ṍ"]={ "õ", "́" }, + ["Ṏ"]={ "Õ", "̈" }, + ["ṏ"]={ "õ", "̈" }, + ["Ṑ"]={ "Ō", "̀" }, + ["ṑ"]={ "ō", "̀" }, + ["Ṓ"]={ "Ō", "́" }, + ["ṓ"]={ "ō", "́" }, + ["Ṕ"]={ "P", "́" }, + ["ṕ"]={ "p", "́" }, + ["Ṗ"]={ "P", "̇" }, + ["ṗ"]={ "p", "̇" }, + ["Ṙ"]={ "R", "̇" }, + ["ṙ"]={ "r", "̇" }, + ["Ṛ"]={ "R", "̣" }, + ["ṛ"]={ "r", "̣" }, + ["Ṝ"]={ "Ṛ", "̄" }, + ["ṝ"]={ "ṛ", "̄" }, + ["Ṟ"]={ "R", "̱" }, + ["ṟ"]={ "r", "̱" }, + ["Ṡ"]={ "S", "̇" }, + ["ṡ"]={ "s", "̇" }, + ["Ṣ"]={ "S", "̣" }, + ["ṣ"]={ "s", "̣" }, + ["Ṥ"]={ "Ś", "̇" }, + ["ṥ"]={ "ś", "̇" }, + ["Ṧ"]={ "Š", "̇" }, + ["ṧ"]={ "š", "̇" }, + ["Ṩ"]={ "Ṣ", "̇" }, + ["ṩ"]={ "ṣ", "̇" }, + ["Ṫ"]={ "T", "̇" }, + ["ṫ"]={ "t", "̇" }, + ["Ṭ"]={ "T", "̣" }, + ["ṭ"]={ "t", "̣" }, + ["Ṯ"]={ "T", "̱" }, + ["ṯ"]={ "t", "̱" }, + ["Ṱ"]={ "T", "̭" }, + ["ṱ"]={ "t", "̭" }, + ["Ṳ"]={ "U", "̤" }, + ["ṳ"]={ "u", "̤" }, + ["Ṵ"]={ "U", "̰" }, + ["ṵ"]={ "u", "̰" }, + ["Ṷ"]={ "U", "̭" }, + ["ṷ"]={ "u", "̭" }, + ["Ṹ"]={ "Ũ", "́" }, + ["ṹ"]={ "ũ", "́" }, + ["Ṻ"]={ "Ū", "̈" }, + ["ṻ"]={ "ū", "̈" }, + ["Ṽ"]={ "V", "̃" }, + ["ṽ"]={ "v", "̃" }, + ["Ṿ"]={ "V", "̣" }, + ["ṿ"]={ "v", "̣" }, + ["Ẁ"]={ "W", "̀" }, + ["ẁ"]={ "w", "̀" }, + ["Ẃ"]={ "W", "́" }, + ["ẃ"]={ "w", "́" }, + ["Ẅ"]={ "W", "̈" }, + ["ẅ"]={ "w", "̈" }, + ["Ẇ"]={ "W", "̇" }, + ["ẇ"]={ "w", "̇" }, + ["Ẉ"]={ "W", "̣" }, + ["ẉ"]={ "w", "̣" }, + ["Ẋ"]={ "X", "̇" }, + ["ẋ"]={ "x", "̇" }, + ["Ẍ"]={ "X", "̈" }, + ["ẍ"]={ "x", "̈" }, + ["Ẏ"]={ "Y", "̇" }, + ["ẏ"]={ "y", "̇" }, + ["Ẑ"]={ "Z", "̂" }, + ["ẑ"]={ "z", "̂" }, + ["Ẓ"]={ "Z", "̣" }, + ["ẓ"]={ "z", "̣" }, + ["Ẕ"]={ "Z", "̱" }, + ["ẕ"]={ "z", "̱" }, + ["ẖ"]={ "h", "̱" }, + ["ẗ"]={ "t", "̈" }, + ["ẘ"]={ "w", "̊" }, + ["ẙ"]={ "y", "̊" }, + ["ẛ"]={ "ſ", "̇" }, + ["Ạ"]={ "A", "̣" }, + ["ạ"]={ "a", "̣" }, + ["Ả"]={ "A", "̉" }, + ["ả"]={ "a", "̉" }, + ["Ấ"]={ "Â", "́" }, + ["ấ"]={ "â", "́" }, + ["Ầ"]={ "Â", "̀" }, + ["ầ"]={ "â", "̀" }, + ["Ẩ"]={ "Â", "̉" }, + ["ẩ"]={ "â", "̉" }, + ["Ẫ"]={ "Â", "̃" }, + ["ẫ"]={ "â", "̃" }, + ["Ậ"]={ "Ạ", "̂" }, + ["ậ"]={ "ạ", "̂" }, + ["Ắ"]={ "Ă", "́" }, + ["ắ"]={ "ă", "́" }, + ["Ằ"]={ "Ă", "̀" }, + ["ằ"]={ "ă", "̀" }, + ["Ẳ"]={ "Ă", "̉" }, + ["ẳ"]={ "ă", "̉" }, + ["Ẵ"]={ "Ă", "̃" }, + ["ẵ"]={ "ă", "̃" }, + ["Ặ"]={ "Ạ", "̆" }, + ["ặ"]={ "ạ", "̆" }, + ["Ẹ"]={ "E", "̣" }, + ["ẹ"]={ "e", "̣" }, + ["Ẻ"]={ "E", "̉" }, + ["ẻ"]={ "e", "̉" }, + ["Ẽ"]={ "E", "̃" }, + ["ẽ"]={ "e", "̃" }, + ["Ế"]={ "Ê", "́" }, + ["ế"]={ "ê", "́" }, + ["Ề"]={ "Ê", "̀" }, + ["ề"]={ "ê", "̀" }, + ["Ể"]={ "Ê", "̉" }, + ["ể"]={ "ê", "̉" }, + ["Ễ"]={ "Ê", "̃" }, + ["ễ"]={ "ê", "̃" }, + ["Ệ"]={ "Ẹ", "̂" }, + ["ệ"]={ "ẹ", "̂" }, + ["Ỉ"]={ "I", "̉" }, + ["ỉ"]={ "i", "̉" }, + ["Ị"]={ "I", "̣" }, + ["ị"]={ "i", "̣" }, + ["Ọ"]={ "O", "̣" }, + ["ọ"]={ "o", "̣" }, + ["Ỏ"]={ "O", "̉" }, + ["ỏ"]={ "o", "̉" }, + ["Ố"]={ "Ô", "́" }, + ["ố"]={ "ô", "́" }, + ["Ồ"]={ "Ô", "̀" }, + ["ồ"]={ "ô", "̀" }, + ["Ổ"]={ "Ô", "̉" }, + ["ổ"]={ "ô", "̉" }, + ["Ỗ"]={ "Ô", "̃" }, + ["ỗ"]={ "ô", "̃" }, + ["Ộ"]={ "Ọ", "̂" }, + ["ộ"]={ "ọ", "̂" }, + ["Ớ"]={ "Ơ", "́" }, + ["ớ"]={ "ơ", "́" }, + ["Ờ"]={ "Ơ", "̀" }, + ["ờ"]={ "ơ", "̀" }, + ["Ở"]={ "Ơ", "̉" }, + ["ở"]={ "ơ", "̉" }, + ["Ỡ"]={ "Ơ", "̃" }, + ["ỡ"]={ "ơ", "̃" }, + ["Ợ"]={ "Ơ", "̣" }, + ["ợ"]={ "ơ", "̣" }, + ["Ụ"]={ "U", "̣" }, + ["ụ"]={ "u", "̣" }, + ["Ủ"]={ "U", "̉" }, + ["ủ"]={ "u", "̉" }, + ["Ứ"]={ "Ư", "́" }, + ["ứ"]={ "ư", "́" }, + ["Ừ"]={ "Ư", "̀" }, + ["ừ"]={ "ư", "̀" }, + ["Ử"]={ "Ư", "̉" }, + ["ử"]={ "ư", "̉" }, + ["Ữ"]={ "Ư", "̃" }, + ["ữ"]={ "ư", "̃" }, + ["Ự"]={ "Ư", "̣" }, + ["ự"]={ "ư", "̣" }, + ["Ỳ"]={ "Y", "̀" }, + ["ỳ"]={ "y", "̀" }, + ["Ỵ"]={ "Y", "̣" }, + ["ỵ"]={ "y", "̣" }, + ["Ỷ"]={ "Y", "̉" }, + ["ỷ"]={ "y", "̉" }, + ["Ỹ"]={ "Y", "̃" }, + ["ỹ"]={ "y", "̃" }, + ["ἀ"]={ "α", "̓" }, + ["ἁ"]={ "α", "̔" }, + ["ἂ"]={ "ἀ", "̀" }, + ["ἃ"]={ "ἁ", "̀" }, + ["ἄ"]={ "ἀ", "́" }, + ["ἅ"]={ "ἁ", "́" }, + ["ἆ"]={ "ἀ", "͂" }, + ["ἇ"]={ "ἁ", "͂" }, + ["Ἀ"]={ "Α", "̓" }, + ["Ἁ"]={ "Α", "̔" }, + ["Ἂ"]={ "Ἀ", "̀" }, + ["Ἃ"]={ "Ἁ", "̀" }, + ["Ἄ"]={ "Ἀ", "́" }, + ["Ἅ"]={ "Ἁ", "́" }, + ["Ἆ"]={ "Ἀ", "͂" }, + ["Ἇ"]={ "Ἁ", "͂" }, + ["ἐ"]={ "ε", "̓" }, + ["ἑ"]={ "ε", "̔" }, + ["ἒ"]={ "ἐ", "̀" }, + ["ἓ"]={ "ἑ", "̀" }, + ["ἔ"]={ "ἐ", "́" }, + ["ἕ"]={ "ἑ", "́" }, + ["Ἐ"]={ "Ε", "̓" }, + ["Ἑ"]={ "Ε", "̔" }, + ["Ἒ"]={ "Ἐ", "̀" }, + ["Ἓ"]={ "Ἑ", "̀" }, + ["Ἔ"]={ "Ἐ", "́" }, + ["Ἕ"]={ "Ἑ", "́" }, + ["ἠ"]={ "η", "̓" }, + ["ἡ"]={ "η", "̔" }, + ["ἢ"]={ "ἠ", "̀" }, + ["ἣ"]={ "ἡ", "̀" }, + ["ἤ"]={ "ἠ", "́" }, + ["ἥ"]={ "ἡ", "́" }, + ["ἦ"]={ "ἠ", "͂" }, + ["ἧ"]={ "ἡ", "͂" }, + ["Ἠ"]={ "Η", "̓" }, + ["Ἡ"]={ "Η", "̔" }, + ["Ἢ"]={ "Ἠ", "̀" }, + ["Ἣ"]={ "Ἡ", "̀" }, + ["Ἤ"]={ "Ἠ", "́" }, + ["Ἥ"]={ "Ἡ", "́" }, + ["Ἦ"]={ "Ἠ", "͂" }, + ["Ἧ"]={ "Ἡ", "͂" }, + ["ἰ"]={ "ι", "̓" }, + ["ἱ"]={ "ι", "̔" }, + ["ἲ"]={ "ἰ", "̀" }, + ["ἳ"]={ "ἱ", "̀" }, + ["ἴ"]={ "ἰ", "́" }, + ["ἵ"]={ "ἱ", "́" }, + ["ἶ"]={ "ἰ", "͂" }, + ["ἷ"]={ "ἱ", "͂" }, + ["Ἰ"]={ "Ι", "̓" }, + ["Ἱ"]={ "Ι", "̔" }, + ["Ἲ"]={ "Ἰ", "̀" }, + ["Ἳ"]={ "Ἱ", "̀" }, + ["Ἴ"]={ "Ἰ", "́" }, + ["Ἵ"]={ "Ἱ", "́" }, + ["Ἶ"]={ "Ἰ", "͂" }, + ["Ἷ"]={ "Ἱ", "͂" }, + ["ὀ"]={ "ο", "̓" }, + ["ὁ"]={ "ο", "̔" }, + ["ὂ"]={ "ὀ", "̀" }, + ["ὃ"]={ "ὁ", "̀" }, + ["ὄ"]={ "ὀ", "́" }, + ["ὅ"]={ "ὁ", "́" }, + ["Ὀ"]={ "Ο", "̓" }, + ["Ὁ"]={ "Ο", "̔" }, + ["Ὂ"]={ "Ὀ", "̀" }, + ["Ὃ"]={ "Ὁ", "̀" }, + ["Ὄ"]={ "Ὀ", "́" }, + ["Ὅ"]={ "Ὁ", "́" }, + ["ὐ"]={ "υ", "̓" }, + ["ὑ"]={ "υ", "̔" }, + ["ὒ"]={ "ὐ", "̀" }, + ["ὓ"]={ "ὑ", "̀" }, + ["ὔ"]={ "ὐ", "́" }, + ["ὕ"]={ "ὑ", "́" }, + ["ὖ"]={ "ὐ", "͂" }, + ["ὗ"]={ "ὑ", "͂" }, + ["Ὑ"]={ "Υ", "̔" }, + ["Ὓ"]={ "Ὑ", "̀" }, + ["Ὕ"]={ "Ὑ", "́" }, + ["Ὗ"]={ "Ὑ", "͂" }, + ["ὠ"]={ "ω", "̓" }, + ["ὡ"]={ "ω", "̔" }, + ["ὢ"]={ "ὠ", "̀" }, + ["ὣ"]={ "ὡ", "̀" }, + ["ὤ"]={ "ὠ", "́" }, + ["ὥ"]={ "ὡ", "́" }, + ["ὦ"]={ "ὠ", "͂" }, + ["ὧ"]={ "ὡ", "͂" }, + ["Ὠ"]={ "Ω", "̓" }, + ["Ὡ"]={ "Ω", "̔" }, + ["Ὢ"]={ "Ὠ", "̀" }, + ["Ὣ"]={ "Ὡ", "̀" }, + ["Ὤ"]={ "Ὠ", "́" }, + ["Ὥ"]={ "Ὡ", "́" }, + ["Ὦ"]={ "Ὠ", "͂" }, + ["Ὧ"]={ "Ὡ", "͂" }, + ["ὰ"]={ "α", "̀" }, + ["ὲ"]={ "ε", "̀" }, + ["ὴ"]={ "η", "̀" }, + ["ὶ"]={ "ι", "̀" }, + ["ὸ"]={ "ο", "̀" }, + ["ὺ"]={ "υ", "̀" }, + ["ὼ"]={ "ω", "̀" }, + ["ᾀ"]={ "ἀ", "ͅ" }, + ["ᾁ"]={ "ἁ", "ͅ" }, + ["ᾂ"]={ "ἂ", "ͅ" }, + ["ᾃ"]={ "ἃ", "ͅ" }, + ["ᾄ"]={ "ἄ", "ͅ" }, + ["ᾅ"]={ "ἅ", "ͅ" }, + ["ᾆ"]={ "ἆ", "ͅ" }, + ["ᾇ"]={ "ἇ", "ͅ" }, + ["ᾈ"]={ "Ἀ", "ͅ" }, + ["ᾉ"]={ "Ἁ", "ͅ" }, + ["ᾊ"]={ "Ἂ", "ͅ" }, + ["ᾋ"]={ "Ἃ", "ͅ" }, + ["ᾌ"]={ "Ἄ", "ͅ" }, + ["ᾍ"]={ "Ἅ", "ͅ" }, + ["ᾎ"]={ "Ἆ", "ͅ" }, + ["ᾏ"]={ "Ἇ", "ͅ" }, + ["ᾐ"]={ "ἠ", "ͅ" }, + ["ᾑ"]={ "ἡ", "ͅ" }, + ["ᾒ"]={ "ἢ", "ͅ" }, + ["ᾓ"]={ "ἣ", "ͅ" }, + ["ᾔ"]={ "ἤ", "ͅ" }, + ["ᾕ"]={ "ἥ", "ͅ" }, + ["ᾖ"]={ "ἦ", "ͅ" }, + ["ᾗ"]={ "ἧ", "ͅ" }, + ["ᾘ"]={ "Ἠ", "ͅ" }, + ["ᾙ"]={ "Ἡ", "ͅ" }, + ["ᾚ"]={ "Ἢ", "ͅ" }, + ["ᾛ"]={ "Ἣ", "ͅ" }, + ["ᾜ"]={ "Ἤ", "ͅ" }, + ["ᾝ"]={ "Ἥ", "ͅ" }, + ["ᾞ"]={ "Ἦ", "ͅ" }, + ["ᾟ"]={ "Ἧ", "ͅ" }, + ["ᾠ"]={ "ὠ", "ͅ" }, + ["ᾡ"]={ "ὡ", "ͅ" }, + ["ᾢ"]={ "ὢ", "ͅ" }, + ["ᾣ"]={ "ὣ", "ͅ" }, + ["ᾤ"]={ "ὤ", "ͅ" }, + ["ᾥ"]={ "ὥ", "ͅ" }, + ["ᾦ"]={ "ὦ", "ͅ" }, + ["ᾧ"]={ "ὧ", "ͅ" }, + ["ᾨ"]={ "Ὠ", "ͅ" }, + ["ᾩ"]={ "Ὡ", "ͅ" }, + ["ᾪ"]={ "Ὢ", "ͅ" }, + ["ᾫ"]={ "Ὣ", "ͅ" }, + ["ᾬ"]={ "Ὤ", "ͅ" }, + ["ᾭ"]={ "Ὥ", "ͅ" }, + ["ᾮ"]={ "Ὦ", "ͅ" }, + ["ᾯ"]={ "Ὧ", "ͅ" }, + ["ᾰ"]={ "α", "̆" }, + ["ᾱ"]={ "α", "̄" }, + ["ᾲ"]={ "ὰ", "ͅ" }, + ["ᾳ"]={ "α", "ͅ" }, + ["ᾴ"]={ "ά", "ͅ" }, + ["ᾶ"]={ "α", "͂" }, + ["ᾷ"]={ "ᾶ", "ͅ" }, + ["Ᾰ"]={ "Α", "̆" }, + ["Ᾱ"]={ "Α", "̄" }, + ["Ὰ"]={ "Α", "̀" }, + ["ᾼ"]={ "Α", "ͅ" }, + ["῁"]={ "¨", "͂" }, + ["ῂ"]={ "ὴ", "ͅ" }, + ["ῃ"]={ "η", "ͅ" }, + ["ῄ"]={ "ή", "ͅ" }, + ["ῆ"]={ "η", "͂" }, + ["ῇ"]={ "ῆ", "ͅ" }, + ["Ὲ"]={ "Ε", "̀" }, + ["Ὴ"]={ "Η", "̀" }, + ["ῌ"]={ "Η", "ͅ" }, + ["῍"]={ "᾿", "̀" }, + ["῎"]={ "᾿", "́" }, + ["῏"]={ "᾿", "͂" }, + ["ῐ"]={ "ι", "̆" }, + ["ῑ"]={ "ι", "̄" }, + ["ῒ"]={ "ϊ", "̀" }, + ["ῖ"]={ "ι", "͂" }, + ["ῗ"]={ "ϊ", "͂" }, + ["Ῐ"]={ "Ι", "̆" }, + ["Ῑ"]={ "Ι", "̄" }, + ["Ὶ"]={ "Ι", "̀" }, + ["῝"]={ "῾", "̀" }, + ["῞"]={ "῾", "́" }, + ["῟"]={ "῾", "͂" }, + ["ῠ"]={ "υ", "̆" }, + ["ῡ"]={ "υ", "̄" }, + ["ῢ"]={ "ϋ", "̀" }, + ["ῤ"]={ "ρ", "̓" }, + ["ῥ"]={ "ρ", "̔" }, + ["ῦ"]={ "υ", "͂" }, + ["ῧ"]={ "ϋ", "͂" }, + ["Ῠ"]={ "Υ", "̆" }, + ["Ῡ"]={ "Υ", "̄" }, + ["Ὺ"]={ "Υ", "̀" }, + ["Ῥ"]={ "Ρ", "̔" }, + ["῭"]={ "¨", "̀" }, + ["ῲ"]={ "ὼ", "ͅ" }, + ["ῳ"]={ "ω", "ͅ" }, + ["ῴ"]={ "ώ", "ͅ" }, + ["ῶ"]={ "ω", "͂" }, + ["ῷ"]={ "ῶ", "ͅ" }, + ["Ὸ"]={ "Ο", "̀" }, + ["Ὼ"]={ "Ω", "̀" }, + ["ῼ"]={ "Ω", "ͅ" }, + ["↚"]={ "←", "̸" }, + ["↛"]={ "→", "̸" }, + ["↮"]={ "↔", "̸" }, + ["⇍"]={ "⇐", "̸" }, + ["⇎"]={ "⇔", "̸" }, + ["⇏"]={ "⇒", "̸" }, + ["∄"]={ "∃", "̸" }, + ["∉"]={ "∈", "̸" }, + ["∌"]={ "∋", "̸" }, + ["∤"]={ "∣", "̸" }, + ["∦"]={ "∥", "̸" }, + ["≁"]={ "∼", "̸" }, + ["≄"]={ "≃", "̸" }, + ["≇"]={ "≅", "̸" }, + ["≉"]={ "≈", "̸" }, + ["≠"]={ "=", "̸" }, + ["≢"]={ "≡", "̸" }, + ["≭"]={ "≍", "̸" }, + ["≮"]={ "<", "̸" }, + ["≯"]={ ">", "̸" }, + ["≰"]={ "≤", "̸" }, + ["≱"]={ "≥", "̸" }, + ["≴"]={ "≲", "̸" }, + ["≵"]={ "≳", "̸" }, + ["≸"]={ "≶", "̸" }, + ["≹"]={ "≷", "̸" }, + ["⊀"]={ "≺", "̸" }, + ["⊁"]={ "≻", "̸" }, + ["⊄"]={ "⊂", "̸" }, + ["⊅"]={ "⊃", "̸" }, + ["⊈"]={ "⊆", "̸" }, + ["⊉"]={ "⊇", "̸" }, + ["⊬"]={ "⊢", "̸" }, + ["⊭"]={ "⊨", "̸" }, + ["⊮"]={ "⊩", "̸" }, + ["⊯"]={ "⊫", "̸" }, + ["⋠"]={ "≼", "̸" }, + ["⋡"]={ "≽", "̸" }, + ["⋢"]={ "⊑", "̸" }, + ["⋣"]={ "⊒", "̸" }, + ["⋪"]={ "⊲", "̸" }, + ["⋫"]={ "⊳", "̸" }, + ["⋬"]={ "⊴", "̸" }, + ["⋭"]={ "⊵", "̸" }, + ["⫝̸"]={ "⫝", "̸" }, + ["が"]={ "か", "゙" }, + ["ぎ"]={ "き", "゙" }, + ["ぐ"]={ "く", "゙" }, + ["げ"]={ "け", "゙" }, + ["ご"]={ "こ", "゙" }, + ["ざ"]={ "さ", "゙" }, + ["じ"]={ "し", "゙" }, + ["ず"]={ "す", "゙" }, + ["ぜ"]={ "せ", "゙" }, + ["ぞ"]={ "そ", "゙" }, + ["だ"]={ "た", "゙" }, + ["ぢ"]={ "ち", "゙" }, + ["づ"]={ "つ", "゙" }, + ["で"]={ "て", "゙" }, + ["ど"]={ "と", "゙" }, + ["ば"]={ "は", "゙" }, + ["ぱ"]={ "は", "゚" }, + ["び"]={ "ひ", "゙" }, + ["ぴ"]={ "ひ", "゚" }, + ["ぶ"]={ "ふ", "゙" }, + ["ぷ"]={ "ふ", "゚" }, + ["べ"]={ "へ", "゙" }, + ["ぺ"]={ "へ", "゚" }, + ["ぼ"]={ "ほ", "゙" }, + ["ぽ"]={ "ほ", "゚" }, + ["ゔ"]={ "う", "゙" }, + ["ゞ"]={ "ゝ", "゙" }, + ["ガ"]={ "カ", "゙" }, + ["ギ"]={ "キ", "゙" }, + ["グ"]={ "ク", "゙" }, + ["ゲ"]={ "ケ", "゙" }, + ["ゴ"]={ "コ", "゙" }, + ["ザ"]={ "サ", "゙" }, + ["ジ"]={ "シ", "゙" }, + ["ズ"]={ "ス", "゙" }, + ["ゼ"]={ "セ", "゙" }, + ["ゾ"]={ "ソ", "゙" }, + ["ダ"]={ "タ", "゙" }, + ["ヂ"]={ "チ", "゙" }, + ["ヅ"]={ "ツ", "゙" }, + ["デ"]={ "テ", "゙" }, + ["ド"]={ "ト", "゙" }, + ["バ"]={ "ハ", "゙" }, + ["パ"]={ "ハ", "゚" }, + ["ビ"]={ "ヒ", "゙" }, + ["ピ"]={ "ヒ", "゚" }, + ["ブ"]={ "フ", "゙" }, + ["プ"]={ "フ", "゚" }, + ["ベ"]={ "ヘ", "゙" }, + ["ペ"]={ "ヘ", "゚" }, + ["ボ"]={ "ホ", "゙" }, + ["ポ"]={ "ホ", "゚" }, + ["ヴ"]={ "ウ", "゙" }, + ["ヷ"]={ "ワ", "゙" }, + ["ヸ"]={ "ヰ", "゙" }, + ["ヹ"]={ "ヱ", "゙" }, + ["ヺ"]={ "ヲ", "゙" }, + ["ヾ"]={ "ヽ", "゙" }, + ["יִ"]={ "י", "ִ" }, + ["ײַ"]={ "ײ", "ַ" }, + ["שׁ"]={ "ש", "ׁ" }, + ["שׂ"]={ "ש", "ׂ" }, + ["שּׁ"]={ "שּ", "ׁ" }, + ["שּׂ"]={ "שּ", "ׂ" }, + ["אַ"]={ "א", "ַ" }, + ["אָ"]={ "א", "ָ" }, + ["אּ"]={ "א", "ּ" }, + ["בּ"]={ "ב", "ּ" }, + ["גּ"]={ "ג", "ּ" }, + ["דּ"]={ "ד", "ּ" }, + ["הּ"]={ "ה", "ּ" }, + ["וּ"]={ "ו", "ּ" }, + ["זּ"]={ "ז", "ּ" }, + ["טּ"]={ "ט", "ּ" }, + ["יּ"]={ "י", "ּ" }, + ["ךּ"]={ "ך", "ּ" }, + ["כּ"]={ "כ", "ּ" }, + ["לּ"]={ "ל", "ּ" }, + ["מּ"]={ "מ", "ּ" }, + ["נּ"]={ "נ", "ּ" }, + ["סּ"]={ "ס", "ּ" }, + ["ףּ"]={ "ף", "ּ" }, + ["פּ"]={ "פ", "ּ" }, + ["צּ"]={ "צ", "ּ" }, + ["קּ"]={ "ק", "ּ" }, + ["רּ"]={ "ר", "ּ" }, + ["שּ"]={ "ש", "ּ" }, + ["תּ"]={ "ת", "ּ" }, + ["וֹ"]={ "ו", "ֹ" }, + ["בֿ"]={ "ב", "ֿ" }, + ["כֿ"]={ "כ", "ֿ" }, + ["פֿ"]={ "פ", "ֿ" }, + ["𑂚"]={ "𑂙", "𑂺" }, + ["𑂜"]={ "𑂛", "𑂺" }, + ["𑂫"]={ "𑂥", "𑂺" }, + ["𑄮"]={ "𑄱", "𑄧" }, + ["𑄯"]={ "𑄲", "𑄧" }, + ["𑍋"]={ "𑍇", "𑌾" }, + ["𑍌"]={ "𑍇", "𑍗" }, + ["𑒻"]={ "𑒹", "𑒺" }, + ["𑒼"]={ "𑒹", "𑒰" }, + ["𑒾"]={ "𑒹", "𑒽" }, + ["𑖺"]={ "𑖸", "𑖯" }, + ["𑖻"]={ "𑖹", "𑖯" }, + ["𝅗𝅥"]={ "𝅗", "𝅥" }, + ["𝅘𝅥"]={ "𝅘", "𝅥" }, + ["𝅘𝅥𝅮"]={ "𝅘𝅥", "𝅮" }, + ["𝅘𝅥𝅯"]={ "𝅘𝅥", "𝅯" }, + ["𝅘𝅥𝅰"]={ "𝅘𝅥", "𝅰" }, + ["𝅘𝅥𝅱"]={ "𝅘𝅥", "𝅱" }, + ["𝅘𝅥𝅲"]={ "𝅘𝅥", "𝅲" }, + ["𝆹𝅥"]={ "𝆹", "𝅥" }, + ["𝆺𝅥"]={ "𝆺", "𝅥" }, + ["𝆹𝅥𝅮"]={ "𝆹𝅥", "𝅮" }, + ["𝆺𝅥𝅮"]={ "𝆺𝅥", "𝅮" }, + ["𝆹𝅥𝅯"]={ "𝆹𝅥", "𝅯" }, + ["𝆺𝅥𝅯"]={ "𝆺𝅥", "𝅯" }, + }, + }, + { + ["data"]={ + ["À"]={ "A", "̀" }, + ["Á"]={ "A", "́" }, + ["Â"]={ "A", "̂" }, + ["Ã"]={ "A", "̃" }, + ["Ä"]={ "A", "̈" }, + ["Å"]={ "A", "̊" }, + ["Ç"]={ "C", "̧" }, + ["È"]={ "E", "̀" }, + ["É"]={ "E", "́" }, + ["Ê"]={ "E", "̂" }, + ["Ë"]={ "E", "̈" }, + ["Ì"]={ "I", "̀" }, + ["Í"]={ "I", "́" }, + ["Î"]={ "I", "̂" }, + ["Ï"]={ "I", "̈" }, + ["Ñ"]={ "N", "̃" }, + ["Ò"]={ "O", "̀" }, + ["Ó"]={ "O", "́" }, + ["Ô"]={ "O", "̂" }, + ["Õ"]={ "O", "̃" }, + ["Ö"]={ "O", "̈" }, + ["Ù"]={ "U", "̀" }, + ["Ú"]={ "U", "́" }, + ["Û"]={ "U", "̂" }, + ["Ü"]={ "U", "̈" }, + ["Ý"]={ "Y", "́" }, + ["à"]={ "a", "̀" }, + ["á"]={ "a", "́" }, + ["â"]={ "a", "̂" }, + ["ã"]={ "a", "̃" }, + ["ä"]={ "a", "̈" }, + ["å"]={ "a", "̊" }, + ["ç"]={ "c", "̧" }, + ["è"]={ "e", "̀" }, + ["é"]={ "e", "́" }, + ["ê"]={ "e", "̂" }, + ["ë"]={ "e", "̈" }, + ["ì"]={ "i", "̀" }, + ["í"]={ "i", "́" }, + ["î"]={ "i", "̂" }, + ["ï"]={ "i", "̈" }, + ["ñ"]={ "n", "̃" }, + ["ò"]={ "o", "̀" }, + ["ó"]={ "o", "́" }, + ["ô"]={ "o", "̂" }, + ["õ"]={ "o", "̃" }, + ["ö"]={ "o", "̈" }, + ["ù"]={ "u", "̀" }, + ["ú"]={ "u", "́" }, + ["û"]={ "u", "̂" }, + ["ü"]={ "u", "̈" }, + ["ý"]={ "y", "́" }, + ["ÿ"]={ "y", "̈" }, + ["Ā"]={ "A", "̄" }, + ["ā"]={ "a", "̄" }, + ["Ă"]={ "A", "̆" }, + ["ă"]={ "a", "̆" }, + ["Ą"]={ "A", "̨" }, + ["ą"]={ "a", "̨" }, + ["Ć"]={ "C", "́" }, + ["ć"]={ "c", "́" }, + ["Ĉ"]={ "C", "̂" }, + ["ĉ"]={ "c", "̂" }, + ["Ċ"]={ "C", "̇" }, + ["ċ"]={ "c", "̇" }, + ["Č"]={ "C", "̌" }, + ["č"]={ "c", "̌" }, + ["Ď"]={ "D", "̌" }, + ["ď"]={ "d", "̌" }, + ["Ē"]={ "E", "̄" }, + ["ē"]={ "e", "̄" }, + ["Ĕ"]={ "E", "̆" }, + ["ĕ"]={ "e", "̆" }, + ["Ė"]={ "E", "̇" }, + ["ė"]={ "e", "̇" }, + ["Ę"]={ "E", "̨" }, + ["ę"]={ "e", "̨" }, + ["Ě"]={ "E", "̌" }, + ["ě"]={ "e", "̌" }, + ["Ĝ"]={ "G", "̂" }, + ["ĝ"]={ "g", "̂" }, + ["Ğ"]={ "G", "̆" }, + ["ğ"]={ "g", "̆" }, + ["Ġ"]={ "G", "̇" }, + ["ġ"]={ "g", "̇" }, + ["Ģ"]={ "G", "̧" }, + ["ģ"]={ "g", "̧" }, + ["Ĥ"]={ "H", "̂" }, + ["ĥ"]={ "h", "̂" }, + ["Ĩ"]={ "I", "̃" }, + ["ĩ"]={ "i", "̃" }, + ["Ī"]={ "I", "̄" }, + ["ī"]={ "i", "̄" }, + ["Ĭ"]={ "I", "̆" }, + ["ĭ"]={ "i", "̆" }, + ["Į"]={ "I", "̨" }, + ["į"]={ "i", "̨" }, + ["İ"]={ "I", "̇" }, + ["Ĵ"]={ "J", "̂" }, + ["ĵ"]={ "j", "̂" }, + ["Ķ"]={ "K", "̧" }, + ["ķ"]={ "k", "̧" }, + ["Ĺ"]={ "L", "́" }, + ["ĺ"]={ "l", "́" }, + ["Ļ"]={ "L", "̧" }, + ["ļ"]={ "l", "̧" }, + ["Ľ"]={ "L", "̌" }, + ["ľ"]={ "l", "̌" }, + ["Ń"]={ "N", "́" }, + ["ń"]={ "n", "́" }, + ["Ņ"]={ "N", "̧" }, + ["ņ"]={ "n", "̧" }, + ["Ň"]={ "N", "̌" }, + ["ň"]={ "n", "̌" }, + ["Ō"]={ "O", "̄" }, + ["ō"]={ "o", "̄" }, + ["Ŏ"]={ "O", "̆" }, + ["ŏ"]={ "o", "̆" }, + ["Ő"]={ "O", "̋" }, + ["ő"]={ "o", "̋" }, + ["Ŕ"]={ "R", "́" }, + ["ŕ"]={ "r", "́" }, + ["Ŗ"]={ "R", "̧" }, + ["ŗ"]={ "r", "̧" }, + ["Ř"]={ "R", "̌" }, + ["ř"]={ "r", "̌" }, + ["Ś"]={ "S", "́" }, + ["ś"]={ "s", "́" }, + ["Ŝ"]={ "S", "̂" }, + ["ŝ"]={ "s", "̂" }, + ["Ş"]={ "S", "̧" }, + ["ş"]={ "s", "̧" }, + ["Š"]={ "S", "̌" }, + ["š"]={ "s", "̌" }, + ["Ţ"]={ "T", "̧" }, + ["ţ"]={ "t", "̧" }, + ["Ť"]={ "T", "̌" }, + ["ť"]={ "t", "̌" }, + ["Ũ"]={ "U", "̃" }, + ["ũ"]={ "u", "̃" }, + ["Ū"]={ "U", "̄" }, + ["ū"]={ "u", "̄" }, + ["Ŭ"]={ "U", "̆" }, + ["ŭ"]={ "u", "̆" }, + ["Ů"]={ "U", "̊" }, + ["ů"]={ "u", "̊" }, + ["Ű"]={ "U", "̋" }, + ["ű"]={ "u", "̋" }, + ["Ų"]={ "U", "̨" }, + ["ų"]={ "u", "̨" }, + ["Ŵ"]={ "W", "̂" }, + ["ŵ"]={ "w", "̂" }, + ["Ŷ"]={ "Y", "̂" }, + ["ŷ"]={ "y", "̂" }, + ["Ÿ"]={ "Y", "̈" }, + ["Ź"]={ "Z", "́" }, + ["ź"]={ "z", "́" }, + ["Ż"]={ "Z", "̇" }, + ["ż"]={ "z", "̇" }, + ["Ž"]={ "Z", "̌" }, + ["ž"]={ "z", "̌" }, + ["Ơ"]={ "O", "̛" }, + ["ơ"]={ "o", "̛" }, + ["Ư"]={ "U", "̛" }, + ["ư"]={ "u", "̛" }, + ["Ǎ"]={ "A", "̌" }, + ["ǎ"]={ "a", "̌" }, + ["Ǐ"]={ "I", "̌" }, + ["ǐ"]={ "i", "̌" }, + ["Ǒ"]={ "O", "̌" }, + ["ǒ"]={ "o", "̌" }, + ["Ǔ"]={ "U", "̌" }, + ["ǔ"]={ "u", "̌" }, + ["Ǖ"]={ "Ü", "̄" }, + ["ǖ"]={ "ü", "̄" }, + ["Ǘ"]={ "Ü", "́" }, + ["ǘ"]={ "ü", "́" }, + ["Ǚ"]={ "Ü", "̌" }, + ["ǚ"]={ "ü", "̌" }, + ["Ǜ"]={ "Ü", "̀" }, + ["ǜ"]={ "ü", "̀" }, + ["Ǟ"]={ "Ä", "̄" }, + ["ǟ"]={ "ä", "̄" }, + ["Ǡ"]={ "Ȧ", "̄" }, + ["ǡ"]={ "ȧ", "̄" }, + ["Ǣ"]={ "Æ", "̄" }, + ["ǣ"]={ "æ", "̄" }, + ["Ǧ"]={ "G", "̌" }, + ["ǧ"]={ "g", "̌" }, + ["Ǩ"]={ "K", "̌" }, + ["ǩ"]={ "k", "̌" }, + ["Ǫ"]={ "O", "̨" }, + ["ǫ"]={ "o", "̨" }, + ["Ǭ"]={ "Ǫ", "̄" }, + ["ǭ"]={ "ǫ", "̄" }, + ["Ǯ"]={ "Ʒ", "̌" }, + ["ǯ"]={ "ʒ", "̌" }, + ["ǰ"]={ "j", "̌" }, + ["Ǵ"]={ "G", "́" }, + ["ǵ"]={ "g", "́" }, + ["Ǹ"]={ "N", "̀" }, + ["ǹ"]={ "n", "̀" }, + ["Ǻ"]={ "Å", "́" }, + ["ǻ"]={ "å", "́" }, + ["Ǽ"]={ "Æ", "́" }, + ["ǽ"]={ "æ", "́" }, + ["Ǿ"]={ "Ø", "́" }, + ["ǿ"]={ "ø", "́" }, + ["Ȁ"]={ "A", "̏" }, + ["ȁ"]={ "a", "̏" }, + ["Ȃ"]={ "A", "̑" }, + ["ȃ"]={ "a", "̑" }, + ["Ȅ"]={ "E", "̏" }, + ["ȅ"]={ "e", "̏" }, + ["Ȇ"]={ "E", "̑" }, + ["ȇ"]={ "e", "̑" }, + ["Ȉ"]={ "I", "̏" }, + ["ȉ"]={ "i", "̏" }, + ["Ȋ"]={ "I", "̑" }, + ["ȋ"]={ "i", "̑" }, + ["Ȍ"]={ "O", "̏" }, + ["ȍ"]={ "o", "̏" }, + ["Ȏ"]={ "O", "̑" }, + ["ȏ"]={ "o", "̑" }, + ["Ȑ"]={ "R", "̏" }, + ["ȑ"]={ "r", "̏" }, + ["Ȓ"]={ "R", "̑" }, + ["ȓ"]={ "r", "̑" }, + ["Ȕ"]={ "U", "̏" }, + ["ȕ"]={ "u", "̏" }, + ["Ȗ"]={ "U", "̑" }, + ["ȗ"]={ "u", "̑" }, + ["Ș"]={ "S", "̦" }, + ["ș"]={ "s", "̦" }, + ["Ț"]={ "T", "̦" }, + ["ț"]={ "t", "̦" }, + ["Ȟ"]={ "H", "̌" }, + ["ȟ"]={ "h", "̌" }, + ["Ȧ"]={ "A", "̇" }, + ["ȧ"]={ "a", "̇" }, + ["Ȩ"]={ "E", "̧" }, + ["ȩ"]={ "e", "̧" }, + ["Ȫ"]={ "Ö", "̄" }, + ["ȫ"]={ "ö", "̄" }, + ["Ȭ"]={ "Õ", "̄" }, + ["ȭ"]={ "õ", "̄" }, + ["Ȯ"]={ "O", "̇" }, + ["ȯ"]={ "o", "̇" }, + ["Ȱ"]={ "Ȯ", "̄" }, + ["ȱ"]={ "ȯ", "̄" }, + ["Ȳ"]={ "Y", "̄" }, + ["ȳ"]={ "y", "̄" }, + ["̈́"]={ "̈", "́" }, + ["΅"]={ "¨", "́" }, + ["Ά"]={ "Α", "́" }, + ["Έ"]={ "Ε", "́" }, + ["Ή"]={ "Η", "́" }, + ["Ί"]={ "Ι", "́" }, + ["Ό"]={ "Ο", "́" }, + ["Ύ"]={ "Υ", "́" }, + ["Ώ"]={ "Ω", "́" }, + ["ΐ"]={ "ϊ", "́" }, + ["Ϊ"]={ "Ι", "̈" }, + ["Ϋ"]={ "Υ", "̈" }, + ["ά"]={ "α", "́" }, + ["έ"]={ "ε", "́" }, + ["ή"]={ "η", "́" }, + ["ί"]={ "ι", "́" }, + ["ΰ"]={ "ϋ", "́" }, + ["ϊ"]={ "ι", "̈" }, + ["ϋ"]={ "υ", "̈" }, + ["ό"]={ "ο", "́" }, + ["ύ"]={ "υ", "́" }, + ["ώ"]={ "ω", "́" }, + ["ϓ"]={ "ϒ", "́" }, + ["ϔ"]={ "ϒ", "̈" }, + ["Ѐ"]={ "Е", "̀" }, + ["Ё"]={ "Е", "̈" }, + ["Ѓ"]={ "Г", "́" }, + ["Ї"]={ "І", "̈" }, + ["Ќ"]={ "К", "́" }, + ["Ѝ"]={ "И", "̀" }, + ["Ў"]={ "У", "̆" }, + ["Й"]={ "И", "̆" }, + ["й"]={ "и", "̆" }, + ["ѐ"]={ "е", "̀" }, + ["ё"]={ "е", "̈" }, + ["ѓ"]={ "г", "́" }, + ["ї"]={ "і", "̈" }, + ["ќ"]={ "к", "́" }, + ["ѝ"]={ "и", "̀" }, + ["ў"]={ "у", "̆" }, + ["Ѷ"]={ "Ѵ", "̏" }, + ["ѷ"]={ "ѵ", "̏" }, + ["Ӂ"]={ "Ж", "̆" }, + ["ӂ"]={ "ж", "̆" }, + ["Ӑ"]={ "А", "̆" }, + ["ӑ"]={ "а", "̆" }, + ["Ӓ"]={ "А", "̈" }, + ["ӓ"]={ "а", "̈" }, + ["Ӗ"]={ "Е", "̆" }, + ["ӗ"]={ "е", "̆" }, + ["Ӛ"]={ "Ә", "̈" }, + ["ӛ"]={ "ә", "̈" }, + ["Ӝ"]={ "Ж", "̈" }, + ["ӝ"]={ "ж", "̈" }, + ["Ӟ"]={ "З", "̈" }, + ["ӟ"]={ "з", "̈" }, + ["Ӣ"]={ "И", "̄" }, + ["ӣ"]={ "и", "̄" }, + ["Ӥ"]={ "И", "̈" }, + ["ӥ"]={ "и", "̈" }, + ["Ӧ"]={ "О", "̈" }, + ["ӧ"]={ "о", "̈" }, + ["Ӫ"]={ "Ө", "̈" }, + ["ӫ"]={ "ө", "̈" }, + ["Ӭ"]={ "Э", "̈" }, + ["ӭ"]={ "э", "̈" }, + ["Ӯ"]={ "У", "̄" }, + ["ӯ"]={ "у", "̄" }, + ["Ӱ"]={ "У", "̈" }, + ["ӱ"]={ "у", "̈" }, + ["Ӳ"]={ "У", "̋" }, + ["ӳ"]={ "у", "̋" }, + ["Ӵ"]={ "Ч", "̈" }, + ["ӵ"]={ "ч", "̈" }, + ["Ӹ"]={ "Ы", "̈" }, + ["ӹ"]={ "ы", "̈" }, + ["آ"]={ "ا", "ٓ" }, + ["أ"]={ "ا", "ٔ" }, + ["ؤ"]={ "و", "ٔ" }, + ["إ"]={ "ا", "ٕ" }, + ["ئ"]={ "ي", "ٔ" }, + ["ۀ"]={ "ە", "ٔ" }, + ["ۂ"]={ "ہ", "ٔ" }, + ["ۓ"]={ "ے", "ٔ" }, + ["ऩ"]={ "न", "़" }, + ["ऱ"]={ "र", "़" }, + ["ऴ"]={ "ळ", "़" }, + ["क़"]={ "क", "़" }, + ["ख़"]={ "ख", "़" }, + ["ग़"]={ "ग", "़" }, + ["ज़"]={ "ज", "़" }, + ["ड़"]={ "ड", "़" }, + ["ढ़"]={ "ढ", "़" }, + ["फ़"]={ "फ", "़" }, + ["य़"]={ "य", "़" }, + ["ো"]={ "ে", "া" }, + ["ৌ"]={ "ে", "ৗ" }, + ["ড়"]={ "ড", "়" }, + ["ঢ়"]={ "ঢ", "়" }, + ["য়"]={ "য", "়" }, + ["ਲ਼"]={ "ਲ", "਼" }, + ["ਸ਼"]={ "ਸ", "਼" }, + ["ਖ਼"]={ "ਖ", "਼" }, + ["ਗ਼"]={ "ਗ", "਼" }, + ["ਜ਼"]={ "ਜ", "਼" }, + ["ਫ਼"]={ "ਫ", "਼" }, + ["ୈ"]={ "େ", "ୖ" }, + ["ୋ"]={ "େ", "ା" }, + ["ୌ"]={ "େ", "ୗ" }, + ["ଡ଼"]={ "ଡ", "଼" }, + ["ଢ଼"]={ "ଢ", "଼" }, + ["ஔ"]={ "ஒ", "ௗ" }, + ["ொ"]={ "ெ", "ா" }, + ["ோ"]={ "ே", "ா" }, + ["ௌ"]={ "ெ", "ௗ" }, + ["ై"]={ "ె", "ౖ" }, + ["ೀ"]={ "ಿ", "ೕ" }, + ["ೇ"]={ "ೆ", "ೕ" }, + ["ೈ"]={ "ೆ", "ೖ" }, + ["ೊ"]={ "ೆ", "ೂ" }, + ["ೋ"]={ "ೊ", "ೕ" }, + ["ൊ"]={ "െ", "ാ" }, + ["ോ"]={ "േ", "ാ" }, + ["ൌ"]={ "െ", "ൗ" }, + ["ේ"]={ "ෙ", "්" }, + ["ො"]={ "ෙ", "ා" }, + ["ෝ"]={ "ො", "්" }, + ["ෞ"]={ "ෙ", "ෟ" }, + ["གྷ"]={ "ག", "ྷ" }, + ["ཌྷ"]={ "ཌ", "ྷ" }, + ["དྷ"]={ "ད", "ྷ" }, + ["བྷ"]={ "བ", "ྷ" }, + ["ཛྷ"]={ "ཛ", "ྷ" }, + ["ཀྵ"]={ "ཀ", "ྵ" }, + ["ཱི"]={ "ཱ", "ི" }, + ["ཱུ"]={ "ཱ", "ུ" }, + ["ྲྀ"]={ "ྲ", "ྀ" }, + ["ླྀ"]={ "ླ", "ྀ" }, + ["ཱྀ"]={ "ཱ", "ྀ" }, + ["ྒྷ"]={ "ྒ", "ྷ" }, + ["ྜྷ"]={ "ྜ", "ྷ" }, + ["ྡྷ"]={ "ྡ", "ྷ" }, + ["ྦྷ"]={ "ྦ", "ྷ" }, + ["ྫྷ"]={ "ྫ", "ྷ" }, + ["ྐྵ"]={ "ྐ", "ྵ" }, + ["ဦ"]={ "ဥ", "ီ" }, + ["ᬆ"]={ "ᬅ", "ᬵ" }, + ["ᬈ"]={ "ᬇ", "ᬵ" }, + ["ᬊ"]={ "ᬉ", "ᬵ" }, + ["ᬌ"]={ "ᬋ", "ᬵ" }, + ["ᬎ"]={ "ᬍ", "ᬵ" }, + ["ᬒ"]={ "ᬑ", "ᬵ" }, + ["ᬻ"]={ "ᬺ", "ᬵ" }, + ["ᬽ"]={ "ᬼ", "ᬵ" }, + ["ᭀ"]={ "ᬾ", "ᬵ" }, + ["ᭁ"]={ "ᬿ", "ᬵ" }, + ["ᭃ"]={ "ᭂ", "ᬵ" }, + ["Ḁ"]={ "A", "̥" }, + ["ḁ"]={ "a", "̥" }, + ["Ḃ"]={ "B", "̇" }, + ["ḃ"]={ "b", "̇" }, + ["Ḅ"]={ "B", "̣" }, + ["ḅ"]={ "b", "̣" }, + ["Ḇ"]={ "B", "̱" }, + ["ḇ"]={ "b", "̱" }, + ["Ḉ"]={ "Ç", "́" }, + ["ḉ"]={ "ç", "́" }, + ["Ḋ"]={ "D", "̇" }, + ["ḋ"]={ "d", "̇" }, + ["Ḍ"]={ "D", "̣" }, + ["ḍ"]={ "d", "̣" }, + ["Ḏ"]={ "D", "̱" }, + ["ḏ"]={ "d", "̱" }, + ["Ḑ"]={ "D", "̧" }, + ["ḑ"]={ "d", "̧" }, + ["Ḓ"]={ "D", "̭" }, + ["ḓ"]={ "d", "̭" }, + ["Ḕ"]={ "Ē", "̀" }, + ["ḕ"]={ "ē", "̀" }, + ["Ḗ"]={ "Ē", "́" }, + ["ḗ"]={ "ē", "́" }, + ["Ḙ"]={ "E", "̭" }, + ["ḙ"]={ "e", "̭" }, + ["Ḛ"]={ "E", "̰" }, + ["ḛ"]={ "e", "̰" }, + ["Ḝ"]={ "Ȩ", "̆" }, + ["ḝ"]={ "ȩ", "̆" }, + ["Ḟ"]={ "F", "̇" }, + ["ḟ"]={ "f", "̇" }, + ["Ḡ"]={ "G", "̄" }, + ["ḡ"]={ "g", "̄" }, + ["Ḣ"]={ "H", "̇" }, + ["ḣ"]={ "h", "̇" }, + ["Ḥ"]={ "H", "̣" }, + ["ḥ"]={ "h", "̣" }, + ["Ḧ"]={ "H", "̈" }, + ["ḧ"]={ "h", "̈" }, + ["Ḩ"]={ "H", "̧" }, + ["ḩ"]={ "h", "̧" }, + ["Ḫ"]={ "H", "̮" }, + ["ḫ"]={ "h", "̮" }, + ["Ḭ"]={ "I", "̰" }, + ["ḭ"]={ "i", "̰" }, + ["Ḯ"]={ "Ï", "́" }, + ["ḯ"]={ "ï", "́" }, + ["Ḱ"]={ "K", "́" }, + ["ḱ"]={ "k", "́" }, + ["Ḳ"]={ "K", "̣" }, + ["ḳ"]={ "k", "̣" }, + ["Ḵ"]={ "K", "̱" }, + ["ḵ"]={ "k", "̱" }, + ["Ḷ"]={ "L", "̣" }, + ["ḷ"]={ "l", "̣" }, + ["Ḹ"]={ "Ḷ", "̄" }, + ["ḹ"]={ "ḷ", "̄" }, + ["Ḻ"]={ "L", "̱" }, + ["ḻ"]={ "l", "̱" }, + ["Ḽ"]={ "L", "̭" }, + ["ḽ"]={ "l", "̭" }, + ["Ḿ"]={ "M", "́" }, + ["ḿ"]={ "m", "́" }, + ["Ṁ"]={ "M", "̇" }, + ["ṁ"]={ "m", "̇" }, + ["Ṃ"]={ "M", "̣" }, + ["ṃ"]={ "m", "̣" }, + ["Ṅ"]={ "N", "̇" }, + ["ṅ"]={ "n", "̇" }, + ["Ṇ"]={ "N", "̣" }, + ["ṇ"]={ "n", "̣" }, + ["Ṉ"]={ "N", "̱" }, + ["ṉ"]={ "n", "̱" }, + ["Ṋ"]={ "N", "̭" }, + ["ṋ"]={ "n", "̭" }, + ["Ṍ"]={ "Õ", "́" }, + ["ṍ"]={ "õ", "́" }, + ["Ṏ"]={ "Õ", "̈" }, + ["ṏ"]={ "õ", "̈" }, + ["Ṑ"]={ "Ō", "̀" }, + ["ṑ"]={ "ō", "̀" }, + ["Ṓ"]={ "Ō", "́" }, + ["ṓ"]={ "ō", "́" }, + ["Ṕ"]={ "P", "́" }, + ["ṕ"]={ "p", "́" }, + ["Ṗ"]={ "P", "̇" }, + ["ṗ"]={ "p", "̇" }, + ["Ṙ"]={ "R", "̇" }, + ["ṙ"]={ "r", "̇" }, + ["Ṛ"]={ "R", "̣" }, + ["ṛ"]={ "r", "̣" }, + ["Ṝ"]={ "Ṛ", "̄" }, + ["ṝ"]={ "ṛ", "̄" }, + ["Ṟ"]={ "R", "̱" }, + ["ṟ"]={ "r", "̱" }, + ["Ṡ"]={ "S", "̇" }, + ["ṡ"]={ "s", "̇" }, + ["Ṣ"]={ "S", "̣" }, + ["ṣ"]={ "s", "̣" }, + ["Ṥ"]={ "Ś", "̇" }, + ["ṥ"]={ "ś", "̇" }, + ["Ṧ"]={ "Š", "̇" }, + ["ṧ"]={ "š", "̇" }, + ["Ṩ"]={ "Ṣ", "̇" }, + ["ṩ"]={ "ṣ", "̇" }, + ["Ṫ"]={ "T", "̇" }, + ["ṫ"]={ "t", "̇" }, + ["Ṭ"]={ "T", "̣" }, + ["ṭ"]={ "t", "̣" }, + ["Ṯ"]={ "T", "̱" }, + ["ṯ"]={ "t", "̱" }, + ["Ṱ"]={ "T", "̭" }, + ["ṱ"]={ "t", "̭" }, + ["Ṳ"]={ "U", "̤" }, + ["ṳ"]={ "u", "̤" }, + ["Ṵ"]={ "U", "̰" }, + ["ṵ"]={ "u", "̰" }, + ["Ṷ"]={ "U", "̭" }, + ["ṷ"]={ "u", "̭" }, + ["Ṹ"]={ "Ũ", "́" }, + ["ṹ"]={ "ũ", "́" }, + ["Ṻ"]={ "Ū", "̈" }, + ["ṻ"]={ "ū", "̈" }, + ["Ṽ"]={ "V", "̃" }, + ["ṽ"]={ "v", "̃" }, + ["Ṿ"]={ "V", "̣" }, + ["ṿ"]={ "v", "̣" }, + ["Ẁ"]={ "W", "̀" }, + ["ẁ"]={ "w", "̀" }, + ["Ẃ"]={ "W", "́" }, + ["ẃ"]={ "w", "́" }, + ["Ẅ"]={ "W", "̈" }, + ["ẅ"]={ "w", "̈" }, + ["Ẇ"]={ "W", "̇" }, + ["ẇ"]={ "w", "̇" }, + ["Ẉ"]={ "W", "̣" }, + ["ẉ"]={ "w", "̣" }, + ["Ẋ"]={ "X", "̇" }, + ["ẋ"]={ "x", "̇" }, + ["Ẍ"]={ "X", "̈" }, + ["ẍ"]={ "x", "̈" }, + ["Ẏ"]={ "Y", "̇" }, + ["ẏ"]={ "y", "̇" }, + ["Ẑ"]={ "Z", "̂" }, + ["ẑ"]={ "z", "̂" }, + ["Ẓ"]={ "Z", "̣" }, + ["ẓ"]={ "z", "̣" }, + ["Ẕ"]={ "Z", "̱" }, + ["ẕ"]={ "z", "̱" }, + ["ẖ"]={ "h", "̱" }, + ["ẗ"]={ "t", "̈" }, + ["ẘ"]={ "w", "̊" }, + ["ẙ"]={ "y", "̊" }, + ["ẛ"]={ "ſ", "̇" }, + ["Ạ"]={ "A", "̣" }, + ["ạ"]={ "a", "̣" }, + ["Ả"]={ "A", "̉" }, + ["ả"]={ "a", "̉" }, + ["Ấ"]={ "Â", "́" }, + ["ấ"]={ "â", "́" }, + ["Ầ"]={ "Â", "̀" }, + ["ầ"]={ "â", "̀" }, + ["Ẩ"]={ "Â", "̉" }, + ["ẩ"]={ "â", "̉" }, + ["Ẫ"]={ "Â", "̃" }, + ["ẫ"]={ "â", "̃" }, + ["Ậ"]={ "Ạ", "̂" }, + ["ậ"]={ "ạ", "̂" }, + ["Ắ"]={ "Ă", "́" }, + ["ắ"]={ "ă", "́" }, + ["Ằ"]={ "Ă", "̀" }, + ["ằ"]={ "ă", "̀" }, + ["Ẳ"]={ "Ă", "̉" }, + ["ẳ"]={ "ă", "̉" }, + ["Ẵ"]={ "Ă", "̃" }, + ["ẵ"]={ "ă", "̃" }, + ["Ặ"]={ "Ạ", "̆" }, + ["ặ"]={ "ạ", "̆" }, + ["Ẹ"]={ "E", "̣" }, + ["ẹ"]={ "e", "̣" }, + ["Ẻ"]={ "E", "̉" }, + ["ẻ"]={ "e", "̉" }, + ["Ẽ"]={ "E", "̃" }, + ["ẽ"]={ "e", "̃" }, + ["Ế"]={ "Ê", "́" }, + ["ế"]={ "ê", "́" }, + ["Ề"]={ "Ê", "̀" }, + ["ề"]={ "ê", "̀" }, + ["Ể"]={ "Ê", "̉" }, + ["ể"]={ "ê", "̉" }, + ["Ễ"]={ "Ê", "̃" }, + ["ễ"]={ "ê", "̃" }, + ["Ệ"]={ "Ẹ", "̂" }, + ["ệ"]={ "ẹ", "̂" }, + ["Ỉ"]={ "I", "̉" }, + ["ỉ"]={ "i", "̉" }, + ["Ị"]={ "I", "̣" }, + ["ị"]={ "i", "̣" }, + ["Ọ"]={ "O", "̣" }, + ["ọ"]={ "o", "̣" }, + ["Ỏ"]={ "O", "̉" }, + ["ỏ"]={ "o", "̉" }, + ["Ố"]={ "Ô", "́" }, + ["ố"]={ "ô", "́" }, + ["Ồ"]={ "Ô", "̀" }, + ["ồ"]={ "ô", "̀" }, + ["Ổ"]={ "Ô", "̉" }, + ["ổ"]={ "ô", "̉" }, + ["Ỗ"]={ "Ô", "̃" }, + ["ỗ"]={ "ô", "̃" }, + ["Ộ"]={ "Ọ", "̂" }, + ["ộ"]={ "ọ", "̂" }, + ["Ớ"]={ "Ơ", "́" }, + ["ớ"]={ "ơ", "́" }, + ["Ờ"]={ "Ơ", "̀" }, + ["ờ"]={ "ơ", "̀" }, + ["Ở"]={ "Ơ", "̉" }, + ["ở"]={ "ơ", "̉" }, + ["Ỡ"]={ "Ơ", "̃" }, + ["ỡ"]={ "ơ", "̃" }, + ["Ợ"]={ "Ơ", "̣" }, + ["ợ"]={ "ơ", "̣" }, + ["Ụ"]={ "U", "̣" }, + ["ụ"]={ "u", "̣" }, + ["Ủ"]={ "U", "̉" }, + ["ủ"]={ "u", "̉" }, + ["Ứ"]={ "Ư", "́" }, + ["ứ"]={ "ư", "́" }, + ["Ừ"]={ "Ư", "̀" }, + ["ừ"]={ "ư", "̀" }, + ["Ử"]={ "Ư", "̉" }, + ["ử"]={ "ư", "̉" }, + ["Ữ"]={ "Ư", "̃" }, + ["ữ"]={ "ư", "̃" }, + ["Ự"]={ "Ư", "̣" }, + ["ự"]={ "ư", "̣" }, + ["Ỳ"]={ "Y", "̀" }, + ["ỳ"]={ "y", "̀" }, + ["Ỵ"]={ "Y", "̣" }, + ["ỵ"]={ "y", "̣" }, + ["Ỷ"]={ "Y", "̉" }, + ["ỷ"]={ "y", "̉" }, + ["Ỹ"]={ "Y", "̃" }, + ["ỹ"]={ "y", "̃" }, + ["ἀ"]={ "α", "̓" }, + ["ἁ"]={ "α", "̔" }, + ["ἂ"]={ "ἀ", "̀" }, + ["ἃ"]={ "ἁ", "̀" }, + ["ἄ"]={ "ἀ", "́" }, + ["ἅ"]={ "ἁ", "́" }, + ["ἆ"]={ "ἀ", "͂" }, + ["ἇ"]={ "ἁ", "͂" }, + ["Ἀ"]={ "Α", "̓" }, + ["Ἁ"]={ "Α", "̔" }, + ["Ἂ"]={ "Ἀ", "̀" }, + ["Ἃ"]={ "Ἁ", "̀" }, + ["Ἄ"]={ "Ἀ", "́" }, + ["Ἅ"]={ "Ἁ", "́" }, + ["Ἆ"]={ "Ἀ", "͂" }, + ["Ἇ"]={ "Ἁ", "͂" }, + ["ἐ"]={ "ε", "̓" }, + ["ἑ"]={ "ε", "̔" }, + ["ἒ"]={ "ἐ", "̀" }, + ["ἓ"]={ "ἑ", "̀" }, + ["ἔ"]={ "ἐ", "́" }, + ["ἕ"]={ "ἑ", "́" }, + ["Ἐ"]={ "Ε", "̓" }, + ["Ἑ"]={ "Ε", "̔" }, + ["Ἒ"]={ "Ἐ", "̀" }, + ["Ἓ"]={ "Ἑ", "̀" }, + ["Ἔ"]={ "Ἐ", "́" }, + ["Ἕ"]={ "Ἑ", "́" }, + ["ἠ"]={ "η", "̓" }, + ["ἡ"]={ "η", "̔" }, + ["ἢ"]={ "ἠ", "̀" }, + ["ἣ"]={ "ἡ", "̀" }, + ["ἤ"]={ "ἠ", "́" }, + ["ἥ"]={ "ἡ", "́" }, + ["ἦ"]={ "ἠ", "͂" }, + ["ἧ"]={ "ἡ", "͂" }, + ["Ἠ"]={ "Η", "̓" }, + ["Ἡ"]={ "Η", "̔" }, + ["Ἢ"]={ "Ἠ", "̀" }, + ["Ἣ"]={ "Ἡ", "̀" }, + ["Ἤ"]={ "Ἠ", "́" }, + ["Ἥ"]={ "Ἡ", "́" }, + ["Ἦ"]={ "Ἠ", "͂" }, + ["Ἧ"]={ "Ἡ", "͂" }, + ["ἰ"]={ "ι", "̓" }, + ["ἱ"]={ "ι", "̔" }, + ["ἲ"]={ "ἰ", "̀" }, + ["ἳ"]={ "ἱ", "̀" }, + ["ἴ"]={ "ἰ", "́" }, + ["ἵ"]={ "ἱ", "́" }, + ["ἶ"]={ "ἰ", "͂" }, + ["ἷ"]={ "ἱ", "͂" }, + ["Ἰ"]={ "Ι", "̓" }, + ["Ἱ"]={ "Ι", "̔" }, + ["Ἲ"]={ "Ἰ", "̀" }, + ["Ἳ"]={ "Ἱ", "̀" }, + ["Ἴ"]={ "Ἰ", "́" }, + ["Ἵ"]={ "Ἱ", "́" }, + ["Ἶ"]={ "Ἰ", "͂" }, + ["Ἷ"]={ "Ἱ", "͂" }, + ["ὀ"]={ "ο", "̓" }, + ["ὁ"]={ "ο", "̔" }, + ["ὂ"]={ "ὀ", "̀" }, + ["ὃ"]={ "ὁ", "̀" }, + ["ὄ"]={ "ὀ", "́" }, + ["ὅ"]={ "ὁ", "́" }, + ["Ὀ"]={ "Ο", "̓" }, + ["Ὁ"]={ "Ο", "̔" }, + ["Ὂ"]={ "Ὀ", "̀" }, + ["Ὃ"]={ "Ὁ", "̀" }, + ["Ὄ"]={ "Ὀ", "́" }, + ["Ὅ"]={ "Ὁ", "́" }, + ["ὐ"]={ "υ", "̓" }, + ["ὑ"]={ "υ", "̔" }, + ["ὒ"]={ "ὐ", "̀" }, + ["ὓ"]={ "ὑ", "̀" }, + ["ὔ"]={ "ὐ", "́" }, + ["ὕ"]={ "ὑ", "́" }, + ["ὖ"]={ "ὐ", "͂" }, + ["ὗ"]={ "ὑ", "͂" }, + ["Ὑ"]={ "Υ", "̔" }, + ["Ὓ"]={ "Ὑ", "̀" }, + ["Ὕ"]={ "Ὑ", "́" }, + ["Ὗ"]={ "Ὑ", "͂" }, + ["ὠ"]={ "ω", "̓" }, + ["ὡ"]={ "ω", "̔" }, + ["ὢ"]={ "ὠ", "̀" }, + ["ὣ"]={ "ὡ", "̀" }, + ["ὤ"]={ "ὠ", "́" }, + ["ὥ"]={ "ὡ", "́" }, + ["ὦ"]={ "ὠ", "͂" }, + ["ὧ"]={ "ὡ", "͂" }, + ["Ὠ"]={ "Ω", "̓" }, + ["Ὡ"]={ "Ω", "̔" }, + ["Ὢ"]={ "Ὠ", "̀" }, + ["Ὣ"]={ "Ὡ", "̀" }, + ["Ὤ"]={ "Ὠ", "́" }, + ["Ὥ"]={ "Ὡ", "́" }, + ["Ὦ"]={ "Ὠ", "͂" }, + ["Ὧ"]={ "Ὡ", "͂" }, + ["ὰ"]={ "α", "̀" }, + ["ὲ"]={ "ε", "̀" }, + ["ὴ"]={ "η", "̀" }, + ["ὶ"]={ "ι", "̀" }, + ["ὸ"]={ "ο", "̀" }, + ["ὺ"]={ "υ", "̀" }, + ["ὼ"]={ "ω", "̀" }, + ["ᾀ"]={ "ἀ", "ͅ" }, + ["ᾁ"]={ "ἁ", "ͅ" }, + ["ᾂ"]={ "ἂ", "ͅ" }, + ["ᾃ"]={ "ἃ", "ͅ" }, + ["ᾄ"]={ "ἄ", "ͅ" }, + ["ᾅ"]={ "ἅ", "ͅ" }, + ["ᾆ"]={ "ἆ", "ͅ" }, + ["ᾇ"]={ "ἇ", "ͅ" }, + ["ᾈ"]={ "Ἀ", "ͅ" }, + ["ᾉ"]={ "Ἁ", "ͅ" }, + ["ᾊ"]={ "Ἂ", "ͅ" }, + ["ᾋ"]={ "Ἃ", "ͅ" }, + ["ᾌ"]={ "Ἄ", "ͅ" }, + ["ᾍ"]={ "Ἅ", "ͅ" }, + ["ᾎ"]={ "Ἆ", "ͅ" }, + ["ᾏ"]={ "Ἇ", "ͅ" }, + ["ᾐ"]={ "ἠ", "ͅ" }, + ["ᾑ"]={ "ἡ", "ͅ" }, + ["ᾒ"]={ "ἢ", "ͅ" }, + ["ᾓ"]={ "ἣ", "ͅ" }, + ["ᾔ"]={ "ἤ", "ͅ" }, + ["ᾕ"]={ "ἥ", "ͅ" }, + ["ᾖ"]={ "ἦ", "ͅ" }, + ["ᾗ"]={ "ἧ", "ͅ" }, + ["ᾘ"]={ "Ἠ", "ͅ" }, + ["ᾙ"]={ "Ἡ", "ͅ" }, + ["ᾚ"]={ "Ἢ", "ͅ" }, + ["ᾛ"]={ "Ἣ", "ͅ" }, + ["ᾜ"]={ "Ἤ", "ͅ" }, + ["ᾝ"]={ "Ἥ", "ͅ" }, + ["ᾞ"]={ "Ἦ", "ͅ" }, + ["ᾟ"]={ "Ἧ", "ͅ" }, + ["ᾠ"]={ "ὠ", "ͅ" }, + ["ᾡ"]={ "ὡ", "ͅ" }, + ["ᾢ"]={ "ὢ", "ͅ" }, + ["ᾣ"]={ "ὣ", "ͅ" }, + ["ᾤ"]={ "ὤ", "ͅ" }, + ["ᾥ"]={ "ὥ", "ͅ" }, + ["ᾦ"]={ "ὦ", "ͅ" }, + ["ᾧ"]={ "ὧ", "ͅ" }, + ["ᾨ"]={ "Ὠ", "ͅ" }, + ["ᾩ"]={ "Ὡ", "ͅ" }, + ["ᾪ"]={ "Ὢ", "ͅ" }, + ["ᾫ"]={ "Ὣ", "ͅ" }, + ["ᾬ"]={ "Ὤ", "ͅ" }, + ["ᾭ"]={ "Ὥ", "ͅ" }, + ["ᾮ"]={ "Ὦ", "ͅ" }, + ["ᾯ"]={ "Ὧ", "ͅ" }, + ["ᾰ"]={ "α", "̆" }, + ["ᾱ"]={ "α", "̄" }, + ["ᾲ"]={ "ὰ", "ͅ" }, + ["ᾳ"]={ "α", "ͅ" }, + ["ᾴ"]={ "ά", "ͅ" }, + ["ᾶ"]={ "α", "͂" }, + ["ᾷ"]={ "ᾶ", "ͅ" }, + ["Ᾰ"]={ "Α", "̆" }, + ["Ᾱ"]={ "Α", "̄" }, + ["Ὰ"]={ "Α", "̀" }, + ["ᾼ"]={ "Α", "ͅ" }, + ["῁"]={ "¨", "͂" }, + ["ῂ"]={ "ὴ", "ͅ" }, + ["ῃ"]={ "η", "ͅ" }, + ["ῄ"]={ "ή", "ͅ" }, + ["ῆ"]={ "η", "͂" }, + ["ῇ"]={ "ῆ", "ͅ" }, + ["Ὲ"]={ "Ε", "̀" }, + ["Ὴ"]={ "Η", "̀" }, + ["ῌ"]={ "Η", "ͅ" }, + ["῍"]={ "᾿", "̀" }, + ["῎"]={ "᾿", "́" }, + ["῏"]={ "᾿", "͂" }, + ["ῐ"]={ "ι", "̆" }, + ["ῑ"]={ "ι", "̄" }, + ["ῒ"]={ "ϊ", "̀" }, + ["ῖ"]={ "ι", "͂" }, + ["ῗ"]={ "ϊ", "͂" }, + ["Ῐ"]={ "Ι", "̆" }, + ["Ῑ"]={ "Ι", "̄" }, + ["Ὶ"]={ "Ι", "̀" }, + ["῝"]={ "῾", "̀" }, + ["῞"]={ "῾", "́" }, + ["῟"]={ "῾", "͂" }, + ["ῠ"]={ "υ", "̆" }, + ["ῡ"]={ "υ", "̄" }, + ["ῢ"]={ "ϋ", "̀" }, + ["ῤ"]={ "ρ", "̓" }, + ["ῥ"]={ "ρ", "̔" }, + ["ῦ"]={ "υ", "͂" }, + ["ῧ"]={ "ϋ", "͂" }, + ["Ῠ"]={ "Υ", "̆" }, + ["Ῡ"]={ "Υ", "̄" }, + ["Ὺ"]={ "Υ", "̀" }, + ["Ῥ"]={ "Ρ", "̔" }, + ["῭"]={ "¨", "̀" }, + ["ῲ"]={ "ὼ", "ͅ" }, + ["ῳ"]={ "ω", "ͅ" }, + ["ῴ"]={ "ώ", "ͅ" }, + ["ῶ"]={ "ω", "͂" }, + ["ῷ"]={ "ῶ", "ͅ" }, + ["Ὸ"]={ "Ο", "̀" }, + ["Ὼ"]={ "Ω", "̀" }, + ["ῼ"]={ "Ω", "ͅ" }, + ["↚"]={ "←", "̸" }, + ["↛"]={ "→", "̸" }, + ["↮"]={ "↔", "̸" }, + ["⇍"]={ "⇐", "̸" }, + ["⇎"]={ "⇔", "̸" }, + ["⇏"]={ "⇒", "̸" }, + ["∄"]={ "∃", "̸" }, + ["∉"]={ "∈", "̸" }, + ["∌"]={ "∋", "̸" }, + ["∤"]={ "∣", "̸" }, + ["∦"]={ "∥", "̸" }, + ["≁"]={ "∼", "̸" }, + ["≄"]={ "≃", "̸" }, + ["≇"]={ "≅", "̸" }, + ["≉"]={ "≈", "̸" }, + ["≠"]={ "=", "̸" }, + ["≢"]={ "≡", "̸" }, + ["≭"]={ "≍", "̸" }, + ["≮"]={ "<", "̸" }, + ["≯"]={ ">", "̸" }, + ["≰"]={ "≤", "̸" }, + ["≱"]={ "≥", "̸" }, + ["≴"]={ "≲", "̸" }, + ["≵"]={ "≳", "̸" }, + ["≸"]={ "≶", "̸" }, + ["≹"]={ "≷", "̸" }, + ["⊀"]={ "≺", "̸" }, + ["⊁"]={ "≻", "̸" }, + ["⊄"]={ "⊂", "̸" }, + ["⊅"]={ "⊃", "̸" }, + ["⊈"]={ "⊆", "̸" }, + ["⊉"]={ "⊇", "̸" }, + ["⊬"]={ "⊢", "̸" }, + ["⊭"]={ "⊨", "̸" }, + ["⊮"]={ "⊩", "̸" }, + ["⊯"]={ "⊫", "̸" }, + ["⋠"]={ "≼", "̸" }, + ["⋡"]={ "≽", "̸" }, + ["⋢"]={ "⊑", "̸" }, + ["⋣"]={ "⊒", "̸" }, + ["⋪"]={ "⊲", "̸" }, + ["⋫"]={ "⊳", "̸" }, + ["⋬"]={ "⊴", "̸" }, + ["⋭"]={ "⊵", "̸" }, + ["⫝̸"]={ "⫝", "̸" }, + ["が"]={ "か", "゙" }, + ["ぎ"]={ "き", "゙" }, + ["ぐ"]={ "く", "゙" }, + ["げ"]={ "け", "゙" }, + ["ご"]={ "こ", "゙" }, + ["ざ"]={ "さ", "゙" }, + ["じ"]={ "し", "゙" }, + ["ず"]={ "す", "゙" }, + ["ぜ"]={ "せ", "゙" }, + ["ぞ"]={ "そ", "゙" }, + ["だ"]={ "た", "゙" }, + ["ぢ"]={ "ち", "゙" }, + ["づ"]={ "つ", "゙" }, + ["で"]={ "て", "゙" }, + ["ど"]={ "と", "゙" }, + ["ば"]={ "は", "゙" }, + ["ぱ"]={ "は", "゚" }, + ["び"]={ "ひ", "゙" }, + ["ぴ"]={ "ひ", "゚" }, + ["ぶ"]={ "ふ", "゙" }, + ["ぷ"]={ "ふ", "゚" }, + ["べ"]={ "へ", "゙" }, + ["ぺ"]={ "へ", "゚" }, + ["ぼ"]={ "ほ", "゙" }, + ["ぽ"]={ "ほ", "゚" }, + ["ゔ"]={ "う", "゙" }, + ["ゞ"]={ "ゝ", "゙" }, + ["ガ"]={ "カ", "゙" }, + ["ギ"]={ "キ", "゙" }, + ["グ"]={ "ク", "゙" }, + ["ゲ"]={ "ケ", "゙" }, + ["ゴ"]={ "コ", "゙" }, + ["ザ"]={ "サ", "゙" }, + ["ジ"]={ "シ", "゙" }, + ["ズ"]={ "ス", "゙" }, + ["ゼ"]={ "セ", "゙" }, + ["ゾ"]={ "ソ", "゙" }, + ["ダ"]={ "タ", "゙" }, + ["ヂ"]={ "チ", "゙" }, + ["ヅ"]={ "ツ", "゙" }, + ["デ"]={ "テ", "゙" }, + ["ド"]={ "ト", "゙" }, + ["バ"]={ "ハ", "゙" }, + ["パ"]={ "ハ", "゚" }, + ["ビ"]={ "ヒ", "゙" }, + ["ピ"]={ "ヒ", "゚" }, + ["ブ"]={ "フ", "゙" }, + ["プ"]={ "フ", "゚" }, + ["ベ"]={ "ヘ", "゙" }, + ["ペ"]={ "ヘ", "゚" }, + ["ボ"]={ "ホ", "゙" }, + ["ポ"]={ "ホ", "゚" }, + ["ヴ"]={ "ウ", "゙" }, + ["ヷ"]={ "ワ", "゙" }, + ["ヸ"]={ "ヰ", "゙" }, + ["ヹ"]={ "ヱ", "゙" }, + ["ヺ"]={ "ヲ", "゙" }, + ["ヾ"]={ "ヽ", "゙" }, + ["יִ"]={ "י", "ִ" }, + ["ײַ"]={ "ײ", "ַ" }, + ["שׁ"]={ "ש", "ׁ" }, + ["שׂ"]={ "ש", "ׂ" }, + ["שּׁ"]={ "שּ", "ׁ" }, + ["שּׂ"]={ "שּ", "ׂ" }, + ["אַ"]={ "א", "ַ" }, + ["אָ"]={ "א", "ָ" }, + ["אּ"]={ "א", "ּ" }, + ["בּ"]={ "ב", "ּ" }, + ["גּ"]={ "ג", "ּ" }, + ["דּ"]={ "ד", "ּ" }, + ["הּ"]={ "ה", "ּ" }, + ["וּ"]={ "ו", "ּ" }, + ["זּ"]={ "ז", "ּ" }, + ["טּ"]={ "ט", "ּ" }, + ["יּ"]={ "י", "ּ" }, + ["ךּ"]={ "ך", "ּ" }, + ["כּ"]={ "כ", "ּ" }, + ["לּ"]={ "ל", "ּ" }, + ["מּ"]={ "מ", "ּ" }, + ["נּ"]={ "נ", "ּ" }, + ["סּ"]={ "ס", "ּ" }, + ["ףּ"]={ "ף", "ּ" }, + ["פּ"]={ "פ", "ּ" }, + ["צּ"]={ "צ", "ּ" }, + ["קּ"]={ "ק", "ּ" }, + ["רּ"]={ "ר", "ּ" }, + ["שּ"]={ "ש", "ּ" }, + ["תּ"]={ "ת", "ּ" }, + ["וֹ"]={ "ו", "ֹ" }, + ["בֿ"]={ "ב", "ֿ" }, + ["כֿ"]={ "כ", "ֿ" }, + ["פֿ"]={ "פ", "ֿ" }, + ["𑂚"]={ "𑂙", "𑂺" }, + ["𑂜"]={ "𑂛", "𑂺" }, + ["𑂫"]={ "𑂥", "𑂺" }, + ["𑄮"]={ "𑄱", "𑄧" }, + ["𑄯"]={ "𑄲", "𑄧" }, + ["𑍋"]={ "𑍇", "𑌾" }, + ["𑍌"]={ "𑍇", "𑍗" }, + ["𑒻"]={ "𑒹", "𑒺" }, + ["𑒼"]={ "𑒹", "𑒰" }, + ["𑒾"]={ "𑒹", "𑒽" }, + ["𑖺"]={ "𑖸", "𑖯" }, + ["𑖻"]={ "𑖹", "𑖯" }, + ["𝅗𝅥"]={ "𝅗", "𝅥" }, + ["𝅘𝅥"]={ "𝅘", "𝅥" }, + ["𝅘𝅥𝅮"]={ "𝅘𝅥", "𝅮" }, + ["𝅘𝅥𝅯"]={ "𝅘𝅥", "𝅯" }, + ["𝅘𝅥𝅰"]={ "𝅘𝅥", "𝅰" }, + ["𝅘𝅥𝅱"]={ "𝅘𝅥", "𝅱" }, + ["𝅘𝅥𝅲"]={ "𝅘𝅥", "𝅲" }, + ["𝆹𝅥"]={ "𝆹", "𝅥" }, + ["𝆺𝅥"]={ "𝆺", "𝅥" }, + ["𝆹𝅥𝅮"]={ "𝆹𝅥", "𝅮" }, + ["𝆺𝅥𝅮"]={ "𝆺𝅥", "𝅮" }, + ["𝆹𝅥𝅯"]={ "𝆹𝅥", "𝅯" }, + ["𝆺𝅥𝅯"]={ "𝆺𝅥", "𝅯" }, + }, + }, + }, + ["name"]="collapse", + ["prepend"]=true, + ["type"]="ligature", +} diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-mis.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-mis.lua new file mode 100644 index 00000000000..d3cfd70a910 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-mis.lua @@ -0,0 +1,31 @@ +if not modules then modules = { } end modules ['luatex-font-mis'] = { + 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 + os.exit() +end + +local currentfont = font.current + +local hashes = fonts.hashes +local identifiers = hashes.identifiers or { } +local marks = hashes.marks or { } + +hashes.identifiers = identifiers +hashes.marks = marks + +table.setmetatableindex(marks,function(t,k) + if k == true then + return marks[currentfont()] + else + local resources = identifiers[k].resources or { } + local marks = resources.marks or { } + t[k] = marks + return marks + end +end) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-syn.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-syn.lua index 376fd05fbf5..2ec075434ff 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-syn.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts-syn.lua @@ -7,7 +7,6 @@ if not modules then modules = { } end modules ['luatex-fonts-syn'] = { } if context then - texio.write_nl("fatal error: this module is not for context") os.exit() end @@ -46,12 +45,12 @@ local loaded = false local fileformats = { "lua", "tex", "other text files" } function fonts.names.reportmissingbase() - texio.write("") + logs.report("fonts","missing font database, run: mtxrun --script fonts --reload --simple") fonts.names.reportmissingbase = nil end function fonts.names.reportmissingname() - texio.write("") + logs.report("fonts","unknown font in font database, run: mtxrun --script fonts --reload --simple") fonts.names.reportmissingname = nil end @@ -67,7 +66,7 @@ function fonts.names.resolve(name,sub) local foundname = resolvers.findfile(basename,format) or "" if foundname ~= "" then data = dofile(foundname) - texio.write("") + logs.report("fonts","font database '%s' loaded",foundname) break end end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts.lua deleted file mode 100644 index c21389c71de..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts.lua +++ /dev/null @@ -1,324 +0,0 @@ -if not modules then modules = { } end modules ['luatex-fonts'] = { - version = 1.001, - comment = "companion to luatex-fonts.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - --- A merged file is generated with: --- --- mtxrun --script package --merge ./luatex-fonts.lua --- --- A needed resource file is made by: --- --- mtxrun --script context luatex-basics-prepare.tex --- --- A font (generic) database is created with: --- --- mtxrun --script font --reload --simple - --- The following code isolates the generic context code from already defined or to be defined --- namespaces. This is the reference loader for plain tex. This generic code is also used in --- luaotfload which is a low level lualatex opentype font loader but somehow has gotten a bit --- too generic name / prefix, originally set up and maintained by Khaled Hosny. Currently that --- set of derived files is maintained by a larger team lead by Philipp Gesang so when there are --- issues with this code in latex, you can best contact him. It might make sense then to first --- check if context has the same issue. We do our best to keep the interface as clean as possible. --- --- The code base is rather stable now, especially if you stay away from the non generic code. All --- relevant data is organized in tables within the main table of a font instance. There are a few --- places where in context other code is plugged in, but this does not affect the core code. Users --- can (given that their macro package provides this option) access the font data (characters, --- descriptions, properties, parameters, etc) of this main table. The documentation is part of --- context. There is also a manual for the helper libraries (maintained as part of the cld manuals). --- --- Future versions will probably have some more specific context code removed, like tracing and --- obscure hooks, so that we have a more efficient version (and less files too). So, don't depend --- too much on low level code that is meant for context as it can change without notice. We might --- also add more helper code here, but that depends to what extend metatex (sidetrack of context) --- evolves into a low level layer (depends on time, as usual). - --- The code here is the same as in context version 2015.09.11 but the rendering in context can be --- different from generic. This can be a side effect of additional callbacks, additional features --- and interferences between mechanisms between macro packages. We use the rendering in context --- and luatex-plain as reference for issues. - -utf = utf or unicode.utf8 - --- We have some (global) hooks (for latex): - -if not non_generic_context then - non_generic_context = { } -end - -if not non_generic_context.luatex_fonts then - non_generic_context.luatex_fonts = { - -- load_before = nil, - -- load_after = nil, - -- skip_loading = nil, - } -end - -if not generic_context then - generic_context = { } -end - -if not generic_context.push_namespaces then - - function generic_context.push_namespaces() - texio.write(" ") - local normalglobal = { } - for k, v in next, _G do - normalglobal[k] = v - end - return normalglobal - end - - function generic_context.pop_namespaces(normalglobal,isolate) - if normalglobal then - texio.write(" ") - for k, v in next, _G do - if not normalglobal[k] then - generic_context[k] = v - if isolate then - _G[k] = nil - end - end - end - for k, v in next, normalglobal do - _G[k] = v - end - -- just to be sure: - setmetatable(generic_context,_G) - else - texio.write(" ") - os.exit() - end - end - -end - -local whatever = generic_context.push_namespaces() - --- We keep track of load time by storing the current time. That way we cannot be accused --- of slowing down loading too much. Anyhow, there is no reason for this library to perform --- slower in any other package as it does in context. --- --- Please don't update to this version without proper testing. It might be that this version --- lags behind stock context and the only formal release takes place around tex live code --- freeze. - -local starttime = os.gettimeofday() - --- As we don't use the context file searching, we need to initialize the kpse library. As the --- progname can be anything we will temporary switch to the context namespace if needed. Just --- adding the context paths to the path specification is somewhat faster. --- --- Now, with lua 5.2 being used we might create a special ENV for this. - --- kpse.set_program_name("luatex") - -local ctxkpse = nil -local verbose = true - -local function loadmodule(name,continue) - local foundname = kpse.find_file(name,"tex") or "" - if not foundname then - if not ctxkpse then - ctxkpse = kpse.new("luatex","context") - end - foundname = ctxkpse:find_file(name,"tex") or "" - end - if foundname == "" then - if not continue then - texio.write_nl(string.format(" ",name)) - os.exit() - end - else - if verbose then - texio.write(string.format(" <%s>",foundname)) -- no file.basename yet - end - dofile(foundname) - end -end - -if non_generic_context.luatex_fonts.load_before then - loadmodule(non_generic_context.luatex_fonts.load_before,true) -end - -if non_generic_context.luatex_fonts.skip_loading ~= true then - - loadmodule('luatex-fonts-merged.lua',true) - - if fonts then - - if not fonts._merge_loaded_message_done_ then - texio.write_nl("log", "!") - texio.write_nl("log", "! I am using the merged version of 'luatex-fonts.lua' here. If") - texio.write_nl("log", "! you run into problems or experience unexpected behaviour, and") - texio.write_nl("log", "! if you have ConTeXt installed you can try to delete the file") - texio.write_nl("log", "! 'luatex-font-merged.lua' as I might then use the possibly") - texio.write_nl("log", "! updated libraries. The merged version is not supported as it") - texio.write_nl("log", "! is a frozen instance. Problems can be reported to the ConTeXt") - texio.write_nl("log", "! mailing list.") - texio.write_nl("log", "!") - end - - fonts._merge_loaded_message_done_ = true - - else - - -- The following helpers are a bit overkill but I don't want to mess up context code for the - -- sake of general generality. Around version 1.0 there will be an official api defined. - -- - -- So, I will strip these libraries and see what is really needed so that we don't have this - -- overhead in the generic modules. The next section is only there for the packager, so stick - -- to using luatex-fonts with luatex-fonts-merged.lua and forget about the rest. The following - -- list might change without prior notice (for instance because we shuffled code around). - - loadmodule("l-lua.lua") - loadmodule("l-lpeg.lua") - loadmodule("l-function.lua") - loadmodule("l-string.lua") - loadmodule("l-table.lua") - loadmodule("l-io.lua") - loadmodule("l-file.lua") - loadmodule("l-boolean.lua") - loadmodule("l-math.lua") - loadmodule("l-unicode.lua") - - -- A few slightly higher level support modules: - - loadmodule("util-str.lua") - loadmodule("util-fil.lua") - - -- The following modules contain code that is either not used at all outside context or will - -- fail when enabled due to lack of other modules. - - -- First we load a few helper modules. This is about the miminum needed to let the font modules - -- do their work. Don't depend on their functions as we might strip them in future versions of - -- this generic variant. - - loadmodule('luatex-basics-gen.lua') - loadmodule('data-con.lua') - - -- We do need some basic node support. The code in there is not for general use as it might - -- change. - - loadmodule('luatex-basics-nod.lua') - - -- We ship a resources needed for font handling (more might end up here). - - loadmodule('luatex-basics-chr.lua') - - -- Now come the font modules that deal with traditional tex fonts as well as open type fonts. - -- - -- The font database file (if used at all) must be put someplace visible for kpse and is not - -- shared with context. The mtx-fonts script can be used to generate this file (using the - -- --reload --force --simple option). - - loadmodule('font-ini.lua') - loadmodule('font-con.lua') - loadmodule('luatex-fonts-enc.lua') -- will load font-age on demand - loadmodule('font-cid.lua') - loadmodule('font-map.lua') -- for loading lum file (will be stripped) - - -- We use a bit simpler database because using the context one demands loading more helper - -- code and although it is more flexible (more wauys to resolve and so) it will never be - -- uses in plain/latex anyway, so let's stick to a simple approach. - - loadmodule('luatex-fonts-syn.lua') - - loadmodule('font-oti.lua') - - -- These are the old loader and processing modules. These use the built-in font loader and - -- will stay around (but not be extended), only fixed. - - -- font-otf.lua - -- font-otb.lua - -- font-inj.lua - -- font-ota.lua - -- font-otn.lua - -- font-otp.lua - - -- Here come the new loader and processing modules. The loader is written in Lua and although - -- initial loading is somewhat slower, identifying is faster, cached files can be slightly - -- more efficient, and processing is somewhat faster (only measureable on complex fonts). - - loadmodule('font-otr.lua') - loadmodule('font-cff.lua') - loadmodule('font-ttf.lua') - loadmodule('font-dsp.lua') - loadmodule('font-oup.lua') - loadmodule('font-otl.lua') - loadmodule('font-oto.lua') - loadmodule('font-otj.lua') - loadmodule('font-ota.lua') - loadmodule('font-ots.lua') - loadmodule('font-osd.lua') - loadmodule('font-ocl.lua') -- svg needs 0.97 (for fix in memstreams) - - loadmodule('font-otc.lua') - - -- type one code - - loadmodule('font-onr.lua') -- was font-afm.lua - loadmodule('font-one.lua') -- was font-afm.lua - loadmodule('font-afk.lua') - - -- traditional code - - loadmodule('font-tfm.lua') - - -- common code - - loadmodule('font-lua.lua') - loadmodule('font-def.lua') - loadmodule('font-xtx.lua') -- xetex compatible specifiers (plain/latex only) - loadmodule('luatex-fonts-ext.lua') -- some extensions - - -- We need to plug into a callback and the following module implements the handlers. Actual - -- plugging in happens later. - - loadmodule('font-gbn.lua') - - end - -end - -if non_generic_context.luatex_fonts.load_after then - loadmodule(non_generic_context.luatex_fonts.load_after,true) -end - -resolvers.loadmodule = loadmodule - --- In order to deal with the fonts we need to initialize some callbacks. One can overload them later --- on if needed. First a bit of abstraction. - -generic_context.callback_ligaturing = false -generic_context.callback_kerning = false -generic_context.callback_pre_linebreak_filter = nodes.simple_font_handler -generic_context.callback_hpack_filter = nodes.simple_font_handler -generic_context.callback_define_font = fonts.definers.read - --- The next ones can be done at a different moment if needed. You can create a generic_context namespace --- and set no_callbacks_yet to true, load this module, and enable the callbacks later. So, there is really --- *no* need to create a alternative for luatex-fonts.lua and luatex-fonts-merged.lua: just load this one --- and overload if needed. - -if not generic_context.no_callbacks_yet then - - callback.register('ligaturing', generic_context.callback_ligaturing) - callback.register('kerning', generic_context.callback_kerning) - callback.register('pre_linebreak_filter', generic_context.callback_pre_linebreak_filter) - callback.register('hpack_filter', generic_context.callback_hpack_filter) - callback.register('define_font' , generic_context.callback_define_font) - -end - --- We're done. - -texio.write(string.format(" ", os.gettimeofday()-starttime)) - -generic_context.pop_namespaces(whatever) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts.tex b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts.tex deleted file mode 100644 index 7b457e9b4fc..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-fonts.tex +++ /dev/null @@ -1,140 +0,0 @@ -%D \module -%D [ file=luatex-fonts, -%D version=2009.12.01, -%D title=\LUATEX\ Support Macros, -%D subtitle=Generic \OPENTYPE\ Font Handler, -%D author=Hans Hagen, -%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] - -%D \subject{Welcome} -%D -%D This file is one of a set of basic functionality enhancements -%D for \LUATEX\ derived from the \CONTEXT\ \MKIV\ code base. Please -%D don't polute the \type {luatex-*} namespace with code not coming -%D from the \CONTEXT\ development team as we may add more files. -%D -%D As this is an experimental setup, it might not always work out as -%D expected. Around \LUATEX\ version 0.50 we expect the code to be -%D more or less okay. -%D -%D This file implements a basic font system for a bare \LUATEX\ -%D system. By default \LUATEX\ only knows about the classic \TFM\ -%D fonts but it can read other font formats and pass them to \LUA. -%D With some glue code one can then construct a suitable \TFM\ -%D representation that \LUATEX\ can work with. For more advanced font -%D support a bit more code is needed that needs to be hooked -%D into the callback mechanism. -%D -%D This file is currently rather simple: it just loads the \LUA\ file -%D with the same name. An example of a \type {luatex.tex} file that is -%D just plain \TEX: -%D -%D \starttyping -%D \catcode`\{=1 % left brace is begin-group character -%D \catcode`\}=2 % right brace is end-group character -%D -%D \input plain -%D -%D \everyjob\expandafter{\the\everyjob\input luatex-fonts\relax} -%D -%D \dump -%D \stoptyping -%D -%D We could load the \LUA\ file in \type {\everyjob} but maybe some -%D day we need more here. -%D -%D When defining a font you can use two prefixes. A \type {file:} -%D prefix forced a file search, while a \type {name:} prefix will -%D result in consulting the names database. Such a database can be -%D generated with: -%D -%D \starttyping -%D mtxrun --usekpse --script fonts --names -%D \stoptyping -%D -%D This will generate a file \type {luatex-fonts-names.lua} that has -%D to be placed in a location where it can be found by \KPSE. Beware: -%D the \type {--kpseonly} flag is only used outside \CONTEXT\ and -%D provides very limited functionality, just enough for this task. -%D -%D The code loaded here does not come out of thin air, but is mostly -%D shared with \CONTEXT, however, in that macropackage we go beyond -%D what is provided here. When you use the code packaged here you -%D need to keep a few things in mind: -%D -%D \startitemize -%D -%D \item This subsystem will be extended, improved etc. in about the -%D same pace as \CONTEXT\ \MKIV. However, because \CONTEXT\ provides a -%D rather high level of integration not all features will be supported -%D in the same quality. Use \CONTEXT\ if you want more goodies. -%D -%D \item There is no official \API\ yet, which means that using -%D functions implemented here is at your own risk, in the sense that -%D names and namespaces might change. There will be a minimal \API\ -%D defined once \LUATEX\ version 1.0 is out. Instead of patching the -%D files it's better to overload functions if needed. -%D -%D \item The modules are not stripped too much, which makes it -%D possible to benefit from improvements in the code that take place -%D in the perspective of \CONTEXT\ development. They might be split a -%D bit more in due time so the baseline might become smaller. -%D -%D \item The code is maintained and tested by the \CONTEXT\ -%D development team. As such it might be better suited for this macro -%D package and integration in other systems might demand some -%D additional wrapping. Problems can be reported to the team but as we -%D use \CONTEXT\ \MKIV\ as baseline, you'd better check if the problem -%D is a general \CONTEXT\ problem too. -%D -%D \item The more high level support for features that is provided in -%D \CONTEXT\ is not part of the code loaded here as it makes no sense -%D elsewhere. Some experimental features are not part of this code -%D either but some might show up later. -%D -%D \item Math font support will be added but only in its basic form -%D once that the Latin Modern and \TEX\ Gyre math fonts are -%D available. -%D -%D \item At this moment the more nifty speed-ups are not enabled -%D because they work in tandem with the alternative file handling -%D that \CONTEXT\ uses. Maybe around \LUATEX\ 1.0 we will bring some -%D speedup into this code too (if it pays off at all). -%D -%D \item The code defines a few global tables. If this code is used -%D in a larger perspective then you can best make sure that no -%D conflicts occur. The \CONTEXT\ package expects users to work in -%D their own namespace (\type {userdata}, \type {thirddata}, \type -%D {moduledata} or \type {document}. The team takes all freedom to -%D use any table at the global level but will not use tables that are -%D named after macro packages. Later the \CONTEXT\ might operate in -%D a more controlled namespace but it has a low priority. -%D -%D \item There is some tracing code present but this is not enabled -%D and not supported outside \CONTEXT\ either as it integrates quite -%D tightly into \CONTEXT. In case of problems you can use \CONTEXT\ -%D for tracking down problems. -%D -%D \item Patching the code in distributions is dangerous as it might -%D fix your problem but introduce new ones for \CONTEXT. So, best keep -%D the original code as it is. -%D -%D \item Attributes are (automatically) taken from the range 127-255 so -%D you'd best not use these yourself. -%D -%D \stopitemize -%D -%D If this all sounds a bit tricky, keep in mind that it makes no sense -%D for us to maintain multiple code bases and we happen to use \CONTEXT. -%D -%D For more details about how the font subsystem works we refer to -%D publications in \TEX\ related journals, the \CONTEXT\ documentation, -%D and the \CONTEXT\ wiki. - -\directlua { - if not fonts then - dofile(kpse.find_file("luatex-fonts.lua","tex")) - end -} - -\endinput diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-file.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-file.lua index f2a27ad1832..46b6847d331 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-file.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-file.lua @@ -69,31 +69,34 @@ local lpegmatch = lpeg.match local getcurrentdir, attributes = lfs.currentdir, lfs.attributes local checkedsplit = string.checkedsplit --- local patterns = file.patterns or { } --- file.patterns = patterns - local P, R, S, C, Cs, Cp, Cc, Ct = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Cc, lpeg.Ct -- better this way: -local tricky = S("/\\") * P(-1) +----- tricky = S("/\\") * P(-1) local attributes = lfs.attributes -if sandbox then - sandbox.redefine(lfs.isfile,"lfs.isfile") - sandbox.redefine(lfs.isdir, "lfs.isdir") -end - function lfs.isdir(name) - if lpegmatch(tricky,name) then - return attributes(name,"mode") == "directory" - else - return attributes(name.."/.","mode") == "directory" - end + -- if not lpegmatch(tricky,name) then + -- name = name .. "/." + -- end + return attributes(name,"mode") == "directory" end function lfs.isfile(name) - return attributes(name,"mode") == "file" + local a = attributes(name,"mode") + return a == "file" or a == "link" or nil +end + +function lfs.isfound(name) + local a = attributes(name,"mode") + return (a == "file" or a == "link") and name or nil +end + +if sandbox then + sandbox.redefine(lfs.isfile,"lfs.isfile") + sandbox.redefine(lfs.isdir, "lfs.isdir") + sandbox.redefine(lfs.isfound, "lfs.isfound") end local colon = P(":") @@ -699,3 +702,32 @@ function lfs.mkdirs(path) lfs.mkdir(full) end end + +-- here is oen i ran into when messign around with xavante code (keppler project) +-- where it's called in_base .. no gain in using lpeg here + +function file.withinbase(path) -- don't go beyond root + local l = 0 + if not find(path,"^/") then + path = "/" .. path + end + for dir in gmatch(path,"/([^/]+)") do + if dir == ".." then + l = l - 1 + elseif dir ~= "." then + l = l + 1 + end + if l < 0 then + return false + end + end + return true +end + +-- not used in context but was in luatex once: + +local symlinkattributes = lfs.symlinkattributes + +function lfs.readlink(name) + return symlinkattributes(name,"target") or nil +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-io.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-io.lua index 2039017b25d..fa3ad3c4cda 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-io.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-io.lua @@ -10,7 +10,7 @@ local io = io local open, flush, write, read = io.open, io.flush, io.write, io.read local byte, find, gsub, format = string.byte, string.find, string.gsub, string.format local concat = table.concat -local floor = math.floor +----- floor = math.floor local type = type if string.find(os.getenv("PATH"),";",1,true) then @@ -19,38 +19,48 @@ else io.fileseparator, io.pathseparator = "/" , ":" end -local function readall(f) - return f:read("*all") -end +-- local function readall(f) +-- return f:read("*all") +-- end -- The next one is upto 50% faster on large files and less memory consumption due -- to less intermediate large allocations. This phenomena was discussed on the -- luatex dev list. +local large = 0x01000000 -- 2^24 16.777.216 +local medium = 0x00100000 -- 2^20 1.048.576 +local small = 0x00020000 -- 2^17 131.072 + +-- local function readall(f) +-- local size = f:seek("end") +-- if size == 0 then +-- return "" +-- end +-- f:seek("set",0) +-- if size < medium then +-- return f:read('*all') +-- else +-- local step = (size > large) and large or (floor(size/(medium)) * small) +-- local data = { } +-- while true do +-- local r = f:read(step) +-- if not r then +-- return concat(data) +-- else +-- data[#data+1] = r +-- end +-- end +-- end +-- end + local function readall(f) + -- return f:read("*all") local size = f:seek("end") - if size == 0 then - return "" - end - f:seek("set",0) - if size < 1024*1024 then - return f:read('*all') + if size > 0 then + f:seek("set",0) + return f:read(size) else - local step - if size > 16*1024*1024 then - step = 16*1024*1024 - else - step = floor(size/(1024*1024)) * 1024 * 1024 / 8 - end - local data = { } - while true do - local r = f:read(step) - if not r then - return concat(data) - else - data[#data+1] = r - end - end + return "" end end @@ -59,52 +69,75 @@ io.readall = readall function io.loaddata(filename,textmode) -- return nil if empty local f = open(filename,(textmode and 'r') or 'rb') if f then - local data = readall(f) - f:close() - if #data > 0 then - return data + local size = f:seek("end") + local data = nil + if size > 0 then + -- data = f:read("*all") + f:seek("set",0) + data = f:read(size) end + f:close() + return data end end +-- function io.copydata(source,target,action) +-- local f = open(source,"rb") +-- if f then +-- local g = open(target,"wb") +-- if g then +-- local size = f:seek("end") +-- if size == 0 then +-- -- empty +-- else +-- f:seek("set",0) +-- if size < medium then +-- local data = f:read('*all') +-- if action then +-- data = action(data) +-- end +-- if data then +-- g:write(data) +-- end +-- else +-- local step = (size > large) and large or (floor(size/(medium)) * small) +-- while true do +-- local data = f:read(step) +-- if data then +-- if action then +-- data = action(data) +-- end +-- if data then +-- g:write(data) +-- end +-- else +-- break +-- end +-- end +-- end +-- end +-- g:close() +-- end +-- f:close() +-- flush() +-- end +-- end + function io.copydata(source,target,action) local f = open(source,"rb") if f then local g = open(target,"wb") if g then local size = f:seek("end") - if size == 0 then - -- empty - else + if size > 0 then + -- local data = f:read('*all') f:seek("set",0) - if size < 1024*1024 then - local data = f:read('*all') - if action then - data = action(data) - end - if data then - g:write(data) - end - else - local step - if size > 16*1024*1024 then - step = 16*1024*1024 - else - step = floor(size/(1024*1024)) * 1024 * 1024 / 8 - end - while true do - local data = f:read(step) - if data then - if action then - data = action(data) - end - if data then - g:write(data) - end - else - break - end - end + local data = f:read(size) + if action then + data = action(data) + end + if data then + g:write(data) end end g:close() @@ -134,32 +167,70 @@ end -- we can also chunk this one if needed: io.lines(filename,chunksize,"*l") -function io.loadlines(filename,n) -- return nil if empty - local f = open(filename,'r') - if not f then - -- no file - elseif n then - local lines = { } - for i=1,n do - local line = f:read("*lines") - if line then - lines[#lines+1] = line - else - break +-- ffi.readline + +if fio and fio.readline then + + local readline = fio.readline + + function io.loadlines(filename,n) -- return nil if empty + local f = open(filename,'r') + if not f then + -- no file + elseif n then + local lines = { } + for i=1,n do + local line = readline(f) + if line then + lines[i] = line + else + break + end + end + f:close() + lines = concat(lines,"\n") + if #lines > 0 then + return lines + end + else + local line = readline(f) + f:close() + if line and #line > 0 then + return line end end - f:close() - lines = concat(lines,"\n") - if #lines > 0 then - return lines - end - else - local line = f:read("*line") or "" - f:close() - if #line > 0 then - return line + end + +else + + function io.loadlines(filename,n) -- return nil if empty + local f = open(filename,'r') + if not f then + -- no file + elseif n then + local lines = { } + for i=1,n do + local line = f:read("*lines") + if line then + lines[i] = line + else + break + end + end + f:close() + lines = concat(lines,"\n") + if #lines > 0 then + return lines + end + else + local line = f:read("*line") or "" + f:close() + if #line > 0 then + return line + end end end + end function io.loadchunk(filename,n) @@ -205,6 +276,7 @@ local function noflines(f) return 0 end else + -- todo: load and lpeg local n = 0 for _ in f:lines() do n = n + 1 @@ -216,7 +288,7 @@ end io.noflines = noflines --- inlined is faster +-- inlined is faster ... beware, better use util-fil local nextchar = { [ 4] = function(f) @@ -331,7 +403,7 @@ function io.ask(question,default,options) end end -local function readnumber(f,n,m) +local function readnumber(f,n,m) -- to be replaced if m then f:seek("set",n) n = m @@ -340,38 +412,32 @@ local function readnumber(f,n,m) return byte(f:read(1)) elseif n == 2 then local a, b = byte(f:read(2),1,2) - return 256 * a + b + return 0x100 * a + b elseif n == 3 then local a, b, c = byte(f:read(3),1,3) - return 256*256 * a + 256 * b + c + return 0x10000 * a + 0x100 * b + c elseif n == 4 then local a, b, c, d = byte(f:read(4),1,4) - return 256*256*256 * a + 256*256 * b + 256 * c + d + return 0x1000000 * a + 0x10000 * b + 0x100 * c + d elseif n == 8 then local a, b = readnumber(f,4), readnumber(f,4) - return 256 * a + b + return 0x100 * a + b elseif n == 12 then local a, b, c = readnumber(f,4), readnumber(f,4), readnumber(f,4) - return 256*256 * a + 256 * b + c + return 0x10000 * a + 0x100 * b + c elseif n == -2 then local b, a = byte(f:read(2),1,2) - return 256*a + b + return 0x100 * a + b elseif n == -3 then local c, b, a = byte(f:read(3),1,3) - return 256*256 * a + 256 * b + c + return 0x10000 * a + 0x100 * b + c elseif n == -4 then local d, c, b, a = byte(f:read(4),1,4) - return 256*256*256 * a + 256*256 * b + 256*c + d + return 0x1000000 * a + 0x10000 * b + 0x100*c + d elseif n == -8 then local h, g, f, e, d, c, b, a = byte(f:read(8),1,8) - return 256*256*256*256*256*256*256 * a + - 256*256*256*256*256*256 * b + - 256*256*256*256*256 * c + - 256*256*256*256 * d + - 256*256*256 * e + - 256*256 * f + - 256 * g + - h + return 0x100000000000000 * a + 0x1000000000000 * b + 0x10000000000 * c + 0x100000000 * d + + 0x1000000 * e + 0x10000 * f + 0x100 * g + h else return 0 end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-lpeg.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-lpeg.lua index 959ca553eb8..750d5e698d7 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-lpeg.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-lpeg.lua @@ -6,6 +6,10 @@ if not modules then modules = { } end modules ['l-lpeg'] = { license = "see context related readme files" } +-- we can get too many captures (e.g. on largexml files) which makes me wonder +-- if P(foo)/"" can't be simplfied to N(foo) i.e. some direct instruction to the +-- lpeg virtual machine to ignore it + -- lpeg 12 vs lpeg 10: slower compilation, similar parsing speed (i need to check -- if i can use new features like capture / 2 and .B (at first sight the xml -- parser is some 5% slower) @@ -17,7 +21,13 @@ if not modules then modules = { } end modules ['l-lpeg'] = { -- move utf -> l-unicode -- move string -> l-string or keep it here -lpeg = require("lpeg") +-- lpeg.B : backward without consumption +-- lpeg.F = getmetatable(lpeg.P(1)).__len : forward without consumption + + +lpeg = require("lpeg") -- does lpeg register itself global? + +local lpeg = lpeg -- The latest lpeg doesn't have print any more, and even the new ones are not -- available by default (only when debug mode is enabled), which is a pitty as @@ -103,11 +113,14 @@ patterns.alwaysmatched = alwaysmatched local sign = S('+-') local zero = P('0') local digit = R('09') +local digits = digit^1 local octdigit = R("07") +local octdigits = octdigit^1 local lowercase = R("az") local uppercase = R("AZ") local underscore = P("_") local hexdigit = digit + lowercase + uppercase +local hexdigits = hexdigit^1 local cr, lf, crlf = P("\r"), P("\n"), P("\r\n") ----- newline = crlf + S("\r\n") -- cr + lf local newline = P("\r") * (P("\n") + P(true)) + P("\n") -- P("\r")^-1 * P("\n")^-1 @@ -187,18 +200,20 @@ local fullstripper = whitespace^0 * C((whitespace^0 * nonwhitespace^1)^0) ----- collapser = Cs(spacer^0/"" * ((spacer^1 * endofstring / "") + (spacer^1/" ") + P(1))^0) local collapser = Cs(spacer^0/"" * nonspacer^0 * ((spacer^0/" " * nonspacer^1)^0)) +local nospacer = Cs((whitespace^1/"" + nonwhitespace^1)^0) -local b_collapser = Cs( whitespace^0 /"" * (nonwhitespace^1 + whitespace^1/" ")^0) -local e_collapser = Cs((whitespace^1 * P(-1)/"" + nonwhitespace^1 + whitespace^1/" ")^0) -local m_collapser = Cs( (nonwhitespace^1 + whitespace^1/" ")^0) +local b_collapser = Cs( whitespace^0 /"" * (nonwhitespace^1 + whitespace^1/" ")^0) +local e_collapser = Cs((whitespace^1 * endofstring/"" + nonwhitespace^1 + whitespace^1/" ")^0) +local m_collapser = Cs( (nonwhitespace^1 + whitespace^1/" ")^0) -local b_stripper = Cs( spacer^0 /"" * (nonspacer^1 + spacer^1/" ")^0) -local e_stripper = Cs((spacer^1 * P(-1)/"" + nonspacer^1 + spacer^1/" ")^0) -local m_stripper = Cs( (nonspacer^1 + spacer^1/" ")^0) +local b_stripper = Cs( spacer^0 /"" * (nonspacer^1 + spacer^1/" ")^0) +local e_stripper = Cs((spacer^1 * endofstring/"" + nonspacer^1 + spacer^1/" ")^0) +local m_stripper = Cs( (nonspacer^1 + spacer^1/" ")^0) patterns.stripper = stripper patterns.fullstripper = fullstripper patterns.collapser = collapser +patterns.nospacer = nospacer patterns.b_collapser = b_collapser patterns.m_collapser = m_collapser @@ -238,43 +253,50 @@ patterns.doublequoted = dquote * patterns.nodquote * dquote patterns.quoted = patterns.doublequoted + patterns.singlequoted patterns.digit = digit +patterns.digits = digits patterns.octdigit = octdigit +patterns.octdigits = octdigits patterns.hexdigit = hexdigit +patterns.hexdigits = hexdigits patterns.sign = sign -patterns.cardinal = digit^1 -patterns.integer = sign^-1 * digit^1 -patterns.unsigned = digit^0 * period * digit^1 +patterns.cardinal = digits +patterns.integer = sign^-1 * digits +patterns.unsigned = digit^0 * period * digits patterns.float = sign^-1 * patterns.unsigned -patterns.cunsigned = digit^0 * comma * digit^1 -patterns.cpunsigned = digit^0 * (period + comma) * digit^1 +patterns.cunsigned = digit^0 * comma * digits +patterns.cpunsigned = digit^0 * (period + comma) * digits patterns.cfloat = sign^-1 * patterns.cunsigned patterns.cpfloat = sign^-1 * patterns.cpunsigned patterns.number = patterns.float + patterns.integer patterns.cnumber = patterns.cfloat + patterns.integer patterns.cpnumber = patterns.cpfloat + patterns.integer -patterns.oct = zero * octdigit^1 +patterns.oct = zero * octdigits -- hm is this ok patterns.octal = patterns.oct patterns.HEX = zero * P("X") * (digit+uppercase)^1 patterns.hex = zero * P("x") * (digit+lowercase)^1 -patterns.hexadecimal = zero * S("xX") * hexdigit^1 +patterns.hexadecimal = zero * S("xX") * hexdigits patterns.hexafloat = sign^-1 * zero * S("xX") - * (hexdigit^0 * period * hexdigit^1 + hexdigit^1 * period * hexdigit^0 + hexdigit^1) - * (S("pP") * sign^-1 * hexdigit^1)^-1 + * (hexdigit^0 * period * hexdigits + hexdigits * period * hexdigit^0 + hexdigits) + * (S("pP") * sign^-1 * hexdigits)^-1 patterns.decafloat = sign^-1 - * (digit^0 * period * digit^1 + digit^1 * period * digit^0 + digit^1) - * S("eE") * sign^-1 * digit^1 + * (digit^0 * period * digits + digits * period * digit^0 + digits) + * S("eE") * sign^-1 * digits patterns.propername = (uppercase + lowercase + underscore) * (uppercase + lowercase + underscore + digit)^0 * endofstring patterns.somecontent = (anything - newline - space)^1 -- (utf8char - newline - space)^1 patterns.beginline = #(1-newline) -patterns.longtostring = Cs(whitespace^0/"" * ((patterns.quoted + nonwhitespace^1 + whitespace^1/"" * (P(-1) + Cc(" ")))^0)) +patterns.longtostring = Cs(whitespace^0/"" * ((patterns.quoted + nonwhitespace^1 + whitespace^1/"" * (endofstring + Cc(" ")))^0)) -local function anywhere(pattern) --slightly adapted from website - return P { P(pattern) + 1 * V(1) } +-- local function anywhere(pattern) -- slightly adapted from website +-- return P { P(pattern) + 1 * V(1) } +-- end + +function anywhere(pattern) -- faster + return (1-P(pattern))^0 * P(pattern) end lpeg.anywhere = anywhere @@ -595,19 +617,27 @@ end -- print(7,lpegmatch(lpeg.secondofsplit(":"),"bc")) -- print(9,lpegmatch(lpeg.secondofsplit(":","123"),"bc")) --- -- slower: +-- this was slower but lpeg has been sped up in the meantime, so we no longer +-- use this (still seems somewhat faster on long strings) +-- +-- local nany = utf8char/"" -- -- function lpeg.counter(pattern) --- local n, pattern = 0, (lpeg.P(pattern)/function() n = n + 1 end + lpeg.anything)^0 --- return function(str) n = 0 ; lpegmatch(pattern,str) ; return n end +-- pattern = Cs((P(pattern)/" " + nany)^0) +-- return function(str) +-- return #lpegmatch(pattern,str) +-- end -- end -local nany = utf8char/"" - -function lpeg.counter(pattern) - pattern = Cs((P(pattern)/" " + nany)^0) - return function(str) - return #lpegmatch(pattern,str) +function lpeg.counter(pattern,action) + local n = 0 + local pattern = (P(pattern) / function() n = n + 1 end + anything)^0 + ----- pattern = (P(pattern) * (P(true) / function() n = n + 1 end) + anything)^0 + ----- pattern = (P(pattern) * P(function() n = n + 1 end) + anything)^0 + if action then + return function(str) n = 0 ; lpegmatch(pattern,str) ; action(n) end + else + return function(str) n = 0 ; lpegmatch(pattern,str) ; return n end end end @@ -839,28 +869,42 @@ end local p_false = P(false) local p_true = P(true) -local function make(t) - local function making(t) - local p = p_false - local keys = sortedkeys(t) - for i=1,#keys do - local k = keys[i] - if k ~= "" then - local v = t[k] - if v == true then - p = p + P(k) * p_true - elseif v == false then - -- can't happen - else - p = p + P(k) * making(v) - end - end - end - if t[""] then - p = p + p_true - end - return p - end +-- local function collapse(t,x) +-- if type(t) ~= "table" then +-- return t, x +-- else +-- local n = next(t) +-- if n == nil then +-- return t, x +-- elseif next(t,n) == nil then +-- -- one entry +-- local k = n +-- local v = t[k] +-- if type(v) == "table" then +-- return collapse(v,x..k) +-- else +-- return v, x .. k +-- end +-- else +-- local tt = { } +-- for k, v in next, t do +-- local vv, kk = collapse(v,k) +-- tt[kk] = vv +-- end +-- return tt, x +-- end +-- end +-- end + +local lower = utf and utf.lower or string.lower +local upper = utf and utf.upper or string.upper + +function lpeg.setutfcasers(l,u) + lower = l or lower + upper = u or upper +end + +local function make1(t,rest) local p = p_false local keys = sortedkeys(t) for i=1,#keys do @@ -872,41 +916,39 @@ local function make(t) elseif v == false then -- can't happen else - p = p + P(k) * making(v) + p = p + P(k) * make1(v,v[""]) end end end + if rest then + p = p + p_true + end return p end -local function collapse(t,x) - if type(t) ~= "table" then - return t, x - else - local n = next(t) - if n == nil then - return t, x - elseif next(t,n) == nil then - -- one entry - local k = n +local function make2(t,rest) -- only ascii + local p = p_false + local keys = sortedkeys(t) + for i=1,#keys do + local k = keys[i] + if k ~= "" then local v = t[k] - if type(v) == "table" then - return collapse(v,x..k) + if v == true then + p = p + (P(lower(k))+P(upper(k))) * p_true + elseif v == false then + -- can't happen else - return v, x .. k + p = p + (P(lower(k))+P(upper(k))) * make2(v,v[""]) end - else - local tt = { } - for k, v in next, t do - local vv, kk = collapse(v,k) - tt[kk] = vv - end - return tt, x end end + if rest then + p = p + p_true + end + return p end -function lpeg.utfchartabletopattern(list) -- goes to util-lpg +local function utfchartabletopattern(list,insensitive) -- goes to util-lpg local tree = { } local n = #list if n == 0 then @@ -979,32 +1021,42 @@ function lpeg.utfchartabletopattern(list) -- goes to util-lpg end end end --- collapse(tree,"") -- needs testing, maybe optional, slightly faster because P("x")*P("X") seems slower than P"(xX") (why) --- inspect(tree) - return make(tree) + -- collapse(tree,"") -- needs testing, maybe optional, slightly faster because P("x")*P("X") seems slower than P"(xX") (why) + -- inspect(tree) + return (insensitive and make2 or make1)(tree) +end + +lpeg.utfchartabletopattern = utfchartabletopattern + +function lpeg.utfreplacer(list,insensitive) + local pattern = Cs((utfchartabletopattern(list,insensitive)/list + utf8character)^0) + return function(str) + return lpegmatch(pattern,str) or str + end end + -- local t = { "start", "stoep", "staart", "paard" } -- local p = lpeg.Cs((lpeg.utfchartabletopattern(t)/string.upper + 1)^1) -- local t = { "a", "abc", "ac", "abe", "abxyz", "xy", "bef","aa" } -- local p = lpeg.Cs((lpeg.utfchartabletopattern(t)/string.upper + 1)^1) --- inspect(lpegmatch(p,"a")) --- inspect(lpegmatch(p,"aa")) --- inspect(lpegmatch(p,"aaaa")) --- inspect(lpegmatch(p,"ac")) --- inspect(lpegmatch(p,"bc")) --- inspect(lpegmatch(p,"zzbczz")) --- inspect(lpegmatch(p,"zzabezz")) --- inspect(lpegmatch(p,"ab")) --- inspect(lpegmatch(p,"abc")) --- inspect(lpegmatch(p,"abe")) --- inspect(lpegmatch(p,"xa")) --- inspect(lpegmatch(p,"bx")) --- inspect(lpegmatch(p,"bax")) --- inspect(lpegmatch(p,"abxyz")) --- inspect(lpegmatch(p,"foobarbefcrap")) +-- inspect(lpegmatch(p,"a")=="A") +-- inspect(lpegmatch(p,"aa")=="AA") +-- inspect(lpegmatch(p,"aaaa")=="AAAA") +-- inspect(lpegmatch(p,"ac")=="AC") +-- inspect(lpegmatch(p,"bc")=="bc") +-- inspect(lpegmatch(p,"zzbczz")=="zzbczz") +-- inspect(lpegmatch(p,"zzabezz")=="zzABEzz") +-- inspect(lpegmatch(p,"ab")=="Ab") +-- inspect(lpegmatch(p,"abc")=="ABC") +-- inspect(lpegmatch(p,"abe")=="ABE") +-- inspect(lpegmatch(p,"xa")=="xA") +-- inspect(lpegmatch(p,"bx")=="bx") +-- inspect(lpegmatch(p,"bax")=="bAx") +-- inspect(lpegmatch(p,"abxyz")=="ABXYZ") +-- inspect(lpegmatch(p,"foobarbefcrap")=="foobArBEFcrAp") -- local t = { ["^"] = 1, ["^^"] = 2, ["^^^"] = 3, ["^^^^"] = 4 } -- local p = lpeg.Cs((lpeg.utfchartabletopattern(t)/t + 1)^1) @@ -1081,25 +1133,38 @@ end -- moved here (before util-str) ------ digit = R("09") ------ period = P(".") ------ zero = P("0") -local trailingzeros = zero^0 * -digit -- suggested by Roberto R -local case_1 = period * trailingzeros / "" -local case_2 = period * (digit - trailingzeros)^1 * (trailingzeros / "") -local number = digit^1 * (case_1 + case_2) -local stripper = Cs((number + 1)^0) +do + + local trailingzeros = zero^0 * -digit -- suggested by Roberto + local stripper = Cs(( + digits * ( + period * trailingzeros / "" + + period * (digit - trailingzeros)^1 * (trailingzeros / "") + ) + 1 + )^0) + + lpeg.patterns.stripzeros = stripper -- multiple in string -lpeg.patterns.stripzeros = stripper + local nonzero = digit - zero + local trailingzeros = zero^1 * endofstring + local stripper = Cs( (1-period)^0 * ( + period * trailingzeros/"" + + period * (nonzero^1 + (trailingzeros/"") + zero^1)^0 + + endofstring + )) --- local sample = "bla 11.00 bla 11 bla 0.1100 bla 1.00100 bla 0.00 bla 0.001 bla 1.1100 bla 0.100100100 bla 0.00100100100" --- collectgarbage("collect") --- str = string.rep(sample,10000) --- local ts = os.clock() --- lpegmatch(stripper,str) --- print(#str, os.clock()-ts, lpegmatch(stripper,sample)) + lpeg.patterns.stripzero = stripper -- slightly more efficient but expects a float ! --- for practical reasone we keep this here: + -- local sample = "bla 11.00 bla 11 bla 0.1100 bla 1.00100 bla 0.00 bla 0.001 bla 1.1100 bla 0.100100100 bla 0.00100100100" + -- collectgarbage("collect") + -- str = string.rep(sample,10000) + -- local ts = os.clock() + -- lpegmatch(stripper,str) + -- print(#str, os.clock()-ts, lpegmatch(stripper,sample)) + +end + +-- for practical reasons we keep this here: local byte_to_HEX = { } local byte_to_hex = { } @@ -1171,3 +1236,22 @@ end -- local h = "ADFE0345" -- local b = lpegmatch(patterns.hextobytes,h) -- print(h,b,string.tohex(b),string.toHEX(b)) + +local patterns = { } -- can be made weak + +local function containsws(what) + local p = patterns[what] + if not p then + local p1 = P(what) * (whitespace + endofstring) * Cc(true) + local p2 = whitespace * P(p1) + p = P(p1) + P(1-p2)^0 * p2 + Cc(false) + patterns[what] = p + end + return p +end + +lpeg.containsws = containsws + +function string.containsws(str,what) + return lpegmatch(patterns[what] or containsws(what),str) +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-lua.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-lua.lua index 35715383635..c0a787a5c43 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-lua.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-lua.lua @@ -17,28 +17,37 @@ if not modules then modules = { } end modules ['l-lua'] = { -- utf.* -- bit32 --- compatibility hacksand helpers +local next, type, tonumber = next, type, tonumber -_MAJORVERSION, _MINORVERSION = string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$") +-- compatibility hacks and helpers -_MAJORVERSION = tonumber(_MAJORVERSION) or 5 -_MINORVERSION = tonumber(_MINORVERSION) or 1 -_LUAVERSION = _MAJORVERSION + _MINORVERSION/10 +LUAMAJORVERSION, LUAMINORVERSION = string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$") -if _LUAVERSION < 5.2 and jit then +LUAMAJORVERSION = tonumber(LUAMAJORVERSION) or 5 +LUAMINORVERSION = tonumber(LUAMINORVERSION) or 1 +LUAVERSION = LUAMAJORVERSION + LUAMINORVERSION/10 + +if LUAVERSION < 5.2 and jit then -- -- we want loadstring cum suis to behave like 5.2 -- - _MINORVERSION = 2 - _LUAVERSION = 5.2 + MINORVERSION = 2 + LUAVERSION = 5.2 end +_LUAVERSION = LUAVERSION -- for old times sake, will go away + -- lpeg if not lpeg then lpeg = require("lpeg") end +-- if utf8 then +-- utf8lua = utf8 +-- utf8 = nil +-- end + -- basics: if loadstring then @@ -188,7 +197,7 @@ if lua then lua.mask = load([[τεχ = 1]]) and "utf" or "ascii" end -local flush = io.flush +local flush = io.flush if flush then @@ -199,3 +208,45 @@ if flush then end +-- new + +FFISUPPORTED = type(ffi) == "table" and ffi.os ~= "" and ffi.arch ~= "" and ffi.load + +if not FFISUPPORTED then + + -- Maybe we should check for LUATEXENGINE but that's also a bti tricky as we still + -- can have a weird ffi library laying around. Checking for presence of 'jit' is + -- also not robust. So for now we hope for the best. + + local okay ; okay, ffi = pcall(require,"ffi") + + FFISUPPORTED = type(ffi) == "table" and ffi.os ~= "" and ffi.arch ~= "" and ffi.load + +end + +if not FFISUPPORTED then + ffi = nil +elseif not ffi.number then + ffi.number = tonumber +end + +if not bit32 then -- and utf8 then + -- bit32 = load ( [[ -- replacement code with 5.3 syntax so that 5.2 doesn't bark on it ]] ) + bit32 = require("l-bit32") +end + +-- We need this due a bug in luatex socket loading: + +-- local loaded = package.loaded +-- +-- if not loaded["socket"] then loaded["socket"] = loaded["socket.core"] end +-- if not loaded["mime"] then loaded["mime"] = loaded["mime.core"] end +-- +-- if not socket.mime then socket.mime = package.loaded["mime"] end +-- +-- if not loaded["socket.mime"] then loaded["socket.mime"] = socket.mime end +-- if not loaded["socket.http"] then loaded["socket.http"] = socket.http end +-- if not loaded["socket.ftp"] then loaded["socket.ftp"] = socket.ftp end +-- if not loaded["socket.smtp"] then loaded["socket.smtp"] = socket.smtp end +-- if not loaded["socket.tp"] then loaded["socket.tp"] = socket.tp end +-- if not loaded["socket.url"] then loaded["socket.url"] = socket.url end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-math.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-math.lua index ec62919b46a..e5668a5db0a 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-math.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-math.lua @@ -6,33 +6,144 @@ if not modules then modules = { } end modules ['l-math'] = { license = "see context related readme files" } -local floor, sin, cos, tan = math.floor, math.sin, math.cos, math.tan - if not math.ceiling then + math.ceiling = math.ceil + end if not math.round then + + local floor = math.floor + function math.round(x) return floor(x + 0.5) end + end if not math.div then + + local floor = math.floor + function math.div(n,m) return floor(n/m) end + end if not math.mod then + function math.mod(n,m) return n % m end -end -local pipi = 2*math.pi/360 +end if not math.sind then + + local sin, cos, tan = math.sin, math.cos, math.tan + + local pipi = 2*math.pi/360 + function math.sind(d) return sin(d*pipi) end function math.cosd(d) return cos(d*pipi) end function math.tand(d) return tan(d*pipi) end + end if not math.odd then + function math.odd (n) return n % 2 ~= 0 end function math.even(n) return n % 2 == 0 end + +end + +if not math.cosh then + + local exp = math.exp + + function math.cosh(x) + local xx = exp(x) + return (xx+1/xx)/2 + end + function math.sinh(x) + local xx = exp(x) + return (xx-1/xx)/2 + end + function math.tanh(x) + local xx = exp(x) + return (xx-1/xx)/(xx+1/xx) + end + +end + +if not math.pow then + + function math.pow(x,y) + return x^y + end + +end + +if not math.atan2 then + + math.atan2 = math.atan + +end + +if not math.ldexp then + + function math.ldexp(x,e) + return x * 2.0^e + end + +end + +-- if not math.frexp then +-- +-- -- not a oneliner so use a math library instead +-- +-- function math.frexp(x,e) +-- -- returns m and e such that x = m2e, e is an integer and the absolute +-- -- value of m is in the range [0.5, 1) (or zero when x is zero) +-- end +-- +-- end + +if not math.log10 then + + local log = math.log + + function math.log10(x) + return log(x,10) + end + +end + +if not math.type then + + function math.type() + return "float" + end + +end + +if not math.tointeger then + + math.mininteger = -0x4FFFFFFFFFFF + math.maxinteger = 0x4FFFFFFFFFFF + + local floor = math.floor + + function math.tointeger(n) + local f = floor(n) + return f == n and f or nil + end + +end + +if not math.ult then + + local floor = math.floor + + function math.tointeger(m,n) + -- not ok but i'm not motivated to look into it now + return floor(m) < floor(n) -- unsigned comparison needed + end + end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-string.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-string.lua index be8f397aee3..8ae8d8d1d76 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-string.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-string.lua @@ -18,7 +18,7 @@ local P, S, C, Ct, Cc, Cs = lpeg.P, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cs -- -- function string.split(str,pattern) -- local t = { } --- if #str > 0 then +-- if str ~= "" then -- local n = 1 -- for s in gmatch(str..pattern,"(.-)"..pattern) do -- t[n] = s @@ -72,6 +72,7 @@ end local stripper = patterns.stripper local fullstripper = patterns.fullstripper local collapser = patterns.collapser +local nospacer = patterns.nospacer local longtostring = patterns.longtostring function string.strip(str) @@ -86,6 +87,10 @@ function string.collapsespaces(str) return str and lpegmatch(collapser,str) or "" end +function string.nospaces(str) + return str and lpegmatch(nospacer,str) or "" +end + function string.longtostring(str) return str and lpegmatch(longtostring,str) or "" end @@ -212,3 +217,24 @@ end string.quote = string.quoted string.unquote = string.unquoted + +-- new + +if not string.bytetable then -- used in font-cff.lua + + local limit = 5000 -- we can go to 8000 in luajit and much higher in lua if needed + + function string.bytetable(str) -- from a string + local n = #str + if n > limit then + local t = { byte(str,1,limit) } + for i=limit+1,n do + t[i] = byte(str,i) + end + return t + else + return { byte(str,1,n) } + end + end + +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-table.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-table.lua index 39357bd25e6..69f45d9b4cd 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-table.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-table.lua @@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['l-table'] = { license = "see context related readme files" } -local type, next, tostring, tonumber, ipairs, select = type, next, tostring, tonumber, ipairs, select +local type, next, tostring, tonumber, select = type, next, tostring, tonumber, select local table, string = table, string local concat, sort, insert, remove = table.concat, table.sort, table.insert, table.remove local format, lower, dump = string.format, string.lower, string.dump @@ -22,6 +22,10 @@ local floor = math.floor local stripper = patterns.stripper +function table.getn(t) + return t and #t -- for very old times sake +end + function table.strip(tab) local lst, l = { }, 0 for i=1,#tab do @@ -416,7 +420,7 @@ end -- todo : copy without metatable -local function copy(t, tables) -- taken from lua wiki, slightly adapted +local function copy(t,tables) -- taken from lua wiki, slightly adapted tables = tables or { } local tcopy = { } if not tables[t] then @@ -427,7 +431,7 @@ local function copy(t, tables) -- taken from lua wiki, slightly adapted if tables[i] then i = tables[i] else - i = copy(i, tables) + i = copy(i,tables) end end if type(v) ~= "table" then @@ -435,7 +439,7 @@ local function copy(t, tables) -- taken from lua wiki, slightly adapted elseif tables[v] then tcopy[i] = tables[v] else - tcopy[i] = copy(v, tables) + tcopy[i] = copy(v,tables) end end local mt = getmetatable(t) @@ -460,7 +464,7 @@ function table.tohash(t,value) local h = { } if t then if value == nil then value = true end - for _, v in next, t do -- no ipairs here + for _, v in next, t do h[v] = value end end @@ -469,7 +473,7 @@ end function table.fromhash(t) local hsh, h = { }, 0 - for k, v in next, t do -- no ipairs here + for k, v in next, t do if v then h = h + 1 hsh[h] = k @@ -486,7 +490,7 @@ local reserved = table.tohash { -- intercept a language inconvenience: no reserv 'NaN', 'goto', } --- local function simple_table(t) +-- local function is_simple_table(t) -- if #t > 0 then -- local n = 0 -- for _,v in next, t do @@ -520,29 +524,67 @@ local reserved = table.tohash { -- intercept a language inconvenience: no reserv -- return nil -- end -local function simple_table(t) +-- local function is_simple_table(t) +-- local nt = #t +-- if nt > 0 then +-- local n = 0 +-- for _,v in next, t do +-- n = n + 1 +-- -- if type(v) == "table" then +-- -- return nil +-- -- end +-- end +-- if n == nt then +-- local tt = { } +-- for i=1,nt do +-- local v = t[i] +-- local tv = type(v) +-- if tv == "number" then +-- if hexify then +-- tt[i] = format("0x%X",v) +-- else +-- tt[i] = tostring(v) -- tostring not needed +-- end +-- elseif tv == "string" then +-- tt[i] = format("%q",v) +-- elseif tv == "boolean" then +-- tt[i] = v and "true" or "false" +-- else +-- return nil +-- end +-- end +-- return tt +-- end +-- end +-- return nil +-- end + +local function is_simple_table(t,hexify) -- also used in util-tab so maybe public local nt = #t if nt > 0 then local n = 0 - for _,v in next, t do + for _, v in next, t do n = n + 1 - -- if type(v) == "table" then - -- return nil - -- end + if type(v) == "table" then + return nil + end end + -- local haszero = t[0] + local haszero = rawget(t,0) -- don't trigger meta if n == nt then local tt = { } for i=1,nt do local v = t[i] local tv = type(v) if tv == "number" then + -- tt[i] = v -- not needed tostring(v) if hexify then tt[i] = format("0x%X",v) else - tt[i] = tostring(v) -- tostring not needed + tt[i] = v -- not needed tostring(v) end elseif tv == "string" then - tt[i] = format("%q",v) + tt[i] = format("%q",v) -- f_string(v) elseif tv == "boolean" then tt[i] = v and "true" or "false" else @@ -550,11 +592,35 @@ local function simple_table(t) end end return tt + elseif haszero and (n == nt + 1) then + local tt = { } + for i=0,nt do + local v = t[i] + local tv = type(v) + if tv == "number" then + -- tt[i+1] = v -- not needed tostring(v) + if hexify then + tt[i+1] = format("0x%X",v) + else + tt[i+1] = v -- not needed tostring(v) + end + elseif tv == "string" then + tt[i+1] = format("%q",v) -- f_string(v) + elseif tv == "boolean" then + tt[i+1] = v and "true" or "false" + else + return nil + end + end + tt[1] = "[0] = " .. tt[1] + return tt end end return nil end +table.is_simple_table = is_simple_table + -- Because this is a core function of mkiv I moved some function calls -- inline. -- @@ -637,7 +703,7 @@ local function do_serialize(root,name,depth,level,indexed) if next(v) == nil then handle(format("%s {},",depth)) elseif inline then -- and #t > 0 - local st = simple_table(v) + local st = is_simple_table(v,hexify) if st then handle(format("%s { %s },",depth,concat(st,", "))) else @@ -723,7 +789,7 @@ local function do_serialize(root,name,depth,level,indexed) handle(format("%s [%q]={},",depth,k)) end elseif inline then - local st = simple_table(v) + local st = is_simple_table(v,hexify) if st then if tk == "number" then if hexify then @@ -1030,7 +1096,9 @@ function table.unnest(t) -- bad name end local function are_equal(a,b,n,m) -- indexed - if a and b and #a == #b then + if a == b then + return true + elseif a and b and #a == #b then n = n or 1 m = m or #a for i=n,m do @@ -1052,16 +1120,18 @@ local function are_equal(a,b,n,m) -- indexed end local function identical(a,b) -- assumes same structure - for ka, va in next, a do - local vb = b[ka] - if va == vb then - -- same - elseif type(va) == "table" and type(vb) == "table" then - if not identical(va,vb) then + if a ~= b then + for ka, va in next, a do + local vb = b[ka] + if va == vb then + -- same + elseif type(va) == "table" and type(vb) == "table" then + if not identical(va,vb) then + return false + end + else return false end - else - return false end end return true @@ -1155,27 +1225,35 @@ function table.reversed(t) end end -function table.reverse(t) +function table.reverse(t) -- check with 5.3 ? if t then local n = #t - for i=1,floor(n/2) do - local j = n - i + 1 + local m = n + 1 + for i=1,floor(n/2) do -- maybe just n//2 + local j = m - i t[i], t[j] = t[j], t[i] end return t end end -function table.sequenced(t,sep,simple) -- hash only +local function sequenced(t,sep,simple) if not t then return "" + elseif type(t) == "string" then + return t -- handy fallback end local n = #t local s = { } if n > 0 then -- indexed for i=1,n do - s[i] = tostring(t[i]) + local v = t[i] + if type(v) == "table" then + s[i] = "{" .. sequenced(v,sep,simple) .. "}" + else + s[i] = tostring(t[i]) + end end else -- hashed @@ -1187,17 +1265,27 @@ function table.sequenced(t,sep,simple) -- hash only s[n] = k elseif v and v~= "" then n = n + 1 - s[n] = k .. "=" .. tostring(v) + if type(v) == "table" then + s[n] = k .. "={" .. sequenced(v,sep,simple) .. "}" + else + s[n] = k .. "=" .. tostring(v) + end end else n = n + 1 - s[n] = k .. "=" .. tostring(v) + if type(v) == "table" then + s[n] = k .. "={" .. sequenced(v,sep,simple) .. "}" + else + s[n] = k .. "=" .. tostring(v) + end end end end return concat(s,sep or " | ") end +table.sequenced = sequenced + function table.print(t,...) if type(t) ~= "table" then print(tostring(t)) @@ -1326,3 +1414,26 @@ function table.filtered(t,pattern,sort,cmp) return nothing end end + +-- lua 5.3: + +if not table.move then + + function table.move(a1,f,e,t,a2) + if a2 and a1 ~= a2 then + for i=f,e do + a2[t] = a1[i] + t = t + 1 + end + return a2 + else + t = t + e - f + for i=e,f,-1 do + a1[t] = a1[i] + t = t - 1 + end + return a1 + end + end + +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-unicode.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-unicode.lua new file mode 100644 index 00000000000..b5f52d3127c --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-l-unicode.lua @@ -0,0 +1,1361 @@ +if not modules then modules = { } end modules ['l-unicode'] = { + version = 1.001, + comment = "companion to luat-lib.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- floor(b/256) => rshift(b, 8) +-- floor(b/1024) => rshift(b,10) + +-- in lua 5.3: + +-- utf8.char(···) : concatinated +-- utf8.charpatt : "[\0-\x7F\xC2-\xF4][\x80-\xBF]*" +-- utf8.codes(s) : for p, c in utf8.codes(s) do body end +-- utf8.codepoint(s [, i [, j]]) +-- utf8.len(s [, i]) +-- utf8.offset(s, n [, i]) + +-- todo: utf.sub replacement (used in syst-aux) +-- we put these in the utf namespace: + +-- used : byte char gmatch len lower sub upper +-- not used : dump find format gfind gsub match rep reverse + +utf = utf or (unicode and unicode.utf8) or { } + +utf.characters = utf.characters or string.utfcharacters +utf.values = utf.values or string.utfvalues + +-- string.utfvalues +-- string.utfcharacters +-- string.characters +-- string.characterpairs +-- string.bytes +-- string.bytepairs +-- string.utflength +-- string.utfvalues +-- string.utfcharacters + +local type = type +local char, byte, format, sub, gmatch = string.char, string.byte, string.format, string.sub, string.gmatch +local concat = table.concat +local P, C, R, Cs, Ct, Cmt, Cc, Carg, Cp = lpeg.P, lpeg.C, lpeg.R, lpeg.Cs, lpeg.Ct, lpeg.Cmt, lpeg.Cc, lpeg.Carg, lpeg.Cp + +local lpegmatch = lpeg.match +local patterns = lpeg.patterns +local tabletopattern = lpeg.utfchartabletopattern + +local bytepairs = string.bytepairs + +local finder = lpeg.finder +local replacer = lpeg.replacer + +local utfvalues = utf.values +local utfgmatch = utf.gmatch -- not always present + +local p_utftype = patterns.utftype +local p_utfstricttype = patterns.utfstricttype +local p_utfoffset = patterns.utfoffset +local p_utf8char = patterns.utf8character +local p_utf8byte = patterns.utf8byte +local p_utfbom = patterns.utfbom +local p_newline = patterns.newline +local p_whitespace = patterns.whitespace + +if not unicode then + + unicode = { utf = utf } -- for a while + +end + +if not utf.char then + + utf.char = string.utfcharacter or (utf8 and utf8.char) + + if not utf.char then + + -- no multiples + + local char = string.char + + if bit32 then + + local rshift = bit32.rshift + + function utf.char(n) + if n < 0x80 then + -- 0aaaaaaa : 0x80 + return char(n) + elseif n < 0x800 then + -- 110bbbaa : 0xC0 : n >> 6 + -- 10aaaaaa : 0x80 : n & 0x3F + return char( + 0xC0 + rshift(n,6), + 0x80 + (n % 0x40) + ) + elseif n < 0x10000 then + -- 1110bbbb : 0xE0 : n >> 12 + -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F + -- 10aaaaaa : 0x80 : n & 0x3F + return char( + 0xE0 + rshift(n,12), + 0x80 + (rshift(n,6) % 0x40), + 0x80 + (n % 0x40) + ) + elseif n < 0x200000 then + -- 11110ccc : 0xF0 : n >> 18 + -- 10ccbbbb : 0x80 : (n >> 12) & 0x3F + -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F + -- 10aaaaaa : 0x80 : n & 0x3F + -- dddd : ccccc - 1 + return char( + 0xF0 + rshift(n,18), + 0x80 + (rshift(n,12) % 0x40), + 0x80 + (rshift(n,6) % 0x40), + 0x80 + (n % 0x40) + ) + else + return "" + end + end + + else + + local floor = math.floor + + function utf.char(n) + if n < 0x80 then + return char(n) + elseif n < 0x800 then + return char( + 0xC0 + floor(n/0x40), + 0x80 + (n % 0x40) + ) + elseif n < 0x10000 then + return char( + 0xE0 + floor(n/0x1000), + 0x80 + (floor(n/0x40) % 0x40), + 0x80 + (n % 0x40) + ) + elseif n < 0x200000 then + return char( + 0xF0 + floor(n/0x40000), + 0x80 + (floor(n/0x1000) % 0x40), + 0x80 + (floor(n/0x40) % 0x40), + 0x80 + (n % 0x40) + ) + else + return "" + end + end + + end + + end + +end + +if not utf.byte then + + utf.byte = string.utfvalue or (utf8 and utf8.codepoint) + + if not utf.byte then + + local utf8byte = patterns.utf8byte + + function utf.byte(c) + return lpegmatch(utf8byte,c) + end + + end + +end + +local utfchar, utfbyte = utf.char, utf.byte + +-- As we want to get rid of the (unmaintained) utf library we implement our own +-- variants (in due time an independent module): + +function utf.filetype(data) + return data and lpegmatch(p_utftype,data) or "unknown" +end + +local toentities = Cs ( + ( + patterns.utf8one + + ( + patterns.utf8two + + patterns.utf8three + + patterns.utf8four + ) / function(s) local b = utfbyte(s) if b < 127 then return s else return format("&#%X;",b) end end + )^0 +) + +patterns.toentities = toentities + +function utf.toentities(str) + return lpegmatch(toentities,str) +end + +-- local utfchr = { } -- 60K -> 2.638 M extra mem but currently not called that often (on latin) +-- +-- setmetatable(utfchr, { __index = function(t,k) local v = utfchar(k) t[k] = v return v end } ) +-- +-- collectgarbage("collect") +-- local u = collectgarbage("count")*1024 +-- local t = os.clock() +-- for i=1,1000 do +-- for i=1,600 do +-- local a = utfchr[i] +-- end +-- end +-- print(os.clock()-t,collectgarbage("count")*1024-u) + +-- collectgarbage("collect") +-- local t = os.clock() +-- for i=1,1000 do +-- for i=1,600 do +-- local a = utfchar(i) +-- end +-- end +-- print(os.clock()-t,collectgarbage("count")*1024-u) + +-- local byte = string.byte +-- local utfchar = utf.char + +local one = P(1) +local two = C(1) * C(1) +local four = C(R(utfchar(0xD8),utfchar(0xFF))) * C(1) * C(1) * C(1) + +local pattern = P("\254\255") * Cs( ( + four / function(a,b,c,d) + local ab = 0xFF * byte(a) + byte(b) + local cd = 0xFF * byte(c) + byte(d) + return utfchar((ab-0xD800)*0x400 + (cd-0xDC00) + 0x10000) + end + + two / function(a,b) + return utfchar(byte(a)*256 + byte(b)) + end + + one + )^1 ) + + P("\255\254") * Cs( ( + four / function(b,a,d,c) + local ab = 0xFF * byte(a) + byte(b) + local cd = 0xFF * byte(c) + byte(d) + return utfchar((ab-0xD800)*0x400 + (cd-0xDC00) + 0x10000) + end + + two / function(b,a) + return utfchar(byte(a)*256 + byte(b)) + end + + one + )^1 ) + +function string.toutf(s) -- in string namespace + return lpegmatch(pattern,s) or s -- todo: utf32 +end + +local validatedutf = Cs ( + ( + patterns.utf8one + + patterns.utf8two + + patterns.utf8three + + patterns.utf8four + + P(1) / "�" + )^0 +) + +patterns.validatedutf = validatedutf + +function utf.is_valid(str) + return type(str) == "string" and lpegmatch(validatedutf,str) or false +end + +if not utf.len then + + utf.len = string.utflength or (utf8 and utf8.len) + + if not utf.len then + + -- -- alternative 1: 0.77 + -- + -- local utfcharcounter = utfbom^-1 * Cs((p_utf8char/'!')^0) + -- + -- function utf.len(str) + -- return #lpegmatch(utfcharcounter,str or "") + -- end + -- + -- -- alternative 2: 1.70 + -- + -- local n = 0 + -- + -- local utfcharcounter = utfbom^-1 * (p_utf8char/function() n = n + 1 end)^0 -- slow + -- + -- function utf.length(str) + -- n = 0 + -- lpegmatch(utfcharcounter,str or "") + -- return n + -- end + -- + -- -- alternative 3: 0.24 (native unicode.utf8.len: 0.047) + + -- local n = 0 + -- + -- -- local utfcharcounter = lpeg.patterns.utfbom^-1 * P ( ( Cp() * ( + -- -- patterns.utf8one ^1 * Cc(1) + -- -- + patterns.utf8two ^1 * Cc(2) + -- -- + patterns.utf8three^1 * Cc(3) + -- -- + patterns.utf8four ^1 * Cc(4) ) * Cp() / function(f,d,t) n = n + (t - f)/d end + -- -- )^0 ) -- just as many captures as below + -- + -- -- local utfcharcounter = lpeg.patterns.utfbom^-1 * P ( ( + -- -- (Cmt(patterns.utf8one ^1,function(_,_,s) n = n + #s return true end)) + -- -- + (Cmt(patterns.utf8two ^1,function(_,_,s) n = n + #s/2 return true end)) + -- -- + (Cmt(patterns.utf8three^1,function(_,_,s) n = n + #s/3 return true end)) + -- -- + (Cmt(patterns.utf8four ^1,function(_,_,s) n = n + #s/4 return true end)) + -- -- )^0 ) -- not interesting as it creates strings but sometimes faster + -- + -- -- The best so far: + -- + -- local utfcharcounter = utfbom^-1 * P ( ( + -- Cp() * (patterns.utf8one )^1 * Cp() / function(f,t) n = n + t - f end + -- + Cp() * (patterns.utf8two )^1 * Cp() / function(f,t) n = n + (t - f)/2 end + -- + Cp() * (patterns.utf8three)^1 * Cp() / function(f,t) n = n + (t - f)/3 end + -- + Cp() * (patterns.utf8four )^1 * Cp() / function(f,t) n = n + (t - f)/4 end + -- )^0 ) + + -- function utf.len(str) + -- n = 0 + -- lpegmatch(utfcharcounter,str or "") + -- return n + -- end + + local n, f = 0, 1 + + local utfcharcounter = patterns.utfbom^-1 * Cmt ( + Cc(1) * patterns.utf8one ^1 + + Cc(2) * patterns.utf8two ^1 + + Cc(3) * patterns.utf8three^1 + + Cc(4) * patterns.utf8four ^1, + function(_,t,d) -- due to Cc no string captures, so faster + n = n + (t - f)/d + f = t + return true + end + )^0 + + function utf.len(str) + n, f = 0, 1 + lpegmatch(utfcharcounter,str or "") + return n + end + + -- -- these are quite a bit slower: + + -- utfcharcounter = utfbom^-1 * (Cmt(P(1) * R("\128\191")^0, function() n = n + 1 return true end))^0 -- 50+ times slower + -- utfcharcounter = utfbom^-1 * (Cmt(P(1), function() n = n + 1 return true end) * R("\128\191")^0)^0 -- 50- times slower + + end + +end + +utf.length = utf.len + +if not utf.sub then + + -- inefficient as lpeg just copies ^n + + -- local function sub(str,start,stop) + -- local pattern = p_utf8char^-(start-1) * C(p_utf8char^-(stop-start+1)) + -- inspect(pattern) + -- return lpegmatch(pattern,str) or "" + -- end + + -- local b, e, n, first, last = 0, 0, 0, 0, 0 + -- + -- local function slide(s,p) + -- n = n + 1 + -- if n == first then + -- b = p + -- if not last then + -- return nil + -- end + -- end + -- if n == last then + -- e = p + -- return nil + -- else + -- return p + -- end + -- end + -- + -- local pattern = Cmt(p_utf8char,slide)^0 + -- + -- function utf.sub(str,start,stop) -- todo: from the end + -- if not start then + -- return str + -- end + -- b, e, n, first, last = 0, 0, 0, start, stop + -- lpegmatch(pattern,str) + -- if not stop then + -- return sub(str,b) + -- else + -- return sub(str,b,e-1) + -- end + -- end + + -- print(utf.sub("Hans Hagen is my name")) + -- print(utf.sub("Hans Hagen is my name",5)) + -- print(utf.sub("Hans Hagen is my name",5,10)) + + local utflength = utf.length + + -- also negative indices, upto 10 times slower than a c variant + + local b, e, n, first, last = 0, 0, 0, 0, 0 + + local function slide_zero(s,p) + n = n + 1 + if n >= last then + e = p - 1 + else + return p + end + end + + local function slide_one(s,p) + n = n + 1 + if n == first then + b = p + end + if n >= last then + e = p - 1 + else + return p + end + end + + local function slide_two(s,p) + n = n + 1 + if n == first then + b = p + else + return true + end + end + + local pattern_zero = Cmt(p_utf8char,slide_zero)^0 + local pattern_one = Cmt(p_utf8char,slide_one )^0 + local pattern_two = Cmt(p_utf8char,slide_two )^0 + + local pattern_first = C(patterns.utf8character) + + function utf.sub(str,start,stop) + if not start then + return str + end + if start == 0 then + start = 1 + end + if not stop then + if start < 0 then + local l = utflength(str) -- we can inline this function if needed + start = l + start + else + start = start - 1 + end + b, n, first = 0, 0, start + lpegmatch(pattern_two,str) + if n >= first then + return sub(str,b) + else + return "" + end + end + if start < 0 or stop < 0 then + local l = utf.length(str) + if start < 0 then + start = l + start + if start <= 0 then + start = 1 + else + start = start + 1 + end + end + if stop < 0 then + stop = l + stop + if stop == 0 then + stop = 1 + else + stop = stop + 1 + end + end + end + if start == 1 and stop == 1 then + return lpegmatch(pattern_first,str) or "" + elseif start > stop then + return "" + elseif start > 1 then + b, e, n, first, last = 0, 0, 0, start - 1, stop + lpegmatch(pattern_one,str) + if n >= first and e == 0 then + e = #str + end + return sub(str,b,e) + else + b, e, n, last = 1, 0, 0, stop + lpegmatch(pattern_zero,str) + if e == 0 then + e = #str + end + return sub(str,b,e) + end + end + + -- local n = 100000 + -- local str = string.rep("123456àáâãäå",100) + -- + -- for i=-15,15,1 do + -- for j=-15,15,1 do + -- if utf.xsub(str,i,j) ~= utf.sub(str,i,j) then + -- print("error",i,j,"l>"..utf.xsub(str,i,j),"s>"..utf.sub(str,i,j)) + -- end + -- end + -- if utf.xsub(str,i) ~= utf.sub(str,i) then + -- print("error",i,"l>"..utf.xsub(str,i),"s>"..utf.sub(str,i)) + -- end + -- end + + -- print(" 1, 7",utf.xsub(str, 1, 7),utf.sub(str, 1, 7)) + -- print(" 0, 7",utf.xsub(str, 0, 7),utf.sub(str, 0, 7)) + -- print(" 0, 9",utf.xsub(str, 0, 9),utf.sub(str, 0, 9)) + -- print(" 4 ",utf.xsub(str, 4 ),utf.sub(str, 4 )) + -- print(" 0 ",utf.xsub(str, 0 ),utf.sub(str, 0 )) + -- print(" 0, 0",utf.xsub(str, 0, 0),utf.sub(str, 0, 0)) + -- print(" 4, 4",utf.xsub(str, 4, 4),utf.sub(str, 4, 4)) + -- print(" 4, 0",utf.xsub(str, 4, 0),utf.sub(str, 4, 0)) + -- print("-3, 0",utf.xsub(str,-3, 0),utf.sub(str,-3, 0)) + -- print(" 0,-3",utf.xsub(str, 0,-3),utf.sub(str, 0,-3)) + -- print(" 5,-3",utf.xsub(str,-5,-3),utf.sub(str,-5,-3)) + -- print("-3 ",utf.xsub(str,-3 ),utf.sub(str,-3 )) + +end + +-- a replacement for simple gsubs: + +-- function utf.remapper(mapping) +-- local pattern = Cs((p_utf8char/mapping)^0) +-- return function(str) +-- if not str or str == "" then +-- return "" +-- else +-- return lpegmatch(pattern,str) +-- end +-- end, pattern +-- end + +function utf.remapper(mapping,option,action) -- static also returns a pattern + local variant = type(mapping) + if variant == "table" then + action = action or mapping + if option == "dynamic" then + local pattern = false + table.setmetatablenewindex(mapping,function(t,k,v) rawset(t,k,v) pattern = false end) + return function(str) + if not str or str == "" then + return "" + else + if not pattern then + pattern = Cs((tabletopattern(mapping)/action + p_utf8char)^0) + end + return lpegmatch(pattern,str) + end + end + elseif option == "pattern" then + return Cs((tabletopattern(mapping)/action + p_utf8char)^0) + -- elseif option == "static" then + else + local pattern = Cs((tabletopattern(mapping)/action + p_utf8char)^0) + return function(str) + if not str or str == "" then + return "" + else + return lpegmatch(pattern,str) + end + end, pattern + end + elseif variant == "function" then + if option == "pattern" then + return Cs((p_utf8char/mapping + p_utf8char)^0) + else + local pattern = Cs((p_utf8char/mapping + p_utf8char)^0) + return function(str) + if not str or str == "" then + return "" + else + return lpegmatch(pattern,str) + end + end, pattern + end + else + -- is actually an error + return function(str) + return str or "" + end + end +end + +-- local remap = utf.remapper { a = 'd', b = "c", c = "b", d = "a" } +-- print(remap("abcd 1234 abcd")) + +function utf.replacer(t) -- no precheck, always string builder + local r = replacer(t,false,false,true) + return function(str) + return lpegmatch(r,str) + end +end + +function utf.subtituter(t) -- with precheck and no building if no match + local f = finder (t) + local r = replacer(t,false,false,true) + return function(str) + local i = lpegmatch(f,str) + if not i then + return str + elseif i > #str then + return str + else + -- return sub(str,1,i-2) .. lpegmatch(r,str,i-1) -- slower + return lpegmatch(r,str) + end + end +end + +-- inspect(utf.split("a b c d")) +-- inspect(utf.split("a b c d",true)) + +local utflinesplitter = p_utfbom^-1 * lpeg.tsplitat(p_newline) +local utfcharsplitter_ows = p_utfbom^-1 * Ct(C(p_utf8char)^0) +local utfcharsplitter_iws = p_utfbom^-1 * Ct((p_whitespace^1 + C(p_utf8char))^0) +local utfcharsplitter_raw = Ct(C(p_utf8char)^0) + +patterns.utflinesplitter = utflinesplitter + +function utf.splitlines(str) + return lpegmatch(utflinesplitter,str or "") +end + +function utf.split(str,ignorewhitespace) -- new + if ignorewhitespace then + return lpegmatch(utfcharsplitter_iws,str or "") + else + return lpegmatch(utfcharsplitter_ows,str or "") + end +end + +function utf.totable(str) -- keeps bom + return lpegmatch(utfcharsplitter_raw,str) +end + +-- 0 EF BB BF UTF-8 +-- 1 FF FE UTF-16-little-endian +-- 2 FE FF UTF-16-big-endian +-- 3 FF FE 00 00 UTF-32-little-endian +-- 4 00 00 FE FF UTF-32-big-endian +-- +-- \000 fails in <= 5.0 but is valid in >=5.1 where %z is depricated + +-- utf.name = { +-- [0] = 'utf-8', +-- [1] = 'utf-16-le', +-- [2] = 'utf-16-be', +-- [3] = 'utf-32-le', +-- [4] = 'utf-32-be' +-- } +-- +-- function utf.magic(f) +-- local str = f:read(4) +-- if not str then +-- f:seek('set') +-- return 0 +-- -- elseif find(str,"^%z%z\254\255") then -- depricated +-- -- elseif find(str,"^\000\000\254\255") then -- not permitted and bugged +-- elseif find(str,"\000\000\254\255",1,true) then -- seems to work okay (TH) +-- return 4 +-- -- elseif find(str,"^\255\254%z%z") then -- depricated +-- -- elseif find(str,"^\255\254\000\000") then -- not permitted and bugged +-- elseif find(str,"\255\254\000\000",1,true) then -- seems to work okay (TH) +-- return 3 +-- elseif find(str,"^\254\255") then +-- f:seek('set',2) +-- return 2 +-- elseif find(str,"^\255\254") then +-- f:seek('set',2) +-- return 1 +-- elseif find(str,"^\239\187\191") then +-- f:seek('set',3) +-- return 0 +-- else +-- f:seek('set') +-- return 0 +-- end +-- end + +function utf.magic(f) -- not used + local str = f:read(4) or "" + local off = lpegmatch(p_utfoffset,str) + if off < 4 then + f:seek('set',off) + end + return lpegmatch(p_utftype,str) +end + +local utf16_to_utf8_be, utf16_to_utf8_le +local utf32_to_utf8_be, utf32_to_utf8_le + +local utf_16_be_getbom = patterns.utfbom_16_be^-1 +local utf_16_le_getbom = patterns.utfbom_16_le^-1 +local utf_32_be_getbom = patterns.utfbom_32_be^-1 +local utf_32_le_getbom = patterns.utfbom_32_le^-1 + +local utf_16_be_linesplitter = utf_16_be_getbom * lpeg.tsplitat(patterns.utf_16_be_nl) +local utf_16_le_linesplitter = utf_16_le_getbom * lpeg.tsplitat(patterns.utf_16_le_nl) +local utf_32_be_linesplitter = utf_32_be_getbom * lpeg.tsplitat(patterns.utf_32_be_nl) +local utf_32_le_linesplitter = utf_32_le_getbom * lpeg.tsplitat(patterns.utf_32_le_nl) + +-- we have three possibilities: bytepairs (using tables), gmatch (using tables), gsub and +-- lpeg. Bytepairs are the fastert but as soon as we need to remove bombs and so the gain +-- is less due to more testing. Also, we seldom have to convert utf16 so we don't care to +-- much about a few milliseconds more runtime. The lpeg variant is upto 20% slower but +-- still pretty fast. +-- +-- for historic resone we keep the bytepairs variants around .. beware they don't grab the +-- bom like the lpegs do so they're not dropins in the functions that follow +-- +-- utf16_to_utf8_be = function(s) +-- if not s then +-- return nil +-- elseif s == "" then +-- return "" +-- end +-- local result, r, more = { }, 0, 0 +-- for left, right in bytepairs(s) do +-- if right then +-- local now = 256*left + right +-- if more > 0 then +-- now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong +-- more = 0 +-- r = r + 1 +-- result[r] = utfchar(now) +-- elseif now >= 0xD800 and now <= 0xDBFF then +-- more = now +-- else +-- r = r + 1 +-- result[r] = utfchar(now) +-- end +-- end +-- end +-- return concat(result) +-- end +-- +-- local utf16_to_utf8_be_t = function(t) +-- if not t then +-- return nil +-- elseif type(t) == "string" then +-- t = lpegmatch(utf_16_be_linesplitter,t) +-- end +-- local result = { } -- we reuse result +-- for i=1,#t do +-- local s = t[i] +-- if s ~= "" then +-- local r, more = 0, 0 +-- for left, right in bytepairs(s) do +-- if right then +-- local now = 256*left + right +-- if more > 0 then +-- now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong +-- more = 0 +-- r = r + 1 +-- result[r] = utfchar(now) +-- elseif now >= 0xD800 and now <= 0xDBFF then +-- more = now +-- else +-- r = r + 1 +-- result[r] = utfchar(now) +-- end +-- end +-- end +-- t[i] = concat(result,"",1,r) -- we reused tmp, hence t +-- end +-- end +-- return t +-- end +-- +-- utf16_to_utf8_le = function(s) +-- if not s then +-- return nil +-- elseif s == "" then +-- return "" +-- end +-- local result, r, more = { }, 0, 0 +-- for left, right in bytepairs(s) do +-- if right then +-- local now = 256*right + left +-- if more > 0 then +-- now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong +-- more = 0 +-- r = r + 1 +-- result[r] = utfchar(now) +-- elseif now >= 0xD800 and now <= 0xDBFF then +-- more = now +-- else +-- r = r + 1 +-- result[r] = utfchar(now) +-- end +-- end +-- end +-- return concat(result) +-- end +-- +-- local utf16_to_utf8_le_t = function(t) +-- if not t then +-- return nil +-- elseif type(t) == "string" then +-- t = lpegmatch(utf_16_le_linesplitter,t) +-- end +-- local result = { } -- we reuse result +-- for i=1,#t do +-- local s = t[i] +-- if s ~= "" then +-- local r, more = 0, 0 +-- for left, right in bytepairs(s) do +-- if right then +-- local now = 256*right + left +-- if more > 0 then +-- now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong +-- more = 0 +-- r = r + 1 +-- result[r] = utfchar(now) +-- elseif now >= 0xD800 and now <= 0xDBFF then +-- more = now +-- else +-- r = r + 1 +-- result[r] = utfchar(now) +-- end +-- end +-- end +-- t[i] = concat(result,"",1,r) -- we reused tmp, hence t +-- end +-- end +-- return t +-- end +-- +-- local utf32_to_utf8_be_t = function(t) +-- if not t then +-- return nil +-- elseif type(t) == "string" then +-- t = lpegmatch(utflinesplitter,t) +-- end +-- local result = { } -- we reuse result +-- for i=1,#t do +-- local r, more = 0, -1 +-- for a,b in bytepairs(t[i]) do +-- if a and b then +-- if more < 0 then +-- more = 256*256*256*a + 256*256*b +-- else +-- r = r + 1 +-- result[t] = utfchar(more + 256*a + b) +-- more = -1 +-- end +-- else +-- break +-- end +-- end +-- t[i] = concat(result,"",1,r) +-- end +-- return t +-- end +-- +-- local utf32_to_utf8_le_t = function(t) +-- if not t then +-- return nil +-- elseif type(t) == "string" then +-- t = lpegmatch(utflinesplitter,t) +-- end +-- local result = { } -- we reuse result +-- for i=1,#t do +-- local r, more = 0, -1 +-- for a,b in bytepairs(t[i]) do +-- if a and b then +-- if more < 0 then +-- more = 256*b + a +-- else +-- r = r + 1 +-- result[t] = utfchar(more + 256*256*256*b + 256*256*a) +-- more = -1 +-- end +-- else +-- break +-- end +-- end +-- t[i] = concat(result,"",1,r) +-- end +-- return t +-- end + +local more = 0 + +local p_utf16_to_utf8_be = C(1) * C(1) /function(left,right) + local now = 256*byte(left) + byte(right) + if more > 0 then + now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong + more = 0 + return utfchar(now) + elseif now >= 0xD800 and now <= 0xDBFF then + more = now + return "" -- else the c's end up in the stream + else + return utfchar(now) + end +end + +local p_utf16_to_utf8_le = C(1) * C(1) /function(right,left) + local now = 256*byte(left) + byte(right) + if more > 0 then + now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong + more = 0 + return utfchar(now) + elseif now >= 0xD800 and now <= 0xDBFF then + more = now + return "" -- else the c's end up in the stream + else + return utfchar(now) + end +end +local p_utf32_to_utf8_be = C(1) * C(1) * C(1) * C(1) /function(a,b,c,d) + return utfchar(256*256*256*byte(a) + 256*256*byte(b) + 256*byte(c) + byte(d)) +end + +local p_utf32_to_utf8_le = C(1) * C(1) * C(1) * C(1) /function(a,b,c,d) + return utfchar(256*256*256*byte(d) + 256*256*byte(c) + 256*byte(b) + byte(a)) +end + +p_utf16_to_utf8_be = P(true) / function() more = 0 end * utf_16_be_getbom * Cs(p_utf16_to_utf8_be^0) +p_utf16_to_utf8_le = P(true) / function() more = 0 end * utf_16_le_getbom * Cs(p_utf16_to_utf8_le^0) +p_utf32_to_utf8_be = P(true) / function() more = 0 end * utf_32_be_getbom * Cs(p_utf32_to_utf8_be^0) +p_utf32_to_utf8_le = P(true) / function() more = 0 end * utf_32_le_getbom * Cs(p_utf32_to_utf8_le^0) + +patterns.utf16_to_utf8_be = p_utf16_to_utf8_be +patterns.utf16_to_utf8_le = p_utf16_to_utf8_le +patterns.utf32_to_utf8_be = p_utf32_to_utf8_be +patterns.utf32_to_utf8_le = p_utf32_to_utf8_le + +utf16_to_utf8_be = function(s) + if s and s ~= "" then + return lpegmatch(p_utf16_to_utf8_be,s) + else + return s + end +end + +local utf16_to_utf8_be_t = function(t) + if not t then + return nil + elseif type(t) == "string" then + t = lpegmatch(utf_16_be_linesplitter,t) + end + for i=1,#t do + local s = t[i] + if s ~= "" then + t[i] = lpegmatch(p_utf16_to_utf8_be,s) + end + end + return t +end + +utf16_to_utf8_le = function(s) + if s and s ~= "" then + return lpegmatch(p_utf16_to_utf8_le,s) + else + return s + end +end + +local utf16_to_utf8_le_t = function(t) + if not t then + return nil + elseif type(t) == "string" then + t = lpegmatch(utf_16_le_linesplitter,t) + end + for i=1,#t do + local s = t[i] + if s ~= "" then + t[i] = lpegmatch(p_utf16_to_utf8_le,s) + end + end + return t +end + +utf32_to_utf8_be = function(s) + if s and s ~= "" then + return lpegmatch(p_utf32_to_utf8_be,s) + else + return s + end +end + +local utf32_to_utf8_be_t = function(t) + if not t then + return nil + elseif type(t) == "string" then + t = lpegmatch(utf_32_be_linesplitter,t) + end + for i=1,#t do + local s = t[i] + if s ~= "" then + t[i] = lpegmatch(p_utf32_to_utf8_be,s) + end + end + return t +end + +utf32_to_utf8_le = function(s) + if s and s ~= "" then + return lpegmatch(p_utf32_to_utf8_le,s) + else + return s + end +end + +local utf32_to_utf8_le_t = function(t) + if not t then + return nil + elseif type(t) == "string" then + t = lpegmatch(utf_32_le_linesplitter,t) + end + for i=1,#t do + local s = t[i] + if s ~= "" then + t[i] = lpegmatch(p_utf32_to_utf8_le,s) + end + end + return t +end + +utf.utf16_to_utf8_le_t = utf16_to_utf8_le_t +utf.utf16_to_utf8_be_t = utf16_to_utf8_be_t +utf.utf32_to_utf8_le_t = utf32_to_utf8_le_t +utf.utf32_to_utf8_be_t = utf32_to_utf8_be_t + +utf.utf16_to_utf8_le = utf16_to_utf8_le +utf.utf16_to_utf8_be = utf16_to_utf8_be +utf.utf32_to_utf8_le = utf32_to_utf8_le +utf.utf32_to_utf8_be = utf32_to_utf8_be + +function utf.utf8_to_utf8_t(t) + return type(t) == "string" and lpegmatch(utflinesplitter,t) or t +end + +function utf.utf16_to_utf8_t(t,endian) + return endian and utf16_to_utf8_be_t(t) or utf16_to_utf8_le_t(t) or t +end + +function utf.utf32_to_utf8_t(t,endian) + return endian and utf32_to_utf8_be_t(t) or utf32_to_utf8_le_t(t) or t +end + +local function little(b) + if b < 0x10000 then + return char(b%256,rshift(b,8)) + else + b = b - 0x10000 + local b1 = rshift(b,10) + 0xD800 + local b2 = b%1024 + 0xDC00 + return char(b1%256,rshift(b1,8),b2%256,rshift(b2,8)) + end +end + +local function big(b) + if b < 0x10000 then + return char(rshift(b,8),b%256) + else + b = b - 0x10000 + local b1 = rshift(b,10) + 0xD800 + local b2 = b%1024 + 0xDC00 + return char(rshift(b1,8),b1%256,rshift(b2,8),b2%256) + end +end + +local l_remap = Cs((p_utf8byte/little+P(1)/"")^0) +local b_remap = Cs((p_utf8byte/big +P(1)/"")^0) + +local function utf8_to_utf16_be(str,nobom) + if nobom then + return lpegmatch(b_remap,str) + else + return char(254,255) .. lpegmatch(b_remap,str) + end +end + +local function utf8_to_utf16_le(str,nobom) + if nobom then + return lpegmatch(l_remap,str) + else + return char(255,254) .. lpegmatch(l_remap,str) + end +end + +utf.utf8_to_utf16_be = utf8_to_utf16_be +utf.utf8_to_utf16_le = utf8_to_utf16_le + +function utf.utf8_to_utf16(str,littleendian,nobom) + if littleendian then + return utf8_to_utf16_le(str,nobom) + else + return utf8_to_utf16_be(str,nobom) + end +end + +-- function utf.tocodes(str,separator) -- can be sped up with an lpeg +-- local t, n = { }, 0 +-- for u in utfvalues(str) do +-- n = n + 1 +-- t[n] = format("0x%04X",u) +-- end +-- return concat(t,separator or " ") +-- end + +local pattern = Cs ( + (p_utf8byte / function(unicode ) return format( "0x%04X", unicode) end) * + (p_utf8byte * Carg(1) / function(unicode,separator) return format("%s0x%04X",separator,unicode) end)^0 +) + +function utf.tocodes(str,separator) + return lpegmatch(pattern,str,1,separator or " ") +end + +function utf.ustring(s) + return format("U+%05X",type(s) == "number" and s or utfbyte(s)) +end + +function utf.xstring(s) + return format("0x%05X",type(s) == "number" and s or utfbyte(s)) +end + +function utf.toeight(str) + if not str or str == "" then + return nil + end + local utftype = lpegmatch(p_utfstricttype,str) + if utftype == "utf-8" then + return sub(str,4) -- remove the bom + elseif utftype == "utf-16-be" then + return utf16_to_utf8_be(str) -- bom gets removed + elseif utftype == "utf-16-le" then + return utf16_to_utf8_le(str) -- bom gets removed + else + return str + end +end + +-- + +local p_nany = p_utf8char / "" + +if utfgmatch then + + function utf.count(str,what) + if type(what) == "string" then + local n = 0 + for _ in utfgmatch(str,what) do + n = n + 1 + end + return n + else -- 4 times slower but still faster than / function + return #lpegmatch(Cs((P(what)/" " + p_nany)^0),str) + end + end + +else + + local cache = { } + + function utf.count(str,what) + if type(what) == "string" then + local p = cache[what] + if not p then + p = Cs((P(what)/" " + p_nany)^0) + cache[p] = p + end + return #lpegmatch(p,str) + else -- 4 times slower but still faster than / function + return #lpegmatch(Cs((P(what)/" " + p_nany)^0),str) + end + end + +end + +-- maybe also register as string.utf* + + +if not utf.characters then + + -- New: this gmatch hack is taken from the Lua 5.2 book. It's about two times slower + -- than the built-in string.utfcharacters. + + function utf.characters(str) + return gmatch(str,".[\128-\191]*") + end + + string.utfcharacters = utf.characters + +end + +if not utf.values then + + -- So, a logical next step is to check for the values variant. It over five times + -- slower than the built-in string.utfvalues. I optimized it a bit for n=0,1. + + ----- wrap, yield, gmatch = coroutine.wrap, coroutine.yield, string.gmatch + local find = string.find + + local dummy = function() + -- we share this one + end + + -- function utf.values(str) + -- local n = #str + -- if n == 0 then + -- return wrap(dummy) + -- elseif n == 1 then + -- return wrap(function() yield(utfbyte(str)) end) + -- else + -- return wrap(function() for s in gmatch(str,".[\128-\191]*") do + -- yield(utfbyte(s)) + -- end end) + -- end + -- end + -- + -- faster: + + function utf.values(str) + local n = #str + if n == 0 then + return dummy + elseif n == 1 then + return function() return utfbyte(str) end + else + local p = 1 + -- local n = #str + return function() + -- if p <= n then -- slower than the last find + local b, e = find(str,".[\128-\191]*",p) + if b then + p = e + 1 + return utfbyte(sub(str,b,e)) + end + -- end + end + end + end + + -- slower: + -- + -- local pattern = C(patterns.utf8character) * Cp() + -- ----- pattern = patterns.utf8character/utfbyte * Cp() + -- ----- pattern = patterns.utf8byte * Cp() + -- + -- function utf.values(str) -- one of the cases where a find is faster than an lpeg + -- local n = #str + -- if n == 0 then + -- return dummy + -- elseif n == 1 then + -- return function() return utfbyte(str) end + -- else + -- local p = 1 + -- return function() + -- local s, e = lpegmatch(pattern,str,p) + -- if e then + -- p = e + -- return utfbyte(s) + -- -- return s + -- end + -- end + -- end + -- end + + string.utfvalues = utf.values + +end + +function utf.chrlen(u) -- u is number + return + (u < 0x80 and 1) or + (u < 0xE0 and 2) or + (u < 0xF0 and 3) or + (u < 0xF8 and 4) or + (u < 0xFC and 5) or + (u < 0xFE and 6) or 0 +end + +-- hashing saves a little but not that much in practice +-- +-- local utf32 = table.setmetatableindex(function(t,k) local v = toutf32(k) t[k] = v return v end) + +if bit32 then + + local extract = bit32.extract + local char = string.char + + function unicode.toutf32string(n) + if n <= 0xFF then + return + char(n) .. + "\000\000\000" + elseif n <= 0xFFFF then + return + char(extract(n, 0,8)) .. + char(extract(n, 8,8)) .. + "\000\000" + elseif n <= 0xFFFFFF then + return + char(extract(n, 0,8)) .. + char(extract(n, 8,8)) .. + char(extract(n,16,8)) .. + "\000" + else + return + char(extract(n, 0,8)) .. + char(extract(n, 8,8)) .. + char(extract(n,16,8)) .. + char(extract(n,24,8)) + end + end + +end + +-- goodie: + +local len = utf.len +local rep = rep + +function string.utfpadd(s,n) + if n and n ~= 0 then + local l = len(s) + if n > 0 then + local d = n - l + if d > 0 then + return rep(c or " ",d) .. s + end + else + local d = - n - l + if d > 0 then + return s .. rep(c or " ",d) + end + end + end + return s +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-languages.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-languages.lua deleted file mode 100644 index cecd60c1331..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-languages.lua +++ /dev/null @@ -1,50 +0,0 @@ -if not modules then modules = { } end modules ['luatex-languages'] = { - version = 1.001, - comment = "companion to luatex-languages.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - --- We borrow from ConTeXt. - -languages = languages or { } - -local loaded = { } - -function languages.loadpatterns(tag) - if not loaded[tag] then - loaded[tag] = 0 - local filename = kpse.find_file("lang-" .. tag .. ".lua") - if not filename or filename == "" then - texio.write("") - else - local whatever = loadfile(filename) - if type(whatever) == "function" then - whatever = whatever() - if type(whatever) == "table" then - texio.write("") - local characters = whatever.patterns.characters or "" - local patterns = whatever.patterns.data or "" - local exceptions = whatever.exceptions.data or "" - for b in string.utfvalues(characters) do - -- what about uppercase --- lang.sethjcode(b,b) - tex.setlccode(b,b) - end - local language = lang.new() - lang.patterns(language, patterns) - lang.hyphenation(language, exceptions) - loaded[tag] = lang.id(language) - else - texio.write("") - os.exit() - end - else - texio.write("") - os.exit() - end - end - end - return loaded[tag] -end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-languages.tex b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-languages.tex deleted file mode 100644 index a087b30939a..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-languages.tex +++ /dev/null @@ -1,19 +0,0 @@ -%D \module -%D [ file=luatex-fonts, -%D version=2009.12.01, -%D title=\LUATEX\ Support Macros, -%D subtitle=Generic \OPENTYPE\ Font Handler, -%D author=Hans Hagen, -%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] - -%D Cf. discussion on \CONTEXT\ list: - -% \savinghyphcodes1 - -\directlua { - dofile(kpse.find_file("luatex-languages.lua","tex")) -} - -\def\loadpatterns#1{\directlua{tex.language = languages.loadpatterns("#1")}} - -\endinput diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-math.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-math.lua deleted file mode 100644 index c316182ba32..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-math.lua +++ /dev/null @@ -1,53 +0,0 @@ -if not modules then modules = { } end modules ['luatex-math'] = { - version = 1.001, - comment = "companion to luatex-math.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -local gaps = { - [0x1D455] = 0x0210E, - [0x1D49D] = 0x0212C, - [0x1D4A0] = 0x02130, - [0x1D4A1] = 0x02131, - [0x1D4A3] = 0x0210B, - [0x1D4A4] = 0x02110, - [0x1D4A7] = 0x02112, - [0x1D4A8] = 0x02133, - [0x1D4AD] = 0x0211B, - [0x1D4BA] = 0x0212F, - [0x1D4BC] = 0x0210A, - [0x1D4C4] = 0x02134, - [0x1D506] = 0x0212D, - [0x1D50B] = 0x0210C, - [0x1D50C] = 0x02111, - [0x1D515] = 0x0211C, - [0x1D51D] = 0x02128, - [0x1D53A] = 0x02102, - [0x1D53F] = 0x0210D, - [0x1D545] = 0x02115, - [0x1D547] = 0x02119, - [0x1D548] = 0x0211A, - [0x1D549] = 0x0211D, - [0x1D551] = 0x02124, -} - - -local function fixmath(tfmdata,key,value) - if value then - local characters = tfmdata.characters - for gap, mess in pairs(gaps) do - characters[gap] = characters[mess] - end - end -end - -fonts.handlers.otf.features.register { - name = "fixmath", - description = "math font fixing", - manipulators = { - base = fixmath, - node = fixmath, - } -} diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-math.tex b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-math.tex deleted file mode 100644 index b24902100a1..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-math.tex +++ /dev/null @@ -1,1874 +0,0 @@ -%D \module -%D [ file=luatex-math, -%D version=2013.04.29, -%D title=\LUATEX\ Support Macros, -%D subtitle=An exmaple of math, -%D author=Hans Hagen, -%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] - -%D This module is in no way a complete plain math implementation. I made this file -%D because I needed it for a tutorial for (mostly) plain \TEX\ users. There are -%D several ways to support math in \LUATEX, and this is just one of them. It was the -%D quickest hack I could come up with and it stays somewhat close to the traditional -%D approach (and thereby far from the \CONTEXT\ way). This file is mainly meant for -%D Boguslaw Jackowski. - -% we provide a remap feature - -\directlua{dofile(kpse.find_file('luatex-math.lua'))} - -% a bunch of fonts: - -\let \teni = \relax -\let \seveni = \relax -\let \fivei = \relax -\let \tensy = \relax -\let \sevensy = \relax -\let \fivesy = \relax -\let \tenex = \relax -\let \sevenbf = \relax -\let \fivebf = \relax - -\def\latinmodern - {\font\tenrm = file:lmroman10-regular.otf:+liga;+kern;+tlig;+trep at 10pt - \font\sevenrm = file:lmroman7-regular.otf:+liga;+kern;+tlig;+trep at 7pt - \font\fiverm = file:lmroman5-regular.otf:+liga;+kern;+tlig;+trep at 5pt - % - \font\tentt = file:lmmono10-regular.otf at 10pt - \font\tensl = file:lmromanslant10-regular.otf:+liga;+kern;+tlig;+trep at 10pt - \font\tenit = file:lmroman10-italic.otf:+liga;+kern;+tlig;+trep at 10pt - \font\tenbf = file:lmroman10-bold.otf:+liga;+kern;+tlig;+trep at 10pt - \font\tenbi = file:lmroman10-bolditalic.otf:+liga;+kern;+tlig;+trep at 10pt - % - \font\mathfonttextupright = file:latinmodern-math.otf:script=math;ssty=0;mathsize=yes at 10pt - \font\mathfontscriptupright = file:latinmodern-math.otf:script=math;ssty=1;mathsize=yes at 7pt - \font\mathfontscriptscriptupright = file:latinmodern-math.otf:script=math;ssty=2;mathsize=yes at 5pt - % - \textfont 0 = \mathfonttextupright - \scriptfont 0 = \mathfontscriptupright - \scriptscriptfont 0 = \mathfontscriptscriptupright - % - \tenrm} - -\def\lucidabright - {\font\tenrm = file:lucidabrightot.otf:+liga;+kern;+tlig;+trep at 10pt - \font\sevenrm = file:lucidabrightot.otf:+liga;+kern;+tlig;+trep at 7pt - \font\fiverm = file:lucidabrightot.otf:+liga;+kern;+tlig;+trep at 5pt - % - \font\tentt = file:lucidabrightot.otf at 10pt - \font\tenit = file:lucidabrightot.otf:+liga;+kern;+tlig;+trep at 10pt - \font\tenit = file:lucidabrightot-italic.otf:+liga;+kern;+tlig;+trep at 10pt - \font\tenbf = file:lucidabrightot-demi.otf:+liga;+kern;+tlig;+trep at 10pt - \font\tenbi = file:lucidabrightot-demiitalic.otf:+liga;+kern;+tlig;+trep at 10pt - % - \font\mathfonttextupright = file:lucidabrightmathot.otf:script=math;ssty=0;mathsize=yes at 10pt - \font\mathfontscriptupright = file:lucidabrightmathot.otf:script=math;ssty=1;mathsize=yes at 7pt - \font\mathfontscriptscriptupright = file:lucidabrightmathot.otf:script=math;ssty=2;mathsize=yes at 5pt - % - \textfont 0 = \mathfonttextupright - \scriptfont 0 = \mathfontscriptupright - \scriptscriptfont 0 = \mathfontscriptscriptupright - % - \tenrm} - -\directlua { - if arguments["mtx:lucidabright"] then - tex.print("\string\\lucidabright") - else - tex.print("\string\\latinmodern") - end -} - -\newtoks\everymathrm -\newtoks\everymathmit -\newtoks\everymathcal -\newtoks\everymathit -\newtoks\everymathsl -\newtoks\everymathbf -\newtoks\everymathbi -\newtoks\everymathtt - -\def\rm{\fam0\relax\the\everymathrm\relax\tenrm\relax} -\def\it{\fam0\relax\the\everymathit\relax\tenit\relax} -\def\sl{\fam0\relax\the\everymathsl\relax\tensl\relax} -\def\bf{\fam0\relax\the\everymathbf\relax\tenbf\relax} -\def\bi{\fam0\relax\the\everymathbi\relax\tenbi\relax} -\def\tt{\fam0\relax\the\everymathtt\relax\tentt\relax} - -\let\mit \relax % use names or \Uchar or define a vector -\let\cal \relax % idem, i'm not in the mood for this now -\let\oldstyle\relax % no longer misuse of math mode - -% tex is fast enough for this kind of assignments: - -\everymathrm {% - \Umathcode"0041="0"0"0041% - \Umathcode"0042="0"0"0042% - \Umathcode"0043="0"0"0043% - \Umathcode"0044="0"0"0044% - \Umathcode"0045="0"0"0045% - \Umathcode"0046="0"0"0046% - \Umathcode"0047="0"0"0047% - \Umathcode"0048="0"0"0048% - \Umathcode"0049="0"0"0049% - \Umathcode"004A="0"0"004A% - \Umathcode"004B="0"0"004B% - \Umathcode"004C="0"0"004C% - \Umathcode"004D="0"0"004D% - \Umathcode"004E="0"0"004E% - \Umathcode"004F="0"0"004F% - \Umathcode"0050="0"0"0050% - \Umathcode"0051="0"0"0051% - \Umathcode"0052="0"0"0052% - \Umathcode"0053="0"0"0053% - \Umathcode"0054="0"0"0054% - \Umathcode"0055="0"0"0055% - \Umathcode"0056="0"0"0056% - \Umathcode"0057="0"0"0057% - \Umathcode"0058="0"0"0058% - \Umathcode"0059="0"0"0059% - \Umathcode"005A="0"0"005A% - \Umathcode"0061="0"0"0061% - \Umathcode"0062="0"0"0062% - \Umathcode"0063="0"0"0063% - \Umathcode"0064="0"0"0064% - \Umathcode"0065="0"0"0065% - \Umathcode"0066="0"0"0066% - \Umathcode"0067="0"0"0067% - \Umathcode"0068="0"0"0068% - \Umathcode"0069="0"0"0069% - \Umathcode"006A="0"0"006A% - \Umathcode"006B="0"0"006B% - \Umathcode"006C="0"0"006C% - \Umathcode"006D="0"0"006D% - \Umathcode"006E="0"0"006E% - \Umathcode"006F="0"0"006F% - \Umathcode"0070="0"0"0070% - \Umathcode"0071="0"0"0071% - \Umathcode"0072="0"0"0072% - \Umathcode"0073="0"0"0073% - \Umathcode"0074="0"0"0074% - \Umathcode"0075="0"0"0075% - \Umathcode"0076="0"0"0076% - \Umathcode"0077="0"0"0077% - \Umathcode"0078="0"0"0078% - \Umathcode"0079="0"0"0079% - \Umathcode"007A="0"0"007A% - \Umathcode"0391="0"0"0391% - \Umathcode"0392="0"0"0392% - \Umathcode"0393="0"0"0393% - \Umathcode"0394="0"0"0394% - \Umathcode"0395="0"0"0395% - \Umathcode"0396="0"0"0396% - \Umathcode"0397="0"0"0397% - \Umathcode"0398="0"0"0398% - \Umathcode"0399="0"0"0399% - \Umathcode"039A="0"0"039A% - \Umathcode"039B="0"0"039B% - \Umathcode"039C="0"0"039C% - \Umathcode"039D="0"0"039D% - \Umathcode"039E="0"0"039E% - \Umathcode"039F="0"0"039F% - \Umathcode"03A0="0"0"03A0% - \Umathcode"03A1="0"0"03A1% - \Umathcode"03A3="0"0"03A3% - \Umathcode"03A4="0"0"03A4% - \Umathcode"03A5="0"0"03A5% - \Umathcode"03A6="0"0"03A6% - \Umathcode"03A7="0"0"03A7% - \Umathcode"03A8="0"0"03A8% - \Umathcode"03A9="0"0"03A9% - \Umathcode"03B1="0"0"03B1% - \Umathcode"03B2="0"0"03B2% - \Umathcode"03B3="0"0"03B3% - \Umathcode"03B4="0"0"03B4% - \Umathcode"03B5="0"0"03B5% - \Umathcode"03B6="0"0"03B6% - \Umathcode"03B7="0"0"03B7% - \Umathcode"03B8="0"0"03B8% - \Umathcode"03B9="0"0"03B9% - \Umathcode"03BA="0"0"03BA% - \Umathcode"03BB="0"0"03BB% - \Umathcode"03BC="0"0"03BC% - \Umathcode"03BD="0"0"03BD% - \Umathcode"03BE="0"0"03BE% - \Umathcode"03BF="0"0"03BF% - \Umathcode"03C0="0"0"03C0% - \Umathcode"03C1="0"0"03C1% - \Umathcode"03C2="0"0"03C2% - \Umathcode"03C3="0"0"03C3% - \Umathcode"03C4="0"0"03C4% - \Umathcode"03C5="0"0"03C5% - \Umathcode"03C6="0"0"03C6% - \Umathcode"03C7="0"0"03C7% - \Umathcode"03C8="0"0"03C8% - \Umathcode"03C9="0"0"03C9% - \Umathcode"03D1="0"0"03D1% - \Umathcode"03D5="0"0"03D5% - \Umathcode"03D6="0"0"03D6% - \Umathcode"03F0="0"0"03F0% - \Umathcode"03F1="0"0"03F1% - \Umathcode"03F4="0"0"03F4% - \Umathcode"03F5="0"0"03F5% - \Umathcode"2202="0"0"2202% - \Umathcode"2207="0"0"2207% - \relax -} - -\everymathmit {% - % not done -} - -\everymathcal {% - % not done -} - -\everymathit {% - \Umathcode"0041="0"0"1D434% - \Umathcode"0042="0"0"1D435% - \Umathcode"0043="0"0"1D436% - \Umathcode"0044="0"0"1D437% - \Umathcode"0045="0"0"1D438% - \Umathcode"0046="0"0"1D439% - \Umathcode"0047="0"0"1D43A% - \Umathcode"0048="0"0"1D43B% - \Umathcode"0049="0"0"1D43C% - \Umathcode"004A="0"0"1D43D% - \Umathcode"004B="0"0"1D43E% - \Umathcode"004C="0"0"1D43F% - \Umathcode"004D="0"0"1D440% - \Umathcode"004E="0"0"1D441% - \Umathcode"004F="0"0"1D442% - \Umathcode"0050="0"0"1D443% - \Umathcode"0051="0"0"1D444% - \Umathcode"0052="0"0"1D445% - \Umathcode"0053="0"0"1D446% - \Umathcode"0054="0"0"1D447% - \Umathcode"0055="0"0"1D448% - \Umathcode"0056="0"0"1D449% - \Umathcode"0057="0"0"1D44A% - \Umathcode"0058="0"0"1D44B% - \Umathcode"0059="0"0"1D44C% - \Umathcode"005A="0"0"1D44D% - \Umathcode"0061="0"0"1D44E% - \Umathcode"0062="0"0"1D44F% - \Umathcode"0063="0"0"1D450% - \Umathcode"0064="0"0"1D451% - \Umathcode"0065="0"0"1D452% - \Umathcode"0066="0"0"1D453% - \Umathcode"0067="0"0"1D454% - \Umathcode"0068="0"0"0210E% - \Umathcode"0069="0"0"1D456% - \Umathcode"006A="0"0"1D457% - \Umathcode"006B="0"0"1D458% - \Umathcode"006C="0"0"1D459% - \Umathcode"006D="0"0"1D45A% - \Umathcode"006E="0"0"1D45B% - \Umathcode"006F="0"0"1D45C% - \Umathcode"0070="0"0"1D45D% - \Umathcode"0071="0"0"1D45E% - \Umathcode"0072="0"0"1D45F% - \Umathcode"0073="0"0"1D460% - \Umathcode"0074="0"0"1D461% - \Umathcode"0075="0"0"1D462% - \Umathcode"0076="0"0"1D463% - \Umathcode"0077="0"0"1D464% - \Umathcode"0078="0"0"1D465% - \Umathcode"0079="0"0"1D466% - \Umathcode"007A="0"0"1D467% - \Umathcode"0391="0"0"1D6E2% - \Umathcode"0392="0"0"1D6E3% - \Umathcode"0393="0"0"1D6E4% - \Umathcode"0394="0"0"1D6E5% - \Umathcode"0395="0"0"1D6E6% - \Umathcode"0396="0"0"1D6E7% - \Umathcode"0397="0"0"1D6E8% - \Umathcode"0398="0"0"1D6E9% - \Umathcode"0399="0"0"1D6EA% - \Umathcode"039A="0"0"1D6EB% - \Umathcode"039B="0"0"1D6EC% - \Umathcode"039C="0"0"1D6ED% - \Umathcode"039D="0"0"1D6EE% - \Umathcode"039E="0"0"1D6EF% - \Umathcode"039F="0"0"1D6F0% - \Umathcode"03A0="0"0"1D6F1% - \Umathcode"03A1="0"0"1D6F2% - \Umathcode"03A3="0"0"1D6F4% - \Umathcode"03A4="0"0"1D6F5% - \Umathcode"03A5="0"0"1D6F6% - \Umathcode"03A6="0"0"1D6F7% - \Umathcode"03A7="0"0"1D6F8% - \Umathcode"03A8="0"0"1D6F9% - \Umathcode"03A9="0"0"1D6FA% - \Umathcode"03B1="0"0"1D6FC% - \Umathcode"03B2="0"0"1D6FD% - \Umathcode"03B3="0"0"1D6FE% - \Umathcode"03B4="0"0"1D6FF% - \Umathcode"03B5="0"0"1D700% - \Umathcode"03B6="0"0"1D701% - \Umathcode"03B7="0"0"1D702% - \Umathcode"03B8="0"0"1D703% - \Umathcode"03B9="0"0"1D704% - \Umathcode"03BA="0"0"1D705% - \Umathcode"03BB="0"0"1D706% - \Umathcode"03BC="0"0"1D707% - \Umathcode"03BD="0"0"1D708% - \Umathcode"03BE="0"0"1D709% - \Umathcode"03BF="0"0"1D70A% - \Umathcode"03C0="0"0"1D70B% - \Umathcode"03C1="0"0"1D70C% - \Umathcode"03C2="0"0"1D70D% - \Umathcode"03C3="0"0"1D70E% - \Umathcode"03C4="0"0"1D70F% - \Umathcode"03C5="0"0"1D710% - \Umathcode"03C6="0"0"1D711% - \Umathcode"03C7="0"0"1D712% - \Umathcode"03C8="0"0"1D713% - \Umathcode"03C9="0"0"1D714% - \Umathcode"03D1="0"0"1D717% - \Umathcode"03D5="0"0"1D719% - \Umathcode"03D6="0"0"1D71B% - \Umathcode"03F0="0"0"1D718% - \Umathcode"03F1="0"0"1D71A% - \Umathcode"03F4="0"0"1D6F3% - \Umathcode"03F5="0"0"1D716% - \Umathcode"2202="0"0"1D715% - \Umathcode"2207="0"0"1D6FB% - \relax -} - -\everymathsl {% - \the\everymathit -} - -\everymathbf {% - \Umathcode"0030="0"0"1D7CE% - \Umathcode"0031="0"0"1D7CF% - \Umathcode"0032="0"0"1D7D0% - \Umathcode"0033="0"0"1D7D1% - \Umathcode"0034="0"0"1D7D2% - \Umathcode"0035="0"0"1D7D3% - \Umathcode"0036="0"0"1D7D4% - \Umathcode"0037="0"0"1D7D5% - \Umathcode"0038="0"0"1D7D6% - \Umathcode"0039="0"0"1D7D7% - \Umathcode"0041="0"0"1D400% - \Umathcode"0042="0"0"1D401% - \Umathcode"0043="0"0"1D402% - \Umathcode"0044="0"0"1D403% - \Umathcode"0045="0"0"1D404% - \Umathcode"0046="0"0"1D405% - \Umathcode"0047="0"0"1D406% - \Umathcode"0048="0"0"1D407% - \Umathcode"0049="0"0"1D408% - \Umathcode"004A="0"0"1D409% - \Umathcode"004B="0"0"1D40A% - \Umathcode"004C="0"0"1D40B% - \Umathcode"004D="0"0"1D40C% - \Umathcode"004E="0"0"1D40D% - \Umathcode"004F="0"0"1D40E% - \Umathcode"0050="0"0"1D40F% - \Umathcode"0051="0"0"1D410% - \Umathcode"0052="0"0"1D411% - \Umathcode"0053="0"0"1D412% - \Umathcode"0054="0"0"1D413% - \Umathcode"0055="0"0"1D414% - \Umathcode"0056="0"0"1D415% - \Umathcode"0057="0"0"1D416% - \Umathcode"0058="0"0"1D417% - \Umathcode"0059="0"0"1D418% - \Umathcode"005A="0"0"1D419% - \Umathcode"0061="0"0"1D41A% - \Umathcode"0062="0"0"1D41B% - \Umathcode"0063="0"0"1D41C% - \Umathcode"0064="0"0"1D41D% - \Umathcode"0065="0"0"1D41E% - \Umathcode"0066="0"0"1D41F% - \Umathcode"0067="0"0"1D420% - \Umathcode"0068="0"0"1D421% - \Umathcode"0069="0"0"1D422% - \Umathcode"006A="0"0"1D423% - \Umathcode"006B="0"0"1D424% - \Umathcode"006C="0"0"1D425% - \Umathcode"006D="0"0"1D426% - \Umathcode"006E="0"0"1D427% - \Umathcode"006F="0"0"1D428% - \Umathcode"0070="0"0"1D429% - \Umathcode"0071="0"0"1D42A% - \Umathcode"0072="0"0"1D42B% - \Umathcode"0073="0"0"1D42C% - \Umathcode"0074="0"0"1D42D% - \Umathcode"0075="0"0"1D42E% - \Umathcode"0076="0"0"1D42F% - \Umathcode"0077="0"0"1D430% - \Umathcode"0078="0"0"1D431% - \Umathcode"0079="0"0"1D432% - \Umathcode"007A="0"0"1D433% - \Umathcode"0391="0"0"1D6A8% - \Umathcode"0392="0"0"1D6A9% - \Umathcode"0393="0"0"1D6AA% - \Umathcode"0394="0"0"1D6AB% - \Umathcode"0395="0"0"1D6AC% - \Umathcode"0396="0"0"1D6AD% - \Umathcode"0397="0"0"1D6AE% - \Umathcode"0398="0"0"1D6AF% - \Umathcode"0399="0"0"1D6B0% - \Umathcode"039A="0"0"1D6B1% - \Umathcode"039B="0"0"1D6B2% - \Umathcode"039C="0"0"1D6B3% - \Umathcode"039D="0"0"1D6B4% - \Umathcode"039E="0"0"1D6B5% - \Umathcode"039F="0"0"1D6B6% - \Umathcode"03A0="0"0"1D6B7% - \Umathcode"03A1="0"0"1D6B8% - \Umathcode"03A3="0"0"1D6BA% - \Umathcode"03A4="0"0"1D6BB% - \Umathcode"03A5="0"0"1D6BC% - \Umathcode"03A6="0"0"1D6BD% - \Umathcode"03A7="0"0"1D6BE% - \Umathcode"03A8="0"0"1D6BF% - \Umathcode"03A9="0"0"1D6C0% - \Umathcode"03B1="0"0"1D6C2% - \Umathcode"03B2="0"0"1D6C3% - \Umathcode"03B3="0"0"1D6C4% - \Umathcode"03B4="0"0"1D6C5% - \Umathcode"03B5="0"0"1D6C6% - \Umathcode"03B6="0"0"1D6C7% - \Umathcode"03B7="0"0"1D6C8% - \Umathcode"03B8="0"0"1D6C9% - \Umathcode"03B9="0"0"1D6CA% - \Umathcode"03BA="0"0"1D6CB% - \Umathcode"03BB="0"0"1D6CC% - \Umathcode"03BC="0"0"1D6CD% - \Umathcode"03BD="0"0"1D6CE% - \Umathcode"03BE="0"0"1D6CF% - \Umathcode"03BF="0"0"1D6D0% - \Umathcode"03C0="0"0"1D6D1% - \Umathcode"03C1="0"0"1D6D2% - \Umathcode"03C2="0"0"1D6D3% - \Umathcode"03C3="0"0"1D6D4% - \Umathcode"03C4="0"0"1D6D5% - \Umathcode"03C5="0"0"1D6D6% - \Umathcode"03C6="0"0"1D6D7% - \Umathcode"03C7="0"0"1D6D8% - \Umathcode"03C8="0"0"1D6D9% - \Umathcode"03C9="0"0"1D6DA% - \Umathcode"03D1="0"0"1D6DD% - \Umathcode"03D5="0"0"1D6DF% - \Umathcode"03D6="0"0"1D6E1% - \Umathcode"03F0="0"0"1D6DE% - \Umathcode"03F1="0"0"1D6E0% - \Umathcode"03F4="0"0"1D6B9% - \Umathcode"03F5="0"0"1D6DC% - \Umathcode"2202="0"0"1D6DB% - \Umathcode"2207="0"0"1D6C1% - \relax -} - -\everymathbi {% - \Umathcode"0030="0"0"1D7CE% - \Umathcode"0031="0"0"1D7CF% - \Umathcode"0032="0"0"1D7D0% - \Umathcode"0033="0"0"1D7D1% - \Umathcode"0034="0"0"1D7D2% - \Umathcode"0035="0"0"1D7D3% - \Umathcode"0036="0"0"1D7D4% - \Umathcode"0037="0"0"1D7D5% - \Umathcode"0038="0"0"1D7D6% - \Umathcode"0039="0"0"1D7D7% - \Umathcode"0041="0"0"1D468% - \Umathcode"0042="0"0"1D469% - \Umathcode"0043="0"0"1D46A% - \Umathcode"0044="0"0"1D46B% - \Umathcode"0045="0"0"1D46C% - \Umathcode"0046="0"0"1D46D% - \Umathcode"0047="0"0"1D46E% - \Umathcode"0048="0"0"1D46F% - \Umathcode"0049="0"0"1D470% - \Umathcode"004A="0"0"1D471% - \Umathcode"004B="0"0"1D472% - \Umathcode"004C="0"0"1D473% - \Umathcode"004D="0"0"1D474% - \Umathcode"004E="0"0"1D475% - \Umathcode"004F="0"0"1D476% - \Umathcode"0050="0"0"1D477% - \Umathcode"0051="0"0"1D478% - \Umathcode"0052="0"0"1D479% - \Umathcode"0053="0"0"1D47A% - \Umathcode"0054="0"0"1D47B% - \Umathcode"0055="0"0"1D47C% - \Umathcode"0056="0"0"1D47D% - \Umathcode"0057="0"0"1D47E% - \Umathcode"0058="0"0"1D47F% - \Umathcode"0059="0"0"1D480% - \Umathcode"005A="0"0"1D481% - \Umathcode"0061="0"0"1D482% - \Umathcode"0062="0"0"1D483% - \Umathcode"0063="0"0"1D484% - \Umathcode"0064="0"0"1D485% - \Umathcode"0065="0"0"1D486% - \Umathcode"0066="0"0"1D487% - \Umathcode"0067="0"0"1D488% - \Umathcode"0068="0"0"1D489% - \Umathcode"0069="0"0"1D48A% - \Umathcode"006A="0"0"1D48B% - \Umathcode"006B="0"0"1D48C% - \Umathcode"006C="0"0"1D48D% - \Umathcode"006D="0"0"1D48E% - \Umathcode"006E="0"0"1D48F% - \Umathcode"006F="0"0"1D490% - \Umathcode"0070="0"0"1D491% - \Umathcode"0071="0"0"1D492% - \Umathcode"0072="0"0"1D493% - \Umathcode"0073="0"0"1D494% - \Umathcode"0074="0"0"1D495% - \Umathcode"0075="0"0"1D496% - \Umathcode"0076="0"0"1D497% - \Umathcode"0077="0"0"1D498% - \Umathcode"0078="0"0"1D499% - \Umathcode"0079="0"0"1D49A% - \Umathcode"007A="0"0"1D49B% - \Umathcode"0391="0"0"1D71C% - \Umathcode"0392="0"0"1D71D% - \Umathcode"0393="0"0"1D71E% - \Umathcode"0394="0"0"1D71F% - \Umathcode"0395="0"0"1D720% - \Umathcode"0396="0"0"1D721% - \Umathcode"0397="0"0"1D722% - \Umathcode"0398="0"0"1D723% - \Umathcode"0399="0"0"1D724% - \Umathcode"039A="0"0"1D725% - \Umathcode"039B="0"0"1D726% - \Umathcode"039C="0"0"1D727% - \Umathcode"039D="0"0"1D728% - \Umathcode"039E="0"0"1D729% - \Umathcode"039F="0"0"1D72A% - \Umathcode"03A0="0"0"1D72B% - \Umathcode"03A1="0"0"1D72C% - \Umathcode"03A3="0"0"1D72E% - \Umathcode"03A4="0"0"1D72F% - \Umathcode"03A5="0"0"1D730% - \Umathcode"03A6="0"0"1D731% - \Umathcode"03A7="0"0"1D732% - \Umathcode"03A8="0"0"1D733% - \Umathcode"03A9="0"0"1D734% - \Umathcode"03B1="0"0"1D736% - \Umathcode"03B2="0"0"1D737% - \Umathcode"03B3="0"0"1D738% - \Umathcode"03B4="0"0"1D739% - \Umathcode"03B5="0"0"1D73A% - \Umathcode"03B6="0"0"1D73B% - \Umathcode"03B7="0"0"1D73C% - \Umathcode"03B8="0"0"1D73D% - \Umathcode"03B9="0"0"1D73E% - \Umathcode"03BA="0"0"1D73F% - \Umathcode"03BB="0"0"1D740% - \Umathcode"03BC="0"0"1D741% - \Umathcode"03BD="0"0"1D742% - \Umathcode"03BE="0"0"1D743% - \Umathcode"03BF="0"0"1D744% - \Umathcode"03C0="0"0"1D745% - \Umathcode"03C1="0"0"1D746% - \Umathcode"03C2="0"0"1D747% - \Umathcode"03C3="0"0"1D748% - \Umathcode"03C4="0"0"1D749% - \Umathcode"03C5="0"0"1D74A% - \Umathcode"03C6="0"0"1D74B% - \Umathcode"03C7="0"0"1D74C% - \Umathcode"03C8="0"0"1D74D% - \Umathcode"03C9="0"0"1D74E% - \Umathcode"03D1="0"0"1D751% - \Umathcode"03D5="0"0"1D753% - \Umathcode"03D6="0"0"1D755% - \Umathcode"03F0="0"0"1D752% - \Umathcode"03F1="0"0"1D754% - \Umathcode"03F4="0"0"1D72D% - \Umathcode"03F5="0"0"1D750% - \Umathcode"2202="0"0"1D74F% - \Umathcode"2207="0"0"1D735% - \relax -} - -\everymathtt {% - % not done -} - -\Udelcode "00021 = "0 "00021 -\Udelcode "00028 = "0 "00028 -\Udelcode "00028 = "0 "00028 -\Udelcode "00029 = "0 "00029 -\Udelcode "00029 = "0 "00029 -\Udelcode "0002F = "0 "0002F -\Udelcode "0002F = "0 "0002F -\Udelcode "0002F = "0 "02044 -\Udelcode "0003F = "0 "0003F -\Udelcode "0005B = "0 "0005B -\Udelcode "0005B = "0 "0005B -\Udelcode "0005D = "0 "0005D -\Udelcode "0005D = "0 "0005D -\Udelcode "0007B = "0 "0007B -\Udelcode "0007B = "0 "0007B -\Udelcode "0007C = "0 "0007C -\Udelcode "0007C = "0 "0007C -\Udelcode "0007C = "0 "0007C -\Udelcode "0007C = "0 "0007C -\Udelcode "0007C = "0 "0007C -\Udelcode "0007D = "0 "0007D -\Udelcode "0007D = "0 "0007D -\Udelcode "02016 = "0 "02016 -\Udelcode "02016 = "0 "02016 -\Udelcode "02016 = "0 "02016 -\Udelcode "02016 = "0 "02016 -\Udelcode "02016 = "0 "02016 -\Udelcode "02044 = "0 "02044 -\Udelcode "02044 = "0 "02044 -\Udelcode "02308 = "0 "02308 -\Udelcode "02308 = "0 "02308 -\Udelcode "02308 = "0 "02308 -\Udelcode "02308 = "0 "02308 -\Udelcode "02308 = "0 "02308 -\Udelcode "02309 = "0 "02309 -\Udelcode "02309 = "0 "02309 -\Udelcode "02309 = "0 "02309 -\Udelcode "02309 = "0 "02309 -\Udelcode "02309 = "0 "02309 -\Udelcode "0230A = "0 "0230A -\Udelcode "0230A = "0 "0230A -\Udelcode "0230B = "0 "0230B -\Udelcode "0230B = "0 "0230B -\Udelcode "0231C = "0 "0231C -\Udelcode "0231C = "0 "0231C -\Udelcode "0231D = "0 "0231D -\Udelcode "0231D = "0 "0231D -\Udelcode "0231E = "0 "0231E -\Udelcode "0231E = "0 "0231E -\Udelcode "0231F = "0 "0231F -\Udelcode "0231F = "0 "0231F -\Udelcode "023B0 = "0 "023B0 -\Udelcode "023B0 = "0 "023B0 -\Udelcode "023B1 = "0 "023B1 -\Udelcode "023B1 = "0 "023B1 -\Udelcode "027E6 = "0 "027E6 -\Udelcode "027E6 = "0 "027E6 -\Udelcode "027E7 = "0 "027E7 -\Udelcode "027E7 = "0 "027E7 -\Udelcode "027E8 = "0 "027E8 -\Udelcode "027E8 = "0 "027E8 -\Udelcode "027E9 = "0 "027E9 -\Udelcode "027E9 = "0 "027E9 -\Udelcode "027EA = "0 "027EA -\Udelcode "027EA = "0 "027EA -\Udelcode "027EB = "0 "027EB -\Udelcode "027EB = "0 "027EB -\Udelcode "027EE = "0 "027EE -\Udelcode "027EE = "0 "027EE -\Udelcode "027EF = "0 "027EF -\Udelcode "027EF = "0 "027EF - -\Umathcode "00021 = "5 "0 "00021 -\Umathcode "00022 = "0 "0 "00022 -\Umathcode "00027 = "0 "0 "00027 -\Umathcode "00028 = "4 "0 "00028 -\Umathcode "00029 = "5 "0 "00029 -\Umathcode "0002A = "2 "0 "02217 -\Umathcode "0002B = "2 "0 "0002B -\Umathcode "0002C = "6 "0 "0002C -\Umathcode "0002D = "2 "0 "02212 -\Umathcode "0002E = "6 "0 "0002E -\Umathcode "0002F = "4 "0 "02044 -\Umathcode "0003A = "3 "0 "0003A -\Umathcode "0003B = "6 "0 "0003B -\Umathcode "0003C = "3 "0 "0003C -\Umathcode "0003D = "3 "0 "0003D -\Umathcode "0003E = "3 "0 "0003E -\Umathcode "0003F = "5 "0 "0003F -\Umathcode "0005B = "4 "0 "0005B -\Umathcode "0005C = "0 "0 "0005C -\Umathcode "0005D = "5 "0 "0005D -\Umathcode "0007B = "4 "0 "0007B -\Umathcode "0007C = "0 "0 "0007C -\Umathcode "0007D = "5 "0 "0007D -\Umathcode "000A5 = "0 "0 "000A5 -\Umathcode "000A7 = "0 "0 "000A7 -\Umathcode "000AC = "0 "0 "000AC -\Umathcode "000B1 = "2 "0 "000B1 -\Umathcode "000B6 = "0 "0 "000B6 -\Umathcode "000B7 = "2 "0 "000B7 -\Umathcode "000D7 = "2 "0 "000D7 -\Umathcode "000F0 = "0 "0 "000F0 -\Umathcode "000F7 = "2 "0 "000F7 -\Umathcode "00338 = "3 "0 "00338 -\Umathcode "003F0 = "0 "0 "003F0 -\Umathcode "02016 = "0 "0 "02016 -\Umathcode "02020 = "2 "0 "02020 -\Umathcode "02021 = "2 "0 "02021 -\Umathcode "02022 = "2 "0 "02022 -\Umathcode "02026 = "0 "0 "02026 -\Umathcode "02032 = "0 "0 "02032 -\Umathcode "02033 = "0 "0 "02033 -\Umathcode "02034 = "0 "0 "02034 -\Umathcode "02044 = "0 "0 "02044 -\Umathcode "0207A = "2 "0 "0207A -\Umathcode "0207B = "2 "0 "0207B -\Umathcode "020DD = "0 "0 "020DD -\Umathcode "020DE = "0 "0 "020DE -\Umathcode "020DF = "0 "0 "020DF -\Umathcode "02111 = "0 "0 "02111 -\Umathcode "02113 = "0 "0 "02113 -\Umathcode "02118 = "0 "0 "02118 -\Umathcode "0211C = "0 "0 "0211C -\Umathcode "02132 = "0 "0 "02132 -\Umathcode "02135 = "0 "0 "02135 -\Umathcode "02136 = "0 "0 "02136 -\Umathcode "02137 = "0 "0 "02137 -\Umathcode "02138 = "0 "0 "02138 -\Umathcode "02141 = "0 "0 "02141 -\Umathcode "02142 = "0 "0 "02142 -\Umathcode "02143 = "0 "0 "02143 -\Umathcode "02144 = "0 "0 "02144 -\Umathcode "02145 = "0 "0 "02145 -\Umathcode "02146 = "0 "0 "02146 -\Umathcode "02147 = "0 "0 "02147 -\Umathcode "02148 = "0 "0 "02148 -\Umathcode "02149 = "0 "0 "02149 -\Umathcode "0214A = "0 "0 "0214A -\Umathcode "0214B = "2 "0 "0214B -\Umathcode "02190 = "3 "0 "02190 -\Umathcode "02191 = "3 "0 "02191 -\Umathcode "02192 = "3 "0 "02192 -\Umathcode "02193 = "3 "0 "02193 -\Umathcode "02194 = "3 "0 "02194 -\Umathcode "02195 = "3 "0 "02195 -\Umathcode "02196 = "3 "0 "02196 -\Umathcode "02197 = "3 "0 "02197 -\Umathcode "02198 = "3 "0 "02198 -\Umathcode "02199 = "3 "0 "02199 -\Umathcode "0219A = "3 "0 "0219A -\Umathcode "0219B = "3 "0 "0219B -\Umathcode "0219C = "3 "0 "0219C -\Umathcode "0219D = "3 "0 "0219D -\Umathcode "0219E = "3 "0 "0219E -\Umathcode "0219F = "3 "0 "0219F -\Umathcode "021A0 = "3 "0 "021A0 -\Umathcode "021A1 = "3 "0 "021A1 -\Umathcode "021A2 = "3 "0 "021A2 -\Umathcode "021A3 = "3 "0 "021A3 -\Umathcode "021A4 = "3 "0 "021A4 -\Umathcode "021A5 = "3 "0 "021A5 -\Umathcode "021A6 = "3 "0 "021A6 -\Umathcode "021A7 = "3 "0 "021A7 -\Umathcode "021A8 = "0 "0 "021A8 -\Umathcode "021A9 = "3 "0 "021A9 -\Umathcode "021AA = "3 "0 "021AA -\Umathcode "021AB = "3 "0 "021AB -\Umathcode "021AC = "3 "0 "021AC -\Umathcode "021AD = "3 "0 "021AD -\Umathcode "021AE = "3 "0 "021AE -\Umathcode "021AF = "3 "0 "021AF -\Umathcode "021B0 = "3 "0 "021B0 -\Umathcode "021B1 = "3 "0 "021B1 -\Umathcode "021B2 = "3 "0 "021B2 -\Umathcode "021B3 = "3 "0 "021B3 -\Umathcode "021B4 = "0 "0 "021B4 -\Umathcode "021B5 = "0 "0 "021B5 -\Umathcode "021B6 = "3 "0 "021B6 -\Umathcode "021B7 = "3 "0 "021B7 -\Umathcode "021B8 = "3 "0 "021B8 -\Umathcode "021B9 = "3 "0 "021B9 -\Umathcode "021BA = "3 "0 "021BA -\Umathcode "021BB = "3 "0 "021BB -\Umathcode "021BC = "3 "0 "021BC -\Umathcode "021BD = "3 "0 "021BD -\Umathcode "021BE = "3 "0 "021BE -\Umathcode "021BF = "3 "0 "021BF -\Umathcode "021C0 = "3 "0 "021C0 -\Umathcode "021C1 = "3 "0 "021C1 -\Umathcode "021C2 = "3 "0 "021C2 -\Umathcode "021C3 = "3 "0 "021C3 -\Umathcode "021C4 = "3 "0 "021C4 -\Umathcode "021C5 = "3 "0 "021C5 -\Umathcode "021C6 = "3 "0 "021C6 -\Umathcode "021C7 = "3 "0 "021C7 -\Umathcode "021C8 = "3 "0 "021C8 -\Umathcode "021C9 = "3 "0 "021C9 -\Umathcode "021CA = "3 "0 "021CA -\Umathcode "021CB = "3 "0 "021CB -\Umathcode "021CC = "3 "0 "021CC -\Umathcode "021CD = "3 "0 "021CD -\Umathcode "021CE = "3 "0 "021CE -\Umathcode "021CF = "3 "0 "021CF -\Umathcode "021D0 = "3 "0 "021D0 -\Umathcode "021D1 = "3 "0 "021D1 -\Umathcode "021D2 = "3 "0 "021D2 -\Umathcode "021D3 = "3 "0 "021D3 -\Umathcode "021D4 = "3 "0 "021D4 -\Umathcode "021D5 = "3 "0 "021D5 -\Umathcode "021D6 = "3 "0 "021D6 -\Umathcode "021D7 = "3 "0 "021D7 -\Umathcode "021D8 = "3 "0 "021D8 -\Umathcode "021D9 = "3 "0 "021D9 -\Umathcode "021DA = "3 "0 "021DA -\Umathcode "021DB = "3 "0 "021DB -\Umathcode "021DC = "3 "0 "021DC -\Umathcode "021DD = "3 "0 "021DD -\Umathcode "021DE = "3 "0 "021DE -\Umathcode "021DF = "3 "0 "021DF -\Umathcode "021E0 = "3 "0 "021E0 -\Umathcode "021E1 = "3 "0 "021E1 -\Umathcode "021E2 = "3 "0 "021E2 -\Umathcode "021E3 = "3 "0 "021E3 -\Umathcode "021E4 = "3 "0 "021E4 -\Umathcode "021E5 = "3 "0 "021E5 -\Umathcode "021E6 = "0 "0 "021E6 -\Umathcode "021E7 = "0 "0 "021E7 -\Umathcode "021E8 = "0 "0 "021E8 -\Umathcode "021E9 = "0 "0 "021E9 -\Umathcode "021EB = "0 "0 "021EB -\Umathcode "021F4 = "3 "0 "021F4 -\Umathcode "021F5 = "3 "0 "021F5 -\Umathcode "021F6 = "3 "0 "021F6 -\Umathcode "021F7 = "3 "0 "021F7 -\Umathcode "021F8 = "3 "0 "021F8 -\Umathcode "021F9 = "3 "0 "021F9 -\Umathcode "021FA = "3 "0 "021FA -\Umathcode "021FB = "3 "0 "021FB -\Umathcode "021FC = "3 "0 "021FC -\Umathcode "021FD = "3 "0 "021FD -\Umathcode "021FE = "3 "0 "021FE -\Umathcode "021FF = "3 "0 "021FF -\Umathcode "02200 = "0 "0 "02200 -\Umathcode "02201 = "0 "0 "02201 -\Umathcode "02202 = "0 "0 "02202 -\Umathcode "02203 = "0 "0 "02203 -\Umathcode "02204 = "0 "0 "02204 -\Umathcode "02205 = "0 "0 "02205 -\Umathcode "02208 = "3 "0 "02208 -\Umathcode "02209 = "3 "0 "02209 -\Umathcode "0220B = "3 "0 "0220B -\Umathcode "0220C = "3 "0 "0220C -\Umathcode "0220F = "1 "0 "0220F -\Umathcode "02210 = "1 "0 "02210 -\Umathcode "02211 = "1 "0 "02211 -\Umathcode "02212 = "2 "0 "02212 -\Umathcode "02213 = "2 "0 "02213 -\Umathcode "02214 = "2 "0 "02214 -\Umathcode "02216 = "2 "0 "02216 -\Umathcode "02217 = "2 "0 "02217 -\Umathcode "02218 = "2 "0 "02218 -\Umathcode "02219 = "2 "0 "02219 -\Umathcode "0221D = "3 "0 "0221D -\Umathcode "0221E = "0 "0 "0221E -\Umathcode "0221F = "0 "0 "0221F -\Umathcode "02220 = "0 "0 "02220 -\Umathcode "02221 = "0 "0 "02221 -\Umathcode "02222 = "0 "0 "02222 -\Umathcode "02223 = "2 "0 "02223 -\Umathcode "02224 = "2 "0 "02224 -\Umathcode "02225 = "3 "0 "02225 -\Umathcode "02226 = "3 "0 "02226 -\Umathcode "02227 = "2 "0 "02227 -\Umathcode "02228 = "2 "0 "02228 -\Umathcode "02229 = "2 "0 "02229 -\Umathcode "0222A = "2 "0 "0222A -\Umathcode "0222B = "1 "0 "0222B -\Umathcode "0222C = "1 "0 "0222C -\Umathcode "0222D = "1 "0 "0222D -\Umathcode "0222E = "1 "0 "0222E -\Umathcode "0222F = "1 "0 "0222F -\Umathcode "02230 = "1 "0 "02230 -\Umathcode "02231 = "1 "0 "02231 -\Umathcode "02232 = "1 "0 "02232 -\Umathcode "02233 = "1 "0 "02233 -\Umathcode "02234 = "3 "0 "02234 -\Umathcode "02235 = "3 "0 "02235 -\Umathcode "02236 = "6 "0 "02236 -\Umathcode "02237 = "3 "0 "02237 -\Umathcode "02238 = "2 "0 "02238 -\Umathcode "02239 = "3 "0 "02239 -\Umathcode "0223C = "3 "0 "0223C -\Umathcode "0223D = "3 "0 "0223D -\Umathcode "02240 = "2 "0 "02240 -\Umathcode "02241 = "3 "0 "02241 -\Umathcode "02242 = "3 "0 "02242 -\Umathcode "02243 = "3 "0 "02243 -\Umathcode "02244 = "3 "0 "02244 -\Umathcode "02245 = "3 "0 "02245 -\Umathcode "02246 = "3 "0 "02246 -\Umathcode "02247 = "3 "0 "02247 -\Umathcode "02248 = "3 "0 "02248 -\Umathcode "02249 = "3 "0 "02249 -\Umathcode "0224A = "3 "0 "0224A -\Umathcode "0224C = "3 "0 "0224C -\Umathcode "0224D = "3 "0 "0224D -\Umathcode "0224E = "3 "0 "0224E -\Umathcode "02250 = "3 "0 "02250 -\Umathcode "02251 = "3 "0 "02251 -\Umathcode "02252 = "3 "0 "02252 -\Umathcode "02253 = "3 "0 "02253 -\Umathcode "02254 = "3 "0 "02254 -\Umathcode "02255 = "3 "0 "02255 -\Umathcode "02256 = "3 "0 "02256 -\Umathcode "02257 = "3 "0 "02257 -\Umathcode "02259 = "3 "0 "02259 -\Umathcode "0225A = "3 "0 "0225A -\Umathcode "0225B = "3 "0 "0225B -\Umathcode "0225C = "3 "0 "0225C -\Umathcode "0225D = "3 "0 "0225D -\Umathcode "0225E = "3 "0 "0225E -\Umathcode "0225F = "3 "0 "0225F -\Umathcode "02260 = "3 "0 "02260 -\Umathcode "02261 = "3 "0 "02261 -\Umathcode "02262 = "3 "0 "02262 -\Umathcode "02263 = "3 "0 "02263 -\Umathcode "02264 = "3 "0 "02264 -\Umathcode "02265 = "3 "0 "02265 -\Umathcode "02266 = "3 "0 "02266 -\Umathcode "02267 = "3 "0 "02267 -\Umathcode "02268 = "3 "0 "02268 -\Umathcode "02269 = "3 "0 "02269 -\Umathcode "0226A = "3 "0 "0226A -\Umathcode "0226B = "3 "0 "0226B -\Umathcode "0226C = "3 "0 "0226C -\Umathcode "0226D = "3 "0 "0226D -\Umathcode "0226E = "3 "0 "0226E -\Umathcode "0226F = "3 "0 "0226F -\Umathcode "02270 = "3 "0 "02270 -\Umathcode "02271 = "3 "0 "02271 -\Umathcode "02272 = "3 "0 "02272 -\Umathcode "02273 = "3 "0 "02273 -\Umathcode "02274 = "3 "0 "02274 -\Umathcode "02275 = "3 "0 "02275 -\Umathcode "02276 = "3 "0 "02276 -\Umathcode "02277 = "3 "0 "02277 -\Umathcode "02278 = "3 "0 "02278 -\Umathcode "02279 = "3 "0 "02279 -\Umathcode "0227A = "3 "0 "0227A -\Umathcode "0227B = "3 "0 "0227B -\Umathcode "0227C = "3 "0 "0227C -\Umathcode "0227D = "3 "0 "0227D -\Umathcode "0227E = "3 "0 "0227E -\Umathcode "0227F = "3 "0 "0227F -\Umathcode "02280 = "3 "0 "02280 -\Umathcode "02281 = "3 "0 "02281 -\Umathcode "02282 = "3 "0 "02282 -\Umathcode "02283 = "3 "0 "02283 -\Umathcode "02284 = "3 "0 "02284 -\Umathcode "02285 = "3 "0 "02285 -\Umathcode "02286 = "3 "0 "02286 -\Umathcode "02287 = "3 "0 "02287 -\Umathcode "02288 = "3 "0 "02288 -\Umathcode "02289 = "3 "0 "02289 -\Umathcode "0228A = "3 "0 "0228A -\Umathcode "0228B = "3 "0 "0228B -\Umathcode "0228E = "2 "0 "0228E -\Umathcode "0228F = "3 "0 "0228F -\Umathcode "02290 = "3 "0 "02290 -\Umathcode "02291 = "2 "0 "02291 -\Umathcode "02292 = "2 "0 "02292 -\Umathcode "02293 = "2 "0 "02293 -\Umathcode "02294 = "2 "0 "02294 -\Umathcode "02295 = "2 "0 "02295 -\Umathcode "02296 = "2 "0 "02296 -\Umathcode "02297 = "2 "0 "02297 -\Umathcode "02298 = "2 "0 "02298 -\Umathcode "02299 = "2 "0 "02299 -\Umathcode "0229A = "2 "0 "0229A -\Umathcode "0229B = "2 "0 "0229B -\Umathcode "0229C = "2 "0 "0229C -\Umathcode "0229D = "2 "0 "0229D -\Umathcode "0229E = "2 "0 "0229E -\Umathcode "0229F = "2 "0 "0229F -\Umathcode "022A0 = "2 "0 "022A0 -\Umathcode "022A1 = "2 "0 "022A1 -\Umathcode "022A2 = "3 "0 "022A2 -\Umathcode "022A3 = "3 "0 "022A3 -\Umathcode "022A4 = "0 "0 "022A4 -\Umathcode "022A5 = "0 "0 "022A5 -\Umathcode "022A7 = "3 "0 "022A7 -\Umathcode "022A8 = "3 "0 "022A8 -\Umathcode "022A9 = "3 "0 "022A9 -\Umathcode "022AA = "3 "0 "022AA -\Umathcode "022AB = "3 "0 "022AB -\Umathcode "022AC = "3 "0 "022AC -\Umathcode "022AD = "3 "0 "022AD -\Umathcode "022AE = "3 "0 "022AE -\Umathcode "022AF = "3 "0 "022AF -\Umathcode "022B2 = "2 "0 "022B2 -\Umathcode "022B3 = "2 "0 "022B3 -\Umathcode "022B8 = "3 "0 "022B8 -\Umathcode "022BA = "2 "0 "022BA -\Umathcode "022BB = "2 "0 "022BB -\Umathcode "022BC = "2 "0 "022BC -\Umathcode "022C0 = "1 "0 "022C0 -\Umathcode "022C1 = "1 "0 "022C1 -\Umathcode "022C2 = "1 "0 "022C2 -\Umathcode "022C3 = "1 "0 "022C3 -\Umathcode "022C4 = "2 "0 "022C4 -\Umathcode "022C5 = "2 "0 "022C5 -\Umathcode "022C6 = "2 "0 "022C6 -\Umathcode "022C7 = "2 "0 "022C7 -\Umathcode "022C8 = "3 "0 "022C8 -\Umathcode "022C9 = "2 "0 "022C9 -\Umathcode "022CA = "2 "0 "022CA -\Umathcode "022CB = "2 "0 "022CB -\Umathcode "022CC = "2 "0 "022CC -\Umathcode "022CE = "2 "0 "022CE -\Umathcode "022CF = "2 "0 "022CF -\Umathcode "022D0 = "3 "0 "022D0 -\Umathcode "022D1 = "3 "0 "022D1 -\Umathcode "022D2 = "2 "0 "022D2 -\Umathcode "022D3 = "2 "0 "022D3 -\Umathcode "022D4 = "3 "0 "022D4 -\Umathcode "022D6 = "2 "0 "022D6 -\Umathcode "022D7 = "2 "0 "022D7 -\Umathcode "022D8 = "3 "0 "022D8 -\Umathcode "022D9 = "3 "0 "022D9 -\Umathcode "022DA = "3 "0 "022DA -\Umathcode "022DB = "3 "0 "022DB -\Umathcode "022DC = "3 "0 "022DC -\Umathcode "022DD = "3 "0 "022DD -\Umathcode "022DE = "3 "0 "022DE -\Umathcode "022DF = "3 "0 "022DF -\Umathcode "022E0 = "3 "0 "022E0 -\Umathcode "022E1 = "3 "0 "022E1 -\Umathcode "022E2 = "3 "0 "022E2 -\Umathcode "022E3 = "3 "0 "022E3 -\Umathcode "022E4 = "3 "0 "022E4 -\Umathcode "022E5 = "3 "0 "022E5 -\Umathcode "022E6 = "3 "0 "022E6 -\Umathcode "022E7 = "3 "0 "022E7 -\Umathcode "022E8 = "3 "0 "022E8 -\Umathcode "022E9 = "3 "0 "022E9 -\Umathcode "022EA = "3 "0 "022EA -\Umathcode "022EB = "3 "0 "022EB -\Umathcode "022EC = "3 "0 "022EC -\Umathcode "022ED = "3 "0 "022ED -\Umathcode "022EE = "0 "0 "022EE -\Umathcode "022EF = "0 "0 "022EF -\Umathcode "022F0 = "0 "0 "022F0 -\Umathcode "022F1 = "0 "0 "022F1 -\Umathcode "02300 = "0 "0 "02300 -\Umathcode "02308 = "4 "0 "02308 -\Umathcode "02309 = "5 "0 "02309 -\Umathcode "0230A = "4 "0 "0230A -\Umathcode "0230B = "5 "0 "0230B -\Umathcode "0231C = "4 "0 "0231C -\Umathcode "0231D = "5 "0 "0231D -\Umathcode "0231E = "4 "0 "0231E -\Umathcode "0231F = "5 "0 "0231F -\Umathcode "02322 = "3 "0 "02322 -\Umathcode "02323 = "3 "0 "02323 -\Umathcode "023B0 = "4 "0 "023B0 -\Umathcode "023B1 = "5 "0 "023B1 -\Umathcode "024C7 = "0 "0 "024C7 -\Umathcode "024C8 = "0 "0 "024C8 -\Umathcode "025A0 = "0 "0 "025A0 -\Umathcode "025A1 = "0 "0 "025A1 -\Umathcode "025A2 = "0 "0 "025A2 -\Umathcode "025B2 = "2 "0 "025B2 -\Umathcode "025B3 = "0 "0 "025B3 -\Umathcode "025B6 = "2 "0 "025B6 -\Umathcode "025B7 = "2 "0 "025B7 -\Umathcode "025BC = "2 "0 "025BC -\Umathcode "025BD = "2 "0 "025BD -\Umathcode "025C0 = "2 "0 "025C0 -\Umathcode "025C1 = "2 "0 "025C1 -\Umathcode "025CA = "0 "0 "025CA -\Umathcode "025EF = "2 "0 "025EF -\Umathcode "02605 = "0 "0 "02605 -\Umathcode "02660 = "0 "0 "02660 -\Umathcode "02661 = "0 "0 "02661 -\Umathcode "02662 = "0 "0 "02662 -\Umathcode "02663 = "0 "0 "02663 -\Umathcode "02666 = "0 "0 "02666 -\Umathcode "0266D = "0 "0 "0266D -\Umathcode "0266E = "0 "0 "0266E -\Umathcode "0266F = "0 "0 "0266F -\Umathcode "02713 = "0 "0 "02713 -\Umathcode "02720 = "0 "0 "02720 -\Umathcode "027E6 = "4 "0 "027E6 -\Umathcode "027E7 = "5 "0 "027E7 -\Umathcode "027E8 = "4 "0 "027E8 -\Umathcode "027E9 = "5 "0 "027E9 -\Umathcode "027EA = "4 "0 "027EA -\Umathcode "027EB = "5 "0 "027EB -\Umathcode "027EE = "4 "0 "027EE -\Umathcode "027EF = "5 "0 "027EF -\Umathcode "027F5 = "3 "0 "027F5 -\Umathcode "027F6 = "3 "0 "027F6 -\Umathcode "027F7 = "3 "0 "027F7 -\Umathcode "027F8 = "3 "0 "027F8 -\Umathcode "027F9 = "3 "0 "027F9 -\Umathcode "027FA = "3 "0 "027FA -\Umathcode "027FB = "3 "0 "027FB -\Umathcode "027FC = "3 "0 "027FC -\Umathcode "027FD = "3 "0 "027FD -\Umathcode "027FE = "3 "0 "027FE -\Umathcode "027FF = "3 "0 "027FF -\Umathcode "02906 = "3 "0 "02906 -\Umathcode "02907 = "3 "0 "02907 -\Umathcode "0290A = "3 "0 "0290A -\Umathcode "0290B = "3 "0 "0290B -\Umathcode "0290C = "3 "0 "0290C -\Umathcode "0290D = "3 "0 "0290D -\Umathcode "02911 = "3 "0 "02911 -\Umathcode "02916 = "3 "0 "02916 -\Umathcode "02917 = "3 "0 "02917 -\Umathcode "02921 = "3 "0 "02921 -\Umathcode "02922 = "3 "0 "02922 -\Umathcode "02923 = "3 "0 "02923 -\Umathcode "02924 = "3 "0 "02924 -\Umathcode "02925 = "3 "0 "02925 -\Umathcode "02926 = "3 "0 "02926 -\Umathcode "02A00 = "1 "0 "02A00 -\Umathcode "02A01 = "1 "0 "02A01 -\Umathcode "02A02 = "1 "0 "02A02 -\Umathcode "02A03 = "1 "0 "02A03 -\Umathcode "02A04 = "1 "0 "02A04 -\Umathcode "02A05 = "1 "0 "02A05 -\Umathcode "02A06 = "1 "0 "02A06 -\Umathcode "02A09 = "1 "0 "02A09 -\Umathcode "02A3F = "2 "0 "02A3F -\Umathcode "02A7D = "3 "0 "02A7D -\Umathcode "02A7E = "3 "0 "02A7E -\Umathcode "02A85 = "3 "0 "02A85 -\Umathcode "02A86 = "3 "0 "02A86 -\Umathcode "02A87 = "3 "0 "02A87 -\Umathcode "02A88 = "3 "0 "02A88 -\Umathcode "02A89 = "3 "0 "02A89 -\Umathcode "02A8A = "3 "0 "02A8A -\Umathcode "02A8B = "3 "0 "02A8B -\Umathcode "02A8C = "3 "0 "02A8C -\Umathcode "02A95 = "3 "0 "02A95 -\Umathcode "02A96 = "3 "0 "02A96 -\Umathcode "02AAF = "3 "0 "02AAF -\Umathcode "02AB0 = "3 "0 "02AB0 -\Umathcode "02AB1 = "3 "0 "02AB1 -\Umathcode "02AB2 = "3 "0 "02AB2 -\Umathcode "02AB3 = "3 "0 "02AB3 -\Umathcode "02AB4 = "3 "0 "02AB4 -\Umathcode "02AB5 = "3 "0 "02AB5 -\Umathcode "02AB6 = "3 "0 "02AB6 -\Umathcode "02AB7 = "3 "0 "02AB7 -\Umathcode "02AB8 = "3 "0 "02AB8 -\Umathcode "02AB9 = "3 "0 "02AB9 -\Umathcode "02ABA = "3 "0 "02ABA -\Umathcode "02AC5 = "3 "0 "02AC5 -\Umathcode "02AC6 = "3 "0 "02AC6 -\Umathcode "02ACB = "3 "0 "02ACB -\Umathcode "02ACC = "3 "0 "02ACC -\Umathcode "12035 = "0 "0 "12035 -\Umathcode "1D6A4 = "0 "0 "1D6A4 -\Umathcode "1D6A5 = "0 "0 "1D6A5 -\Umathcode "1D6FB = "0 "0 "1D6FB -\Umathcode "1D717 = "0 "0 "1D717 -\Umathcode "1D718 = "0 "0 "1D718 - -% gaps .. done in lua (as example) - -% \Umathcode "1D455 = "0 "0 "0210E -% \Umathcode "1D49D = "0 "0 "0212C -% \Umathcode "1D4A0 = "0 "0 "02130 -% \Umathcode "1D4A1 = "0 "0 "02131 -% \Umathcode "1D4A3 = "0 "0 "0210B -% \Umathcode "1D4A4 = "0 "0 "02110 -% \Umathcode "1D4A7 = "0 "0 "02112 -% \Umathcode "1D4A8 = "0 "0 "02133 -% \Umathcode "1D4AD = "0 "0 "0211B -% \Umathcode "1D4BA = "0 "0 "0212F -% \Umathcode "1D4BC = "0 "0 "0210A -% \Umathcode "1D4C4 = "0 "0 "02134 -% \Umathcode "1D506 = "0 "0 "0212D -% \Umathcode "1D50B = "0 "0 "0210C -% \Umathcode "1D50C = "0 "0 "02111 -% \Umathcode "1D515 = "0 "0 "0211C -% \Umathcode "1D51D = "0 "0 "02128 -% \Umathcode "1D53A = "0 "0 "02102 -% \Umathcode "1D53F = "0 "0 "0210D -% \Umathcode "1D545 = "0 "0 "02115 -% \Umathcode "1D547 = "0 "0 "02119 -% \Umathcode "1D548 = "0 "0 "0211A -% \Umathcode "1D549 = "0 "0 "0211D -% \Umathcode "1D551 = "0 "0 "02124 - -% initialization - -\the\everymathit - -% a couple of definitions (we could also use \mathchardef): - -\def\acute {\Umathaccent"0"0"0000B4 } -\def\acwopencirclearrow {\Umathchar "3"0"0021BA } -\def\aleph {\Umathchar "0"0"002135 } -\def\Alpha {\Umathchar "0"0"000391 } -\def\alpha {\Umathchar "0"0"0003B1 } -\def\amalg {\Umathchar "2"0"002A3F } -\def\angle {\Umathchar "0"0"002220 } -\def\Angstrom {\Umathchar "0"0"00212B } -\def\approx {\Umathchar "3"0"002248 } -\def\approxEq {\Umathchar "3"0"002245 } -\def\approxeq {\Umathchar "3"0"00224A } -\def\approxnEq {\Umathchar "3"0"002247 } -\def\arrowvert {\Umathchar "0"0"00007C } -\def\Arrowvert {\Umathchar "0"0"002016 } -\def\ast {\Umathchar "2"0"002217 } -\def\ast {\Umathchar "2"0"002217 } -\def\asymp {\Umathchar "3"0"00224D } -\def\backepsilon {\Umathchar "0"0"0003F6 } -\def\backprime {\Umathchar "0"0"012035 } -\def\backsim {\Umathchar "3"0"00223D } -\def\backslash {\Umathchar "0"0"00005C } -\def\bar {\Umathaccent"0"0"0000AF } -\def\barleftarrow {\Umathchar "3"0"0021E4 } -\def\barleftarrowrightarrowbar {\Umathchar "3"0"0021B9 } -\def\barovernorthwestarrow {\Umathchar "3"0"0021B8 } -\def\barwedge {\Umathchar "2"0"0022BC } -\def\because {\Umathchar "3"0"002235 } -\def\Beta {\Umathchar "0"0"000392 } -\def\beta {\Umathchar "0"0"0003B2 } -\def\beth {\Umathchar "0"0"002136 } -\def\between {\Umathchar "3"0"00226C } -\def\bigcap {\Umathchar "1"0"0022C2 } -\def\bigcirc {\Umathchar "2"0"0025EF } -\def\bigcircle {\Umathchar "2"0"0020DD } -\def\bigcircle {\Umathchar "2"0"0020DD } -\def\bigcup {\Umathchar "1"0"0022C3 } -\def\bigdiamond {\Umathchar "0"0"0020DF } -\def\bigodot {\Umathchar "1"0"002A00 } -\def\bigoplus {\Umathchar "1"0"002A01 } -\def\bigotimes {\Umathchar "1"0"002A02 } -\def\bigsqcap {\Umathchar "1"0"002A05 } -\def\bigsqcup {\Umathchar "1"0"002A06 } -\def\bigsquare {\Umathchar "0"0"0020DE } -\def\bigstar {\Umathchar "0"0"002605 } -\def\bigtimes {\Umathchar "1"0"002A09 } -\def\bigtriangledown {\Umathchar "2"0"0025BD } -\def\bigtriangleup {\Umathchar "2"0"0025B3 } -\def\bigudot {\Umathchar "1"0"002A03 } -\def\biguplus {\Umathchar "1"0"002A04 } -\def\bigvee {\Umathchar "1"0"0022C1 } -\def\bigwedge {\Umathchar "1"0"0022C0 } -\def\blacklozenge {\Umathchar "0"0"002666 } -\def\blacksquare {\Umathchar "0"0"0025A0 } -\def\blacktriangle {\Umathchar "2"0"0025B2 } -\def\blacktriangledown {\Umathchar "2"0"0025BC } -\def\blacktriangleleft {\Umathchar "2"0"0025C0 } -\def\blacktriangleright {\Umathchar "2"0"0025B6 } -\def\bot {\Umathchar "0"0"0022A5 } -\def\bowtie {\Umathchar "3"0"0022C8 } -\def\Box {\Umathchar "0"0"0025A1 } -\def\boxdot {\Umathchar "2"0"0022A1 } -\def\boxminus {\Umathchar "2"0"00229F } -\def\boxplus {\Umathchar "2"0"00229E } -\def\boxtimes {\Umathchar "2"0"0022A0 } -%def\braceld {\Umathchar "0"0"000000 } -%def\bracerd {\Umathchar "0"0"000000 } -%def\bracelu {\Umathchar "0"0"000000 } -%def\braceru {\Umathchar "0"0"000000 } -\def\breve {\Umathaccent"0"0"0002D8 } -\def\bullet {\Umathchar "2"0"002022 } -\def\bullet {\Umathchar "2"0"002022 } -\def\Bumpeq {\Umathchar "3"0"00224E } -\def\cap {\Umathchar "2"0"002229 } -\def\Cap {\Umathchar "2"0"0022D2 } -\def\carriagereturn {\Umathchar "0"0"0021B5 } -\def\cdot {\Umathchar "2"0"0022C5 } -\def\cdotp {\Umathchar "6"0"0022C5 } -\def\cdots {\Umathchar "0"0"0022EF } -\def\centerdot {\Umathchar "2"0"0000B7 } -\def\check {\Umathaccent"0"0"0002C7 } -\def\checkmark {\Umathchar "0"0"002713 } -\def\Chi {\Umathchar "0"0"0003A7 } -\def\chi {\Umathchar "0"0"0003C7 } -\def\circ {\Umathchar "2"0"002218 } -\def\circeq {\Umathchar "3"0"002257 } -\def\circlearrowleft {\Umathchar "3"0"0021BB } -\def\circlearrowright {\Umathchar "3"0"0021BA } -\def\circledast {\Umathchar "2"0"00229B } -\def\circledcirc {\Umathchar "2"0"00229A } -\def\circleddash {\Umathchar "2"0"00229D } -\def\circledequals {\Umathchar "2"0"00229C } -\def\circledR {\Umathchar "0"0"0024C7 } -\def\circledS {\Umathchar "0"0"0024C8 } -\def\circleonrightarrow {\Umathchar "3"0"0021F4 } -\def\clubsuit {\Umathchar "0"0"002663 } -\def\colon {\Umathchar "6"0"002236 } -\def\colonequals {\Umathchar "3"0"002254 } -\def\complement {\Umathchar "0"0"002201 } -\def\complexes {\Umathchar "0"0"002102 } -\def\cong {\Umathchar "3"0"002245 } -\def\coprod {\Umathchar "1"0"002210 } -\def\cup {\Umathchar "2"0"00222A } -\def\Cup {\Umathchar "2"0"0022D3 } -\def\curlyeqprec {\Umathchar "3"0"0022DE } -\def\curlyeqsucc {\Umathchar "3"0"0022DF } -\def\curlyvee {\Umathchar "2"0"0022CE } -\def\curlywedge {\Umathchar "2"0"0022CF } -\def\curvearrowleft {\Umathchar "3"0"0021B6 } -\def\curvearrowright {\Umathchar "3"0"0021B7 } -\def\cwopencirclearrow {\Umathchar "3"0"0021BB } -\def\dag {\Umathchar "0"0"002020 } -\def\dagger {\Umathchar "2"0"002020 } -\def\daleth {\Umathchar "0"0"002138 } -\def\dasharrow {\Umathchar "3"0"0021E2 } -\def\dashedleftarrow {\Umathchar "3"0"00290C } -\def\dashedrightarrow {\Umathchar "3"0"00290D } -\def\dashv {\Umathchar "3"0"0022A3 } -\def\ddag {\Umathchar "0"0"002021 } -\def\ddagger {\Umathchar "2"0"002021 } -\def\dddot {\Umathaccent"0"0"0020DB } -\def\ddot {\Umathaccent"0"0"0000A8 } -\def\ddots {\Umathchar "0"0"0022F1 } -\def\Ddownarrow {\Umathchar "3"0"00290B } -\def\definedeq {\Umathchar "3"0"00225D } -\def\Delta {\Umathchar "0"0"000394 } -\def\delta {\Umathchar "0"0"0003B4 } -\def\diamond {\Umathchar "2"0"0022C4 } -\def\diamondsuit {\Umathchar "0"0"002662 } -\def\differentialD {\Umathchar "0"0"002145 } -\def\differentiald {\Umathchar "0"0"002146 } -\def\digamma {\Umathchar "0"0"0003DC } -\def\div {\Umathchar "2"0"0000F7 } -\def\divideontimes {\Umathchar "2"0"0022C7 } -\def\divides {\Umathchar "2"0"002223 } -\def\dot {\Umathaccent"0"0"0002D9 } -\def\doteq {\Umathchar "3"0"002250 } -\def\Doteq {\Umathchar "3"0"002251 } -\def\doteqdot {\Umathchar "3"0"002251 } -\def\dotminus {\Umathchar "2"0"002238 } -\def\dotplus {\Umathchar "2"0"002214 } -\def\dots {\Umathchar "0"0"002026 } -\def\dottedrightarrow {\Umathchar "3"0"002911 } -\def\doublecap {\Umathchar "2"0"0022D2 } -\def\doublecup {\Umathchar "2"0"0022D3 } -\def\doubleprime {\Umathchar "0"0"002033 } -\def\downarrow {\Umathchar "3"0"002193 } -\def\Downarrow {\Umathchar "3"0"0021D3 } -\def\downdasharrow {\Umathchar "3"0"0021E3 } -\def\downdownarrows {\Umathchar "3"0"0021CA } -\def\downharpoonleft {\Umathchar "3"0"0021C3 } -\def\downharpoonright {\Umathchar "3"0"0021C2 } -\def\downuparrows {\Umathchar "3"0"0021F5 } -\def\downwhitearrow {\Umathchar "0"0"0021E9 } -\def\downzigzagarrow {\Umathchar "3"0"0021AF } -\def\ell {\Umathchar "0"0"002113 } -\def\emptyset {\Umathchar "0"0"002205 } -\def\Epsilon {\Umathchar "0"0"000395 } -\def\epsilon {\Umathchar "0"0"0003F5 } -\def\eq {\Umathchar "3"0"00003D } -\def\eqcirc {\Umathchar "3"0"002256 } -\def\eqgtr {\Umathchar "3"0"0022DD } -\def\eqless {\Umathchar "3"0"0022DC } -\def\eqsim {\Umathchar "3"0"002242 } -\def\eqslantgtr {\Umathchar "3"0"002A96 } -\def\eqslantless {\Umathchar "3"0"002A95 } -\def\equalscolon {\Umathchar "3"0"002255 } -\def\equiv {\Umathchar "3"0"002261 } -\def\Eta {\Umathchar "0"0"000397 } -\def\eta {\Umathchar "0"0"0003B7 } -\def\eth {\Umathchar "0"0"0000F0 } -\def\Eulerconst {\Umathchar "0"0"002107 } -\def\exists {\Umathchar "0"0"002203 } -\def\exponentiale {\Umathchar "0"0"002147 } -\def\fallingdotseq {\Umathchar "3"0"002252 } -\def\Finv {\Umathchar "0"0"002132 } -\def\flat {\Umathchar "0"0"00266D } -\def\forall {\Umathchar "0"0"002200 } -\def\frown {\Umathchar "3"0"002322 } -\def\Game {\Umathchar "0"0"002141 } -\def\Gamma {\Umathchar "0"0"000393 } -\def\gamma {\Umathchar "0"0"0003B3 } -\def\ge {\Umathchar "3"0"002265 } -\def\geq {\Umathchar "3"0"002265 } -\def\geqq {\Umathchar "3"0"002267 } -\def\geqslant {\Umathchar "3"0"002A7E } -\def\gets {\Umathchar "3"0"002190 } -\def\gg {\Umathchar "3"0"00226B } -\def\ggg {\Umathchar "3"0"0022D9 } -\def\gggtr {\Umathchar "3"0"0022D9 } -\def\gimel {\Umathchar "0"0"002137 } -\def\gnapprox {\Umathchar "3"0"002A8A } -\def\gneqq {\Umathchar "3"0"002269 } -\def\gnsim {\Umathchar "3"0"0022E7 } -\def\grave {\Umathaccent"0"0"000060 } -\def\gt {\Umathchar "3"0"00003E } -\def\gtrapprox {\Umathchar "3"0"002A86 } -\def\gtrdot {\Umathchar "2"0"0022D7 } -\def\gtreqless {\Umathchar "3"0"0022DB } -\def\gtreqqless {\Umathchar "3"0"002A8C } -\def\gtrless {\Umathchar "3"0"002277 } -\def\gtrsim {\Umathchar "3"0"002273 } -\def\hat {\Umathaccent"0"0"0002C6 } -\def\hbar {\Umathchar "0"0"00210F } -\def\heartsuit {\Umathchar "0"0"002661 } -\def\hookleftarrow {\Umathchar "3"0"0021A9 } -\def\hookrightarrow {\Umathchar "3"0"0021AA } -\def\hslash {\Umathchar "0"0"00210F } -\def\iiint {\Umathchar "1"0"00222D } -\def\iiintop {\Umathchar "0"0"00222D } -\def\iint {\Umathchar "1"0"00222C } -\def\iintop {\Umathchar "0"0"00222C } -\def\Im {\Umathchar "0"0"002111 } -\def\imaginaryi {\Umathchar "0"0"002148 } -\def\imaginaryj {\Umathchar "0"0"002149 } -\def\imath {\Umathchar "0"0"01D6A4 } -\def\imply {\Umathchar "3"0"0021D2 } -\def\in {\Umathchar "0"0"002208 } -\def\infty {\Umathchar "0"0"00221E } -\def\int {\Umathchar "1"0"00222B } -\def\intclockwise {\Umathchar "1"0"002231 } -\def\integers {\Umathchar "0"0"002124 } -\def\intercal {\Umathchar "2"0"0022BA } -\def\intop {\Umathchar "0"0"00222B } -\def\Iota {\Umathchar "0"0"000399 } -\def\iota {\Umathchar "0"0"0003B9 } -\def\jmath {\Umathchar "0"0"01D6A5 } -\def\Join {\Umathchar "3"0"0022C8 } -\def\Kappa {\Umathchar "0"0"00039A } -\def\kappa {\Umathchar "0"0"0003BA } -\def\Lambda {\Umathchar "0"0"00039B } -\def\lambda {\Umathchar "0"0"0003BB } -\def\land {\Umathchar "2"0"002227 } -\def\langle {\Udelimiter "4"0"0027E8 } -\def\lbrace {\Udelimiter "4"0"00007B } -\def\lbrack {\Udelimiter "4"0"00005B } -\def\lceil {\Udelimiter "4"0"002308 } -\def\lceiling {\Udelimiter "4"0"002308 } -\def\ldotp {\Umathchar "6"0"00002E } -\def\ldots {\Umathchar "0"0"002026 } -\def\Ldsh {\Umathchar "3"0"0021B2 } -\def\le {\Umathchar "3"0"002264 } -\def\leadsto {\Umathchar "3"0"0021DD } -\def\leftarrow {\Umathchar "3"0"002190 } -\def\Leftarrow {\Umathchar "3"0"0021D0 } -\def\leftarrowtail {\Umathchar "3"0"0021A2 } -\def\leftarrowtriangle {\Umathchar "3"0"0021FD } -\def\leftdasharrow {\Umathchar "3"0"0021E0 } -\def\leftharpoondown {\Umathchar "3"0"0021BD } -\def\leftharpoonup {\Umathchar "3"0"0021BC } -\def\leftleftarrows {\Umathchar "3"0"0021C7 } -\def\leftrightarrow {\Umathchar "3"0"002194 } -\def\Leftrightarrow {\Umathchar "3"0"0021D4 } -\def\leftrightarrows {\Umathchar "3"0"0021C6 } -\def\leftrightarrowtriangle {\Umathchar "3"0"0021FF } -\def\leftrightharpoons {\Umathchar "3"0"0021CB } -\def\leftrightsquigarrow {\Umathchar "3"0"0021AD } -\def\leftsquigarrow {\Umathchar "3"0"0021DC } -\def\leftthreetimes {\Umathchar "2"0"0022CB } -\def\leftwavearrow {\Umathchar "3"0"00219C } -\def\leftwhitearrow {\Umathchar "0"0"0021E6 } -\def\leq {\Umathchar "3"0"002264 } -\def\leqq {\Umathchar "3"0"002266 } -\def\leqslant {\Umathchar "3"0"002A7D } -\def\lessapprox {\Umathchar "3"0"002A85 } -\def\lessdot {\Umathchar "2"0"0022D6 } -\def\lesseqgtr {\Umathchar "3"0"0022DA } -\def\lesseqqgtr {\Umathchar "3"0"002A8B } -\def\lessgtr {\Umathchar "3"0"002276 } -\def\lesssim {\Umathchar "3"0"002272 } -\def\lfloor {\Udelimiter "4"0"00230A } -\def\lgroup {\Udelimiter "4"0"0027EE } -\def\lhook {\Umathchar "3"0"0FE322 } -\def\lhooknwarrow {\Umathchar "3"0"002923 } -\def\lhooksearrow {\Umathchar "3"0"002925 } -\def\linefeed {\Umathchar "0"0"0021B4 } -\def\ll {\Umathchar "3"0"00226A } -\def\llangle {\Udelimiter "4"0"0027EA } -\def\llbracket {\Udelimiter "4"0"0027E6 } -\def\llcorner {\Udelimiter "4"0"00231E } -\def\Lleftarrow {\Umathchar "3"0"0021DA } -\def\lll {\Umathchar "3"0"0022D8 } -\def\llless {\Umathchar "3"0"0022D8 } -\def\lmoustache {\Udelimiter "4"0"0023B0 } -\def\lnapprox {\Umathchar "3"0"002A89 } -\def\lneq {\Umathchar "3"0"002A87 } -\def\lneqq {\Umathchar "3"0"002268 } -\def\lnot {\Umathchar "0"0"0000AC } -\def\lnsim {\Umathchar "3"0"0022E6 } -\def\longleftarrow {\Umathchar "3"0"0027F5 } -\def\Longleftarrow {\Umathchar "3"0"0027F8 } -\def\longleftrightarrow {\Umathchar "3"0"0027F7 } -\def\Longleftrightarrow {\Umathchar "3"0"0027FA } -\def\longmapsfrom {\Umathchar "3"0"0027FB } -\def\Longmapsfrom {\Umathchar "3"0"0027FD } -\def\longmapsto {\Umathchar "3"0"0027FC } -\def\Longmapsto {\Umathchar "3"0"0027FE } -\def\longrightarrow {\Umathchar "3"0"0027F6 } -\def\Longrightarrow {\Umathchar "3"0"0027F9 } -\def\longrightsquigarrow {\Umathchar "3"0"0027FF } -\def\looparrowleft {\Umathchar "3"0"0021AB } -\def\looparrowright {\Umathchar "3"0"0021AC } -\def\lor {\Umathchar "2"0"002228 } -\def\lozenge {\Umathchar "0"0"0025CA } -\def\lparent {\Udelimiter "4"0"000028 } -\def\lrcorner {\Udelimiter "5"0"00231F } -\def\Lsh {\Umathchar "3"0"0021B0 } -\def\lt {\Umathchar "3"0"00003C } -\def\ltimes {\Umathchar "2"0"0022C9 } -\def\lvert {\Udelimiter "4"0"00007C } -\def\lVert {\Udelimiter "4"0"002016 } -\def\maltese {\Umathchar "0"0"002720 } -\def\mapsdown {\Umathchar "3"0"0021A7 } -\def\mapsfrom {\Umathchar "3"0"0021A4 } -\def\Mapsfrom {\Umathchar "3"0"002906 } -\def\mapsfromchar {\Umathchar "3"0"0FE324 } -\def\mapsto {\Umathchar "3"0"0021A6 } -\def\Mapsto {\Umathchar "3"0"002907 } -\def\mapstochar {\Umathchar "3"0"0FE321 } -\def\mapsup {\Umathchar "3"0"0021A5 } -\def\mathring {\Umathaccent"0"0"0002DA } -\def\measuredangle {\Umathchar "0"0"002221 } -\def\measuredeq {\Umathchar "3"0"00225E } -\def\mho {\Umathchar "0"0"002127 } -\def\mid {\Umathchar "3"0"00007C } -\def\minus {\Umathchar "2"0"002212 } -\def\minuscolon {\Umathchar "2"0"002239 } -\def\models {\Umathchar "3"0"0022A7 } -\def\mp {\Umathchar "2"0"002213 } -\def\Mu {\Umathchar "0"0"00039C } -\def\mu {\Umathchar "0"0"0003BC } -\def\multimap {\Umathchar "3"0"0022B8 } -\def\napprox {\Umathchar "3"0"002249 } -\def\napproxEq {\Umathchar "3"0"002246 } -\def\nasymp {\Umathchar "3"0"00226D } -\def\natural {\Umathchar "0"0"00266E } -\def\naturalnumbers {\Umathchar "0"0"002115 } -\def\ncong {\Umathchar "3"0"002246 } -\def\ndivides {\Umathchar "2"0"002224 } -\def\ne {\Umathchar "3"0"002260 } -\def\nearrow {\Umathchar "3"0"002197 } -\def\Nearrow {\Umathchar "3"0"0021D7 } -\def\neg {\Umathchar "0"0"0000AC } -\def\negativesign {\Umathchar "2"0"00207B } -\def\neq {\Umathchar "3"0"002260 } -\def\nequiv {\Umathchar "3"0"002262 } -\def\neswarrow {\Umathchar "3"0"002922 } -\def\nexists {\Umathchar "0"0"002204 } -\def\ngeq {\Umathchar "3"0"002271 } -\def\ngtr {\Umathchar "3"0"00226F } -\def\ngtrless {\Umathchar "3"0"002279 } -\def\ngtrsim {\Umathchar "3"0"002275 } -\def\nHdownarrow {\Umathchar "3"0"0021DF } -\def\nHuparrow {\Umathchar "3"0"0021DE } -\def\ni {\Umathchar "3"0"00220B } -\def\nin {\Umathchar "3"0"002209 } -\def\nleftarrow {\Umathchar "3"0"00219A } -\def\nLeftarrow {\Umathchar "3"0"0021CD } -\def\nleftrightarrow {\Umathchar "3"0"0021AE } -\def\nLeftrightarrow {\Umathchar "3"0"0021CE } -\def\nleq {\Umathchar "3"0"002270 } -\def\nless {\Umathchar "3"0"00226E } -\def\nlessgtr {\Umathchar "3"0"002278 } -\def\nlesssim {\Umathchar "3"0"002274 } -\def\nmid {\Umathchar "3"0"002224 } -\def\nni {\Umathchar "3"0"00220C } -\def\not {\Umathchar "3"0"000338 } -\def\notin {\Umathchar "3"0"002209 } -\def\nowns {\Umathchar "3"0"00220C } -\def\nparallel {\Umathchar "3"0"002226 } -\def\nprec {\Umathchar "3"0"002280 } -\def\npreccurlyeq {\Umathchar "3"0"0022E0 } -\def\nrightarrow {\Umathchar "3"0"00219B } -\def\nRightarrow {\Umathchar "3"0"0021CF } -\def\nsim {\Umathchar "3"0"002241 } -\def\nsimeq {\Umathchar "3"0"002244 } -\def\nsqsubseteq {\Umathchar "3"0"0022E2 } -\def\nsqsupseteq {\Umathchar "3"0"0022E3 } -\def\nsubset {\Umathchar "3"0"002284 } -\def\nsubseteq {\Umathchar "3"0"002288 } -\def\nsucc {\Umathchar "3"0"002281 } -\def\nsucccurlyeq {\Umathchar "3"0"0022E1 } -\def\nsupset {\Umathchar "3"0"002285 } -\def\nsupseteq {\Umathchar "3"0"002289 } -\def\ntriangleleft {\Umathchar "3"0"0022EB } -\def\ntrianglelefteq {\Umathchar "3"0"0022EC } -\def\ntriangleright {\Umathchar "3"0"0022EA } -\def\ntrianglerighteq {\Umathchar "3"0"0022ED } -\def\Nu {\Umathchar "0"0"00039D } -\def\nu {\Umathchar "0"0"0003BD } -\def\nvdash {\Umathchar "3"0"0022AC } -\def\nvDash {\Umathchar "3"0"0022AD } -\def\nVdash {\Umathchar "3"0"0022AE } -\def\nVDash {\Umathchar "3"0"0022AF } -\def\nvleftarrow {\Umathchar "3"0"0021F7 } -\def\nVleftarrow {\Umathchar "3"0"0021FA } -\def\nvleftrightarrow {\Umathchar "3"0"0021F9 } -\def\nVleftrightarrow {\Umathchar "3"0"0021FC } -\def\nvrightarrow {\Umathchar "3"0"0021F8 } -\def\nVrightarrow {\Umathchar "3"0"0021FB } -\def\nwarrow {\Umathchar "3"0"002196 } -\def\Nwarrow {\Umathchar "3"0"0021D6 } -\def\nwsearrow {\Umathchar "3"0"002921 } -\def\odot {\Umathchar "2"0"002299 } -\def\ohm {\Umathchar "0"0"002126 } -\def\oiiint {\Umathchar "1"0"002230 } -\def\oiint {\Umathchar "1"0"00222F } -\def\oint {\Umathchar "1"0"00222E } -\def\ointclockwise {\Umathchar "1"0"002232 } -\def\ointctrclockwise {\Umathchar "1"0"002233 } -\def\Omega {\Umathchar "0"0"0003A9 } -\def\omega {\Umathchar "0"0"0003C9 } -\def\Omicron {\Umathchar "0"0"00039F } -\def\omicron {\Umathchar "0"0"0003BF } -\def\ominus {\Umathchar "2"0"002296 } -\def\oplus {\Umathchar "2"0"002295 } -\def\oslash {\Umathchar "2"0"002298 } -\def\otimes {\Umathchar "2"0"002297 } -\def\overbar {\Umathaccent"0"0"00203E } -\def\overbrace {\Umathaccent"0"0"0023DE } -\def\overbracket {\Umathaccent"0"0"0023B4 } -\def\overparent {\Umathaccent"0"0"0023DC } -\def\owns {\Umathchar "3"0"00220B } -\def\P {\Umathchar "0"0"0000B6 } -\def\parallel {\Umathchar "3"0"002225 } -\def\partial {\Umathchar "0"0"002202 } -\def\perp {\Umathchar "3"0"0022A5 } -\def\Phi {\Umathchar "0"0"0003A6 } -\def\phi {\Umathchar "0"0"0003D5 } -\def\Pi {\Umathchar "0"0"0003A0 } -\def\pi {\Umathchar "0"0"0003C0 } -\def\pitchfork {\Umathchar "3"0"0022D4 } -\def\Plankconst {\Umathchar "0"0"00210E } -\def\pm {\Umathchar "2"0"0000B1 } -\def\positivesign {\Umathchar "2"0"00207A } -\def\prec {\Umathchar "3"0"00227A } -\def\precapprox {\Umathchar "3"0"002AB7 } -\def\preccurlyeq {\Umathchar "3"0"00227C } -\def\preceq {\Umathchar "3"0"002AAF } -\def\preceqq {\Umathchar "3"0"002AB3 } -\def\precnapprox {\Umathchar "3"0"002AB9 } -\def\precneq {\Umathchar "3"0"002AB1 } -\def\precneqq {\Umathchar "3"0"002AB5 } -\def\precnsim {\Umathchar "3"0"0022E8 } -\def\precsim {\Umathchar "3"0"00227E } -\def\prime {\Umathchar "0"0"002032 } -\def\primes {\Umathchar "0"0"002119 } -\def\prod {\Umathchar "1"0"00220F } -\def\PropertyLine {\Umathchar "0"0"00214A } -\def\propto {\Umathchar "3"0"00221D } -\def\Psi {\Umathchar "0"0"0003A8 } -\def\psi {\Umathchar "0"0"0003C8 } -\def\questionedeq {\Umathchar "3"0"00225F } -\def\rangle {\Udelimiter "5"0"0027E9 } -\def\rationals {\Umathchar "0"0"00211A } -\def\rbrace {\Udelimiter "5"0"00007D } -\def\rbrack {\Udelimiter "5"0"00005D } -\def\rceil {\Udelimiter "5"0"002309 } -\def\rceiling {\Udelimiter "5"0"002309 } -\def\Rdsh {\Umathchar "3"0"0021B3 } -\def\Re {\Umathchar "0"0"00211C } -\def\reals {\Umathchar "0"0"00211D } -\def\Relbar {\Umathchar "3"0"00003D } -\def\relbar {\Umathchar "3"0"002212 } -\def\restriction {\Umathchar "3"0"0021BE } -\def\rfloor {\Udelimiter "5"0"00230B } -\def\rgroup {\Udelimiter "5"0"0027EF } -\def\Rho {\Umathchar "0"0"0003A1 } -\def\rho {\Umathchar "0"0"0003C1 } -\def\rhook {\Umathchar "3"0"0FE323 } -\def\rhooknearrow {\Umathchar "3"0"002924 } -\def\rhookswarrow {\Umathchar "3"0"002926 } -\def\rightangle {\Umathchar "0"0"00221F } -\def\rightarrow {\Umathchar "3"0"002192 } -\def\Rightarrow {\Umathchar "3"0"0021D2 } -\def\rightarrowbar {\Umathchar "3"0"0021E5 } -\def\rightarrowtail {\Umathchar "3"0"0021A3 } -\def\rightarrowtriangle {\Umathchar "3"0"0021FE } -\def\rightdasharrow {\Umathchar "3"0"0021E2 } -\def\rightharpoondown {\Umathchar "3"0"0021C1 } -\def\rightharpoonup {\Umathchar "3"0"0021C0 } -\def\rightleftarrows {\Umathchar "3"0"0021C4 } -\def\rightleftharpoons {\Umathchar "3"0"0021CC } -\def\rightrightarrows {\Umathchar "3"0"0021C9 } -\def\rightsquigarrow {\Umathchar "3"0"0021DD } -\def\rightthreearrows {\Umathchar "3"0"0021F6 } -\def\rightthreetimes {\Umathchar "2"0"0022CC } -\def\rightwavearrow {\Umathchar "3"0"00219D } -\def\rightwhitearrow {\Umathchar "0"0"0021E8 } -\def\risingdotseq {\Umathchar "3"0"002253 } -\def\rmoustache {\Udelimiter "5"0"0023B1 } -\def\rneq {\Umathchar "3"0"002A88 } -\def\rparent {\Udelimiter "5"0"000029 } -\def\rrangle {\Udelimiter "5"0"0027EB } -\def\rrbracket {\Udelimiter "5"0"0027E7 } -\def\Rrightarrow {\Umathchar "3"0"0021DB } -\def\Rsh {\Umathchar "3"0"0021B1 } -\def\rtimes {\Umathchar "2"0"0022CA } -\def\rvert {\Udelimiter "5"0"00007C } -\def\rVert {\Udelimiter "5"0"002016 } -\def\S {\Umathchar "0"0"0000A7 } -\def\searrow {\Umathchar "3"0"002198 } -\def\Searrow {\Umathchar "3"0"0021D8 } -\def\setminus {\Umathchar "2"0"002216 } -\def\sharp {\Umathchar "0"0"00266F } -\def\Sigma {\Umathchar "0"0"0003A3 } -\def\sigma {\Umathchar "0"0"0003C3 } -\def\sim {\Umathchar "3"0"00223C } -\def\simeq {\Umathchar "3"0"002243 } -\def\slash {\Umathchar "0"0"002044 } -\def\smile {\Umathchar "3"0"002323 } -\def\solidus {\Udelimiter "5"0"002044 } -\def\spadesuit {\Umathchar "0"0"002660 } -\def\sphericalangle {\Umathchar "0"0"002222 } -\def\sqcap {\Umathchar "2"0"002293 } -\def\sqcup {\Umathchar "2"0"002294 } -\def\sqsubset {\Umathchar "3"0"00228F } -\def\sqsubseteq {\Umathchar "2"0"002291 } -\def\sqsubsetneq {\Umathchar "3"0"0022E4 } -\def\sqsupset {\Umathchar "3"0"002290 } -\def\sqsupseteq {\Umathchar "2"0"002292 } -\def\sqsupsetneq {\Umathchar "3"0"0022E5 } -\def\square {\Umathchar "0"0"0025A1 } -\def\squaredots {\Umathchar "3"0"002237 } -\def\star {\Umathchar "2"0"0022C6 } -\def\stareq {\Umathchar "3"0"00225B } -\def\subset {\Umathchar "3"0"002282 } -\def\Subset {\Umathchar "3"0"0022D0 } -\def\subseteq {\Umathchar "3"0"002286 } -\def\subseteqq {\Umathchar "3"0"002AC5 } -\def\subsetneq {\Umathchar "3"0"00228A } -\def\subsetneqq {\Umathchar "3"0"002ACB } -\def\succ {\Umathchar "3"0"00227B } -\def\succapprox {\Umathchar "3"0"002AB8 } -\def\succcurlyeq {\Umathchar "3"0"00227D } -\def\succeq {\Umathchar "3"0"002AB0 } -\def\succeqq {\Umathchar "3"0"002AB4 } -\def\succnapprox {\Umathchar "3"0"002ABA } -\def\succneq {\Umathchar "3"0"002AB2 } -\def\succneqq {\Umathchar "3"0"002AB6 } -\def\succnsim {\Umathchar "3"0"0022E9 } -\def\succsim {\Umathchar "3"0"00227F } -\def\sum {\Umathchar "1"0"002211 } -\def\supset {\Umathchar "3"0"002283 } -\def\Supset {\Umathchar "3"0"0022D1 } -\def\supseteq {\Umathchar "3"0"002287 } -\def\supseteqq {\Umathchar "3"0"002AC6 } -\def\supsetneq {\Umathchar "3"0"00228B } -\def\supsetneqq {\Umathchar "3"0"002ACC } -\def\surd {\Umathchar "2"0"00221A } -\def\swarrow {\Umathchar "3"0"002199 } -\def\Swarrow {\Umathchar "3"0"0021D9 } -\def\Tau {\Umathchar "0"0"0003A4 } -\def\tau {\Umathchar "0"0"0003C4 } -\def\therefore {\Umathchar "3"0"002234 } -\def\Theta {\Umathchar "0"0"000398 } -\def\theta {\Umathchar "0"0"0003B8 } -\def\tilde {\Umathaccent"0"0"0002DC } -\def\times {\Umathchar "2"0"0000D7 } -\def\to {\Umathchar "3"0"002192 } -\def\top {\Umathchar "0"0"0022A4 } -\def\triangle {\Umathchar "0"0"0025B3 } -\def\triangledown {\Umathchar "2"0"0025BD } -\def\triangleleft {\Umathchar "2"0"0025C1 } -\def\triangleq {\Umathchar "3"0"00225C } -\def\triangleright {\Umathchar "2"0"0025B7 } -\def\tripleprime {\Umathchar "0"0"002034 } -\def\turnediota {\Umathchar "0"0"002129 } -\def\twoheaddownarrow {\Umathchar "3"0"0021A1 } -\def\twoheadleftarrow {\Umathchar "3"0"00219E } -\def\twoheadrightarrow {\Umathchar "3"0"0021A0 } -\def\twoheadrightarrowtail {\Umathchar "3"0"002916 } -\def\twoheaduparrow {\Umathchar "3"0"00219F } -\def\udots {\Umathchar "0"0"0022F0 } -\def\ulcorner {\Udelimiter "4"0"00231C } -\def\underbar {\Umathaccent bottom "0"0"00203E } -\def\underbrace {\Umathaccent bottom "0"0"0023DF } -\def\underbracket {\Umathaccent bottom "0"0"0023B5 } -\def\underparent {\Umathaccent bottom "0"0"0023DD } -\def\upand {\Umathchar "2"0"00214B } -\def\uparrow {\Umathchar "3"0"002191 } -\def\Uparrow {\Umathchar "3"0"0021D1 } -\def\updasharrow {\Umathchar "3"0"0021E1 } -\def\updownarrow {\Umathchar "3"0"002195 } -\def\Updownarrow {\Umathchar "3"0"0021D5 } -\def\updownarrowbar {\Umathchar "0"0"0021A8 } -\def\updownarrows {\Umathchar "3"0"0021C5 } -\def\upharpoonleft {\Umathchar "3"0"0021BF } -\def\upharpoonright {\Umathchar "3"0"0021BE } -\def\uplus {\Umathchar "2"0"00228E } -\def\Upsilon {\Umathchar "0"0"0003A5 } -\def\upsilon {\Umathchar "0"0"0003C5 } -\def\upuparrows {\Umathchar "3"0"0021C8 } -\def\upwhitearrow {\Umathchar "0"0"0021E7 } -\def\urcorner {\Udelimiter "5"0"00231D } -\def\Uuparrow {\Umathchar "3"0"00290A } -\def\varepsilon {\Umathchar "0"0"0003B5 } -\def\varkappa {\Umathchar "0"0"0003F0 } -\def\varkappa {\Umathchar "0"0"0003F0 } -\def\varnothing {\Umathchar "0"0"002300 } -\def\varphi {\Umathchar "0"0"0003C6 } -\def\varpi {\Umathchar "0"0"0003D6 } -\def\varrho {\Umathchar "0"0"01D71A } -\def\varsigma {\Umathchar "0"0"0003C2 } -\def\vartheta {\Umathchar "0"0"01D717 } -\def\varTheta {\Umathchar "0"0"0003D1 } -\def\vdash {\Umathchar "3"0"0022A2 } -\def\vDash {\Umathchar "3"0"0022A8 } -\def\Vdash {\Umathchar "3"0"0022A9 } -\def\VDash {\Umathchar "3"0"0022AB } -\def\vdots {\Umathchar "0"0"0022EE } -\def\vec {\Umathaccent"0"0"0020D7 } -\def\vee {\Umathchar "2"0"002228 } -\def\veebar {\Umathchar "2"0"0022BB } -\def\veeeq {\Umathchar "3"0"00225A } -\def\vert {\Udelimiter "0"0"00007C } -\def\Vert {\Udelimiter "0"0"002016 } -\def\Vvdash {\Umathchar "3"0"0022AA } -\def\wedge {\Umathchar "2"0"002227 } -\def\wedgeeq {\Umathchar "3"0"002259 } -\def\whitearrowupfrombar {\Umathchar "0"0"0021EB } -\def\widehat {\Umathaccent"0"0"000302 } -\def\widetilde {\Umathaccent"0"0"000303 } -\def\wp {\Umathchar "0"0"002118 } -\def\wr {\Umathchar "2"0"002240 } -\def\Xi {\Umathchar "0"0"00039E } -\def\xi {\Umathchar "0"0"0003BE } -\def\yen {\Umathchar "0"0"0000A5 } -\def\Zeta {\Umathchar "0"0"000396 } -\def\zeta {\Umathchar "0"0"0003B6 } - -% a few definitions: - -\def\sqrt {\Uroot "0 "221A{}} -\def\root#1\of{\Uroot "0 "221A{#1}} - -% \skewchar\teni='177 \skewchar\seveni='177 \skewchar\fivei='177 -% \skewchar\tensy='60 \skewchar\sevensy='60 \skewchar\fivesy='60 - -\chardef\% = "25 -\chardef\& = "26 -\chardef\# = "23 -\chardef\$ = "24 -\chardef\_ = "5F - -\let\ss ß -\let\ae æ -\let\oe œ -\let\o ø -\let\AE Æ -\let\OE Œ -\let\O Ø -\let\i ı -\let\aa å -\let\l ł -\let\L Ł -\let\AA Å -\let\copyright © - -% just use utf - -\def\`#1{\string\`\string{#1\string}} -\def\'#1{\string\'\string{#1\string}} -\def\v#1{\string\v\string{#1\string}} -\def\u#1{\string\u\string{#1\string}} -\def\=#1{\string\=\string{#1\string}} -\def\^#1{\string\^\string{#1\string}} -\def\.#1{\string\.\string{#1\string}} -\def\H#1{\string\H\string{#1\string}} -\def\~#1{\string\~\string{#1\string}} -\def\"#1{\string\"\string{#1\string}} -\def\d#1{\string\d\string{#1\string}} -\def\b#1{\string\b\string{#1\string}} -\def\c#1{\string\c\string{#1\string}} - -\endinput diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-mplib.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-mplib.lua deleted file mode 100644 index 976bb59f91b..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-mplib.lua +++ /dev/null @@ -1,591 +0,0 @@ -if not modules then modules = { } end modules ['luatex-mplib'] = { - version = 1.001, - comment = "companion to luatex-mplib.tex", - author = "Hans Hagen & Taco Hoekwater", - copyright = "ConTeXt Development Team", - license = "public domain", -} - ---[[ldx-- -

This module is a stripped down version of libraries that are used -by . It can be used in other macro packages and/or -serve as an example. Embedding in a macro package is upto others and -normally boils down to inputting supp-mpl.tex.

---ldx]]-- - -if metapost and metapost.version then - - --[[ldx-- -

Let's silently quit and make sure that no one loads it - manually in .

- --ldx]]-- - -else - - local format, match, gsub = string.format, string.match, string.gsub - local concat = table.concat - local abs = math.abs - - local mplib = require ('mplib') - local kpse = require ('kpse') - - --[[ldx-- -

We create a namespace and some variables to it. If a namespace is - already defined it wil not be initialized. This permits hooking - in code beforehand.

- -

We don't make a format automatically. After all, distributions - might have their own preferences and normally a format (mem) file will - have some special place in the tree. Also, there can already - be format files, different memort settings and other nasty pitfalls that - we don't want to interfere with. If you want, you can define a function - metapost.make(name,mem_name) that does the job.

- --ldx]]-- - - metapost = metapost or { } - metapost.version = 1.00 - metapost.showlog = metapost.showlog or false - metapost.lastlog = "" - - --[[ldx-- -

A few helpers, taken from l-file.lua.

- --ldx]]-- - - local file = file or { } - - function file.replacesuffix(filename, suffix) - return (string.gsub(filename,"%.[%a%d]+$","")) .. "." .. suffix - end - - function file.stripsuffix(filename) - return (string.gsub(filename,"%.[%a%d]+$","")) - end - - --[[ldx-- -

We use the library unless a finder is already - defined.

- --ldx]]-- - - local mpkpse = kpse.new("luatex","mpost") - - metapost.finder = metapost.finder or function(name, mode, ftype) - if mode == "w" then - return name - else - return mpkpse:find_file(name,ftype) - end - end - - --[[ldx-- -

You can use your own reported if needed, as long as it handles multiple - arguments and formatted strings.

- --ldx]]-- - - metapost.report = metapost.report or function(...) - texio.write(format("",format(...))) - end - - --[[ldx-- -

The rest of this module is not documented. More info can be found in the - manual, articles in user group journals and the files that - ship with .

- --ldx]]-- - - function metapost.resetlastlog() - metapost.lastlog = "" - end - - local mplibone = tonumber(mplib.version()) <= 1.50 - - if mplibone then - - metapost.make = metapost.make or function(name,mem_name,dump) - local t = os.clock() - local mpx = mplib.new { - ini_version = true, - find_file = metapost.finder, - job_name = file.stripsuffix(name) - } - mpx:execute(string.format("input %s ;",name)) - if dump then - mpx:execute("dump ;") - metapost.report("format %s made and dumped for %s in %0.3f seconds",mem_name,name,os.clock()-t) - else - metapost.report("%s read in %0.3f seconds",name,os.clock()-t) - end - return mpx - end - - function metapost.load(name) - local mem_name = file.replacesuffix(name,"mem") - local mpx = mplib.new { - ini_version = false, - mem_name = mem_name, - find_file = metapost.finder - } - if not mpx and type(metapost.make) == "function" then - -- when i have time i'll locate the format and dump - mpx = metapost.make(name,mem_name) - end - if mpx then - metapost.report("using format %s",mem_name,false) - return mpx, nil - else - return nil, { status = 99, error = "out of memory or invalid format" } - end - end - - else - - local preamble = [[ - boolean mplib ; mplib := true ; - let dump = endinput ; - input %s ; - ]] - - metapost.make = metapost.make or function() - end - - local template = [[ - \pdfoutput=1 - \pdfpkresolution600 - \pdfcompresslevel=9 - %s\relax - \hsize=100in - \vsize=\hsize - \hoffset=-1in - \voffset=\hoffset - \topskip=0pt - \setbox0=\hbox{%s}\relax - \pageheight=\ht0 - \pagewidth=\wd0 - \box0 - \bye - ]] - - metapost.texrunner = "mtxrun --script plain" - - local texruns = 0 -- per document - local texhash = { } -- per document - - function metapost.maketext(mpd,str,what) - -- inefficient but one can always use metafun .. it's more a test - -- feature - local verbatimtex = mpd.verbatimtex - if not verbatimtex then - verbatimtex = { } - mpd.verbatimtex = verbatimtex - end - if what == 1 then - table.insert(verbatimtex,str) - else - local texcode = format(template,concat(verbatimtex,"\n"),str) - local texdone = texhash[texcode] - local jobname = tex.jobname - if not texdone then - texruns = texruns + 1 - texdone = texruns - texhash[texcode] = texdone - local texname = format("%s-mplib-%s.tmp",jobname,texdone) - local logname = format("%s-mplib-%s.log",jobname,texdone) - local pdfname = format("%s-mplib-%s.pdf",jobname,texdone) - io.savedata(texname,texcode) - os.execute(format("%s %s",metapost.texrunner,texname)) - os.remove(texname) - os.remove(logname) - end - return format('"image::%s-mplib-%s.pdf" infont defaultfont',jobname,texdone) - end - end - - local function mpprint(buffer,...) - for i=1,select("#",...) do - local value = select(i,...) - if value ~= nil then - local t = type(value) - if t == "number" then - buffer[#buffer+1] = format("%.16f",value) - elseif t == "string" then - buffer[#buffer+1] = value - elseif t == "table" then - buffer[#buffer+1] = "(" .. concat(value,",") .. ")" - else -- boolean or whatever - buffer[#buffer+1] = tostring(value) - end - end - end - end - - function metapost.runscript(mpd,code) - local code = loadstring(code) - if type(code) == "function" then - local buffer = { } - function metapost.print(...) - mpprint(buffer,...) - end - code() - -- mpd.buffer = buffer -- for tracing - return concat(buffer,"") - end - return "" - end - - function metapost.load(name) - local mpd = { - buffer = { }, - verbatim = { } - } - local mpx = mplib.new { - ini_version = true, - find_file = metapost.finder, - make_text = function(...) return metapost.maketext (mpd,...) end, - run_script = function(...) return metapost.runscript(mpd,...) end, - extensions = 1, - } - local result - if not mpx then - result = { status = 99, error = "out of memory"} - else - result = mpx:execute(format(preamble, file.replacesuffix(name,"mp"))) - end - metapost.reporterror(result) - return mpx, result - end - - end - - function metapost.unload(mpx) - if mpx then - mpx:finish() - end - end - - function metapost.reporterror(result) - if not result then - metapost.report("mp error: no result object returned") - elseif result.status > 0 then - local t, e, l = result.term, result.error, result.log - if t then - metapost.report("mp terminal: %s",t) - end - if e then - metapost.report("mp error: %s", e) - end - if not t and not e and l then - metapost.lastlog = metapost.lastlog .. "\n " .. l - metapost.report("mp log: %s",l) - else - metapost.report("mp error: unknown, no error, terminal or log messages") - end - else - return false - end - return true - end - - function metapost.process(mpx, data) - local converted, result = false, {} - mpx = metapost.load(mpx) - if mpx and data then - local result = mpx:execute(data) - if not result then - metapost.report("mp error: no result object returned") - elseif result.status > 0 then - metapost.report("mp error: %s",(result.term or "no-term") .. "\n" .. (result.error or "no-error")) - elseif metapost.showlog then - metapost.lastlog = metapost.lastlog .. "\n" .. result.term - metapost.report("mp info: %s",result.term or "no-term") - elseif result.fig then - converted = metapost.convert(result) - else - metapost.report("mp error: unknown error, maybe no beginfig/endfig") - end - else - metapost.report("mp error: mem file not found") - end - return converted, result - end - - local function getobjects(result,figure,f) - return figure:objects() - end - - function metapost.convert(result,flusher) - metapost.flush(result,flusher) - return true -- done - end - - --[[ldx-- -

We removed some message and tracing code. We might even remove the flusher

- --ldx]]-- - - local function pdf_startfigure(n,llx,lly,urx,ury) - tex.sprint(format("\\startMPLIBtoPDF{%s}{%s}{%s}{%s}",llx,lly,urx,ury)) - end - - local function pdf_stopfigure() - tex.sprint("\\stopMPLIBtoPDF") - end - - function pdf_literalcode(fmt,...) -- table - tex.sprint(format("\\MPLIBtoPDF{%s}",format(fmt,...))) - end - - function pdf_textfigure(font,size,text,width,height,depth) - local how, what = match(text,"^(.-)::(.+)$") - if how == "image" then - tex.sprint(format("\\MPLIBpdftext{%s}{%s}",what,depth)) - else - text = gsub(text,".","\\hbox{%1}") -- kerning happens in metapost - tex.sprint(format("\\MPLIBtextext{%s}{%s}{%s}{%s}",font,size,text,depth)) - end - end - - local bend_tolerance = 131/65536 - - local rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1 - - local function pen_characteristics(object) - local t = mplib.pen_info(object) - rx, ry, sx, sy, tx, ty = t.rx, t.ry, t.sx, t.sy, t.tx, t.ty - divider = sx*sy - rx*ry - return not (sx==1 and rx==0 and ry==0 and sy==1 and tx==0 and ty==0), t.width - end - - local function concatinated(px, py) -- no tx, ty here - return (sy*px-ry*py)/divider,(sx*py-rx*px)/divider - end - - local function curved(ith,pth) - local d = pth.left_x - ith.right_x - if abs(ith.right_x - ith.x_coord - d) <= bend_tolerance and abs(pth.x_coord - pth.left_x - d) <= bend_tolerance then - d = pth.left_y - ith.right_y - if abs(ith.right_y - ith.y_coord - d) <= bend_tolerance and abs(pth.y_coord - pth.left_y - d) <= bend_tolerance then - return false - end - end - return true - end - - local function flushnormalpath(path,open) - local pth, ith - for i=1,#path do - pth = path[i] - if not ith then - pdf_literalcode("%f %f m",pth.x_coord,pth.y_coord) - elseif curved(ith,pth) then - pdf_literalcode("%f %f %f %f %f %f c",ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord) - else - pdf_literalcode("%f %f l",pth.x_coord,pth.y_coord) - end - ith = pth - end - if not open then - local one = path[1] - if curved(pth,one) then - pdf_literalcode("%f %f %f %f %f %f c",pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord ) - else - pdf_literalcode("%f %f l",one.x_coord,one.y_coord) - end - elseif #path == 1 then - -- special case .. draw point - local one = path[1] - pdf_literalcode("%f %f l",one.x_coord,one.y_coord) - end - return t - end - - local function flushconcatpath(path,open) - pdf_literalcode("%f %f %f %f %f %f cm", sx, rx, ry, sy, tx ,ty) - local pth, ith - for i=1,#path do - pth = path[i] - if not ith then - pdf_literalcode("%f %f m",concatinated(pth.x_coord,pth.y_coord)) - elseif curved(ith,pth) then - local a, b = concatinated(ith.right_x,ith.right_y) - local c, d = concatinated(pth.left_x,pth.left_y) - pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concatinated(pth.x_coord, pth.y_coord)) - else - pdf_literalcode("%f %f l",concatinated(pth.x_coord, pth.y_coord)) - end - ith = pth - end - if not open then - local one = path[1] - if curved(pth,one) then - local a, b = concatinated(pth.right_x,pth.right_y) - local c, d = concatinated(one.left_x,one.left_y) - pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concatinated(one.x_coord, one.y_coord)) - else - pdf_literalcode("%f %f l",concatinated(one.x_coord,one.y_coord)) - end - elseif #path == 1 then - -- special case .. draw point - local one = path[1] - pdf_literalcode("%f %f l",concatinated(one.x_coord,one.y_coord)) - end - return t - end - - --[[ldx-- -

Support for specials has been removed.

- --ldx]]-- - - function metapost.flush(result,flusher) - if result then - local figures = result.fig - if figures then - for f=1, #figures do - metapost.report("flushing figure %s",f) - local figure = figures[f] - local objects = getobjects(result,figure,f) - local fignum = tonumber(match(figure:filename(),"([%d]+)$") or figure:charcode() or 0) - local miterlimit, linecap, linejoin, dashed = -1, -1, -1, false - local bbox = figure:boundingbox() - local llx, lly, urx, ury = bbox[1], bbox[2], bbox[3], bbox[4] -- faster than unpack - if urx < llx then - -- invalid - pdf_startfigure(fignum,0,0,0,0) - pdf_stopfigure() - else - pdf_startfigure(fignum,llx,lly,urx,ury) - pdf_literalcode("q") - if objects then - for o=1,#objects do - local object = objects[o] - local objecttype = object.type - if objecttype == "start_bounds" or objecttype == "stop_bounds" then - -- skip - elseif objecttype == "start_clip" then - pdf_literalcode("q") - flushnormalpath(object.path,t,false) - pdf_literalcode("W n") - elseif objecttype == "stop_clip" then - pdf_literalcode("Q") - miterlimit, linecap, linejoin, dashed = -1, -1, -1, false - elseif objecttype == "special" then - -- not supported - elseif objecttype == "text" then - local ot = object.transform -- 3,4,5,6,1,2 - pdf_literalcode("q %f %f %f %f %f %f cm",ot[3],ot[4],ot[5],ot[6],ot[1],ot[2]) - pdf_textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth) - pdf_literalcode("Q") - else - local cs = object.color - local cr = false - if cs and #cs > 0 then - cs, cr = metapost.colorconverter(cs) - pdf_literalcode(cs) - end - local ml = object.miterlimit - if ml and ml ~= miterlimit then - miterlimit = ml - pdf_literalcode("%f M",ml) - end - local lj = object.linejoin - if lj and lj ~= linejoin then - linejoin = lj - pdf_literalcode("%i j",lj) - end - local lc = object.linecap - if lc and lc ~= linecap then - linecap = lc - pdf_literalcode("%i J",lc) - end - local dl = object.dash - if dl then - local d = format("[%s] %i d",concat(dl.dashes or {}," "),dl.offset) - if d ~= dashed then - dashed = d - pdf_literalcode(dashed) - end - elseif dashed then - pdf_literalcode("[] 0 d") - dashed = false - end - local path = object.path - local transformed, penwidth = false, 1 - local open = path and path[1].left_type and path[#path].right_type - local pen = object.pen - if pen then - if pen.type == 'elliptical' then - transformed, penwidth = pen_characteristics(object) -- boolean, value - pdf_literalcode("%f w",penwidth) - if objecttype == 'fill' then - objecttype = 'both' - end - else -- calculated by mplib itself - objecttype = 'fill' - end - end - if transformed then - pdf_literalcode("q") - end - if path then - if transformed then - flushconcatpath(path,open) - else - flushnormalpath(path,open) - end - if objecttype == "fill" then - pdf_literalcode("h f") - elseif objecttype == "outline" then - pdf_literalcode((open and "S") or "h S") - elseif objecttype == "both" then - pdf_literalcode("h B") - end - end - if transformed then - pdf_literalcode("Q") - end - local path = object.htap - if path then - if transformed then - pdf_literalcode("q") - end - if transformed then - flushconcatpath(path,open) - else - flushnormalpath(path,open) - end - if objecttype == "fill" then - pdf_literalcode("h f") - elseif objecttype == "outline" then - pdf_literalcode((open and "S") or "h S") - elseif objecttype == "both" then - pdf_literalcode("h B") - end - if transformed then - pdf_literalcode("Q") - end - end - if cr then - pdf_literalcode(cr) - end - end - end - end - pdf_literalcode("Q") - pdf_stopfigure() - end - end - end - end - end - - function metapost.colorconverter(cr) - local n = #cr - if n == 4 then - local c, m, y, k = cr[1], cr[2], cr[3], cr[4] - return format("%.3f %.3f %.3f %.3f k %.3f %.3f %.3f %.3f K",c,m,y,k,c,m,y,k), "0 g 0 G" - elseif n == 3 then - local r, g, b = cr[1], cr[2], cr[3] - return format("%.3f %.3f %.3f rg %.3f %.3f %.3f RG",r,g,b,r,g,b), "0 g 0 G" - else - local s = cr[1] - return format("%.3f g %.3f G",s,s), "0 g 0 G" - end - end - -end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-mplib.tex b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-mplib.tex deleted file mode 100644 index f9de4b223f4..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-mplib.tex +++ /dev/null @@ -1,140 +0,0 @@ -%D \module -%D [ file=luatex-mplib, -%D version=2009.12.01, -%D title=\LUATEX\ Support Macros, -%D subtitle=\METAPOST\ to \PDF\ conversion, -%D author=Taco Hoekwater \& Hans Hagen, -%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] - -%D This is the companion to the \LUA\ module \type {supp-mpl.lua}. Further -%D embedding is up to others. A simple example of usage in plain \TEX\ is: -%D -%D \starttyping -%D \pdfoutput=1 -%D -%D \input luatex-mplib.tex -%D -%D \setmplibformat{plain} -%D -%D \mplibcode -%D beginfig(1); -%D draw fullcircle -%D scaled 10cm -%D withcolor red -%D withpen pencircle xscaled 4mm yscaled 2mm rotated 30 ; -%D endfig; -%D \endmplibcode -%D -%D \end -%D \stoptyping - -\def\setmplibformat#1{\def\mplibformat{#1}} - -\def\setupmplibcatcodes - {\catcode`\{=12 % could be optional .. not really needed - \catcode`\}=12 % could be optional .. not really needed - \catcode`\#=12 - \catcode`\^=12 - \catcode`\~=12 - \catcode`\_=12 - \catcode`\%=12 - \catcode`\&=12 - \catcode`\$=12 } - -\def\mplibcode - {\bgroup - \setupmplibcatcodes - \domplibcode} - -\long\def\domplibcode#1\endmplibcode - {\egroup - \directlua{metapost.process('\mplibformat',[[#1]])}} - -%D We default to \type {plain} \METAPOST: - -\def\mplibformat{plain} - -%D We use a dedicated scratchbox: - -\ifx\mplibscratchbox\undefined \newbox\mplibscratchbox \fi - -%D Now load the needed \LUA\ code. - -\directlua{dofile(kpse.find_file('luatex-mplib.lua'))} -% \directlua{dofile(resolvers.findfile('luatex-mplib.lua'))} - -%D The following code takes care of encapsulating the literals: - -\def\startMPLIBtoPDF#1#2#3#4% - {\hbox\bgroup - \xdef\MPllx{#1}\xdef\MPlly{#2}% - \xdef\MPurx{#3}\xdef\MPury{#4}% - \xdef\MPwidth{\the\dimexpr#3bp-#1bp\relax}% - \xdef\MPheight{\the\dimexpr#4bp-#2bp\relax}% - \parskip0pt% - \leftskip0pt% - \parindent0pt% - \everypar{}% - \setbox\mplibscratchbox\vbox\bgroup - \noindent} - -\def\stopMPLIBtoPDF - {\egroup - \setbox\mplibscratchbox\hbox - {\hskip-\MPllx bp% - \raise-\MPlly bp% - \box\mplibscratchbox}% - \setbox\mplibscratchbox\vbox to \MPheight - {\vfill - \hsize\MPwidth - \wd\mplibscratchbox0pt% - \ht\mplibscratchbox0pt% - \dp\mplibscratchbox0pt% - \box\mplibscratchbox}% - \wd\mplibscratchbox\MPwidth - \ht\mplibscratchbox\MPheight - \box\mplibscratchbox - \egroup} - -%D The body of picture, except for text items, is taken care of by: - -\ifnum\pdfoutput>0 - \let\MPLIBtoPDF\pdfliteral -\else - \def\MPLIBtoPDF#1{\special{pdf:literal direct #1}} % not ok yet -\fi - -%D Text items have a special handler: - -\def\MPLIBtextext#1#2#3#4% - {\begingroup - \setbox\mplibscratchbox\hbox - {\font\temp=#1 at #2bp% - \temp - #3}% - \setbox\mplibscratchbox\hbox - {\raise#4sp% - \box\mplibscratchbox}% - \wd\mplibscratchbox0pt% - \ht\mplibscratchbox0pt% - \dp\mplibscratchbox0pt% - \box\mplibscratchbox - \endgroup} - -\def\MPLIBpdftext#1#2% - {\ifcsname mplib::#1\endcsname - % already done, forgotten outside convert group - \message{}% - \else - \message{}% - \immediate\pdfximage{#1}% we cannot remove the file as it is included last - \expandafter\edef\csname mplib::#1\endcsname{\the\pdflastximage}% - \fi - \setbox\mplibscratchbox\hbox - {\raise#2sp\hbox{\pdfrefximage\csname mplib::#1\endcsname}}% - \wd\mplibscratchbox0pt% - \ht\mplibscratchbox0pt% - \dp\mplibscratchbox0pt% - \box\mplibscratchbox} - -\endinput diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-plain.tex b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-plain.tex deleted file mode 100644 index 0a806c76ff1..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-plain.tex +++ /dev/null @@ -1,55 +0,0 @@ -%D \module -%D [ file=luatex-plain, -%D version=2009.12.01, -%D title=\LUATEX\ Macros, -%D subtitle=Plain Format, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] - -\input plain - -\directlua {tex.enableprimitives('', tex.extraprimitives())} - -% We assume that pdf is used. - -\ifdefined\pdfextension - \input luatex-pdf \relax -\fi - -\outputmode 1 - -% \outputmode 0 \magnification\magstep5 - -% We set the page dimensions because otherwise the backend does weird things -% when we have for instance this on a line of its own: -% -% \hbox to 100cm {\hss wide indeed\hss} -% -% The page dimension calculation is a fuzzy one as there are some compensations -% for the \hoffset and \voffset and such. I remember long discussions and much -% trial and error in figuring this out during pdftex development times. Where -% a dvi driver will project on a papersize (and thereby clip) the pdf backend -% has to deal with the lack of a page concept on tex by some guessing. Normally -% a macro package will set the dimensions to something reasonable anyway. - -\pagewidth 8.5truein -\pageheight 11.0truein - -% We load some code at runtime: - -\everyjob \expandafter {% - \the\everyjob - \input {luatex-basics}% - \input {luatex-fonts}% - \input {luatex-math}% - \input {luatex-languages}% - \input {luatex-mplib}% - \input {luatex-gadgets}% -} - -% We also patch the version number: - -\edef\fmtversion{\fmtversion+luatex} - -\dump diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-preprocessor-test.tex b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-preprocessor-test.tex deleted file mode 100644 index 857b28f831b..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-preprocessor-test.tex +++ /dev/null @@ -1,30 +0,0 @@ -\ifdefined\inputpreprocessed - - \def\TestOne[#1]% - {test one: [#1]\par} - - \def\TestTwo#some% - {test two: #some\par} - - \def\TestThree[#whatever][#more]% - {test three: [#more] and [#whatever]\par} - - \def\TestFour[#one]#two% - {\def\TestFive[#alpha][#one]% - {test four and five: [#one], [#two] and [#alpha]}\par} - - \def\TestSix[#{one}]#{two}% - {test six: [#{one}] and #{two}\par} - - \TestOne [one] - \TestTwo {one} - \TestThree[one][two] - \TestFour [one]{two} - \TestFive [one][two] - \TestSix [one]{two} - -\else - \input{luatex-preprocessor.tex} - \inputpreprocessed{luatex-preprocessor-test.tex} - \expandafter \end -\fi diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-preprocessor.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-preprocessor.lua deleted file mode 100644 index 8faa0b47e69..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-preprocessor.lua +++ /dev/null @@ -1,163 +0,0 @@ -if not modules then modules = { } end modules ['luatex-preprocessor'] = { - version = 1.001, - comment = "companion to luatex-preprocessor.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - ---[[ldx -

This is a stripped down version of the preprocessor. In - we have a bit more, use a different logger, and -use a few optimizations. A few examples are shown at the end.

---ldx]] - -local rep, sub, gmatch = string.rep, string.sub, string.gmatch -local insert, remove = table.insert, table.remove -local setmetatable = setmetatable - -local stack, top, n, hashes = { }, nil, 0, { } - -local function set(s) - if top then - n = n + 1 - if n > 9 then - texio.write_nl("number of arguments > 9, ignoring: " .. s) - else - local ns = #stack - local h = hashes[ns] - if not h then - h = rep("#",ns) - hashes[ns] = h - end - m = h .. n - top[s] = m - return m - end - end -end - -local function get(s) - local m = top and top[s] or s - return m -end - -local function push() - top = { } - n = 0 - local s = stack[#stack] - if s then - setmetatable(top,{ __index = s }) - end - insert(stack,top) -end - -local function pop() - top = remove(stack) -end - -local leftbrace = lpeg.P("{") -local rightbrace = lpeg.P("}") -local escape = lpeg.P("\\") - -local space = lpeg.P(" ") -local spaces = space^1 -local newline = lpeg.S("\r\n") -local nobrace = 1 - leftbrace - rightbrace - -local name = lpeg.R("AZ","az")^1 -local longname = (leftbrace/"") * (nobrace^1) * (rightbrace/"") -local variable = lpeg.P("#") * lpeg.Cs(name + longname) -local escapedname = escape * name -local definer = escape * (lpeg.P("def") + lpeg.P("egdx") * lpeg.P("def")) -local anything = lpeg.P(1) -local always = lpeg.P(true) - -local pushlocal = always / push -local poplocal = always / pop -local declaration = variable / set -local identifier = variable / get - -local function matcherror(str,pos) - texio.write_nl("runaway definition at: " .. sub(str,pos-30,pos)) -end - -local parser = lpeg.Cs { "converter", - definition = pushlocal - * definer - * escapedname - * (declaration + (1-leftbrace))^0 - * lpeg.V("braced") - * poplocal, - braced = leftbrace - * ( lpeg.V("definition") - + identifier - + lpeg.V("braced") - + nobrace - )^0 - * (rightbrace + lpeg.Cmt(always,matcherror)), - converter = (lpeg.V("definition") + anything)^1, -} - ---[[ldx -

We provide a few commands.

---ldx]] - --- local texkpse - -local function find_file(...) - -- texkpse = texkpse or kpse.new("luatex","tex") - -- return texkpse:find_file(...) or "" - return kpse.find_file(...) or "" -end - -commands = commands or { } - -function commands.preprocessed(str) - return lpeg.match(parser,str) -end - -function commands.inputpreprocessed(name) - local name = find_file(name) or "" - if name ~= "" then - -- we could use io.loaddata as it's loaded in luatex-plain - local f = io.open(name,'rb') - if f then - texio.write("("..name) - local d = commands.preprocessed(f:read("*a")) - if d and d ~= "" then - texio.write("processed: " .. name) - for s in gmatch(d,"[^\n\r]+") do - tex.print(s) -- we do a dumb feedback - end - end - f:close() - texio.write(")") - else - tex.error("preprocessor error, invalid file: " .. name) - end - else - tex.error("preprocessor error, unknown file: " .. name) - end -end - -function commands.preprocessfile(oldfile,newfile) -- no checking - if oldfile and oldfile ~= newfile then - local f = io.open(oldfile,'rb') - if f then - local g = io.open(newfile,'wb') - if g then - g:write(lpeg.match(parser,f:read("*a") or "")) - g:close() - end - f:close() - end - end -end - ---~ print(preprocessed([[\def\test#oeps{test:#oeps}]])) ---~ print(preprocessed([[\def\test#oeps{test:#{oeps}}]])) ---~ print(preprocessed([[\def\test#{oeps:1}{test:#{oeps:1}}]])) ---~ print(preprocessed([[\def\test#{oeps}{test:#oeps}]])) ---~ preprocessed([[\def\test#{oeps}{test:#oeps \halign{##\cr #oeps\cr}]]) ---~ print(preprocessed([[\def\test#{oeps}{test:#oeps \halign{##\cr #oeps\cr}}]])) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-preprocessor.tex b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-preprocessor.tex deleted file mode 100644 index 03b483f4185..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-preprocessor.tex +++ /dev/null @@ -1,14 +0,0 @@ -%D \module -%D [ file=luatex-preprocessor, -%D version=2010.12.02, -%D title=\LUATEX\ Support Macros, -%D subtitle=Generic Preprocessor, -%D author=Hans Hagen, -%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] - -\directlua{dofile(kpse.find_file('luatex-preprocessor.lua'))} - -\def\inputpreprocessed#1% - {\directlua{commands.inputpreprocessed("#1")}} - -\endinput diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-reference.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-reference.lua index 3a4a70c6261..8ee2fcb32de 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-reference.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-reference.lua @@ -1,6 +1,6 @@ -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua -- parent file : c:/data/develop/context/sources/luatex-fonts.lua --- merge date : 01/27/17 14:39:39 +-- merge date : 09/24/18 14:11:29 do -- begin closure to overcome local limits and interference @@ -11,14 +11,16 @@ if not modules then modules={} end modules ['l-lua']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -_MAJORVERSION,_MINORVERSION=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$") -_MAJORVERSION=tonumber(_MAJORVERSION) or 5 -_MINORVERSION=tonumber(_MINORVERSION) or 1 -_LUAVERSION=_MAJORVERSION+_MINORVERSION/10 -if _LUAVERSION<5.2 and jit then - _MINORVERSION=2 - _LUAVERSION=5.2 -end +local next,type,tonumber=next,type,tonumber +LUAMAJORVERSION,LUAMINORVERSION=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$") +LUAMAJORVERSION=tonumber(LUAMAJORVERSION) or 5 +LUAMINORVERSION=tonumber(LUAMINORVERSION) or 1 +LUAVERSION=LUAMAJORVERSION+LUAMINORVERSION/10 +if LUAVERSION<5.2 and jit then + MINORVERSION=2 + LUAVERSION=5.2 +end +_LUAVERSION=LUAVERSION if not lpeg then lpeg=require("lpeg") end @@ -108,6 +110,19 @@ if flush then local spawn=os.spawn if spawn then function os.spawn (...) flush() return spawn (...) end end local popen=io.popen if popen then function io.popen (...) flush() return popen (...) end end end +FFISUPPORTED=type(ffi)=="table" and ffi.os~="" and ffi.arch~="" and ffi.load +if not FFISUPPORTED then + local okay;okay,ffi=pcall(require,"ffi") + FFISUPPORTED=type(ffi)=="table" and ffi.os~="" and ffi.arch~="" and ffi.load +end +if not FFISUPPORTED then + ffi=nil +elseif not ffi.number then + ffi.number=tonumber +end +if not bit32 then + bit32=require("l-bit32") +end end -- closure @@ -120,7 +135,8 @@ if not modules then modules={} end modules ['l-lpeg']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -lpeg=require("lpeg") +lpeg=require("lpeg") +local lpeg=lpeg if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end local type,next,tostring=type,next,tostring local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.format @@ -142,11 +158,14 @@ patterns.alwaysmatched=alwaysmatched local sign=S('+-') local zero=P('0') local digit=R('09') +local digits=digit^1 local octdigit=R("07") +local octdigits=octdigit^1 local lowercase=R("az") local uppercase=R("AZ") local underscore=P("_") local hexdigit=digit+lowercase+uppercase +local hexdigits=hexdigit^1 local cr,lf,crlf=P("\r"),P("\n"),P("\r\n") local newline=P("\r")*(P("\n")+P(true))+P("\n") local escaped=P("\\")*anything @@ -203,15 +222,17 @@ patterns.nonwhitespace=nonwhitespace local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0) local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0)) +local nospacer=Cs((whitespace^1/""+nonwhitespace^1)^0) local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0) -local e_collapser=Cs((whitespace^1*P(-1)/""+nonwhitespace^1+whitespace^1/" ")^0) +local e_collapser=Cs((whitespace^1*endofstring/""+nonwhitespace^1+whitespace^1/" ")^0) local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0) local b_stripper=Cs(spacer^0/""*(nonspacer^1+spacer^1/" ")^0) -local e_stripper=Cs((spacer^1*P(-1)/""+nonspacer^1+spacer^1/" ")^0) +local e_stripper=Cs((spacer^1*endofstring/""+nonspacer^1+spacer^1/" ")^0) local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0) patterns.stripper=stripper patterns.fullstripper=fullstripper patterns.collapser=collapser +patterns.nospacer=nospacer patterns.b_collapser=b_collapser patterns.m_collapser=m_collapser patterns.e_collapser=e_collapser @@ -246,33 +267,36 @@ patterns.singlequoted=squote*patterns.nosquote*squote patterns.doublequoted=dquote*patterns.nodquote*dquote patterns.quoted=patterns.doublequoted+patterns.singlequoted patterns.digit=digit +patterns.digits=digits patterns.octdigit=octdigit +patterns.octdigits=octdigits patterns.hexdigit=hexdigit +patterns.hexdigits=hexdigits patterns.sign=sign -patterns.cardinal=digit^1 -patterns.integer=sign^-1*digit^1 -patterns.unsigned=digit^0*period*digit^1 +patterns.cardinal=digits +patterns.integer=sign^-1*digits +patterns.unsigned=digit^0*period*digits patterns.float=sign^-1*patterns.unsigned -patterns.cunsigned=digit^0*comma*digit^1 -patterns.cpunsigned=digit^0*(period+comma)*digit^1 +patterns.cunsigned=digit^0*comma*digits +patterns.cpunsigned=digit^0*(period+comma)*digits patterns.cfloat=sign^-1*patterns.cunsigned patterns.cpfloat=sign^-1*patterns.cpunsigned patterns.number=patterns.float+patterns.integer patterns.cnumber=patterns.cfloat+patterns.integer patterns.cpnumber=patterns.cpfloat+patterns.integer -patterns.oct=zero*octdigit^1 +patterns.oct=zero*octdigits patterns.octal=patterns.oct patterns.HEX=zero*P("X")*(digit+uppercase)^1 patterns.hex=zero*P("x")*(digit+lowercase)^1 -patterns.hexadecimal=zero*S("xX")*hexdigit^1 -patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigit^1+hexdigit^1*period*hexdigit^0+hexdigit^1)*(S("pP")*sign^-1*hexdigit^1)^-1 -patterns.decafloat=sign^-1*(digit^0*period*digit^1+digit^1*period*digit^0+digit^1)*S("eE")*sign^-1*digit^1 +patterns.hexadecimal=zero*S("xX")*hexdigits +patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigits+hexdigits*period*hexdigit^0+hexdigits)*(S("pP")*sign^-1*hexdigits)^-1 +patterns.decafloat=sign^-1*(digit^0*period*digits+digits*period*digit^0+digits)*S("eE")*sign^-1*digits patterns.propername=(uppercase+lowercase+underscore)*(uppercase+lowercase+underscore+digit)^0*endofstring patterns.somecontent=(anything-newline-space)^1 patterns.beginline=#(1-newline) -patterns.longtostring=Cs(whitespace^0/""*((patterns.quoted+nonwhitespace^1+whitespace^1/""*(P(-1)+Cc(" ")))^0)) -local function anywhere(pattern) - return P { P(pattern)+1*V(1) } +patterns.longtostring=Cs(whitespace^0/""*((patterns.quoted+nonwhitespace^1+whitespace^1/""*(endofstring+Cc(" ")))^0)) +function anywhere(pattern) + return (1-P(pattern))^0*P(pattern) end lpeg.anywhere=anywhere function lpeg.instringchecker(p) @@ -513,11 +537,13 @@ function lpeg.balancer(left,right) left,right=P(left),P(right) return P { left*((1-left-right)+V(1))^0*right } end -local nany=utf8char/"" -function lpeg.counter(pattern) - pattern=Cs((P(pattern)/" "+nany)^0) - return function(str) - return #lpegmatch(pattern,str) +function lpeg.counter(pattern,action) + local n=0 + local pattern=(P(pattern)/function() n=n+1 end+anything)^0 + if action then + return function(str) n=0;lpegmatch(pattern,str);action(n) end + else + return function(str) n=0;lpegmatch(pattern,str);return n end end end utf=utf or (unicode and unicode.utf8) or {} @@ -678,27 +704,13 @@ function lpeg.append(list,pp,delayed,checked) end local p_false=P(false) local p_true=P(true) -local function make(t) - local function making(t) - local p=p_false - local keys=sortedkeys(t) - for i=1,#keys do - local k=keys[i] - if k~="" then - local v=t[k] - if v==true then - p=p+P(k)*p_true - elseif v==false then - else - p=p+P(k)*making(v) - end - end - end - if t[""] then - p=p+p_true - end - return p - end +local lower=utf and utf.lower or string.lower +local upper=utf and utf.upper or string.upper +function lpeg.setutfcasers(l,u) + lower=l or lower + upper=u or upper +end +local function make1(t,rest) local p=p_false local keys=sortedkeys(t) for i=1,#keys do @@ -709,38 +721,36 @@ local function make(t) p=p+P(k)*p_true elseif v==false then else - p=p+P(k)*making(v) + p=p+P(k)*make1(v,v[""]) end end end + if rest then + p=p+p_true + end return p end -local function collapse(t,x) - if type(t)~="table" then - return t,x - else - local n=next(t) - if n==nil then - return t,x - elseif next(t,n)==nil then - local k=n +local function make2(t,rest) + local p=p_false + local keys=sortedkeys(t) + for i=1,#keys do + local k=keys[i] + if k~="" then local v=t[k] - if type(v)=="table" then - return collapse(v,x..k) + if v==true then + p=p+(P(lower(k))+P(upper(k)))*p_true + elseif v==false then else - return v,x..k - end - else - local tt={} - for k,v in next,t do - local vv,kk=collapse(v,k) - tt[kk]=vv + p=p+(P(lower(k))+P(upper(k)))*make2(v,v[""]) end - return tt,x end end + if rest then + p=p+p_true + end + return p end -function lpeg.utfchartabletopattern(list) +local function utfchartabletopattern(list,insensitive) local tree={} local n=#list if n==0 then @@ -811,7 +821,14 @@ function lpeg.utfchartabletopattern(list) end end end - return make(tree) + return (insensitive and make2 or make1)(tree) +end +lpeg.utfchartabletopattern=utfchartabletopattern +function lpeg.utfreplacer(list,insensitive) + local pattern=Cs((utfchartabletopattern(list,insensitive)/list+utf8character)^0) + return function(str) + return lpegmatch(pattern,str) or str + end end patterns.containseol=lpeg.finder(eol) local function nextstep(n,step,result) @@ -842,12 +859,21 @@ end function lpeg.times(pattern,n) return P(nextstep(n,2^16,{ "start",["1"]=pattern })) end -local trailingzeros=zero^0*-digit -local case_1=period*trailingzeros/"" -local case_2=period*(digit-trailingzeros)^1*(trailingzeros/"") -local number=digit^1*(case_1+case_2) -local stripper=Cs((number+1)^0) -lpeg.patterns.stripzeros=stripper +do + local trailingzeros=zero^0*-digit + local stripper=Cs(( + digits*( + period*trailingzeros/""+period*(digit-trailingzeros)^1*(trailingzeros/"") + )+1 + )^0) + lpeg.patterns.stripzeros=stripper + local nonzero=digit-zero + local trailingzeros=zero^1*endofstring + local stripper=Cs((1-period)^0*( + period*trailingzeros/""+period*(nonzero^1+(trailingzeros/"")+zero^1)^0+endofstring + )) + lpeg.patterns.stripzero=stripper +end local byte_to_HEX={} local byte_to_hex={} local byte_to_dec={} @@ -907,6 +933,21 @@ function string.tobytes(s) return lpegmatch(hextobytes,s) end end +local patterns={} +local function containsws(what) + local p=patterns[what] + if not p then + local p1=P(what)*(whitespace+endofstring)*Cc(true) + local p2=whitespace*P(p1) + p=P(p1)+P(1-p2)^0*p2+Cc(false) + patterns[what]=p + end + return p +end +lpeg.containsws=containsws +function string.containsws(str,what) + return lpegmatch(patterns[what] or containsws(what),str) +end end -- closure @@ -962,6 +1003,7 @@ end local stripper=patterns.stripper local fullstripper=patterns.fullstripper local collapser=patterns.collapser +local nospacer=patterns.nospacer local longtostring=patterns.longtostring function string.strip(str) return str and lpegmatch(stripper,str) or "" @@ -972,6 +1014,9 @@ end function string.collapsespaces(str) return str and lpegmatch(collapser,str) or "" end +function string.nospaces(str) + return str and lpegmatch(nospacer,str) or "" +end function string.longtostring(str) return str and lpegmatch(longtostring,str) or "" end @@ -1022,6 +1067,21 @@ function string.tformat(fmt,...) end string.quote=string.quoted string.unquote=string.unquoted +if not string.bytetable then + local limit=5000 + function string.bytetable(str) + local n=#str + if n>limit then + local t={ byte(str,1,limit) } + for i=limit+1,n do + t[i]=byte(str,i) + end + return t + else + return { byte(str,1,n) } + end + end +end end -- closure @@ -1034,7 +1094,7 @@ if not modules then modules={} end modules ['l-table']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local type,next,tostring,tonumber,ipairs,select=type,next,tostring,tonumber,ipairs,select +local type,next,tostring,tonumber,select=type,next,tostring,tonumber,select local table,string=table,string local concat,sort,insert,remove=table.concat,table.sort,table.insert,table.remove local format,lower,dump=string.format,string.lower,string.dump @@ -1043,6 +1103,9 @@ local getinfo=debug.getinfo local lpegmatch,patterns=lpeg.match,lpeg.patterns local floor=math.floor local stripper=patterns.stripper +function table.getn(t) + return t and #t +end function table.strip(tab) local lst,l={},0 for i=1,#tab do @@ -1336,7 +1399,7 @@ function table.tohash(t,value) local h={} if t then if value==nil then value=true end - for _,v in next,t do + for _,v in next,t do h[v]=value end end @@ -1344,7 +1407,7 @@ function table.tohash(t,value) end function table.fromhash(t) local hsh,h={},0 - for k,v in next,t do + for k,v in next,t do if v then h=h+1 hsh[h]=k @@ -1358,13 +1421,17 @@ local reserved=table.tohash { 'in','local','nil','not','or','repeat','return','then','true','until','while', 'NaN','goto', } -local function simple_table(t) +local function is_simple_table(t,hexify) local nt=#t if nt>0 then local n=0 for _,v in next,t do n=n+1 + if type(v)=="table" then + return nil + end end + local haszero=rawget(t,0) if n==nt then local tt={} for i=1,nt do @@ -1374,10 +1441,10 @@ local function simple_table(t) if hexify then tt[i]=format("0x%X",v) else - tt[i]=tostring(v) + tt[i]=v end elseif tv=="string" then - tt[i]=format("%q",v) + tt[i]=format("%q",v) elseif tv=="boolean" then tt[i]=v and "true" or "false" else @@ -1385,10 +1452,32 @@ local function simple_table(t) end end return tt + elseif haszero and (n==nt+1) then + local tt={} + for i=0,nt do + local v=t[i] + local tv=type(v) + if tv=="number" then + if hexify then + tt[i+1]=format("0x%X",v) + else + tt[i+1]=v + end + elseif tv=="string" then + tt[i+1]=format("%q",v) + elseif tv=="boolean" then + tt[i+1]=v and "true" or "false" + else + return nil + end + end + tt[1]="[0] = "..tt[1] + return tt end end return nil end +table.is_simple_table=is_simple_table local propername=patterns.propername local function dummy() end local function do_serialize(root,name,depth,level,indexed) @@ -1450,7 +1539,7 @@ local function do_serialize(root,name,depth,level,indexed) if next(v)==nil then handle(format("%s {},",depth)) elseif inline then - local st=simple_table(v) + local st=is_simple_table(v,hexify) if st then handle(format("%s { %s },",depth,concat(st,", "))) else @@ -1533,7 +1622,7 @@ local function do_serialize(root,name,depth,level,indexed) handle(format("%s [%q]={},",depth,k)) end elseif inline then - local st=simple_table(v) + local st=is_simple_table(v,hexify) if st then if tk=="number" then if hexify then @@ -1793,7 +1882,9 @@ function table.unnest(t) return unnest(t) end local function are_equal(a,b,n,m) - if a and b and #a==#b then + if a==b then + return true + elseif a and b and #a==#b then n=n or 1 m=m or #a for i=n,m do @@ -1813,15 +1904,17 @@ local function are_equal(a,b,n,m) end end local function identical(a,b) - for ka,va in next,a do - local vb=b[ka] - if va==vb then - elseif type(va)=="table" and type(vb)=="table" then - if not identical(va,vb) then + if a~=b then + for ka,va in next,a do + local vb=b[ka] + if va==vb then + elseif type(va)=="table" and type(vb)=="table" then + if not identical(va,vb) then + return false + end + else return false end - else - return false end end return true @@ -1904,25 +1997,33 @@ function table.reversed(t) return tt end end -function table.reverse(t) +function table.reverse(t) if t then local n=#t - for i=1,floor(n/2) do - local j=n-i+1 + local m=n+1 + for i=1,floor(n/2) do + local j=m-i t[i],t[j]=t[j],t[i] end return t end end -function table.sequenced(t,sep,simple) +local function sequenced(t,sep,simple) if not t then return "" + elseif type(t)=="string" then + return t end local n=#t local s={} if n>0 then for i=1,n do - s[i]=tostring(t[i]) + local v=t[i] + if type(v)=="table" then + s[i]="{"..sequenced(v,sep,simple).."}" + else + s[i]=tostring(t[i]) + end end else n=0 @@ -1933,16 +2034,25 @@ function table.sequenced(t,sep,simple) s[n]=k elseif v and v~="" then n=n+1 - s[n]=k.."="..tostring(v) + if type(v)=="table" then + s[n]=k.."={"..sequenced(v,sep,simple).."}" + else + s[n]=k.."="..tostring(v) + end end else n=n+1 - s[n]=k.."="..tostring(v) + if type(v)=="table" then + s[n]=k.."={"..sequenced(v,sep,simple).."}" + else + s[n]=k.."="..tostring(v) + end end end end return concat(s,sep or " | ") end +table.sequenced=sequenced function table.print(t,...) if type(t)~="table" then print(tostring(t)) @@ -2043,6 +2153,24 @@ function table.filtered(t,pattern,sort,cmp) return nothing end end +if not table.move then + function table.move(a1,f,e,t,a2) + if a2 and a1~=a2 then + for i=f,e do + a2[t]=a1[i] + t=t+1 + end + return a2 + else + t=t+e-f + for i=e,f,-1 do + a1[t]=a1[i] + t=t-1 + end + return a1 + end + end +end end -- closure @@ -2059,51 +2187,36 @@ local io=io local open,flush,write,read=io.open,io.flush,io.write,io.read local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format local concat=table.concat -local floor=math.floor local type=type if string.find(os.getenv("PATH"),";",1,true) then io.fileseparator,io.pathseparator="\\",";" else io.fileseparator,io.pathseparator="/",":" end -local function readall(f) - return f:read("*all") -end +local large=0x01000000 +local medium=0x00100000 +local small=0x00020000 local function readall(f) local size=f:seek("end") - if size==0 then - return "" - end - f:seek("set",0) - if size<1024*1024 then - return f:read('*all') + if size>0 then + f:seek("set",0) + return f:read(size) else - local step - if size>16*1024*1024 then - step=16*1024*1024 - else - step=floor(size/(1024*1024))*1024*1024/8 - end - local data={} - while true do - local r=f:read(step) - if not r then - return concat(data) - else - data[#data+1]=r - end - end + return "" end end io.readall=readall function io.loaddata(filename,textmode) local f=open(filename,(textmode and 'r') or 'rb') if f then - local data=readall(f) - f:close() - if #data>0 then - return data + local size=f:seek("end") + local data=nil + if size>0 then + f:seek("set",0) + data=f:read(size) end + f:close() + return data end end function io.copydata(source,target,action) @@ -2112,37 +2225,14 @@ function io.copydata(source,target,action) local g=open(target,"wb") if g then local size=f:seek("end") - if size==0 then - else + if size>0 then f:seek("set",0) - if size<1024*1024 then - local data=f:read('*all') - if action then - data=action(data) - end - if data then - g:write(data) - end - else - local step - if size>16*1024*1024 then - step=16*1024*1024 - else - step=floor(size/(1024*1024))*1024*1024/8 - end - while true do - local data=f:read(step) - if data then - if action then - data=action(data) - end - if data then - g:write(data) - end - else - break - end - end + local data=f:read(size) + if action then + data=action(data) + end + if data then + g:write(data) end end g:close() @@ -2168,29 +2258,59 @@ function io.savedata(filename,data,joiner) return false end end -function io.loadlines(filename,n) - local f=open(filename,'r') - if not f then - elseif n then - local lines={} - for i=1,n do - local line=f:read("*lines") - if line then - lines[#lines+1]=line - else - break +if fio and fio.readline then + local readline=fio.readline + function io.loadlines(filename,n) + local f=open(filename,'r') + if not f then + elseif n then + local lines={} + for i=1,n do + local line=readline(f) + if line then + lines[i]=line + else + break + end + end + f:close() + lines=concat(lines,"\n") + if #lines>0 then + return lines + end + else + local line=readline(f) + f:close() + if line and #line>0 then + return line end end - f:close() - lines=concat(lines,"\n") - if #lines>0 then - return lines - end - else - local line=f:read("*line") or "" - f:close() - if #line>0 then - return line + end +else + function io.loadlines(filename,n) + local f=open(filename,'r') + if not f then + elseif n then + local lines={} + for i=1,n do + local line=f:read("*lines") + if line then + lines[i]=line + else + break + end + end + f:close() + lines=concat(lines,"\n") + if #lines>0 then + return lines + end + else + local line=f:read("*line") or "" + f:close() + if #line>0 then + return line + end end end end @@ -2351,7 +2471,7 @@ function io.ask(question,default,options) end end end -local function readnumber(f,n,m) +local function readnumber(f,n,m) if m then f:seek("set",n) n=m @@ -2360,31 +2480,31 @@ local function readnumber(f,n,m) return byte(f:read(1)) elseif n==2 then local a,b=byte(f:read(2),1,2) - return 256*a+b + return 0x100*a+b elseif n==3 then local a,b,c=byte(f:read(3),1,3) - return 256*256*a+256*b+c + return 0x10000*a+0x100*b+c elseif n==4 then local a,b,c,d=byte(f:read(4),1,4) - return 256*256*256*a+256*256*b+256*c+d + return 0x1000000*a+0x10000*b+0x100*c+d elseif n==8 then local a,b=readnumber(f,4),readnumber(f,4) - return 256*a+b + return 0x100*a+b elseif n==12 then local a,b,c=readnumber(f,4),readnumber(f,4),readnumber(f,4) - return 256*256*a+256*b+c + return 0x10000*a+0x100*b+c elseif n==-2 then local b,a=byte(f:read(2),1,2) - return 256*a+b + return 0x100*a+b elseif n==-3 then local c,b,a=byte(f:read(3),1,3) - return 256*256*a+256*b+c + return 0x10000*a+0x100*b+c elseif n==-4 then local d,c,b,a=byte(f:read(4),1,4) - return 256*256*256*a+256*256*b+256*c+d + return 0x1000000*a+0x10000*b+0x100*c+d elseif n==-8 then local h,g,f,e,d,c,b,a=byte(f:read(8),1,8) - return 256*256*256*256*256*256*256*a+256*256*256*256*256*256*b+256*256*256*256*256*c+256*256*256*256*d+256*256*256*e+256*256*f+256*g+h + return 0x100000000000000*a+0x1000000000000*b+0x10000000000*c+0x100000000*d+0x1000000*e+0x10000*f+0x100*g+h else return 0 end @@ -2421,21 +2541,22 @@ local lpegmatch=lpeg.match local getcurrentdir,attributes=lfs.currentdir,lfs.attributes local checkedsplit=string.checkedsplit local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct -local tricky=S("/\\")*P(-1) local attributes=lfs.attributes -if sandbox then - sandbox.redefine(lfs.isfile,"lfs.isfile") - sandbox.redefine(lfs.isdir,"lfs.isdir") -end function lfs.isdir(name) - if lpegmatch(tricky,name) then - return attributes(name,"mode")=="directory" - else - return attributes(name.."/.","mode")=="directory" - end + return attributes(name,"mode")=="directory" end function lfs.isfile(name) - return attributes(name,"mode")=="file" + local a=attributes(name,"mode") + return a=="file" or a=="link" or nil +end +function lfs.isfound(name) + local a=attributes(name,"mode") + return (a=="file" or a=="link") and name or nil +end +if sandbox then + sandbox.redefine(lfs.isfile,"lfs.isfile") + sandbox.redefine(lfs.isdir,"lfs.isdir") + sandbox.redefine(lfs.isfound,"lfs.isfound") end local colon=P(":") local period=P(".") @@ -2775,6 +2896,27 @@ function lfs.mkdirs(path) lfs.mkdir(full) end end +function file.withinbase(path) + local l=0 + if not find(path,"^/") then + path="/"..path + end + for dir in gmatch(path,"/([^/]+)") do + if dir==".." then + l=l-1 + elseif dir~="." then + l=l+1 + end + if l<0 then + return false + end + end + return true +end +local symlinkattributes=lfs.symlinkattributes +function lfs.readlink(name) + return symlinkattributes(name,"target") or nil +end end -- closure @@ -2854,21 +2996,23 @@ if not modules then modules={} end modules ['l-math']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local floor,sin,cos,tan=math.floor,math.sin,math.cos,math.tan if not math.ceiling then math.ceiling=math.ceil end if not math.round then + local floor=math.floor function math.round(x) return floor(x+0.5) end end if not math.div then + local floor=math.floor function math.div(n,m) return floor(n/m) end end if not math.mod then function math.mod(n,m) return n%m end end -local pipi=2*math.pi/360 if not math.sind then + local sin,cos,tan=math.sin,math.cos,math.tan + local pipi=2*math.pi/360 function math.sind(d) return sin(d*pipi) end function math.cosd(d) return cos(d*pipi) end function math.tand(d) return tan(d*pipi) end @@ -2877,6 +3021,60 @@ if not math.odd then function math.odd (n) return n%2~=0 end function math.even(n) return n%2==0 end end +if not math.cosh then + local exp=math.exp + function math.cosh(x) + local xx=exp(x) + return (xx+1/xx)/2 + end + function math.sinh(x) + local xx=exp(x) + return (xx-1/xx)/2 + end + function math.tanh(x) + local xx=exp(x) + return (xx-1/xx)/(xx+1/xx) + end +end +if not math.pow then + function math.pow(x,y) + return x^y + end +end +if not math.atan2 then + math.atan2=math.atan +end +if not math.ldexp then + function math.ldexp(x,e) + return x*2.0^e + end +end +if not math.log10 then + local log=math.log + function math.log10(x) + return log(x,10) + end +end +if not math.type then + function math.type() + return "float" + end +end +if not math.tointeger then + math.mininteger=-0x4FFFFFFFFFFF + math.maxinteger=0x4FFFFFFFFFFF + local floor=math.floor + function math.tointeger(n) + local f=floor(n) + return f==n and f or nil + end +end +if not math.ult then + local floor=math.floor + function math.tointeger(m,n) + return floor(m)0 then + local d=n-l + if d>0 then + return rep(c or " ",d)..s + end + else + local d=- n-l + if d>0 then + return s..rep(c or " ",d) + end + end + end + return s +end end -- closure @@ -3508,13 +3785,15 @@ utilities.strings=utilities.strings or {} local strings=utilities.strings local format,gsub,rep,sub,find=string.format,string.gsub,string.rep,string.sub,string.find local load,dump=load,string.dump -local tonumber,type,tostring=tonumber,type,tostring +local tonumber,type,tostring,next,setmetatable=tonumber,type,tostring,next,setmetatable +local unpack,concat=table.unpack,table.concat local unpack,concat=table.unpack,table.concat local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc local patterns,lpegmatch=lpeg.patterns,lpeg.match -local utfchar,utfbyte=utf.char,utf.byte +local utfchar,utfbyte,utflen=utf.char,utf.byte,utf.len local loadstripped=nil -if _LUAVERSION<5.2 then +local oldfashioned=LUAVERSION<5.2 +if oldfashioned then loadstripped=function(str,shortcuts) return load(str) end @@ -3528,25 +3807,52 @@ else end end if not number then number={} end -local stripper=patterns.stripzeros +local stripzero=patterns.stripzero +local stripzeros=patterns.stripzeros local newline=patterns.newline local endofstring=patterns.endofstring +local anything=patterns.anything local whitespace=patterns.whitespace +local space=patterns.space local spacer=patterns.spacer local spaceortab=patterns.spaceortab +local digit=patterns.digit +local sign=patterns.sign +local period=patterns.period +local ptf=1/65536 +local bpf=(7200/7227)/65536 local function points(n) + if n==0 then + return "0pt" + end n=tonumber(n) - return (not n or n==0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536)) + if not n or n==0 then + return "0pt" + end + n=n*ptf + if n%1==0 then + return format("%ipt",n) + end + return lpegmatch(stripzeros,format("%.5fpt",n)) end local function basepoints(n) + if n==0 then + return "0pt" + end n=tonumber(n) - return (not n or n==0) and "0bp" or lpegmatch(stripper,format("%.5fbp",n*(7200/7227)/65536)) + if not n or n==0 then + return "0pt" + end + n=n*bpf + if n%1==0 then + return format("%ibp",n) + end + return lpegmatch(stripzeros,format("%.5fbp",n)) end number.points=points number.basepoints=basepoints local rubish=spaceortab^0*newline local anyrubish=spaceortab+newline -local anything=patterns.anything local stripped=(spaceortab^1/"")*newline local leading=rubish^0/"" local trailing=(anyrubish^1*endofstring)/"" @@ -3597,24 +3903,39 @@ local pattern=Carg(1)/function(t) end end+newline*Cp()/function(position) extra,start=0,position - end+patterns.anything + end+anything )^1) function strings.tabtospace(str,tab) return lpegmatch(pattern,str,1,tab or 7) end -local space=spacer^0 -local nospace=space/"" +function string.utfpadding(s,n) + if not n or n==0 then + return "" + end + local l=utflen(s) + if n>0 then + return nspaces[n-l] + else + return nspaces[-n-l] + end +end +local optionalspace=spacer^0 +local nospace=optionalspace/"" local endofline=nospace*newline local stripend=(whitespace^1*endofstring)/"" -local normalline=(nospace*((1-space*(newline+endofstring))^1)*nospace) +local normalline=(nospace*((1-optionalspace*(newline+endofstring))^1)*nospace) local stripempty=endofline^1/"" local normalempty=endofline^1 local singleempty=endofline*(endofline^0/"") local doubleempty=endofline*endofline^-1*(endofline^0/"") local stripstart=stripempty^0 +local intospace=whitespace^1/" " +local noleading=whitespace^1/"" +local notrailing=noleading*endofstring local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 ) local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 ) local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 ) +local p_prune_intospace=Cs (noleading*(notrailing+intospace+1 )^0 ) local p_retain_normal=Cs ((normalline+normalempty )^0 ) local p_retain_collapse=Cs ((normalline+doubleempty )^0 ) local p_retain_noempty=Cs ((normalline+singleempty )^0 ) @@ -3622,6 +3943,7 @@ local striplinepatterns={ ["prune"]=p_prune_normal, ["prune and collapse"]=p_prune_collapse, ["prune and no empty"]=p_prune_noempty, + ["prune and to space"]=p_prune_intospace, ["retain"]=p_retain_normal, ["retain and collapse"]=p_retain_collapse, ["retain and no empty"]=p_retain_noempty, @@ -3632,6 +3954,9 @@ strings.striplinepatterns=striplinepatterns function strings.striplines(str,how) return str and lpegmatch(striplinepatterns[how],str) or str end +function strings.collapse(str) + return str and lpegmatch(p_prune_intospace,str) or str +end strings.striplong=strings.striplines function strings.nice(str) str=gsub(str,"[:%-+_]+"," ") @@ -3689,34 +4014,60 @@ function number.signed(i) return "-",-i end end -local digit=patterns.digit -local period=patterns.period -local three=digit*digit*digit +local two=digit*digit +local three=two*digit +local prefix=(Carg(1)*three)^1 local splitter=Cs ( - (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2) + (((1-(three^1*period))^1+C(three))*prefix+C((1-period)^1))*(anything/""*Carg(2))*C(2) +) +local splitter3=Cs ( + three*prefix*endofstring+two*prefix*endofstring+digit*prefix*endofstring+three+two+digit ) patterns.formattednumber=splitter function number.formatted(n,sep1,sep2) - local s=type(s)=="string" and n or format("%0.2f",n) - if sep1==true then - return lpegmatch(splitter,s,1,".",",") - elseif sep1=="." then - return lpegmatch(splitter,s,1,sep1,sep2 or ",") - elseif sep1=="," then - return lpegmatch(splitter,s,1,sep1,sep2 or ".") + if sep1==false then + if type(n)=="number" then + n=tostring(n) + end + return lpegmatch(splitter3,n,1,sep2 or ".") else - return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") + if type(n)=="number" then + n=format("%0.2f",n) + end + if sep1==true then + return lpegmatch(splitter,n,1,".",",") + elseif sep1=="." then + return lpegmatch(splitter,n,1,sep1,sep2 or ",") + elseif sep1=="," then + return lpegmatch(splitter,n,1,sep1,sep2 or ".") + else + return lpegmatch(splitter,n,1,sep1 or ",",sep2 or ".") + end + end +end +local p=Cs( + P("-")^0*(P("0")^1/"")^0*(1-period)^0*(period*P("0")^1*endofstring/""+period^0)*P(1-P("0")^1*endofstring)^0 + ) +function number.compactfloat(n,fmt) + if n==0 then + return "0" + elseif n==1 then + return "1" end + n=lpegmatch(p,format(fmt or "%0.3f",n)) + if n=="." or n=="" or n=="-" then + return "0" + end + return n end local zero=P("0")^1/"" local plus=P("+")/"" local minus=P("-") -local separator=S(".") -local digit=R("09") +local separator=period local trailing=zero^1*#S("eE") -local exponent=(S("eE")*(plus+Cs((minus*zero^0*P(-1))/"")+minus)*zero^0*(P(-1)*Cc("0")+P(1)^1)) +local exponent=(S("eE")*(plus+Cs((minus*zero^0*endofstring)/"")+minus)*zero^0*(endofstring*Cc("0")+anything^1)) local pattern_a=Cs(minus^0*digit^1*(separator/""*trailing+separator*(trailing+digit)^0)*exponent) -local pattern_b=Cs((exponent+P(1))^0) +local pattern_b=Cs((exponent+anything)^0) function number.sparseexponent(f,n) if not n then n=f @@ -3733,13 +4084,34 @@ function number.sparseexponent(f,n) end return tostring(n) end +local hf={} +local hs={} +setmetatable(hf,{ __index=function(t,k) + local v="%."..k.."f" + t[k]=v + return v +end } ) +setmetatable(hs,{ __index=function(t,k) + local v="%"..k.."s" + t[k]=v + return v +end } ) +function number.formattedfloat(n,b,a) + local s=format(hf[a],n) + local l=(b or 0)+(a or 0)+1 + if #s0 then + return format("utfpadding(a%s,%i)..a%s",n,f,n) + else + return format("a%s..utfpadding(a%s,%i)",n,n,f) + end +end +local format_left=function(f) + n=n+1 + f=tonumber(f) + if not f or f==0 then + return format("(a%s or '')",n) + end + if f<0 then + return format("utfpadding(a%s,%i)..a%s",n,-f,n) + else + return format("a%s..utfpadding(a%s,%i)",n,n,-f) + end +end local format_q=function() n=n+1 - return format("(a%s and format('%%q',a%s) or '')",n,n) + return format("(a%s ~= nil and format('%%q',tostring(a%s)) or '')",n,n) end local format_Q=function() n=n+1 @@ -3843,6 +4247,10 @@ local format_F=function(f) return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n) end end +local format_k=function(b,a) + n=n+1 + return format("formattedfloat(a%s,%i,%i)",n,b or 0,a or 0) +end local format_g=function(f) n=n+1 return format("format('%%%sg',a%s)",f,n) @@ -3959,9 +4367,16 @@ local format_L=function() n=n+1 return format("(a%s and 'TRUE' or 'FALSE')",n) end -local format_N=function() +local format_n=function() + n=n+1 + return format("((a%s %% 1 == 0) and format('%%i',a%s) or tostring(a%s))",n,n,n) +end +local format_N=function(f) n=n+1 - return format("tostring(tonumber(a%s) or a%s)",n,n) + if not f or f=="" then + f=".9" + end + return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%%sf',a%s)))",n,n,f,n) end local format_a=function(f) n=n+1 @@ -3996,14 +4411,22 @@ local format_m=function(f) if not f or f=="" then f="," end - return format([[formattednumber(a%s,%q,".")]],n,f) + if f=="0" then + return format([[formattednumber(a%s,false)]],n) + else + return format([[formattednumber(a%s,%q,".")]],n,f) + end end local format_M=function(f) n=n+1 if not f or f=="" then f="." end - return format([[formattednumber(a%s,%q,",")]],n,f) + if f=="0" then + return format([[formattednumber(a%s,false)]],n) + else + return format([[formattednumber(a%s,%q,",")]],n,f) + end end local format_z=function(f) n=n+(tonumber(f) or 1) @@ -4051,7 +4474,9 @@ local builder=Cs { "start", +V("s")+V("q")+V("i")+V("d")+V("f")+V("F")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") +V("c")+V("C")+V("S") +V("Q") -+V("N") ++V("n") ++V("N") ++V("k") +V("r")+V("h")+V("H")+V("u")+V("U")+V("p")+V("b")+V("t")+V("T")+V("l")+V("L")+V("I")+V("w") +V("W") +V("a") @@ -4059,8 +4484,10 @@ local builder=Cs { "start", +V("j")+V("J") +V("m")+V("M") +V("z") ++V(">") ++V("<") )+V("*") - )*(P(-1)+Carg(1)) + )*(endofstring+Carg(1)) )^0, ["s"]=(prefix_any*P("s"))/format_s, ["q"]=(prefix_any*P("q"))/format_q, @@ -4076,8 +4503,10 @@ local builder=Cs { "start", ["X"]=(prefix_any*P("X"))/format_X, ["o"]=(prefix_any*P("o"))/format_o, ["S"]=(prefix_any*P("S"))/format_S, - ["Q"]=(prefix_any*P("Q"))/format_S, + ["Q"]=(prefix_any*P("Q"))/format_Q, + ["n"]=(prefix_any*P("n"))/format_n, ["N"]=(prefix_any*P("N"))/format_N, + ["k"]=(prefix_sub*P("k"))/format_k, ["c"]=(prefix_any*P("c"))/format_c, ["C"]=(prefix_any*P("C"))/format_C, ["r"]=(prefix_any*P("r"))/format_r, @@ -4096,21 +4525,29 @@ local builder=Cs { "start", ["W"]=(prefix_any*P("W"))/format_W, ["j"]=(prefix_any*P("j"))/format_j, ["J"]=(prefix_any*P("J"))/format_J, - ["m"]=(prefix_tab*P("m"))/format_m, - ["M"]=(prefix_tab*P("M"))/format_M, + ["m"]=(prefix_any*P("m"))/format_m, + ["M"]=(prefix_any*P("M"))/format_M, ["z"]=(prefix_any*P("z"))/format_z, ["a"]=(prefix_any*P("a"))/format_a, ["A"]=(prefix_any*P("A"))/format_A, + ["<"]=(prefix_any*P("<"))/format_left, + [">"]=(prefix_any*P(">"))/format_right, ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest, ["?"]=Cs(((1-P("%"))^1 )^1)/format_rest, ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension, } -local direct=Cs ( - P("%")*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*P(-1)/[[local format = string.format return function(str) return format("%0",str) end]] -) +local xx=setmetatable({},{ __index=function(t,k) local v=format("%02x",k) t[k]=v return v end }) +local XX=setmetatable({},{ __index=function(t,k) local v=format("%02X",k) t[k]=v return v end }) +local preset={ + ["%02x"]=function(n) return xx[n] end, + ["%02X"]=function(n) return XX[n] end, +} +local direct=P("%")*(sign+space+period+digit)^0*S("sqidfgGeExXo")*endofstring/[[local format = string.format return function(str) return format("%0",str) end]] local function make(t,str) - local f - local p + local f=preset[str] + if f then + return f + end local p=lpegmatch(direct,str) if p then f=loadstripped(p)() @@ -4131,7 +4568,7 @@ local function use(t,fmt,...) return t[fmt](...) end strings.formatters={} -if _LUAVERSION<5.2 then +if oldfashioned then function strings.formatters.new(noconcat) local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_=preamble,_environment_={} } setmetatable(t,{ __index=make,__call=use }) @@ -4164,11 +4601,11 @@ local function add(t,name,template,preamble) end end strings.formatters.add=add -patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"""+P(1))^0) -patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0) +patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"""+anything)^0) +patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+anything)^0) patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"')) -if _LUAVERSION<5.2 then +if oldfashioned then add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape") add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape") add(formatters,"lua",[[lpegmatch(luaescape,%s)]],"local luaescape = lpeg.patterns.luaescape") @@ -4179,7 +4616,6 @@ else end local dquote=patterns.dquote local equote=patterns.escaped+dquote/'\\"'+1 -local space=patterns.space local cquote=Cc('"') local pattern=Cs(dquote*(equote-P(-2))^0*dquote) +Cs(cquote*(equote-space)^0*space*equote^0*cquote) @@ -4190,6 +4626,21 @@ local pattern=Cs((newline/(os.newline or "\r")+1)^0) function string.replacenewlines(str) return lpegmatch(pattern,str) end +function strings.newcollector() + local result,r={},0 + return + function(fmt,str,...) + r=r+1 + result[r]=str==nil and fmt or formatters[fmt](str,...) + end, + function(connector) + if result then + local str=concat(result,connector) + result,r={},0 + return str + end + end +end end -- closure @@ -4204,8 +4655,6 @@ if not modules then modules={} end modules ['util-fil']={ } local byte=string.byte local char=string.char -local extract=bit32 and bit32.extract -local floor=math.floor utilities=utilities or {} local files={} utilities.files=files @@ -4222,7 +4671,10 @@ function files.close(f) f:close() end function files.size(f) - return f:seek("end") + local current=f:seek() + local size=f:seek("end") + f:seek("set",current) + return size end files.getsize=files.size function files.setposition(f,n) @@ -4262,6 +4714,10 @@ end function files.readbytes(f,n) return byte(f:read(n),1,n) end +function files.readbytetable(f,n) + local s=f:read(n or 1) + return { byte(s,1,#s) } +end function files.readchar(f) return f:read(1) end @@ -4279,6 +4735,7 @@ end files.readcardinal1=files.readbyte files.readcardinal=files.readcardinal1 files.readinteger=files.readinteger1 +files.readsignedbyte=files.readinteger1 function files.readcardinal2(f) local a,b=byte(f:read(2),1,2) return 0x100*a+b @@ -4289,20 +4746,18 @@ function files.readcardinal2le(f) end function files.readinteger2(f) local a,b=byte(f:read(2),1,2) - local n=0x100*a+b - if n>=0x8000 then - return n-0x10000 + if a>=0x80 then + return 0x100*a+b-0x10000 else - return n + return 0x100*a+b end end function files.readinteger2le(f) local b,a=byte(f:read(2),1,2) - local n=0x100*a+b - if n>=0x8000 then - return n-0x10000 + if a>=0x80 then + return 0x100*a+b-0x10000 else - return n + return 0x100*a+b end end function files.readcardinal3(f) @@ -4315,20 +4770,18 @@ function files.readcardinal3le(f) end function files.readinteger3(f) local a,b,c=byte(f:read(3),1,3) - local n=0x10000*a+0x100*b+c - if n>=0x80000 then - return n-0x1000000 + if a>=0x80 then + return 0x10000*a+0x100*b+c-0x1000000 else - return n + return 0x10000*a+0x100*b+c end end function files.readinteger3le(f) local c,b,a=byte(f:read(3),1,3) - local n=0x10000*a+0x100*b+c - if n>=0x80000 then - return n-0x1000000 + if a>=0x80 then + return 0x10000*a+0x100*b+c-0x1000000 else - return n + return 0x10000*a+0x100*b+c end end function files.readcardinal4(f) @@ -4341,42 +4794,47 @@ function files.readcardinal4le(f) end function files.readinteger4(f) local a,b,c,d=byte(f:read(4),1,4) - local n=0x1000000*a+0x10000*b+0x100*c+d - if n>=0x8000000 then - return n-0x100000000 + if a>=0x80 then + return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000 else - return n + return 0x1000000*a+0x10000*b+0x100*c+d end end function files.readinteger4le(f) local d,c,b,a=byte(f:read(4),1,4) - local n=0x1000000*a+0x10000*b+0x100*c+d - if n>=0x8000000 then - return n-0x100000000 + if a>=0x80 then + return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000 else - return n + return 0x1000000*a+0x10000*b+0x100*c+d + end +end +function files.readfixed2(f) + local a,b=byte(f:read(2),1,2) + if a>=0x80 then + return (a-0x100)+b/0x100 + else + return (a )+b/0x100 end end function files.readfixed4(f) local a,b,c,d=byte(f:read(4),1,4) - local n=0x100*a+b - if n>=0x8000 then - return n-0x10000+(0x100*c+d)/0xFFFF + if a>=0x80 then + return (0x100*a+b-0x10000)+(0x100*c+d)/0x10000 else - return n+(0x100*c+d)/0xFFFF + return (0x100*a+b )+(0x100*c+d)/0x10000 end end -if extract then +if bit32 then + local extract=bit32.extract + local band=bit32.band function files.read2dot14(f) local a,b=byte(f:read(2),1,2) - local n=0x100*a+b - local m=extract(n,0,30) - if n>0x7FFF then - n=extract(n,30,2) - return m/0x4000-4 + if a>=0x80 then + local n=-(0x100*a+b) + return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0)) else - n=extract(n,30,2) - return n+m/0x4000 + local n=0x100*a+b + return (extract(n,14,2)+(band(n,0x3FFF)/16384.0)) end end end @@ -4386,19 +4844,30 @@ end function files.skiplong(f,n) f:read(4*(n or 1)) end -function files.writecardinal2(f,n) - local a=char(n%256) - n=floor(n/256) - local b=char(n%256) - f:write(b,a) +if bit32 then + local rshift=bit32.rshift + function files.writecardinal2(f,n) + local a=char(n%256) + n=rshift(n,8) + local b=char(n%256) + f:write(b,a) + end +else + local floor=math.floor + function files.writecardinal2(f,n) + local a=char(n%256) + n=floor(n/256) + local b=char(n%256) + f:write(b,a) + end end function files.writecardinal4(f,n) local a=char(n%256) - n=floor(n/256) + n=rshift(n,8) local b=char(n%256) - n=floor(n/256) + n=rshift(n,8) local c=char(n%256) - n=floor(n/256) + n=rshift(n,8) local d=char(n%256) f:write(d,c,b,a) end @@ -4408,6 +4877,64 @@ end function files.writebyte(f,b) f:write(char(b)) end +if fio and fio.readcardinal1 then + files.readcardinal1=fio.readcardinal1 + files.readcardinal2=fio.readcardinal2 + files.readcardinal3=fio.readcardinal3 + files.readcardinal4=fio.readcardinal4 + files.readinteger1=fio.readinteger1 + files.readinteger2=fio.readinteger2 + files.readinteger3=fio.readinteger3 + files.readinteger4=fio.readinteger4 + files.readfixed2=fio.readfixed2 + files.readfixed4=fio.readfixed4 + files.read2dot14=fio.read2dot14 + files.setposition=fio.setposition + files.getposition=fio.getposition + files.readbyte=files.readcardinal1 + files.readsignedbyte=files.readinteger1 + files.readcardinal=files.readcardinal1 + files.readinteger=files.readinteger1 + local skipposition=fio.skipposition + files.skipposition=skipposition + files.readbytes=fio.readbytes + files.readbytetable=fio.readbytetable + function files.skipshort(f,n) + skipposition(f,2*(n or 1)) + end + function files.skiplong(f,n) + skipposition(f,4*(n or 1)) + end +end +if fio and fio.readcardinaltable then + files.readcardinaltable=fio.readcardinaltable + files.readintegertable=fio.readintegertable +else + local readcardinal1=files.readcardinal1 + local readcardinal2=files.readcardinal2 + local readcardinal3=files.readcardinal3 + local readcardinal4=files.readcardinal4 + function files.readcardinaltable(f,n,b) + local t={} + if b==1 then for i=1,n do t[i]=readcardinal1(f) end + elseif b==2 then for i=1,n do t[i]=readcardinal2(f) end + elseif b==3 then for i=1,n do t[i]=readcardinal3(f) end + elseif b==4 then for i=1,n do t[i]=readcardinal4(f) end end + return t + end + local readinteger1=files.readinteger1 + local readinteger2=files.readinteger2 + local readinteger3=files.readinteger3 + local readinteger4=files.readinteger4 + function files.readintegertable(f,n,b) + local t={} + if b==1 then for i=1,n do t[i]=readinteger1(f) end + elseif b==2 then for i=1,n do t[i]=readinteger2(f) end + elseif b==3 then for i=1,n do t[i]=readinteger3(f) end + elseif b==4 then for i=1,n do t[i]=readinteger4(f) end end + return t + end +end end -- closure @@ -4421,21 +4948,33 @@ if not modules then modules={} end modules ['luat-basics-gen']={ license="see context related readme files" } if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() +--removed + end +local match,gmatch,gsub,lower=string.match,string.gmatch,string.gsub,string.lower +local formatters,split,format,dump=string.formatters,string.split,string.format,string.dump +local loadfile,type=loadfile,type +local setmetatable,getmetatable,collectgarbage=setmetatable,getmetatable,collectgarbage local dummyfunction=function() end local dummyreporter=function(c) return function(f,...) local r=texio.reporter or texio.write_nl if f then - r(c.." : "..string.formatters(f,...)) + r(c.." : "..(formatters or format)(f,...)) else r("") end end end +local dummyreport=function(c,f,...) + local r=texio.reporter or texio.write_nl + if f then + r(c.." : "..(formatters or format)(f,...)) + else + r("") + end +end statistics={ register=dummyfunction, starttiming=dummyfunction, @@ -4465,14 +5004,15 @@ logs={ new=dummyreporter, reporter=dummyreporter, messenger=dummyreporter, - report=dummyfunction, + report=dummyreport, } callbacks={ register=function(n,f) return callback.register(n,f) end, } -utilities=utilities or {} utilities.storage={ +utilities=utilities or {} +utilities.storage=utilities.storage or { allocate=function(t) return t or {} end, @@ -4480,6 +5020,25 @@ utilities=utilities or {} utilities.storage={ return t or {} end, } +utilities.parsers=utilities.parsers or { + settings_to_array=function(s) + return split(s,",") + end, + settings_to_hash=function(s) + local t={} + for k,v in gmatch(s,"([^%s,=]+)=([^%s,]+)") do + t[k]=v + end + return t + end, + settings_to_hash_colon_too=function(s) + local t={} + for k,v in gmatch(s,"([^%s,=:]+)[=:]([^%s,]+)") do + t[k]=v + end + return t + end, +} characters=characters or { data={} } @@ -4494,16 +5053,17 @@ local remapper={ pfb="type1 fonts", afm="afm", enc="enc files", + lua="tex", } function resolvers.findfile(name,fileformat) - name=string.gsub(name,"\\","/") + name=gsub(name,"\\","/") if not fileformat or fileformat=="" then fileformat=file.suffix(name) if fileformat=="" then fileformat="tex" end end - fileformat=string.lower(fileformat) + fileformat=lower(fileformat) fileformat=remapper[fileformat] or fileformat local found=kpse.find_file(name,fileformat) if not found or found=="" then @@ -4549,13 +5109,13 @@ do if cachepaths=="" then cachepaths="." end - cachepaths=string.split(cachepaths,os.type=="windows" and ";" or ":") + cachepaths=split(cachepaths,os.type=="windows" and ";" or ":") for i=1,#cachepaths do local cachepath=cachepaths[i] if not lfs.isdir(cachepath) then lfs.mkdirs(cachepath) if lfs.isdir(cachepath) then - texio.write(string.format("(created cache path: %s)",cachepath)) + logs.report("system","creating cache path '%s'",cachepath) end end if file.is_writable(cachepath) then @@ -4572,16 +5132,16 @@ do end end if not writable then - texio.write_nl("quiting: fix your writable cache path") + logs.report("system","no writeable cache path, quiting") os.exit() elseif #readables==0 then - texio.write_nl("quiting: fix your readable cache path") + logs.report("system","no readable cache path, quiting") os.exit() elseif #readables==1 and readables[1]==writable then - texio.write(string.format("(using cache: %s)",writable)) + logs.report("system","using cache '%s'",writable) else - texio.write(string.format("(using write cache: %s)",writable)) - texio.write(string.format("(using read cache: %s)",table.concat(readables," "))) + logs.report("system","using write cache '%s'",writable) + logs.report("system","using read cache '%s'",table.concat(readables," ")) end end function caches.getwritablepath(category,subcategory) @@ -4613,27 +5173,27 @@ function caches.loaddata(readables,name,writable) local loader=false local luaname,lucname=makefullname(path,name) if lfs.isfile(lucname) then - texio.write(string.format("(load luc: %s)",lucname)) + logs.report("system","loading luc file '%s'",lucname) loader=loadfile(lucname) end if not loader and lfs.isfile(luaname) then local luacrap,lucname=makefullname(writable,name) - texio.write(string.format("(compiling luc: %s)",lucname)) + logs.report("system","compiling luc file '%s'",lucname) if lfs.isfile(lucname) then loader=loadfile(lucname) end caches.compile(data,luaname,lucname) if lfs.isfile(lucname) then - texio.write(string.format("(load luc: %s)",lucname)) + logs.report("system","loading luc file '%s'",lucname) loader=loadfile(lucname) else - texio.write(string.format("(loading failed: %s)",lucname)) + logs.report("system","error in loading luc file '%s'",lucname) end if not loader then - texio.write(string.format("(load lua: %s)",luaname)) + logs.report("system","loading lua file '%s'",luaname) loader=loadfile(luaname) else - texio.write(string.format("(loading failed: %s)",luaname)) + logs.report("system","error in loading lua file '%s'",luaname) end end if loader then @@ -4647,11 +5207,11 @@ end function caches.savedata(path,name,data) local luaname,lucname=makefullname(path,name) if luaname then - texio.write(string.format("(save: %s)",luaname)) + logs.report("system","saving lua file '%s'",luaname) table.tofile(luaname,data,true) if lucname and type(caches.compile)=="function" then os.remove(lucname) - texio.write(string.format("(save: %s)",lucname)) + logs.report("system","saving luc file '%s'",lucname) caches.compile(data,luaname,lucname) end end @@ -4666,7 +5226,7 @@ function caches.compile(data,luaname,lucname) if f then local s=loadstring(d) if s then - f:write(string.dump(s,true)) + f:write(dump(s,true)) end f:close() end @@ -4687,10 +5247,19 @@ function table.setmetatableindex(t,f) end return t end +function table.makeweak(t) + local m=getmetatable(t) + if m then + m.__mode="v" + else + setmetatable(t,{ __mode="v" }) + end + return t +end arguments={} if arg then for i=1,#arg do - local k,v=string.match(arg[i],"^%-%-([^=]+)=?(.-)$") + local k,v=match(arg[i],"^%-%-([^=]+)=?(.-)$") if k and v then arguments[k]=v end @@ -4823,8 +5392,8 @@ if not modules then modules={} end modules ['luatex-fonts-nod']={ license="see context related readme files" } if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() +--removed + end if tex.attribute[0]~=0 then texio.write_nl("log","!") @@ -4849,7 +5418,6 @@ attributes.private=attributes.private or function(name) return number end nodes={} -nodes.pool={} nodes.handlers={} local nodecodes={} local glyphcodes=node.subtypes("glyph") @@ -4859,21 +5427,20 @@ for k,v in next,node.types() do nodecodes[k]=v nodecodes[v]=k end -for i=0,#glyphcodes do - glyphcodes[glyphcodes[i]]=i +for k,v in next,glyphcodes do + glyphcodes[v]=k end -for i=0,#disccodes do - disccodes[disccodes[i]]=i +for k,v in next,glyphcodes do + disccodes[v]=k 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 +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) @@ -4899,11 +5466,6 @@ 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 @@ -4969,16 +5531,38 @@ 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.setsubtype=direct.setsubtype nuts.getchar=direct.getchar nuts.setchar=direct.setchar nuts.getdisc=direct.getdisc nuts.setdisc=direct.setdisc nuts.setlink=direct.setlink +nuts.setsplit=direct.setsplit 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 +nuts.setlist=direct.setlist +nuts.getoffsets=direct.getoffsets or + function(n) + return getfield(n,"xoffset"),getfield(n,"yoffset") + end +nuts.setoffsets=direct.setoffsets or + function(n,x,y) + if x then setfield(n,"xoffset",x) end + if y then setfield(n,"xoffset",y) end + end +nuts.getleader=direct.getleader or function(n) return getfield(n,"leader") end +nuts.setleader=direct.setleader or function(n,l) setfield(n,"leader",l) end +nuts.getcomponents=direct.getcomponents or function(n) return getfield(n,"components") end +nuts.setcomponents=direct.setcomponents or function(n,c) setfield(n,"components",c) end +nuts.getkern=direct.getkern or function(n) return getfield(n,"kern") end +nuts.setkern=direct.setkern or function(n,k) setfield(n,"kern",k) end +nuts.getdir=direct.getdir or function(n) return getfield(n,"dir") end +nuts.setdir=direct.setdir or function(n,d) setfield(n,"dir",d) end +nuts.getwidth=direct.getwidth or function(n) return getfield(n,"width") end +nuts.setwidth=direct.setwidth or function(n,w) return setfield(n,"width",w) end +nuts.getheight=direct.getheight or function(n) return getfield(n,"height") end +nuts.setheight=direct.setheight or function(n,h) return setfield(n,"height",h) end +nuts.getdepth=direct.getdepth or function(n) return getfield(n,"depth") end +nuts.setdepth=direct.setdepth or function(n,d) return setfield(n,"depth",d) end if not direct.is_glyph then local getchar=direct.getchar local getid=direct.getid @@ -5032,18 +5616,12 @@ 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.traverse_glyph=direct.traverse_glyph nuts.ligaturing=direct.ligaturing nuts.kerning=direct.kerning +nuts.new=direct.new 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) @@ -5066,6 +5644,157 @@ nuts.setprop=function(n,k,v) end nodes.setprop=nodes.setproperty nodes.getprop=nodes.getproperty +local setprev=nuts.setprev +local setnext=nuts.setnext +local getnext=nuts.getnext +local setlink=nuts.setlink +local getfield=nuts.getfield +local setfield=nuts.setfield +local getcomponents=nuts.getcomponents +local setcomponents=nuts.setcomponents +local find_tail=nuts.tail +local flush_list=nuts.flush_list +local flush_node=nuts.flush_node +local traverse_id=nuts.traverse_id +local copy_node=nuts.copy_node +local glyph_code=nodes.nodecodes.glyph +function nuts.set_components(target,start,stop) + local head=getcomponents(target) + if head then + flush_list(head) + head=nil + end + if start then + setprev(start) + else + return nil + end + if stop then + setnext(stop) + end + local tail=nil + while start do + local c=getcomponents(start) + local n=getnext(start) + if c then + if head then + setlink(tail,c) + else + head=c + end + tail=find_tail(c) + setcomponents(start) + flush_node(start) + else + if head then + setlink(tail,start) + else + head=start + end + tail=start + end + start=n + end + setcomponents(target,head) + return head +end +nuts.get_components=nuts.getcomponents +function nuts.take_components(target) + local c=getcomponents(target) + setcomponents(target) + return c +end +function nuts.count_components(n,marks) + local components=getcomponents(n) + if components then + if marks then + local i=0 + for g in traverse_id(glyph_code,components) do + if not marks[getchar(g)] then + i=i+1 + end + end + return i + else + return count(glyph_code,components) + end + else + return 0 + end +end +function nuts.copy_no_components(g,copyinjection) + local components=getcomponents(g) + if components then + setcomponents(g) + local n=copy_node(g) + if copyinjection then + copyinjection(n,g) + end + setcomponents(g,components) + return n + else + local n=copy_node(g) + if copyinjection then + copyinjection(n,g) + end + return n + end +end +function nuts.copy_only_glyphs(current) + local head=nil + local previous=nil + for n in traverse_id(glyph_code,current) do + n=copy_node(n) + if head then + setlink(previous,n) + else + head=n + end + previous=n + end + return head +end +nuts.uses_font=direct.uses_font +if not nuts.uses_font then + local getdisc=nuts.getdisc + local getfont=nuts.getfont + function nuts.uses_font(n,font) + local pre,post,replace=getdisc(n) + if pre then + for n in traverse_id(glyph_code,pre) do + if getfont(n)==font then + return true + end + end + end + if post then + for n in traverse_id(glyph_code,post) do + if getfont(n)==font then + return true + end + end + end + if replace then + for n in traverse_id(glyph_code,replace) do + if getfont(n)==font then + return true + end + end + end + return false + end +end +do + local dummy=tonut(node.new("glyph")) + nuts.traversers={ + glyph=nuts.traverse_id(nodecodes.glyph,dummy), + glue=nuts.traverse_id(nodecodes.glue,dummy), + disc=nuts.traverse_id(nodecodes.disc,dummy), + boundary=nuts.traverse_id(nodecodes.boundary,dummy), + char=nuts.traverse_char(dummy), + node=nuts.traverse(dummy), + } +end end -- closure @@ -5075,6 +5804,174 @@ do -- begin closure to overcome local limits and interference characters=characters or {} characters.blockrange={} characters.classifiers={ + [768]=5, + [769]=5, + [770]=5, + [771]=5, + [772]=5, + [773]=5, + [774]=5, + [775]=5, + [776]=5, + [777]=5, + [778]=5, + [779]=5, + [780]=5, + [781]=5, + [782]=5, + [783]=5, + [784]=5, + [785]=5, + [786]=5, + [787]=5, + [788]=5, + [789]=5, + [790]=5, + [791]=5, + [792]=5, + [793]=5, + [794]=5, + [795]=5, + [796]=5, + [797]=5, + [798]=5, + [799]=5, + [800]=5, + [801]=5, + [802]=5, + [803]=5, + [804]=5, + [805]=5, + [806]=5, + [807]=5, + [808]=5, + [809]=5, + [810]=5, + [811]=5, + [812]=5, + [813]=5, + [814]=5, + [815]=5, + [816]=5, + [817]=5, + [818]=5, + [819]=5, + [820]=5, + [821]=5, + [822]=5, + [823]=5, + [824]=5, + [825]=5, + [826]=5, + [827]=5, + [828]=5, + [829]=5, + [830]=5, + [831]=5, + [832]=5, + [833]=5, + [834]=5, + [835]=5, + [836]=5, + [837]=5, + [838]=5, + [839]=5, + [840]=5, + [841]=5, + [842]=5, + [843]=5, + [844]=5, + [845]=5, + [846]=5, + [847]=5, + [848]=5, + [849]=5, + [850]=5, + [851]=5, + [852]=5, + [853]=5, + [854]=5, + [855]=5, + [856]=5, + [857]=5, + [858]=5, + [859]=5, + [860]=5, + [861]=5, + [862]=5, + [863]=5, + [864]=5, + [865]=5, + [866]=5, + [867]=5, + [868]=5, + [869]=5, + [870]=5, + [871]=5, + [872]=5, + [873]=5, + [874]=5, + [875]=5, + [876]=5, + [877]=5, + [878]=5, + [879]=5, + [1155]=5, + [1156]=5, + [1157]=5, + [1158]=5, + [1159]=5, + [1425]=5, + [1426]=5, + [1427]=5, + [1428]=5, + [1429]=5, + [1430]=5, + [1431]=5, + [1432]=5, + [1433]=5, + [1434]=5, + [1435]=5, + [1436]=5, + [1437]=5, + [1438]=5, + [1439]=5, + [1440]=5, + [1441]=5, + [1442]=5, + [1443]=5, + [1444]=5, + [1445]=5, + [1446]=5, + [1447]=5, + [1448]=5, + [1449]=5, + [1450]=5, + [1451]=5, + [1452]=5, + [1453]=5, + [1454]=5, + [1455]=5, + [1456]=5, + [1457]=5, + [1458]=5, + [1459]=5, + [1460]=5, + [1461]=5, + [1462]=5, + [1463]=5, + [1464]=5, + [1465]=5, + [1466]=5, + [1467]=5, + [1468]=5, + [1469]=5, + [1471]=5, + [1473]=5, + [1474]=5, + [1476]=5, + [1477]=5, + [1479]=5, [1536]=4, [1537]=4, [1538]=4, @@ -5455,6 +6352,17 @@ characters.classifiers={ [1917]=2, [1918]=2, [1919]=2, + [1958]=5, + [1959]=5, + [1960]=5, + [1961]=5, + [1962]=5, + [1963]=5, + [1964]=5, + [1965]=5, + [1966]=5, + [1967]=5, + [1968]=5, [1984]=6, [1985]=6, [1986]=6, @@ -5514,6 +6422,27 @@ characters.classifiers={ [2040]=6, [2041]=6, [2042]=2, + [2070]=5, + [2071]=5, + [2072]=5, + [2073]=5, + [2075]=5, + [2076]=5, + [2077]=5, + [2078]=5, + [2079]=5, + [2080]=5, + [2081]=5, + [2082]=5, + [2083]=5, + [2085]=5, + [2086]=5, + [2087]=5, + [2089]=5, + [2090]=5, + [2091]=5, + [2092]=5, + [2093]=5, [2112]=3, [2113]=2, [2114]=2, @@ -5539,6 +6468,20 @@ characters.classifiers={ [2134]=4, [2135]=4, [2136]=4, + [2137]=5, + [2138]=5, + [2139]=5, + [2144]=2, + [2145]=4, + [2146]=2, + [2147]=2, + [2148]=2, + [2149]=2, + [2150]=4, + [2151]=3, + [2152]=2, + [2153]=3, + [2154]=3, [2208]=2, [2209]=2, [2210]=2, @@ -5560,9 +6503,348 @@ characters.classifiers={ [2226]=3, [2227]=2, [2228]=2, + [2230]=2, + [2231]=2, + [2232]=2, + [2233]=3, + [2234]=2, + [2235]=2, + [2236]=2, + [2237]=2, + [2260]=5, + [2261]=5, + [2262]=5, + [2263]=5, + [2264]=5, + [2265]=5, + [2266]=5, + [2267]=5, + [2268]=5, + [2269]=5, + [2270]=5, + [2271]=5, + [2272]=5, + [2273]=5, + [2274]=4, + [2275]=5, + [2276]=5, + [2277]=5, + [2278]=5, + [2279]=5, + [2280]=5, + [2281]=5, + [2282]=5, + [2283]=5, + [2284]=5, + [2285]=5, + [2286]=5, + [2287]=5, + [2288]=5, + [2289]=5, + [2290]=5, + [2291]=5, + [2292]=5, + [2293]=5, + [2294]=5, + [2295]=5, + [2296]=5, + [2297]=5, + [2298]=5, + [2299]=5, + [2300]=5, + [2301]=5, + [2302]=5, + [2303]=5, + [2304]=5, + [2305]=5, + [2306]=5, + [2362]=5, + [2364]=5, + [2369]=5, + [2370]=5, + [2371]=5, + [2372]=5, + [2373]=5, + [2374]=5, + [2375]=5, + [2376]=5, + [2381]=5, + [2385]=5, + [2386]=5, + [2387]=5, + [2388]=5, + [2389]=5, + [2390]=5, + [2391]=5, + [2402]=5, + [2403]=5, + [2433]=5, + [2492]=5, + [2497]=5, + [2498]=5, + [2499]=5, + [2500]=5, + [2509]=5, + [2530]=5, + [2531]=5, + [2561]=5, + [2562]=5, + [2620]=5, + [2625]=5, + [2626]=5, + [2631]=5, + [2632]=5, + [2635]=5, + [2636]=5, + [2637]=5, + [2641]=5, + [2672]=5, + [2673]=5, + [2677]=5, + [2689]=5, + [2690]=5, + [2748]=5, + [2753]=5, + [2754]=5, + [2755]=5, + [2756]=5, + [2757]=5, + [2759]=5, + [2760]=5, + [2765]=5, + [2786]=5, + [2787]=5, + [2810]=5, + [2811]=5, + [2812]=5, + [2813]=5, + [2814]=5, + [2815]=5, + [2817]=5, + [2876]=5, + [2879]=5, + [2881]=5, + [2882]=5, + [2883]=5, + [2884]=5, + [2893]=5, + [2902]=5, + [2914]=5, + [2915]=5, + [2946]=5, + [3008]=5, + [3021]=5, + [3072]=5, + [3134]=5, + [3135]=5, + [3136]=5, + [3142]=5, + [3143]=5, + [3144]=5, + [3146]=5, + [3147]=5, + [3148]=5, + [3149]=5, + [3157]=5, + [3158]=5, + [3170]=5, + [3171]=5, + [3201]=5, + [3260]=5, + [3263]=5, + [3270]=5, + [3276]=5, + [3277]=5, + [3298]=5, + [3299]=5, + [3328]=5, + [3329]=5, + [3387]=5, + [3388]=5, + [3393]=5, + [3394]=5, + [3395]=5, + [3396]=5, + [3405]=5, + [3426]=5, + [3427]=5, + [3530]=5, + [3538]=5, + [3539]=5, + [3540]=5, + [3542]=5, + [3633]=5, + [3636]=5, + [3637]=5, + [3638]=5, + [3639]=5, + [3640]=5, + [3641]=5, + [3642]=5, + [3655]=5, + [3656]=5, + [3657]=5, + [3658]=5, + [3659]=5, + [3660]=5, + [3661]=5, + [3662]=5, + [3761]=5, + [3764]=5, + [3765]=5, + [3766]=5, + [3767]=5, + [3768]=5, + [3769]=5, + [3771]=5, + [3772]=5, + [3784]=5, + [3785]=5, + [3786]=5, + [3787]=5, + [3788]=5, + [3789]=5, + [3864]=5, + [3865]=5, + [3893]=5, + [3895]=5, + [3897]=5, + [3953]=5, + [3954]=5, + [3955]=5, + [3956]=5, + [3957]=5, + [3958]=5, + [3959]=5, + [3960]=5, + [3961]=5, + [3962]=5, + [3963]=5, + [3964]=5, + [3965]=5, + [3966]=5, + [3968]=5, + [3969]=5, + [3970]=5, + [3971]=5, + [3972]=5, + [3974]=5, + [3975]=5, + [3981]=5, + [3982]=5, + [3983]=5, + [3984]=5, + [3985]=5, + [3986]=5, + [3987]=5, + [3988]=5, + [3989]=5, + [3990]=5, + [3991]=5, + [3993]=5, + [3994]=5, + [3995]=5, + [3996]=5, + [3997]=5, + [3998]=5, + [3999]=5, + [4000]=5, + [4001]=5, + [4002]=5, + [4003]=5, + [4004]=5, + [4005]=5, + [4006]=5, + [4007]=5, + [4008]=5, + [4009]=5, + [4010]=5, + [4011]=5, + [4012]=5, + [4013]=5, + [4014]=5, + [4015]=5, + [4016]=5, + [4017]=5, + [4018]=5, + [4019]=5, + [4020]=5, + [4021]=5, + [4022]=5, + [4023]=5, + [4024]=5, + [4025]=5, + [4026]=5, + [4027]=5, + [4028]=5, + [4038]=5, + [4141]=5, + [4142]=5, + [4143]=5, + [4144]=5, + [4146]=5, + [4147]=5, + [4148]=5, + [4149]=5, + [4150]=5, + [4151]=5, + [4153]=5, + [4154]=5, + [4157]=5, + [4158]=5, + [4184]=5, + [4185]=5, + [4190]=5, + [4191]=5, + [4192]=5, + [4209]=5, + [4210]=5, + [4211]=5, + [4212]=5, + [4226]=5, + [4229]=5, + [4230]=5, + [4237]=5, + [4253]=5, + [4957]=5, + [4958]=5, + [4959]=5, + [5906]=5, + [5907]=5, + [5908]=5, + [5938]=5, + [5939]=5, + [5940]=5, + [5970]=5, + [5971]=5, + [6002]=5, + [6003]=5, + [6071]=5, + [6072]=5, + [6073]=5, + [6074]=5, + [6075]=5, + [6076]=5, + [6077]=5, + [6086]=5, + [6089]=5, + [6090]=5, + [6091]=5, + [6092]=5, + [6093]=5, + [6094]=5, + [6095]=5, + [6096]=5, + [6097]=5, + [6098]=5, + [6099]=5, + [6109]=5, [6150]=4, [6151]=2, [6154]=2, + [6155]=5, + [6156]=5, + [6157]=5, [6158]=4, [6176]=2, [6177]=2, @@ -5657,8 +6939,6 @@ characters.classifiers={ [6274]=4, [6275]=4, [6276]=4, - [6277]=4, - [6278]=4, [6279]=2, [6280]=2, [6281]=2, @@ -5693,13 +6973,295 @@ characters.classifiers={ [6310]=2, [6311]=2, [6312]=2, + [6313]=5, [6314]=2, + [6432]=5, + [6433]=5, + [6434]=5, + [6439]=5, + [6440]=5, + [6450]=5, + [6457]=5, + [6458]=5, + [6459]=5, + [6679]=5, + [6680]=5, + [6742]=5, + [6744]=5, + [6745]=5, + [6746]=5, + [6747]=5, + [6748]=5, + [6749]=5, + [6750]=5, + [6752]=5, + [6754]=5, + [6757]=5, + [6758]=5, + [6759]=5, + [6760]=5, + [6761]=5, + [6762]=5, + [6763]=5, + [6764]=5, + [6771]=5, + [6772]=5, + [6773]=5, + [6774]=5, + [6775]=5, + [6776]=5, + [6777]=5, + [6778]=5, + [6779]=5, + [6780]=5, + [6783]=5, + [6832]=5, + [6833]=5, + [6834]=5, + [6835]=5, + [6836]=5, + [6837]=5, + [6838]=5, + [6839]=5, + [6840]=5, + [6841]=5, + [6842]=5, + [6843]=5, + [6844]=5, + [6845]=5, + [6912]=5, + [6913]=5, + [6914]=5, + [6915]=5, + [6964]=5, + [6966]=5, + [6967]=5, + [6968]=5, + [6969]=5, + [6970]=5, + [6972]=5, + [6978]=5, + [7019]=5, + [7020]=5, + [7021]=5, + [7022]=5, + [7023]=5, + [7024]=5, + [7025]=5, + [7026]=5, + [7027]=5, + [7040]=5, + [7041]=5, + [7074]=5, + [7075]=5, + [7076]=5, + [7077]=5, + [7080]=5, + [7081]=5, + [7083]=5, + [7142]=5, + [7144]=5, + [7145]=5, + [7149]=5, + [7151]=5, + [7152]=5, + [7153]=5, + [7212]=5, + [7213]=5, + [7214]=5, + [7215]=5, + [7216]=5, + [7217]=5, + [7218]=5, + [7219]=5, + [7222]=5, + [7223]=5, + [7376]=5, + [7377]=5, + [7378]=5, + [7380]=5, + [7381]=5, + [7382]=5, + [7383]=5, + [7384]=5, + [7385]=5, + [7386]=5, + [7387]=5, + [7388]=5, + [7389]=5, + [7390]=5, + [7391]=5, + [7392]=5, + [7394]=5, + [7395]=5, + [7396]=5, + [7397]=5, + [7398]=5, + [7399]=5, + [7400]=5, + [7405]=5, + [7412]=5, + [7416]=5, + [7417]=5, + [7616]=5, + [7617]=5, + [7618]=5, + [7619]=5, + [7620]=5, + [7621]=5, + [7622]=5, + [7623]=5, + [7624]=5, + [7625]=5, + [7626]=5, + [7627]=5, + [7628]=5, + [7629]=5, + [7630]=5, + [7631]=5, + [7632]=5, + [7633]=5, + [7634]=5, + [7635]=5, + [7636]=5, + [7637]=5, + [7638]=5, + [7639]=5, + [7640]=5, + [7641]=5, + [7642]=5, + [7643]=5, + [7644]=5, + [7645]=5, + [7646]=5, + [7647]=5, + [7648]=5, + [7649]=5, + [7650]=5, + [7651]=5, + [7652]=5, + [7653]=5, + [7654]=5, + [7655]=5, + [7656]=5, + [7657]=5, + [7658]=5, + [7659]=5, + [7660]=5, + [7661]=5, + [7662]=5, + [7663]=5, + [7664]=5, + [7665]=5, + [7666]=5, + [7667]=5, + [7668]=5, + [7669]=5, + [7670]=5, + [7671]=5, + [7672]=5, + [7673]=5, + [7675]=5, + [7676]=5, + [7677]=5, + [7678]=5, + [7679]=5, [8204]=4, [8205]=2, + [8239]=4, [8294]=4, [8295]=4, [8296]=4, [8297]=4, + [8400]=5, + [8401]=5, + [8402]=5, + [8403]=5, + [8404]=5, + [8405]=5, + [8406]=5, + [8407]=5, + [8408]=5, + [8409]=5, + [8410]=5, + [8411]=5, + [8412]=5, + [8417]=5, + [8421]=5, + [8422]=5, + [8423]=5, + [8424]=5, + [8425]=5, + [8426]=5, + [8427]=5, + [8428]=5, + [8429]=5, + [8430]=5, + [8431]=5, + [8432]=5, + [11503]=5, + [11504]=5, + [11505]=5, + [11647]=5, + [11744]=5, + [11745]=5, + [11746]=5, + [11747]=5, + [11748]=5, + [11749]=5, + [11750]=5, + [11751]=5, + [11752]=5, + [11753]=5, + [11754]=5, + [11755]=5, + [11756]=5, + [11757]=5, + [11758]=5, + [11759]=5, + [11760]=5, + [11761]=5, + [11762]=5, + [11763]=5, + [11764]=5, + [11765]=5, + [11766]=5, + [11767]=5, + [11768]=5, + [11769]=5, + [11770]=5, + [11771]=5, + [11772]=5, + [11773]=5, + [11774]=5, + [11775]=5, + [12330]=5, + [12331]=5, + [12332]=5, + [12333]=5, + [12334]=5, + [12335]=5, + [12441]=5, + [12442]=5, + [42607]=5, + [42612]=5, + [42613]=5, + [42614]=5, + [42615]=5, + [42616]=5, + [42617]=5, + [42618]=5, + [42619]=5, + [42620]=5, + [42621]=5, + [42654]=5, + [42655]=5, + [42736]=5, + [42737]=5, + [43014]=5, + [43019]=5, + [43045]=5, + [43046]=5, [43072]=2, [43073]=2, [43074]=2, @@ -5752,6 +7314,120 @@ characters.classifiers={ [43121]=2, [43122]=1, [43123]=4, + [43204]=5, + [43205]=5, + [43232]=5, + [43233]=5, + [43234]=5, + [43235]=5, + [43236]=5, + [43237]=5, + [43238]=5, + [43239]=5, + [43240]=5, + [43241]=5, + [43242]=5, + [43243]=5, + [43244]=5, + [43245]=5, + [43246]=5, + [43247]=5, + [43248]=5, + [43249]=5, + [43302]=5, + [43303]=5, + [43304]=5, + [43305]=5, + [43306]=5, + [43307]=5, + [43308]=5, + [43309]=5, + [43335]=5, + [43336]=5, + [43337]=5, + [43338]=5, + [43339]=5, + [43340]=5, + [43341]=5, + [43342]=5, + [43343]=5, + [43344]=5, + [43345]=5, + [43392]=5, + [43393]=5, + [43394]=5, + [43443]=5, + [43446]=5, + [43447]=5, + [43448]=5, + [43449]=5, + [43452]=5, + [43493]=5, + [43561]=5, + [43562]=5, + [43563]=5, + [43564]=5, + [43565]=5, + [43566]=5, + [43569]=5, + [43570]=5, + [43573]=5, + [43574]=5, + [43587]=5, + [43596]=5, + [43644]=5, + [43696]=5, + [43698]=5, + [43699]=5, + [43700]=5, + [43703]=5, + [43704]=5, + [43710]=5, + [43711]=5, + [43713]=5, + [43756]=5, + [43757]=5, + [43766]=5, + [44005]=5, + [44008]=5, + [44013]=5, + [64286]=5, + [65056]=5, + [65057]=5, + [65058]=5, + [65059]=5, + [65060]=5, + [65061]=5, + [65062]=5, + [65063]=5, + [65064]=5, + [65065]=5, + [65066]=5, + [65067]=5, + [65068]=5, + [65069]=5, + [65070]=5, + [65071]=5, + [66045]=5, + [66272]=5, + [66422]=5, + [66423]=5, + [66424]=5, + [66425]=5, + [66426]=5, + [68097]=5, + [68098]=5, + [68099]=5, + [68101]=5, + [68102]=5, + [68108]=5, + [68109]=5, + [68110]=5, + [68111]=5, + [68152]=5, + [68153]=5, + [68154]=5, + [68159]=5, [68288]=2, [68289]=2, [68290]=2, @@ -5789,6 +7465,8 @@ characters.classifiers={ [68322]=4, [68323]=4, [68324]=3, + [68325]=5, + [68326]=5, [68331]=2, [68332]=2, [68333]=2, @@ -5819,6 +7497,1544 @@ characters.classifiers={ [68525]=2, [68526]=2, [68527]=4, + [69633]=5, + [69688]=5, + [69689]=5, + [69690]=5, + [69691]=5, + [69692]=5, + [69693]=5, + [69694]=5, + [69695]=5, + [69696]=5, + [69697]=5, + [69698]=5, + [69699]=5, + [69700]=5, + [69701]=5, + [69702]=5, + [69759]=5, + [69760]=5, + [69761]=5, + [69811]=5, + [69812]=5, + [69813]=5, + [69814]=5, + [69817]=5, + [69818]=5, + [69888]=5, + [69889]=5, + [69890]=5, + [69927]=5, + [69928]=5, + [69929]=5, + [69930]=5, + [69931]=5, + [69933]=5, + [69934]=5, + [69935]=5, + [69936]=5, + [69937]=5, + [69938]=5, + [69939]=5, + [69940]=5, + [70003]=5, + [70016]=5, + [70017]=5, + [70070]=5, + [70071]=5, + [70072]=5, + [70073]=5, + [70074]=5, + [70075]=5, + [70076]=5, + [70077]=5, + [70078]=5, + [70090]=5, + [70091]=5, + [70092]=5, + [70191]=5, + [70192]=5, + [70193]=5, + [70196]=5, + [70198]=5, + [70199]=5, + [70206]=5, + [70367]=5, + [70371]=5, + [70372]=5, + [70373]=5, + [70374]=5, + [70375]=5, + [70376]=5, + [70377]=5, + [70378]=5, + [70400]=5, + [70401]=5, + [70460]=5, + [70464]=5, + [70502]=5, + [70503]=5, + [70504]=5, + [70505]=5, + [70506]=5, + [70507]=5, + [70508]=5, + [70512]=5, + [70513]=5, + [70514]=5, + [70515]=5, + [70516]=5, + [70712]=5, + [70713]=5, + [70714]=5, + [70715]=5, + [70716]=5, + [70717]=5, + [70718]=5, + [70719]=5, + [70722]=5, + [70723]=5, + [70724]=5, + [70726]=5, + [70835]=5, + [70836]=5, + [70837]=5, + [70838]=5, + [70839]=5, + [70840]=5, + [70842]=5, + [70847]=5, + [70848]=5, + [70850]=5, + [70851]=5, + [71090]=5, + [71091]=5, + [71092]=5, + [71093]=5, + [71100]=5, + [71101]=5, + [71103]=5, + [71104]=5, + [71132]=5, + [71133]=5, + [71219]=5, + [71220]=5, + [71221]=5, + [71222]=5, + [71223]=5, + [71224]=5, + [71225]=5, + [71226]=5, + [71229]=5, + [71231]=5, + [71232]=5, + [71339]=5, + [71341]=5, + [71344]=5, + [71345]=5, + [71346]=5, + [71347]=5, + [71348]=5, + [71349]=5, + [71351]=5, + [71453]=5, + [71454]=5, + [71455]=5, + [71458]=5, + [71459]=5, + [71460]=5, + [71461]=5, + [71463]=5, + [71464]=5, + [71465]=5, + [71466]=5, + [71467]=5, + [72193]=5, + [72194]=5, + [72195]=5, + [72196]=5, + [72197]=5, + [72198]=5, + [72201]=5, + [72202]=5, + [72243]=5, + [72244]=5, + [72245]=5, + [72246]=5, + [72247]=5, + [72248]=5, + [72251]=5, + [72252]=5, + [72253]=5, + [72254]=5, + [72263]=5, + [72273]=5, + [72274]=5, + [72275]=5, + [72276]=5, + [72277]=5, + [72278]=5, + [72281]=5, + [72282]=5, + [72283]=5, + [72330]=5, + [72331]=5, + [72332]=5, + [72333]=5, + [72334]=5, + [72335]=5, + [72336]=5, + [72337]=5, + [72338]=5, + [72339]=5, + [72340]=5, + [72341]=5, + [72342]=5, + [72344]=5, + [72345]=5, + [72752]=5, + [72753]=5, + [72754]=5, + [72755]=5, + [72756]=5, + [72757]=5, + [72758]=5, + [72760]=5, + [72761]=5, + [72762]=5, + [72763]=5, + [72764]=5, + [72765]=5, + [72767]=5, + [72850]=5, + [72851]=5, + [72852]=5, + [72853]=5, + [72854]=5, + [72855]=5, + [72856]=5, + [72857]=5, + [72858]=5, + [72859]=5, + [72860]=5, + [72861]=5, + [72862]=5, + [72863]=5, + [72864]=5, + [72865]=5, + [72866]=5, + [72867]=5, + [72868]=5, + [72869]=5, + [72870]=5, + [72871]=5, + [72874]=5, + [72875]=5, + [72876]=5, + [72877]=5, + [72878]=5, + [72879]=5, + [72880]=5, + [72882]=5, + [72883]=5, + [72885]=5, + [72886]=5, + [73009]=5, + [73010]=5, + [73011]=5, + [73012]=5, + [73013]=5, + [73014]=5, + [73018]=5, + [73020]=5, + [73021]=5, + [73023]=5, + [73024]=5, + [73025]=5, + [73026]=5, + [73027]=5, + [73028]=5, + [73029]=5, + [73031]=5, + [92912]=5, + [92913]=5, + [92914]=5, + [92915]=5, + [92916]=5, + [92976]=5, + [92977]=5, + [92978]=5, + [92979]=5, + [92980]=5, + [92981]=5, + [92982]=5, + [94095]=5, + [94096]=5, + [94097]=5, + [94098]=5, + [113821]=5, + [113822]=5, + [119143]=5, + [119144]=5, + [119145]=5, + [119163]=5, + [119164]=5, + [119165]=5, + [119166]=5, + [119167]=5, + [119168]=5, + [119169]=5, + [119170]=5, + [119173]=5, + [119174]=5, + [119175]=5, + [119176]=5, + [119177]=5, + [119178]=5, + [119179]=5, + [119210]=5, + [119211]=5, + [119212]=5, + [119213]=5, + [119362]=5, + [119363]=5, + [119364]=5, + [121344]=5, + [121345]=5, + [121346]=5, + [121347]=5, + [121348]=5, + [121349]=5, + [121350]=5, + [121351]=5, + [121352]=5, + [121353]=5, + [121354]=5, + [121355]=5, + [121356]=5, + [121357]=5, + [121358]=5, + [121359]=5, + [121360]=5, + [121361]=5, + [121362]=5, + [121363]=5, + [121364]=5, + [121365]=5, + [121366]=5, + [121367]=5, + [121368]=5, + [121369]=5, + [121370]=5, + [121371]=5, + [121372]=5, + [121373]=5, + [121374]=5, + [121375]=5, + [121376]=5, + [121377]=5, + [121378]=5, + [121379]=5, + [121380]=5, + [121381]=5, + [121382]=5, + [121383]=5, + [121384]=5, + [121385]=5, + [121386]=5, + [121387]=5, + [121388]=5, + [121389]=5, + [121390]=5, + [121391]=5, + [121392]=5, + [121393]=5, + [121394]=5, + [121395]=5, + [121396]=5, + [121397]=5, + [121398]=5, + [121403]=5, + [121404]=5, + [121405]=5, + [121406]=5, + [121407]=5, + [121408]=5, + [121409]=5, + [121410]=5, + [121411]=5, + [121412]=5, + [121413]=5, + [121414]=5, + [121415]=5, + [121416]=5, + [121417]=5, + [121418]=5, + [121419]=5, + [121420]=5, + [121421]=5, + [121422]=5, + [121423]=5, + [121424]=5, + [121425]=5, + [121426]=5, + [121427]=5, + [121428]=5, + [121429]=5, + [121430]=5, + [121431]=5, + [121432]=5, + [121433]=5, + [121434]=5, + [121435]=5, + [121436]=5, + [121437]=5, + [121438]=5, + [121439]=5, + [121440]=5, + [121441]=5, + [121442]=5, + [121443]=5, + [121444]=5, + [121445]=5, + [121446]=5, + [121447]=5, + [121448]=5, + [121449]=5, + [121450]=5, + [121451]=5, + [121452]=5, + [121461]=5, + [121476]=5, + [121499]=5, + [121500]=5, + [121501]=5, + [121502]=5, + [121503]=5, + [121505]=5, + [121506]=5, + [121507]=5, + [121508]=5, + [121509]=5, + [121510]=5, + [121511]=5, + [121512]=5, + [121513]=5, + [121514]=5, + [121515]=5, + [121516]=5, + [121517]=5, + [121518]=5, + [121519]=5, + [122880]=5, + [122881]=5, + [122882]=5, + [122883]=5, + [122884]=5, + [122885]=5, + [122886]=5, + [122888]=5, + [122889]=5, + [122890]=5, + [122891]=5, + [122892]=5, + [122893]=5, + [122894]=5, + [122895]=5, + [122896]=5, + [122897]=5, + [122898]=5, + [122899]=5, + [122900]=5, + [122901]=5, + [122902]=5, + [122903]=5, + [122904]=5, + [122907]=5, + [122908]=5, + [122909]=5, + [122910]=5, + [122911]=5, + [122912]=5, + [122913]=5, + [122915]=5, + [122916]=5, + [122918]=5, + [122919]=5, + [122920]=5, + [122921]=5, + [122922]=5, + [125136]=5, + [125137]=5, + [125138]=5, + [125139]=5, + [125140]=5, + [125141]=5, + [125142]=5, + [125184]=2, + [125185]=2, + [125186]=2, + [125187]=2, + [125188]=2, + [125189]=2, + [125190]=2, + [125191]=2, + [125192]=2, + [125193]=2, + [125194]=2, + [125195]=2, + [125196]=2, + [125197]=2, + [125198]=2, + [125199]=2, + [125200]=2, + [125201]=2, + [125202]=2, + [125203]=2, + [125204]=2, + [125205]=2, + [125206]=2, + [125207]=2, + [125208]=2, + [125209]=2, + [125210]=2, + [125211]=2, + [125212]=2, + [125213]=2, + [125214]=2, + [125215]=2, + [125216]=2, + [125217]=2, + [125218]=2, + [125219]=2, + [125220]=2, + [125221]=2, + [125222]=2, + [125223]=2, + [125224]=2, + [125225]=2, + [125226]=2, + [125227]=2, + [125228]=2, + [125229]=2, + [125230]=2, + [125231]=2, + [125232]=2, + [125233]=2, + [125234]=2, + [125235]=2, + [125236]=2, + [125237]=2, + [125238]=2, + [125239]=2, + [125240]=2, + [125241]=2, + [125242]=2, + [125243]=2, + [125244]=2, + [125245]=2, + [125246]=2, + [125247]=2, + [125248]=2, + [125249]=2, + [125250]=2, + [125251]=2, + [125252]=5, + [125253]=5, + [125254]=5, + [125255]=5, + [125256]=5, + [125257]=5, + [125258]=5, + [1042752]=5, +} +characters.indicgroups={ + ["above_mark"]={ + [2304]=true, + [2305]=true, + [2306]=true, + [2362]=true, + [2373]=true, + [2374]=true, + [2375]=true, + [2376]=true, + [2385]=true, + [2387]=true, + [2388]=true, + [2389]=true, + [2631]=true, + [2632]=true, + [2635]=true, + [2636]=true, + [2757]=true, + [2759]=true, + [2760]=true, + [2879]=true, + [3008]=true, + [3021]=true, + [3134]=true, + [3135]=true, + [3136]=true, + [3142]=true, + [3143]=true, + [3144]=true, + [3146]=true, + [3147]=true, + [3148]=true, + [3149]=true, + [3263]=true, + [3270]=true, + [3406]=true, + [43232]=true, + [43233]=true, + [43234]=true, + [43235]=true, + [43236]=true, + [43237]=true, + [43238]=true, + [43239]=true, + [43240]=true, + [43241]=true, + [43242]=true, + [43243]=true, + [43244]=true, + [43245]=true, + [43246]=true, + [43247]=true, + [43248]=true, + [43249]=true, + }, + ["after_half"]={}, + ["after_main"]={ + [2864]=true, + [2879]=true, + [2902]=true, + [3376]=true, + }, + ["after_postscript"]={ + [2433]=true, + [2494]=true, + [2496]=true, + [2519]=true, + [2561]=true, + [2562]=true, + [2622]=true, + [2624]=true, + [2625]=true, + [2626]=true, + [2672]=true, + [2673]=true, + [2750]=true, + [2752]=true, + [2753]=true, + [2754]=true, + [2755]=true, + [2756]=true, + [2761]=true, + [2763]=true, + [2764]=true, + [2786]=true, + [2787]=true, + [2878]=true, + [2880]=true, + [2903]=true, + [2992]=true, + [3006]=true, + [3007]=true, + [3009]=true, + [3010]=true, + [3031]=true, + [3120]=true, + [3248]=true, + [3390]=true, + [3391]=true, + [3392]=true, + [3393]=true, + [3394]=true, + [3395]=true, + [3415]=true, + }, + ["after_subscript"]={ + [2366]=true, + [2368]=true, + [2369]=true, + [2370]=true, + [2371]=true, + [2372]=true, + [2373]=true, + [2374]=true, + [2375]=true, + [2376]=true, + [2377]=true, + [2378]=true, + [2379]=true, + [2380]=true, + [2402]=true, + [2403]=true, + [2480]=true, + [2497]=true, + [2498]=true, + [2499]=true, + [2500]=true, + [2530]=true, + [2531]=true, + [2544]=true, + [2631]=true, + [2632]=true, + [2635]=true, + [2636]=true, + [2757]=true, + [2759]=true, + [2760]=true, + [2881]=true, + [2882]=true, + [2883]=true, + [3008]=true, + [3139]=true, + [3140]=true, + [3267]=true, + [3268]=true, + [3285]=true, + [3286]=true, + }, + ["anudatta"]={ + [2386]=true, + }, + ["before_half"]={ + [2367]=true, + [2382]=true, + [2495]=true, + [2503]=true, + [2504]=true, + [2623]=true, + [2751]=true, + [2887]=true, + }, + ["before_main"]={ + [3014]=true, + [3015]=true, + [3016]=true, + [3398]=true, + [3399]=true, + [3400]=true, + }, + ["before_postscript"]={ + [2352]=true, + [2736]=true, + }, + ["before_subscript"]={ + [2608]=true, + [2817]=true, + [3134]=true, + [3135]=true, + [3136]=true, + [3137]=true, + [3138]=true, + [3142]=true, + [3143]=true, + [3146]=true, + [3147]=true, + [3148]=true, + [3157]=true, + [3158]=true, + [3262]=true, + [3263]=true, + [3265]=true, + [3266]=true, + [3270]=true, + [3276]=true, + [3298]=true, + [3299]=true, + }, + ["below_mark"]={ + [2364]=true, + [2369]=true, + [2370]=true, + [2371]=true, + [2372]=true, + [2381]=true, + [2386]=true, + [2390]=true, + [2391]=true, + [2402]=true, + [2403]=true, + [2492]=true, + [2497]=true, + [2498]=true, + [2499]=true, + [2500]=true, + [2509]=true, + [2620]=true, + [2625]=true, + [2626]=true, + [2637]=true, + [2748]=true, + [2753]=true, + [2754]=true, + [2755]=true, + [2756]=true, + [2765]=true, + [2876]=true, + [2881]=true, + [2882]=true, + [2883]=true, + [2884]=true, + [2893]=true, + [2914]=true, + [2915]=true, + [3009]=true, + [3010]=true, + [3170]=true, + [3171]=true, + [3260]=true, + [3298]=true, + [3299]=true, + [3426]=true, + [3427]=true, + }, + ["consonant"]={ + [2325]=true, + [2326]=true, + [2327]=true, + [2328]=true, + [2329]=true, + [2330]=true, + [2331]=true, + [2332]=true, + [2333]=true, + [2334]=true, + [2335]=true, + [2336]=true, + [2337]=true, + [2338]=true, + [2339]=true, + [2340]=true, + [2341]=true, + [2342]=true, + [2343]=true, + [2344]=true, + [2345]=true, + [2346]=true, + [2347]=true, + [2348]=true, + [2349]=true, + [2350]=true, + [2351]=true, + [2352]=true, + [2353]=true, + [2354]=true, + [2355]=true, + [2356]=true, + [2357]=true, + [2358]=true, + [2359]=true, + [2360]=true, + [2361]=true, + [2392]=true, + [2393]=true, + [2394]=true, + [2395]=true, + [2396]=true, + [2397]=true, + [2398]=true, + [2399]=true, + [2424]=true, + [2425]=true, + [2426]=true, + [2453]=true, + [2454]=true, + [2455]=true, + [2456]=true, + [2457]=true, + [2458]=true, + [2459]=true, + [2460]=true, + [2461]=true, + [2462]=true, + [2463]=true, + [2464]=true, + [2465]=true, + [2466]=true, + [2467]=true, + [2468]=true, + [2469]=true, + [2470]=true, + [2471]=true, + [2472]=true, + [2474]=true, + [2475]=true, + [2476]=true, + [2477]=true, + [2478]=true, + [2479]=true, + [2480]=true, + [2482]=true, + [2486]=true, + [2487]=true, + [2488]=true, + [2489]=true, + [2510]=true, + [2524]=true, + [2525]=true, + [2527]=true, + [2581]=true, + [2582]=true, + [2583]=true, + [2584]=true, + [2585]=true, + [2586]=true, + [2587]=true, + [2588]=true, + [2589]=true, + [2590]=true, + [2591]=true, + [2592]=true, + [2593]=true, + [2594]=true, + [2595]=true, + [2596]=true, + [2597]=true, + [2598]=true, + [2599]=true, + [2600]=true, + [2602]=true, + [2603]=true, + [2604]=true, + [2605]=true, + [2606]=true, + [2607]=true, + [2608]=true, + [2610]=true, + [2611]=true, + [2613]=true, + [2614]=true, + [2616]=true, + [2617]=true, + [2649]=true, + [2650]=true, + [2651]=true, + [2652]=true, + [2654]=true, + [2709]=true, + [2710]=true, + [2711]=true, + [2712]=true, + [2713]=true, + [2714]=true, + [2715]=true, + [2716]=true, + [2717]=true, + [2718]=true, + [2719]=true, + [2720]=true, + [2721]=true, + [2722]=true, + [2723]=true, + [2724]=true, + [2725]=true, + [2726]=true, + [2727]=true, + [2728]=true, + [2730]=true, + [2731]=true, + [2732]=true, + [2733]=true, + [2734]=true, + [2735]=true, + [2736]=true, + [2738]=true, + [2739]=true, + [2741]=true, + [2742]=true, + [2743]=true, + [2744]=true, + [2745]=true, + [2837]=true, + [2838]=true, + [2839]=true, + [2840]=true, + [2841]=true, + [2842]=true, + [2843]=true, + [2844]=true, + [2845]=true, + [2846]=true, + [2847]=true, + [2848]=true, + [2849]=true, + [2850]=true, + [2851]=true, + [2852]=true, + [2853]=true, + [2854]=true, + [2855]=true, + [2856]=true, + [2858]=true, + [2859]=true, + [2860]=true, + [2861]=true, + [2862]=true, + [2863]=true, + [2864]=true, + [2866]=true, + [2867]=true, + [2869]=true, + [2870]=true, + [2871]=true, + [2872]=true, + [2873]=true, + [2908]=true, + [2909]=true, + [2929]=true, + [2965]=true, + [2969]=true, + [2970]=true, + [2972]=true, + [2974]=true, + [2975]=true, + [2979]=true, + [2980]=true, + [2984]=true, + [2985]=true, + [2986]=true, + [2990]=true, + [2991]=true, + [2992]=true, + [2993]=true, + [2994]=true, + [2995]=true, + [2996]=true, + [2997]=true, + [2998]=true, + [2999]=true, + [3000]=true, + [3001]=true, + [3093]=true, + [3094]=true, + [3095]=true, + [3096]=true, + [3097]=true, + [3098]=true, + [3099]=true, + [3100]=true, + [3101]=true, + [3102]=true, + [3103]=true, + [3104]=true, + [3105]=true, + [3106]=true, + [3107]=true, + [3108]=true, + [3109]=true, + [3110]=true, + [3111]=true, + [3112]=true, + [3114]=true, + [3115]=true, + [3116]=true, + [3117]=true, + [3118]=true, + [3119]=true, + [3120]=true, + [3121]=true, + [3122]=true, + [3123]=true, + [3124]=true, + [3125]=true, + [3126]=true, + [3127]=true, + [3128]=true, + [3129]=true, + [3133]=true, + [3221]=true, + [3222]=true, + [3223]=true, + [3224]=true, + [3225]=true, + [3226]=true, + [3227]=true, + [3228]=true, + [3229]=true, + [3230]=true, + [3231]=true, + [3232]=true, + [3233]=true, + [3234]=true, + [3235]=true, + [3236]=true, + [3237]=true, + [3238]=true, + [3239]=true, + [3240]=true, + [3242]=true, + [3243]=true, + [3244]=true, + [3245]=true, + [3246]=true, + [3247]=true, + [3248]=true, + [3249]=true, + [3250]=true, + [3251]=true, + [3253]=true, + [3254]=true, + [3255]=true, + [3256]=true, + [3257]=true, + [3294]=true, + [3349]=true, + [3350]=true, + [3351]=true, + [3352]=true, + [3353]=true, + [3354]=true, + [3355]=true, + [3356]=true, + [3357]=true, + [3358]=true, + [3359]=true, + [3360]=true, + [3361]=true, + [3362]=true, + [3363]=true, + [3364]=true, + [3365]=true, + [3366]=true, + [3367]=true, + [3368]=true, + [3369]=true, + [3370]=true, + [3371]=true, + [3372]=true, + [3373]=true, + [3374]=true, + [3375]=true, + [3376]=true, + [3377]=true, + [3378]=true, + [3379]=true, + [3380]=true, + [3381]=true, + [3382]=true, + [3383]=true, + [3384]=true, + [3385]=true, + [3386]=true, + }, + ["dependent_vowel"]={ + [2362]=true, + [2363]=true, + [2366]=true, + [2367]=true, + [2368]=true, + [2369]=true, + [2370]=true, + [2371]=true, + [2372]=true, + [2373]=true, + [2374]=true, + [2375]=true, + [2376]=true, + [2377]=true, + [2378]=true, + [2379]=true, + [2380]=true, + [2382]=true, + [2383]=true, + [2389]=true, + [2390]=true, + [2391]=true, + [2402]=true, + [2403]=true, + [2494]=true, + [2495]=true, + [2496]=true, + [2497]=true, + [2498]=true, + [2499]=true, + [2500]=true, + [2503]=true, + [2504]=true, + [2622]=true, + [2623]=true, + [2624]=true, + [2625]=true, + [2626]=true, + [2631]=true, + [2632]=true, + [2635]=true, + [2636]=true, + [2750]=true, + [2751]=true, + [2752]=true, + [2753]=true, + [2754]=true, + [2755]=true, + [2756]=true, + [2757]=true, + [2759]=true, + [2760]=true, + [2761]=true, + [2763]=true, + [2764]=true, + [2878]=true, + [2879]=true, + [2880]=true, + [2881]=true, + [2882]=true, + [2883]=true, + [2884]=true, + [2887]=true, + [2888]=true, + [2891]=true, + [2892]=true, + [2914]=true, + [2915]=true, + [3006]=true, + [3007]=true, + [3008]=true, + [3009]=true, + [3010]=true, + [3014]=true, + [3015]=true, + [3016]=true, + [3018]=true, + [3019]=true, + [3020]=true, + [3134]=true, + [3135]=true, + [3136]=true, + [3137]=true, + [3138]=true, + [3139]=true, + [3140]=true, + [3142]=true, + [3143]=true, + [3144]=true, + [3146]=true, + [3147]=true, + [3148]=true, + [3170]=true, + [3171]=true, + [3262]=true, + [3263]=true, + [3264]=true, + [3265]=true, + [3266]=true, + [3267]=true, + [3268]=true, + [3270]=true, + [3271]=true, + [3272]=true, + [3274]=true, + [3275]=true, + [3276]=true, + [3298]=true, + [3299]=true, + [3390]=true, + [3391]=true, + [3392]=true, + [3393]=true, + [3394]=true, + [3395]=true, + [3396]=true, + [3398]=true, + [3399]=true, + [3400]=true, + [3402]=true, + [3403]=true, + [3404]=true, + [3415]=true, + [3426]=true, + [3427]=true, + }, + ["halant"]={ + [2381]=true, + [2509]=true, + [2637]=true, + [2765]=true, + [2893]=true, + [3021]=true, + [3149]=true, + [3277]=true, + [3405]=true, + }, + ["independent_vowel"]={ + [2308]=true, + [2309]=true, + [2310]=true, + [2311]=true, + [2312]=true, + [2313]=true, + [2314]=true, + [2315]=true, + [2316]=true, + [2317]=true, + [2318]=true, + [2319]=true, + [2320]=true, + [2321]=true, + [2322]=true, + [2323]=true, + [2324]=true, + [2400]=true, + [2401]=true, + [2418]=true, + [2419]=true, + [2420]=true, + [2421]=true, + [2422]=true, + [2423]=true, + [2437]=true, + [2438]=true, + [2439]=true, + [2440]=true, + [2441]=true, + [2442]=true, + [2443]=true, + [2444]=true, + [2447]=true, + [2448]=true, + [2451]=true, + [2452]=true, + [2528]=true, + [2529]=true, + [2530]=true, + [2531]=true, + [2565]=true, + [2566]=true, + [2567]=true, + [2568]=true, + [2569]=true, + [2570]=true, + [2575]=true, + [2576]=true, + [2579]=true, + [2580]=true, + [2693]=true, + [2694]=true, + [2695]=true, + [2696]=true, + [2697]=true, + [2698]=true, + [2699]=true, + [2700]=true, + [2701]=true, + [2703]=true, + [2704]=true, + [2705]=true, + [2707]=true, + [2708]=true, + [2784]=true, + [2785]=true, + [2786]=true, + [2787]=true, + [2821]=true, + [2822]=true, + [2823]=true, + [2824]=true, + [2825]=true, + [2826]=true, + [2827]=true, + [2828]=true, + [2831]=true, + [2832]=true, + [2835]=true, + [2836]=true, + [2912]=true, + [2913]=true, + [2949]=true, + [2950]=true, + [2951]=true, + [2952]=true, + [2953]=true, + [2954]=true, + [2958]=true, + [2959]=true, + [2960]=true, + [2962]=true, + [2963]=true, + [2964]=true, + [3077]=true, + [3078]=true, + [3079]=true, + [3080]=true, + [3081]=true, + [3082]=true, + [3083]=true, + [3084]=true, + [3086]=true, + [3087]=true, + [3088]=true, + [3090]=true, + [3091]=true, + [3092]=true, + [3168]=true, + [3169]=true, + [3205]=true, + [3206]=true, + [3207]=true, + [3208]=true, + [3209]=true, + [3210]=true, + [3211]=true, + [3212]=true, + [3214]=true, + [3215]=true, + [3216]=true, + [3218]=true, + [3219]=true, + [3220]=true, + [3296]=true, + [3297]=true, + [3333]=true, + [3334]=true, + [3335]=true, + [3336]=true, + [3337]=true, + [3338]=true, + [3339]=true, + [3340]=true, + [3342]=true, + [3343]=true, + [3344]=true, + [3346]=true, + [3347]=true, + [3348]=true, + [3423]=true, + [3424]=true, + [3425]=true, + }, + ["nukta"]={ + [2364]=true, + [2492]=true, + [2620]=true, + [2748]=true, + [2876]=true, + [3260]=true, + }, + ["post_mark"]={ + [2307]=true, + [2363]=true, + [2366]=true, + [2368]=true, + [2377]=true, + [2378]=true, + [2379]=true, + [2380]=true, + [2383]=true, + [2494]=true, + [2496]=true, + [2503]=true, + [2504]=true, + [2622]=true, + [2624]=true, + [2750]=true, + [2752]=true, + [2761]=true, + [2763]=true, + [2764]=true, + [2878]=true, + [2880]=true, + [3006]=true, + [3007]=true, + [3137]=true, + [3138]=true, + [3139]=true, + [3140]=true, + [3262]=true, + [3264]=true, + [3265]=true, + [3266]=true, + [3267]=true, + [3268]=true, + [3271]=true, + [3272]=true, + [3274]=true, + [3275]=true, + [3276]=true, + [3390]=true, + [3391]=true, + [3392]=true, + [3393]=true, + [3394]=true, + [3395]=true, + [3396]=true, + [3415]=true, + }, + ["pre_mark"]={ + [2367]=true, + [2382]=true, + [2495]=true, + [2623]=true, + [2751]=true, + [2887]=true, + [2888]=true, + [3014]=true, + [3015]=true, + [3016]=true, + [3398]=true, + [3399]=true, + [3400]=true, + }, + ["ra"]={ + [2352]=true, + [2480]=true, + [2608]=true, + [2736]=true, + [2864]=true, + [2992]=true, + [3120]=true, + [3248]=true, + [3376]=true, + }, + ["stress_tone_mark"]={ + [2385]=true, + [2386]=true, + [2387]=true, + [2388]=true, + [2507]=true, + [2508]=true, + [3277]=true, + [3405]=true, + }, + ["twopart_mark"]={ + [2891]={ 2887,2878 }, + [2892]={ 2887,2903 }, + [3018]={ 3014,3006 }, + [3019]={ 3015,3006 }, + [3020]={ 3014,3031 }, + [3402]={ 3398,3390 }, + [3403]={ 3399,3390 }, + [3404]={ 3398,3415 }, + }, + ["vowel_modifier"]={ + [2304]=true, + [2305]=true, + [2306]=true, + [2307]=true, + [3330]=true, + [3331]=true, + [43232]=true, + [43233]=true, + [43234]=true, + [43235]=true, + [43236]=true, + [43237]=true, + [43238]=true, + [43239]=true, + [43240]=true, + [43241]=true, + [43242]=true, + [43243]=true, + [43244]=true, + [43245]=true, + [43246]=true, + [43247]=true, + [43248]=true, + [43249]=true, + }, } end -- closure @@ -5835,7 +9051,7 @@ if not modules then modules={} end modules ['font-ini']={ local allocate=utilities.storage.allocate fonts=fonts or {} local fonts=fonts -fonts.hashes={ identifiers=allocate() } +fonts.hashes=fonts.hashes or { identifiers=allocate() } fonts.tables=fonts.tables or {} fonts.helpers=fonts.helpers or {} fonts.tracers=fonts.tracers or {} @@ -5844,7 +9060,42 @@ fonts.analyzers={} fonts.readers={} fonts.definers={ methods={} } fonts.loggers={ register=function() end } -fontloader.totable=fontloader.to_table +if context then +--removed + +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luatex-font-mis']={ + 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 +--removed + +end +local currentfont=font.current +local hashes=fonts.hashes +local identifiers=hashes.identifiers or {} +local marks=hashes.marks or {} +hashes.identifiers=identifiers +hashes.marks=marks +table.setmetatableindex(marks,function(t,k) + if k==true then + return marks[currentfont()] + else + local resources=identifiers[k].resources or {} + local marks=resources.marks or {} + t[k]=marks + return marks + end +end) end -- closure @@ -5857,11 +9108,14 @@ if not modules then modules={} end modules ['font-con']={ copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local next,tostring,rawget=next,tostring,rawget +local next,tostring,tonumber,rawget=next,tostring,tonumber,rawget local format,match,lower,gsub,find=string.format,string.match,string.lower,string.gsub,string.find -local sort,insert,concat,sortedkeys,serialize,fastcopy=table.sort,table.insert,table.concat,table.sortedkeys,table.serialize,table.fastcopy +local sort,insert,concat=table.sort,table.insert,table.concat +local sortedkeys,sortedhash,serialize,fastcopy=table.sortedkeys,table.sortedhash,table.serialize,table.fastcopy local derivetable=table.derive local ioflush=io.flush +local round=math.round +local setmetatable,getmetatable,rawget,rawset=setmetatable,getmetatable,rawget,rawset local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end) local trace_scaling=false trackers.register("fonts.scaling",function(v) trace_scaling=v end) local report_defining=logs.reporter("fonts","defining") @@ -5907,6 +9161,28 @@ function constructors.scaled(scaledpoints,designsize) return scaledpoints end end +function constructors.getprivate(tfmdata) + local properties=tfmdata.properties + local private=properties.private + properties.private=private+1 + return private +end +function constructors.setmathparameter(tfmdata,name,value) + local m=tfmdata.mathparameters + local c=tfmdata.MathConstants + if m then + m[name]=value + end + if c and c~=m then + c[name]=value + end +end +function constructors.getmathparameter(tfmdata,name) + local p=tfmdata.mathparameters or tfmdata.MathConstants + if p then + return p[name] + end +end function constructors.cleanuptable(tfmdata) if constructors.autocleanup and tfmdata.properties.virtualized then for k,v in next,tfmdata.characters do @@ -5983,27 +9259,62 @@ function constructors.trytosharefont(target,tfmdata) end end end +local synonyms={ + exheight="x_height", + xheight="x_height", + ex="x_height", + emwidth="quad", + em="quad", + spacestretch="space_stretch", + stretch="space_stretch", + spaceshrink="space_shrink", + shrink="space_shrink", + extraspace="extra_space", + xspace="extra_space", + slantperpoint="slant", +} function constructors.enhanceparameters(parameters) - local 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, - } + local mt=getmetatable(parameters) + local getter=function(t,k) + if not k then + return nil + end + local s=synonyms[k] + if s then + return rawget(t,s) or (mt and mt[s]) or nil + end + if k=="spacing" then + return { + width=t.space, + stretch=t.space_stretch, + shrink=t.space_shrink, + extra=t.extra_space, + } + end + return mt and mt[k] or nil + end + local setter=function(t,k,v) + if not k then + return 0 + end + local s=synonyms[k] + if s then + rawset(t,s,v) + elseif k=="spacing" then + if type(v)=="table" then + rawset(t,"space",v.width or 0) + rawset(t,"space_stretch",v.stretch or 0) + rawset(t,"space_shrink",v.shrink or 0) + rawset(t,"extra_space",v.extra or 0) + end + else + rawset(t,k,v) + end + end + setmetatable(parameters,{ + __index=getter, + __newindex=setter, + }) end local function mathkerns(v,vdelta) local k={} @@ -6022,7 +9333,7 @@ local psfake=0 local function fixedpsname(psname,fallback) local usedname=psname if psname and psname~="" then - if find(psname," ") then + if find(psname," ",1,true) then usedname=gsub(psname,"[%s]+","-") else end @@ -6067,7 +9378,7 @@ function constructors.scale(tfmdata,specification) 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 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 @@ -6075,6 +9386,7 @@ function constructors.scale(tfmdata,specification) scaledpoints=parameters.scriptscriptpercentage*textsize/100 elseif forcedsize>1000 then scaledpoints=forcedsize + else end targetparameters.mathsize=mathsize targetparameters.textsize=textsize @@ -6085,9 +9397,6 @@ function constructors.scale(tfmdata,specification) 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" @@ -6129,24 +9438,34 @@ function constructors.scale(tfmdata,specification) 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 + local slantfactor=parameters.slantfactor or 0 + if slantfactor~=0 then + target.slant=slantfactor*1000 + else + target.slant=0 end local extendfactor=parameters.extendfactor or 0 if extendfactor~=0 and extendfactor~=1 then hdelta=hdelta*extendfactor - target.extend=extendfactor*1000 + target.extend=extendfactor*1000 else target.extend=1000 end - local slantfactor=parameters.slantfactor or 0 - if slantfactor~=0 then - target.slant=slantfactor*1000 + local squeezefactor=parameters.squeezefactor or 0 + if squeezefactor~=0 and squeezefactor~=1 then + vdelta=vdelta*squeezefactor + target.squeeze=squeezefactor*1000 else - target.slant=0 + target.squeeze=1000 + end + local mode=parameters.mode or 0 + if mode~=0 then + target.mode=mode + end + local width=parameters.width or 0 + if width~=0 then + target.width=width*delta*1000/655360 end targetparameters.factor=delta targetparameters.hfactor=hdelta @@ -6154,8 +9473,10 @@ function constructors.scale(tfmdata,specification) targetparameters.size=scaledpoints targetparameters.units=units targetparameters.scaledpoints=askedscaledpoints + targetparameters.mode=mode + targetparameters.width=width local isvirtual=properties.virtualized or tfmdata.type=="virtual" - local hasquality=target.auto_expand or target.auto_protrude + local hasquality=parameters.expansion or parameters.protrusion local hasitalics=properties.hasitalics local autoitalicamount=properties.autoitalicamount local stackmath=not properties.nostackmath @@ -6165,6 +9486,12 @@ function constructors.scale(tfmdata,specification) local realdimensions=properties.realdimensions local writingmode=properties.writingmode or "horizontal" local identity=properties.identity or "horizontal" + local vfonts=target.fonts + if vfonts and #vfonts>0 then + target.fonts=fastcopy(vfonts) + elseif isvirtual then + target.fonts={ { id=0 } } + end if changed and not next(changed) then changed=false end @@ -6240,10 +9567,15 @@ function constructors.scale(tfmdata,specification) 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 + if c and c~=unicode then + if c then + description=descriptions[c] or descriptions[unicode] or character + character=characters[c] or character + index=description.index or c + else + description=descriptions[unicode] or character + index=description.index or unicode + end else description=descriptions[unicode] or character index=description.index or unicode @@ -6340,10 +9672,13 @@ function constructors.scale(tfmdata,specification) 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, + local s=vvi["start"] or 0 + local e=vvi["end"] or 0 + local a=vvi["advance"] or 0 + t[i]={ + ["start"]=s==0 and 0 or s*vdelta, + ["end"]=e==0 and 0 or e*vdelta, + ["advance"]=a==0 and 0 or a*vdelta, ["extender"]=vvi["extender"], ["glyph"]=vvi["glyph"], } @@ -6355,10 +9690,13 @@ function constructors.scale(tfmdata,specification) 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, + local s=hvi["start"] or 0 + local e=hvi["end"] or 0 + local a=hvi["advance"] or 0 + t[i]={ + ["start"]=s==0 and 0 or s*hdelta, + ["end"]=e==0 and 0 or e*hdelta, + ["advance"]=a==0 and 0 or a*hdelta, ["extender"]=hvi["extender"], ["glyph"]=hvi["glyph"], } @@ -6445,7 +9783,7 @@ function constructors.scale(tfmdata,specification) local ok=false for i=1,#vc do local key=vc[i][1] - if key=="right" or key=="down" then + if key=="right" or key=="down" or key=="rule" then ok=true break end @@ -6469,7 +9807,6 @@ function constructors.scale(tfmdata,specification) else chr.commands=vc end - chr.index=nil end end targetcharacters[unicode]=chr @@ -6477,8 +9814,22 @@ function constructors.scale(tfmdata,specification) properties.setitalics=hasitalics constructors.aftercopyingcharacters(target,tfmdata) constructors.trytosharefont(target,tfmdata) - return target -end + local vfonts=target.fonts +if isvirtual or target.type=="virtual" or properties.virtualized then + properties.virtualized=true +target.type="virtual" + if not vfonts or #vfonts==0 then + target.fonts={ { id=0 } } + end + elseif vfonts then + properties.virtualized=true + target.type="virtual" + if #vfonts==0 then + target.fonts={ { id=0 } } + end + end + return target +end function constructors.finalize(tfmdata) if tfmdata.properties and tfmdata.properties.finalized then return @@ -6495,26 +9846,29 @@ function constructors.finalize(tfmdata) 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 + stretch=tfmdata.stretch or 0, + shrink=tfmdata.shrink or 0, + step=tfmdata.step or 0, } end if not parameters.size then parameters.size=tfmdata.size end - if not parameters.extendfactor then - parameters.extendfactor=tfmdata.extend or 0 + if not parameters.mode then + parameters.mode=0 + end + if not parameters.width then + parameters.width=0 end if not parameters.slantfactor then parameters.slantfactor=tfmdata.slant or 0 end + if not parameters.extendfactor then + parameters.extendfactor=tfmdata.extend or 0 + end + if not parameters.squeezefactor then + parameters.squeezefactor=tfmdata.squeeze or 0 + end local designsize=parameters.designsize if designsize then parameters.minsize=tfmdata.minsize or designsize @@ -6589,10 +9943,11 @@ function constructors.finalize(tfmdata) tfmdata.stretch=nil tfmdata.shrink=nil tfmdata.step=nil - tfmdata.auto_expand=nil - tfmdata.auto_protrude=nil - tfmdata.extend=nil tfmdata.slant=nil + tfmdata.extend=nil + tfmdata.squeeze=nil + tfmdata.mode=nil + tfmdata.width=nil tfmdata.units=nil tfmdata.units_per_em=nil tfmdata.cache=nil @@ -6604,20 +9959,20 @@ 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 + local t,n={},0 + for category,list in sortedhash(features) do if next(list) then local hasher=hashmethods[category] if hasher then local hash=hasher(list) if hash then - tn=tn+1 - t[tn]=category..":"..hash + n=n+1 + t[n]=category..":"..hash end end end end - if tn>0 then + if n>0 then return concat(t," & ") end end @@ -6631,7 +9986,17 @@ hashmethods.normal=function(list) elseif k=="number" or k=="features" then else n=n+1 - s[n]=k..'='..tostring(v) + if type(v)=="table" then + local t={} + local m=0 + for k,v in next,v do + m=m+1 + t[m]=k..'='..tostring(v) + end + s[n]=k..'={'..concat(t,",").."}" + else + s[n]=k..'='..tostring(v) + end end end if n>0 then @@ -6646,13 +10011,15 @@ function constructors.hashinstance(specification,force) specification.hash=hash end if size<1000 and designsizes[hash] then - size=math.round(constructors.scaled(size,designsizes[hash])) - specification.size=size + size=round(constructors.scaled(size,designsizes[hash])) + else + size=round(size) end + specification.size=size if fallbacks then - return hash..' @ '..tostring(size)..' @ '..fallbacks + return hash..' @ '..size..' @ '..fallbacks else - return hash..' @ '..tostring(size) + return hash..' @ '..size end end function constructors.setname(tfmdata,specification) @@ -6833,10 +10200,12 @@ do local handler=handlers[format] local enhancers=handler.enhancers if not enhancers then - local actions=allocate() + local actions=allocate() local before=allocate() local after=allocate() local order=allocate() + local known={} + local nofsteps=0 local patches={ before=before,after=after } local trace=false local report=logs.reporter("fonts",format.." enhancing") @@ -6858,7 +10227,7 @@ do report("%s enhancing file %a","start",filename) end ioflush() - for e=1,#order do + for e=1,nofsteps do local enhancer=order[e] local b=before[enhancer] if b then @@ -6868,7 +10237,7 @@ do end end end - enhance(enhancer,data,filename,raw) + enhance(enhancer,data,filename,raw) local a=after[enhancer] if a then for pattern,action in next,a do @@ -6888,7 +10257,9 @@ do if action then if actions[what] then else - order[#order+1]=what + nofsteps=nofsteps+1 + order[nofsteps]=what + known[what]=nofsteps end actions[what]=action else @@ -6902,7 +10273,12 @@ do if ww then ww[pattern]=action else - pw[where]={ [pattern]=action} + pw[where]={ [pattern]=action } + if not known[where] then + nofsteps=nofsteps+1 + order[nofsteps]=where + known[where]=nofsteps + end end end end @@ -6910,7 +10286,11 @@ do register=register, apply=apply, patch=patch, - patches={ register=patch }, + report=report, + patches={ + register=patch, + report=report, + }, } handler.enhancers=enhancers end @@ -7070,8 +10450,8 @@ if not modules then modules={} end modules ['luatex-font-enc']={ license="see context related readme files" } if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() +--removed + end local fonts=fonts local encodings={} @@ -7080,7 +10460,7 @@ encodings.agl={} encodings.known={} setmetatable(encodings.agl,{ __index=function(t,k) if k=="unicodes" then - texio.write(" ") + logs.report("fonts","loading (extended) adobe glyph list") local unicodes=dofile(resolvers.findfile("font-age.lua")) encodings.agl={ unicodes=unicodes } return unicodes @@ -7136,7 +10516,7 @@ do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['font-cid']={ version=1.001, - comment="companion to font-otf.lua (cidmaps)", + 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" @@ -7297,12 +10677,12 @@ if not modules then modules={} end modules ['font-map']={ 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 rshift=bit32.rshift 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 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 {} @@ -7337,7 +10717,7 @@ local function tounicode16(unicode) return f_single(unicode) else unicode=unicode-0x10000 - return f_double(floor(unicode/1024)+0xD800,unicode%1024+0xDC00) + return f_double(rshift(unicode,10)+0xD800,unicode%1024+0xDC00) end end local function tounicode16sequence(unicodes) @@ -7348,32 +10728,40 @@ local function tounicode16sequence(unicodes) t[l]=f_single(u) else u=u-0x10000 - t[l]=f_double(floor(u/1024)+0xD800,u%1024+0xDC00) + t[l]=f_double(rshift(u,10)+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) +local unknown=f_single(0xFFFD) +local hash={} +local conc={} +table.setmetatableindex(hash,function(t,k) + if type(k)=="table" then + local n=#k + for l=1,n do + conc[l]=hash[k[l]] + end + return concat(conc,"",1,n) + end + local v + if k>=0x00E000 and k<=0x00F8FF then + v=unknown + elseif k>=0x0F0000 and k<=0x0FFFFF then + v=unknown + elseif k>=0x100000 and k<=0x10FFFF then + v=unknown + elseif k<0xD7FF or (k>0xDFFF and k<=0xFFFF) then + v=f_single(k) 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 + k=k-0x10000 + v=f_double(rshift(k,10)+0xD800,k%1024+0xDC00) end + t[k]=v + return v +end) +local function tounicode(unicode) + return hash[unicode] end local function fromunicode16(str) if #str==4 then @@ -7392,7 +10780,7 @@ 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 { + local overloads={ 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 }, @@ -7403,7 +10791,7 @@ do fj={ name="f_j",unicode={ 0x66,0x6A } }, fk={ name="f_k",unicode={ 0x66,0x6B } }, } - local o={} + local o=allocate {} for k,v in next,overloads do local name=v.name local mess=v.mess @@ -7417,7 +10805,7 @@ do end mappings.overloads=o end -function mappings.addtounicode(data,filename,checklookups) +function mappings.addtounicode(data,filename,checklookups,forceligatures) local resources=data.resources local unicodes=resources.unicodes if not unicodes then @@ -7586,34 +10974,46 @@ function mappings.addtounicode(data,filename,checklookups) 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 + local collected=fonts.handlers.otf.readers.getcomponents(data) + local function resolve(glyph,u) + local n=#u + for i=1,n do + if u[i]>private then + n=0 + break 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 + if not collected then + elseif forceligatures or force_ligatures then + for i=1,#dlist do + local du=dlist[i] + if du>=private or (du>=0xE000 and du<=0xF8FF) then + local u=collected[du] + if u then + resolve(descriptions[du],u) end - if n>0 then - if n>1 then - glyph.unicode=u - else - glyph.unicode=u[1] + end + end + else + for i=1,#dlist do + local du=dlist[i] + if du>=private or (du>=0xE000 and du<=0xF8FF) then + local glyph=descriptions[du] + if glyph.class=="ligature" and not glyph.unicode then + local u=collected[du] + if u then + resolve(glyph,u) end - unicoded=unicoded+1 end end end @@ -7660,8 +11060,8 @@ if not modules then modules={} end modules ['luatex-fonts-syn']={ license="see context related readme files" } if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() +--removed + end local fonts=fonts fonts.names=fonts.names or {} @@ -7672,11 +11072,11 @@ local data=nil local loaded=false local fileformats={ "lua","tex","other text files" } function fonts.names.reportmissingbase() - texio.write("") + logs.report("fonts","missing font database, run: mtxrun --script fonts --reload --simple") fonts.names.reportmissingbase=nil end function fonts.names.reportmissingname() - texio.write("") + logs.report("fonts","unknown font in font database, run: mtxrun --script fonts --reload --simple") fonts.names.reportmissingname=nil end function fonts.names.resolve(name,sub) @@ -7691,7 +11091,7 @@ function fonts.names.resolve(name,sub) local foundname=resolvers.findfile(basename,format) or "" if foundname~="" then data=dofile(foundname) - texio.write("") + logs.report("fonts","font database '%s' loaded",foundname) break end end @@ -7729,146 +11129,79 @@ end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules={} end modules ['font-oti']={ +if not modules then modules={} end modules ['font-vfc']={ version=1.001, - comment="companion to font-ini.mkiv", + comment="companion to font-ini.mkiv and hand-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 select=select +local insert=table.insert 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" +local helpers=fonts.helpers +local setmetatableindex=table.setmetatableindex +local makeweak=table.makeweak +local push={ "push" } +local pop={ "pop" } +local dummy={ "comment" } +function helpers.prependcommands(commands,...) + insert(commands,1,push) + for i=select("#",...),1,-1 do + local s=select(i,...) + if s then + insert(commands,1,s) end end + insert(commands,pop) + return commands 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" +function helpers.appendcommands(commands,...) + insert(commands,1,push) + insert(commands,pop) + for i=1,select("#",...) do + local s=select(i,...) + if s then + insert(commands,s) end end + return commands 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", +local char=setmetatableindex(function(t,k) + local v={ "slot",0,k } + t[k]=v + return v +end) +local right=setmetatableindex(function(t,k) + local v={ "right",k } + t[k]=v + return v +end) +local left=setmetatableindex(function(t,k) + local v={ "right",-k } + t[k]=v + return v +end) +local down=setmetatableindex(function(t,k) + local v={ "down",k } + t[k]=v + return v +end) +local up=setmetatableindex(function(t,k) + local v={ "down",-k } + t[k]=v + return v +end) +helpers.commands=utilities.storage.allocate { + char=char, + right=right, + left=left, + down=down, + up=up, + push=push, + pop=pop, + dummy=dummy, } -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 -- closure @@ -7881,21 +11214,21 @@ if not modules then modules={} end modules ['font-otr']={ 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 next,type,tonumber=next,type,tonumber +local byte,lower,char,gsub=string.byte,string.lower,string.char,string.gsub +local floor,round=math.floor,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 rshift=bit32.rshift local setmetatableindex=table.setmetatableindex local formatters=string.formatters local sortedkeys=table.sortedkeys local sortedhash=table.sortedhash -local stripstring=string.strip +local stripstring=string.nospaces local utf16_to_utf8_be=utf.utf16_to_utf8_be local report=logs.reporter("otf reader") local trace_cmap=false +local trace_cmap_detail=false fonts=fonts or {} local handlers=fonts.handlers or {} fonts.handlers=handlers @@ -7920,27 +11253,50 @@ local readulong=streamreader.readcardinal4 local readshort=streamreader.readinteger2 local readlong=streamreader.readinteger4 local readfixed=streamreader.readfixed4 +local read2dot14=streamreader.read2dot14 local readfword=readshort local readufword=readushort local readoffset=readushort -local read2dot14=streamreader.read2dot14 +local readcardinaltable=streamreader.readcardinaltable +local readintegertable=streamreader.readintegertable function streamreader.readtag(f) - return lower(strip(readstring(f,4))) -end + return lower(stripstring(readstring(f,4))) +end +local short=2 +local ushort=2 +local ulong=4 +directives.register("fonts.streamreader",function() + streamreader=utilities.streams + openfile=streamreader.open + closefile=streamreader.close + setposition=streamreader.setposition + skipshort=streamreader.skipshort + readbytes=streamreader.readbytes + readstring=streamreader.readstring + readbyte=streamreader.readcardinal1 + readushort=streamreader.readcardinal2 + readuint=streamreader.readcardinal3 + readulong=streamreader.readcardinal4 + readshort=streamreader.readinteger2 + readlong=streamreader.readinteger4 + readfixed=streamreader.readfixed4 + read2dot14=streamreader.read2dot14 + readfword=readshort + readufword=readushort + readoffset=readushort + readcardinaltable=streamreader.readcardinaltable + readintegertable=streamreader.readintegertable + function streamreader.readtag(f) + return lower(stripstring(readstring(f,4))) + end +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 privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000 local reservednames={ [0]="copyright", "family", "subfamily", @@ -7966,6 +11322,7 @@ local reservednames={ [0]="copyright", "wwssubfamily", "lightbackgroundpalette", "darkbackgroundpalette", + "variationspostscriptnameprefix", } local platforms={ [0]="unicode", "macintosh", @@ -8131,6 +11488,37 @@ local panosewidths={ [ 8]="verycondensed", [ 9]="monospaced", } +local helpers={} +readers.helpers=helpers +local function gotodatatable(f,fontdata,tag,criterium) + if criterium and f then + local datatable=fontdata.tables[tag] + if datatable then + local tableoffset=datatable.offset + setposition(f,tableoffset) + return tableoffset + end + end +end +local function reportskippedtable(f,fontdata,tag,criterium) + if criterium and f then + local datatable=fontdata.tables[tag] + if datatable then + report("loading of table %a skipped",tag) + end + end +end +local function setvariabledata(fontdata,tag,data) + local variabledata=fontdata.variabledata + if variabledata then + variabledata[tag]=data + else + fontdata.variabledata={ [tag]=data } + end +end +helpers.gotodatatable=gotodatatable +helpers.setvariabledata=setvariabledata +helpers.reportskippedtable=reportskippedtable local platformnames={ postscriptname=true, fullname=true, @@ -8141,13 +11529,12 @@ local platformnames={ compatiblefullname=true, } function readers.name(f,fontdata,specification) - local datatable=fontdata.tables.name - if datatable then - setposition(f,datatable.offset) + local tableoffset=gotodatatable(f,fontdata,"name",true) + if tableoffset then local format=readushort(f) local nofnames=readushort(f) local offset=readushort(f) - local start=datatable.offset+offset + local start=tableoffset+offset local namelists={ unicode={}, windows={}, @@ -8166,19 +11553,17 @@ function readers.name(f,fontdata,specification) 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 + local index=readushort(f) + local name=reservednames[index] + namelist[#namelist+1]={ + platform=platform, + encoding=encoding, + language=language, + name=name, + index=index, + length=readushort(f), + offset=start+readushort(f), + } else skipshort(f,3) end @@ -8194,12 +11579,14 @@ function readers.name(f,fontdata,specification) end local names={} local done={} + local extras={} 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 index=name.index + if not done[nametag or i] then local encoding=name.encoding local language=name.language if (not e or encoding==e) and (not l or language==l) then @@ -8212,13 +11599,16 @@ function readers.name(f,fontdata,specification) if decoder then content=decoder(content) end - names[nametag]={ - content=content, - platform=platform, - encoding=encoding, - language=language, - } - done[nametag]=true + if nametag then + names[nametag]={ + content=content, + platform=platform, + encoding=encoding, + language=language, + } + end + extras[index]=content + done[nametag or i]=true end end end @@ -8229,6 +11619,7 @@ function readers.name(f,fontdata,specification) filter("macintosh") filter("unicode") fontdata.names=names + fontdata.extras=extras if specification.platformnames then local collected={} for platform,namelist in next,namelists do @@ -8276,9 +11667,8 @@ local function getname(fontdata,key) end end readers["os/2"]=function(f,fontdata) - local datatable=fontdata.tables["os/2"] - if datatable then - setposition(f,datatable.offset) + local tableoffset=gotodatatable(f,fontdata,"os/2",true) + if tableoffset then local version=readushort(f) local windowsmetrics={ version=version, @@ -8328,9 +11718,8 @@ readers["os/2"]=function(f,fontdata) end end readers.head=function(f,fontdata) - local datatable=fontdata.tables.head - if datatable then - setposition(f,datatable.offset) + local tableoffset=gotodatatable(f,fontdata,"head",true) + if tableoffset then local fontheader={ version=readfixed(f), revision=readfixed(f), @@ -8357,178 +11746,155 @@ readers.head=function(f,fontdata) 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 + local tableoffset=gotodatatable(f,fontdata,"hhea",specification.details) + if tableoffset then + 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 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={ + local tableoffset=gotodatatable(f,fontdata,"vhea",specification.details) + if tableoffset then + fontdata.verticalheader={ + version=readfixed(f), + 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 +readers.maxp=function(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"maxp",specification.details) + if tableoffset then + local version=readfixed(f) + local nofglyphs=readushort(f) + fontdata.nofglyphs=nofglyphs + if version==0.5 then + fontdata.maximumprofile={ + version=version, + nofglyphs=nofglyphs, + } + elseif version==1.0 then + fontdata.maximumprofile={ 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), + 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), } else - fontdata.verticalheader={ - nofmetrics=0, + fontdata.maximumprofile={ + version=version, + nofglyphs=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 + local tableoffset=gotodatatable(f,fontdata,"hmtx",specification.glyphs) + if tableoffset then + 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 - for i=nofmetrics,nofglyphs-1 do - local glyph=glyphs[i] - 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 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 + local tableoffset=gotodatatable(f,fontdata,"vmtx",specification.glyphs) + if tableoffset then + 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 - for i=nofmetrics,nofglyphs-1 do - local glyph=glyphs[i] - 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 readers.vorg=function(f,fontdata,specification) - if specification.glyphs then - local datatable=fontdata.tables.vorg - if datatable then - report("todo: %s","vorg") - end - end + reportskippedtable(f,fontdata,"vorg",specification.glyphs) end readers.post=function(f,fontdata,specification) - local datatable=fontdata.tables.post - if datatable then - setposition(f,datatable.offset) + local tableoffset=gotodatatable(f,fontdata,"post",true) + if tableoffset then local version=readfixed(f) fontdata.postscript={ version=version, @@ -8585,9 +11951,7 @@ readers.post=function(f,fontdata,specification) end end readers.cff=function(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("cff") - end + reportskippedtable(f,fontdata,"cff",specification.glyphs) end local formatreaders={} local duplicatestoo=true @@ -8599,11 +11963,13 @@ local sequence={ { 0,0,6 }, { 3,0,6 }, { 0,5,14 }, +{ 0,4,12 }, { 3,10,13 }, } local supported={} for i=1,#sequence do - local sp,se,sf=unpack(sequence[i]) + local si=sequence[i] + local sp,se,sf=si[1],si[2],si[3] local p=supported[sp] if not p then p={} @@ -8622,32 +11988,17 @@ formatreaders[4]=function(f,fontdata,offset) 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 endchars=readcardinaltable(f,nofsegments,ushort) 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 + local startchars=readcardinaltable(f,nofsegments,ushort) + local deltas=readcardinaltable(f,nofsegments,ushort) + local offsets=readcardinaltable(f,nofsegments,ushort) + local size=(length-2*2-5*2-4*2*nofsegments)/2 + local indices=readcardinaltable(f,size-1,ushort) for segment=1,nofsegments do local startchar=startchars[segment] local endchar=endchars[segment] @@ -8657,7 +12008,7 @@ formatreaders[4]=function(f,fontdata,offset) elseif startchar==0xFFFF and offset==0 then elseif offset==0xFFFF then elseif offset==0 then - if trace_cmap then + if trace_cmap_detail 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 @@ -8689,7 +12040,7 @@ formatreaders[4]=function(f,fontdata,offset) end else local shift=(segment-nofsegments+offset/2)-startchar - if trace_cmap then + if trace_cmap_detail 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 @@ -8737,7 +12088,7 @@ formatreaders[6]=function(f,fontdata,offset) local count=readushort(f) local stop=start+count-1 local nofdone=0 - if trace_cmap then + if trace_cmap_detail then report("format 6 from %C to %C",2,start,stop) end for unicode=start,stop do @@ -8770,7 +12121,7 @@ formatreaders[12]=function(f,fontdata,offset) local first=readulong(f) local last=readulong(f) local index=readulong(f) - if trace_cmap then + if trace_cmap_detail then report("format 12 from %C to %C starts at index %i",first,last,index) end for unicode=first,last do @@ -8809,7 +12160,7 @@ formatreaders[13]=function(f,fontdata,offset) local last=readulong(f) local index=readulong(f) if first0 then - ok=true - end - end - if not ok then - report("no useable unicode cmap found") + end + local ok=false + for i=1,#sequence do + local si=sequence[i] + local sp,se,sf=si[1],si[2],si[3] + if checkcmap(f,fontdata,records,sp,se,sf)>0 then + ok=true end - fontdata.cidmaps={ - version=version, - noftables=noftables, - records=records, - } - else - fontdata.cidmaps={} 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 function readers.loca(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("loca") - end + reportskippedtable(f,fontdata,"loca",specification.glyphs) end function readers.glyf(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("glyf") - end + reportskippedtable(f,fontdata,"glyf",specification.glyphs) end function readers.colr(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("colr") - end + reportskippedtable(f,fontdata,"colr",specification.glyphs) end function readers.cpal(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("cpal") - end + reportskippedtable(f,fontdata,"cpal",specification.glyphs) end function readers.svg(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("svg") - end + reportskippedtable(f,fontdata,"svg",specification.glyphs) +end +function readers.sbix(f,fontdata,specification) + reportskippedtable(f,fontdata,"sbix",specification.glyphs) +end +function readers.cbdt(f,fontdata,specification) + reportskippedtable(f,fontdata,"cbdt",specification.glyphs) +end +function readers.cblc(f,fontdata,specification) + reportskippedtable(f,fontdata,"cblc",specification.glyphs) +end +function readers.ebdt(f,fontdata,specification) + reportskippedtable(f,fontdata,"ebdt",specification.glyphs) +end +function readers.ebsc(f,fontdata,specification) + reportskippedtable(f,fontdata,"ebsc",specification.glyphs) +end +function readers.eblc(f,fontdata,specification) + reportskippedtable(f,fontdata,"eblc",specification.glyphs) end function readers.kern(f,fontdata,specification) - if specification.kerns then - local datatable=fontdata.tables.kern - if datatable then - setposition(f,datatable.offset) + local tableoffset=gotodatatable(f,fontdata,"kern",specification.kerns) + if tableoffset then + local version=readushort(f) + local noftables=readushort(f) + for i=1,noftables do 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 + local length=readushort(f) + local coverage=readushort(f) + local format=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 - elseif format==2 then - report("todo: kern classes") - else - report("todo: kerns") end + elseif format==2 then + report("todo: kern classes") + else + report("todo: kerns") end end end end function readers.gdef(f,fontdata,specification) - if specification.details then - reportskippedtable("gdef") - end + reportskippedtable(f,fontdata,"gdef",specification.details) end function readers.gsub(f,fontdata,specification) - if specification.details then - reportskippedtable("gsub") - end + reportskippedtable(f,fontdata,"gsub",specification.details) end function readers.gpos(f,fontdata,specification) - if specification.details then - reportskippedtable("gpos") - end + reportskippedtable(f,fontdata,"gpos",specification.details) end function readers.math(f,fontdata,specification) - if specification.glyphs then - reportskippedtable("math") - end + reportskippedtable(f,fontdata,"math",specification.details) end -local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo) +local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo,instancenames) local fontdata=sub and maindata.subfonts and maindata.subfonts[sub] or maindata local names=fontdata.names local info=nil @@ -9106,6 +12461,25 @@ local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo) if not familyname then familyname=family end if not subfamilyname then subfamilyname=subfamily end end + if platformnames then + platformnames=fontdata.platformnames + end + if instancenames then + local variabledata=fontdata.variabledata + if variabledata then + local instances=variabledata and variabledata.instances + if instances then + instancenames={} + for i=1,#instances do + instancenames[i]=lower(stripstring(instances[i].subfamily)) + end + else + instancenames=nil + end + else + instancenames=nil + end + end info={ subfontindex=fontdata.subfontindex or sub or 0, version=getname(fontdata,"version"), @@ -9133,7 +12507,8 @@ local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo) capheight=metrics.capheight, ascender=metrics.typoascender, descender=metrics.typodescender, - platformnames=platformnames and fontdata.platformnames or nil, + platformnames=platformnames or nil, + instancenames=instancenames or nil, } if metricstoo then local keys={ @@ -9187,6 +12562,7 @@ local function loadtables(f,specification,offset) entryselector=readushort(f), rangeshift=readushort(f), tables=tables, + foundtables=false, } for i=1,fontdata.noftables do local tag=lower(stripstring(readstring(f,4))) @@ -9202,7 +12578,8 @@ local function loadtables(f,specification,offset) length=length, } end - if tables.cff then + fontdata.foundtables=sortedkeys(tables) + if tables.cff or tables.cff2 then fontdata.format="opentype" else fontdata.format="truetype" @@ -9220,12 +12597,25 @@ local function prepareglyps(fontdata) fontdata.glyphs=glyphs fontdata.mapping={} end +local function readtable(tag,f,fontdata,specification,...) + local reader=readers[tag] + if reader then + reader(f,fontdata,specification,...) + end +end +local variablefonts_supported=(context and true) or (logs and logs.application and true) or false local function readdata(f,offset,specification) local fontdata=loadtables(f,specification,offset) if specification.glyphs then prepareglyps(fontdata) end - readers["name"](f,fontdata,specification) + if not variablefonts_supported then + specification.instance=nil + specification.variable=nil + specification.factors=nil + end + fontdata.temporary={} + readtable("name",f,fontdata,specification) local askedname=specification.askedname if askedname then local fullname=getname(fontdata,"fullname") or "" @@ -9235,27 +12625,95 @@ local function readdata(f,offset,specification) 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) + readtable("stat",f,fontdata,specification) + readtable("avar",f,fontdata,specification) + readtable("fvar",f,fontdata,specification) + if variablefonts_supported then + local variabledata=fontdata.variabledata + if variabledata then + local instances=variabledata.instances + local axis=variabledata.axis + if axis and (not instances or #instances==0) then + instances={} + variabledata.instances=instances + local function add(n,subfamily,value) + local values={} + for i=1,#axis do + local a=axis[i] + values[i]={ + axis=a.tag, + value=i==n and value or a.default, + } + end + instances[#instances+1]={ + subfamily=subfamily, + values=values, + } + end + for i=1,#axis do + local a=axis[i] + local tag=a.tag + add(i,"default"..tag,a.default) + add(i,"minimum"..tag,a.minimum) + add(i,"maximum"..tag,a.maximum) + end + end + end + if not specification.factors then + local instance=specification.instance + if type(instance)=="string" then + local factors=helpers.getfactors(fontdata,instance) + if factors then + specification.factors=factors + fontdata.factors=factors + fontdata.instance=instance + report("user instance: %s, factors: % t",instance,factors) + else + report("user instance: %s, bad factors",instance) + end + end + end + if not fontdata.factors then + if fontdata.variabledata then + local factors=helpers.getfactors(fontdata,true) + if factors then + specification.factors=factors + fontdata.factors=factors + end + else + end + end + end + readtable("os/2",f,fontdata,specification) + readtable("head",f,fontdata,specification) + readtable("maxp",f,fontdata,specification) + readtable("hhea",f,fontdata,specification) + readtable("vhea",f,fontdata,specification) + readtable("hmtx",f,fontdata,specification) + readtable("vmtx",f,fontdata,specification) + readtable("vorg",f,fontdata,specification) + readtable("post",f,fontdata,specification) + readtable("mvar",f,fontdata,specification) + readtable("hvar",f,fontdata,specification) + readtable("vvar",f,fontdata,specification) + readtable("gdef",f,fontdata,specification) + readtable("cff",f,fontdata,specification) + readtable("cff2",f,fontdata,specification) + readtable("cmap",f,fontdata,specification) + readtable("loca",f,fontdata,specification) + readtable("glyf",f,fontdata,specification) + readtable("colr",f,fontdata,specification) + readtable("cpal",f,fontdata,specification) + readtable("svg",f,fontdata,specification) + readtable("sbix",f,fontdata,specification) + readtable("cbdt",f,fontdata,specification) + readtable("cblc",f,fontdata,specification) + readtable("ebdt",f,fontdata,specification) + readtable("eblc",f,fontdata,specification) + readtable("kern",f,fontdata,specification) + readtable("gsub",f,fontdata,specification) + readtable("gpos",f,fontdata,specification) + readtable("math",f,fontdata,specification) fontdata.locations=nil fontdata.tables=nil fontdata.cidmaps=nil @@ -9282,12 +12740,9 @@ local function loadfontdata(specification) 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 + local offsets=readcardinaltable(f,nofsubfonts,ulong) if subfont then if subfont>=1 and subfont<=nofsubfonts then fontdata=readdata(f,offsets[subfont],specification) @@ -9332,7 +12787,7 @@ local function loadfontdata(specification) return fontdata or {} end end -local function loadfont(specification,n) +local function loadfont(specification,n,instance) if type(specification)=="string" then specification={ filename=specification, @@ -9341,10 +12796,12 @@ local function loadfont(specification,n) glyphs=true, shapes=true, kerns=true, + variable=true, globalkerns=true, lookups=true, subfont=n or true, tounicode=false, + instance=instance } end if specification.shapes or specification.lookups or specification.kerns then @@ -9359,6 +12816,10 @@ local function loadfont(specification,n) if specification.platformnames then specification.platformnames=true end + if specification.instance or instance then + specification.variable=true + specification.instance=specification.instance or instance + end local function message(str) report("fatal error in file %a: %s\n%s",specification.filename,str,debug.traceback()) end @@ -9367,11 +12828,14 @@ local function loadfont(specification,n) return result end end -function readers.loadshapes(filename,n) +function readers.loadshapes(filename,n,instance,streams) local fontdata=loadfont { filename=filename, shapes=true, + streams=streams, + variable=true, subfont=n, + instance=instance, } if fontdata then for k,v in next,fontdata.glyphs do @@ -9392,13 +12856,15 @@ function readers.loadshapes(filename,n) units=0, } end -function readers.loadfont(filename,n) +function readers.loadfont(filename,n,instance) local fontdata=loadfont { filename=filename, glyphs=true, shapes=false, lookups=true, + variable=true, subfont=n, + instance=instance, } if fontdata then return { @@ -9410,11 +12876,13 @@ function readers.loadfont(filename,n) descriptions=fontdata.descriptions, format=fontdata.format, goodies={}, - metadata=getinfo(fontdata,n,false,false,true), + metadata=getinfo(fontdata,n,false,false,true,true), properties={ hasitalics=fontdata.hasitalics or false, maxcolorclass=fontdata.maxcolorclass, hascolor=fontdata.hascolor or false, + instance=fontdata.instance, + factors=fontdata.factors, }, resources={ filename=filename, @@ -9432,6 +12900,9 @@ function readers.loadfont(filename,n) mathconstants=fontdata.mathconstants, colorpalettes=fontdata.colorpalettes, svgshapes=fontdata.svgshapes, + sbixshapes=fontdata.sbixshapes, + variabledata=fontdata.variabledata, + foundtables=fontdata.foundtables, }, } end @@ -9440,6 +12911,7 @@ function readers.getinfo(filename,specification) local subfont=nil local platformnames=false local rawfamilynames=false + local instancenames=true if type(specification)=="table" then subfont=tonumber(specification.subfont) platformnames=specification.platformnames @@ -9451,19 +12923,20 @@ function readers.getinfo(filename,specification) filename=filename, details=true, platformnames=platformnames, + instancenames=true, } if fontdata then local subfonts=fontdata.subfonts if not subfonts then - return getinfo(fontdata,nil,platformnames,rawfamilynames) + return getinfo(fontdata,nil,platformnames,rawfamilynames,false,instancenames) elseif not subfont then local info={} for i=1,#subfonts do - info[i]=getinfo(fontdata,i,platformnames,rawfamilynames) + info[i]=getinfo(fontdata,i,platformnames,rawfamilynames,false,instancenames) end return info elseif subfont>=1 and subfont<=#subfonts then - return getinfo(fontdata,subfont,platformnames,rawfamilynames) + return getinfo(fontdata,subfont,platformnames,rawfamilynames,false,instancenames) else return { filename=filename, @@ -9514,54 +12987,1261 @@ end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules={} end modules ['font-cff']={ +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 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", +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 -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ["font-ott"]={ + 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,tostring,rawget,rawset=type,next,tonumber,tostring,rawget,rawset +local gsub,lower,format,match,gmatch,find=string.gsub,string.lower,string.format,string.match,string.gmatch,string.find +local sequenced=table.sequenced +local is_boolean=string.is_boolean +local setmetatableindex=table.setmetatableindex +local setmetatablenewindex=table.setmetatablenewindex +local allocate=utilities.storage.allocate +local fonts=fonts +local otf=fonts.handlers.otf +local otffeatures=otf.features +local tables=otf.tables or {} +otf.tables=tables +local statistics=otf.statistics or {} +otf.statistics=statistics +local scripts=allocate { + ["arab"]="arabic", + ["armi"]="imperial aramaic", + ["armn"]="armenian", + ["avst"]="avestan", + ["bali"]="balinese", + ["bamu"]="bamum", + ["batk"]="batak", + ["beng"]="bengali", + ["bng2"]="bengali variant 2", + ["bopo"]="bopomofo", + ["brah"]="brahmi", + ["brai"]="braille", + ["bugi"]="buginese", + ["buhd"]="buhid", + ["byzm"]="byzantine music", + ["cakm"]="chakma", + ["cans"]="canadian syllabics", + ["cari"]="carian", + ["cham"]="cham", + ["cher"]="cherokee", + ["copt"]="coptic", + ["cprt"]="cypriot syllabary", + ["cyrl"]="cyrillic", + ["deva"]="devanagari", + ["dev2"]="devanagari variant 2", + ["dsrt"]="deseret", + ["egyp"]="egyptian heiroglyphs", + ["ethi"]="ethiopic", + ["geor"]="georgian", + ["glag"]="glagolitic", + ["goth"]="gothic", + ["grek"]="greek", + ["gujr"]="gujarati", + ["gjr2"]="gujarati variant 2", + ["guru"]="gurmukhi", + ["gur2"]="gurmukhi variant 2", + ["hang"]="hangul", + ["hani"]="cjk ideographic", + ["hano"]="hanunoo", + ["hebr"]="hebrew", + ["ital"]="old italic", + ["jamo"]="hangul jamo", + ["java"]="javanese", + ["kali"]="kayah li", + ["kana"]="hiragana and katakana", + ["khar"]="kharosthi", + ["khmr"]="khmer", + ["knda"]="kannada", + ["knd2"]="kannada variant 2", + ["kthi"]="kaithi", + ["lana"]="tai tham", + ["lao" ]="lao", + ["latn"]="latin", + ["lepc"]="lepcha", + ["limb"]="limbu", + ["linb"]="linear b", + ["lisu"]="lisu", + ["lyci"]="lycian", + ["lydi"]="lydian", + ["mand"]="mandaic and mandaean", + ["math"]="mathematical alphanumeric symbols", + ["merc"]="meroitic cursive", + ["mero"]="meroitic hieroglyphs", + ["mlym"]="malayalam", + ["mlm2"]="malayalam variant 2", + ["mong"]="mongolian", + ["mtei"]="meitei Mayek", + ["musc"]="musical symbols", + ["mym2"]="myanmar variant 2", + ["mymr"]="myanmar", + ["nko" ]='n"ko', + ["ogam"]="ogham", + ["olck"]="ol chiki", + ["orkh"]="old turkic and orkhon runic", + ["orya"]="oriya", + ["ory2"]="odia variant 2", + ["osma"]="osmanya", + ["phag"]="phags-pa", + ["phli"]="inscriptional pahlavi", + ["phnx"]="phoenician", + ["prti"]="inscriptional parthian", + ["rjng"]="rejang", + ["runr"]="runic", + ["samr"]="samaritan", + ["sarb"]="old south arabian", + ["saur"]="saurashtra", + ["shaw"]="shavian", + ["shrd"]="sharada", + ["sinh"]="sinhala", + ["sora"]="sora sompeng", + ["sund"]="sundanese", + ["sylo"]="syloti nagri", + ["syrc"]="syriac", + ["tagb"]="tagbanwa", + ["takr"]="takri", + ["tale"]="tai le", + ["talu"]="tai lu", + ["taml"]="tamil", + ["tavt"]="tai viet", + ["telu"]="telugu", + ["tel2"]="telugu variant 2", + ["tfng"]="tifinagh", + ["tglg"]="tagalog", + ["thaa"]="thaana", + ["thai"]="thai", + ["tibt"]="tibetan", + ["tml2"]="tamil variant 2", + ["ugar"]="ugaritic cuneiform", + ["vai" ]="vai", + ["xpeo"]="old persian cuneiform", + ["xsux"]="sumero-akkadian cuneiform", + ["yi" ]="yi", +} +local languages=allocate { + ["aba" ]="abaza", + ["abk" ]="abkhazian", + ["ach" ]="acholi", + ["acr" ]="achi", + ["ady" ]="adyghe", + ["afk" ]="afrikaans", + ["afr" ]="afar", + ["agw" ]="agaw", + ["aio" ]="aiton", + ["aka" ]="akan", + ["als" ]="alsatian", + ["alt" ]="altai", + ["amh" ]="amharic", + ["ang" ]="anglo-saxon", + ["apph"]="phonetic transcription—americanist conventions", + ["ara" ]="arabic", + ["arg" ]="aragonese", + ["ari" ]="aari", + ["ark" ]="rakhine", + ["asm" ]="assamese", + ["ast" ]="asturian", + ["ath" ]="athapaskan", + ["avr" ]="avar", + ["awa" ]="awadhi", + ["aym" ]="aymara", + ["azb" ]="torki", + ["aze" ]="azerbaijani", + ["bad" ]="badaga", + ["bad0"]="banda", + ["bag" ]="baghelkhandi", + ["bal" ]="balkar", + ["ban" ]="balinese", + ["bar" ]="bavarian", + ["bau" ]="baulé", + ["bbc" ]="batak toba", + ["bbr" ]="berber", + ["bch" ]="bench", + ["bcr" ]="bible cree", + ["bdy" ]="bandjalang", + ["bel" ]="belarussian", + ["bem" ]="bemba", + ["ben" ]="bengali", + ["bgc" ]="haryanvi", + ["bgq" ]="bagri", + ["bgr" ]="bulgarian", + ["bhi" ]="bhili", + ["bho" ]="bhojpuri", + ["bik" ]="bikol", + ["bil" ]="bilen", + ["bis" ]="bislama", + ["bjj" ]="kanauji", + ["bkf" ]="blackfoot", + ["bli" ]="baluchi", + ["blk" ]="pa'o karen", + ["bln" ]="balante", + ["blt" ]="balti", + ["bmb" ]="bambara (bamanankan)", + ["bml" ]="bamileke", + ["bos" ]="bosnian", + ["bpy" ]="bishnupriya manipuri", + ["bre" ]="breton", + ["brh" ]="brahui", + ["bri" ]="braj bhasha", + ["brm" ]="burmese", + ["brx" ]="bodo", + ["bsh" ]="bashkir", + ["bti" ]="beti", + ["bts" ]="batak simalungun", + ["bug" ]="bugis", + ["cak" ]="kaqchikel", + ["cat" ]="catalan", + ["cbk" ]="zamboanga chavacano", + ["ceb" ]="cebuano", + ["cgg" ]="chiga", + ["cha" ]="chamorro", + ["che" ]="chechen", + ["chg" ]="chaha gurage", + ["chh" ]="chattisgarhi", + ["chi" ]="chichewa (chewa, nyanja)", + ["chk" ]="chukchi", + ["chk0"]="chuukese", + ["cho" ]="choctaw", + ["chp" ]="chipewyan", + ["chr" ]="cherokee", + ["chu" ]="chuvash", + ["chy" ]="cheyenne", + ["cmr" ]="comorian", + ["cop" ]="coptic", + ["cor" ]="cornish", + ["cos" ]="corsican", + ["cpp" ]="creoles", + ["cre" ]="cree", + ["crr" ]="carrier", + ["crt" ]="crimean tatar", + ["csb" ]="kashubian", + ["csl" ]="church slavonic", + ["csy" ]="czech", + ["ctg" ]="chittagonian", + ["cuk" ]="san blas kuna", + ["dan" ]="danish", + ["dar" ]="dargwa", + ["dax" ]="dayi", + ["dcr" ]="woods cree", + ["deu" ]="german", + ["dgo" ]="dogri", + ["dgr" ]="dogri", + ["dhg" ]="dhangu", + ["dhv" ]="divehi (dhivehi, maldivian)", + ["diq" ]="dimli", + ["div" ]="divehi (dhivehi, maldivian)", + ["djr" ]="zarma", + ["djr0"]="djambarrpuyngu", + ["dng" ]="dangme", + ["dnj" ]="dan", + ["dnk" ]="dinka", + ["dri" ]="dari", + ["duj" ]="dhuwal", + ["dun" ]="dungan", + ["dzn" ]="dzongkha", + ["ebi" ]="ebira", + ["ecr" ]="eastern cree", + ["edo" ]="edo", + ["efi" ]="efik", + ["ell" ]="greek", + ["emk" ]="eastern maninkakan", + ["eng" ]="english", + ["erz" ]="erzya", + ["esp" ]="spanish", + ["esu" ]="central yupik", + ["eti" ]="estonian", + ["euq" ]="basque", + ["evk" ]="evenki", + ["evn" ]="even", + ["ewe" ]="ewe", + ["fan" ]="french antillean", + ["fan0"]=" fang", + ["far" ]="persian", + ["fat" ]="fanti", + ["fin" ]="finnish", + ["fji" ]="fijian", + ["fle" ]="dutch (flemish)", + ["fne" ]="forest nenets", + ["fon" ]="fon", + ["fos" ]="faroese", + ["fra" ]="french", + ["frc" ]="cajun french", + ["fri" ]="frisian", + ["frl" ]="friulian", + ["frp" ]="arpitan", + ["fta" ]="futa", + ["ful" ]="fulah", + ["fuv" ]="nigerian fulfulde", + ["gad" ]="ga", + ["gae" ]="scottish gaelic (gaelic)", + ["gag" ]="gagauz", + ["gal" ]="galician", + ["gar" ]="garshuni", + ["gaw" ]="garhwali", + ["gez" ]="ge'ez", + ["gih" ]="githabul", + ["gil" ]="gilyak", + ["gil0"]="kiribati (gilbertese)", + ["gkp" ]="kpelle (guinea)", + ["glk" ]="gilaki", + ["gmz" ]="gumuz", + ["gnn" ]="gumatj", + ["gog" ]="gogo", + ["gon" ]="gondi", + ["grn" ]="greenlandic", + ["gro" ]="garo", + ["gua" ]="guarani", + ["guc" ]="wayuu", + ["guf" ]="gupapuyngu", + ["guj" ]="gujarati", + ["guz" ]="gusii", + ["hai" ]="haitian (haitian creole)", + ["hal" ]="halam", + ["har" ]="harauti", + ["hau" ]="hausa", + ["haw" ]="hawaiian", + ["hay" ]="haya", + ["haz" ]="hazaragi", + ["hbn" ]="hammer-banna", + ["her" ]="herero", + ["hil" ]="hiligaynon", + ["hin" ]="hindi", + ["hma" ]="high mari", + ["hmn" ]="hmong", + ["hmo" ]="hiri motu", + ["hnd" ]="hindko", + ["ho" ]="ho", + ["hri" ]="harari", + ["hrv" ]="croatian", + ["hun" ]="hungarian", + ["hye" ]="armenian", + ["hye0"]="armenian east", + ["iba" ]="iban", + ["ibb" ]="ibibio", + ["ibo" ]="igbo", + ["ido" ]="ido", + ["ijo" ]="ijo languages", + ["ile" ]="interlingue", + ["ilo" ]="ilokano", + ["ina" ]="interlingua", + ["ind" ]="indonesian", + ["ing" ]="ingush", + ["inu" ]="inuktitut", + ["ipk" ]="inupiat", + ["ipph"]="phonetic transcription—ipa conventions", + ["iri" ]="irish", + ["irt" ]="irish traditional", + ["isl" ]="icelandic", + ["ism" ]="inari sami", + ["ita" ]="italian", + ["iwr" ]="hebrew", + ["jam" ]="jamaican creole", + ["jan" ]="japanese", + ["jav" ]="javanese", + ["jbo" ]="lojban", + ["jii" ]="yiddish", + ["jud" ]="ladino", + ["jul" ]="jula", + ["kab" ]="kabardian", + ["kab0"]="kabyle", + ["kac" ]="kachchi", + ["kal" ]="kalenjin", + ["kan" ]="kannada", + ["kar" ]="karachay", + ["kat" ]="georgian", + ["kaz" ]="kazakh", + ["kde" ]="makonde", + ["kea" ]="kabuverdianu (crioulo)", + ["keb" ]="kebena", + ["kek" ]="kekchi", + ["kge" ]="khutsuri georgian", + ["kha" ]="khakass", + ["khk" ]="khanty-kazim", + ["khm" ]="khmer", + ["khs" ]="khanty-shurishkar", + ["kht" ]="khamti shan", + ["khv" ]="khanty-vakhi", + ["khw" ]="khowar", + ["kik" ]="kikuyu (gikuyu)", + ["kir" ]="kirghiz (kyrgyz)", + ["kis" ]="kisii", + ["kiu" ]="kirmanjki", + ["kjd" ]="southern kiwai", + ["kjp" ]="eastern pwo karen", + ["kkn" ]="kokni", + ["klm" ]="kalmyk", + ["kmb" ]="kamba", + ["kmn" ]="kumaoni", + ["kmo" ]="komo", + ["kms" ]="komso", + ["knr" ]="kanuri", + ["kod" ]="kodagu", + ["koh" ]="korean old hangul", + ["kok" ]="konkani", + ["kom" ]="komi", + ["kon" ]="kikongo", + ["kon0"]="kongo", + ["kop" ]="komi-permyak", + ["kor" ]="korean", + ["kos" ]="kosraean", + ["koz" ]="komi-zyrian", + ["kpl" ]="kpelle", + ["kri" ]="krio", + ["krk" ]="karakalpak", + ["krl" ]="karelian", + ["krm" ]="karaim", + ["krn" ]="karen", + ["krt" ]="koorete", + ["ksh" ]="kashmiri", + ["ksh0"]="ripuarian", + ["ksi" ]="khasi", + ["ksm" ]="kildin sami", + ["ksw" ]="s’gaw karen", + ["kua" ]="kuanyama", + ["kui" ]="kui", + ["kul" ]="kulvi", + ["kum" ]="kumyk", + ["kur" ]="kurdish", + ["kuu" ]="kurukh", + ["kuy" ]="kuy", + ["kyk" ]="koryak", + ["kyu" ]="western kayah", + ["lad" ]="ladin", + ["lah" ]="lahuli", + ["lak" ]="lak", + ["lam" ]="lambani", + ["lao" ]="lao", + ["lat" ]="latin", + ["laz" ]="laz", + ["lcr" ]="l-cree", + ["ldk" ]="ladakhi", + ["lez" ]="lezgi", + ["lij" ]="ligurian", + ["lim" ]="limburgish", + ["lin" ]="lingala", + ["lis" ]="lisu", + ["ljp" ]="lampung", + ["lki" ]="laki", + ["lma" ]="low mari", + ["lmb" ]="limbu", + ["lmo" ]="lombard", + ["lmw" ]="lomwe", + ["lom" ]="loma", + ["lrc" ]="luri", + ["lsb" ]="lower sorbian", + ["lsm" ]="lule sami", + ["lth" ]="lithuanian", + ["ltz" ]="luxembourgish", + ["lua" ]="luba-lulua", + ["lub" ]="luba-katanga", + ["lug" ]="ganda", + ["luh" ]="luyia", + ["luo" ]="luo", + ["lvi" ]="latvian", + ["mad" ]="madura", + ["mag" ]="magahi", + ["mah" ]="marshallese", + ["maj" ]="majang", + ["mak" ]="makhuwa", + ["mal" ]="malayalam reformed", + ["mam" ]="mam", + ["man" ]="mansi", + ["map" ]="mapudungun", + ["mar" ]="marathi", + ["maw" ]="marwari", + ["mbn" ]="mbundu", + ["mch" ]="manchu", + ["mcr" ]="moose cree", + ["mde" ]="mende", + ["mdr" ]="mandar", + ["men" ]="me'en", + ["mer" ]="meru", + ["mfe" ]="morisyen", + ["min" ]="minangkabau", + ["miz" ]="mizo", + ["mkd" ]="macedonian", + ["mkr" ]="makasar", + ["mkw" ]="kituba", + ["mle" ]="male", + ["mlg" ]="malagasy", + ["mln" ]="malinke", + ["mly" ]="malay", + ["mnd" ]="mandinka", + ["mng" ]="mongolian", + ["mni" ]="manipuri", + ["mnk" ]="maninka", + ["mnx" ]="manx", + ["moh" ]="mohawk", + ["mok" ]="moksha", + ["mol" ]="moldavian", + ["mon" ]="mon", + ["mor" ]="moroccan", + ["mos" ]="mossi", + ["mri" ]="maori", + ["mth" ]="maithili", + ["mts" ]="maltese", + ["mun" ]="mundari", + ["mus" ]="muscogee", + ["mwl" ]="mirandese", + ["mww" ]="hmong daw", + ["myn" ]="mayan", + ["mzn" ]="mazanderani", + ["nag" ]="naga-assamese", + ["nah" ]="nahuatl", + ["nan" ]="nanai", + ["nap" ]="neapolitan", + ["nas" ]="naskapi", + ["nau" ]="nauruan", + ["nav" ]="navajo", + ["ncr" ]="n-cree", + ["ndb" ]="ndebele", + ["ndc" ]="ndau", + ["ndg" ]="ndonga", + ["nds" ]="low saxon", + ["nep" ]="nepali", + ["new" ]="newari", + ["nga" ]="ngbaka", + ["ngr" ]="nagari", + ["nhc" ]="norway house cree", + ["nis" ]="nisi", + ["niu" ]="niuean", + ["nkl" ]="nyankole", + ["nko" ]="n'ko", + ["nld" ]="dutch", + ["noe" ]="nimadi", + ["nog" ]="nogai", + ["nor" ]="norwegian", + ["nov" ]="novial", + ["nsm" ]="northern sami", + ["nso" ]="sotho, northern", + ["nta" ]="northern tai", + ["nto" ]="esperanto", + ["nym" ]="nyamwezi", + ["nyn" ]="norwegian nynorsk", + ["oci" ]="occitan", + ["ocr" ]="oji-cree", + ["ojb" ]="ojibway", + ["ori" ]="odia", + ["oro" ]="oromo", + ["oss" ]="ossetian", + ["paa" ]="palestinian aramaic", + ["pag" ]="pangasinan", + ["pal" ]="pali", + ["pam" ]="pampangan", + ["pan" ]="punjabi", + ["pap" ]="palpa", + ["pap0"]="papiamentu", + ["pas" ]="pashto", + ["pau" ]="palauan", + ["pcc" ]="bouyei", + ["pcd" ]="picard", + ["pdc" ]="pennsylvania german", + ["pgr" ]="polytonic greek", + ["phk" ]="phake", + ["pih" ]="norfolk", + ["pil" ]="filipino", + ["plg" ]="palaung", + ["plk" ]="polish", + ["pms" ]="piemontese", + ["pnb" ]="western panjabi", + ["poh" ]="pocomchi", + ["pon" ]="pohnpeian", + ["pro" ]="provencal", + ["ptg" ]="portuguese", + ["pwo" ]="western pwo karen", + ["qin" ]="chin", + ["quc" ]="k’iche’", + ["quh" ]="quechua (bolivia)", + ["quz" ]="quechua", + ["qvi" ]="quechua (ecuador)", + ["qwh" ]="quechua (peru)", + ["raj" ]="rajasthani", + ["rar" ]="rarotongan", + ["rbu" ]="russian buriat", + ["rcr" ]="r-cree", + ["rej" ]="rejang", + ["ria" ]="riang", + ["rif" ]="tarifit", + ["rit" ]="ritarungo", + ["rkw" ]="arakwal", + ["rms" ]="romansh", + ["rmy" ]="vlax romani", + ["rom" ]="romanian", + ["roy" ]="romany", + ["rsy" ]="rusyn", + ["rtm" ]="rotuman", + ["rua" ]="kinyarwanda", + ["run" ]="rundi", + ["rup" ]="aromanian", + ["rus" ]="russian", + ["sad" ]="sadri", + ["san" ]="sanskrit", + ["sas" ]="sasak", + ["sat" ]="santali", + ["say" ]="sayisi", + ["scn" ]="sicilian", + ["sco" ]="scots", + ["sek" ]="sekota", + ["sel" ]="selkup", + ["sga" ]="old irish", + ["sgo" ]="sango", + ["sgs" ]="samogitian", + ["shi" ]="tachelhit", + ["shn" ]="shan", + ["sib" ]="sibe", + ["sid" ]="sidamo", + ["sig" ]="silte gurage", + ["sks" ]="skolt sami", + ["sky" ]="slovak", + ["sla" ]="slavey", + ["slv" ]="slovenian", + ["sml" ]="somali", + ["smo" ]="samoan", + ["sna" ]="sena", + ["sna0"]="shona", + ["snd" ]="sindhi", + ["snh" ]="sinhala (sinhalese)", + ["snk" ]="soninke", + ["sog" ]="sodo gurage", + ["sop" ]="songe", + ["sot" ]="sotho, southern", + ["sqi" ]="albanian", + ["srb" ]="serbian", + ["srd" ]="sardinian", + ["srk" ]="saraiki", + ["srr" ]="serer", + ["ssl" ]="south slavey", + ["ssm" ]="southern sami", + ["stq" ]="saterland frisian", + ["suk" ]="sukuma", + ["sun" ]="sundanese", + ["sur" ]="suri", + ["sva" ]="svan", + ["sve" ]="swedish", + ["swa" ]="swadaya aramaic", + ["swk" ]="swahili", + ["swz" ]="swati", + ["sxt" ]="sutu", + ["sxu" ]="upper saxon", + ["syl" ]="sylheti", + ["syr" ]="syriac", + ["szl" ]="silesian", + ["tab" ]="tabasaran", + ["taj" ]="tajiki", + ["tam" ]="tamil", + ["tat" ]="tatar", + ["tcr" ]="th-cree", + ["tdd" ]="dehong dai", + ["tel" ]="telugu", + ["tet" ]="tetum", + ["tgl" ]="tagalog", + ["tgn" ]="tongan", + ["tgr" ]="tigre", + ["tgy" ]="tigrinya", + ["tha" ]="thai", + ["tht" ]="tahitian", + ["tib" ]="tibetan", + ["tiv" ]="tiv", + ["tkm" ]="turkmen", + ["tmh" ]="tamashek", + ["tmn" ]="temne", + ["tna" ]="tswana", + ["tne" ]="tundra nenets", + ["tng" ]="tonga", + ["tod" ]="todo", + ["tod0"]="toma", + ["tpi" ]="tok pisin", + ["trk" ]="turkish", + ["tsg" ]="tsonga", + ["tua" ]="turoyo aramaic", + ["tul" ]="tulu", + ["tuv" ]="tuvin", + ["tvl" ]="tuvalu", + ["twi" ]="twi", + ["tyz" ]="tày", + ["tzm" ]="tamazight", + ["tzo" ]="tzotzil", + ["udm" ]="udmurt", + ["ukr" ]="ukrainian", + ["umb" ]="umbundu", + ["urd" ]="urdu", + ["usb" ]="upper sorbian", + ["uyg" ]="uyghur", + ["uzb" ]="uzbek", + ["vec" ]="venetian", + ["ven" ]="venda", + ["vit" ]="vietnamese", + ["vol" ]="volapük", + ["vro" ]="võro", + ["wa" ]="wa", + ["wag" ]="wagdi", + ["war" ]="waray-waray", + ["wcr" ]="west-cree", + ["wel" ]="welsh", + ["wlf" ]="wolof", + ["wln" ]="walloon", + ["xbd" ]="lü", + ["xhs" ]="xhosa", + ["xjb" ]="minjangbal", + ["xog" ]="soga", + ["xpe" ]="kpelle (liberia)", + ["yak" ]="sakha", + ["yao" ]="yao", + ["yap" ]="yapese", + ["yba" ]="yoruba", + ["ycr" ]="y-cree", + ["yic" ]="yi classic", + ["yim" ]="yi modern", + ["zea" ]="zealandic", + ["zgh" ]="standard morrocan tamazigh", + ["zha" ]="zhuang", + ["zhh" ]="chinese, hong kong sar", + ["zhp" ]="chinese phonetic", + ["zhs" ]="chinese simplified", + ["zht" ]="chinese traditional", + ["znd" ]="zande", + ["zul" ]="zulu", + ["zza" ]="zazaki", +} +local features=allocate { + ["aalt"]="access all alternates", + ["abvf"]="above-base forms", + ["abvm"]="above-base mark positioning", + ["abvs"]="above-base substitutions", + ["afrc"]="alternative fractions", + ["akhn"]="akhands", + ["blwf"]="below-base forms", + ["blwm"]="below-base mark positioning", + ["blws"]="below-base substitutions", + ["c2pc"]="petite capitals from capitals", + ["c2sc"]="small capitals from capitals", + ["calt"]="contextual alternates", + ["case"]="case-sensitive forms", + ["ccmp"]="glyph composition/decomposition", + ["cfar"]="conjunct form after ro", + ["cjct"]="conjunct forms", + ["clig"]="contextual ligatures", + ["cpct"]="centered cjk punctuation", + ["cpsp"]="capital spacing", + ["cswh"]="contextual swash", + ["curs"]="cursive positioning", + ["dflt"]="default processing", + ["dist"]="distances", + ["dlig"]="discretionary ligatures", + ["dnom"]="denominators", + ["dtls"]="dotless forms", + ["expt"]="expert forms", + ["falt"]="final glyph alternates", + ["fin2"]="terminal forms #2", + ["fin3"]="terminal forms #3", + ["fina"]="terminal forms", + ["flac"]="flattened accents over capitals", + ["frac"]="fractions", + ["fwid"]="full width", + ["half"]="half forms", + ["haln"]="halant forms", + ["halt"]="alternate half width", + ["hist"]="historical forms", + ["hkna"]="horizontal kana alternates", + ["hlig"]="historical ligatures", + ["hngl"]="hangul", + ["hojo"]="hojo kanji forms", + ["hwid"]="half width", + ["init"]="initial forms", + ["isol"]="isolated forms", + ["ital"]="italics", + ["jalt"]="justification alternatives", + ["jp04"]="jis2004 forms", + ["jp78"]="jis78 forms", + ["jp83"]="jis83 forms", + ["jp90"]="jis90 forms", + ["kern"]="kerning", + ["lfbd"]="left bounds", + ["liga"]="standard ligatures", + ["ljmo"]="leading jamo forms", + ["lnum"]="lining figures", + ["locl"]="localized forms", + ["ltra"]="left-to-right alternates", + ["ltrm"]="left-to-right mirrored forms", + ["mark"]="mark positioning", + ["med2"]="medial forms #2", + ["medi"]="medial forms", + ["mgrk"]="mathematical greek", + ["mkmk"]="mark to mark positioning", + ["mset"]="mark positioning via substitution", + ["nalt"]="alternate annotation forms", + ["nlck"]="nlc kanji forms", + ["nukt"]="nukta forms", + ["numr"]="numerators", + ["onum"]="old style figures", + ["opbd"]="optical bounds", + ["ordn"]="ordinals", + ["ornm"]="ornaments", + ["palt"]="proportional alternate width", + ["pcap"]="petite capitals", + ["pkna"]="proportional kana", + ["pnum"]="proportional figures", + ["pref"]="pre-base forms", + ["pres"]="pre-base substitutions", + ["pstf"]="post-base forms", + ["psts"]="post-base substitutions", + ["pwid"]="proportional widths", + ["qwid"]="quarter widths", + ["rand"]="randomize", + ["rclt"]="required contextual alternates", + ["rkrf"]="rakar forms", + ["rlig"]="required ligatures", + ["rphf"]="reph form", + ["rtbd"]="right bounds", + ["rtla"]="right-to-left alternates", + ["rtlm"]="right to left mirrored forms", + ["rvrn"]="required variation alternates", + ["ruby"]="ruby notation forms", + ["salt"]="stylistic alternates", + ["sinf"]="scientific inferiors", + ["size"]="optical size", + ["smcp"]="small capitals", + ["smpl"]="simplified forms", + ["ssty"]="script style", + ["stch"]="stretching glyph decomposition", + ["subs"]="subscript", + ["sups"]="superscript", + ["swsh"]="swash", + ["titl"]="titling", + ["tjmo"]="trailing jamo forms", + ["tnam"]="traditional name forms", + ["tnum"]="tabular figures", + ["trad"]="traditional forms", + ["twid"]="third widths", + ["unic"]="unicase", + ["valt"]="alternate vertical metrics", + ["vatu"]="vattu variants", + ["vert"]="vertical writing", + ["vhal"]="alternate vertical half metrics", + ["vjmo"]="vowel jamo forms", + ["vkna"]="vertical kana alternates", + ["vkrn"]="vertical kerning", + ["vpal"]="proportional alternate vertical metrics", + ["vrt2"]="vertical rotation", + ["zero"]="slashed zero", + ["trep"]="traditional tex replacements", + ["tlig"]="traditional tex ligatures", + ["ss.."]="stylistic set ..", + ["cv.."]="character variant ..", + ["js.."]="justification ..", + ["dv.."]="devanagari ..", + ["ml.."]="malayalam ..", +} +local baselines=allocate { + ["hang"]="hanging baseline", + ["icfb"]="ideographic character face bottom edge baseline", + ["icft"]="ideographic character face tope edige baseline", + ["ideo"]="ideographic em-box bottom edge baseline", + ["idtp"]="ideographic em-box top edge baseline", + ["math"]="mathematical centered baseline", + ["romn"]="roman baseline" +} +tables.scripts=scripts +tables.languages=languages +tables.features=features +tables.baselines=baselines +local acceptscripts=true directives.register("otf.acceptscripts",function(v) acceptscripts=v end) +local acceptlanguages=true directives.register("otf.acceptlanguages",function(v) acceptlanguages=v end) +local report_checks=logs.reporter("fonts","checks") +if otffeatures.features then + for k,v in next,otffeatures.features do + features[k]=v + end + otffeatures.features=features +end +local function swapped(h) + local r={} + for k,v in next,h do + r[gsub(v,"[^a-z0-9]","")]=k + end + return r +end +local verbosescripts=allocate(swapped(scripts )) +local verboselanguages=allocate(swapped(languages)) +local verbosefeatures=allocate(swapped(features )) +local verbosebaselines=allocate(swapped(baselines)) +local function resolve(t,k) + if k then + k=gsub(lower(k),"[^a-z0-9]","") + local v=rawget(t,k) + if v then + return v + end + end +end +setmetatableindex(verbosescripts,resolve) +setmetatableindex(verboselanguages,resolve) +setmetatableindex(verbosefeatures,resolve) +setmetatableindex(verbosebaselines,resolve) +setmetatableindex(scripts,function(t,k) + if k then + k=lower(k) + if k=="dflt" then + return k + end + local v=rawget(t,k) + if v then + return v + end + k=gsub(k," ","") + v=rawget(t,v) + if v then + return v + elseif acceptscripts then + report_checks("registering extra script %a",k) + rawset(t,k,k) + return k + end + end + return "dflt" +end) +setmetatableindex(languages,function(t,k) + if k then + k=lower(k) + if k=="dflt" then + return k + end + local v=rawget(t,k) + if v then + return v + end + k=gsub(k," ","") + v=rawget(t,v) + if v then + return v + elseif acceptlanguages then + report_checks("registering extra language %a",k) + rawset(t,k,k) + return k + end + end + return "dflt" +end) +if setmetatablenewindex then + setmetatablenewindex(languages,"ignore") + setmetatablenewindex(scripts,"ignore") + setmetatablenewindex(baselines,"ignore") +end +local function resolve(t,k) + if k then + k=lower(k) + local v=rawget(t,k) + if v then + return v + end + k=gsub(k," ","") + local v=rawget(t,k) + if v then + return v + end + local tag,dd=match(k,"(..)(%d+)") + if tag and dd then + local v=rawget(t,tag) + if v then + return v + else + local v=rawget(t,tag.."..") + if v then + return (gsub(v,"%.%.",tonumber(dd))) + end + end + end + end + return k +end +setmetatableindex(features,resolve) +local function assign(t,k,v) + if k and v then + v=lower(v) + rawset(t,k,v) + end +end +if setmetatablenewindex then + setmetatablenewindex(features,assign) +end +local checkers={ + rand=function(v) + return v==true and "random" or v + end +} +if not storage then + return +end +local usedfeatures=statistics.usedfeatures or {} +statistics.usedfeatures=usedfeatures +table.setmetatableindex(usedfeatures,function(t,k) if k then local v={} t[k]=v return v end end) +storage.register("fonts/otf/usedfeatures",usedfeatures,"fonts.handlers.otf.statistics.usedfeatures" ) +local normalizedaxis=otf.readers.helpers.normalizedaxis or function(s) return s end +function otffeatures.normalize(features) + if features then + local h={} + for key,value in next,features do + local k=lower(key) + if k=="language" then + local v=gsub(lower(value),"[^a-z0-9]","") + h.language=rawget(verboselanguages,v) or (languages[v] and v) or "dflt" + elseif k=="script" then + local v=gsub(lower(value),"[^a-z0-9]","") + h.script=rawget(verbosescripts,v) or (scripts[v] and v) or "dflt" + elseif k=="axis" then + h[k]=normalizedaxis(value) + if not callbacks.supported.glyph_stream_provider then + h.variableshapes=true + end + else + local uk=usedfeatures[key] + local uv=uk[value] + if uv then + else + uv=tonumber(value) + if uv then + elseif type(value)=="string" then + local b=is_boolean(value) + if type(b)=="nil" then + uv=lower(value) + else + uv=b + end + elseif type(value)=="table" then + uv=sequenced(t,",") + else + uv=value + end + if not rawget(features,k) then + k=rawget(verbosefeatures,k) or k + end + local c=checkers[k] + if c then + uv=c(uv) or vc + end + uk[value]=uv + end + h[k]=uv + end + end + return h + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +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,char,gmatch=string.byte,string.char,string.gmatch +local concat,remove=table.concat,table.remove +local floor,abs,round,ceil,min,max=math.floor,math.abs,math.round,math.ceil,math.min,math.max +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 bytetable=string.bytetable +local readers=fonts.handlers.otf.readers +local streamreader=readers.streamreader +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 readbytetable=streamreader.readbytetable +directives.register("fonts.streamreader",function() + streamreader=utilities.streams + readstring=streamreader.readstring + readbyte=streamreader.readcardinal1 + readushort=streamreader.readcardinal2 + readuint=streamreader.readcardinal3 + readulong=streamreader.readcardinal4 + setposition=streamreader.setposition + getposition=streamreader.getposition + readbytetable=streamreader.readbytetable +end) +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 startparsing +local stopparsing +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", @@ -9621,18 +14301,24 @@ local cffreaders={ } local function readheader(f) local offset=getposition(f) + local major=readbyte(f) local header={ offset=offset, - major=readbyte(f), + major=major, minor=readbyte(f), size=readbyte(f), - osize=readbyte(f), } + if major==1 then + header.dsize=readbyte(f) + elseif major==2 then + header.dsize=readushort(f) + else + end setposition(f,offset+header.size) return header end -local function readlengths(f) - local count=readushort(f) +local function readlengths(f,longcount) + local count=longcount and readulong(f) or readushort(f) if count==0 then return {} end @@ -9646,7 +14332,12 @@ local function readlengths(f) local previous=read(f) for i=1,count do local offset=read(f) - lengths[i]=offset-previous + local length=offset-previous + if length<0 then + report("bad offset: %i",length) + length=0 + end + lengths[i]=length previous=offset end return lengths @@ -9711,7 +14402,7 @@ do end+P("\16")/function() result.encoding=stack[top] top=0 - end+P("\17")/function() + end+P("\17")/function() result.charstrings=stack[top] top=0 end+P("\18")/function() @@ -9722,10 +14413,20 @@ do top=0 end+P("\19")/function() result.subroutines=stack[top] + top=0 end+P("\20")/function() result.defaultwidthx=stack[top] + top=0 end+P("\21")/function() result.nominalwidthx=stack[top] + top=0 + end ++P("\24")/function() + result.vstore=stack[top] + top=0 + end+P("\25")/function() + result.maxstack=stack[top] + top=0 end local p_double=P("\12")*( P("\00")/function() @@ -9749,7 +14450,7 @@ do end+P("\06")/function() result.charstringtype=stack[top] top=0 - end+P("\07")/function() + end+P("\07")/function() result.fontmatrix={ unpack(stack,1,6) } top=0 end+P("\08")/function() @@ -9787,10 +14488,10 @@ do end+P("\35")/function() result.cid.uidbase=stack[top] top=0 - end+P("\36")/function() + end+P("\36")/function() result.cid.fdarray=stack[top] top=0 - end+P("\37")/function() + end+P("\37")/function() result.cid.fdselect=stack[top] top=0 end+P("\38")/function() @@ -9855,12 +14556,12 @@ do 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) + parsedictionaries=function(data,dictionaries,what) stack={} strings=data.strings for i=1,#dictionaries do top=0 - result={ + result=what=="cff" and { monospaced=false, italicangle=0, underlineposition=-100, @@ -9878,6 +14579,12 @@ do fonttype=0, count=8720, } + } or { + charstringtype=2, + charset=0, + vstore=0, + cid={ + }, } lpegmatch(p_dictionary,dictionaries[i]) dictionaries[i]=result @@ -9919,6 +14626,9 @@ do local stems=0 local globalbias=0 local localbias=0 + local nominalwidth=0 + local defaultwidth=0 + local charset=false local globals=false local locals=false local depth=1 @@ -9929,6 +14639,12 @@ do local checked=false local keepcurve=false local version=2 + local regions=false + local nofregions=0 + local region=false + local factors=false + local axis=false + local vsindex=0 local function showstate(where) report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top) end @@ -9939,14 +14655,14 @@ do report("%w%-10s : %s",depth*2,where,tostring(value)) end end - local function moveto(x,y) + local function xymoveto() if keepcurve then r=r+1 result[r]={ x,y,"m" } end if checked then - if xxmax then xmax=x end - if yymax then ymax=y end + if x>xmax then xmax=x elseif xymax then ymax=y elseif yxmax then + xmax=x + elseif xymax then + ymax=y + elseif yxmax then xmax=x end - if yymax then ymax=y end + if x>xmax then xmax=x elseif xymax then ymax=y elseif yxmax then + xmax=x + elseif xymax then + ymax=y + elseif yxmax then xmax=x1 end - if y1ymax then ymax=y1 end + if x1>xmax then xmax=x1 elseif x1ymax then ymax=y1 elseif y1xmax then xmax=x2 end - if y2ymax then ymax=y2 end - if x3xmax then xmax=x3 end - if y3ymax then ymax=y3 end + if x2>xmax then xmax=x2 elseif x2ymax then ymax=y2 elseif y2xmax then xmax=x3 elseif x3ymax then ymax=y3 elseif y32 then - if not width then + if not width then + if top>2 then width=stack[1] if trace_charstrings then - showvalue("width",width) + showvalue("backtrack width",width) end + else + width=true end - elseif not width then - width=true end if trace_charstrings then showstate("rmoveto") @@ -10008,43 +14802,43 @@ do x=x+stack[top-1] y=y+stack[top] top=0 - moveto(x,y) + xymoveto() end local function hmoveto() - if top>1 then - if not width then + if not width then + if top>1 then width=stack[1] if trace_charstrings then - showvalue("width",width) + showvalue("backtrack width",width) end + else + width=true end - elseif not width then - width=true end if trace_charstrings then showstate("hmoveto") end x=x+stack[top] top=0 - moveto(x,y) + xmoveto() end local function vmoveto() - if top>1 then - if not width then + if not width then + if top>1 then width=stack[1] if trace_charstrings then - showvalue("width",width) + showvalue("backtrack width",width) end + else + width=true end - elseif not width then - width=true end if trace_charstrings then showstate("vmoveto") end y=y+stack[top] top=0 - moveto(x,y) + ymoveto() end local function rlineto() if trace_charstrings then @@ -10053,20 +14847,7 @@ do 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) + xylineto() end top=0 end @@ -10074,13 +14855,47 @@ do if trace_charstrings then showstate("hlineto") end - xlineto(true) + if top==1 then + x=x+stack[1] + xlineto() + else + local swap=true + for i=1,top do + if swap then + x=x+stack[i] + xlineto() + swap=false + else + y=y+stack[i] + ylineto() + swap=true + end + end + end + top=0 end local function vlineto() if trace_charstrings then showstate("vlineto") end - xlineto(false) + if top==1 then + y=y+stack[1] + ylineto() + else + local swap=false + for i=1,top do + if swap then + x=x+stack[i] + xlineto() + swap=false + else + y=y+stack[i] + ylineto() + swap=true + end + end + end + top=0 end local function rrcurveto() if trace_charstrings then @@ -10091,9 +14906,9 @@ do 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) + x=bx+stack[i+4] + y=by+stack[i+5] + xycurveto(ax,ay,bx,by,x,y) end top=0 end @@ -10103,17 +14918,17 @@ do end local s=1 if top%2~=0 then - y=y+stack[1] + y=y+stack[1] s=2 end for i=s,top,4 do - local ax=x+stack[i] + 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] + x=bx+stack[i+3] y=by - curveto(ax,ay,bx,by,x,y) + xycurveto(ax,ay,bx,by,x,y) end top=0 end @@ -10124,17 +14939,17 @@ do local s=1 local d=0 if top%2~=0 then - d=stack[1] + d=stack[1] s=2 end for i=s,top,4 do local ax=x+d - local ay=y+stack[i] + 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) + y=by+stack[i+3] + xycurveto(ax,ay,bx,by,x,y) d=0 end top=0 @@ -10144,7 +14959,6 @@ do if last then top=top-1 end - local sw=swap for i=1,top,4 do local ax,ay,bx,by if swap then @@ -10172,7 +14986,7 @@ do end swap=true end - curveto(ax,ay,bx,by,x,y) + xycurveto(ax,ay,bx,by,x,y) end top=0 end @@ -10199,11 +15013,11 @@ do local by=ay+stack[i+3] x=bx+stack[i+4] y=by+stack[i+5] - curveto(ax,ay,bx,by,x,y) + xycurveto(ax,ay,bx,by,x,y) end x=x+stack[top-1] y=y+stack[top] - lineto(x,y) + xylineto() top=0 end local function rlinecurve() @@ -10214,7 +15028,7 @@ do for i=1,top-6,2 do x=x+stack[i] y=y+stack[i+1] - lineto(x,y) + xylineto() end end local ax=x+stack[top-5] @@ -10223,7 +15037,7 @@ do local by=ay+stack[top-2] x=bx+stack[top-1] y=by+stack[top] - curveto(ax,ay,bx,by,x,y) + xycurveto(ax,ay,bx,by,x,y) top=0 end local function flex() @@ -10236,33 +15050,33 @@ do local by=ay+stack[4] local cx=bx+stack[5] local cy=by+stack[6] - curveto(ax,ay,bx,by,cx,cy) + xycurveto(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) + xycurveto(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 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) + xycurveto(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) + xycurveto(dx,dy,ex,ey,x,y) top=0 end local function hflex1() @@ -10275,13 +15089,13 @@ do local by=ay+stack[4] local cx=bx+stack[5] local cy=by - curveto(ax,ay,bx,by,cx,cy) + xycurveto(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) + xycurveto(dx,dy,ex,ey,x,y) top=0 end local function flex1() @@ -10294,7 +15108,7 @@ do local by=ay+stack[4] local cx=bx+stack[5] local cy=by+stack[6] - curveto(ax,ay,bx,by,cx,cy) + xycurveto(ax,ay,bx,by,cx,cy) local dx=cx+stack[7] local dy=cy+stack[8] local ex=dx+stack[9] @@ -10304,7 +15118,7 @@ do else y=ey+stack[11] end - curveto(dx,dy,ex,ey,x,y) + xycurveto(dx,dy,ex,ey,x,y) top=0 end local function getstem() @@ -10439,6 +15253,92 @@ do end top=0 end + local reginit=false + local function updateregions(n) + if regions then + local current=regions[n] or regions[1] + nofregions=#current + if axis and n~=reginit then + factors={} + for i=1,nofregions do + local region=current[i] + local s=1 + for j=1,#axis do + local f=axis[j] + local r=region[j] + local start=r.start + local peak=r.peak + local stop=r.stop + if start>peak or peak>stop then + elseif start<0 and stop>0 and peak~=0 then + elseif peak==0 then + elseif fstop then + s=0 + break + elseif fpeak then + s=s*(stop-f)/(stop-peak) + else + end + end + factors[i]=s + end + end + end + reginit=n + end + local function setvsindex() + local vsindex=stack[top] + if trace_charstrings then + showstate(formatters["vsindex %i"](vsindex)) + end + updateregions(vsindex) + top=top-1 + end + local function blend() + local n=stack[top] + top=top-1 + if axis then + if trace_charstrings then + local t=top-nofregions*n + local m=t-n + for i=1,n do + local k=m+i + local d=m+n+(i-1)*nofregions + local old=stack[k] + local new=old + for r=1,nofregions do + new=new+stack[d+r]*factors[r] + end + stack[k]=new + showstate(formatters["blend %i of %i: %s -> %s"](i,n,old,new)) + end + top=t + elseif n==1 then + top=top-nofregions + local v=stack[top] + for r=1,nofregions do + v=v+stack[top+r]*factors[r] + end + stack[top]=v + else + top=top-nofregions*n + local d=top + local k=top-n + for i=1,n do + k=k+1 + local v=stack[k] + for r=1,nofregions do + v=v+stack[d+r]*factors[r] + end + stack[k]=v + d=d+nofregions + end + end + else + end + end local actions={ [0]=unsupported, getstem, unsupported, @@ -10454,8 +15354,8 @@ do unsupported, hsbw, unsupported, - unsupported, - unsupported, + setvsindex, + blend, unsupported, getstem, getmask, @@ -10487,8 +15387,80 @@ do [036]=hflex1, [037]=flex1, } - local p_bytes=Ct((P(1)/byte)^0) - local function call(scope,list,bias,process) + local c_endchar=char(14) + local passon do + local rshift=bit32.rshift + local band=bit32.band + local round=math.round + local encode=table.setmetatableindex(function(t,i) + for i=-2048,-1130 do + t[i]=char(28,band(rshift(i,8),0xFF),band(i,0xFF)) + end + for i=-1131,-108 do + local v=0xFB00-i-108 + t[i]=char(band(rshift(v,8),0xFF),band(v,0xFF)) + end + for i=-107,107 do + t[i]=char(i+139) + end + for i=108,1131 do + local v=0xF700+i-108 + t[i]=char(band(rshift(v,8),0xFF),band(v,0xFF)) + end + for i=1132,2048 do + t[i]=char(28,band(rshift(i,8),0xFF),band(i,0xFF)) + end + return t[i] + end) + local function setvsindex() + local vsindex=stack[top] + updateregions(vsindex) + top=top-1 + end + local function blend() + local n=stack[top] + top=top-1 + if not axis then + elseif n==1 then + top=top-nofregions + local v=stack[top] + for r=1,nofregions do + v=v+stack[top+r]*factors[r] + end + stack[top]=round(v) + else + top=top-nofregions*n + local d=top + local k=top-n + for i=1,n do + k=k+1 + local v=stack[k] + for r=1,nofregions do + v=v+stack[d+r]*factors[r] + end + stack[k]=round(v) + d=d+nofregions + end + end + end + passon=function(operation) + if operation==15 then + setvsindex() + elseif operation==16 then + blend() + else + for i=1,top do + r=r+1 + result[r]=encode[stack[i]] + end + r=r+1 + result[r]=char(operation) + top=0 + end + end + end + local process + local function call(scope,list,bias) depth=depth+1 if top==0 then showstate(formatters["unknown %s call"](scope)) @@ -10501,10 +15473,6 @@ do 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)) @@ -10513,50 +15481,50 @@ do end depth=depth-1 end - local function process(tab) + local justpass=false + process=function(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 + if t>=32 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 + if t<=246 then + stack[top]=t-139 + i=i+1 + elseif t<=250 then + stack[top]=t*256-63124+tab[i+1] + i=i+2 + elseif t<=254 then + stack[top]=-t*256+64148-tab[i+1] + i=i+2 + else + local n=0x100*tab[i+1]+tab[i+2] + if n>=0x8000 then + stack[top]=n-0x10000+(0x100*tab[i+3]+tab[i+4])/0xFFFF + else + stack[top]=n+(0x100*tab[i+3]+tab[i+4])/0xFFFF + end + i=i+5 + end 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 + stack[top]=n-0x10000 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 + elseif t==11 then if trace_charstrings then showstate("return") end return elseif t==10 then - call("local",locals,localbias,process) + call("local",locals,localbias) i=i+1 - elseif t==14 then + elseif t==14 then if width then elseif top>0 then width=stack[1] @@ -10571,7 +15539,7 @@ do end return elseif t==29 then - call("global",globals,globalbias,process) + call("global",globals,globalbias) i=i+1 elseif t==12 then i=i+1 @@ -10586,20 +15554,25 @@ do top=0 end i=i+1 + elseif justpass then + passon(t) + i=i+1 else local a=actions[t] if a then local s=a(t) if s then - i=i+s + i=i+s+1 + else + i=i+1 end else if trace_charstrings then showvalue("",t) end top=0 + i=i+1 end - i=i+1 end end end @@ -10615,96 +15588,15 @@ do ((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 + local function processshape(tab,index) + tab=bytetable(tab) x=0 y=0 width=false r=0 top=0 stems=0 - result={} + result={} xmin=0 xmax=0 ymin=0 @@ -10712,26 +15604,35 @@ do checked=false if trace_charstrings then report("glyph: %i",index) - report("data: % t",tab) + report("data : % t",tab) + end + if regions then + updateregions(vsindex) end process(tab) - local boundingbox={ xmin,ymin,xmax,ymax } + local boundingbox={ + round(xmin), + round(ymin), + round(xmax), + round(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 + if justpass then + r=r+1 + result[r]=c_endchar + local stream=concat(result) + if glyph then + glyph.stream=stream + else + glyphs[index]={ stream=stream } + end + elseif glyph then + glyph.segments=keepcurve~=false and result or nil glyph.boundingbox=boundingbox if not glyph.width then glyph.width=width @@ -10739,22 +15640,93 @@ do if charset and not glyph.name then glyph.name=charset[index] end + elseif keepcurve then + glyphs[index]={ + segments=result, + boundingbox=boundingbox, + width=width, + name=charset and charset[index] or nil, + } + else + glyphs[index]={ + boundingbox=boundingbox, + width=width, + name=charset and charset[index] or nil, + } end if trace_charstrings then - report("width: %s",tostring(width)) + report("width : %s",tostring(width)) report("boundingbox: % t",boundingbox) end end - resetcharstrings=function() + startparsing=function(fontdata,data,streams) + reginit=false + axis=false + regions=data.regions + justpass=streams==true + if regions then + regions={ regions } + axis=data.factors or false + end + end + stopparsing=function(fontdata,data) + stack={} + glyphs=false result={} top=0 - stack={} + locals=false + globals=false + strings=false + end + local function setwidths(private) + if not private then + return 0,0 + end + local privatedata=private.data + if not privatedata then + return 0,0 + end + return privatedata.nominalwidthx or 0,privatedata.defaultwidthx or 0 + end + parsecharstrings=function(fontdata,data,glphs,doshapes,tversion,streams) + local dictionary=data.dictionaries[1] + local charstrings=dictionary.charstrings + keepcurve=doshapes + version=tversion + strings=data.strings + globals=data.routines or {} + locals=dictionary.subroutines or {} + charset=dictionary.charset + vsindex=dictionary.vsindex or 0 + glyphs=glphs or {} + globalbias,localbias=setbias(globals,locals) + nominalwidth,defaultwidth=setwidths(dictionary.private) + startparsing(fontdata,data,streams) + for index=1,#charstrings do + processshape(charstrings[index],index-1) + charstrings[index]=nil + end + stopparsing(fontdata,data) + return glyphs + end + parsecharstring=function(fontdata,data,dictionary,tab,glphs,index,doshapes,tversion) + keepcurve=doshapes + version=tversion + strings=data.strings + globals=data.routines or {} + locals=dictionary.subroutines or {} + charset=false + vsindex=dictionary.vsindex or 0 + glyphs=glphs or {} + globalbias,localbias=setbias(globals,locals) + nominalwidth,defaultwidth=setwidths(dictionary.private) + processshape(tab,index-1) end end local function readglobals(f,data) local routines=readlengths(f) for i=1,#routines do - routines[i]=readstring(f,routines[i]) + routines[i]=readbytetable(f,routines[i]) end data.routines=routines end @@ -10766,7 +15738,7 @@ local function readcharsets(f,data,dictionary) local strings=data.strings local nofglyphs=data.nofglyphs local charsetoffset=dictionary.charset - if charsetoffset~=0 then + if charsetoffset and charsetoffset~=0 then setposition(f,header.offset+charsetoffset) local format=readbyte(f) local charset={ [0]=".notdef" } @@ -10792,6 +15764,9 @@ local function readcharsets(f,data,dictionary) else report("cff parser: unsupported charset format %a",format) end + else + dictionary.nocharset=true + dictionary.charset=nil end end local function readprivates(f,data) @@ -10812,7 +15787,7 @@ local function readlocals(f,data,dictionary) setposition(f,header.offset+private.offset+subroutineoffset) local subroutines=readlengths(f) for i=1,#subroutines do - subroutines[i]=readstring(f,subroutines[i]) + subroutines[i]=readbytetable(f,subroutines[i]) end dictionary.subroutines=subroutines private.data.subroutines=nil @@ -10823,15 +15798,16 @@ local function readlocals(f,data,dictionary) dictionary.subroutines={} end end -local function readcharstrings(f,data) +local function readcharstrings(f,data,what) local header=data.header local dictionaries=data.dictionaries local dictionary=dictionaries[1] - local type=dictionary.charstringtype + local stringtype=dictionary.charstringtype local offset=dictionary.charstrings - if type==2 then + if type(offset)~="number" then + elseif stringtype==2 then setposition(f,header.offset+offset) - local charstrings=readlengths(f) + local charstrings=readlengths(f,what=="cff2") local nofglyphs=#charstrings for i=1,nofglyphs do charstrings[i]=readstring(f,charstrings[i]) @@ -10839,7 +15815,7 @@ local function readcharstrings(f,data) data.nofglyphs=nofglyphs dictionary.charstrings=charstrings else - report("unsupported charstr type %i",type) + report("unsupported charstr type %i",stringtype) data.nofglyphs=0 dictionary.charstrings={} end @@ -10857,29 +15833,36 @@ local function readcidprivates(f,data) end parseprivates(data,dictionaries) end -local function readnoselect(f,data,glyphs,doshapes,version) +readers.parsecharstrings=parsecharstrings +local function readnoselect(f,fontdata,data,glyphs,doshapes,version,streams) local dictionaries=data.dictionaries local dictionary=dictionaries[1] readglobals(f,data) - readcharstrings(f,data) - readencodings(f,data) - readcharsets(f,data,dictionary) + readcharstrings(f,data,version) + if version=="cff2" then + dictionary.charset=nil + else + readencodings(f,data) + readcharsets(f,data,dictionary) + end readprivates(f,data) parseprivates(data,data.dictionaries) readlocals(f,data,dictionary) - parsecharstrings(data,glyphs,doshapes,version) - resetcharstrings() + startparsing(fontdata,data,streams) + parsecharstrings(fontdata,data,glyphs,doshapes,version,streams) + stopparsing(fontdata,data) end -readers.parsecharstrings=parsecharstrings -local function readfdselect(f,data,glyphs,doshapes,version) +local function readfdselect(f,fontdata,data,glyphs,doshapes,version,streams) 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) + readcharstrings(f,data,version) + if version~="cff2" then + readencodings(f,data) + end local charstrings=dictionary.charstrings local fdindex={} local nofglyphs=data.nofglyphs @@ -10928,68 +15911,133 @@ local function readfdselect(f,data,glyphs,doshapes,version) for i=1,#dictionaries do readlocals(f,data,dictionaries[i]) end + startparsing(fontdata,data,streams) for i=1,#charstrings do - parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version) + parsecharstring(fontdata,data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version) + charstrings[i]=nil end - resetcharstrings() + stopparsing(fontdata,data) end end +local gotodatatable=readers.helpers.gotodatatable +local function cleanup(data,dictionaries) +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 + local tableoffset=gotodatatable(f,fontdata,"cff",specification.details) + if tableoffset then + local header=readheader(f) + if header.major~=1 then + report("only version %s is supported for table %a",1,"cff") + return + end + local glyphs=fontdata.glyphs + 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,dictionaries,"cff") + local dic=dictionaries[1] + local cid=dic.cid + fontdata.cffinfo={ + familynamename=dic.familyname, + fullname=dic.fullname, + boundingbox=dic.boundingbox, + weight=dic.weight, + italicangle=dic.italicangle, + underlineposition=dic.underlineposition, + underlinethickness=dic.underlinethickness, + monospaced=dic.monospaced, + } + fontdata.cidinfo=cid and { + registry=cid.registry, + ordering=cid.ordering, + supplement=cid.supplement, + } + if specification.glyphs then + local all=specification.shapes or false + if cid and cid.fdselect then + readfdselect(f,fontdata,data,glyphs,all,"cff") 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 + readnoselect(f,fontdata,data,glyphs,all,"cff") end end + cleanup(data,dictionaries) + end +end +function readers.cff2(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"cff2",specification.glyphs) + if tableoffset then + local header=readheader(f) + if header.major~=2 then + report("only version %s is supported for table %a",2,"cff2") + return + end + local glyphs=fontdata.glyphs + local dictionaries={ readstring(f,header.dsize) } + local data={ + header=header, + dictionaries=dictionaries, + nofglyphs=fontdata.nofglyphs, + } + parsedictionaries(data,dictionaries,"cff2") + local offset=dictionaries[1].vstore + if offset>0 then + local storeoffset=dictionaries[1].vstore+data.header.offset+2 + local regions,deltas=readers.helpers.readvariationdata(f,storeoffset,factors) + data.regions=regions + data.deltas=deltas + else + data.regions={} + data.deltas={} + end + data.factors=specification.factors + local cid=data.dictionaries[1].cid + local all=specification.shapes or false + if cid and cid.fdselect then + readfdselect(f,fontdata,data,glyphs,all,"cff2",specification.streams) + else + readnoselect(f,fontdata,data,glyphs,all,"cff2",specification.streams) + end + cleanup(data,dictionaries) + end +end +function readers.cffcheck(filename) + local f=io.open(filename,"rb") + if f then + local fontdata={ + glyphs={}, + } + local header=readheader(f) + if header.major~=1 then + report("only version %s is supported for table %a",1,"cff") + return + end + local names=readfontnames(f) + local dictionaries=readtopdictionaries(f) + local strings=readstrings(f) + local glyphs={} + local data={ + header=header, + names=names, + dictionaries=dictionaries, + strings=strings, + glyphs=glyphs, + nofglyphs=4, + } + parsedictionaries(data,dictionaries,"cff") + local cid=data.dictionaries[1].cid + if cid and cid.fdselect then + readfdselect(f,fontdata,data,glyphs,false) + else + readnoselect(f,fontdata,data,glyphs,false) + end + return data end end @@ -11005,9 +16053,12 @@ if not modules then modules={} end modules ['font-ttf']={ license="see context related readme files" } local next,type,unpack=next,type,unpack -local bittest=bit32.btest -local sqrt=math.sqrt +local band,rshift=bit32.band,bit32.rshift +local sqrt,round=math.sqrt,math.round +local char=string.char +local concat=table.concat local report=logs.reporter("otf reader","ttf") +local trace_deltas=false local readers=fonts.handlers.otf.readers local streamreader=readers.streamreader local setposition=streamreader.setposition @@ -11019,22 +16070,50 @@ local readulong=streamreader.readcardinal4 local readchar=streamreader.readinteger1 local readshort=streamreader.readinteger2 local read2dot14=streamreader.read2dot14 +local readinteger=streamreader.readinteger1 +local readcardinaltable=streamreader.readcardinaltable +local readintegertable=streamreader.readintegertable +directives.register("fonts.streamreader",function() + streamreader=utilities.streams + setposition=streamreader.setposition + getposition=streamreader.getposition + skipbytes=streamreader.skip + readbyte=streamreader.readcardinal1 + readushort=streamreader.readcardinal2 + readulong=streamreader.readcardinal4 + readchar=streamreader.readinteger1 + readshort=streamreader.readinteger2 + read2dot14=streamreader.read2dot14 + readinteger=streamreader.readinteger1 + readcardinaltable=streamreader.readcardinaltable + readintegertable=streamreader.readintegertable +end) +local short=2 +local ushort=2 +local ulong=4 +local helpers=readers.helpers +local gotodatatable=helpers.gotodatatable local function mergecomposites(glyphs,shapes) local function merge(index,shape,components) local contours={} + local points={} local nofcontours=0 + local nofpoints=0 + local offset=0 + local deltas=shape.deltas for i=1,#components do local component=components[i] local subindex=component.index local subshape=shapes[subindex] local subcontours=subshape.contours + local subpoints=subshape.points if not subcontours then local subcomponents=subshape.components if subcomponents then - subcontours=merge(subindex,subshape,subcomponents) + subcontours,subpoints=merge(subindex,subshape,subcomponents) end end - if subcontours then + if subpoints then local matrix=component.matrix local xscale=matrix[1] local xrotate=matrix[2] @@ -11042,35 +16121,38 @@ local function mergecomposites(glyphs,shapes) local yscale=matrix[4] local xoffset=matrix[5] local yoffset=matrix[6] + for i=1,#subpoints do + local p=subpoints[i] + local x=p[1] + local y=p[2] + nofpoints=nofpoints+1 + points[nofpoints]={ + xscale*x+xrotate*y+xoffset, + yscale*y+yrotate*x+yoffset, + p[3] + } + end 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 + contours[nofcontours]=offset+subcontours[i] end + offset=offset+#subpoints else report("missing contours composite %s, component %s of %s, glyph %s",index,i,#components,subindex) end end + shape.points=points shape.contours=contours shape.components=nil - return contours + return contours,points end for index=1,#glyphs do local shape=shapes[index] - local components=shape.components - if components then - merge(index,shape,components) + if shape then + local components=shape.components + if components then + merge(index,shape,components) + end end end end @@ -11080,123 +16162,510 @@ local function readnothing(f,nofcontours) } end local function curveto(m_x,m_y,l_x,l_y,r_x,r_y) - return { + 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" - } + r_x,r_y,"c" +end +local function applyaxis(glyph,shape,deltas,dowidth) + local points=shape.points + if points then + local nofpoints=#points + local h=nofpoints+2 + local l=nofpoints+1 + local dw=0 + local dl=0 + for i=1,#deltas do + local deltaset=deltas[i] + local xvalues=deltaset.xvalues + local yvalues=deltaset.yvalues + local dpoints=deltaset.points + local factor=deltaset.factor + if dpoints then + local nofdpoints=#dpoints + for i=1,nofdpoints do + local d=dpoints[i] + local p=points[d] + if p then + if xvalues then + local x=xvalues[i] + if x and x~=0 then + p[1]=p[1]+factor*x + end + end + if yvalues then + local y=yvalues[i] + if y and y~=0 then + p[2]=p[2]+factor*y + end + end + elseif dowidth then + if d==h then + local x=xvalues[i] + if x then + dw=dw+factor*x + end + elseif d==l then + local x=xvalues[i] + if x then + dl=dl+factor*x + end + end + end + end + else + for i=1,nofpoints do + local p=points[i] + if xvalues then + local x=xvalues[i] + if x and x~=0 then + p[1]=p[1]+factor*x + end + end + if yvalues then + local y=yvalues[i] + if y and y~=0 then + p[2]=p[2]+factor*y + end + end + end + if dowidth then + local x=xvalues[h] + if x then + dw=dw+factor*x + end + local x=xvalues[l] + if x then + dl=dl+factor*x + end + end + end + end + if dowidth then + local width=glyph.width or 0 + glyph.width=width+dw-dl + end + else + report("no points for glyph %a",glyph.name) + end end -local function contours2outlines(glyphs,shapes) - local quadratic=true +local quadratic=false +local function contours2outlines_normal(glyphs,shapes) 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 + if shape then + local glyph=glyphs[index] + local contours=shape.contours + local points=shape.points + if contours then + local nofcontours=#contours + local segments={} + local nofsegments=0 + glyph.segments=segments + if nofcontours>0 then + local px,py=0,0 + local first=1 + for i=1,nofcontours do + local last=contours[i] + if last>=first then + local first_pt=points[first] 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 + if first==last then + first_pt[3]="m" + nofsegments=nofsegments+1 + segments[nofsegments]=first_pt + else + local first_on=first_pt[3] + local last_pt=points[last] + local last_on=last_pt[3] + local start=1 + local control_pt=false + if first_on then + start=2 else - first_pt={ (first_pt[1]+last_pt[1])/2,(first_pt[2]+last_pt[2])/2,false } + 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 - 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 + local x,y=first_pt[1],first_pt[2] + if not done then + xmin,ymin,xmax,ymax=x,y,x,y + done=true + end + nofsegments=nofsegments+1 + segments[nofsegments]={ x,y,"m" } + if not quadratic then + px,py=x,y + end + local previous_pt=first_pt + for i=first,last do + local current_pt=points[i] + local current_on=current_pt[3] + local previous_on=previous_pt[3] + if previous_on then + if current_on then + local x,y=current_pt[1],current_pt[2] + nofsegments=nofsegments+1 + segments[nofsegments]={ x,y,"l" } + if not quadratic then + px,py=x,y + end + else + control_pt=current_pt + end + elseif current_on then + local x1,y1=control_pt[1],control_pt[2] + local x2,y2=current_pt[1],current_pt[2] nofsegments=nofsegments+1 - segments[nofsegments]={ current_pt[1],current_pt[2],"l" } + if quadratic then + segments[nofsegments]={ x1,y1,x2,y2,"q" } + else + x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2) + segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } + end + control_pt=false else + local x2,y2=(previous_pt[1]+current_pt[1])/2,(previous_pt[2]+current_pt[2])/2 + local x1,y1=control_pt[1],control_pt[2] + nofsegments=nofsegments+1 + if quadratic then + segments[nofsegments]={ x1,y1,x2,y2,"q" } + else + x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2) + segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } + end control_pt=current_pt end - elseif current_on then - local ps=segments[nofsegments] + previous_pt=current_pt + end + if first_pt==last_pt then + else nofsegments=nofsegments+1 - if quadratic then - segments[nofsegments]={ control_pt[1],control_pt[2],current_pt[1],current_pt[2],"q" } + local x2,y2=first_pt[1],first_pt[2] + if not control_pt then + segments[nofsegments]={ x2,y2,"l" } + elseif quadratic then + local x1,y1=control_pt[1],control_pt[2] + segments[nofsegments]={ x1,y1,x2,y2,"q" } + else + local x1,y1=control_pt[1],control_pt[2] + x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2) + segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } + end + end + end + end + first=last+1 + end + end + end + end + end +end +local function contours2outlines_shaped(glyphs,shapes,keepcurve) + for index=1,#glyphs do + local shape=shapes[index] + if shape then + local glyph=glyphs[index] + local contours=shape.contours + local points=shape.points + if contours then + local nofcontours=#contours + local segments=keepcurve and {} or nil + local nofsegments=0 + if keepcurve then + glyph.segments=segments + end + if nofcontours>0 then + local xmin,ymin,xmax,ymax,done=0,0,0,0,false + local px,py=0,0 + local first=1 + for i=1,nofcontours do + local last=contours[i] + if last>=first then + local first_pt=points[first] + local first_on=first_pt[3] + if first==last then + if keepcurve then + first_pt[3]="m" + nofsegments=nofsegments+1 + segments[nofsegments]=first_pt + end + else + local first_on=first_pt[3] + local last_pt=points[last] + 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 - 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]) + first_pt={ (first_pt[1]+last_pt[1])/2,(first_pt[2]+last_pt[2])/2,false } end - control_pt=false + control_pt=first_pt + end + local x,y=first_pt[1],first_pt[2] + if not done then + xmin,ymin,xmax,ymax=x,y,x,y + done=true else + if xxmax then xmax=x end + if yymax then ymax=y end + end + if keepcurve then nofsegments=nofsegments+1 - local halfway_x=(previous_pt[1]+current_pt[1])/2 - local halfway_y=(previous_pt[2]+current_pt[2])/2 + segments[nofsegments]={ x,y,"m" } + end + if not quadratic then + px,py=x,y + end + local previous_pt=first_pt + for i=first,last do + local current_pt=points[i] + local current_on=current_pt[3] + local previous_on=previous_pt[3] + if previous_on then + if current_on then + local x,y=current_pt[1],current_pt[2] + if xxmax then xmax=x end + if yymax then ymax=y end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x,y,"l" } + end + if not quadratic then + px,py=x,y + end + else + control_pt=current_pt + end + elseif current_on then + local x1,y1=control_pt[1],control_pt[2] + local x2,y2=current_pt[1],current_pt[2] + if quadratic then + if x1xmax then xmax=x1 end + if y1ymax then ymax=y1 end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x1,y1,x2,y2,"q" } + end + else + x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2) + if x1xmax then xmax=x1 end + if y1ymax then ymax=y1 end + if x2xmax then xmax=x2 end + if y2ymax then ymax=y2 end + if pxxmax then xmax=px end + if pyymax then ymax=py end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } + end + end + control_pt=false + else + local x2,y2=(previous_pt[1]+current_pt[1])/2,(previous_pt[2]+current_pt[2])/2 + local x1,y1=control_pt[1],control_pt[2] + if quadratic then + if x1xmax then xmax=x1 end + if y1ymax then ymax=y1 end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x1,y1,x2,y2,"q" } + end + else + x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2) + if x1xmax then xmax=x1 end + if y1ymax then ymax=y1 end + if x2xmax then xmax=x2 end + if y2ymax then ymax=y2 end + if pxxmax then xmax=px end + if pyymax then ymax=py end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } + end + end + control_pt=current_pt + end + previous_pt=current_pt + end + if first_pt==last_pt then + elseif not control_pt then + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ first_pt[1],first_pt[2],"l" } + end + else + local x1,y1=control_pt[1],control_pt[2] + local x2,y2=first_pt[1],first_pt[2] + if x1xmax then xmax=x1 end + if y1ymax then ymax=y1 end if quadratic then - segments[nofsegments]={ control_pt[1],control_pt[2],halfway_x,halfway_y,"q" } + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x1,y1,x2,y2,"q" } + end 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) + x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2) + if x2xmax then xmax=x2 end + if y2ymax then ymax=y2 end + if pxxmax then xmax=px end + if pyymax then ymax=py end + if keepcurve then + nofsegments=nofsegments+1 + segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } + end end - control_pt=current_pt end - previous_pt=current_pt end - if first_pt==last_pt then + end + first=last+1 + end + glyph.boundingbox={ round(xmin),round(ymin),round(xmax),round(ymax) } + end + end + end + end +end +local c_zero=char(0) +local s_zero=char(0,0) +local function toushort(n) + return char(band(rshift(n,8),0xFF),band(n,0xFF)) +end +local function toshort(n) + if n<0 then + n=n+0x10000 + end + return char(band(rshift(n,8),0xFF),band(n,0xFF)) +end +local function repackpoints(glyphs,shapes) + local noboundingbox={ 0,0,0,0 } + local result={} + for index=1,#glyphs do + local shape=shapes[index] + if shape then + local r=0 + local glyph=glyphs[index] + if false then + else + local contours=shape.contours + local nofcontours=contours and #contours or 0 + local boundingbox=glyph.boundingbox or noboundingbox + r=r+1 result[r]=toshort(nofcontours) + r=r+1 result[r]=toshort(boundingbox[1]) + r=r+1 result[r]=toshort(boundingbox[2]) + r=r+1 result[r]=toshort(boundingbox[3]) + r=r+1 result[r]=toshort(boundingbox[4]) + if nofcontours>0 then + for i=1,nofcontours do + r=r+1 result[r]=toshort(contours[i]-1) + end + r=r+1 result[r]=s_zero + local points=shape.points + local currentx=0 + local currenty=0 + local xpoints={} + local ypoints={} + local x=0 + local y=0 + local lastflag=nil + local nofflags=0 + for i=1,#points do + local pt=points[i] + local px=pt[1] + local py=pt[2] + local fl=pt[3] and 0x01 or 0x00 + if px==currentx then + fl=fl+0x10 + else + local dx=round(px-currentx) + if dx<-255 or dx>255 then + x=x+1 xpoints[x]=toshort(dx) + elseif dx<0 then + fl=fl+0x02 + x=x+1 xpoints[x]=char(-dx) + elseif dx>0 then + fl=fl+0x12 + x=x+1 xpoints[x]=char(dx) 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 + fl=fl+0x02 + x=x+1 xpoints[x]=c_zero + end + end + if py==currenty then + fl=fl+0x20 + else + local dy=round(py-currenty) + if dy<-255 or dy>255 then + y=y+1 ypoints[y]=toshort(dy) + elseif dy<0 then + fl=fl+0x04 + y=y+1 ypoints[y]=char(-dy) + elseif dy>0 then + fl=fl+0x24 + y=y+1 ypoints[y]=char(dy) + else + fl=fl+0x04 + y=y+1 ypoints[y]=c_zero + end + end + currentx=px + currenty=py + if lastflag==fl then + nofflags=nofflags+1 + else + if nofflags==1 then + r=r+1 result[r]=char(lastflag) + elseif nofflags==2 then + r=r+1 result[r]=char(lastflag,lastflag) + elseif nofflags>2 then + lastflag=lastflag+0x08 + r=r+1 result[r]=char(lastflag,nofflags-1) end + nofflags=1 + lastflag=fl end end + if nofflags==1 then + r=r+1 result[r]=char(lastflag) + elseif nofflags==2 then + r=r+1 result[r]=char(lastflag,lastflag) + elseif nofflags>2 then + lastflag=lastflag+0x08 + r=r+1 result[r]=char(lastflag,nofflags-1) + end + r=r+1 result[r]=concat(xpoints) + r=r+1 result[r]=concat(ypoints) end end + glyph.stream=concat(result,"",1,r) + else end end end -local function readglyph(f,nofcontours) +local function readglyph(f,nofcontours) local points={} - local endpoints={} local instructions={} local flags={} + local contours={} for i=1,nofcontours do - endpoints[i]=readshort(f)+1 + contours[i]=readshort(f)+1 end - local nofpoints=endpoints[nofcontours] + local nofpoints=contours[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 + if band(flag,0x08)~=0 then for j=1,readbyte(f) do i=i+1 flags[i]=flag @@ -11207,8 +16676,8 @@ local function readglyph(f,nofcontours) local x=0 for i=1,nofpoints do local flag=flags[i] - local short=bittest(flag,0x0002) - local same=bittest(flag,0x0010) + local short=band(flag,0x02)~=0 + local same=band(flag,0x10)~=0 if short then if same then x=x+readbyte(f) @@ -11219,13 +16688,13 @@ local function readglyph(f,nofcontours) else x=x+readshort(f) end - points[i]={ x,y,bittest(flag,0x0001) } + points[i]={ x,0,band(flag,0x01)~=0 } end local y=0 for i=1,nofpoints do local flag=flags[i] - local short=bittest(flag,0x0004) - local same=bittest(flag,0x0020) + local short=band(flag,0x04)~=0 + local same=band(flag,0x20)~=0 if short then if same then y=y+readbyte(f) @@ -11238,15 +16707,11 @@ local function readglyph(f,nofcontours) 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, + points=points, + contours=contours, + nofpoints=nofpoints, } end local function readcomposite(f) @@ -11256,8 +16721,8 @@ local function readcomposite(f) while true do local flags=readushort(f) local index=readushort(f) - local f_xyarg=bittest(flags,0x0002) - local f_offset=bittest(flags,0x0800) + local f_xyarg=band(flags,0x0002)~=0 + local f_offset=band(flags,0x0800)~=0 local xscale=1 local xrotate=0 local yrotate=0 @@ -11267,7 +16732,7 @@ local function readcomposite(f) local base=false local reference=false if f_xyarg then - if bittest(flags,0x0001) then + if band(flags,0x0001)~=0 then xoffset=readshort(f) yoffset=readshort(f) else @@ -11275,7 +16740,7 @@ local function readcomposite(f) yoffset=readchar(f) end else - if bittest(flags,0x0001) then + if band(flags,0x0001)~=0 then base=readshort(f) reference=readshort(f) else @@ -11283,21 +16748,21 @@ local function readcomposite(f) reference=readchar(f) end end - if bittest(flags,0x0008) then + if band(flags,0x0008)~=0 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 + elseif band(flags,0x0040)~=0 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 + elseif band(flags,0x0080)~=0 then xscale=read2dot14(f) xrotate=read2dot14(f) yrotate=read2dot14(f) @@ -11310,16 +16775,16 @@ local function readcomposite(f) nofcomponents=nofcomponents+1 components[nofcomponents]={ index=index, - usemine=bittest(flags,0x0200), - round=bittest(flags,0x0006), + usemine=band(flags,0x0200)~=0, + round=band(flags,0x0006)~=0, base=base, reference=reference, matrix={ xscale,xrotate,yrotate,yscale,xoffset,yoffset }, } - if bittest(flags,0x0100) then + if band(flags,0x0100)~=0 then instructions=true end - if not bittest(flags,0x0020) then + if not band(flags,0x0020)~=0 then break end end @@ -11337,15 +16802,13 @@ function readers.loca(f,fontdata,specification) local locations={} setposition(f,datatable.offset) if format==1 then - local nofglyphs=datatable.length/4-1 - -1 + local nofglyphs=datatable.length/4-2 for i=0,nofglyphs do locations[i]=offset+readulong(f) end fontdata.nofglyphs=nofglyphs else - local nofglyphs=datatable.length/2-1 - -1 + local nofglyphs=datatable.length/2-2 for i=0,nofglyphs do locations[i]=offset+readushort(f)*2 end @@ -11356,53 +16819,317 @@ function readers.loca(f,fontdata,specification) 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) + local tableoffset=gotodatatable(f,fontdata,"glyf",specification.glyphs) + if tableoffset 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 or specification.instance + 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 + if readers.gvar then + readers.gvar(f,fontdata,specification,glyphs,shapes) + end + mergecomposites(glyphs,shapes) + if specification.instance then + if specification.streams then + repackpoints(glyphs,shapes) + else + contours2outlines_shaped(glyphs,shapes,specification.shapes) + end + elseif specification.shapes then + contours2outlines_normal(glyphs,shapes) + end + end + end + end +end +local function readtuplerecord(f,nofaxis) + local record={} + for i=1,nofaxis do + record[i]=read2dot14(f) + end + return record +end +local function readpoints(f) + local count=readbyte(f) + if count==0 then + return nil,0 + else + if count<128 then + elseif band(count,0x80)~=0 then + count=band(count,0x7F)*256+readbyte(f) + else + end + local points={} + local p=0 + local n=1 + while p0 do + local control=readbyte(f) +if not control then + break +end + local allzero=band(control,0x80)~=0 + local runlength=band(control,0x3F)+1 + if allzero then + z=z+runlength + else + local runreader=band(control,0x40)~=0 and readshort or readinteger + if z>0 then + for i=1,z do + p=p+1 + deltas[p]=0 + end + z=0 + end + for i=1,runlength do + p=p+1 + deltas[p]=runreader(f) + end + end + nofpoints=nofpoints-runlength + end + if p>0 then + return deltas + else + end +end +local function readdeltas(f,nofpoints) + local deltas={} + local p=0 + while nofpoints>0 do + local control=readbyte(f) + if control then + local allzero=band(control,0x80)~=0 + local runlength=band(control,0x3F)+1 + if allzero then + for i=1,runlength do + p=p+1 + deltas[p]=0 + end + else + local runreader=band(control,0x40)~=0 and readshort or readinteger + for i=1,runlength do + p=p+1 + deltas[p]=runreader(f) + end + end + nofpoints=nofpoints-runlength + else + break + end + end + if p>0 then + return deltas + else + end +end +function readers.gvar(f,fontdata,specification,glyphdata,shapedata) + local instance=specification.instance + if not instance then + return + end + local factors=specification.factors + if not factors then + return + end + local tableoffset=gotodatatable(f,fontdata,"gvar",specification.variable or specification.shapes) + if tableoffset then + local version=readulong(f) + local nofaxis=readushort(f) + local noftuples=readushort(f) + local tupleoffset=tableoffset+readulong(f) + local nofglyphs=readushort(f) + local flags=readushort(f) + local dataoffset=tableoffset+readulong(f) + local data={} + local tuples={} + local glyphdata=fontdata.glyphs + local dowidth=not fontdata.variabledata.hvarwidths + if band(flags,0x0001)~=0 then + for i=1,nofglyphs+1 do + data[i]=dataoffset+readulong(f) + end + else + for i=1,nofglyphs+1 do + data[i]=dataoffset+2*readushort(f) + end + end + if noftuples>0 then + setposition(f,tupleoffset) + for i=1,noftuples do + tuples[i]=readtuplerecord(f,nofaxis) + end + end + local nextoffset=false + local startoffset=data[1] + for i=1,nofglyphs do + nextoffset=data[i+1] + local glyph=glyphdata[i-1] + local name=trace_deltas and glyph.name + if startoffset==nextoffset then + if name then + report("no deltas for glyph %a",name) + end + else + local shape=shapedata[i-1] + if not shape then + if name then + report("no shape for glyph %a",name) + end + else + lastoffset=startoffset + setposition(f,startoffset) + local flags=readushort(f) + local count=band(flags,0x0FFF) + local offset=startoffset+readushort(f) + local deltas={} + local allpoints=(shape.nofpoints or 0) + local shared=false + local nofshared=0 + if band(flags,0x8000)~=0 then + local current=getposition(f) + setposition(f,offset) + shared,nofshared=readpoints(f) + offset=getposition(f) + setposition(f,current) + end + for j=1,count do + local size=readushort(f) + local flags=readushort(f) + local index=band(flags,0x0FFF) + local haspeak=band(flags,0x8000)~=0 + local intermediate=band(flags,0x4000)~=0 + local private=band(flags,0x2000)~=0 + local peak=nil + local start=nil + local stop=nil + local xvalues=nil + local yvalues=nil + local points=shared + local nofpoints=nofshared + if haspeak then + peak=readtuplerecord(f,nofaxis) else - shapes[index]=readcomposite(f,nofcontours) + if index+1>#tuples then + report("error, bad tuple index",index) + end + peak=tuples[index+1] end - else - if loadshapes then - shapes[index]={} + if intermediate then + start=readtuplerecord(f,nofaxis) + stop=readtuplerecord(f,nofaxis) + end + if size>0 then + local current=getposition(f) + setposition(f,offset) + if private then + points,nofpoints=readpoints(f) + end + if nofpoints==0 then + nofpoints=allpoints+4 + end + if nofpoints>0 then + xvalues=readdeltas(f,nofpoints) + yvalues=readdeltas(f,nofpoints) + end + offset=offset+size + setposition(f,current) + end + if not xvalues and not yvalues then + points=nil + end + local s=1 + for i=1,nofaxis do + local f=factors[i] + local peak=peak and peak [i] or 0 + local start=start and start[i] or (peak<0 and peak or 0) + local stop=stop and stop [i] or (peak>0 and peak or 0) + if start>peak or peak>stop then + elseif start<0 and stop>0 and peak~=0 then + elseif peak==0 then + elseif fstop then + s=0 + break + elseif fpeak then + s=s*(stop-f)/(stop-peak) + else + end + end + if s==0 then + if name then + report("no deltas applied for glyph %a",name) + end + else + deltas[#deltas+1]={ + factor=s, + points=points, + xvalues=xvalues, + yvalues=yvalues, + } end - glyphs[index].boundingbox=nothing end - end - if loadshapes then - mergecomposites(glyphs,shapes) - contours2outlines(glyphs,shapes) + if shape.type=="glyph" then + applyaxis(glyph,shape,deltas,dowidth) + else + shape.deltas=deltas + end end end + startoffset=nextoffset end end end @@ -11419,34 +17146,80 @@ if not modules then modules={} end modules ['font-dsp']={ license="see context related readme files" } local next,type=next,type -local bittest=bit32.btest +local band=bit32.band +local extract=bit32.extract +local bor=bit32.bor +local lshift=bit32.lshift local rshift=bit32.rshift -local concat=table.concat +local gsub=string.gsub local lower=string.lower -local copy=table.copy local sub=string.sub local strip=string.strip local tohash=table.tohash +local concat=table.concat +local copy=table.copy local reversed=table.reversed +local sort=table.sort +local insert=table.insert +local round=math.round +local settings_to_hash=utilities.parsers.settings_to_hash_colon_too local setmetatableindex=table.setmetatableindex local formatters=string.formatters local sortedkeys=table.sortedkeys local sortedhash=table.sortedhash +local sequenced=table.sequenced 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 readinteger=streamreader.readinteger1 local readshort=streamreader.readinteger2 -local readfword=readshort local readstring=streamreader.readstring local readtag=streamreader.readtag local readbytes=streamreader.readbytes +local readfixed=streamreader.readfixed4 +local read2dot14=streamreader.read2dot14 +local skipshort=streamreader.skipshort +local skipbytes=streamreader.skip +local readbytetable=streamreader.readbytetable +local readbyte=streamreader.readbyte +local readcardinaltable=streamreader.readcardinaltable +local readintegertable=streamreader.readintegertable +local readfword=readshort +local short=2 +local ushort=2 +local ulong=4 +directives.register("fonts.streamreader",function() + streamreader=utilities.streams + setposition=streamreader.setposition + getposition=streamreader.getposition + readushort=streamreader.readcardinal2 + readulong=streamreader.readcardinal4 + readinteger=streamreader.readinteger1 + readshort=streamreader.readinteger2 + readstring=streamreader.readstring + readtag=streamreader.readtag + readbytes=streamreader.readbytes + readfixed=streamreader.readfixed4 + read2dot14=streamreader.read2dot14 + skipshort=streamreader.skipshort + skipbytes=streamreader.skip + readbytetable=streamreader.readbytetable + readbyte=streamreader.readbyte + readcardinaltable=streamreader.readcardinaltable + readintegertable=streamreader.readintegertable + readfword=readshort +end) local gsubhandlers={} local gposhandlers={} +readers.gsubhandlers=gsubhandlers +readers.gposhandlers=gposhandlers +local helpers=readers.helpers +local gotodatatable=helpers.gotodatatable +local setvariabledata=helpers.setvariabledata local lookupidoffset=-1 local classes={ "base", @@ -11480,6 +17253,51 @@ local chaindirections={ chainedcontext=1, reversechainedcontextsingle=-1, } +local function setmetrics(data,where,tag,d) + local w=data[where] + if w then + local v=w[tag] + if v then + w[tag]=v+d + end + end +end +local variabletags={ + hasc=function(data,d) setmetrics(data,"windowsmetrics","typoascender",d) end, + hdsc=function(data,d) setmetrics(data,"windowsmetrics","typodescender",d) end, + hlgp=function(data,d) setmetrics(data,"windowsmetrics","typolinegap",d) end, + hcla=function(data,d) setmetrics(data,"windowsmetrics","winascent",d) end, + hcld=function(data,d) setmetrics(data,"windowsmetrics","windescent",d) end, + vasc=function(data,d) setmetrics(data,"vhea not done","ascent",d) end, + vdsc=function(data,d) setmetrics(data,"vhea not done","descent",d) end, + vlgp=function(data,d) setmetrics(data,"vhea not done","linegap",d) end, + xhgt=function(data,d) setmetrics(data,"windowsmetrics","xheight",d) end, + cpht=function(data,d) setmetrics(data,"windowsmetrics","capheight",d) end, + sbxs=function(data,d) setmetrics(data,"windowsmetrics","subscriptxsize",d) end, + sbys=function(data,d) setmetrics(data,"windowsmetrics","subscriptysize",d) end, + sbxo=function(data,d) setmetrics(data,"windowsmetrics","subscriptxoffset",d) end, + sbyo=function(data,d) setmetrics(data,"windowsmetrics","subscriptyoffset",d) end, + spxs=function(data,d) setmetrics(data,"windowsmetrics","superscriptxsize",d) end, + spys=function(data,d) setmetrics(data,"windowsmetrics","superscriptysize",d) end, + spxo=function(data,d) setmetrics(data,"windowsmetrics","superscriptxoffset",d) end, + spyo=function(data,d) setmetrics(data,"windowsmetrics","superscriptyoffset",d) end, + strs=function(data,d) setmetrics(data,"windowsmetrics","strikeoutsize",d) end, + stro=function(data,d) setmetrics(data,"windowsmetrics","strikeoutpos",d) end, + unds=function(data,d) setmetrics(data,"postscript","underlineposition",d) end, + undo=function(data,d) setmetrics(data,"postscript","underlinethickness",d) end, +} +local read_cardinal={ + streamreader.readcardinal1, + streamreader.readcardinal2, + streamreader.readcardinal3, + streamreader.readcardinal4, +} +local read_integer={ + streamreader.readinteger1, + streamreader.readinteger2, + streamreader.readinteger3, + streamreader.readinteger4, +} local lookupnames={ gsub={ single="gsub_single", @@ -11503,31 +17321,236 @@ local lookupnames={ } 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, + band(k,0x0008)~=0 and true or false, + band(k,0x0004)~=0 and true or false, + band(k,0x0002)~=0 and true or false, + band(k,0x0001)~=0 and true or false, } t[k]=v return v end) +local function axistofactors(str) + local t=settings_to_hash(str) + for k,v in next,t do + t[k]=tonumber(v) or v + end + return t +end +local hash=table.setmetatableindex(function(t,k) + local v=sequenced(axistofactors(k),",") + t[k]=v + return v +end) +helpers.normalizedaxishash=hash +local cleanname=fonts.names and fonts.names.cleanname or function(name) + return name and (gsub(lower(name),"[^%a%d]","")) or nil +end +helpers.cleanname=cleanname +function helpers.normalizedaxis(str) + return hash[str] or str +end +local function getaxisscale(segments,minimum,default,maximum,user) + if not minimum or not default or not maximum then + return false + end + if usermaximum then + user=maximum + end + if userdefault then + default=(user-default)/(maximum-default) + else + default=0 + end + if not segments then + return default + end + local e + for i=1,#segments do + local s=segments[i] + if type(s)~="number" then + report("using default axis scale") + return default + elseif s[1]>=default then + if s[2]==default then + return default + else + e=i + break + end + end + end + if e then + local b=segments[e-1] + local e=segments[e] + return b[2]+(e[2]-b[2])*(default-b[1])/(e[1]-b[1]) + else + return false + end +end +local function getfactors(data,instancespec) + if instancespec==true then + elseif type(instancespec)~="string" or instancespec=="" then + return + end + local variabledata=data.variabledata + if not variabledata then + return + end + local instances=variabledata.instances + local axis=variabledata.axis + local segments=variabledata.segments + if instances and axis then + local values + if instancespec==true then + values={} + for i=1,#axis do + values[i]={ + value=axis[i].default, + } + end + else + for i=1,#instances do + local instance=instances[i] + if cleanname(instance.subfamily)==instancespec then + values=instance.values + break + end + end + end + if values then + local factors={} + for i=1,#axis do + local a=axis[i] + factors[i]=getaxisscale(segments,a.minimum,a.default,a.maximum,values[i].value) + end + return factors + end + local values=axistofactors(hash[instancespec] or instancespec) + if values then + local factors={} + for i=1,#axis do + local a=axis[i] + local d=a.default + factors[i]=getaxisscale(segments,a.minimum,d,a.maximum,values[a.name or a.tag] or d) + end + return factors + end + end +end +local function getscales(regions,factors) + local scales={} + for i=1,#regions do + local region=regions[i] + local s=1 + for j=1,#region do + local axis=region[j] + local f=factors[j] + local start=axis.start + local peak=axis.peak + local stop=axis.stop + if start>peak or peak>stop then + elseif start<0 and stop>0 and peak~=0 then + elseif peak==0 then + elseif fstop then + s=0 + break + elseif fpeak then + s=s*(stop-f)/(stop-peak) + else + end + end + scales[i]=s + end + return scales +end +helpers.getaxisscale=getaxisscale +helpers.getfactors=getfactors +helpers.getscales=getscales +helpers.axistofactors=axistofactors +local function readvariationdata(f,storeoffset,factors) + local position=getposition(f) + setposition(f,storeoffset) + local format=readushort(f) + local regionoffset=storeoffset+readulong(f) + local nofdeltadata=readushort(f) + local deltadata=readcardinaltable(f,nofdeltadata,ulong) + setposition(f,regionoffset) + local nofaxis=readushort(f) + local nofregions=readushort(f) + local regions={} + for i=1,nofregions do + local t={} + for i=1,nofaxis do + t[i]={ + start=read2dot14(f), + peak=read2dot14(f), + stop=read2dot14(f), + } + end + regions[i]=t + end + if factors then + for i=1,nofdeltadata do + setposition(f,storeoffset+deltadata[i]) + local nofdeltasets=readushort(f) + local nofshorts=readushort(f) + local nofregions=readushort(f) + local usedregions={} + local deltas={} + for i=1,nofregions do + usedregions[i]=regions[readushort(f)+1] + end + for i=1,nofdeltasets do + local t=readintegertable(f,nofshorts,short) + for i=nofshorts+1,nofregions do + t[i]=readinteger(f) + end + deltas[i]=t + end + deltadata[i]={ + regions=usedregions, + deltas=deltas, + scales=factors and getscales(usedregions,factors) or nil, + } + end + end + setposition(f,position) + return regions,deltadata +end +helpers.readvariationdata=readvariationdata 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) + if nofcoverage==1 then + return { readushort(f) } + elseif nofcoverage==2 then + return { readushort(f),readushort(f) } + else + return readcardinaltable(f,nofcoverage,ushort) end + elseif nofcoverage==1 then + return { [readushort(f)]=0 } + elseif nofcoverage==2 then + return { [readushort(f)]=0,[readushort(f)]=1 } else + local coverage={} for i=0,nofcoverage-1 do coverage[readushort(f)]=i end + return coverage end elseif coverageformat==2 then local nofranges=readushort(f) + local coverage={} local n=simple and 1 or 0 for i=1,nofranges do local firstindex=readushort(f) @@ -11545,10 +17568,11 @@ local function readcoverage(f,offset,simple) end end end + return coverage else report("unknown coverage format %a ",coverageformat) + return {} end - return coverage end local function readclassdef(f,offset,preset) setposition(f,offset) @@ -11603,34 +17627,150 @@ local function classtocoverage(defs) return list end end -local function readposition(f,format) +local skips={ [0]=0, + 1, + 1, + 2, + 1, + 2, + 2, + 3, + 2, + 2, + 3, + 2, + 3, + 3, + 4, +} +local function readvariation(f,offset) + local p=getposition(f) + setposition(f,offset) + local outer=readushort(f) + local inner=readushort(f) + local format=readushort(f) + setposition(f,p) + if format==0x8000 then + return outer,inner + end +end +local function readposition(f,format,mainoffset,getdelta) if format==0 then - return nil + return false 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 + if format==0x04 then + local h=readshort(f) + if h==0 then + return true + else + return { 0,0,h,0 } + end + end + if format==0x05 then + local x=readshort(f) + local h=readshort(f) + if x==0 and h==0 then + return true + else + return { x,0,h,0 } + end + end + if format==0x44 then + local h=readshort(f) + if getdelta then + local d=readshort(f) + if d>0 then + local outer,inner=readvariation(f,mainoffset+d) + if outer then + h=h+getdelta(outer,inner) + end + end + else + skipshort(f,1) + end + if h==0 then + return true + else + return { 0,0,h,0 } + end + end + local x=band(format,0x1)~=0 and readshort(f) or 0 + local y=band(format,0x2)~=0 and readshort(f) or 0 + local h=band(format,0x4)~=0 and readshort(f) or 0 + local v=band(format,0x8)~=0 and readshort(f) or 0 + if format>=0x10 then + local X=band(format,0x10)~=0 and skipshort(f) or 0 + local Y=band(format,0x20)~=0 and skipshort(f) or 0 + local H=band(format,0x40)~=0 and skipshort(f) or 0 + local V=band(format,0x80)~=0 and skipshort(f) or 0 + local s=skips[extract(format,4,4)] + if s>0 then + skipshort(f,s) + end + if getdelta then + if X>0 then + local outer,inner=readvariation(f,mainoffset+X) + if outer then + x=x+getdelta(outer,inner) + end + end + if Y>0 then + local outer,inner=readvariation(f,mainoffset+Y) + if outer then + y=y+getdelta(outer,inner) + end + end + if H>0 then + local outer,inner=readvariation(f,mainoffset+H) + if outer then + h=h+getdelta(outer,inner) + end + end + if V>0 then + local outer,inner=readvariation(f,mainoffset+V) + if outer then + v=v+getdelta(outer,inner) + end + end + end + return { x,y,h,v } + elseif x==0 and y==0 and h==0 and v==0 then + return true else return { x,y,h,v } end end -local function readanchor(f,offset) +local function readanchor(f,offset,getdelta) 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 + local format=readshort(f) + local x=readshort(f) + local y=readshort(f) + if format==3 then + if getdelta then + local X=readshort(f) + local Y=readshort(f) + if X>0 then + local outer,inner=readvariation(f,offset+X) + if outer then + x=x+getdelta(outer,inner) + end + end + if Y>0 then + local outer,inner=readvariation(f,offset+Y) + if outer then + y=y+getdelta(outer,inner) + end + end + else + skipshort(f,2) + end + return { x,y } + else + return { x,y } end - return { readshort(f),readshort(f) } end local function readfirst(f,offset) if offset then @@ -11638,23 +17778,15 @@ local function readfirst(f,offset) end return { readushort(f) } end -local function readarray(f,offset,first) +function readarray(f,offset) 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 + if n==1 then + return { readushort(f) },1 elseif n>0 then - local t={} - for i=1,n do - t[i]=readushort(f) - end - return t,n + return readcardinaltable(f,n,ushort),n end end local function readcoveragearray(f,offset,t,simple) @@ -11695,7 +17827,13 @@ local function readlookuparray(f,noflookups,nofcurrent) if index>length then length=index end - lookups[index]=readushort(f)+1 + local lookup=readushort(f)+1 + local list=lookups[index] + if list then + list[#list+1]=lookup + else + lookups[index]={ lookup } + end end for index=1,length do if not lookups[index] then @@ -12001,10 +18139,7 @@ function gsubhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofg 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 replacements=readcardinaltable(f,nofreplacements,ushort) local coverage=readcoverage(f,tableoffset+coverage) for index,newindex in next,coverage do newindex=newindex+1 @@ -12029,18 +18164,10 @@ local function sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp if subtype==1 then local coverage=readushort(f) local nofsequence=readushort(f) - local sequences={} - for i=1,nofsequence do - sequences[i]=readushort(f) - end + local sequences=readcardinaltable(f,nofsequence,ushort) 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 + sequences[i]=readcardinaltable(f,readushort(f),ushort) end local coverage=readcoverage(f,tableoffset+coverage) for index,newindex in next,coverage do @@ -12072,19 +18199,20 @@ function gsubhandlers.ligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,no if subtype==1 then local coverage=readushort(f) local nofsets=readushort(f) - local ligatures={} - for i=1,nofsets do - ligatures[i]=readushort(f) - end + local ligatures=readcardinaltable(f,nofsets,ushort) 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) + if n==1 then + ligatures[i]={ offset+readushort(f) } + else + local l={} + for i=1,n do + l[i]=offset+readushort(f) + end + ligatures[i]=l end - ligatures[i]=l end local coverage=readcoverage(f,tableoffset+coverage) for index,newindex in next,coverage do @@ -12138,32 +18266,35 @@ function gsubhandlers.reversechainedcontextsingle(f,fontdata,lookupid,lookupoffs before=readcoveragearray(f,tableoffset,before,true) after=readcoveragearray(f,tableoffset,after,true) return { - coverage={ - format="reversecoverage", - before=before, - current=current, - after=after, - replacements=replacements, + format="reversecoverage", + rules={ + { + 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 function readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta) local done={} for i=1,#sets do local offset=sets[i] local reused=done[offset] if not reused then - setposition(f,tableoffset+offset) + offset=tableoffset+offset + setposition(f,offset) local n=readushort(f) reused={} for i=1,n do reused[i]={ readushort(f), - readposition(f,format1), - readposition(f,format2) + readposition(f,format1,offset,getdelta), + readposition(f,format2,offset,getdelta), } end done[offset]=reused @@ -12172,14 +18303,14 @@ local function readpairsets(f,tableoffset,sets,format1,format2) end return sets end -local function readpairclasssets(f,nofclasses1,nofclasses2,format1,format2) +local function readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,mainoffset,getdelta) 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) + local one=readposition(f,format1,mainoffset,getdelta) + local two=readposition(f,format2,mainoffset,getdelta) if one or two then classlist2[j]={ one,two } else @@ -12193,33 +18324,34 @@ function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofg local tableoffset=lookupoffset+offset setposition(f,tableoffset) local subtype=readushort(f) + local getdelta=fontdata.temporary.getdelta if subtype==1 then local coverage=readushort(f) local format=readushort(f) - local value=readposition(f,format) + local value=readposition(f,format,tableoffset,getdelta) local coverage=readcoverage(f,tableoffset+coverage) for index,newindex in next,coverage do - coverage[index]=value + coverage[index]=value end return { - format="pair", - coverage=coverage + format="single", + coverage=coverage, } elseif subtype==2 then local coverage=readushort(f) local format=readushort(f) - local values={} local nofvalues=readushort(f) + local values={} for i=1,nofvalues do - values[i]=readposition(f,format) + values[i]=readposition(f,format,tableoffset,getdelta) 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 + format="single", + coverage=coverage, } else report("unsupported subtype %a in %a positioning",subtype,"single") @@ -12229,12 +18361,13 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly local tableoffset=lookupoffset+offset setposition(f,tableoffset) local subtype=readushort(f) + local getdelta=fontdata.temporary.getdelta 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) + sets=readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta) coverage=readcoverage(f,tableoffset+coverage) for index,newindex in next,coverage do local set=sets[newindex+1] @@ -12246,9 +18379,9 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly local first=value[2] local second=value[3] if first or second then - hash[other]={ first,second } + hash[other]={ first,second or nil } else - hash[other]=nil + hash[other]=nil end end end @@ -12256,7 +18389,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly end return { format="pair", - coverage=coverage + coverage=coverage, } elseif subtype==2 then local coverage=readushort(f) @@ -12266,7 +18399,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly local classdef2=readushort(f) local nofclasses1=readushort(f) local nofclasses2=readushort(f) - local classlist=readpairclasssets(f,nofclasses1,nofclasses2,format1,format2) + local classlist=readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,tableoffset,getdelta) coverage=readcoverage(f,tableoffset+coverage) classdef1=readclassdef(f,tableoffset+classdef1,coverage) classdef2=readclassdef(f,tableoffset+classdef2,nofglyphs) @@ -12282,7 +18415,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly local first=offsets[1] local second=offsets[2] if first or second then - hash[paired]={ first,second } + hash[paired]={ first,second or nil } else end end @@ -12293,7 +18426,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly end return { format="pair", - coverage=usedcoverage + coverage=usedcoverage, } elseif subtype==3 then report("yet unsupported subtype %a in %a positioning",subtype,"pair") @@ -12305,6 +18438,7 @@ function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nof local tableoffset=lookupoffset+offset setposition(f,tableoffset) local subtype=readushort(f) + local getdelta=fontdata.temporary.getdelta if subtype==1 then local coverage=tableoffset+readushort(f) local nofrecords=readushort(f) @@ -12313,24 +18447,27 @@ function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nof 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, + entry~=0 and (tableoffset+entry) or false, + exit~=0 and (tableoffset+exit ) or nil, } end + local cc=(fontdata.temporary.cursivecount or 0)+1 + fontdata.temporary.cursivecount=cc + cc="cc-"..cc 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, + cc, + readanchor(f,r[1],getdelta) or false, + readanchor(f,r[2],getdelta) or nil, } end for index,newindex in next,coverage do coverage[index]=records[newindex+1] end return { - coverage=coverage + coverage=coverage, } else report("unsupported subtype %a in %a positioning",subtype,"cursive") @@ -12340,6 +18477,7 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp local tableoffset=lookupoffset+offset setposition(f,tableoffset) local subtype=readushort(f) + local getdelta=fontdata.temporary.getdelta if subtype==1 then local markcoverage=tableoffset+readushort(f) local basecoverage=tableoffset+readushort(f) @@ -12366,7 +18504,7 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp for i=1,nofmarkclasses do local mc=markclasses[i] if mc then - mc[2]=readanchor(f,mc[2]) + mc[2]=readanchor(f,mc[2],getdelta) end end setposition(f,baseoffset) @@ -12414,7 +18552,7 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp local classes=components[c] if classes then for i=1,nofclasses do - local anchor=readanchor(f,classes[i]) + local anchor=readanchor(f,classes[i],getdelta) local bclass=baseclasses[i] local bentry=bclass[b] if bentry then @@ -12456,7 +18594,7 @@ local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyp local r=baserecords[i] local b=basecoverage[i] for j=1,nofclasses do - baseclasses[j][b]=readanchor(f,r[j]) + baseclasses[j][b]=readanchor(f,r[j],getdelta) end end for index,newindex in next,markcoverage do @@ -12499,7 +18637,7 @@ do setposition(f,offset) local designsize=readushort(f) if designsize>0 then - local fontstyle=readushort(f) + local fontstyleid=readushort(f) local guimenuid=readushort(f) local minsize=readushort(f) local maxsize=readushort(f) @@ -12651,10 +18789,10 @@ do local parameters=readushort(f) local noflookups=readushort(f) if noflookups>0 then - local lookups={} + local lookups=readcardinaltable(f,noflookups,ushort) feature.lookups=lookups for j=1,noflookups do - lookups[j]=readushort(f)+1 + lookups[j]=lookups[j]+1 end end if parameters>0 then @@ -12669,14 +18807,11 @@ do 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 + local lookups=readcardinaltable(f,noflookups,ushort) for lookupid=1,noflookups do - local index=lookups[lookupid] - setposition(f,lookupoffset+index) + local offset=lookups[lookupid] + setposition(f,lookupoffset+offset) local subtables={} local typebits=readushort(f) local flagbits=readushort(f) @@ -12684,10 +18819,9 @@ do local lookupflags=lookupflags[flagbits] local nofsubtables=readushort(f) for j=1,nofsubtables do - local offset=readushort(f) - subtables[j]=offset+index + subtables[j]=offset+readushort(f) end - local markclass=bittest(flagbits,0x0010) + local markclass=band(flagbits,0x0010)~=0 if markclass then markclass=readushort(f) end @@ -12707,21 +18841,9 @@ do 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 function resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset) + local sequences=fontdata.sequences or {} local sublookuplist=fontdata.sublookups or {} fontdata.sequences=sequences fontdata.sublookups=sublookuplist @@ -12736,6 +18858,7 @@ do local nofglyphs=fontdata.nofglyphs or #glyphs local noflookups=#lookups local lookupprefix=sub(what,2,2) + local usedlookups=false for lookupid=1,noflookups do local lookup=lookups[lookupid] local lookuptype=lookup.type @@ -12775,6 +18898,7 @@ do local before=rule.before local current=rule.current local after=rule.after + local replacements=rule.replacements if before then for i=1,#before do before[i]=tohash(before[i]) @@ -12782,15 +18906,41 @@ do rule.before=reversed(before) end if current then - for i=1,#current do - current[i]=tohash(current[i]) + if replacements then + local first=current[1] + local hash={} + local repl={} + for i=1,#first do + local c=first[i] + hash[c]=true + repl[c]=replacements[i] + end + rule.current={ hash } + rule.replacements=repl + else + for i=1,#current do + current[i]=tohash(current[i]) + end end + else end if after then for i=1,#after do after[i]=tohash(after[i]) end end + if usedlookups then + local lookups=rule.lookups + if lookups then + for k,v in next,lookups do + if v then + for k,v in next,v do + usedlookups[v]=usedlookups[v]+1 + end + end + end + end + end end end end @@ -12837,6 +18987,9 @@ do report("no handler for lookup %a with type %a",lookupid,lookuptype) end end + if usedlookups then + report("used %s lookups: % t",what,sortedkeys(usedlookups)) + end local reported={} local function report_issue(i,what,sequence,kind) local name=sequence.name @@ -12863,42 +19016,55 @@ do 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 + local lookuplist=rlookups[index] + if lookuplist then + local length=#lookuplist + local found={} + local noffound=0 + for index=1,length do + local lookupid=lookuplist[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 + local l={ + index=nofsublookups, + name=f_lookupname(lookupprefix,"d",lookupid+lookupidoffset), + derived=true, + steps=d.steps, + nofsteps=d.nofsteps, + type=d.lookuptype or "gsub_single", + markclass=d.markclass or nil, + flags=d.flags, + } + sublookuplist[nofsublookups]=copy(l) + 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 - report_issue(i,what,sequence,"missing") - rule.lookups=nil - break + sublookupcheck[lookupid]=sublookupcheck[lookupid]+1 + end + if h then + noffound=noffound+1 + found[noffound]=h 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 + rlookups[index]=noffound>0 and found or false else rlookups[index]=false end @@ -12917,33 +19083,108 @@ do 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 + local function loadvariations(f,fontdata,variationsoffset,lookuptypes,featurehash,featureorder) + setposition(f,variationsoffset) + local version=readulong(f) + local nofrecords=readulong(f) + local records={} + for i=1,nofrecords do + records[i]={ + conditions=readulong(f), + substitutions=readulong(f), + } end - local scriptoffset,featureoffset,lookupoffset=readscriptoffsets(f,fontdata,tableoffset) - if not scriptoffset then - return + for i=1,nofrecords do + local record=records[i] + local offset=record.conditions + if offset==0 then + record.condition=nil + record.matchtype="always" + else + local offset=variationsoffset+offset + setposition(f,offset) + local nofconditions=readushort(f) + local conditions={} + for i=1,nofconditions do + conditions[i]=offset+readulong(f) + end + record.conditions=conditions + record.matchtype="condition" + end 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 } + for i=1,nofrecords do + local record=records[i] + if record.matchtype=="condition" then + local conditions=record.conditions + for i=1,#conditions do + setposition(f,conditions[i]) + conditions[i]={ + format=readushort(f), + axis=readushort(f), + minvalue=read2dot14(f), + maxvalue=read2dot14(f), + } + end + end end - if not lookupstoo then - return + for i=1,nofrecords do + local record=records[i] + local offset=record.substitutions + if offset==0 then + record.substitutions={} + else + setposition(f,variationsoffset+offset) + local version=readulong(f) + local nofsubstitutions=readushort(f) + local substitutions={} + for i=1,nofsubstitutions do + substitutions[readushort(f)]=readulong(f) + end + for index,alternates in sortedhash(substitutions) do + if index==0 then + record.substitutions=false + else + local tableoffset=variationsoffset+offset+alternates + setposition(f,tableoffset) + local parameters=readulong(f) + local noflookups=readushort(f) + local lookups=readcardinaltable(f,noflookups,ushort) + record.substitutions=lookups + end + end + end end - local lookups=readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder) - if lookups then - resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what) + setvariabledata(fontdata,"features",records) + end + local function readscripts(f,fontdata,what,lookuptypes,lookuphandlers,lookupstoo) + local tableoffset=gotodatatable(f,fontdata,what,true) + if tableoffset then + local version=readulong(f) + local scriptoffset=tableoffset+readushort(f) + local featureoffset=tableoffset+readushort(f) + local lookupoffset=tableoffset+readushort(f) + local variationsoffset=version>0x00010000 and (tableoffset+readulong(f)) or 0 + 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,tableoffset) + end + if variationsoffset>0 then + loadvariations(f,fontdata,variationsoffset,lookuptypes,featurehash,featureorder) + end end end local function checkkerns(f,fontdata,specification) @@ -12962,57 +19203,61 @@ do 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) + if noftables>1 then + report("adding global kern table as gpos feature %a",name) + local kerns=setmetatableindex("table") + for i=1,noftables do + local version=readushort(f) + local length=readushort(f) + local coverage=readushort(f) + local format=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 - elseif format==2 then + 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 - end - local feature={ dflt={ dflt=true } } - if not features then - fontdata.features={ gpos={ [name]=feature } } - elseif not gposfeatures then - fontdata.features.gpos={ [name]=feature } + 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 }, + } else - gposfeatures[name]=feature - end - local sequences=fontdata.sequences - if not sequences then - sequences={} - fontdata.sequences=sequences + report("ignoring empty kern table of feature %a",name) 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 @@ -13029,25 +19274,29 @@ do 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) + if not specification.glyphs then + return + end + local datatable=fontdata.tables.gdef + if datatable then + local tableoffset=datatable.offset + setposition(f,tableoffset) + local version=readulong(f) + local classoffset=readushort(f) + local attachmentoffset=readushort(f) + local ligaturecarets=readushort(f) + local markclassoffset=readushort(f) + local marksetsoffset=version>=0x00010002 and readushort(f) or 0 + local varsetsoffset=version>=0x00010003 and readulong(f) or 0 + local glyphs=fontdata.glyphs + local marks={} + local markclasses=setmetatableindex("table") + local marksets=setmetatableindex("table") + fontdata.marks=marks + fontdata.markclasses=markclasses + fontdata.marksets=marksets + if classoffset~=0 then + setposition(f,tableoffset+classoffset) local classformat=readushort(f) if classformat==1 then local firstindex=readushort(f) @@ -13075,7 +19324,9 @@ function readers.gdef(f,fontdata,specification) end end end - setposition(f,markclassoffset) + end + if markclassoffset~=0 then + setposition(f,tableoffset+markclassoffset) local classformat=readushort(f) if classformat==1 then local firstindex=readushort(f) @@ -13094,21 +19345,45 @@ function readers.gdef(f,fontdata,specification) 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 + if marksetsoffset~=0 then + marksetsoffset=tableoffset+marksetsoffset + setposition(f,marksetsoffset) + local format=readushort(f) + if format==1 then + local nofsets=readushort(f) + local sets=readcardinaltable(f,nofsets,ulong) + for i=1,nofsets do + local offset=sets[i] + if offset~=0 then + marksets[i]=readcoverage(f,marksetsoffset+offset) + end + end + end + end + local factors=specification.factors + if (specification.variable or factors) and varsetsoffset~=0 then + local regions,deltas=readvariationdata(f,tableoffset+varsetsoffset,factors) + if factors then + fontdata.temporary.getdelta=function(outer,inner) + local delta=deltas[outer+1] + if delta then + local d=delta.deltas[inner+1] + if d then + local scales=delta.scales + local dd=0 + for i=1,#scales do + local di=d[i] + if di then + dd=dd+scales[i]*di + else + break + end + end + return round(dd) end end + return 0 end end end @@ -13299,14 +19574,8 @@ local function readmathvariants(f,fontdata,offset) 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 + local vconstruction=readcardinaltable(f,vnofglyphs,ushort) + local hconstruction=readcardinaltable(f,hnofglyphs,ushort) fontdata.mathconstants.MinConnectorOverlap=minoverlap local function get(offset,coverage,nofglyphs,construction,kvariants,kparts,kitalic) if coverage~=0 and nofglyphs>0 then @@ -13355,7 +19624,7 @@ local function readmathvariants(f,fontdata,offset) advance=readushort(f), } local flags=readushort(f) - if bittest(flags,0x0001) then + if band(flags,0x0001)~=0 then p.extender=1 end parts[i]=p @@ -13380,167 +19649,501 @@ local function readmathvariants(f,fontdata,offset) 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) + local tableoffset=gotodatatable(f,fontdata,"math",specification.glyphs) + if tableoffset then + local version=readulong(f) + 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 +function readers.colr(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"colr",specification.glyphs) + if tableoffset then + 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 - if variants~=0 then - readmathvariants(f,fontdata,tableoffset+variants) + glyphs[glyphindex].colors=t + end + end + fontdata.hascolor=true +end +function readers.cpal(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"cpal",specification.glyphs) + if tableoffset then + local version=readushort(f) + local nofpaletteentries=readushort(f) + local nofpalettes=readushort(f) + local nofcolorrecords=readushort(f) + local firstcoloroffset=readulong(f) + local colorrecords={} + local palettes=readcardinaltable(f,nofpalettes,ushort) + 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 -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 +function readers.svg(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"svg",specification.glyphs) + if tableoffset then + local version=readushort(f) + 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 +function readers.sbix(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"sbix",specification.glyphs) + if tableoffset then + local version=readushort(f) + local flags=readushort(f) + local nofstrikes=readulong(f) + local strikes={} + local nofglyphs=fontdata.nofglyphs + for i=1,nofstrikes do + strikes[i]=readulong(f) + end + local shapes={} + local done=0 + for i=1,nofstrikes do + local strikeoffset=strikes[i]+tableoffset + setposition(f,strikeoffset) + strikes[i]={ + ppem=readushort(f), + ppi=readushort(f), + offset=strikeoffset + } + end + sort(strikes,function(a,b) + if b.ppem==a.ppem then + return b.ppi0 then + setposition(f,strikeoffset+glyphoffset) + shapes[i]={ + x=readshort(f), + y=readshort(f), + tag=readtag(f), + data=readstring(f,datasize-8), + ppem=strikeppem, + ppi=strikeppi, + } + done=done+1 + if done==nofglyphs then + break + end + end + end + glyphoffset=nextoffset + end 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={} + fontdata.sbixshapes=shapes + end +end +function readers.stat(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"stat",true) + if tableoffset then + local extras=fontdata.extras + local version=readulong(f) + local axissize=readushort(f) + local nofaxis=readushort(f) + local axisoffset=readulong(f) + local nofvalues=readushort(f) + local valuesoffset=readulong(f) + local fallbackname=extras[readushort(f)] + local axis={} + local values={} + setposition(f,tableoffset+axisoffset) + for i=1,nofaxis do + local tag=readtag(f) + axis[i]={ + tag=tag, + name=lower(extras[readushort(f)] or tag), + ordering=readushort(f), + variants={} + } + end + setposition(f,tableoffset+valuesoffset) + for i=1,nofvalues do + values[i]=readushort(f) + end + for i=1,nofvalues do + setposition(f,tableoffset+valuesoffset+values[i]) + local format=readushort(f) + local index=readushort(f)+1 + local flags=readushort(f) + local name=lower(extras[readushort(f)] or "no name") + local value=readfixed(f) + local variant + if format==1 then + variant={ + flags=flags, + name=name, + value=value, + } + elseif format==2 then + variant={ + flags=flags, + name=name, + value=value, + minimum=readfixed(f), + maximum=readfixed(f), + } + elseif format==3 then + variant={ + flags=flags, + name=name, + value=value, + link=readfixed(f), + } 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 + insert(axis[index].variants,variant) + end + sort(axis,function(a,b) + return a.ordering=lastto then + else + values[#values+1]={ f,t } + lastfrom,lastto=f,t + end + end + nofvalues=#values + if nofvalues>2 then + local some=values[1] + if some[1]==-1 and some[2]==-1 then + some=values[nofvalues] + if some[1]==1 and some[2]==1 then + for i=2,nofvalues-1 do + some=values[i] + if some[1]==0 and some[2]==0 then + return values + end + end end end - layerrecords[i]={ - slot=slot, - class=class, + end + return false + end + local majorversion=readushort(f) + local minorversion=readushort(f) + local reserved=readushort(f) + local nofaxis=readushort(f) + local segments={} + for i=1,nofaxis do + segments[i]=collect() + end + setvariabledata(fontdata,"segments",segments) + end +end +function readers.fvar(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"fvar",true) + if tableoffset then + local version=readulong(f) + local offsettoaxis=tableoffset+readushort(f) + local reserved=skipshort(f) + local nofaxis=readushort(f) + local sizeofaxis=readushort(f) + local nofinstances=readushort(f) + local sizeofinstances=readushort(f) + local extras=fontdata.extras + local axis={} + local instances={} + setposition(f,offsettoaxis) + for i=1,nofaxis do + axis[i]={ + tag=readtag(f), + minimum=readfixed(f), + default=readfixed(f), + maximum=readfixed(f), + flags=readushort(f), + name=lower(extras[readushort(f)] or "bad name"), + } + local n=sizeofaxis-20 + if n>0 then + skipbytes(f,n) + elseif n<0 then + end + end + local nofbytes=2+2+2+nofaxis*4 + local readpsname=nofbytes<=sizeofinstances + local skippable=sizeofinstances-nofbytes + for i=1,nofinstances do + local subfamid=readushort(f) + local flags=readushort(f) + local values={} + for i=1,nofaxis do + values[i]={ + axis=axis[i].tag, + value=readfixed(f), } 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 + local psnameid=readpsname and readushort(f) or 0xFFFF + if subfamid==2 or subfamid==17 then + elseif subfamid==0xFFFF then + subfamid=nil + elseif subfamid<=256 or subfamid>=32768 then + subfamid=nil + end + if psnameid==6 then + elseif psnameid==0xFFFF then + psnameid=nil + elseif psnameid<=256 or psnameid>=32768 then + psnameid=nil + end + instances[i]={ + subfamily=extras[subfamid], + psname=psnameid and extras[psnameid] or nil, + values=values, + } + if skippable>0 then + skipbytes(f,skippable) end end - fontdata.hascolor=true + setvariabledata(fontdata,"axis",axis) + setvariabledata(fontdata,"instances",instances) 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] +function readers.hvar(f,fontdata,specification) + local factors=specification.factors + if not factors then + return + end + local tableoffset=gotodatatable(f,fontdata,"hvar",specification.variable) + if not tableoffset then + return + end + local version=readulong(f) + local variationoffset=tableoffset+readulong(f) + local advanceoffset=tableoffset+readulong(f) + local lsboffset=tableoffset+readulong(f) + local rsboffset=tableoffset+readulong(f) + local regions={} + local variations={} + local innerindex={} + local outerindex={} + if variationoffset>0 then + regions,deltas=readvariationdata(f,variationoffset,factors) + end + if not regions then + return + end + if advanceoffset>0 then + setposition(f,advanceoffset) + local format=readushort(f) + local mapcount=readushort(f) + local entrysize=rshift(band(format,0x0030),4)+1 + local nofinnerbits=band(format,0x000F)+1 + local innermask=lshift(1,nofinnerbits)-1 + local readcardinal=read_cardinal[entrysize] + for i=0,mapcount-1 do + local mapdata=readcardinal(f) + outerindex[i]=rshift(mapdata,nofinnerbits) + innerindex[i]=band(mapdata,innermask) + end + setvariabledata(fontdata,"hvarwidths",true) + local glyphs=fontdata.glyphs + for i=0,fontdata.nofglyphs-1 do + local glyph=glyphs[i] + local width=glyph.width + if width then + local outer=outerindex[i] or 0 + local inner=innerindex[i] or i + if outer and inner then + local delta=deltas[outer+1] + if delta then + local d=delta.deltas[inner+1] + if d then + local scales=delta.scales + local deltaw=0 + for i=1,#scales do + local di=d[i] + if di then + deltaw=deltaw+scales[i]*di + else + break + end + end + glyph.width=width+round(deltaw) + end + end 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) - } +function readers.vvar(f,fontdata,specification) + if not specification.variable then + return + end +end +function readers.mvar(f,fontdata,specification) + local tableoffset=gotodatatable(f,fontdata,"mvar",specification.variable) + if tableoffset then + local version=readulong(f) + local reserved=skipshort(f,1) + local recordsize=readushort(f) + local nofrecords=readushort(f) + local offsettostore=tableoffset+readushort(f) + local dimensions={} + local factors=specification.factors + if factors then + local regions,deltas=readvariationdata(f,offsettostore,factors) + for i=1,nofrecords do + local tag=readtag(f) + local var=variabletags[tag] + if var then + local outer=readushort(f) + local inner=readushort(f) + local delta=deltas[outer+1] + if delta then + local d=delta.deltas[inner+1] + if d then + local scales=delta.scales + local dd=0 + for i=1,#scales do + dd=dd+scales[i]*d[i] + end + var(fontdata,round(dd)) + end + end + else + skipshort(f,2) + end + if recordsize>8 then + skipbytes(recordsize-8) + end end - fontdata.svgshapes=entries end - fontdata.hascolor=true end end @@ -13563,6 +20166,7 @@ local formatters=string.formatters local sortedkeys=table.sortedkeys local sortedhash=table.sortedhash local tohash=table.tohash +local setmetatableindex=table.setmetatableindex 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 @@ -13573,7 +20177,7 @@ 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 +local check_soft_hyphen=true directives.register("otf.checksofthyphen",function(v) check_soft_hyphen=v end) @@ -14123,7 +20727,7 @@ local function checklookups(fontdata,missing,nofmissing) end end if next(done) then - report("not unicoded: % t",table.sortedkeys(done)) + report("not unicoded: % t",sortedkeys(done)) end end end @@ -14133,7 +20737,6 @@ local function unifymissing(fontdata) 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 @@ -14171,8 +20774,8 @@ local function unifyglyphs(fontdata,usenames) end for index=1,#glyphs do local glyph=glyphs[index] - local unicode=glyph.unicode - if not unicode then + local unicode=glyph.unicode + if not unicode or unicode>=private or (unicode>=0xE000 and unicode<=0xF8FF) or unicode==0xFFFE or unicode==0xFFFF then unicode=private if names then local name=glyph.name or f_private(unicode) @@ -14295,9 +20898,9 @@ function readers.getcomponents(fontdata) end end for i=1,#steps do - local coverage=steps[i].coverage - if coverage then - for k,v in next,coverage do + local c=steps[i].coverage + if c then + for k,v in next,c do traverse(k,k,v) end end @@ -14360,7 +20963,7 @@ function readers.rehash(fontdata,hashmethod) copyduplicates(fontdata) unifymissing(fontdata) else - fontdata.hashmethod="unicode" + fontdata.hashmethod="unicodes" local indices=unifyglyphs(fontdata) unifyresources(fontdata,indices) copyduplicates(fontdata) @@ -14375,10 +20978,10 @@ function readers.checkhash(fontdata) elseif hashmethod=="names" and fontdata.names then unifyresources(fontdata,fontdata.names) copyduplicates(fontdata) - fontdata.hashmethod="unicode" + fontdata.hashmethod="unicodes" fontdata.names=nil else - readers.rehash(fontdata,"unicode") + readers.rehash(fontdata,"unicodes") end end function readers.addunicodetable(fontdata) @@ -14512,13 +21115,14 @@ function readers.pack(data) return nt end end - local function pack_flat(v) - local tag=tabstr_flat(v) + local function pack_normal_cc(v) + local tag=tabstr_normal(v) local ht=h[tag] if ht then c[ht]=c[ht]+1 return ht else + v[1]=0 nt=nt+1 t[nt]=v h[tag]=nt @@ -14526,8 +21130,8 @@ function readers.pack(data) return nt end end - local function pack_boolean(v) - local tag=tabstr_boolean(v) + local function pack_flat(v) + local tag=tabstr_flat(v) local ht=h[tag] if ht then c[ht]=c[ht]+1 @@ -14568,6 +21172,20 @@ function readers.pack(data) 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_final(v) if c[v]<=criterium then return t[v] @@ -14584,6 +21202,22 @@ function readers.pack(data) end end end + local function pack_final_cc(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 @@ -14629,9 +21263,9 @@ function readers.pack(data) end local function packers(pass) if pass==1 then - return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed + return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc else - return pack_final,pack_final,pack_final,pack_final,pack_final + return pack_final,pack_final,pack_final,pack_final,pack_final,pack_final_cc end end local resources=data.resources @@ -14639,6 +21273,7 @@ function readers.pack(data) local sublookups=resources.sublookups local features=resources.features local palettes=resources.colorpalettes + local variable=resources.variabledata local chardata=characters and characters.data local descriptions=data.descriptions or data.glyphs if not descriptions then @@ -14648,7 +21283,7 @@ function readers.pack(data) 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) + local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc=packers(pass) for unicode,description in next,descriptions do local boundingbox=description.boundingbox if boundingbox then @@ -14678,28 +21313,30 @@ function readers.pack(data) if kind=="gpos_pair" then local c=step.coverage if c then - if step.format=="kern" then + if step.format=="pair" then for g1,d1 in next,c do - c[g1]=pack_normal(d1) + for g2,d2 in next,d1 do + local f=d2[1] if f and f~=true then d2[1]=pack_indexed(f) end + local s=d2[2] if s and s~=true then d2[2]=pack_indexed(s) end + end 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 + c[g1]=pack_normal(d1) 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 + if step.format=="single" then for g1,d1 in next,c do - c[g1]=pack_indexed(d1) + if d1 and d1~=true then + c[g1]=pack_indexed(d1) + end end + else + step.coverage=pack_normal(c) end end elseif kind=="gpos_cursive" then @@ -14750,7 +21387,6 @@ function readers.pack(data) 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 @@ -14790,6 +21426,45 @@ function readers.pack(data) end end end + if variable then + local instances=variable.instances + if instances then + for i=1,#instances do + local v=instances[i].values + for j=1,#v do + v[j]=pack_normal(v[j]) + end + end + end + local function packdeltas(main) + if main then + local deltas=main.deltas + if deltas then + for i=1,#deltas do + local di=deltas[i] + local d=di.deltas + for j=1,#d do + d[j]=pack_indexed(d[j]) + end + di.regions=pack_indexed(di.regions) + end + end + local regions=main.regions + if regions then + for i=1,#regions do + local r=regions[i] + for j=1,#r do + r[j]=pack_normal(r[j]) + end + end + end + end + end + packdeltas(variable.global) + packdeltas(variable.horizontal) + packdeltas(variable.vertical) + packdeltas(variable.metrics) + end if not success(1,pass) then return end @@ -14799,7 +21474,7 @@ function readers.pack(data) 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) + local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc=packers(pass) for unicode,description in next,descriptions do local math=description.math if math then @@ -14821,8 +21496,7 @@ function readers.pack(data) if kind=="gpos_pair" then local c=step.coverage if c then - if step.format=="kern" then - else + if step.format=="pair" then for g1,d1 in next,c do for g2,d2 in next,d1 do d1[g2]=pack_normal(d2) @@ -14830,6 +21504,15 @@ function readers.pack(data) 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 + d1[g2]=pack_normal(d2) + end + end + end end local rules=step.rules if rules then @@ -14853,14 +21536,26 @@ function readers.pack(data) if sublookups then packthem(sublookups) end - if not success(2,pass) then + if variable then + local function unpackdeltas(main) + if main then + local regions=main.regions + if regions then + main.regions=pack_normal(regions) + end + end + end + unpackdeltas(variable.global) + unpackdeltas(variable.horizontal) + unpackdeltas(variable.vertical) + unpackdeltas(variable.metrics) 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 pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc=packers(pass) local function packthem(sequences) for i=1,#sequences do local sequence=sequences[i] @@ -14873,13 +21568,19 @@ function readers.pack(data) if kind=="gpos_pair" then local c=step.coverage if c then - if step.format=="kern" then - else + if step.format=="pair" then for g1,d1 in next,c do c[g1]=pack_normal(d1) end end end + elseif kind=="gpos_cursive" then + local c=step.coverage + if c then + for g1,d1 in next,c do + c[g1]=pack_normal_cc(d1) + end + end end end end @@ -14911,6 +21612,7 @@ function readers.unpack(data) local sublookups=resources.sublookups local features=resources.features local palettes=resources.colorpalettes + local variable=resources.variabledata local unpacked={} setmetatable(unpacked,unpacked_mt) for unicode,description in next,descriptions do @@ -14947,20 +21649,26 @@ function readers.unpack(data) local features=sequence.features local flags=sequence.flags local markclass=sequence.markclass + 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 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 + if step.format=="pair" then for g1,d1 in next,c do local tv=tables[d1] if tv then @@ -14977,29 +21685,41 @@ function readers.unpack(data) local s=tables[d2[2]] if s then d2[2]=s end end 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_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 + if step.format=="single" then for g1,d1 in next,c do local tv=tables[d1] if tv then c[g1]=tv end end + else + local tv=tables[c] + if tv then + step.coverage=tv + end end end elseif kind=="gpos_cursive" then local c=step.coverage if c then for g1,d1 in next,c do + local tv=tables[d1] + if tv then + d1=tv + c[g1]=d1 + end 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 @@ -15030,6 +21750,11 @@ function readers.unpack(data) if c then for g1,d1 in next,c do for g2,d2 in next,d1 do + local tv=tables[d2] + if tv then + d2=tv + d1[g2]=d2 + end for g3,d3 in next,d2 do local tv=tables[d2[g3]] if tv then @@ -15095,13 +21820,6 @@ function readers.unpack(data) 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] @@ -15113,19 +21831,6 @@ function readers.unpack(data) 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 @@ -15167,6 +21872,63 @@ function readers.unpack(data) end end end + if variable then + local instances=variable.instances + if instances then + for i=1,#instances do + local v=instances[i].values + for j=1,#v do + local tv=tables[v[j]] + if tv then + v[j]=tv + end + end + end + end + local function unpackdeltas(main) + if main then + local deltas=main.deltas + if deltas then + for i=1,#deltas do + local di=deltas[i] + local d=di.deltas + local r=di.regions + for j=1,#d do + local tv=tables[d[j]] + if tv then + d[j]=tv + end + end + local tv=di.regions + if tv then + di.regions=tv + end + end + end + local regions=main.regions + if regions then + local tv=tables[regions] + if tv then + main.regions=tv + regions=tv + end + for i=1,#regions do + local r=regions[i] + for j=1,#r do + local tv=tables[r[j]] + if tv then + r[j]=tv + end + end + end + end + end + end + unpackdeltas(variable.global) + unpackdeltas(variable.horizontal) + unpackdeltas(variable.vertical) + unpackdeltas(variable.metrics) + end data.tables=nil end end @@ -15205,9 +21967,12 @@ local function mergesteps_1(lookup,strict) 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 + local c=steps[i].coverage + if c then + for k,v in next,c do + if not target[k] then + target[k]=v + end end end end @@ -15216,7 +21981,7 @@ local function mergesteps_1(lookup,strict) lookup.steps={ first } return nofsteps-1 end -local function mergesteps_2(lookup,strict) +local function mergesteps_2(lookup) local steps=lookup.steps local nofsteps=lookup.nofsteps local first=steps[1] @@ -15232,69 +21997,62 @@ local function mergesteps_2(lookup,strict) 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 + local c=steps[i].coverage + if c then + for k,v in next,c do + local tk=target[k] + if tk then + for kk,vv in next,v do + if tk[kk]==nil then + tk[kk]=vv + end end + else + target[k]=v end - else - target[k]=v end end end lookup.nofsteps=1 + lookup.merged=true 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 c=steps[i].coverage + if c then + for k,v in next,c do + local tk=coverage[k] + if tk then + report("quitting merge due to multiple checks") + return nofsteps + else + coverage[k]=v + end + end + end + end + local first=steps[1] + local baseclasses={} + 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) + v[1]=offset+v[1] end end first.baseclasses=baseclasses first.coverage=coverage lookup.nofsteps=1 + lookup.merged=true lookup.steps={ first } return nofsteps-1 end @@ -15321,80 +22079,155 @@ local function mergesteps_4(lookup) 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 + local c=steps[i].coverage + if c then + for k,v in next,c do + local tk=target[k] + if tk then + nested(v,tk) + else + target[k]=v + end + end + end + end + lookup.nofsteps=1 + lookup.steps={ first } + return nofsteps-1 +end +local function mergesteps_5(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 + local hash=nil + for k,v in next,target do + hash=v[1] + break + end + for i=2,nofsteps do + local c=steps[i].coverage + if c then + for k,v in next,c do + local tk=target[k] + if tk then + if not tk[2] then + tk[2]=v[2] + end + if not tk[3] then + tk[3]=v[3] + end + else + target[k]=v + v[1]=hash + end end end end lookup.nofsteps=1 + lookup.merged=true lookup.steps={ first } return nofsteps-1 end local function checkkerns(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 - if d1[1]~=0 or d1[2]~=0 or d1[4]~=0 then + if d1==true then + elseif not d1 then + elseif 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) + local c={} for g1,d1 in next,coverage do - coverage[g1]=d1[3] + if d1 and d1~=true then + c[g1]=d1[3] + end end - step.format="kern" + step.coverage=c + step.format="move" + kerned=kerned+1 end end end + return kerned 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 + local function onlykerns(step) + local coverage=step.coverage + for g1,d1 in next,coverage do + for g2,d2 in next,d1 do + if d2[2] then + return false + else + local v=d2[1] + if v==true then + elseif v and (v[1]~=0 or v[2]~=0 or v[4]~=0) then + return false end end end - if kerns then + end + return coverage + end + for i=1,nofsteps do + local step=steps[i] + if step.format=="pair" then + local coverage=onlykerns(step) + if coverage 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 + local d={} for g2,d2 in next,d1 do - d1[g2]=d2[1][3] + local v=d2[1] + if v==true then + elseif v then + d[g2]=v[3] + end end + coverage[g1]=d end - step.format="kern" + step.format="move" kerned=kerned+1 end end end return kerned end +local compact_pairs=true +local compact_singles=true +local merge_pairs=true +local merge_singles=true +local merge_substitutions=true +local merge_alternates=true +local merge_multiples=true +local merge_ligatures=true +local merge_cursives=true +local merge_marks=true +directives.register("otf.compact.pairs",function(v) compact_pairs=v end) +directives.register("otf.compact.singles",function(v) compact_singles=v end) +directives.register("otf.merge.pairs",function(v) merge_pairs=v end) +directives.register("otf.merge.singles",function(v) merge_singles=v end) +directives.register("otf.merge.substitutions",function(v) merge_substitutions=v end) +directives.register("otf.merge.alternates",function(v) merge_alternates=v end) +directives.register("otf.merge.multiples",function(v) merge_multiples=v end) +directives.register("otf.merge.ligatures",function(v) merge_ligatures=v end) +directives.register("otf.merge.cursives",function(v) merge_cursives=v end) +directives.register("otf.merge.marks",function(v) merge_marks=v end) function readers.compact(data) if not data or data.compacted then return @@ -15411,23 +22244,64 @@ function readers.compact(data) for i=1,#lookups do local lookup=lookups[i] local nofsteps=lookup.nofsteps + local kind=lookup.type 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) + local merg=merged + if kind=="gsub_single" then + if merge_substitutions then + merged=merged+mergesteps_1(lookup) + end + elseif kind=="gsub_alternate" then + if merge_alternates then + merged=merged+mergesteps_1(lookup) + end + elseif kind=="gsub_multiple" then + if merge_multiples then + merged=merged+mergesteps_1(lookup) + end elseif kind=="gsub_ligature" then - merged=merged+mergesteps_4(lookup) + if merge_ligatures then + merged=merged+mergesteps_4(lookup) + end elseif kind=="gpos_single" then - merged=merged+mergesteps_1(lookup,true) - checkkerns(lookup) + if merge_singles then + merged=merged+mergesteps_1(lookup,true) + end + if compact_singles then + kerned=kerned+checkkerns(lookup) + end elseif kind=="gpos_pair" then - merged=merged+mergesteps_2(lookup,true) - kerned=kerned+checkpairs(lookup) + if merge_pairs then + merged=merged+mergesteps_2(lookup) + end + if compact_pairs then + kerned=kerned+checkpairs(lookup) + end elseif kind=="gpos_cursive" then - merged=merged+mergesteps_2(lookup) + if merge_cursives then + merged=merged+mergesteps_5(lookup) + end elseif kind=="gpos_mark2mark" or kind=="gpos_mark2base" or kind=="gpos_mark2ligature" then - merged=merged+mergesteps_3(lookup) + if merge_marks then + merged=merged+mergesteps_3(lookup) + end + end + if merg~=merged then + lookup.merged=true + end + elseif nofsteps==1 then + local kern=kerned + if kind=="gpos_single" then + if compact_singles then + kerned=kerned+checkkerns(lookup) + end + elseif kind=="gpos_pair" then + if compact_pairs then + kerned=kerned+checkpairs(lookup) + end + end + if kern~=kerned then end end end @@ -15444,6 +22318,72 @@ function readers.compact(data) report("%i steps of %i steps turned from pairs into kerns",kerned,allsteps) end end +local function mergesteps(t,k) + if k=="merged" then + local merged={} + for i=1,#t do + local step=t[i] + local coverage=step.coverage + for k in next,coverage do + local m=merged[k] + if m then + m[2]=i + else + merged[k]={ i,i } + end + end + end + t.merged=merged + return merged + end +end +local function checkmerge(sequence) + local steps=sequence.steps + if steps then + setmetatableindex(steps,mergesteps) + end +end +local function checkflags(sequence,resources) + if not sequence.skiphash then + local flags=sequence.flags + if flags then + local skipmark=flags[1] + local skipligature=flags[2] + local skipbase=flags[3] + local markclass=sequence.markclass + local skipsome=skipmark or skipligature or skipbase or markclass or false + if skipsome then + sequence.skiphash=setmetatableindex(function(t,k) + local c=resources.classes[k] + local v=c==skipmark + or (markclass and c=="mark" and not markclass[k]) + or c==skipligature + or c==skipbase + or false + t[k]=v + return v + end) + else + sequence.skiphash=false + end + else + sequence.skiphash=false + end + end +end +local function checksteps(sequence) + local steps=sequence.steps + if steps then + for i=1,#steps do + steps[i].index=i + end + end +end +if fonts.helpers then + fonts.helpers.checkmerge=checkmerge + fonts.helpers.checkflags=checkflags + fonts.helpers.checksteps=checksteps +end function readers.expand(data) if not data or data.expanded then return @@ -15488,6 +22428,7 @@ function readers.expand(data) local sequence=sequences[i] local steps=sequence.steps if steps then + local nofsteps=sequence.nofsteps local kind=sequence.type local markclass=sequence.markclass if markclass then @@ -15498,7 +22439,7 @@ function readers.expand(data) sequence.markclass=markclasses[markclass] end end - for i=1,sequence.nofsteps do + for i=1,nofsteps do local step=steps[i] local baseclasses=step.baseclasses if baseclasses then @@ -15514,13 +22455,14 @@ function readers.expand(data) end local rules=step.rules if rules then - local rulehash={} + local rulehash={ n=0 } local rulesize=0 local coverage={} local lookuptype=sequence.type + local nofrules=#rules step.coverage=coverage - for nofrules=1,#rules do - local rule=rules[nofrules] + for currentrule=1,nofrules do + local rule=rules[currentrule] local current=rule.current local before=rule.before local after=rule.after @@ -15548,21 +22490,26 @@ function readers.expand(data) 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 + for i=1,#lookups do + local lookups=lookups[i] + 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 - else end end end if sequence[1] then - rulesize=rulesize+1 - rulehash[rulesize]={ - nofrules, + sequence.n=#sequence + local ruledata={ + currentrule, lookuptype, sequence, start, @@ -15571,16 +22518,36 @@ function readers.expand(data) replacements, subtype, } - for unic in next,sequence[start] do - local cu=coverage[unic] - if not cu then - coverage[unic]=rulehash + rulesize=rulesize+1 + rulehash[rulesize]=ruledata + rulehash.n=rulesize + if true then + for unic in next,sequence[start] do + local cu=coverage[unic] + if cu then + local n=#cu+1 + cu[n]=ruledata + cu.n=n + else + coverage[unic]={ ruledata,n=1 } + end + end + else + for unic in next,sequence[start] do + local cu=coverage[unic] + if cu then + else + coverage[unic]=rulehash + end end end end end end end + checkmerge(sequence) + checkflags(sequence,resources) + checksteps(sequence) end end end @@ -15600,7 +22567,7 @@ if not modules then modules={} end modules ['font-otl']={ 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 lower=string.lower local type,next,tonumber,tostring,unpack=type,next,tonumber,tostring,unpack local abs=math.abs local derivetable=table.derive @@ -15619,11 +22586,13 @@ local trace_defining=false registertracker("fonts.defining",function(v) trace_de local report_otf=logs.reporter("fonts","otf loading") local fonts=fonts local otf=fonts.handlers.otf -otf.version=3.027 +otf.version=3.104 otf.cache=containers.define("fonts","otl",otf.version,true) otf.svgcache=containers.define("fonts","svg",otf.version,true) +otf.sbixcache=containers.define("fonts","sbix",otf.version,true) otf.pdfcache=containers.define("fonts","pdf",otf.version,true) otf.svgenabled=false +otf.sbixenabled=false local otfreaders=otf.readers local hashes=fonts.hashes local definers=fonts.definers @@ -15637,6 +22606,7 @@ local forceload=false local cleanup=0 local syncspace=true local forcenotdef=false +local privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000 local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes local wildcard="*" local default="dflt" @@ -15648,17 +22618,31 @@ registerdirective("fonts.otf.loader.cleanup",function(v) cleanup=tonumber(v) or 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 +registerotfenhancer("check extra features",function() end) +local checkmemory=utilities.lua and utilities.lua.checkmemory +local threshold=100 +local tracememory=false +registertracker("fonts.otf.loader.memory",function(v) tracememory=v end) +if not checkmemory then + local collectgarbage=collectgarbage + checkmemory=function(previous,threshold) + local current=collectgarbage("count") + if previous then + local checked=(threshold or 64)*1024 + if current-previous>checked then + collectgarbage("collect") + current=collectgarbage("count") + end + end + return current + end +end +function otf.load(filename,sub,instance) local base=file.basename(file.removesuffix(filename)) - local name=file.removesuffix(base) + 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 @@ -15666,27 +22650,10 @@ function otf.load(filename,sub,featurefile) 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 + if instance then + hash=hash.."-"..instance end + hash=containers.cleanname(hash) 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 @@ -15696,10 +22663,15 @@ function otf.load(filename,sub,featurefile) if reload then report_otf("loading %a, hash %a",filename,hash) starttiming(otfreaders) - data=otfreaders.loadfont(filename,sub or 1) + data=otfreaders.loadfont(filename,sub or 1,instance) if data then + local used=checkmemory() local resources=data.resources local svgshapes=resources.svgshapes + local sbixshapes=resources.sbixshapes + if cleanup==0 then + checkmemory(used,threshold,tracememory) + end if svgshapes then resources.svgshapes=nil if otf.svgenabled then @@ -15713,17 +22685,49 @@ function otf.load(filename,sub,featurefile) timestamp=timestamp, } end + if cleanup>1 then + collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) + end + end + if sbixshapes then + resources.sbixshapes=nil + if otf.sbixenabled then + local timestamp=os.date() + containers.write(otf.sbixcache,hash,{ + sbixshapes=sbixshapes, + timestamp=timestamp, + }) + data.properties.sbix={ + hash=hash, + timestamp=timestamp, + } + end + if cleanup>1 then + collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) + end end otfreaders.compact(data) + if cleanup==0 then + checkmemory(used,threshold,tracememory) + end otfreaders.rehash(data,"unicodes") otfreaders.addunicodetable(data) otfreaders.extend(data) + if cleanup==0 then + checkmemory(used,threshold,tracememory) + end 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") + else + checkmemory(used,threshold,tracememory) end stoptiming(otfreaders) if elapsedtime then @@ -15731,10 +22735,14 @@ function otf.load(filename,sub,featurefile) end if cleanup>3 then collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) end data=containers.read(otf.cache,hash) if cleanup>2 then collectgarbage("collect") + else + checkmemory(used,threshold,tracememory) end else data=nil @@ -15750,9 +22758,20 @@ function otf.load(filename,sub,featurefile) otfreaders.addunicodetable(data) otfenhancers.apply(data,filename,data) if applyruntimefixes then - applyruntimefixes(filename,data) + applyruntimefixes(filename,data) end data.metadata.math=data.resources.mathconstants + local classes=data.resources.classes + if not classes then + local descriptions=data.descriptions + classes=setmetatableindex(function(t,k) + local d=descriptions[k] + local v=(d and d.class or "base") or false + t[k]=v + return v + end) + data.resources.classes=classes + end end return data end @@ -15796,7 +22815,7 @@ local function copytotfm(data,cache_id) end if mathspecs then for unicode,character in next,characters do - local d=descriptions[unicode] + local d=descriptions[unicode] local m=d.math if m then local italic=m.italic @@ -15849,6 +22868,7 @@ local function copytotfm(data,cache_id) local fontname=metadata.fontname local fullname=metadata.fullname or fontname local psname=fontname or fullname + local subfont=metadata.subfontindex local units=metadata.units or 1000 if units==0 then units=1000 @@ -15936,6 +22956,8 @@ local function copytotfm(data,cache_id) properties.fullname=fullname properties.psname=psname properties.name=filename or fullname + properties.subfont=subfont + properties.private=properties.private or data.private or privateoffset return { characters=characters, descriptions=descriptions, @@ -15984,7 +23006,8 @@ local function otftotfm(specification) local subindex=specification.subindex local filename=specification.filename local features=specification.features.normal - local rawdata=otf.load(filename,sub,features and features.featurefile) + local instance=specification.instance or (features and features.axis) + local rawdata=otf.load(filename,sub,instance) if rawdata and next(rawdata) then local descriptions=rawdata.descriptions rawdata.lookuphash={} @@ -16027,7 +23050,7 @@ local function checkmathsize(tfmdata,mathsize) local parameters=tfmdata.parameters parameters.scriptpercentage=mathdata.ScriptPercentScaleDown parameters.scriptscriptpercentage=mathdata.ScriptScriptPercentScaleDown - parameters.mathsize=mathsize + parameters.mathsize=mathsize end end registerotffeature { @@ -16209,6 +23232,7 @@ otf.coverup={ multiple=justset, kern=justset, pair=justset, + single=justset, ligature=function(coverage,unicode,ligature) local first=ligature[1] local tree=coverage[first] @@ -16264,6 +23288,7 @@ local otf=fonts.handlers.otf local otffeatures=otf.features local registerotffeature=otffeatures.register otf.defaultbasealternate="none" +local getprivate=fonts.constructors.getprivate local wildcard="*" local default="dflt" local formatters=string.formatters @@ -16297,19 +23322,31 @@ end local function cref(feature,sequence) return formatters["feature %a, type %a, chain lookup %a"](feature,sequence.type,sequence.name) end -local function report_alternate(feature,sequence,descriptions,unicode,replacement,value,comment) - report_prepare("%s: base alternate %s => %s (%S => %S)", - cref(feature,sequence), - gref(descriptions,unicode), - replacement and gref(descriptions,replacement), - value, - comment) -end local function report_substitution(feature,sequence,descriptions,unicode,substitution) - report_prepare("%s: base substitution %s => %S", - cref(feature,sequence), - gref(descriptions,unicode), - gref(descriptions,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", @@ -16342,13 +23379,11 @@ local function registerbasefeature(feature,value) applied[#applied+1]=feature.."="..tostring(value) end local function makefake(tfmdata,name,present) - local resources=tfmdata.resources - local private=resources.private + local private=getprivate(tfmdata) 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 @@ -16414,37 +23449,44 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis if kind=="gsub_single" then for i=1,#steps do for unicode,data in next,steps[i].coverage do - if trace_singles then - report_substitution(feature,sequence,descriptions,unicode,data) - end + 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 - if not changed[unicode] then - local replacement=data[alternate] - if replacement then + local replacement=data[alternate] + if replacement then + if unicode~=replacement then changed[unicode]=replacement - if trace_alternatives then - report_alternate(feature,sequence,descriptions,unicode,replacement,value,"normal") - end - elseif defaultalt=="first" then - replacement=data[1] + 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 - if trace_alternatives then - report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt) - end - elseif defaultalt=="last" then - replacement=data[#data] - 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 + 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 @@ -16491,7 +23533,8 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist if kind=="gpos_pair" then for i=1,#steps do local step=steps[i] - if step.format=="kern" then + local format=step.format + if format=="kern" or format=="move" then for unicode,data in next,steps[i].coverage do local character=characters[unicode] local kerns=character.kerns @@ -16519,9 +23562,11 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist 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 other=kern[2] + if other==true or (not other and not (kerns and kerns[otherunicode])) then local kern=kern[1] - if kern[1]~=0 or kern[2]~=0 or kern[4]~=0 then + if kern==true then + elseif kern[1]~=0 or kern[2]~=0 or kern[4]~=0 then else kern=kern[3] if kern~=0 then @@ -16546,6 +23591,51 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist end local function initializehashes(tfmdata) end +local function checkmathreplacements(tfmdata,fullname,fixitalics) + 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 fixitalics then + local ui=u.italic + if ui and not r.italic then + if trace_preparing then + report_prepare("using %i units of italic correction from %C for %U",ui,unicode,replacement) + end + r.italic=ui + end + end + 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 local starttime=trace_preparing and os.clock() @@ -16562,6 +23652,8 @@ local function featuresinitializer(tfmdata,value) 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 @@ -16582,12 +23674,14 @@ local function featuresinitializer(tfmdata,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 @@ -16595,6 +23689,9 @@ local function featuresinitializer(tfmdata,value) end end end + if substitutionsdone then + checkmathreplacements(tfmdata,fullname,features.fixitalics) + end registerbasehash(tfmdata) end if trace_preparing then @@ -16624,20 +23721,21 @@ if not modules then modules={} end modules ['font-otj']={ license="see context related readme files", } if not nodes.properties then return end -local next,rawget=next,rawget +local next,rawget,tonumber=next,rawget,tonumber local fastcopy=table.fastcopy local registertracker=trackers.register +local registerdirective=directives.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 +local fontmarks=hashes.marks nodes.injections=nodes.injections or {} local injections=nodes.injections local tracers=nodes.tracers @@ -16650,31 +23748,62 @@ 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 getoffsets=nuts.getoffsets 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 setoffsets=nuts.setoffsets +local ischar=nuts.is_char +local getkern=nuts.getkern +local setkern=nuts.setkern +local setlink=nuts.setlink +local setwidth=nuts.setwidth +local getwidth=nuts.getwidth +local nextchar=nuts.traversers.char +local nextglue=nuts.traversers.glue 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 fontkern=nuts.pool and nuts.pool.fontkern +local italickern=nuts.pool and nuts.pool.italickern +local useitalickerns=false +directives.register("fonts.injections.useitalics",function(v) + if v then + report_injections("using italics for space kerns (tracing only)") + end + useitalickerns=v +end) +do if not fontkern then + local thekern=nuts.new("kern",0) + local setkern=nuts.setkern + local copy_node=nuts.copy_node + fontkern=function(k) + local n=copy_node(thekern) + setkern(n,k) + return n + end +end end +do if not italickern then + local thekern=nuts.new("kern",3) + local setkern=nuts.setkern + local copy_node=nuts.copy_node + italickern=function(k) + local n=copy_node(thekern) + setkern(n,k) + return n + end +end end +function injections.installnewkern() end local nofregisteredkerns=0 -local nofregisteredpairs=0 +local nofregisteredpositions=0 local nofregisteredmarks=0 local nofregisteredcursives=0 local keepregisteredcounts=false @@ -16683,7 +23812,7 @@ function injections.keepcounts() end function injections.resetcounts() nofregisteredkerns=0 - nofregisteredpairs=0 + nofregisteredpositions=0 nofregisteredmarks=0 nofregisteredcursives=0 keepregisteredcounts=false @@ -16706,7 +23835,7 @@ function injections.copy(target,source) if tp then tp.injections=si else - propertydata[target]={ + properties[target]={ injections=si, } end @@ -16724,7 +23853,7 @@ function injections.copy(target,source) end end end -function injections.setligaindex(n,index) +function injections.setligaindex(n,index) local p=rawget(properties,n) if p then local i=p.injections @@ -16753,7 +23882,7 @@ function injections.getligaindex(n,default) end return default end -function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) +function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext,r2lflag) local dx=factor*(exit[1]-entry[1]) local dy=-factor*(exit[2]-entry[2]) local ws=tfmstart.width @@ -16806,17 +23935,17 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne 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] +function injections.setposition(kind,current,factor,rlmode,spec,injection) + local x=factor*(spec[1] or 0) + local y=factor*(spec[2] or 0) + local w=factor*(spec[3] or 0) + local h=factor*(spec[4] or 0) 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 + nofregisteredpositions=nofregisteredpositions+1 if rlmode and rlmode<0 then leftkern,rightkern=rightkern,leftkern end @@ -16825,7 +23954,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) end local p=rawget(properties,current) if p then - local i=rawget(p,injection) + local i=p[injection] if i then if leftkern~=0 then i.leftkern=(i.leftkern or 0)+leftkern @@ -16862,7 +23991,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) }, } end - return x,y,w,h,nofregisteredpairs + return x,y,w,h,nofregisteredpositions end end return x,y,w,h @@ -16876,7 +24005,7 @@ function injections.setkern(current,factor,rlmode,x,injection) injection="injections" end if p then - local i=rawget(p,injection) + local i=p[injection] if i then i.leftkern=dx+(i.leftkern or 0) else @@ -16896,6 +24025,54 @@ function injections.setkern(current,factor,rlmode,x,injection) return 0,0 end end +function injections.setmove(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 rlmode and rlmode<0 then + if p then + local i=p[injection] + if i then + i.rightkern=dx+(i.rightkern or 0) + else + p[injection]={ + rightkern=dx, + } + end + else + properties[current]={ + [injection]={ + rightkern=dx, + }, + } + end + else + if p then + local i=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 + 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 @@ -16908,9 +24085,15 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmar if i then if i.markmark then else - i.markx=dx - i.marky=dy - i.markdir=rlmode or 0 + if dx~=0 then + i.markx=dx + end + if y~=0 then + i.marky=dy + end + if rlmode then + i.markdir=rlmode + end i.markbase=nofregisteredmarks i.markbasenode=base i.markmark=mkmk @@ -16953,7 +24136,7 @@ local function show(n,what,nested,symbol) if n then local p=rawget(properties,n) if p then - local i=rawget(p,what) + local i=p[what] if i then local leftkern=i.leftkern or 0 local rightkern=i.rightkern or 0 @@ -16993,15 +24176,16 @@ local function show(n,what,nested,symbol) end local function showsub(n,what,where) report_injections("begin subrun: %s",where) - for n in traverse_id(glyph_code,n) do + for n in nextchar,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) + report_injections() + report_injections("begin run %s: %s kerns, %s positions, %s marks and %s cursives registered", + where or "",nofregisteredkerns,nofregisteredpositions,nofregisteredmarks,nofregisteredcursives) local n=head while n do local id=getid(n) @@ -17035,11 +24219,12 @@ local function show_result(head) 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")) + local w=getwidth(current) + local x,y=getoffsets(current) + report_injections("char: %C, width %p, xoffset %p, yoffset %p",getchar(current),w,x,y) skipping=false elseif id==kern_code then - report_injections("kern: %p",getfield(current,"kern")) + report_injections("kern: %p",getkern(current)) skipping=false elseif not skipping then report_injections() @@ -17047,9 +24232,9 @@ local function show_result(head) end current=getnext(current) end + report_injections() end local function inject_kerns_only(head,where) - head=tonut(head) if trace_injections then trace(head,"kerns") end @@ -17057,7 +24242,6 @@ local function inject_kerns_only(head,where) local prev=nil local next=nil local prevdisc=nil - local prevglyph=nil local pre=nil local post=nil local replace=nil @@ -17065,128 +24249,99 @@ local function inject_kerns_only(head,where) 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 + local char,id=ischar(current) + if char 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 + head=insert_node_before(head,current,fontkern(leftkern)) 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 + 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 + setlink(posttail,fontkern(leftkern)) + done=true 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 + if replace then + local i=p.replaceinjections + if i then + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + setlink(replacetail,fontkern(leftkern)) + done=true end end - if done then - setdisc(prevdisc,pre,post,replace) + else + local i=p.emptyinjections + if i then + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + setfield(prev,"replace",fontkern(leftkern)) + end end end + if done then + setdisc(prevdisc,pre,post,replace) + end end end prevdisc=nil - prevglyph=current + elseif char==false then + prevdisc=nil 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 + for n in nextchar,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 + pre=insert_node_before(pre,n,fontkern(leftkern)) + done=true end end end end end if post then - for n in traverse_char(post) do + for n in nextchar,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 + post=insert_node_before(post,n,fontkern(leftkern)) + done=true end end end end end if replace then - for n in traverse_char(replace) do + for n in nextchar,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 + replace=insert_node_before(replace,n,fontkern(leftkern)) + done=true end end end @@ -17195,10 +24350,8 @@ local function inject_kerns_only(head,where) if done then setdisc(current,pre,post,replace) end - prevglyph=nil prevdisc=current else - prevglyph=nil prevdisc=nil end prev=current @@ -17209,12 +24362,14 @@ local function inject_kerns_only(head,where) else nofregisteredkerns=0 end - return tonode(head),true + if trace_injections then + show_result(head) + end + return head end -local function inject_pairs_only(head,where) - head=tonut(head) +local function inject_positions_only(head,where) if trace_injections then - trace(head,"pairs") + trace(head,"positions") end local current=head local prev=nil @@ -17228,99 +24383,105 @@ local function inject_pairs_only(head,where) 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)) + local char,id=ischar(current) + if char 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 + setoffsets(current,false,yoffset) + end + local leftkern=i.leftkern + local rightkern=i.rightkern + if leftkern and leftkern~=0 then + if rightkern and leftkern==-rightkern then + setoffsets(current,leftkern,false) + rightkern=0 + else + head=insert_node_before(head,current,fontkern(leftkern)) end + end + if rightkern and rightkern~=0 then + insert_node_after(head,current,fontkern(rightkern)) + end + else + local i=p.emptyinjections + if i then 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 + if next and getid(next)==disc_code then + if replace then + else + setfield(next,"replace",fontkern(rightkern)) 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 + 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 + setlink(posttail,fontkern(leftkern)) + done=true 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 + if replace then + local i=p.replaceinjections + if i then + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + setlink(replacetail,fontkern(leftkern)) + done=true end end - if done then - setdisc(prevdisc,pre,post,replace) + else + local i=p.emptyinjections + if i then + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + setfield(prev,"replace",fontkern(leftkern)) + end end end + if done then + setdisc(prevdisc,pre,post,replace) + end end end prevdisc=nil prevglyph=current + elseif char==false then + 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 + for n in nextchar,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) + setoffsets(n,false,yoffset) end local leftkern=i.leftkern if leftkern and leftkern~=0 then - pre=insert_node_before(pre,n,newkern(leftkern)) + pre=insert_node_before(pre,n,fontkern(leftkern)) done=true end local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(pre,n,newkern(rightkern)) + insert_node_after(pre,n,fontkern(rightkern)) done=true end end @@ -17328,23 +24489,23 @@ local function inject_pairs_only(head,where) end end if post then - for n in traverse_char(post) do + for n in nextchar,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) + setoffsets(n,false,yoffset) end local leftkern=i.leftkern if leftkern and leftkern~=0 then - post=insert_node_before(post,n,newkern(leftkern)) + post=insert_node_before(post,n,fontkern(leftkern)) done=true end local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(post,n,newkern(rightkern)) + insert_node_after(post,n,fontkern(rightkern)) done=true end end @@ -17352,23 +24513,23 @@ local function inject_pairs_only(head,where) end end if replace then - for n in traverse_char(replace) do + for n in nextchar,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) + setoffsets(n,false,yoffset) end local leftkern=i.leftkern if leftkern and leftkern~=0 then - replace=insert_node_before(replace,n,newkern(leftkern)) + replace=insert_node_before(replace,n,fontkern(leftkern)) done=true end local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(replace,n,newkern(rightkern)) + insert_node_after(replace,n,fontkern(rightkern)) done=true end end @@ -17383,7 +24544,7 @@ local function inject_pairs_only(head,where) if i then local rightkern=i.rightkern if rightkern and rightkern~=0 then - pre=insert_node_before(pre,pre,newkern(rightkern)) + pre=insert_node_before(pre,pre,fontkern(rightkern)) done=true end end @@ -17396,7 +24557,7 @@ local function inject_pairs_only(head,where) if i then local rightkern=i.rightkern if rightkern and rightkern~=0 then - replace=insert_node_before(replace,replace,newkern(rightkern)) + replace=insert_node_before(replace,replace,fontkern(rightkern)) done=true end end @@ -17418,23 +24579,20 @@ local function inject_pairs_only(head,where) if keepregisteredcounts then keepregisteredcounts=false else - nofregisteredkerns=0 + nofregisteredpositions=0 + end + if trace_injections then + show_result(head) end - return tonode(head),true + return head 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) + local x,y=getoffsets(n) + if x~=0 or y~=0 then + setcolor(n,"darkgray") end end local function inject_everything(head,where) - head=tonut(head) if trace_injections then trace(head,"everything") end @@ -17442,8 +24600,6 @@ local function inject_everything(head,where) 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 @@ -17461,7 +24617,8 @@ local function inject_everything(head,where) local marks={} local nofmarks=0 local function processmark(p,n,pn) - local px=getfield(p,"xoffset") + local px,py=getoffsets(p) + local nx,ny=getoffsets(n) local ox=0 local rightkern=nil local pp=rawget(properties,p) @@ -17471,212 +24628,232 @@ local function inject_everything(head,where) rightkern=pp.rightkern end end + local markdir=pn.markdir if rightkern then - if pn.markdir<0 then - ox=px-pn.markx-rightkern + ox=px-(pn.markx or 0)-rightkern + if markdir and markdir<0 then + if not pn.markmark then + ox=ox+(pn.leftkern or 0) + end else if false then local leftkern=pp.leftkern if leftkern then - ox=px-pn.markx-leftkern - else - ox=px-pn.markx + ox=ox-leftkern end - else - ox=px-pn.markx end end else - ox=px-pn.markx + ox=px-(pn.markx or 0) + if markdir and markdir<0 then + if not pn.markmark then + local leftkern=pn.leftkern + if leftkern then + ox=ox+leftkern + end + end + end if pn.checkmark then - local wn=getfield(n,"width") - if wn~=0 then + local wn=getwidth(n) + if wn and 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)) + insert_node_before(n,n,fontkern(-wn)) + insert_node_after(n,n,fontkern(-wn)) end end end - local oy=getfield(n,"yoffset")+getfield(p,"yoffset")+pn.marky - setfield(n,"xoffset",ox) - setfield(n,"yoffset",oy) + local oy=ny+py+(pn.marky or 0) + if not pn.markmark then + local yoffset=pn.yoffset + if yoffset then + oy=oy+yoffset + end + end + setoffsets(n,ox,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 + local char,id=ischar(current) + if char 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 + setoffsets(current,false,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=0 + maxc=maxc+1 + glyphs[maxc]=cursiveanchor + end + properties[cursiveanchor].cursivedy=i.cursivey + last=current + else + maxc=0 + end + elseif maxc>0 then + local nx,ny=getoffsets(current) + for i=maxc,minc,-1 do + local ti=glyphs[i] + ny=ny+properties[ti].cursivedy + setoffsets(ti,false,ny) + if trace_cursive then + showoffset(ti) end - elseif maxc>0 then - local ny=getfield(current,"yoffset") + end + maxc=0 + cursiveanchor=nil + end + if i.cursiveanchor then + cursiveanchor=current + else + if maxc>0 then + local nx,ny=getoffsets(current) for i=maxc,minc,-1 do local ti=glyphs[i] ny=ny+properties[ti].cursivedy - setfield(ti,"yoffset",ny) + setoffsets(ti,false,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 + cursiveanchor=nil 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 + local leftkern=i.leftkern + local rightkern=i.rightkern + if leftkern and leftkern~=0 then + if rightkern and leftkern==-rightkern then + setoffsets(current,leftkern,false) + rightkern=0 + else + head=insert_node_before(head,current,fontkern(leftkern)) 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 + if rightkern and rightkern~=0 then + insert_node_after(head,current,fontkern(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",fontkern(rightkern)) 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 + 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 + setlink(posttail,fontkern(leftkern)) + done=true 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 + if replace then + local i=p.replaceinjections + if i then + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + setlink(replacetail,fontkern(leftkern)) + done=true end end - if done then - setdisc(prevdisc,pre,post,replace) + else + local i=p.emptyinjections + if i then + local leftkern=i.leftkern + if leftkern and leftkern~=0 then + setfield(prev,"replace",fontkern(leftkern)) + end 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) + if done then + setdisc(prevdisc,pre,post,replace) end - maxc=0 - cursiveanchor=nil end end + else + if hascursives and maxc>0 then + local nx,ny=getoffsets(current) + for i=maxc,minc,-1 do + local ti=glyphs[i] + ny=ny+properties[ti].cursivedy + local xi,yi=getoffsets(ti) + setoffsets(ti,xi,yi+ny) + end + maxc=0 + cursiveanchor=nil + end end prevdisc=nil prevglyph=current + elseif char==false then + 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 + for n in nextchar,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) + setoffsets(n,false,yoffset) end local leftkern=i.leftkern if leftkern and leftkern~=0 then - pre=insert_node_before(pre,n,newkern(leftkern)) + pre=insert_node_before(pre,n,fontkern(leftkern)) done=true end local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(pre,n,newkern(rightkern)) + insert_node_after(pre,n,fontkern(rightkern)) done=true end if hasmarks then local pm=i.markbasenode if pm then - processmark(pm,current,i) + processmark(pm,n,i) end end end @@ -17684,29 +24861,29 @@ local function inject_everything(head,where) end end if post then - for n in traverse_char(post) do + for n in nextchar,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) + setoffsets(n,false,yoffset) end local leftkern=i.leftkern if leftkern and leftkern~=0 then - post=insert_node_before(post,n,newkern(leftkern)) + post=insert_node_before(post,n,fontkern(leftkern)) done=true end local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(post,n,newkern(rightkern)) + insert_node_after(post,n,fontkern(rightkern)) done=true end if hasmarks then local pm=i.markbasenode if pm then - processmark(pm,current,i) + processmark(pm,n,i) end end end @@ -17714,29 +24891,29 @@ local function inject_everything(head,where) end end if replace then - for n in traverse_char(replace) do + for n in nextchar,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) + setoffsets(n,false,yoffset) end local leftkern=i.leftkern if leftkern and leftkern~=0 then - replace=insert_node_before(replace,n,newkern(leftkern)) + replace=insert_node_before(replace,n,fontkern(leftkern)) done=true end local rightkern=i.rightkern if rightkern and rightkern~=0 then - insert_node_after(replace,n,newkern(rightkern)) + insert_node_after(replace,n,fontkern(rightkern)) done=true end if hasmarks then local pm=i.markbasenode if pm then - processmark(pm,current,i) + processmark(pm,n,i) end end end @@ -17751,7 +24928,7 @@ local function inject_everything(head,where) if i then local rightkern=i.rightkern if rightkern and rightkern~=0 then - pre=insert_node_before(pre,pre,newkern(rightkern)) + pre=insert_node_before(pre,pre,fontkern(rightkern)) done=true end end @@ -17764,7 +24941,7 @@ local function inject_everything(head,where) if i then local rightkern=i.rightkern if rightkern and rightkern~=0 then - replace=insert_node_before(replace,replace,newkern(rightkern)) + replace=insert_node_before(replace,replace,fontkern(rightkern)) done=true end end @@ -17784,11 +24961,11 @@ local function inject_everything(head,where) current=next end if hascursives and maxc>0 then - local ny=getfield(last,"yoffset") + local nx,ny=getoffsets(last) for i=maxc,minc,-1 do local ti=glyphs[i] ny=ny+properties[ti].cursivedy - setfield(ti,"yoffset",ny) + setoffsets(ti,false,ny) if trace_cursive then showoffset(ti) end @@ -17808,11 +24985,14 @@ local function inject_everything(head,where) keepregisteredcounts=false else nofregisteredkerns=0 - nofregisteredpairs=0 + nofregisteredpositions=0 nofregisteredmarks=0 nofregisteredcursives=0 end - return tonode(head),true + if trace_injections then + show_result(head) + end + return head end local triggers=false function nodes.injections.setspacekerns(font,sequence) @@ -17824,16 +25004,9 @@ function nodes.injections.setspacekerns(font,sequence) 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 + +--removed + else injections.threshold=0 getthreshold=function(font) @@ -17845,17 +25018,21 @@ else end end injections.getthreshold=getthreshold -function injections.isspace(n,threshold) - if getid(n)==glue_code then - local w=getfield(n,"width") +function injections.isspace(n,threshold,id) + if (id or getid(n))==glue_code then + local w=getwidth(n) if threshold and w>threshold then return 32 end end end +local getspaceboth=getboth +function injections.installgetspaceboth(gb) + getspaceboth=gb or getboth +end local function injectspaces(head) if not triggers then - return head,false + return head end local lastfont=nil local spacekerns=nil @@ -17872,10 +25049,10 @@ local function injectspaces(head) 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) + for n in nextglue,head do + local prev,next=getspaceboth(n) + local prevchar=prev and ischar(prev) + local nextchar=next and ischar(next) if nextchar then local font=getfont(next) local trig=triggers[font] @@ -17901,38 +25078,64 @@ local function injectspaces(head) end end if leftkern then - local old=getfield(n,"width") + local old=getwidth(n) 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) + if useitalickerns then + local lnew=leftkern*factor + local rnew=rightkern*factor + if trace_spaces then + report_spaces("%C [%p + %p + %p] %C",prevchar,lnew,old,rnew,nextchar) + end + head=insert_node_before(head,n,italickern(lnew)) + insert_node_after(head,n,italickern(rnew)) + else + local new=old+(leftkern+rightkern)*factor + if trace_spaces then + report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar) + end + setwidth(n,new) end - setfield(n,"width",new) - leftkern=false + rightkern=false else - local new=old+leftkern*factor - if trace_spaces then - report_spaces("%C [%p -> %p]",prevchar,old,new) + if useitalickerns then + local new=leftkern*factor + if trace_spaces then + report_spaces("%C [%p + %p]",prevchar,old,new) + end + insert_node_after(head,n,italickern(new)) + else + local new=old+leftkern*factor + if trace_spaces then + report_spaces("%C [%p -> %p]",prevchar,old,new) + end + setwidth(n,new) end - setfield(n,"width",new) end end leftkern=false elseif rightkern then - local old=getfield(n,"width") + local old=getwidth(n) if old>threshold then - local new=old+rightkern*factor - if trace_spaces then - report_spaces("[%p -> %p] %C",nextchar,old,new) + if useitalickerns then + local new=rightkern*factor + if trace_spaces then + report_spaces("%C [%p + %p]",nextchar,old,new) + end + insert_node_after(head,n,italickern(new)) + else + local new=old+rightkern*factor + if trace_spaces then + report_spaces("[%p -> %p] %C",nextchar,old,new) + end + setwidth(n,new) end - setfield(n,"width",new) end rightkern=false end end triggers=false - return head,true + return head end function injections.handler(head,where) if triggers then @@ -17943,18 +25146,18 @@ function injections.handler(head,where) report_injections("injection variant %a","everything") end return inject_everything(head,where) - elseif nofregisteredpairs>0 then + elseif nofregisteredpositions>0 then if trace_injections then - report_injections("injection variant %a","pairs") + report_injections("injection variant %a","positions") end - return inject_pairs_only(head,where) + return inject_positions_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 + return head end end @@ -17964,7 +25167,7 @@ do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['font-ota']={ version=1.001, - comment="companion to font-otf.lua (analysing)", + 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" @@ -17982,7 +25185,6 @@ 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 @@ -17992,7 +25194,6 @@ 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 @@ -18008,7 +25209,7 @@ 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={ +local states=allocate { init=s_init, medi=s_medi, med2=s_medi, @@ -18024,7 +25225,7 @@ local states={ blwf=s_blwf, pstf=s_pstf, } -local features={ +local features=allocate { init=s_init, medi=s_medi, med2=s_medi, @@ -18156,7 +25357,7 @@ local function warning(current,what) arab_warned[char]=true end end -local mappers={ +local mappers=allocate { l=s_init, d=s_medi, c=s_medi, @@ -18355,12 +25556,14 @@ 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 logs=logs +local trackers=trackers +local nodes=nodes +local attributes=attributes local fonts=fonts local otf=fonts.handlers.otf -local trace_lookups=false registertracker("otf.lookups",function(v) trace_lookups=v end) +local tracers=nodes.tracers 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) @@ -18374,30 +25577,32 @@ 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_chains=false registertracker("otf.chains",function(v) trace_chains=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 forcediscretionaries=false +local forcepairadvance=false +directives.register("otf.forcediscretionaries",function(v) + forcediscretionaries=v +end) +directives.register("otf.forcepairadvance",function(v) + forcepairadvance=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") +registertracker("otf.substitutions","otf.singles","otf.multiples","otf.alternatives","otf.ligatures") +registertracker("otf.positions","otf.marks","otf.kerns","otf.cursive") +registertracker("otf.actions","otf.substitutions","otf.positions") +registertracker("otf.sample","otf.steps","otf.substitutions","otf.positions","otf.analyzing") +registertracker("otf.sample.silent","otf.steps=silent","otf.substitutions","otf.positions","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 @@ -18417,22 +25622,28 @@ local setchar=nuts.setchar local getdisc=nuts.getdisc local setdisc=nuts.setdisc local setlink=nuts.setlink +local getcomponents=nuts.getcomponents +local setcomponents=nuts.setcomponents +local getdir=nuts.getdir +local getwidth=nuts.getwidth local ischar=nuts.is_char +local usesfont=nuts.uses_font local insert_node_after=nuts.insert_after local copy_node=nuts.copy local copy_node_list=nuts.copy_list +local remove_node=nuts.remove 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 set_components=nuts.set_components +local take_components=nuts.take_components +local count_components=nuts.count_components +local copy_no_components=nuts.copy_no_components +local copy_only_glyphs=nuts.copy_only_glyphs +local setmetatable=setmetatable local setmetatableindex=table.setmetatableindex -local zwnj=0x200C -local zwj=0x200D -local wildcard="*" -local default="dflt" +local nextnode=nuts.traversers.node local nodecodes=nodes.nodecodes local glyphcodes=nodes.glyphcodes local disccodes=nodes.disccodes @@ -18444,21 +25655,20 @@ 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 a_state=attributes.private('state') +local a_noligature=attributes.private("noligature") local injections=nodes.injections local setmark=injections.setmark local setcursive=injections.setcursive local setkern=injections.setkern -local setpair=injections.setpair +local setmove=injections.setmove +local setposition=injections.setposition 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 fontdata=fonts.hashes.identifiers +local fontfeatures=fonts.hashes.features local otffeatures=fonts.constructors.features.otf local registerotffeature=otffeatures.register local onetimemessage=fonts.loggers.onetimemessage or function() end @@ -18468,98 +25678,91 @@ local tfmdata=false local characters=false local descriptions=false local marks=false +local classes=false local currentfont=false local factor=0 local threshold=0 local checkmarks=false +local discs=false +local spaces=false local sweepnode=nil -local sweepprev=nil -local sweepnext=nil -local sweephead={} -local notmatchpre={} -local notmatchpost={} -local notmatchreplace={} +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 checkstep=(tracers and tracers.steppers.check) or function() end +local registerstep=(tracers and tracers.steppers.register) or function() end +local registermessage=(tracers and tracers.steppers.message) or function() end local function logprocess(...) if trace_steps then registermessage(...) + if trace_steps=="silent" then + return + end 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 "-" +local gref do + local f_unicode=formatters["U+%X"] + local f_uniname=formatters["U+%X (%s)"] + local f_unilist=formatters["% t"] + gref=function(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 t={} + for i=1,#n do + local ni=n[i] + if tonumber(ni) then + local di=descriptions[ni] + local nn=di and di.name + if nn then + t[#t+1]=f_uniname(ni,nn) + else + t[#t+1]=f_unicode(ni) + end + end end + return f_unilist(t) + else + return "" end - return f_unilist(num,nam) - else - return "" 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) + end + local merged=sequence.merged and "merged " or "" + if index then + return formatters["feature %a, type %a, %schain lookup %a, index %a"]( + dataset[4],sequence.type,merged,sequence.name,index) else - return formatters["feature %a, type %a, chain lookup %a"](dataset[4],sequence.type,sequence.name) + return formatters["feature %a, type %a, %schain lookup %a"]( + dataset[4],sequence.type,merged,sequence.name) end end local function pref(dataset,sequence) - return formatters["feature %a, type %a, lookup %a"](dataset[4],sequence.type,sequence.name) + return formatters["feature %a, type %a, %slookup %a"]( + dataset[4],sequence.type,sequence.merged and "merged " or "",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 + if not rlmode or rlmode>=0 then 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 + return "r2l" end end local function flattendisk(head,disc) @@ -18583,7 +25786,6 @@ local function flattendisk(head,disc) elseif next then return next,next else - return end else if replace then @@ -18605,15 +25807,34 @@ local function appenddisc(disc,list) if post then setlink(posttail,posthead) else - post=phead + post=posthead end if replace then setlink(replacetail,replacehead) else - replace=rhead + replace=replacehead end setdisc(disc,pre,post,replace) end +local take_components=getcomponents +local set_components=setcomponents +local function count_components(start,marks) + if getid(start)~=glyph_code then + return 0 + elseif getsubtype(start)==ligature_code then + local i=0 + local components=getcomponents(start) + while components do + i=i+count_components(components,marks) + components=getnext(components) + end + return i + elseif not marks[getchar(start)] then + return 1 + else + return 0 + end +end local function markstoligature(head,start,stop,char) if start==stop and getchar(start)==char then return head,start @@ -18622,38 +25843,19 @@ local function markstoligature(head,start,stop,char) local next=getnext(stop) setprev(start) setnext(stop) - local base=copy_glyph(start) + local base=copy_no_components(start,copyinjection) 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) + set_components(base,start) + setlink(prev,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) +local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfound,hasmarks) if getattr(start,a_noligature)==1 then return head,start end @@ -18662,31 +25864,22 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou 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) + local base=copy_no_components(start,copyinjection) 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) + set_components(base,comp) + setlink(prev,base,next) if not discfound then - local deletemarks=markflag~="mark" + local deletemarks=not skiphash or hasmarks local components=start local baseindex=0 local componentindex=0 @@ -18696,14 +25889,14 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou local char=getchar(start) if not marks[char] then baseindex=baseindex+componentindex - componentindex=getcomponentindex(start) - elseif not deletemarks then + componentindex=count_components(start,marks) + 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) + 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)) @@ -18731,27 +25924,14 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou local discprev,discnext=getboth(discfound) if discprev and discnext then local pre,post,replace,pretail,posttail,replacetail=getdisc(discfound,true) - if not replace then + 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) + local comp=take_components(base) + local copied=copy_only_glyphs(comp) if pre then setlink(discprev,pre) + else + setnext(discprev) end pre=comp if post then @@ -18759,19 +25939,24 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou setprev(post) else post=discnext + setprev(discnext) end - setlink(prev,discfound) - setlink(discfound,next) + setlink(prev,discfound,next) setboth(base) - setfield(base,"components",copied) - setdisc(discfound,pre,post,base) - base=prev + set_components(base,copied) + replace=base + if forcediscretionaries then + setdisc(discfound,pre,post,replace,discretionary_code) + else + setdisc(discfound,pre,post,replace) + end + base=prev end end end return head,base end -local function multiple_glyphs(head,start,multiple,ignoremarks,what) +local function multiple_glyphs(head,start,multiple,skiphash,what,stop) local nofmultiples=#multiple if nofmultiples>0 then resetinjection(start) @@ -18807,7 +25992,9 @@ local function multiple_glyphs(head,start,multiple,ignoremarks,what) end local function get_alternative_glyph(start,alternatives,value) local n=#alternatives - if value=="random" then + if n==1 then + return alternatives[1],trace_alternatives and "1 (only one present)" + elseif 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 @@ -18862,20 +26049,20 @@ function handlers.gsub_alternate(head,start,dataset,sequence,alternative) end return head,start,true end -function handlers.gsub_multiple(head,start,dataset,sequence,multiple) +function handlers.gsub_multiple(head,start,dataset,sequence,multiple,rlmode,skiphash) 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]) + return multiple_glyphs(head,start,multiple,skiphash,dataset[1]) end -function handlers.gsub_ligature(head,start,dataset,sequence,ligature) +function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skiphash) 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 + if skiphash and skiphash[startchar] then while current do local char=ischar(current,currentfont) if char then @@ -18905,14 +26092,14 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) else end end - else - local skipmark=sequence.flags[1] + else local discfound=false local lastdisc=nil + local hasmarks=marks[startchar] while current do local char,id=ischar(current,currentfont) if char then - if skipmark and marks[char] then + if skiphash and skiphash[char] then current=getnext(current) else local lg=ligature[char] @@ -18921,6 +26108,9 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) discfound=lastdisc lastdisc=nil end + if marks[char] then + hasmarks=true + end stop=current ligature=lg current=getnext(current) @@ -18931,13 +26121,16 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) elseif char==false then break elseif id==disc_code then - local replace=getfield(current,"replace") + 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 + if marks[char] then + hasmarks=true + end ligature=lg replace=getnext(replace) else @@ -18960,10 +26153,10 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) if stop then if trace_ligatures then local stopchar=getchar(stop) - head,start=toligature(head,start,stop,lig,dataset,sequence,skipmark,discfound) + head,start=toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) 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) + head,start=toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) end else resetinjection(start) @@ -18978,83 +26171,81 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature) end return head,start,false,discfound end -function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,step,i,injection) +function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection) local startchar=getchar(start) - if step.format=="pair" then - local dx,dy,w,h=setpair(start,factor,rlmode,sequence.flags[4],kerns,injection) + local format=step.format + if format=="single" or type(kerns)=="table" then + local dx,dy,w,h=setposition(0,start,factor,rlmode,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) + logprocess("%s: shifting single %s by %s xy (%p,%p) and wh (%p,%p)",pref(dataset,sequence),gref(startchar),format,dx,dy,w,h) end else - local k=setkern(start,factor,rlmode,kerns,injection) + local k=(format=="move" and setmove or setkern)(start,factor,rlmode,kerns,injection) if trace_kerns then - logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(startchar),k) + logprocess("%s: shifting single %s by %s %p",pref(dataset,sequence),gref(startchar),format,k) end end - return head,start,false + return head,start,true end -function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,injection) +function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,skiphash,step,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 + if skiphash and skiphash[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) + else + local krn=kerns[nextchar] + if not krn then + break + end + local format=step.format + if format=="pair" then + local a,b=krn[1],krn[2] + if a==true then + elseif a then + local x,y,w,h=setposition(1,start,factor,rlmode,a,injection) if trace_kerns then - logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(nextchar),k) + local startchar=getchar(start) + logprocess("%s: shifting first of pair %s and %s by xy (%p,%p) and wh (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections") 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") + if b==true then + start=snext + elseif b then + local x,y,w,h=setposition(2,snext,factor,rlmode,b,injection) + if trace_kerns then + local startchar=getchar(start) + logprocess("%s: shifting second of pair %s and %s by xy (%p,%p) and wh (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections") + end + start=snext + elseif forcepairadvance then + start=snext end - end - if b and #b>0 then - local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,injection) + return head,start,true + elseif krn~=0 then + local k=(format=="move" and setmove or setkern)(snext,factor,rlmode,krn,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") + logprocess("%s: inserting %s %p between %s and %s as %s",pref(dataset,sequence),format,k,gref(getchar(prev)),gref(nextchar),injection or "injections") end + return head,start,true + else + break 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 + return head,start,false end end -function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode) +function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode,skiphash) local markchar=getchar(start) if marks[markchar] then local base=getprev(start) @@ -19089,10 +26280,12 @@ function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode) 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) + logprocess("%s, bound %s, anchoring mark %s to basechar %s => (%p,%p)", + pref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) end return head,start,true + elseif trace_bugs then + logwarning("%s: mark %s is not anchored to %s",pref(dataset,sequence),gref(markchar),gref(basechar)) end elseif trace_bugs then logwarning("%s: nothing preceding, case %i",pref(dataset,sequence),1) @@ -19105,7 +26298,7 @@ function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode) end return head,start,false end -function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlmode) +function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlmode,skiphash) local markchar=getchar(start) if marks[markchar] then local base=getprev(start) @@ -19144,8 +26337,8 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm 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) + logprocess("%s, index %s, bound %s, anchoring mark %s to baselig %s at index %s => (%p,%p)", + pref(dataset,sequence),index,bound,gref(markchar),gref(basechar),index,dx,dy) end return head,start,true else @@ -19168,7 +26361,7 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm end return head,start,false end -function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode) +function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode,skiphash) local markchar=getchar(start) if marks[markchar] then local base=getprev(start) @@ -19191,8 +26384,8 @@ function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode) 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) + logprocess("%s, bound %s, anchoring mark %s to basemark %s => (%p,%p)", + pref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) end return head,start,true end @@ -19203,8 +26396,7 @@ function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode) end return head,start,false end -function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,step,i) - local done=false +function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,skiphash,step) local startchar=getchar(start) if marks[startchar] then if trace_cursive then @@ -19212,11 +26404,11 @@ function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,st end else local nxt=getnext(start) - while not done and nxt do + while nxt do local nextchar=ischar(nxt,currentfont) if not nextchar then break - elseif marks[nextchar] then + elseif marks[nextchar] then nxt=getnext(nxt) else local exit=exitanchors[3] @@ -19225,11 +26417,12 @@ function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,st if entry then entry=entry[2] if entry then - local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + local r2lflag=sequence.flags[4] + local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar],r2lflag) 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)) + logprocess("%s: moving %s to %s cursive (%p,%p) using bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,bound,mref(rlmode)) end - done=true + return head,start,true end end end @@ -19237,12 +26430,15 @@ function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,st end end end - return head,start,done + return head,start,false end local chainprocs={} local function logprocess(...) if trace_steps then registermessage(...) + if trace_steps=="silent" then + return + end end report_subchain(...) end @@ -19250,11 +26446,14 @@ local logwarning=report_subchain local function logprocess(...) if trace_steps then registermessage(...) + if trace_steps=="silent" then + return + end end report_chain(...) end local logwarning=report_chain -local function reversesub(head,start,stop,dataset,sequence,replacements,rlmode) +local function reversesub(head,start,stop,dataset,sequence,replacements,rlmode,skiphash) local char=getchar(start) local replacement=replacements[char] if replacement then @@ -19269,509 +26468,551 @@ local function reversesub(head,start,stop,dataset,sequence,replacements,rlmode) end end chainprocs.reversesub=reversesub +local function reportzerosteps(dataset,sequence) + logwarning("%s: no steps",cref(dataset,sequence)) +end 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 function getmapping(dataset,sequence,currentlookup) local steps=currentlookup.steps local nofsteps=currentlookup.nofsteps - if nofsteps>1 then - reportmoresteps(dataset,sequence) + if nofsteps==0 then + reportzerosteps(dataset,sequence) + currentlookup.mapping=false + return false + else + if nofsteps>1 then + reportmoresteps(dataset,sequence) + end + local mapping=steps[1].coverage + currentlookup.mapping=mapping + currentlookup.format=steps[1].format + return mapping 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 +function chainprocs.gsub_remove(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + if trace_chains then + logprocess("%s: removing character %s",cref(dataset,sequence,chainindex),gref(getchar(start))) + end + head,start=remove_node(head,start,true) + return head,getprev(start),true +end +function chainprocs.gsub_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local current=start + while current do + local currentchar=ischar(current) + if currentchar then + local replacement=mapping[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 - 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) + current=getnext(current) 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) +function chainprocs.gsub_alternate(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) 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)) + if mapping then + 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=mapping[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(currentchar),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(currentchar),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 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) +function chainprocs.gsub_multiple(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) 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 + if mapping then + local startchar=getchar(start) + local replacement=mapping[startchar] + if not replacement or replacement=="" then + if trace_bugs then + logwarning("%s: no multiple for %s",cref(dataset,sequence),gref(startchar)) end - return head,start,true - elseif currentchar==false then - break - elseif current==stop then - break else - current=getnext(current) + 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,skiphash,dataset[1],stop) 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) +function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) 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 + if mapping then + local startchar=getchar(start) + local ligatures=mapping[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 hasmarks=marks[startchar] + local current=getnext(start) + local discfound=false + local last=stop + local nofreplacements=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 lg=ligatures[schar] - if lg then - ligatures=lg - last=current - nofreplacements=nofreplacements+1 - if current==stop then - break - else + local schar=getchar(current) + if skiphash and skiphash[schar] then current=getnext(current) - end else - break + local lg=ligatures[schar] + if lg then + ligatures=lg + last=current + nofreplacements=nofreplacements+1 + if marks[char] then + hasmarks=true + end + if current==stop then + break + else + current=getnext(current) + end + else + break + end end end end - end - local ligature=ligatures.ligature - if ligature then - if chainindex then - stop=last - end - if trace_ligatures then + 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,skiphash,discfound,hasmarks) + return head,start,true,nofreplacements,discfound + elseif trace_bugs then if start==stop then - logprocess("%s: replacing character %s by ligature %s case 3",cref(dataset,sequence,chainindex),gref(startchar),gref(ligature)) + logwarning("%s: replacing character %s by ligature fails",cref(dataset,sequence,chainindex),gref(startchar)) 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) + logwarning("%s: replacing character %s upto %s by ligature fails",cref(dataset,sequence,chainindex),gref(startchar),gref(getchar(stop))) + end + end end end - return head,start,false + return head,start,false,0,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) +function chainprocs.gpos_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) end - local snext=getnext(start) - if snext then + if mapping then local startchar=getchar(start) - local step=steps[1] - local kerns=step.coverage[startchar] + local kerns=mapping[startchar] if kerns then - local prev=start - local done=false - while snext do - local nextchar=ischar(snext,currentfont) - if not nextchar then - break + local format=currentlookup.format + if format=="single" then + local dx,dy,w,h=setposition(0,start,factor,rlmode,kerns) + if trace_kerns then + logprocess("%s: shifting single %s by %s (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),format,dx,dy,w,h) 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") + else + local k=(format=="move" and setmove or setkern)(start,factor,rlmode,kerns,injection) + if trace_kerns then + logprocess("%s: shifting single %s by %s %p",cref(dataset,sequence),gref(startchar),format,k) + end + end + return head,start,true + end + end + return head,start,false +end +function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local snext=getnext(start) + if snext then + local startchar=getchar(start) + local kerns=mapping[startchar] + if kerns then + local prev=start + while snext do + local nextchar=ischar(snext,currentfont) + if not nextchar then + break + end + if skiphash and skiphash[nextchar] then + prev=snext + snext=getnext(snext) + else + local krn=kerns[nextchar] + if not krn then + break + end + local format=currentlookup.format + if format=="pair" then + local a,b=krn[1],krn[2] + if a==true then + elseif a then + local x,y,w,h=setposition(1,start,factor,rlmode,a,"injections") + if trace_kerns then + local startchar=getchar(start) + 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==true then + start=snext + elseif b then + local x,y,w,h=setposition(2,snext,factor,rlmode,b,"injections") + if trace_kerns then + local startchar=getchar(start) + 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 + start=snext + elseif forcepairadvance then + start=snext + end + return head,start,true + elseif krn~=0 then + local k=(format=="move" and setmove or setkern)(snext,factor,rlmode,krn) if trace_kerns then - logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k) + logprocess("%s: inserting %s %p between %s and %s",cref(dataset,sequence),format,k,gref(getchar(prev)),gref(nextchar)) end - done=true + return head,start,true + else 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 +function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local markchar=getchar(start) + if marks[markchar] then + local markanchors=mapping[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),1) + logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2) 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) + 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, bound %s, anchoring mark %s to basechar %s => (%p,%p)", + cref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) + end + return head,start,true end - return head,start,true 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),1) + logwarning("%s: prev node is no char, case %i",cref(dataset,sequence),2) end elseif trace_bugs then - logwarning("%s: prev node is no char, case %i",cref(dataset,sequence),2) + logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar)) end elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar)) + logwarning("%s: mark %s is no mark",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 +function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local markchar=getchar(start) + if marks[markchar] then + local markanchors=mapping[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,1) + logwarning("%s: no base for mark %s, case %i",cref(dataset,sequence),markchar,2) 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) + 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, bound %s, anchoring mark %s to baselig %s at index %s => (%p,%p)", + cref(dataset,sequence),a or bound,gref(markchar),gref(basechar),index,dx,dy) + end + return head,start,true 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),1) + logwarning("%s, prev node is no char, case %i",cref(dataset,sequence),2) end elseif trace_bugs then - logwarning("%s, prev node is no char, case %i",cref(dataset,sequence),2) + logwarning("%s, mark %s has no anchors",cref(dataset,sequence),gref(markchar)) end elseif trace_bugs then - logwarning("%s, mark %s has no anchors",cref(dataset,sequence),gref(markchar)) + logwarning("%s, mark %s is no mark",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 +function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) + end + if mapping then + local markchar=getchar(start) + if marks[markchar] then + local markanchors=mapping[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 - 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) + 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, bound %s, anchoring mark %s to basemark %s => (%p,%p)", + cref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy) + end + return head,start,true end - return head,start,true 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),1) + logwarning("%s: prev node is no mark, case %i",cref(dataset,sequence),2) end elseif trace_bugs then - logwarning("%s: prev node is no mark, case %i",cref(dataset,sequence),2) + logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar)) end elseif trace_bugs then - logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar)) + logwarning("%s: mark %s is no mark",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) +function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex) + local mapping=currentlookup.mapping + if mapping==nil then + mapping=getmapping(dataset,sequence,currentlookup) 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 mapping then + local startchar=getchar(start) + local exitanchors=mapping[startchar] + if exitanchors then + 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 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 - 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)) + entry=entry[2] + if entry then + local r2lflag=sequence.flags[4] + local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar],r2lflag) + if trace_cursive then + logprocess("%s: moving %s to %s cursive (%p,%p) using bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,bound,mref(rlmode)) + end + return head,start,true end - done=true - break end + elseif trace_bugs then + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) end - elseif trace_bugs then - onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) + break end - break end end - end - return head,start,done - else - if trace_cursive and trace_details then + elseif 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 + return head,start,false 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 userkern=nuts.pool and nuts.pool.newkern +do if not userkern then + local thekern=nuts.new("kern",1) + local setkern=nuts.setkern + userkern=function(k) + local n=copy_node(thekern) + setkern(n,k) + return n + end +end end local function checked(head) local current=head while current do if getid(current)==glue_code then - local kern=new_kern(getfield(current,"width")) + local kern=userkern(getwidth(current)) if head==current then local next=getnext(current) if next then @@ -19782,8 +27023,7 @@ local function checked(head) current=next else local prev,next=getboth(current) - setlink(prev,kern) - setlink(kern,next) + setlink(prev,kern,next) flush_node(current) current=next end @@ -19799,7 +27039,95 @@ local function setdiscchecked(d,pre,post,replace) 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) +local noflags={ false,false,false,false } +local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck) + local size=ck[5]-ck[4]+1 + local chainlookups=ck[6] + local done=false + if chainlookups then + if size==1 then + local chainlookup=chainlookups[1] + for j=1,#chainlookup do + local chainstep=chainlookup[j] + local chainkind=chainstep.type + local chainproc=chainprocs[chainkind] + if chainproc then + local ok + head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash) + if ok then + done=true + end + else + logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind) + end + end + else + local i=1 + local laststart=start + local nofchainlookups=#chainlookups + while start do + if skiphash then + while start do + local char=ischar(start,currentfont) + if char then + if skiphash and skiphash[char] then + start=getnext(start) + else + break + end + else + break + end + end + end + local chainlookup=chainlookups[i] + if chainlookup then + for j=1,#chainlookup do + local chainstep=chainlookup[j] + local chainkind=chainstep.type + local chainproc=chainprocs[chainkind] + if chainproc then + local ok,n + head,start,ok,n=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,i) + if ok then + done=true + if n and n>1 and i+n>nofchainlookups then + i=size + break + end + end + else + logprocess("%s: %s is not yet supported (2)",cref(dataset,sequence),chainkind) + end + end + else + end + i=i+1 + if i>size or not start then + break + elseif start then + laststart=start + start=getnext(start) + end + end + if not start then + start=laststart + end + end + else + local replacements=ck[7] + if replacements then + head,start,done=reversesub(head,start,last,dataset,sequence,replacements,rlmode,skiphash) + else + done=true + if trace_contexts then + logprocess("%s: skipping match",cref(dataset,sequence)) + end + end + end + return head,start,done +end +local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck) if not start then return head,start,false end @@ -19812,7 +27140,6 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c local sweepnode=sweepnode local sweeptype=sweeptype local sweepoverflow=false - local checkdisc=getprev(head) local keepdisc=not sweepnode local lookaheaddisc=nil local backtrackdisc=nil @@ -19835,18 +27162,22 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c elseif id==disc_code then if keepdisc then keepdisc=false - if notmatchpre[current]~=notmatchreplace[current] then - lookaheaddisc=current - end + lookaheaddisc=current local replace=getfield(current,"replace") - while replace and i<=l do - if getid(replace)==glyph_code then - i=i+1 + if not replace then + sweepoverflow=true + sweepnode=current + current=getnext(current) + else + while replace and i<=l do + if getid(replace)==glyph_code then + i=i+1 + end + replace=getnext(replace) end - replace=getnext(replace) + current=getnext(replace) end last=current - current=getnext(c) else head,current=flattendisk(head,current) end @@ -19904,7 +27235,7 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c if notmatchpre[current]~=notmatchreplace[current] then lookaheaddisc=current end - local replace=getfield(c,"replace") + local replace=getfield(current,"replace") while replace and i1 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 nf then + local discfound + local n=f+1 + last=startnext + while n<=l do + if postreplace and not last then + last=getnext(sweepnode) + sweeptype=nil + end + if last then + local char,id=ischar(last,currentfont) + if char then + if skiphash and skiphash[char] then + skipped=true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + last=getnext(last) + elseif seq[n][char] then + if nl then - break - end - else - notmatchpre[last]=true + else + goto next + end + elseif id==disc_code then + discseen=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 + if n>l then break end - end - if n<=l then + pre=getnext(pre) + else notmatchpre[last]=true + break 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 + notmatchpre[last]=true 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 + if replace then + while replace do + if seq[n][getchar(replace)] then + n=n+1 + if n>l then break end - elseif char==false then - if discfound then - notmatchreplace[discfound]=true - match=not notmatchpost[discfound] + replace=getnext(replace) + else + notmatchreplace[last]=true + if notmatchpre[last] then + goto next 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 + break 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 + if notmatchpre[last] then + goto next end end + last=getnext(last) else - match=false + goto next end else - match=false + goto next 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 f>1 then + if startprev then + local prev=startprev + if prereplace and prev==checkdisc then + prev=getprev(sweepnode) 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 prev then + local discfound + 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 - current=getnext(current) - elseif seq[n][char] then - if n1 then + prev=getprev(prev) + end + n=n-1 + elseif discfound then + notmatchreplace[discfound]=true + if notmatchpost[discfound] then + goto next 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 + goto next end elseif char==false then if discfound then notmatchreplace[discfound]=true - match=not notmatchpre[discfound] + if notmatchpost[discfound] then + goto next + end else - match=false + goto next 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 + discseen=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 or n<1 then + break + else + posttail=getprev(posttail) + end + else + notmatchpost[prev]=true break end - else - notmatchpre[current]=true - break end + if n>=1 then + notmatchpost[prev]=true + end + else + notmatchpost[prev]=true 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 + if replace then + while replacetail do + if seq[n][getchar(replacetail)] then + n=n-1 + if replacetail==replace or n<1 then + break + else + replacetail=getprev(replacetail) + end + else + notmatchreplace[prev]=true + if notmatchpost[prev] then + goto next + else + break + end end - else - notmatchreplace[current]=true - match=notmatchpre[current] - break end + else + notmatchreplace[prev]=true end - if not match then - break - end + end + prev=getprev(prev) + elseif id==glue_code then + local sn=seq[n] + if (sn[32] and spaces[prev]) or sn[0xFFFC] then + n=n-1 + prev=getprev(prev) else + goto next end - current=getnext(current) - elseif seq[n][32] and isspace(current,threshold) then - n=n+1 - current=getnext(current) + elseif seq[n][0xFFFC] then + n=n-1 + prev=getprev(prev) else - match=false - break + goto next end else - match=false - break + goto next end end else - match=false + goto next end + else + goto next 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 + if s>l then + local current=last and getnext(last) + if not current and postreplace then + current=getnext(sweepnode) + end + if current then + local discfound + local n=l+1 + while n<=s do + if current then + local char,id=ischar(current,currentfont) + if char then + if skiphash and skiphash[char] then + skipped=true + if trace_skips then + show_skip(dataset,sequence,char,ck,classes[char]) + end + current=getnext(current) + elseif seq[n][char] then + if n1 and i+n>nofchainlookups then + else + goto next + end + elseif id==disc_code then + discseen=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 + if n>s then + break + else + pre=getnext(pre) + end + else + notmatchpre[current]=true break end end + if n<=s then + notmatchpre[current]=true + end else - logprocess("%s: %s is not yet supported (2)",cref(dataset,sequence),chainkind) + notmatchpre[current]=true end + if replace then + while replace do + if seq[n][getchar(replace)] then + n=n+1 + if n>s then + break + else + replace=getnext(replace) + end + else + notmatchreplace[current]=true + if notmatchpre[current] then + goto next + else + break + end + end + end + else + notmatchreplace[current]=true + end + current=getnext(current) + elseif id==glue_code then + local sn=seq[n] + if (sn[32] and spaces[current]) or sn[0xFFFC] then + n=n+1 + current=getnext(current) + else + goto next + end + elseif seq[n][0xFFFC] then + n=n+1 + current=getnext(current) + else + goto next end - i=i+1 - if i>size or not start then - break - elseif start then - start=getnext(start) - end + else + goto next 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 + goto next end end + if trace_contexts then + chaintrac(head,start,dataset,sequence,rlmode,skipped and skiphash,ck,true,discseen,sweepnode) + end + if discseen or sweepnode then + head,start,done=chaindisk(head,start,dataset,sequence,rlmode,skipped and skiphash,ck) + else + head,start,done=chainrun(head,start,last,dataset,sequence,rlmode,skipped and skiphash,ck) + end + if done then + break + end + ::next:: end - if diskseen then + if discseen then notmatchpre={} notmatchpost={} notmatchreplace={} @@ -20572,13 +27836,18 @@ 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 function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash) 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) + local l=steps[1].coverage[getchar(start)] + if l then + return handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash) + else + return head,start,false + end end chainprocs.gsub_context=chained_contextchain chainprocs.gsub_contextchain=chained_contextchain @@ -20586,22 +27855,17 @@ chainprocs.gsub_reversecontextchain=chained_contextchain chainprocs.gpos_contextchain=chained_contextchain chainprocs.gpos_context=chained_contextchain local missing=setmetatableindex("table") +local logwarning=report_process +local resolved={} local function logprocess(...) if trace_steps then registermessage(...) + if trace_steps=="silent" then + return + end 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 @@ -20610,75 +27874,79 @@ local sequencelists=setmetatableindex(function(t,font) 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 } +do + local autofeatures=fonts.analyzers.features + local featuretypes=otf.tables.featuretypes + local defaultscript=otf.features.checkeddefaultscript + local defaultlanguage=otf.features.checkeddefaultlanguage + local wildcard="*" + local default="dflt" + 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 - else end + return false 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 + 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 - return rl end local function report_disc(what,n) report_run("%s: %s > %s",what,n,languages.serializediscretionary(n)) @@ -20711,13 +27979,12 @@ local function kernrun(disc,k_run,font,attr,...) 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) + setprev(pre) + setlink(prev,disc) end end if post then @@ -20730,7 +27997,7 @@ local function kernrun(disc,k_run,font,attr,...) done=true end setnext(posttail) - setprev(next,disc) + setlink(disc,next) end end if replace then @@ -20738,13 +28005,12 @@ local function kernrun(disc,k_run,font,attr,...) 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) + setprev(replace) + setlink(prev,disc) end if next then setlink(replacetail,next) @@ -20752,19 +28018,21 @@ local function kernrun(disc,k_run,font,attr,...) done=true end setnext(replacetail) - setprev(next,disc) + setlink(disc,next) 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) + setlink(prev,disc,next) + end + if done and trace_testruns then + report_disc("done",disc) end return nextstart,done end -local function comprun(disc,c_run,...) +local function comprun(disc,c_run,...) if trace_compruns then report_disc("comp",disc) end @@ -20800,6 +28068,9 @@ local function comprun(disc,c_run,...) sweepnode=nil sweeptype=nil if renewed then + if trace_testruns then + report_disc("done",disc) + end setdisc(disc,pre,post,replace) end return getnext(disc),renewed @@ -20813,22 +28084,66 @@ local function testrun(disc,t_run,c_run,...) 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 + local renewed=false + if (post or replace) and prev then + if post then + setlink(posttail,next) else - setnext(replacetail) - setprev(next,disc) + post=next + end + if replace then + setlink(replacetail,next) + else + replace=next + end + local d_post=t_run(post,next,...) + local d_replace=t_run(replace,next,...) + if d_post>0 or d_replace>0 then + local d=d_replace>d_post and d_replace or d_post + local head=getnext(disc) + local tail=head + for i=1,d do + local nx=getnext(tail) + local id=getid(nx) + if id==disc_code then + head,tail=flattendisk(head,nx) + elseif id==glyph_code then + tail=nx + else + break + end + end + next=getnext(tail) + setnext(tail) + setprev(head) + local new=copy_node_list(head) + if posttail then + setlink(posttail,head) + else + post=head + end + if replacetail then + setlink(replacetail,new) + else + replace=new + end + else + if posttail then + setnext(posttail) + else + post=nil + end + if replacetail then + setnext(replacetail) + else + replace=nil + end end + setlink(disc,next) + end + if trace_testruns then + report_disc("more",disc) end - local renewed=false if pre then sweepnode=disc sweeptype="pre" @@ -20860,30 +28175,34 @@ local function testrun(disc,t_run,c_run,...) sweeptype=nil if renewed then setdisc(disc,pre,post,replace) - return next,true - else - return next,done + if trace_testruns then + report_disc("done",disc) + end end + return getnext(disc),renewed end local nesting=0 -local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) +local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) local done=false local sweep=sweephead[head] if sweep then start=sweep - sweephead[head]=nil + sweephead[head]=false else start=head end while start do - local char=ischar(start,font) + local char,id=ischar(start,font) if char then - local a=attr and getattr(start,0) + local a + if attr then + a=getattr(start,0) + end 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) + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) if ok then done=true end @@ -20905,46 +28224,91 @@ local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlm return head,done end local function t_run_single(start,stop,font,attr,lookupcache) + local lastd=nil while start~=stop do local char=ischar(start,font) if char then - local a=attr and getattr(start,0) + local a + if attr then + a=getattr(start,0) + end + local startnext=getnext(start) if not a or (a==attr) then local lookupmatch=lookupcache[char] if lookupmatch then - local s=getnext(start) + local s=startnext + local ss=nil + local sstop=s==stop + if not s then + s=ss + ss=nil + end + while getid(s)==disc_code do + ss=getnext(s) + s=getfield(s,"replace") + if not s then + s=ss + ss=nil + end + end 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) + local char=ischar(s,font) + if char then + local lg=lookupmatch[char] + if lg then + if sstop then + d=1 + elseif d>0 then + d=d+1 + end + l=lg + s=getnext(s) + sstop=s==stop + if not s then + s=ss + ss=nil + end + while getid(s)==disc_code do + ss=getnext(s) + s=getfield(s,"replace") + if not s then + s=ss + ss=nil + end + end + else + break + end else break end end - if l and l.ligature then - return true,d>1 + if l and l.ligature then + lastd=d end + else end else end - start=getnext(start) + if lastd then + return lastd + end + start=startnext else break end end + return 0 end -local function k_run_single(sub,injection,last,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) - local a=attr and getattr(sub,0) +local function k_run_single(sub,injection,last,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + local a + if attr then + a=getattr(sub,0) + end if not a or (a==attr) then - for n in traverse_nodes(sub) do + for n in nextnode,sub do if n==last then break end @@ -20952,7 +28316,7 @@ local function k_run_single(sub,injection,last,font,attr,lookupcache,step,datase if char then local lookupmatch=lookupcache[char] if lookupmatch then - local h,d,ok=handler(sub,n,dataset,sequence,lookupmatch,rlmode,step,1,injection) + local h,d,ok=handler(sub,n,dataset,sequence,lookupmatch,rlmode,skiphash,step,injection) if ok then return true end @@ -20961,37 +28325,36 @@ local function k_run_single(sub,injection,last,font,attr,lookupcache,step,datase end end end -local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler) +local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) local done=false local sweep=sweephead[head] if sweep then start=sweep - sweephead[head]=nil + sweephead[head]=false else start=head end while start do local char=ischar(start,font) if char then - local a=attr and getattr(start,0) + local a + if attr then + a=getattr(start,0) + end 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 + local lookupmatch=lookupcache[char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + done=true + break + elseif not start then + break end - else - report_missing_coverage(dataset,sequence) end end if start then @@ -21011,54 +28374,94 @@ local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlm return head,done end local function t_run_multiple(start,stop,font,attr,steps,nofsteps) + local lastd=nil while start~=stop do local char=ischar(start,font) if char then - local a=attr and getattr(start,0) + local a + if attr then + a=getattr(start,0) + end + local startnext=getnext(start) 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)] + local lookupmatch=lookupcache[char] + if lookupmatch then + local s=startnext + local ss=nil + local sstop=s==stop + if not s then + s=ss + ss=nil + end + while getid(s)==disc_code do + ss=getnext(s) + s=getfield(s,"replace") + if not s then + s=ss + ss=nil + end + end + local l=nil + local d=0 + while s do + local char=ischar(s) + if char then + local lg=lookupmatch[char] if lg then + if sstop then + d=1 + elseif d>0 then + d=d+1 + end l=lg s=getnext(s) + sstop=s==stop + if not s then + s=ss + ss=nil + end + while getid(s)==disc_code do + ss=getnext(s) + s=getfield(s,"replace") + if not s then + s=ss + ss=nil + end + end else break end - end - if l and l.ligature then - return true,d>1 + else + break end end - else - report_missing_coverage(dataset,sequence) + if l and l.ligature then + lastd=d + end end end else end - start=getnext(start) + if lastd then + return lastd + end + start=startnext else break end end + return 0 end -local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler) - local a=attr and getattr(sub,0) +local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + local a + if attr then + a=getattr(sub,0) + end if not a or (a==attr) then - for n in traverse_nodes(sub) do + for n in nextnode,sub do if n==last then break end @@ -21067,16 +28470,12 @@ local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,datase 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 + local lookupmatch=lookupcache[char] + if lookupmatch then + local h,d,ok=handler(sub,n,dataset,sequence,lookupmatch,rlmode,skiphash,step,injection) + if ok then + return true end - else - report_missing_coverage(dataset,sequence) end end end @@ -21084,176 +28483,262 @@ local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,datase end end local function txtdirstate(start,stack,top,rlparmode) - local dir=getfield(start,"dir") - local new=1 + local nxt=getnext(start) + local dir=getdir(start) if dir=="+TRT" then top=top+1 stack[top]=dir - new=-1 + return nxt,top,-1 elseif dir=="+TLT" then top=top+1 stack[top]=dir + return nxt,top,1 elseif dir=="-TRT" or dir=="-TLT" then - top=top-1 - if stack[top]=="+TRT" then - new=-1 + if top==1 then + return nxt,0,rlparmode + else + top=top-1 + if stack[top]=="+TRT" then + return nxt,top,-1 + else + return nxt,top,1 + end 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) + return nxt,top,rlparmode end - return getnext(start),top,new end local function pardirstate(start) - local dir=getfield(start,"dir") - local new=0 + local nxt=getnext(start) + local dir=getdir(start) if dir=="TLT" then - new=1 + return nxt,1,1 elseif dir=="TRT" then - new=-1 - end - if trace_directions then - report_process("directions after pardir %a: parmode %a",dir,mref(new)) + return nxt,-1,-1 + else + return nxt,0,0 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 +do + local fastdisc=true + local testdics=false + directives.register("otf.fastdisc",function(v) fastdisc=v end) + local otfdataset=nil + local getfastdisc={ __index=function(t,k) + local v=usesfont(k,currentfont) + t[k]=v + return v + end } + local getfastspace={ __index=function(t,k) + local v=isspace(k,threshold) or false + t[k]=v + return v + end } + function otf.featuresprocessor(head,font,attr,direction,n) + local sequences=sequencelists[font] + nesting=nesting+1 + if nesting==1 then + currentfont=font + tfmdata=fontdata[font] + descriptions=tfmdata.descriptions + characters=tfmdata.characters + local resources=tfmdata.resources + marks=resources.marks + classes=resources.classes + threshold, + factor=getthreshold(font) + checkmarks=tfmdata.properties.checkmarks + if not otfdataset then + otfdataset=otf.dataset + end + discs=fastdisc and n and n>1 and setmetatable({},getfastdisc) + spaces=setmetatable({},getfastspace) + elseif currentfont~=font then + report_warning("nested call with a different font, level %s, quitting",nesting) + nesting=nesting-1 + return head,false + end + if trace_steps then + checkstep(head) + end + local initialrl=direction=="TRT" and -1 or 0 + local datasets=otfdataset(tfmdata,font,attr) + local dirstack={ nil } + sweephead={} + for s=1,#datasets do + local dataset=datasets[s] + local attribute=dataset[2] + local sequence=dataset[3] + local rlparmode=initialrl + local topstack=0 + local typ=sequence.type + local gpossing=typ=="gpos_single" or typ=="gpos_pair" + local forcetestrun=typ=="gsub_ligature" + local handler=handlers[typ] + local steps=sequence.steps + local nofsteps=sequence.nofsteps + local skiphash=sequence.skiphash + if not steps then + local h,ok=handler(head,dataset,sequence,initialrl,font,attr) + if h and h~=head 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 + elseif typ=="gsub_reversecontextchain" then + local start=find_node_tail(head) + local rlmode=0 + local merged=steps.merged + while start do + local char=ischar(start,font) + if char then + local m=merged[char] + if m then + local a + if attr then + a=getattr(start,0) + end + if not a or (a==attr) then + for i=m[1],m[2] do + local step=steps[i] + local lookupcache=step.coverage + local lookupmatch=lookupcache[char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + break + end end end + if start then + start=getprev(start) + end else - report_missing_coverage(dataset,sequence) + start=getprev(start) end - end - if start then + else 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 + else + local start=head + local rlmode=initialrl + if nofsteps==1 then + local step=steps[1] + local lookupcache=step.coverage 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) + if skiphash and skiphash[char] then + start=getnext(start) 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 + local a + if attr then + if getattr(start,0)==attr and (not attribute or getprop(start,a_state)==attribute) then + a=true + end + elseif not attribute or getprop(start,a_state)==attribute then + a=true + end + if a then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if start then + start=getnext(start) + end + else + start=getnext(start) + end + else + start=getnext(start) + end + end + elseif char==false or id==glue_code then + start=getnext(start) + elseif id==disc_code then + if not discs or discs[start]==true then + local ok + if gpossing then + start,ok=kernrun(start,k_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + elseif forcetestrun then + start,ok=testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + else + start,ok=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler) + 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 + else + local merged=steps.merged + while start do + local char,id=ischar(start,font) + if char then + if skiphash and skiphash[char] then + start=getnext(start) + else + local m=merged[char] + if m then + local a + if attr then + if getattr(start,0)==attr and (not attribute or getprop(start,a_state)==attribute) then + a=true + end + elseif not attribute or getprop(start,a_state)==attribute then + a=true + end + if a then + for i=m[1],m[2] do + local step=steps[i] + local lookupcache=step.coverage + local lookupmatch=lookupcache[char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + break + elseif not start then + break + end + end + end + if start then + start=getnext(start) + end + else + start=getnext(start) end - end - if start then + else start=getnext(start) end - else - start=getnext(start) end - elseif char==false then + elseif char==false or id==glue_code 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 + if not discs or discs[start]==true 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) + start,ok=kernrun(start,k_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) + elseif forcetestrun then + start,ok=testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) else - start,ok=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) - end - if ok then - success=true + start,ok=comprun(start,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler) end else start=getnext(start) @@ -21269,109 +28754,128 @@ local function featuresprocessor(head,font,attr) 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 + if trace_steps then + registerstep(head) + end + end + nesting=nesting-1 + return head + end + function otf.datasetpositionprocessor(head,font,direction,dataset) + currentfont=font + tfmdata=fontdata[font] + descriptions=tfmdata.descriptions + characters=tfmdata.characters + local resources=tfmdata.resources + marks=resources.marks + classes=resources.classes + threshold, + factor=getthreshold(font) + checkmarks=tfmdata.properties.checkmarks + if type(dataset)=="number" then + dataset=otfdataset(tfmdata,font,0)[dataset] + end + local sequence=dataset[3] + local typ=sequence.type + local handler=handlers[typ] + local steps=sequence.steps + local nofsteps=sequence.nofsteps + local done=false + local dirstack={ nil } + local start=head + local initialrl=direction=="TRT" and -1 or 0 + local rlmode=initialrl + local rlparmode=initialrl + local topstack=0 + local merged=steps.merged + local position=0 + while start do + local char,id=ischar(start,font) + if char then + position=position+1 + local m=merged[char] + if m then + if skiphash and skiphash[char] then + start=getnext(start) + else + for i=m[1],m[2] do + local step=steps[i] + local lookupcache=step.coverage + local lookupmatch=lookupcache[char] + if lookupmatch then + local ok + head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step) + if ok then + break + elseif not start then + break 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 + if start then 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 + else + start=getnext(start) end + elseif char==false or id==glue_code then + start=getnext(start) + 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 - if success then - done=true - end - if trace_steps then - registerstep(head) - end + return head end - nesting=nesting-1 - head=tonode(head) - return head,done end local plugins={} otf.plugins=plugins +local report=logs.reporter("fonts") function otf.registerplugin(name,f) if type(name)=="string" and type(f)=="function" then plugins[name]={ name,f } + report() + report("plugin %a has been loaded, please be aware of possible side effects",name) + report() + if logs.pushtarget then + logs.pushtarget("log") + end + report("Plugins are not officially supported unless stated otherwise. This is because") + report("they bypass the regular font handling and therefore some features in ConTeXt") + report("(especially those related to fonts) might not work as expected or might not work") + report("at all. Some plugins are for testing and development only and might change") + report("whenever we feel the need for it.") + report() + if logs.poptarget then + logs.poptarget() + end end end -local function plugininitializer(tfmdata,value) +function otf.plugininitializer(tfmdata,value) if type(value)=="string" then tfmdata.shared.plugin=plugins[value] end end -local function pluginprocessor(head,font) +function otf.pluginprocessor(head,font,attr,direction) 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) + return p[2](head,font,attr,direction) else return head,false end end -local function featuresinitializer(tfmdata,value) +function otf.featuresinitializer(tfmdata,value) end registerotffeature { name="features", @@ -21379,53 +28883,87 @@ registerotffeature { default=true, initializers={ position=1, - node=featuresinitializer, - plug=plugininitializer, + node=otf.featuresinitializer, + plug=otf.plugininitializer, }, processors={ - node=featuresprocessor, - plug=pluginprocessor, + node=otf.featuresprocessor, + plug=otf.pluginprocessor, } } -otf.nodemodeinitializer=featuresinitializer -otf.featuresprocessor=featuresprocessor otf.handlers=handlers local setspacekerns=nodes.injections.setspacekerns if not setspacekerns then os.exit() end +local tag="kern" if fontfeatures then - function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr) + function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,attr) local features=fontfeatures[font] - local enabled=features and features.spacekern and features.kern + local enabled=features and features.spacekern and features[tag] if enabled then setspacekerns(font,sequence) end - return head,start,enabled + return head,enabled end else - function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr) + function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,attr) local shared=fontdata[font].shared local features=shared and shared.features - local enabled=features and features.spacekern and features.kern + local enabled=features and features.spacekern and features[tag] if enabled then setspacekerns(font,sequence) end - return head,start,enabled + return head,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 + local resources=data.resources + local sequences=resources.sequences + local validgpos=resources.features.gpos + if validgpos and sequences then + for i=1,#sequences do + local sequence=sequences[i] + local steps=sequence.steps + if steps and sequence.features[tag] then + local kind=sequence.type + if kind=="gpos_pair" or kind=="gpos_single" then + for i=1,#steps do + local step=steps[i] + local coverage=step.coverage + local rules=step.rules + if rules then + elseif not coverage then + elseif kind=="gpos_single" then + elseif kind=="gpos_pair" then + local format=step.format + if format=="move" or format=="kern" then + local kerns=coverage[32] + if kerns then + return true + end + for k,v in next,coverage do + if v[32] then + return true + end + end + elseif format=="pair" then + local kerns=coverage[32] + if kerns then + for k,v in next,kerns do + local one=v[1] + if one and one~=true then + return true + end + end + end + for k,v in next,coverage do + local kern=v[32] + if kern then + local one=kern[1] + if one and one~=true then + return true + end + end + end + end end end end @@ -21443,103 +28981,110 @@ otf.readers.registerextender { 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 + local rawdata=tfmdata.shared and tfmdata.shared.rawdata + local properties=rawdata.properties 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] + local validgpos=resources.features.gpos + if validgpos and sequences then + 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[tag] + if kern then + local kind=sequence.type + if kind=="gpos_pair" or kind=="gpos_single" 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 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] + for i=1,#steps do + local step=steps[i] + local coverage=step.coverage + local rules=step.rules + if rules then + elseif not coverage then + elseif kind=="gpos_single" then + elseif kind=="gpos_pair" then + local format=step.format + if format=="move" or format=="kern" then + local kerns=coverage[32] + if kerns then + for k,v in next,kerns do + right[k]=v + end + end + for k,v in next,coverage do + local kern=v[32] + if kern then + left[k]=kern + end + end + elseif format=="pair" then + local kerns=coverage[32] + if kerns then + for k,v in next,kerns do + local one=v[1] + if one and one~=true then + right[k]=one[3] + end + end + end + for k,v in next,coverage do + local kern=v[32] + if kern then + local one=kern[1] + if one and one~=true then + left[k]=one[3] + end + end end end end end + last=i end + else 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=next(left) and left or false + right=next(right) and right or false + if left or right then + spacekerns={ left=left, right=right, } - insert(sequences,last,triggersequence) + if last>0 then + local triggersequence={ + features={ [tag]=feat or { dflt={ dflt=true,} } }, + flags=noflags, + name="trigger_space_kerns", + order={ tag }, + type="trigger_space_kerns", + left=left, + right=right, + } + insert(sequences,last,triggersequence) + end end - else - spacekerns=false end - else - spacekerns=false end resources.spacekerns=spacekerns end @@ -21579,7 +29124,7 @@ if not modules then modules={} end modules ['font-osd']={ } local insert,imerge,copy=table.insert,table.imerge,table.copy local next,type=next,type -local report_devanagari=logs.reporter("otf","devanagari") +local report=logs.reporter("otf","devanagari") fonts=fonts or {} fonts.analyzers=fonts.analyzers or {} fonts.analyzers.methods=fonts.analyzers.methods or { node={ otf={} } } @@ -21589,8 +29134,6 @@ 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 @@ -21616,6 +29159,9 @@ local fontdata=fonts.hashes.identifiers local a_state=attributes.private('state') local a_syllabe=attributes.private('syllabe') local dotted_circle=0x25CC +local c_nbsp=0x00A0 +local c_zwnj=0x200C +local c_zwj=0x200D local states=fonts.analyzers.states local s_rphf=states.rphf local s_half=states.half @@ -21629,214 +29175,156 @@ replace_all_nbsp=function(head) end return replace_all_nbsp(head) end -local xprocesscharacters=nil +local processcharacters=nil if context then - xprocesscharacters=function(head,font) - xprocesscharacters=nodes.handlers.characters - return xprocesscharacters(head,font) - end + +--removed + 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 + function processcharacters(head,font) + local processors=fontdata[font].shared.processes + for i=1,#processors do + head=processors[i](head,font,0) + end + return head + end +end +local indicgroups=characters and characters.indicgroups +if not indicgroups and characters then + local indic={ + c={}, + i={}, + d={}, + m={}, + s={}, + o={}, + } + local indicmarks={ + l={}, + t={}, + b={}, + r={}, + s={}, + } + local indicclasses={ + nukta={}, + halant={}, + ra={}, + anudatta={}, + } + local indicorders={ + bp={}, + ap={}, + bs={}, + as={}, + bh={}, + ah={}, + bm={}, + am={}, + } + for k,v in next,characters.data do + local i=v.indic + if i then + indic[i][k]=true + i=v.indicmark + if i then + if i=="s" then + local s=v.specials + indicmarks[i][k]={ s[2],s[3] } + else + indicmarks[i][k]=true + end + end + i=v.indicclass + if i then + indicclasses[i][k]=true + end + i=v.indicorder + if i then + indicorders[i][k]=true + end + end + end + indicgroups={ + consonant=indic.c, + independent_vowel=indic.i, + dependent_vowel=indic.d, + vowel_modifier=indic.m, + stress_tone_mark=indic.s, + pre_mark=indicmarks.l, + above_mark=indicmarks.t, + below_mark=indicmarks.b, + post_mark=indicmarks.r, + twopart_mark=indicmarks.s, + nukta=indicclasses.nukta, + halant=indicclasses.halant, + ra=indicclasses.ra, + anudatta=indicclasses.anudatta, + before_postscript=indicorders.bp, + after_postscript=indicorders.ap, + before_half=indicorders.bh, + after_half=indicorders.ah, + before_subscript=indicorders.bs, + after_subscript=indicorders.as, + before_main=indicorders.bm, + after_main=indicorders.am, + } + indic=nil + indicmarks=nil + indicclasses=nil + indicorders=nil + characters.indicgroups=indicgroups +end +local consonant=indicgroups.consonant +local independent_vowel=indicgroups.independent_vowel +local dependent_vowel=indicgroups.dependent_vowel +local vowel_modifier=indicgroups.vowel_modifier +local stress_tone_mark=indicgroups.stress_tone_mark +local pre_mark=indicgroups.pre_mark +local above_mark=indicgroups.above_mark +local below_mark=indicgroups.below_mark +local post_mark=indicgroups.post_mark +local twopart_mark=indicgroups.twopart_mark +local nukta=indicgroups.nukta +local halant=indicgroups.halant +local ra=indicgroups.ra +local anudatta=indicgroups.anudatta +local before_postscript=indicgroups.before_postscript +local after_postscript=indicgroups.after_postscript +local before_half=indicgroups.before_half +local after_half=indicgroups.after_half +local before_subscript=indicgroups.before_subscript +local after_subscript=indicgroups.after_subscript +local before_main=indicgroups.before_main +local after_main=indicgroups.after_main +local mark_four=table.merged ( + pre_mark, + above_mark, + below_mark, + post_mark +) +local mark_above_below_post=table.merged ( + above_mark, + below_mark, + post_mark +) 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", + [c_zwnj]=true, + [c_zwj ]=true, } local dflt_true={ dflt=true } -local dev2_defaults={ +local two_defaults={ dev2=dflt_true, } -local deva_defaults={ +local one_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 }, + features={ dv01=two_defaults }, flags=false_flags, name="dv01_reorder_matras", order={ "dv01" }, @@ -21844,13 +29332,12 @@ local sequence_reorder_matras={ nofsteps=1, steps={ { - osdstep=true, coverage=pre_mark, } } } local sequence_reorder_reph={ - features={ dv02=dev2_defaults }, + features={ dv02=two_defaults }, flags=false_flags, name="dv02_reorder_reph", order={ "dv02" }, @@ -21858,13 +29345,12 @@ local sequence_reorder_reph={ nofsteps=1, steps={ { - osdstep=true, coverage={}, } } } local sequence_reorder_pre_base_reordering_consonants={ - features={ dv03=dev2_defaults }, + features={ dv03=two_defaults }, flags=false_flags, name="dv03_reorder_pre_base_reordering_consonants", order={ "dv03" }, @@ -21872,54 +29358,80 @@ local sequence_reorder_pre_base_reordering_consonants={ nofsteps=1, steps={ { - osdstep=true, coverage={}, } } } local sequence_remove_joiners={ - features={ dv04=deva_defaults }, + features={ dv04=one_defaults }, flags=false_flags, name="dv04_remove_joiners", order={ "dv04" }, type="devanagari_remove_joiners", nofsteps=1, steps={ - { osdstep=true, - coverage=both_joiners_true, + { + coverage=zw_char, }, } } local basic_shaping_forms={ - nukt=true, akhn=true, - rphf=true, - pref=true, - rkrf=true, blwf=true, + cjct=true, half=true, + nukt=true, + pref=true, pstf=true, + rkrf=true, + rphf=true, vatu=true, - cjct=true, } local valid={ + abvs=true, akhn=true, - rphf=true, - pref=true, - half=true, blwf=true, + calt=true, + cjct=true, + half=true, + haln=true, + nukt=true, + pref=true, + pres=true, pstf=true, + psts=true, + rkrf=true, + rphf=true, + vatu=true, pres=true, + abvs=true, blws=true, psts=true, + haln=true, + calt=true, } +local scripts={} +local scripts_one={ "deva","mlym","beng","gujr","guru","knda","orya","taml","telu" } +local scripts_two={ "dev2","mlm2","bng2","gjr2","gur2","knd2","ory2","tml2","tel2" } +local nofscripts=#scripts_one +for i=1,nofscripts do + local one=scripts_one[i] + local two=scripts_two[i] + scripts[one]=true + scripts[two]=true + two_defaults[one]=dflt_true + one_defaults[one]=dflt_true + one_defaults[two]=dflt_true +end +local function valid_one(s) for i=1,nofscripts do if s[scripts_one[i]] then return true end end end +local function valid_two(s) for i=1,nofscripts do if s[scripts_two[i]] then return true end end end local function initializedevanagi(tfmdata) local script,language=otf.scriptandlanguage(tfmdata,attr) - if script=="deva" or script=="dev2" or script=="mlym" or script=="mlm2" then + if scripts[script] then local resources=tfmdata.resources local devanagari=resources.devanagari if not devanagari then - report_devanagari("adding devanagari features to font") + report("adding devanagari features to font") local gsubfeatures=resources.features.gsub local sequences=resources.sequences local sharedfeatures=tfmdata.shared.features @@ -21935,10 +29447,10 @@ local function initializedevanagi(tfmdata) end end local insertindex=lastmatch+1 - gsubfeatures["dv01"]=dev2_defaults - gsubfeatures["dv02"]=dev2_defaults - gsubfeatures["dv03"]=dev2_defaults - gsubfeatures["dv04"]=deva_defaults + gsubfeatures["dv01"]=two_defaults + gsubfeatures["dv02"]=two_defaults + gsubfeatures["dv03"]=two_defaults + gsubfeatures["dv04"]=one_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) @@ -21986,32 +29498,29 @@ local function initializedevanagi(tfmdata) end end end - for kind,spec in next,features do - if spec.dev2 and valid[kind] then + for kind,spec in next,features do + if valid[kind] and valid_two(spec)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 + 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 - else end end seqsubset[#seqsubset+1]={ kind,coverage,reph } @@ -22059,8 +29568,14 @@ local function initializedevanagi(tfmdata) sharedfeatures["pstf"]=true sharedfeatures["pref"]=true sharedfeatures["dv03"]=true - gsubfeatures ["dv03"]=dev2_defaults + gsubfeatures ["dv03"]=two_defaults insert(sequences,insertindex,sequence_reorder_pre_base_reordering_consonants) + elseif script=="taml" then + sharedfeatures["dv04"]=true +sharedfeatures["pstf"]=true + elseif script=="tml2" then + else + report("todo: enable the right features for script %a",script) end end end @@ -22073,7 +29588,18 @@ registerotffeature { node=initializedevanagi, }, } -local function deva_initialize(font,attr) +local show_syntax_errors=false +local function inject_syntax_error(head,current,char) + local signal=copy_node(current) + copyinjection(signal,current) + if pre_mark[char] then + setchar(signal,dotted_circle) + else + setchar(current,dotted_circle) + end + return insert_node_after(head,current,signal) +end +local function initialize_one(font,attr) local tfmdata=fontdata[font] local datasets=otf.dataset(tfmdata,font,attr) local devanagaridata=datasets.devanagari @@ -22101,8 +29627,8 @@ local function deva_initialize(font,attr) 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 function reorder_one(head,start,stop,font,attr,nbspaces) + local reph,vattu,blwfcache=initialize_one(font,attr) local current=start local n=getnext(start) local base=nil @@ -22133,40 +29659,39 @@ local function deva_reorder(head,start,stop,font,attr,nbspaces) lastcons=current current=getnext(current) if current~=stop then - if nukta[getchar(current)] then + local char=getchar(current) + if nukta[char] then current=getnext(current) + char=getchar(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 + if char==c_zwj and 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) + if show_syntax_errors then + head,current=inject_syntax_error(head,current,char) + end + 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 @@ -22439,9 +29964,11 @@ function handlers.devanagari_reorder_reph(head,start) local startprev=nil local startfont=getfont(start) local startattr=getprop(start,a_syllabe) + ::step_1:: + ::step_2:: while current do local char=ischar(current,startfont) - if char and getprop(current,a_syllabe)==startattr then + 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 @@ -22464,16 +29991,17 @@ function handlers.devanagari_reorder_reph(head,start) break end end + ::step_3:: + ::step_4:: 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 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(getprev(current),start) setlink(start,current) start=startnext startattr=getprop(start,a_syllabe) @@ -22485,13 +30013,14 @@ function handlers.devanagari_reorder_reph(head,start) end end end + ::step_5:: 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 + if char and getprop(current,a_syllabe)==startattr then + if not c and mark_above_below_post[char] and not after_subscript[char] then c=current end current=getnext(current) @@ -22502,19 +30031,19 @@ function handlers.devanagari_reorder_reph(head,start) if c then startnext=getnext(start) head=remove_node(head,start) - local prev=getprev(c) - setlink(prev,start) + setlink(getprev(c),start) setlink(start,c) start=startnext startattr=getprop(start,a_syllabe) end end + ::step_6:: 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 + if nextchar and getprop(next,a_syllabe)==startattr then current=next next=getnext(current) else @@ -22524,8 +30053,7 @@ function handlers.devanagari_reorder_reph(head,start) if start~=current then startnext=getnext(start) head=remove_node(head,start) - local next=getnext(current) - setlink(start,next) + setlink(start,getnext(current)) setlink(current,start) start=startnext end @@ -22573,8 +30101,7 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start) if not consonant[char] and getprop(current,a_state) then startnext=getnext(start) removenode(start,start) - local prev=getprev(current) - setlink(prev,start) + setlink(getprev(current),start) setlink(start,current) start=startnext break @@ -22608,12 +30135,12 @@ function handlers.devanagari_remove_joiners(head,start,kind,lookupname,replaceme setnext(prev) end if head==start then - head=stop + head=stop end flush_list(start) return head,stop,true end -local function dev2_initialize(font,attr) +local function initialize_two(font,attr) local devanagari=fontdata[font].resources.devanagari if devanagari then return devanagari.seqsubset or {},devanagari.reorderreph or {} @@ -22621,8 +30148,8 @@ local function dev2_initialize(font,attr) return {},{} end end -local function dev2_reorder(head,start,stop,font,attr,nbspaces) - local seqsubset,reorderreph=dev2_initialize(font,attr) +local function reorder_two(head,start,stop,font,attr,nbspaces) + local seqsubset,reorderreph=initialize_two(font,attr) local reph=false local halfpos=nil local basepos=nil @@ -22647,8 +30174,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) if found[n] then local afternext=next~=stop and getnext(next) if afternext and zw_char[getchar(afternext)] then - current=next - current=getnext(current) + current=afternext elseif current==start then setprop(current,a_state,s_rphf) current=next @@ -22774,7 +30300,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) next=getnext(current) local tmp=getnext(next) local changestop=next==stop - setnext(next,nil) + setnext(next) setprop(current,a_state,s_pref) current=processcharacters(current,font) setprop(current,a_state,s_blwf) @@ -22784,10 +30310,9 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) 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) + if show_syntax_errors then + head,current=inject_syntax_error(head,current,char) + end else setnext(current,tmp) if changestop then @@ -22867,8 +30392,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) end start=current end - local prev=getprev(halfpos) - setlink(prev,current) + setlink(getprev(halfpos),current) setlink(current,halfpos) halfpos=current elseif above_mark[char] then @@ -22898,8 +30422,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) if current==stop then stop=prev end - local next=getnext(target) - setlink(current,next) + setlink(current,getnext(target)) setlink(target,current) end end @@ -22924,8 +30447,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) if stop==next then stop=current end - local prev=getprev(c) - setlink(prev,next) + setlink(getprev(c),next) local nextnext=getnext(next) setnext(current,nextnext) local nextnextnext=getnext(nextnext) @@ -23159,7 +30681,7 @@ local function analyze_next_chars_two(c,font) if not v then return c end - if v==c_anudatta then + if anudatta[v] then c=n n=getnext(c) if not n then @@ -23254,31 +30776,20 @@ local function analyze_next_chars_two(c,font) 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 function method_one(head,font,attr) local current=head local start=true local done=false local nbspaces=0 while current do - local char=ischar(current,font) + 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 + local first=char if n and ra[first] then local second=ischar(n,font) if second and halant[second] then @@ -23306,10 +30817,10 @@ function methods.deva(head,font,attr) end end if standalone then - local syllableend=analyze_next_chars_one(c,font,2) - current=getnext(syllableend) + 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) + head,current,nbspaces=reorder_one(head,syllablestart,syllableend,font,attr,nbspaces) current=getnext(current) end else @@ -23406,7 +30917,7 @@ function methods.deva(head,font,attr) end end if syllablestart~=syllableend then - head,current,nbspaces=deva_reorder(head,syllablestart,syllableend,font,attr,nbspaces) + head,current,nbspaces=reorder_one(head,syllablestart,syllableend,font,attr,nbspaces) current=getnext(current) end elseif independent_vowel[char] then @@ -23427,9 +30938,11 @@ function methods.deva(head,font,attr) end end else - local mark=mark_four[char] - if mark then - head,current=inject_syntax_error(head,current,mark) + if show_syntax_errors then + local mark=mark_four[char] + if mark then + head,current=inject_syntax_error(head,current,char) + end end current=getnext(current) end @@ -23442,11 +30955,9 @@ function methods.deva(head,font,attr) 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 function method_two(head,font,attr) local current=head local start=true local done=false @@ -23469,7 +30980,7 @@ function methods.dev2(head,font,attr) local nextnextchar=ischar(n,font) if nextnextchar then c=n - char=nextnextchar + char=nextnextchar end end end @@ -23508,14 +31019,14 @@ function methods.dev2(head,font,attr) end end if syllableend and syllablestart~=syllableend then - head,current,nbspaces=dev2_reorder(head,syllablestart,syllableend,font,attr,nbspaces) + head,current,nbspaces=reorder_two(head,syllablestart,syllableend,font,attr,nbspaces) end - if not syllableend then + if not syllableend and show_syntax_errors 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) + head,current=inject_syntax_error(head,current,char) end end end @@ -23525,11 +31036,12 @@ function methods.dev2(head,font,attr) 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 +for i=1,nofscripts do + methods[scripts_one[i]]=method_one + methods[scripts_two[i]]=method_two +end end -- closure @@ -23537,127 +31049,144 @@ do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['font-ocl']={ version=1.001, - comment="companion to font-otf.lua (context)", + 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 tostring,next,format=tostring,next,string.format +local tostring,tonumber,next=tostring,tonumber,next local round,max=math.round,math.round +local sortedkeys,sortedhash=table.sortedkeys,table.sortedhash +local setmetatableindex=table.setmetatableindex local formatters=string.formatters local tounicode=fonts.mappings.tounicode +local helpers=fonts.helpers +local charcommand=helpers.commands.char +local rightcommand=helpers.commands.right +local leftcommand=helpers.commands.left +local downcommand=helpers.commands.down 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" +local f_color=formatters["%.3f %.3f %.3f rg"] +local f_gray=formatters["%.3f 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 + +--removed + else local tounicode=fonts.mappings.tounicode16 function otf.getactualtext(s) return - "/Span << /ActualText >> BDC", + "/Span << /ActualText >> BDC", "EMC" end end local sharedpalettes={} +local hash=setmetatableindex(function(t,k) + local v={ "pdf","direct",k } + t[k]=v + return v +end) if context then - local graytorgb=attributes.colors.graytorgb - local cmyktorgb=attributes.colors.cmyktorgb + +--removed + +else 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) - } + values[i]=hash[f_color( + max(round((v.r or 0)*255),255)/255, + max(round((v.g or 0)*255),255)/255, + max(round((v.b or 0)*255),255)/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 +local function convert(t,k) + local v={} + for i=1,#k do + local p=k[i] + local r,g,b=p[1],p[2],p[3] + if r==g and g==b then + v[i]=hash[f_gray(r/255)] + else + v[i]=hash[f_color(r/255,g/255,b/255)] end end + t[k]=v + return v +end +local start={ "pdf","mode","font" } +local push={ "pdf","page","q" } +local pop={ "pdf","page","Q" } +if not LUATEXFUNCTIONALITY or LUATEXFUNCTIONALITY<6472 then + start={ "nop" } end -local function initializecolr(tfmdata,kind,value) +local function initialize(tfmdata,kind,value) if value then - local palettes=tfmdata.resources.colorpalettes + local resources=tfmdata.resources + local palettes=resources.colorpalettes if palettes then - local palette=sharedpalettes[value] or palettes[tonumber(value) or 1] or palettes[1] or {} - local classes=#palette + local converted=resources.converted + if not converted then + converted=setmetatableindex(convert) + resources.converted=converted + end + local colorvalues=sharedpalettes[value] or converted[palettes[tonumber(value) or 1] or palettes[1]] or {} + local classes=#colorvalues 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 + local default=colorvalues[#colorvalues] + local b,e=getactualtext(tounicode(0xFFFD)) + local actualb={ "pdf","page",b } + local actuale={ "pdf","page",e } 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 u=description.unicode or characters[unicode].unicode local w=character.width or 0 local s=#colorlist + local goback=w~=0 and leftcommand[w] or nil local t={ - { "special","pdf:page:q" }, - { "special","pdf:raw:"..b } + start, + not u and actualb or { "pdf","page",(getactualtext(tounicode(u))) } } - local n=#t + local n=2 + local l=nil + local f=false 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 i1 and i temp-otf-svg-shape.log","w") + 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()) + local inkscape=runner() + if inkscape then + 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 + local nofdone=0 + 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 + nofdone=nofdone+1 + if nofdone%100==0 then + report_svg("%i shapes processed",nofdone) + end + 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() or "-") + end 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 svg=tfmdata.properties.svg local hash=svg and svg.hash local timestamp=svg and svg.timestamp if not hash then @@ -23792,40 +31390,7 @@ local function initializesvg(tfmdata,kind,value) 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 + pdftovirtual(tfmdata,pdfshapes,"svg") end end fonts.handlers.otf.features.register { @@ -23836,6 +31401,91 @@ fonts.handlers.otf.features.register { node=initializesvg, } } +local otfsbix=otf.sbix or {} +otf.sbix=otfsbix +otf.sbixenabled=true +do + local report_sbix=logs.reporter("fonts","sbix conversion") + local loaddata=io.loaddata + local savedata=io.savedata + local remove=os.remove + local runner=sandbox and sandbox.registerrunner { + name="otfsbix", + program="gm", + template="convert -quality 100 temp-otf-sbix-shape.sbix temp-otf-sbix-shape.pdf > temp-otf-svg-shape.log", + } + if not runner then + runner=function() + return os.execute("gm convert -quality 100 temp-otf-sbix-shape.sbix temp-otf-sbix-shape.pdf > temp-otf-svg-shape.log") + end + end + function otfsbix.topdf(sbixshapes) + local pdfshapes={} + local sbixfile="temp-otf-sbix-shape.sbix" + local pdffile="temp-otf-sbix-shape.pdf" + local nofdone=0 + local indices=sortedkeys(sbixshapes) + local nofindices=#indices + report_sbix("processing %i sbix containers",nofindices) + statistics.starttiming() + for i=1,nofindices do + local index=indices[i] + local entry=sbixshapes[index] + local data=entry.data + local x=entry.x + local y=entry.y + savedata(sbixfile,data) + runner() + pdfshapes[index]={ + x=x~=0 and x or nil, + y=y~=0 and y or nil, + data=loaddata(pdffile), + } + nofdone=nofdone+1 + if nofdone%100==0 then + report_sbix("%i shapes processed",nofdone) + end + end + report_sbix("processing %i pdf results",nofindices) + remove(sbixfile) + remove(pdffile) + statistics.stoptiming() + if statistics.elapsedseconds then + report_sbix("sbix conversion time %s",statistics.elapsedseconds() or "-") + end + return pdfshapes + end +end +local function initializesbix(tfmdata,kind,value) + if value and otf.sbixenabled then + local sbix=tfmdata.properties.sbix + local hash=sbix and sbix.hash + local timestamp=sbix and sbix.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 sbixfile=containers.read(otf.sbixcache,hash) + local sbixshapes=sbixfile and sbixfile.sbixshapes + pdfshapes=sbixshapes and otfsbix.topdf(sbixshapes) or {} + containers.write(otf.pdfcache,hash,{ + pdfshapes=pdfshapes, + timestamp=timestamp, + }) + end + pdftovirtual(tfmdata,pdfshapes,"sbix") + end +end +fonts.handlers.otf.features.register { + name="sbix", + description="sbix glyphs", + manipulators={ + base=initializesbix, + node=initializesbix, + } +} end -- closure @@ -23843,21 +31493,25 @@ do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['font-otc']={ version=1.001, - comment="companion to font-otf.lua (context)", + comment="companion to font-ini.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } -local format,insert,sortedkeys,tohash=string.format,table.insert,table.sortedkeys,table.tohash +local insert,sortedkeys,sortedhash,tohash=table.insert,table.sortedkeys,table.sortedhash,table.tohash local type,next=type,next local lpegmatch=lpeg.match local utfbyte,utflen=utf.byte,utf.len +local sortedhash=table.sortedhash 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 checkmerge=fonts.helpers.checkmerge +local checkflags=fonts.helpers.checkflags +local checksteps=fonts.helpers.checksteps local normalized={ substitution="substitution", single="substitution", @@ -23866,6 +31520,7 @@ local normalized={ multiple="multiple", kern="kern", pair="pair", + single="single", chainsubstitution="chainsubstitution", chainposition="chainposition", } @@ -23876,6 +31531,7 @@ local types={ multiple="gsub_multiple", kern="gpos_pair", pair="gpos_pair", + single="gpos_single", chainsubstitution="gsub_contextchain", chainposition="gpos_contextchain", } @@ -24007,13 +31663,15 @@ local function addfeature(data,feature,specifications) local coveractions=coverup.actions local stepkey=coverup.stepkey local register=coverup.register - local function prepare_substitution(list,featuretype) + local function prepare_substitution(list,featuretype,nocheck) 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 not nocheck and not description then + skip=skip+1 + else if type(replacement)=="table" then replacement=replacement[1] end @@ -24024,25 +31682,23 @@ local function addfeature(data,feature,specifications) else skip=skip+1 end - else - skip=skip+1 end end return coverage end - local function prepare_alternate(list,featuretype) + local function prepare_alternate(list,featuretype,nocheck) 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 + if not nocheck and 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 + r[i]=(nocheck or descriptions[u]) and u or unicode end cover(coverage,unicode,r) done=done+1 @@ -24058,19 +31714,19 @@ local function addfeature(data,feature,specifications) end return coverage end - local function prepare_multiple(list,featuretype) + local function prepare_multiple(list,featuretype,nocheck) 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 + if not nocheck and 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 + if nocheck or descriptions[u] then n=n+1 r[n]=u end @@ -24093,13 +31749,15 @@ local function addfeature(data,feature,specifications) end return coverage end - local function prepare_ligature(list,featuretype) + local function prepare_ligature(list,featuretype,nocheck) 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 not nocheck and not description then + skip=skip+1 + else if type(ligature)=="string" then ligature={ lpegmatch(splitter,ligature) } end @@ -24107,7 +31765,7 @@ local function addfeature(data,feature,specifications) for i=1,#ligature do local l=ligature[i] local u=tounicode(l) - if descriptions[u] then + if nocheck or descriptions[u] then ligature[i]=u else present=false @@ -24120,15 +31778,18 @@ local function addfeature(data,feature,specifications) else skip=skip+1 end - else - skip=skip+1 end end return coverage end + local function resetspacekerns() + data.properties.hasspacekerns=true + data.resources .spacekerns=nil + end local function prepare_kern(list,featuretype) local coverage={} local cover=coveractions[featuretype] + local isspace=false for code,replacement in next,list do local unicode=tounicode(code) local description=descriptions[unicode] @@ -24138,11 +31799,17 @@ local function addfeature(data,feature,specifications) local u=tounicode(k) if u then r[u]=v + if u==32 then + isspace=true + end end end if next(r) then cover(coverage,unicode,r) done=done+1 + if unicode==32 then + isspace=true + end else skip=skip+1 end @@ -24150,6 +31817,9 @@ local function addfeature(data,feature,specifications) skip=skip+1 end end + if isspace then + resetspacekerns() + end return coverage end local function prepare_pair(list,featuretype) @@ -24165,11 +31835,17 @@ local function addfeature(data,feature,specifications) local u=tounicode(k) if u then r[u]=v + if u==32 then + isspace=true + end end end if next(r) then cover(coverage,unicode,r) done=done+1 + if unicode==32 then + isspace=true + end else skip=skip+1 end @@ -24177,19 +31853,21 @@ local function addfeature(data,feature,specifications) skip=skip+1 end end + if isspace then + resetspacekerns() + end else report_otf("unknown cover type %a",featuretype) end return coverage end + local prepare_single=prepare_pair 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] @@ -24220,14 +31898,29 @@ local function addfeature(data,feature,specifications) 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 + for k,v in sortedhash(lookups) do + local t=type(v) + if t=="table" then + for i=1,#v do + local vi=v[i] + if type(vi)~="table" then + v[i]={ vi } + end + end + elseif t=="number" then + local lookup=sublookups[v] + if lookup then + lookups[k]={ lookup } + if not subtype then + subtype=lookup.type + end + elseif v==0 then + lookups[k]={ { type="gsub_remove" } } + else + lookups[k]=false end else + lookups[k]=false end end end @@ -24256,14 +31949,16 @@ local function addfeature(data,feature,specifications) replacements, subtype, } - for unic in next,sequence[start] do + for unic in sortedhash(sequence[start]) do local cu=coverage[unic] if not cu then coverage[unic]=rulehash end end + sequence.n=nofsequences end end + rulehash.n=rulesize end return coverage end @@ -24301,7 +31996,7 @@ local function addfeature(data,feature,specifications) local s=sequences[i] local f=s.features if f then - for k in next,f do + for k in sortedhash(f) do if k==position then index=i break @@ -24349,12 +32044,15 @@ local function addfeature(data,feature,specifications) 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 nocheck=specification.nocheck + local futuresteps=specification.futuresteps 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 + checkflags(specification,resources) if sublookups then local s={} for i=1,#sublookups do @@ -24369,28 +32067,34 @@ local function addfeature(data,feature,specifications) local coverage=nil local format=nil if featuretype=="substitution" then - coverage=prepare_substitution(list,featuretype) + coverage=prepare_substitution(list,featuretype,nocheck) elseif featuretype=="ligature" then - coverage=prepare_ligature(list,featuretype) + coverage=prepare_ligature(list,featuretype,nocheck) elseif featuretype=="alternate" then - coverage=prepare_alternate(list,featuretype) + coverage=prepare_alternate(list,featuretype,nocheck) elseif featuretype=="multiple" then - coverage=prepare_multiple(list,featuretype) - elseif featuretype=="kern" then - format="kern" + coverage=prepare_multiple(list,featuretype,nocheck) + elseif featuretype=="kern" or featuretype=="move" then + format=featuretype coverage=prepare_kern(list,featuretype) elseif featuretype=="pair" then format="pair" coverage=prepare_pair(list,featuretype) + elseif featuretype=="single" then + format="single" + coverage=prepare_single(list,featuretype) end if coverage and next(coverage) then nofsteps=nofsteps+1 steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources) end end + checkmerge(specification) + checksteps(specification) s[i]={ [stepkey]=steps, nofsteps=nofsteps, + flags=featureflags, type=types[featuretype], } end @@ -24402,24 +32106,28 @@ local function addfeature(data,feature,specifications) local format=nil if featuretype=="substitution" then category="gsub" - coverage=prepare_substitution(list,featuretype) + coverage=prepare_substitution(list,featuretype,nocheck) elseif featuretype=="ligature" then category="gsub" - coverage=prepare_ligature(list,featuretype) + coverage=prepare_ligature(list,featuretype,nocheck) elseif featuretype=="alternate" then category="gsub" - coverage=prepare_alternate(list,featuretype) + coverage=prepare_alternate(list,featuretype,nocheck) elseif featuretype=="multiple" then category="gsub" - coverage=prepare_multiple(list,featuretype) - elseif featuretype=="kern" then + coverage=prepare_multiple(list,featuretype,nocheck) + elseif featuretype=="kern" or featuretype=="move" then category="gpos" - format="kern" + format=featuretype coverage=prepare_kern(list,featuretype) elseif featuretype=="pair" then category="gpos" format="pair" coverage=prepare_pair(list,featuretype) + elseif featuretype=="single" then + category="gpos" + format="single" + coverage=prepare_single(list,featuretype) elseif featuretype=="chainsubstitution" then category="gsub" coverage=prepare_chain(list,featuretype,sublookups) @@ -24455,6 +32163,9 @@ local function addfeature(data,feature,specifications) nofsteps=nofsteps, type=steptype, } + checkflags(sequence,resources) + checkmerge(sequence) + checksteps(sequence) local first,last=getrange(sequences,category) inject(specification,sequences,sequence,first,last,category,feature) local features=fontfeatures[category] @@ -24498,10 +32209,13 @@ function otf.addfeature(name,specification) specification,name=validspecification(specification,name) if name and specification then local slot=knownfeatures[name] - if slot then - else + if not slot then + slot=#extrafeatures+1 + knownfeatures[name]=slot + elseif specification.overload==false then slot=#extrafeatures+1 knownfeatures[name]=slot + else end specification.name=name extrafeatures[slot]=specification @@ -24515,98 +32229,6 @@ local function enhance(data,filename,raw) 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 -- closure @@ -24636,7 +32258,7 @@ local afm=handlers.afm or {} handlers.afm=afm local readers=afm.readers or {} afm.readers=readers -afm.version=1.512 +afm.version=1.513 local get_indexes,get_shapes do local decrypt @@ -24660,7 +32282,7 @@ do 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 name=P("/")*C((R("az","AZ","09")+S("-_."))^1) local digits=R("09")^1 local cardinal=digits/tonumber local spaces=P(" ")^1 @@ -24671,30 +32293,38 @@ do m=size return position+1 end - local setroutine=function(str,position,index,size) + local setroutine=function(str,position,index,size,filename) local forward=position+tonumber(size) - local stream=sub(str,position+1,forward) - routines[index]=decrypt(stream,4330,4) + local stream=decrypt(sub(str,position+1,forward),4330,4) + routines[index]={ byte(stream,1,#stream) } return forward end - local setvector=function(str,position,name,size) + local setvector=function(str,position,name,size,filename) local forward=position+tonumber(size) if n>=m then return #str elseif forward<#str then + if n==0 and name~=".notdef" then + report_pfb("reserving .notdef at index 0 in %a",filename) + n=n+1 + end vector[n]=name - n=n+1 + n=n+1 return forward else return #str end end - local setshapes=function(str,position,name,size) + local setshapes=function(str,position,name,size,filename) local forward=position+tonumber(size) local stream=sub(str,position+1,forward) if n>m then return #str elseif forward<#str then + if n==0 and name~=".notdef" then + report_pfb("reserving .notdef at index 0 in %a",filename) + n=n+1 + end vector[n]=name n=n+1 chars [n]=decrypt(stream,4330,4) @@ -24707,11 +32337,11 @@ do 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 + (1-subroutines)^0*subroutines*spaces*Cmt(cardinal,initialize)*(Cmt(cardinal*spaces*cardinal*p_rd*Carg(1),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 + (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal*p_rd*Carg(1),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 + (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal*Carg(1),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 @@ -24722,7 +32352,7 @@ do report_pfb("no data in %a",filename) return end - if not (find(data,"!PS%-AdobeFont%-") or find(data,"%%!FontType1")) then + if not (find(data,"!PS-AdobeFont-",1,true) or find(data,"%!FontType1",1,true)) then report_pfb("no font in %a",filename) return end @@ -24737,8 +32367,8 @@ do local glyphs={} routines,vector,chars={},{},{} if shapestoo then - lpegmatch(p_filterroutines,binary) - lpegmatch(p_filtershapes,binary) + lpegmatch(p_filterroutines,binary,1,filename) + lpegmatch(p_filtershapes,binary,1,filename) local data={ dictionaries={ { @@ -24748,9 +32378,9 @@ do } }, } - fonts.handlers.otf.readers.parsecharstrings(data,glyphs,true,true) + fonts.handlers.otf.readers.parsecharstrings(false,data,glyphs,true,true) else - lpegmatch(p_filternames,binary) + lpegmatch(p_filternames,binary,1,filename) end names=vector routines,vector,chars=nil,nil,nil @@ -24766,7 +32396,7 @@ do if trace_loading then report_afm("getting index data from %a",pfbname) end - for index=1,#vector do + for index=0,#vector do local name=vector[index] local char=characters[name] if char then @@ -24774,6 +32404,10 @@ do report_afm("glyph %a has index %a",name,index) end char.index=index + else + if trace_indexing then + report_afm("glyph %a has index %a but no data",name,index) + end end end end @@ -24879,7 +32513,6 @@ local p_parameters=P(false)+fontdata*((P("FontName")+P("FullName")+P("FamilyName +(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) @@ -24918,16 +32551,15 @@ 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) + pfbname=resolvers.findfile(file.replacesuffix(file.nameonly(afmname),"pfb")) end if pfbname and pfbname~="" then data.resources.filename=resolvers.unresolve(pfbname) get_indexes(data,pfbname) - elseif trace_loading then + return data + else report_afm("no pfb file for %a",afmname) end - return data end end function readers.loadshapes(filename) @@ -24966,12 +32598,11 @@ if not modules then modules={} end modules ['font-one']={ } 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 match,gsub=string.match,string.gsub 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 sortedhash=table.sortedhash 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) @@ -24980,6 +32611,7 @@ local report_afm=logs.reporter("fonts","afm loading") local setmetatableindex=table.setmetatableindex local derivetable=table.derive local findbinfile=resolvers.findbinfile +local privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000 local definers=fonts.definers local readers=fonts.readers local constructors=fonts.constructors @@ -24992,7 +32624,7 @@ local afmfeatures=constructors.features.afm local registerafmfeature=afmfeatures.register local afmenhancers=constructors.enhancers.afm local registerafmenhancer=afmenhancers.register -afm.version=1.512 +afm.version=1.513 afm.cache=containers.define("fonts","one",afm.version,true) afm.autoprefixed=true afm.helpdata={} @@ -25005,7 +32637,7 @@ function afm.load(filename) 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 size,time=attr and attr.size or 0,attr and attr.modification or 0 local pfbfile=file.replacesuffix(name,"pfb") local pfbname=resolvers.findfile(pfbfile,"pfb") or "" if pfbname=="" then @@ -25050,9 +32682,9 @@ local function enhance_unify_names(data,filename) local unicodevector=fonts.encodings.agl.unicodes local unicodes={} local names={} - local private=constructors.privateoffset + local private=data.private or privateoffset local descriptions=data.descriptions - for name,blob in next,data.characters do + for name,blob in sortedhash(data.characters) do local code=unicodevector[name] if not code then code=lpegmatch(uparser,name) @@ -25089,12 +32721,12 @@ local function enhance_unify_names(data,filename) end end data.characters=nil + data.private=private 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 } @@ -25448,6 +33080,7 @@ local function copytotfm(data) properties.fullname=fullname properties.psname=fullname properties.name=filename or fullname or fontname + properties.private=properties.private or data.private or privateoffset if next(characters) then return { characters=characters, @@ -25659,7 +33292,7 @@ do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['font-afk']={ version=1.001, - comment="companion to font-afm.lua", + 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", @@ -25844,6 +33477,7 @@ local findbinfile=resolvers.findbinfile local setmetatableindex=table.setmetatableindex local fonts=fonts local handlers=fonts.handlers +local helpers=fonts.helpers local readers=fonts.readers local constructors=fonts.constructors local encodings=fonts.encodings @@ -25857,6 +33491,7 @@ local tfmfeatures=constructors.features.tfm local registertfmfeature=tfmfeatures.register local tfmenhancers=constructors.enhancers.tfm local registertfmenhancer=tfmenhancers.register +local charcommand=helpers.commands.char constructors.resolvevirtualtoo=false fonts.formats.tfm="type1" fonts.formats.ofm="type1" @@ -25950,6 +33585,7 @@ local function read_from_tfm(specification) parameters.quad=parameters.quad or parameters[6] or 0 parameters.extra_space=parameters.extra_space or parameters[7] or 0 constructors.enhanceparameters(parameters) + properties.private=properties.private or tfmdata.private or privateoffset if newtfmdata then elseif constructors.resolvevirtualtoo then fonts.loggers.register(tfmdata,file.suffix(filename),specification) @@ -26096,7 +33732,7 @@ do local originals=tfmdata.characters local indices={} local parentfont={ "font",1 } - local private=fonts.constructors.privateoffset + local private=tfmdata or fonts.constructors.privateoffset local reported=encdone[tfmfile][encfile] local backmap=vector and table.swapped(vector) local done={} @@ -26119,7 +33755,7 @@ do if backmap then original.index=backmap[name] else - original.commands={ parentfont,{ "char",index } } + original.commands={ parentfont,charcommand[index] } original.oindex=index end done[name]=true @@ -26163,6 +33799,7 @@ do tfmdata.tounicode=1 tfmdata.embedding="subset" tfmdata.usedbitmap=bitmap and virtualid + tfmdata.private=private return tfmdata end end @@ -26187,7 +33824,8 @@ end ]] local flushstreamobject=lpdf and lpdf.flushstreamobject local setfontattributes=pdf.setfontattributes - if not flushstreamobject then + if flushstreamobject then + else flushstreamobject=function(data) return pdf.obj { immediate=true, @@ -26386,13 +34024,13 @@ if not modules then modules={} end modules ['font-def']={ local lower,gsub=string.lower,string.gsub local tostring,next=tostring,next local lpegmatch=lpeg.match -local suffixonly,removesuffix=file.suffix,file.removesuffix +local suffixonly,removesuffix,basename=file.suffix,file.removesuffix,file.basename local formatters=string.formatters +local sortedhash,sortedkeys=table.sortedhash,table.sortedkeys local allocate=utilities.storage.allocate local trace_defining=false trackers .register("fonts.defining",function(v) trace_defining=v end) local directive_embedall=false directives.register("fonts.embedall",function(v) directive_embedall=v end) trackers.register("fonts.loading","fonts.defining","otf.loading","afm.loading","tfm.loading") -trackers.register("fonts.all","fonts.*","otf.*","afm.*","tfm.*") local report_defining=logs.reporter("fonts","defining") local fonts=fonts local fontdata=fonts.hashes.identifiers @@ -26410,41 +34048,6 @@ 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 @@ -26469,9 +34072,10 @@ local function makespecification(specification,lookup,name,sub,method,detail,siz 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) +if context then + +--removed + end definers.resolvers=definers.resolvers or {} local resolvers=definers.resolvers @@ -26489,11 +34093,28 @@ end function resolvers.name(specification) local resolve=fonts.names.resolve if resolve then - local resolved,sub,subindex=resolve(specification.name,specification.sub,specification) + local resolved,sub,subindex,instance=resolve(specification.name,specification.sub,specification) if resolved then specification.resolved=resolved specification.sub=sub specification.subindex=subindex + if instance then + specification.instance=instance + local features=specification.features + if not features then + features={} + specification.features=features + end + local normal=features.normal + if not normal then + normal={} + features.normal=normal + end + normal.instance=instance + if not callbacks.supported.glyph_stream_provider then + normal.variableshapes=true + end + end local suffix=lower(suffixonly(resolved)) if fonts.formats[suffix] then specification.forced=suffix @@ -26571,6 +34192,60 @@ local function checkembedding(tfmdata) end tfmdata.embedding=embedding end +local function checkfeatures(tfmdata) + local resources=tfmdata.resources + local shared=tfmdata.shared + if resources and shared then + local features=resources.features + local usedfeatures=shared.features + if features and usedfeatures then + local usedlanguage=usedfeatures.language or "dflt" + local usedscript=usedfeatures.script or "dflt" + local function check(what) + if what then + local foundlanguages={} + for feature,scripts in next,what do + if usedscript=="auto" or scripts["*"] then + elseif not scripts[usedscript] then + else + for script,languages in next,scripts do + if languages["*"] then + elseif not languages[usedlanguage] then + report_defining("font %!font:name!, feature %a, script %a, no language %a", + tfmdata,feature,script,usedlanguage) + end + end + end + for script,languages in next,scripts do + for language in next,languages do + foundlanguages[language]=true + end + end + end + if false then + foundlanguages["*"]=nil + foundlanguages=sortedkeys(foundlanguages) + for feature,scripts in sortedhash(what) do + for script,languages in next,scripts do + if not languages["*"] then + for i=1,#foundlanguages do + local language=foundlanguages[i] + if not languages[language] then + report_defining("font %!font:name!, feature %a, script %a, no language %a", + tfmdata,feature,script,language) + end + end + end + end + end + end + end + end + check(features.gsub) + check(features.gpos) + end + end +end function definers.loadfont(specification) local hash=constructors.hashinstance(specification) local tfmdata=loadedfonts[hash] @@ -26604,6 +34279,7 @@ function definers.loadfont(specification) checkembedding(tfmdata) loadedfonts[hash]=tfmdata designsizes[specification.hash]=tfmdata.parameters.designsize + checkfeatures(tfmdata) end end if not tfmdata then @@ -26611,8 +34287,6 @@ function definers.loadfont(specification) end return tfmdata end -function constructors.checkvirtualids() -end function constructors.readanddefine(name,size) local specification=definers.analyze(name,size) local method=specification.method @@ -26626,7 +34300,6 @@ function constructors.readanddefine(name,size) local tfmdata=definers.loadfont(specification) if tfmdata then tfmdata.properties.hash=hash - constructors.checkvirtualids(tfmdata) id=font.define(tfmdata) definers.register(tfmdata,id) else @@ -26652,160 +34325,584 @@ function definers.register(tfmdata,id) if trace_defining then report_defining("registering font, id %s, hash %a",id,hash) end - fontdata[id]=tfmdata + 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 or "-",properties.name,parameters.size,properties.encodingbytes, + properties.encodingname,properties.fullname,basename(properties.filename)) + end + statistics.stoptiming(fonts) + return tfmdata +end +function font.getfont(id) + return fontdata[id] +end +callbacks.register('define_font',definers.read,"definition of fonts (tfmdata preparation)") + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luatex-fonts-def']={ + version=1.001, + comment="companion to luatex-*.tex", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then +--removed + +end +local fonts=fonts +fonts.constructors.namemode="specification" +function fonts.definers.getspecification(str) + return "",str,"",":",str +end +local list={} +local function issome () list.lookup='name' end +local function isfile () list.lookup='file' end +local function isname () list.lookup='name' end +local function thename(s) list.name=s end +local function issub (v) list.sub=v end +local function iscrap (s) list.crap=string.lower(s) end +local function iskey (k,v) list[k]=v end +local function istrue (s) list[s]=true end +local function isfalse(s) list[s]=false end +local P,S,R,C,Cs=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cs +local spaces=P(" ")^0 +local namespec=Cs((P("{")/"")*(1-S("}"))^0*(P("}")/"")+(1-S("/:("))^0) +local crapspec=spaces*P("/")*(((1-P(":"))^0)/iscrap)*spaces +local filename_1=P("file:")/isfile*(namespec/thename) +local filename_2=P("[")*P(true)/isfile*(((1-P("]"))^0)/thename)*P("]") +local fontname_1=P("name:")/isname*(namespec/thename) +local fontname_2=P(true)/issome*(namespec/thename) +local sometext=R("az","AZ","09")^1 +local somekey=R("az","AZ","09")^1 +local somevalue=(P("{")/"")*(1-P("}"))^0*(P("}")/"")+(1-S(";"))^1 +local truevalue=P("+")*spaces*(sometext/istrue) +local falsevalue=P("-")*spaces*(sometext/isfalse) +local keyvalue=(C(somekey)*spaces*P("=")*spaces*C(somevalue))/iskey +local somevalue=sometext/istrue +local subvalue=P("(")*(C(P(1-S("()"))^1)/issub)*P(")") +local option=spaces*(keyvalue+falsevalue+truevalue+somevalue)*spaces +local options=P(":")*spaces*(P(";")^0*option)^0 +local pattern=(filename_1+filename_2+fontname_1+fontname_2)*subvalue^0*crapspec^0*options^0 +function fonts.definers.analyze(str,size) + local specification=fonts.definers.makespecification(str,nil,nil,nil,":",nil,size) + list={} + lpeg.match(pattern,str) + list.crap=nil + if list.name then + specification.name=list.name + list.name=nil + end + if list.lookup then + specification.lookup=list.lookup + list.lookup=nil + end + if list.sub then + specification.sub=list.sub + list.sub=nil + end + specification.features.normal=fonts.handlers.otf.features.normalize(list) + list=nil + return specification +end +function fonts.definers.applypostprocessors(tfmdata) + local postprocessors=tfmdata.postprocessors + if postprocessors then + for i=1,#postprocessors do + local extrahash=postprocessors[i](tfmdata) + if type(extrahash)=="string" and extrahash~="" then + extrahash=string.gsub(lower(extrahash),"[^a-z]","-") + tfmdata.properties.fullname=format("%s-%s",tfmdata.properties.fullname,extrahash) + end + end + end + return tfmdata +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['luatex-fonts-ext']={ + version=1.001, + comment="companion to luatex-*.tex", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if context then +--removed + +end +local byte=string.byte +local fonts=fonts +local handlers=fonts.handlers +local otf=handlers.otf +local afm=handlers.afm +local registerotffeature=otf.features.register +local registerafmfeature=afm.features.register +function fonts.loggers.onetimemessage() end +fonts.protrusions=fonts.protrusions or {} +fonts.protrusions.setups=fonts.protrusions.setups or {} +local setups=fonts.protrusions.setups +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 }, +} +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 +local specification={ + name="protrusion", + description="shift characters into the left and or right margin", + initializers={ + base=initializeprotrusion, + node=initializeprotrusion, + } +} +registerotffeature(specification) +registerafmfeature(specification) +fonts.expansions=fonts.expansions or {} +fonts.expansions.setups=fonts.expansions.setups or {} +local setups=fonts.expansions.setups +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, +} +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 -function definers.read(specification,size,id) - statistics.starttiming(fonts) - if type(specification)=="string" then - specification=definers.analyze(specification,size) +local specification={ + name="expansion", + description="apply hz optimization", + initializers={ + base=initializeexpansion, + node=initializeexpansion, + } +} +registerotffeature(specification) +registerafmfeature(specification) +if not otf.features.normalize then + otf.features.normalize=function(t) + if t.rand then + t.rand="random" + end + return t end - local method=specification.method - if method and variants[method] then - specification=variants[method](specification) +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 - 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 +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 - 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 + for k,v in next,temp do + char[k]=temp[k] end + return string.format("reencoded:%s",value) 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] +local function initialize(tfmdata,value) + tfmdata.postprocessors=tfmdata.postprocessors or {} + table.insert(tfmdata.postprocessors, + function(tfmdata) + return specialreencode(tfmdata,value) + end + ) end -callbacks.register('define_font',definers.read,"definition of fonts (tfmdata preparation)") +registerotffeature { + name="reencode", + description="reencode characters", + manipulators={ + base=initialize, + node=initialize, + } +} +local function initialize(tfmdata,key,value) + if value then + tfmdata.mathparameters=nil + end +end +registerotffeature { + name="ignoremathconstants", + description="ignore math constants table", + initializers={ + base=initialize, + node=initialize, + } +} end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules={} end modules ['luatex-fonts-def']={ +if not modules then modules={} end modules ['font-imp-tex']={ version=1.001, - comment="companion to luatex-*.tex", + 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" } -if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() -end +local next=next local fonts=fonts -fonts.constructors.namemode="specification" -function fonts.definers.getspecification(str) - return "",str,"",":",str -end -local list={} -local function issome () list.lookup='name' end -local function isfile () list.lookup='file' end -local function isname () list.lookup='name' end -local function thename(s) list.name=s end -local function issub (v) list.sub=v end -local function iscrap (s) list.crap=string.lower(s) end -local function iskey (k,v) list[k]=v end -local function istrue (s) list[s]=true end -local function isfalse(s) list[s]=false end -local P,S,R,C=lpeg.P,lpeg.S,lpeg.R,lpeg.C -local spaces=P(" ")^0 -local namespec=(1-S("/:("))^0 -local crapspec=spaces*P("/")*(((1-P(":"))^0)/iscrap)*spaces -local filename_1=P("file:")/isfile*(namespec/thename) -local filename_2=P("[")*P(true)/isname*(((1-P("]"))^0)/thename)*P("]") -local fontname_1=P("name:")/isname*(namespec/thename) -local fontname_2=P(true)/issome*(namespec/thename) -local sometext=(R("az","AZ","09")+S("+-.{}"))^1 -local truevalue=P("+")*spaces*(sometext/istrue) -local falsevalue=P("-")*spaces*(sometext/isfalse) -local keyvalue=(C(sometext)*spaces*P("=")*spaces*C(sometext))/iskey -local somevalue=sometext/istrue -local subvalue=P("(")*(C(P(1-S("()"))^1)/issub)*P(")") -local option=spaces*(keyvalue+falsevalue+truevalue+somevalue)*spaces -local options=P(":")*spaces*(P(";")^0*option)^0 -local pattern=(filename_1+filename_2+fontname_1+fontname_2)*subvalue^0*crapspec^0*options^0 -local function colonized(specification) - list={} - lpeg.match(pattern,specification.specification) - list.crap=nil - if list.name then - specification.name=list.name - list.name=nil - end - if list.lookup then - specification.lookup=list.lookup - list.lookup=nil - end - if list.sub then - specification.sub=list.sub - list.sub=nil +local otf=fonts.handlers.otf +local registerotffeature=otf.features.register +local addotffeature=otf.addfeature +local specification={ + type="ligature", + order={ "tlig" }, + prepend=true, + data={ + [0x2013]={ 0x002D,0x002D }, + [0x2014]={ 0x002D,0x002D,0x002D }, + }, +} +addotffeature("tlig",specification) +registerotffeature { + name="tlig", + description="tex ligatures", +} +local specification={ + type="substitution", + order={ "trep" }, + prepend=true, + data={ + [0x0027]=0x2019, + }, +} +addotffeature("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 - specification.features.normal=fonts.handlers.otf.features.normalize(list) - return specification end -fonts.definers.registersplit(":",colonized,"cryptic") -fonts.definers.registersplit("",colonized,"more cryptic") -function fonts.definers.applypostprocessors(tfmdata) - local postprocessors=tfmdata.postprocessors - if postprocessors then - for i=1,#postprocessors do - local extrahash=postprocessors[i](tfmdata) - if type(extrahash)=="string" and extrahash~="" then - extrahash=string.gsub(lower(extrahash),"[^a-z]","-") - tfmdata.properties.fullname=format("%s-%s",tfmdata.properties.fullname,extrahash) +local specification={ + { + type="substitution", + features={ arab={ urd=true,dflt=true } }, + order={ "anum" }, + data=anum_arabic, + valid=valid, + }, + { + type="substitution", + features={ arab={ urd=true } }, + order={ "anum" }, + data=anum_persian, + valid=valid, + }, +} +addotffeature("anum",specification) +registerotffeature { + name="anum", + description="arabic digits", +} + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-imp-ligatures']={ + 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 lpegmatch=lpeg.match +local utfsplit=utf.split +local settings_to_array=utilities.parsers.settings_to_array +local fonts=fonts +local otf=fonts.handlers.otf +local registerotffeature=otf.features.register +local addotffeature=otf.addfeature +local lookups={} +local protect={} +local revert={} +local zwjchar=0x200C +local zwj={ zwjchar } +addotffeature { + name="blockligatures", + type="chainsubstitution", + nocheck=true, + prepend=true, + future=true, + lookups={ + { + type="multiple", + data=lookups, + }, + }, + data={ + rules=protect, + } +} +addotffeature { + name="blockligatures", + type="chainsubstitution", + nocheck=true, + append=true, + overload=false, + lookups={ + { + type="ligature", + data=lookups, + }, + }, + data={ + rules=revert, + } +} +registerotffeature { + name='blockligatures', + description='block certain ligatures', +} +local splitter=lpeg.splitat(":") +local function blockligatures(str) + local t=settings_to_array(str) + for i=1,#t do + local ti=t[i] + local before,current,after=lpegmatch(splitter,ti) + if current and after then + if before then + before=utfsplit(before) + for i=1,#before do + before[i]={ before[i] } + end + end + if current then + current=utfsplit(current) + end + if after then + after=utfsplit(after) + for i=1,#after do + after[i]={ after[i] } + end end + else + before=nil + current=utfsplit(ti) + after=nil + end + if #current>1 then + local one=current[1] + local two=current[2] + lookups[one]={ one,zwjchar } + local one={ one } + local two={ two } + local new=#protect+1 + protect[new]={ + before=before, + current={ one,two }, + after=after, + lookups={ 1 }, + } + revert[new]={ + current={ one,zwj }, + after={ two }, + lookups={ 1 }, + } end end - return tfmdata +end +otf.helpers.blockligatures=blockligatures +if context then + +--removed + end end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules={} end modules ['luatex-fonts-ext']={ +if not modules then modules={} end modules ['font-imp-italics']={ version=1.001, - comment="companion to luatex-*.tex", + comment="companion to font-ini.mkiv and hand-ini.mkiv", 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 next=next local fonts=fonts -local otffeatures=fonts.constructors.features.otf -local function initializeitlc(tfmdata,value) +local handlers=fonts.handlers +local registerotffeature=handlers.otf.features.register +local registerafmfeature=handlers.afm.features.register +local function initialize(tfmdata,key,value) + for unicode,character in next,tfmdata.characters do + local olditalic=character.italic + if olditalic and olditalic~=0 then + character.width=character.width+olditalic + character.italic=0 + end + end +end +local specification={ + name="italicwidths", + description="add italic to width", + manipulators={ + base=initialize, + node=initialize, + } +} +registerotffeature(specification) +registerafmfeature(specification) +local function initialize(tfmdata,value) if value then local parameters=tfmdata.parameters local italicangle=parameters.italicangle @@ -26817,14 +34914,92 @@ local function initializeitlc(tfmdata,value) end end end -otffeatures.register { +local specification={ name="itlc", description="italic correction", initializers={ - base=initializeitlc, - node=initializeitlc, + base=initialize, + node=initialize, + } +} +registerotffeature(specification) +registerafmfeature(specification) +if context then + +--removed + +end +if context then + local letter=characters.is_letter + local always=true + local function collapseitalics(tfmdata,key,value) + local threshold=value==true and 100 or tonumber(value) + if threshold and threshold>0 then + if threshold>100 then + threshold=100 + end + for unicode,data in next,tfmdata.characters do + if always or letter[unicode] or letter[data.unicode] then + local italic=data.italic + if italic and italic~=0 then + local width=data.width + if width and width~=0 then + local delta=threshold*italic/100 + data.width=width+delta + data.italic=italic-delta + end + end + end + end + end + end + local dimensions_specification={ + name="collapseitalics", + description="collapse italics", + manipulators={ + base=collapseitalics, + node=collapseitalics, + } } + registerotffeature(dimensions_specification) + registerafmfeature(dimensions_specification) +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['font-imp-effects']={ + version=1.001, + comment="companion to font-ini.mkiv and hand-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 is_boolean=string.is_boolean +local fonts=fonts +local handlers=fonts.handlers +local registerotffeature=handlers.otf.features.register +local registerafmfeature=handlers.afm.features.register +local settings_to_hash=utilities.parsers.settings_to_hash_colon_too +local helpers=fonts.helpers +local prependcommands=helpers.prependcommands +local charcommand=helpers.commands.char +local leftcommand=helpers.commands.left +local rightcommand=helpers.commands.right +local upcommand=helpers.commands.up +local downcommand=helpers.commands.down +local dummycommand=helpers.commands.dummy +local report_effect=logs.reporter("fonts","effect") +local report_slant=logs.reporter("fonts","slant") +local report_extend=logs.reporter("fonts","extend") +local report_squeeze=logs.reporter("fonts","squeeze") +local trace=false +trackers.register("fonts.effect",function(v) trace=v end) +trackers.register("fonts.slant",function(v) trace=v end) +trackers.register("fonts.extend",function(v) trace=v end) +trackers.register("fonts.squeeze",function(v) trace=v end) local function initializeslant(tfmdata,value) value=tonumber(value) if not value then @@ -26834,9 +35009,12 @@ local function initializeslant(tfmdata,value) elseif value<-1 then value=-1 end + if trace then + report_slant("applying %0.3f",value) + end tfmdata.parameters.slantfactor=value end -otffeatures.register { +local specification={ name="slant", description="slant glyphs", initializers={ @@ -26844,6 +35022,8 @@ otffeatures.register { node=initializeslant, } } +registerotffeature(specification) +registerafmfeature(specification) local function initializeextend(tfmdata,value) value=tonumber(value) if not value then @@ -26853,9 +35033,12 @@ local function initializeextend(tfmdata,value) elseif value<-10 then value=-10 end + if trace then + report_extend("applying %0.3f",value) + end tfmdata.parameters.extendfactor=value end -otffeatures.register { +local specification={ name="extend", description="scale glyphs horizontally", initializers={ @@ -26863,168 +35046,2360 @@ otffeatures.register { 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 +registerotffeature(specification) +registerafmfeature(specification) +local function initializesqueeze(tfmdata,value) + value=tonumber(value) + if not value then + value=0 + elseif value>10 then + value=10 + elseif value<-10 then + value=-10 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 + if trace then + report_squeeze("applying %0.3f",value) end + tfmdata.parameters.squeezefactor=value end -otffeatures.register { - name="expansion", - description="apply hz optimization", +local specification={ + name="squeeze", + description="scale glyphs vertically", initializers={ - base=initializeexpansion, - node=initializeexpansion, + base=initializesqueeze, + node=initializesqueeze, } } -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 }, +registerotffeature(specification) +registerafmfeature(specification) +local effects={ + inner=0, + normal=0, + outer=1, + outline=1, + both=2, + hidden=3, } -fonts.handlers.otf.features.normalize=function(t) - if t.rand then - t.rand="random" +local function initializeeffect(tfmdata,value) + local spec + if type(value)=="number" then + spec={ width=value } + else + spec=settings_to_hash(value) + end + local effect=spec.effect or "both" + local width=tonumber(spec.width) or 0 + local mode=effects[effect] + if not mode then + report_effect("invalid effect %a",effect) + elseif width==0 and mode==0 then + report_effect("invalid width %a for effect %a",width,effect) + else + local parameters=tfmdata.parameters + local properties=tfmdata.properties + parameters.mode=mode + parameters.width=width*1000 + if is_boolean(spec.auto)==true then + local squeeze=1-width/20 + local average=(1-squeeze)*width*100 + spec.squeeze=squeeze + spec.extend=1+width/2 + spec.wdelta=average + spec.hdelta=average/2 + spec.ddelta=average/2 + spec.vshift=average/2 + end + local factor=tonumber(spec.factor) or 0 + local hfactor=tonumber(spec.hfactor) or factor + local vfactor=tonumber(spec.vfactor) or factor + local delta=tonumber(spec.delta) or 1 + local wdelta=tonumber(spec.wdelta) or delta + local hdelta=tonumber(spec.hdelta) or delta + local ddelta=tonumber(spec.ddelta) or hdelta + local vshift=tonumber(spec.vshift) or 0 + local slant=spec.slant + local extend=spec.extend + local squeeze=spec.squeeze + if slant then + initializeslant(tfmdata,slant) + end + if extend then + initializeextend(tfmdata,extend) + end + if squeeze then + initializesqueeze(tfmdata,squeeze) + end + properties.effect={ + effect=effect, + width=width, + factor=factor, + hfactor=hfactor, + vfactor=vfactor, + wdelta=wdelta, + hdelta=hdelta, + ddelta=ddelta, + vshift=vshift, + slant=tfmdata.parameters.slantfactor, + extend=tfmdata.parameters.extendfactor, + squeeze=tfmdata.parameters.squeezefactor, + } 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 +local rules={ + "RadicalRuleThickness", + "OverbarRuleThickness", + "FractionRuleThickness", + "UnderbarRuleThickness", +} +local function setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze) + if delta~=0 then + for i=1,#rules do + local name=rules[i] + local value=mathparameters[name] + if value then + mathparameters[name]=(squeeze or 1)*(value+dx) + end + end 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] +local function setmathcharacters(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta) + local function wdpatch(char) + if wsnap~=0 then + char.width=char.width+wdelta/2 end - for k,v in next,temp do - char[k]=temp[k] + end + local function htpatch(char) + if hsnap~=0 then + local height=char.height + if height then + char.height=char.height+2*dy + end + end + end + local character=characters[0x221A] + if character and character.next then + local char=character + local next=character.next + wdpatch(char) + htpatch(char) + while next do + char=characters[next] + wdpatch(char) + htpatch(char) + next=char.next + end + if char then + local v=char.vert_variants + if v then + local top=v[#v] + if top then + local char=characters[top.glyph] + htpatch(char) + end + end 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) +local function manipulateeffect(tfmdata) + local effect=tfmdata.properties.effect + if effect then + local characters=tfmdata.characters + local parameters=tfmdata.parameters + local mathparameters=tfmdata.mathparameters + local multiplier=effect.width*100 + local factor=parameters.factor + local hfactor=parameters.hfactor + local vfactor=parameters.vfactor + local wdelta=effect.wdelta*hfactor*multiplier + local hdelta=effect.hdelta*vfactor*multiplier + local ddelta=effect.ddelta*vfactor*multiplier + local vshift=effect.vshift*vfactor*multiplier + local squeeze=effect.squeeze + local hshift=wdelta/2 + local dx=multiplier*vfactor + local dy=vshift + local factor=(1+effect.factor)*factor + local hfactor=(1+effect.hfactor)*hfactor + local vfactor=(1+effect.vfactor)*vfactor + local vshift=vshift~=0 and upcommand[vshift] or false + for unicode,character in next,characters do + local oldwidth=character.width + local oldheight=character.height + local olddepth=character.depth + if oldwidth and oldwidth>0 then + character.width=oldwidth+wdelta + local commands=character.commands + local hshift=rightcommand[hshift] + if vshift then + if commands then + prependcommands (commands, + hshift, + vshift + ) + else + character.commands={ + hshift, + vshift, + charcommand[unicode] + } + end + else + if commands then + prependcommands (commands, + hshift + ) + else + character.commands={ + hshift, + charcommand[unicode] + } + end + end + end + if oldheight and oldheight>0 then + character.height=oldheight+hdelta + end + if olddepth and olddepth>0 then + character.depth=olddepth+ddelta + end end - ) + if mathparameters then + setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze) + setmathcharacters(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta) + end + parameters.factor=factor + parameters.hfactor=hfactor + parameters.vfactor=vfactor + if trace then + report_effect("applying") + report_effect(" effect : %s",effect.effect) + report_effect(" width : %s => %s",effect.width,multiplier) + report_effect(" factor : %s => %s",effect.factor,factor ) + report_effect(" hfactor : %s => %s",effect.hfactor,hfactor) + report_effect(" vfactor : %s => %s",effect.vfactor,vfactor) + report_effect(" wdelta : %s => %s",effect.wdelta,wdelta) + report_effect(" hdelta : %s => %s",effect.hdelta,hdelta) + report_effect(" ddelta : %s => %s",effect.ddelta,ddelta) + end + end end -otffeatures.register { - name="reencode", - description="reencode characters", +local specification={ + name="effect", + description="apply effects to glyphs", + initializers={ + base=initializeeffect, + node=initializeeffect, + }, manipulators={ - base=reencode, - node=reencode, - } + base=manipulateeffect, + node=manipulateeffect, + }, } -local function ignore(tfmdata,key,value) - if value then - tfmdata.mathparameters=nil +registerotffeature(specification) +registerafmfeature(specification) +local function initializeoutline(tfmdata,value) + value=tonumber(value) + if not value then + value=0 + else + value=tonumber(value) or 0 end + local parameters=tfmdata.parameters + local properties=tfmdata.properties + parameters.mode=effects.outline + parameters.width=value*1000 + properties.effect={ + effect=effect, + width=width, + } end -otffeatures.register { - name="ignoremathconstants", - description="ignore math constants table", +local specification={ + name="outline", + description="outline glyphs", initializers={ - base=ignore, - node=ignore, + base=initializeoutline, + node=initializeoutline, } } +registerotffeature(specification) +registerafmfeature(specification) + +end -- closure + +do -- begin closure to overcome local limits and interference + + +fonts.handlers.otf.addfeature { + ["dataset"]={ + { + ["data"]={ + ["À"]={ "A","̀" }, + ["Á"]={ "A","́" }, + ["Â"]={ "A","̂" }, + ["Ã"]={ "A","̃" }, + ["Ä"]={ "A","̈" }, + ["Å"]={ "A","̊" }, + ["Ç"]={ "C","̧" }, + ["È"]={ "E","̀" }, + ["É"]={ "E","́" }, + ["Ê"]={ "E","̂" }, + ["Ë"]={ "E","̈" }, + ["Ì"]={ "I","̀" }, + ["Í"]={ "I","́" }, + ["Î"]={ "I","̂" }, + ["Ï"]={ "I","̈" }, + ["Ñ"]={ "N","̃" }, + ["Ò"]={ "O","̀" }, + ["Ó"]={ "O","́" }, + ["Ô"]={ "O","̂" }, + ["Õ"]={ "O","̃" }, + ["Ö"]={ "O","̈" }, + ["Ù"]={ "U","̀" }, + ["Ú"]={ "U","́" }, + ["Û"]={ "U","̂" }, + ["Ü"]={ "U","̈" }, + ["Ý"]={ "Y","́" }, + ["à"]={ "a","̀" }, + ["á"]={ "a","́" }, + ["â"]={ "a","̂" }, + ["ã"]={ "a","̃" }, + ["ä"]={ "a","̈" }, + ["å"]={ "a","̊" }, + ["ç"]={ "c","̧" }, + ["è"]={ "e","̀" }, + ["é"]={ "e","́" }, + ["ê"]={ "e","̂" }, + ["ë"]={ "e","̈" }, + ["ì"]={ "i","̀" }, + ["í"]={ "i","́" }, + ["î"]={ "i","̂" }, + ["ï"]={ "i","̈" }, + ["ñ"]={ "n","̃" }, + ["ò"]={ "o","̀" }, + ["ó"]={ "o","́" }, + ["ô"]={ "o","̂" }, + ["õ"]={ "o","̃" }, + ["ö"]={ "o","̈" }, + ["ù"]={ "u","̀" }, + ["ú"]={ "u","́" }, + ["û"]={ "u","̂" }, + ["ü"]={ "u","̈" }, + ["ý"]={ "y","́" }, + ["ÿ"]={ "y","̈" }, + ["Ā"]={ "A","̄" }, + ["ā"]={ "a","̄" }, + ["Ă"]={ "A","̆" }, + ["ă"]={ "a","̆" }, + ["Ą"]={ "A","̨" }, + ["ą"]={ "a","̨" }, + ["Ć"]={ "C","́" }, + ["ć"]={ "c","́" }, + ["Ĉ"]={ "C","̂" }, + ["ĉ"]={ "c","̂" }, + ["Ċ"]={ "C","̇" }, + ["ċ"]={ "c","̇" }, + ["Č"]={ "C","̌" }, + ["č"]={ "c","̌" }, + ["Ď"]={ "D","̌" }, + ["ď"]={ "d","̌" }, + ["Ē"]={ "E","̄" }, + ["ē"]={ "e","̄" }, + ["Ĕ"]={ "E","̆" }, + ["ĕ"]={ "e","̆" }, + ["Ė"]={ "E","̇" }, + ["ė"]={ "e","̇" }, + ["Ę"]={ "E","̨" }, + ["ę"]={ "e","̨" }, + ["Ě"]={ "E","̌" }, + ["ě"]={ "e","̌" }, + ["Ĝ"]={ "G","̂" }, + ["ĝ"]={ "g","̂" }, + ["Ğ"]={ "G","̆" }, + ["ğ"]={ "g","̆" }, + ["Ġ"]={ "G","̇" }, + ["ġ"]={ "g","̇" }, + ["Ģ"]={ "G","̧" }, + ["ģ"]={ "g","̧" }, + ["Ĥ"]={ "H","̂" }, + ["ĥ"]={ "h","̂" }, + ["Ĩ"]={ "I","̃" }, + ["ĩ"]={ "i","̃" }, + ["Ī"]={ "I","̄" }, + ["ī"]={ "i","̄" }, + ["Ĭ"]={ "I","̆" }, + ["ĭ"]={ "i","̆" }, + ["Į"]={ "I","̨" }, + ["į"]={ "i","̨" }, + ["İ"]={ "I","̇" }, + ["Ĵ"]={ "J","̂" }, + ["ĵ"]={ "j","̂" }, + ["Ķ"]={ "K","̧" }, + ["ķ"]={ "k","̧" }, + ["Ĺ"]={ "L","́" }, + ["ĺ"]={ "l","́" }, + ["Ļ"]={ "L","̧" }, + ["ļ"]={ "l","̧" }, + ["Ľ"]={ "L","̌" }, + ["ľ"]={ "l","̌" }, + ["Ń"]={ "N","́" }, + ["ń"]={ "n","́" }, + ["Ņ"]={ "N","̧" }, + ["ņ"]={ "n","̧" }, + ["Ň"]={ "N","̌" }, + ["ň"]={ "n","̌" }, + ["Ō"]={ "O","̄" }, + ["ō"]={ "o","̄" }, + ["Ŏ"]={ "O","̆" }, + ["ŏ"]={ "o","̆" }, + ["Ő"]={ "O","̋" }, + ["ő"]={ "o","̋" }, + ["Ŕ"]={ "R","́" }, + ["ŕ"]={ "r","́" }, + ["Ŗ"]={ "R","̧" }, + ["ŗ"]={ "r","̧" }, + ["Ř"]={ "R","̌" }, + ["ř"]={ "r","̌" }, + ["Ś"]={ "S","́" }, + ["ś"]={ "s","́" }, + ["Ŝ"]={ "S","̂" }, + ["ŝ"]={ "s","̂" }, + ["Ş"]={ "S","̧" }, + ["ş"]={ "s","̧" }, + ["Š"]={ "S","̌" }, + ["š"]={ "s","̌" }, + ["Ţ"]={ "T","̧" }, + ["ţ"]={ "t","̧" }, + ["Ť"]={ "T","̌" }, + ["ť"]={ "t","̌" }, + ["Ũ"]={ "U","̃" }, + ["ũ"]={ "u","̃" }, + ["Ū"]={ "U","̄" }, + ["ū"]={ "u","̄" }, + ["Ŭ"]={ "U","̆" }, + ["ŭ"]={ "u","̆" }, + ["Ů"]={ "U","̊" }, + ["ů"]={ "u","̊" }, + ["Ű"]={ "U","̋" }, + ["ű"]={ "u","̋" }, + ["Ų"]={ "U","̨" }, + ["ų"]={ "u","̨" }, + ["Ŵ"]={ "W","̂" }, + ["ŵ"]={ "w","̂" }, + ["Ŷ"]={ "Y","̂" }, + ["ŷ"]={ "y","̂" }, + ["Ÿ"]={ "Y","̈" }, + ["Ź"]={ "Z","́" }, + ["ź"]={ "z","́" }, + ["Ż"]={ "Z","̇" }, + ["ż"]={ "z","̇" }, + ["Ž"]={ "Z","̌" }, + ["ž"]={ "z","̌" }, + ["Ơ"]={ "O","̛" }, + ["ơ"]={ "o","̛" }, + ["Ư"]={ "U","̛" }, + ["ư"]={ "u","̛" }, + ["Ǎ"]={ "A","̌" }, + ["ǎ"]={ "a","̌" }, + ["Ǐ"]={ "I","̌" }, + ["ǐ"]={ "i","̌" }, + ["Ǒ"]={ "O","̌" }, + ["ǒ"]={ "o","̌" }, + ["Ǔ"]={ "U","̌" }, + ["ǔ"]={ "u","̌" }, + ["Ǖ"]={ "Ü","̄" }, + ["ǖ"]={ "ü","̄" }, + ["Ǘ"]={ "Ü","́" }, + ["ǘ"]={ "ü","́" }, + ["Ǚ"]={ "Ü","̌" }, + ["ǚ"]={ "ü","̌" }, + ["Ǜ"]={ "Ü","̀" }, + ["ǜ"]={ "ü","̀" }, + ["Ǟ"]={ "Ä","̄" }, + ["ǟ"]={ "ä","̄" }, + ["Ǡ"]={ "Ȧ","̄" }, + ["ǡ"]={ "ȧ","̄" }, + ["Ǣ"]={ "Æ","̄" }, + ["ǣ"]={ "æ","̄" }, + ["Ǧ"]={ "G","̌" }, + ["ǧ"]={ "g","̌" }, + ["Ǩ"]={ "K","̌" }, + ["ǩ"]={ "k","̌" }, + ["Ǫ"]={ "O","̨" }, + ["ǫ"]={ "o","̨" }, + ["Ǭ"]={ "Ǫ","̄" }, + ["ǭ"]={ "ǫ","̄" }, + ["Ǯ"]={ "Ʒ","̌" }, + ["ǯ"]={ "ʒ","̌" }, + ["ǰ"]={ "j","̌" }, + ["Ǵ"]={ "G","́" }, + ["ǵ"]={ "g","́" }, + ["Ǹ"]={ "N","̀" }, + ["ǹ"]={ "n","̀" }, + ["Ǻ"]={ "Å","́" }, + ["ǻ"]={ "å","́" }, + ["Ǽ"]={ "Æ","́" }, + ["ǽ"]={ "æ","́" }, + ["Ǿ"]={ "Ø","́" }, + ["ǿ"]={ "ø","́" }, + ["Ȁ"]={ "A","̏" }, + ["ȁ"]={ "a","̏" }, + ["Ȃ"]={ "A","̑" }, + ["ȃ"]={ "a","̑" }, + ["Ȅ"]={ "E","̏" }, + ["ȅ"]={ "e","̏" }, + ["Ȇ"]={ "E","̑" }, + ["ȇ"]={ "e","̑" }, + ["Ȉ"]={ "I","̏" }, + ["ȉ"]={ "i","̏" }, + ["Ȋ"]={ "I","̑" }, + ["ȋ"]={ "i","̑" }, + ["Ȍ"]={ "O","̏" }, + ["ȍ"]={ "o","̏" }, + ["Ȏ"]={ "O","̑" }, + ["ȏ"]={ "o","̑" }, + ["Ȑ"]={ "R","̏" }, + ["ȑ"]={ "r","̏" }, + ["Ȓ"]={ "R","̑" }, + ["ȓ"]={ "r","̑" }, + ["Ȕ"]={ "U","̏" }, + ["ȕ"]={ "u","̏" }, + ["Ȗ"]={ "U","̑" }, + ["ȗ"]={ "u","̑" }, + ["Ș"]={ "S","̦" }, + ["ș"]={ "s","̦" }, + ["Ț"]={ "T","̦" }, + ["ț"]={ "t","̦" }, + ["Ȟ"]={ "H","̌" }, + ["ȟ"]={ "h","̌" }, + ["Ȧ"]={ "A","̇" }, + ["ȧ"]={ "a","̇" }, + ["Ȩ"]={ "E","̧" }, + ["ȩ"]={ "e","̧" }, + ["Ȫ"]={ "Ö","̄" }, + ["ȫ"]={ "ö","̄" }, + ["Ȭ"]={ "Õ","̄" }, + ["ȭ"]={ "õ","̄" }, + ["Ȯ"]={ "O","̇" }, + ["ȯ"]={ "o","̇" }, + ["Ȱ"]={ "Ȯ","̄" }, + ["ȱ"]={ "ȯ","̄" }, + ["Ȳ"]={ "Y","̄" }, + ["ȳ"]={ "y","̄" }, + ["̈́"]={ "̈","́" }, + ["΅"]={ "¨","́" }, + ["Ά"]={ "Α","́" }, + ["Έ"]={ "Ε","́" }, + ["Ή"]={ "Η","́" }, + ["Ί"]={ "Ι","́" }, + ["Ό"]={ "Ο","́" }, + ["Ύ"]={ "Υ","́" }, + ["Ώ"]={ "Ω","́" }, + ["ΐ"]={ "ϊ","́" }, + ["Ϊ"]={ "Ι","̈" }, + ["Ϋ"]={ "Υ","̈" }, + ["ά"]={ "α","́" }, + ["έ"]={ "ε","́" }, + ["ή"]={ "η","́" }, + ["ί"]={ "ι","́" }, + ["ΰ"]={ "ϋ","́" }, + ["ϊ"]={ "ι","̈" }, + ["ϋ"]={ "υ","̈" }, + ["ό"]={ "ο","́" }, + ["ύ"]={ "υ","́" }, + ["ώ"]={ "ω","́" }, + ["ϓ"]={ "ϒ","́" }, + ["ϔ"]={ "ϒ","̈" }, + ["Ѐ"]={ "Е","̀" }, + ["Ё"]={ "Е","̈" }, + ["Ѓ"]={ "Г","́" }, + ["Ї"]={ "І","̈" }, + ["Ќ"]={ "К","́" }, + ["Ѝ"]={ "И","̀" }, + ["Ў"]={ "У","̆" }, + ["Й"]={ "И","̆" }, + ["й"]={ "и","̆" }, + ["ѐ"]={ "е","̀" }, + ["ё"]={ "е","̈" }, + ["ѓ"]={ "г","́" }, + ["ї"]={ "і","̈" }, + ["ќ"]={ "к","́" }, + ["ѝ"]={ "и","̀" }, + ["ў"]={ "у","̆" }, + ["Ѷ"]={ "Ѵ","̏" }, + ["ѷ"]={ "ѵ","̏" }, + ["Ӂ"]={ "Ж","̆" }, + ["ӂ"]={ "ж","̆" }, + ["Ӑ"]={ "А","̆" }, + ["ӑ"]={ "а","̆" }, + ["Ӓ"]={ "А","̈" }, + ["ӓ"]={ "а","̈" }, + ["Ӗ"]={ "Е","̆" }, + ["ӗ"]={ "е","̆" }, + ["Ӛ"]={ "Ә","̈" }, + ["ӛ"]={ "ә","̈" }, + ["Ӝ"]={ "Ж","̈" }, + ["ӝ"]={ "ж","̈" }, + ["Ӟ"]={ "З","̈" }, + ["ӟ"]={ "з","̈" }, + ["Ӣ"]={ "И","̄" }, + ["ӣ"]={ "и","̄" }, + ["Ӥ"]={ "И","̈" }, + ["ӥ"]={ "и","̈" }, + ["Ӧ"]={ "О","̈" }, + ["ӧ"]={ "о","̈" }, + ["Ӫ"]={ "Ө","̈" }, + ["ӫ"]={ "ө","̈" }, + ["Ӭ"]={ "Э","̈" }, + ["ӭ"]={ "э","̈" }, + ["Ӯ"]={ "У","̄" }, + ["ӯ"]={ "у","̄" }, + ["Ӱ"]={ "У","̈" }, + ["ӱ"]={ "у","̈" }, + ["Ӳ"]={ "У","̋" }, + ["ӳ"]={ "у","̋" }, + ["Ӵ"]={ "Ч","̈" }, + ["ӵ"]={ "ч","̈" }, + ["Ӹ"]={ "Ы","̈" }, + ["ӹ"]={ "ы","̈" }, + ["آ"]={ "ا","ٓ" }, + ["أ"]={ "ا","ٔ" }, + ["ؤ"]={ "و","ٔ" }, + ["إ"]={ "ا","ٕ" }, + ["ئ"]={ "ي","ٔ" }, + ["ۀ"]={ "ە","ٔ" }, + ["ۂ"]={ "ہ","ٔ" }, + ["ۓ"]={ "ے","ٔ" }, + ["ऩ"]={ "न","़" }, + ["ऱ"]={ "र","़" }, + ["ऴ"]={ "ळ","़" }, + ["क़"]={ "क","़" }, + ["ख़"]={ "ख","़" }, + ["ग़"]={ "ग","़" }, + ["ज़"]={ "ज","़" }, + ["ड़"]={ "ड","़" }, + ["ढ़"]={ "ढ","़" }, + ["फ़"]={ "फ","़" }, + ["य़"]={ "य","़" }, + ["ো"]={ "ে","া" }, + ["ৌ"]={ "ে","ৗ" }, + ["ড়"]={ "ড","়" }, + ["ঢ়"]={ "ঢ","়" }, + ["য়"]={ "য","়" }, + ["ਲ਼"]={ "ਲ","਼" }, + ["ਸ਼"]={ "ਸ","਼" }, + ["ਖ਼"]={ "ਖ","਼" }, + ["ਗ਼"]={ "ਗ","਼" }, + ["ਜ਼"]={ "ਜ","਼" }, + ["ਫ਼"]={ "ਫ","਼" }, + ["ୈ"]={ "େ","ୖ" }, + ["ୋ"]={ "େ","ା" }, + ["ୌ"]={ "େ","ୗ" }, + ["ଡ଼"]={ "ଡ","଼" }, + ["ଢ଼"]={ "ଢ","଼" }, + ["ஔ"]={ "ஒ","ௗ" }, + ["ொ"]={ "ெ","ா" }, + ["ோ"]={ "ே","ா" }, + ["ௌ"]={ "ெ","ௗ" }, + ["ై"]={ "ె","ౖ" }, + ["ೀ"]={ "ಿ","ೕ" }, + ["ೇ"]={ "ೆ","ೕ" }, + ["ೈ"]={ "ೆ","ೖ" }, + ["ೊ"]={ "ೆ","ೂ" }, + ["ೋ"]={ "ೊ","ೕ" }, + ["ൊ"]={ "െ","ാ" }, + ["ോ"]={ "േ","ാ" }, + ["ൌ"]={ "െ","ൗ" }, + ["ේ"]={ "ෙ","්" }, + ["ො"]={ "ෙ","ා" }, + ["ෝ"]={ "ො","්" }, + ["ෞ"]={ "ෙ","ෟ" }, + ["གྷ"]={ "ག","ྷ" }, + ["ཌྷ"]={ "ཌ","ྷ" }, + ["དྷ"]={ "ད","ྷ" }, + ["བྷ"]={ "བ","ྷ" }, + ["ཛྷ"]={ "ཛ","ྷ" }, + ["ཀྵ"]={ "ཀ","ྵ" }, + ["ཱི"]={ "ཱ","ི" }, + ["ཱུ"]={ "ཱ","ུ" }, + ["ྲྀ"]={ "ྲ","ྀ" }, + ["ླྀ"]={ "ླ","ྀ" }, + ["ཱྀ"]={ "ཱ","ྀ" }, + ["ྒྷ"]={ "ྒ","ྷ" }, + ["ྜྷ"]={ "ྜ","ྷ" }, + ["ྡྷ"]={ "ྡ","ྷ" }, + ["ྦྷ"]={ "ྦ","ྷ" }, + ["ྫྷ"]={ "ྫ","ྷ" }, + ["ྐྵ"]={ "ྐ","ྵ" }, + ["ဦ"]={ "ဥ","ီ" }, + ["ᬆ"]={ "ᬅ","ᬵ" }, + ["ᬈ"]={ "ᬇ","ᬵ" }, + ["ᬊ"]={ "ᬉ","ᬵ" }, + ["ᬌ"]={ "ᬋ","ᬵ" }, + ["ᬎ"]={ "ᬍ","ᬵ" }, + ["ᬒ"]={ "ᬑ","ᬵ" }, + ["ᬻ"]={ "ᬺ","ᬵ" }, + ["ᬽ"]={ "ᬼ","ᬵ" }, + ["ᭀ"]={ "ᬾ","ᬵ" }, + ["ᭁ"]={ "ᬿ","ᬵ" }, + ["ᭃ"]={ "ᭂ","ᬵ" }, + ["Ḁ"]={ "A","̥" }, + ["ḁ"]={ "a","̥" }, + ["Ḃ"]={ "B","̇" }, + ["ḃ"]={ "b","̇" }, + ["Ḅ"]={ "B","̣" }, + ["ḅ"]={ "b","̣" }, + ["Ḇ"]={ "B","̱" }, + ["ḇ"]={ "b","̱" }, + ["Ḉ"]={ "Ç","́" }, + ["ḉ"]={ "ç","́" }, + ["Ḋ"]={ "D","̇" }, + ["ḋ"]={ "d","̇" }, + ["Ḍ"]={ "D","̣" }, + ["ḍ"]={ "d","̣" }, + ["Ḏ"]={ "D","̱" }, + ["ḏ"]={ "d","̱" }, + ["Ḑ"]={ "D","̧" }, + ["ḑ"]={ "d","̧" }, + ["Ḓ"]={ "D","̭" }, + ["ḓ"]={ "d","̭" }, + ["Ḕ"]={ "Ē","̀" }, + ["ḕ"]={ "ē","̀" }, + ["Ḗ"]={ "Ē","́" }, + ["ḗ"]={ "ē","́" }, + ["Ḙ"]={ "E","̭" }, + ["ḙ"]={ "e","̭" }, + ["Ḛ"]={ "E","̰" }, + ["ḛ"]={ "e","̰" }, + ["Ḝ"]={ "Ȩ","̆" }, + ["ḝ"]={ "ȩ","̆" }, + ["Ḟ"]={ "F","̇" }, + ["ḟ"]={ "f","̇" }, + ["Ḡ"]={ "G","̄" }, + ["ḡ"]={ "g","̄" }, + ["Ḣ"]={ "H","̇" }, + ["ḣ"]={ "h","̇" }, + ["Ḥ"]={ "H","̣" }, + ["ḥ"]={ "h","̣" }, + ["Ḧ"]={ "H","̈" }, + ["ḧ"]={ "h","̈" }, + ["Ḩ"]={ "H","̧" }, + ["ḩ"]={ "h","̧" }, + ["Ḫ"]={ "H","̮" }, + ["ḫ"]={ "h","̮" }, + ["Ḭ"]={ "I","̰" }, + ["ḭ"]={ "i","̰" }, + ["Ḯ"]={ "Ï","́" }, + ["ḯ"]={ "ï","́" }, + ["Ḱ"]={ "K","́" }, + ["ḱ"]={ "k","́" }, + ["Ḳ"]={ "K","̣" }, + ["ḳ"]={ "k","̣" }, + ["Ḵ"]={ "K","̱" }, + ["ḵ"]={ "k","̱" }, + ["Ḷ"]={ "L","̣" }, + ["ḷ"]={ "l","̣" }, + ["Ḹ"]={ "Ḷ","̄" }, + ["ḹ"]={ "ḷ","̄" }, + ["Ḻ"]={ "L","̱" }, + ["ḻ"]={ "l","̱" }, + ["Ḽ"]={ "L","̭" }, + ["ḽ"]={ "l","̭" }, + ["Ḿ"]={ "M","́" }, + ["ḿ"]={ "m","́" }, + ["Ṁ"]={ "M","̇" }, + ["ṁ"]={ "m","̇" }, + ["Ṃ"]={ "M","̣" }, + ["ṃ"]={ "m","̣" }, + ["Ṅ"]={ "N","̇" }, + ["ṅ"]={ "n","̇" }, + ["Ṇ"]={ "N","̣" }, + ["ṇ"]={ "n","̣" }, + ["Ṉ"]={ "N","̱" }, + ["ṉ"]={ "n","̱" }, + ["Ṋ"]={ "N","̭" }, + ["ṋ"]={ "n","̭" }, + ["Ṍ"]={ "Õ","́" }, + ["ṍ"]={ "õ","́" }, + ["Ṏ"]={ "Õ","̈" }, + ["ṏ"]={ "õ","̈" }, + ["Ṑ"]={ "Ō","̀" }, + ["ṑ"]={ "ō","̀" }, + ["Ṓ"]={ "Ō","́" }, + ["ṓ"]={ "ō","́" }, + ["Ṕ"]={ "P","́" }, + ["ṕ"]={ "p","́" }, + ["Ṗ"]={ "P","̇" }, + ["ṗ"]={ "p","̇" }, + ["Ṙ"]={ "R","̇" }, + ["ṙ"]={ "r","̇" }, + ["Ṛ"]={ "R","̣" }, + ["ṛ"]={ "r","̣" }, + ["Ṝ"]={ "Ṛ","̄" }, + ["ṝ"]={ "ṛ","̄" }, + ["Ṟ"]={ "R","̱" }, + ["ṟ"]={ "r","̱" }, + ["Ṡ"]={ "S","̇" }, + ["ṡ"]={ "s","̇" }, + ["Ṣ"]={ "S","̣" }, + ["ṣ"]={ "s","̣" }, + ["Ṥ"]={ "Ś","̇" }, + ["ṥ"]={ "ś","̇" }, + ["Ṧ"]={ "Š","̇" }, + ["ṧ"]={ "š","̇" }, + ["Ṩ"]={ "Ṣ","̇" }, + ["ṩ"]={ "ṣ","̇" }, + ["Ṫ"]={ "T","̇" }, + ["ṫ"]={ "t","̇" }, + ["Ṭ"]={ "T","̣" }, + ["ṭ"]={ "t","̣" }, + ["Ṯ"]={ "T","̱" }, + ["ṯ"]={ "t","̱" }, + ["Ṱ"]={ "T","̭" }, + ["ṱ"]={ "t","̭" }, + ["Ṳ"]={ "U","̤" }, + ["ṳ"]={ "u","̤" }, + ["Ṵ"]={ "U","̰" }, + ["ṵ"]={ "u","̰" }, + ["Ṷ"]={ "U","̭" }, + ["ṷ"]={ "u","̭" }, + ["Ṹ"]={ "Ũ","́" }, + ["ṹ"]={ "ũ","́" }, + ["Ṻ"]={ "Ū","̈" }, + ["ṻ"]={ "ū","̈" }, + ["Ṽ"]={ "V","̃" }, + ["ṽ"]={ "v","̃" }, + ["Ṿ"]={ "V","̣" }, + ["ṿ"]={ "v","̣" }, + ["Ẁ"]={ "W","̀" }, + ["ẁ"]={ "w","̀" }, + ["Ẃ"]={ "W","́" }, + ["ẃ"]={ "w","́" }, + ["Ẅ"]={ "W","̈" }, + ["ẅ"]={ "w","̈" }, + ["Ẇ"]={ "W","̇" }, + ["ẇ"]={ "w","̇" }, + ["Ẉ"]={ "W","̣" }, + ["ẉ"]={ "w","̣" }, + ["Ẋ"]={ "X","̇" }, + ["ẋ"]={ "x","̇" }, + ["Ẍ"]={ "X","̈" }, + ["ẍ"]={ "x","̈" }, + ["Ẏ"]={ "Y","̇" }, + ["ẏ"]={ "y","̇" }, + ["Ẑ"]={ "Z","̂" }, + ["ẑ"]={ "z","̂" }, + ["Ẓ"]={ "Z","̣" }, + ["ẓ"]={ "z","̣" }, + ["Ẕ"]={ "Z","̱" }, + ["ẕ"]={ "z","̱" }, + ["ẖ"]={ "h","̱" }, + ["ẗ"]={ "t","̈" }, + ["ẘ"]={ "w","̊" }, + ["ẙ"]={ "y","̊" }, + ["ẛ"]={ "ſ","̇" }, + ["Ạ"]={ "A","̣" }, + ["ạ"]={ "a","̣" }, + ["Ả"]={ "A","̉" }, + ["ả"]={ "a","̉" }, + ["Ấ"]={ "Â","́" }, + ["ấ"]={ "â","́" }, + ["Ầ"]={ "Â","̀" }, + ["ầ"]={ "â","̀" }, + ["Ẩ"]={ "Â","̉" }, + ["ẩ"]={ "â","̉" }, + ["Ẫ"]={ "Â","̃" }, + ["ẫ"]={ "â","̃" }, + ["Ậ"]={ "Ạ","̂" }, + ["ậ"]={ "ạ","̂" }, + ["Ắ"]={ "Ă","́" }, + ["ắ"]={ "ă","́" }, + ["Ằ"]={ "Ă","̀" }, + ["ằ"]={ "ă","̀" }, + ["Ẳ"]={ "Ă","̉" }, + ["ẳ"]={ "ă","̉" }, + ["Ẵ"]={ "Ă","̃" }, + ["ẵ"]={ "ă","̃" }, + ["Ặ"]={ "Ạ","̆" }, + ["ặ"]={ "ạ","̆" }, + ["Ẹ"]={ "E","̣" }, + ["ẹ"]={ "e","̣" }, + ["Ẻ"]={ "E","̉" }, + ["ẻ"]={ "e","̉" }, + ["Ẽ"]={ "E","̃" }, + ["ẽ"]={ "e","̃" }, + ["Ế"]={ "Ê","́" }, + ["ế"]={ "ê","́" }, + ["Ề"]={ "Ê","̀" }, + ["ề"]={ "ê","̀" }, + ["Ể"]={ "Ê","̉" }, + ["ể"]={ "ê","̉" }, + ["Ễ"]={ "Ê","̃" }, + ["ễ"]={ "ê","̃" }, + ["Ệ"]={ "Ẹ","̂" }, + ["ệ"]={ "ẹ","̂" }, + ["Ỉ"]={ "I","̉" }, + ["ỉ"]={ "i","̉" }, + ["Ị"]={ "I","̣" }, + ["ị"]={ "i","̣" }, + ["Ọ"]={ "O","̣" }, + ["ọ"]={ "o","̣" }, + ["Ỏ"]={ "O","̉" }, + ["ỏ"]={ "o","̉" }, + ["Ố"]={ "Ô","́" }, + ["ố"]={ "ô","́" }, + ["Ồ"]={ "Ô","̀" }, + ["ồ"]={ "ô","̀" }, + ["Ổ"]={ "Ô","̉" }, + ["ổ"]={ "ô","̉" }, + ["Ỗ"]={ "Ô","̃" }, + ["ỗ"]={ "ô","̃" }, + ["Ộ"]={ "Ọ","̂" }, + ["ộ"]={ "ọ","̂" }, + ["Ớ"]={ "Ơ","́" }, + ["ớ"]={ "ơ","́" }, + ["Ờ"]={ "Ơ","̀" }, + ["ờ"]={ "ơ","̀" }, + ["Ở"]={ "Ơ","̉" }, + ["ở"]={ "ơ","̉" }, + ["Ỡ"]={ "Ơ","̃" }, + ["ỡ"]={ "ơ","̃" }, + ["Ợ"]={ "Ơ","̣" }, + ["ợ"]={ "ơ","̣" }, + ["Ụ"]={ "U","̣" }, + ["ụ"]={ "u","̣" }, + ["Ủ"]={ "U","̉" }, + ["ủ"]={ "u","̉" }, + ["Ứ"]={ "Ư","́" }, + ["ứ"]={ "ư","́" }, + ["Ừ"]={ "Ư","̀" }, + ["ừ"]={ "ư","̀" }, + ["Ử"]={ "Ư","̉" }, + ["ử"]={ "ư","̉" }, + ["Ữ"]={ "Ư","̃" }, + ["ữ"]={ "ư","̃" }, + ["Ự"]={ "Ư","̣" }, + ["ự"]={ "ư","̣" }, + ["Ỳ"]={ "Y","̀" }, + ["ỳ"]={ "y","̀" }, + ["Ỵ"]={ "Y","̣" }, + ["ỵ"]={ "y","̣" }, + ["Ỷ"]={ "Y","̉" }, + ["ỷ"]={ "y","̉" }, + ["Ỹ"]={ "Y","̃" }, + ["ỹ"]={ "y","̃" }, + ["ἀ"]={ "α","̓" }, + ["ἁ"]={ "α","̔" }, + ["ἂ"]={ "ἀ","̀" }, + ["ἃ"]={ "ἁ","̀" }, + ["ἄ"]={ "ἀ","́" }, + ["ἅ"]={ "ἁ","́" }, + ["ἆ"]={ "ἀ","͂" }, + ["ἇ"]={ "ἁ","͂" }, + ["Ἀ"]={ "Α","̓" }, + ["Ἁ"]={ "Α","̔" }, + ["Ἂ"]={ "Ἀ","̀" }, + ["Ἃ"]={ "Ἁ","̀" }, + ["Ἄ"]={ "Ἀ","́" }, + ["Ἅ"]={ "Ἁ","́" }, + ["Ἆ"]={ "Ἀ","͂" }, + ["Ἇ"]={ "Ἁ","͂" }, + ["ἐ"]={ "ε","̓" }, + ["ἑ"]={ "ε","̔" }, + ["ἒ"]={ "ἐ","̀" }, + ["ἓ"]={ "ἑ","̀" }, + ["ἔ"]={ "ἐ","́" }, + ["ἕ"]={ "ἑ","́" }, + ["Ἐ"]={ "Ε","̓" }, + ["Ἑ"]={ "Ε","̔" }, + ["Ἒ"]={ "Ἐ","̀" }, + ["Ἓ"]={ "Ἑ","̀" }, + ["Ἔ"]={ "Ἐ","́" }, + ["Ἕ"]={ "Ἑ","́" }, + ["ἠ"]={ "η","̓" }, + ["ἡ"]={ "η","̔" }, + ["ἢ"]={ "ἠ","̀" }, + ["ἣ"]={ "ἡ","̀" }, + ["ἤ"]={ "ἠ","́" }, + ["ἥ"]={ "ἡ","́" }, + ["ἦ"]={ "ἠ","͂" }, + ["ἧ"]={ "ἡ","͂" }, + ["Ἠ"]={ "Η","̓" }, + ["Ἡ"]={ "Η","̔" }, + ["Ἢ"]={ "Ἠ","̀" }, + ["Ἣ"]={ "Ἡ","̀" }, + ["Ἤ"]={ "Ἠ","́" }, + ["Ἥ"]={ "Ἡ","́" }, + ["Ἦ"]={ "Ἠ","͂" }, + ["Ἧ"]={ "Ἡ","͂" }, + ["ἰ"]={ "ι","̓" }, + ["ἱ"]={ "ι","̔" }, + ["ἲ"]={ "ἰ","̀" }, + ["ἳ"]={ "ἱ","̀" }, + ["ἴ"]={ "ἰ","́" }, + ["ἵ"]={ "ἱ","́" }, + ["ἶ"]={ "ἰ","͂" }, + ["ἷ"]={ "ἱ","͂" }, + ["Ἰ"]={ "Ι","̓" }, + ["Ἱ"]={ "Ι","̔" }, + ["Ἲ"]={ "Ἰ","̀" }, + ["Ἳ"]={ "Ἱ","̀" }, + ["Ἴ"]={ "Ἰ","́" }, + ["Ἵ"]={ "Ἱ","́" }, + ["Ἶ"]={ "Ἰ","͂" }, + ["Ἷ"]={ "Ἱ","͂" }, + ["ὀ"]={ "ο","̓" }, + ["ὁ"]={ "ο","̔" }, + ["ὂ"]={ "ὀ","̀" }, + ["ὃ"]={ "ὁ","̀" }, + ["ὄ"]={ "ὀ","́" }, + ["ὅ"]={ "ὁ","́" }, + ["Ὀ"]={ "Ο","̓" }, + ["Ὁ"]={ "Ο","̔" }, + ["Ὂ"]={ "Ὀ","̀" }, + ["Ὃ"]={ "Ὁ","̀" }, + ["Ὄ"]={ "Ὀ","́" }, + ["Ὅ"]={ "Ὁ","́" }, + ["ὐ"]={ "υ","̓" }, + ["ὑ"]={ "υ","̔" }, + ["ὒ"]={ "ὐ","̀" }, + ["ὓ"]={ "ὑ","̀" }, + ["ὔ"]={ "ὐ","́" }, + ["ὕ"]={ "ὑ","́" }, + ["ὖ"]={ "ὐ","͂" }, + ["ὗ"]={ "ὑ","͂" }, + ["Ὑ"]={ "Υ","̔" }, + ["Ὓ"]={ "Ὑ","̀" }, + ["Ὕ"]={ "Ὑ","́" }, + ["Ὗ"]={ "Ὑ","͂" }, + ["ὠ"]={ "ω","̓" }, + ["ὡ"]={ "ω","̔" }, + ["ὢ"]={ "ὠ","̀" }, + ["ὣ"]={ "ὡ","̀" }, + ["ὤ"]={ "ὠ","́" }, + ["ὥ"]={ "ὡ","́" }, + ["ὦ"]={ "ὠ","͂" }, + ["ὧ"]={ "ὡ","͂" }, + ["Ὠ"]={ "Ω","̓" }, + ["Ὡ"]={ "Ω","̔" }, + ["Ὢ"]={ "Ὠ","̀" }, + ["Ὣ"]={ "Ὡ","̀" }, + ["Ὤ"]={ "Ὠ","́" }, + ["Ὥ"]={ "Ὡ","́" }, + ["Ὦ"]={ "Ὠ","͂" }, + ["Ὧ"]={ "Ὡ","͂" }, + ["ὰ"]={ "α","̀" }, + ["ὲ"]={ "ε","̀" }, + ["ὴ"]={ "η","̀" }, + ["ὶ"]={ "ι","̀" }, + ["ὸ"]={ "ο","̀" }, + ["ὺ"]={ "υ","̀" }, + ["ὼ"]={ "ω","̀" }, + ["ᾀ"]={ "ἀ","ͅ" }, + ["ᾁ"]={ "ἁ","ͅ" }, + ["ᾂ"]={ "ἂ","ͅ" }, + ["ᾃ"]={ "ἃ","ͅ" }, + ["ᾄ"]={ "ἄ","ͅ" }, + ["ᾅ"]={ "ἅ","ͅ" }, + ["ᾆ"]={ "ἆ","ͅ" }, + ["ᾇ"]={ "ἇ","ͅ" }, + ["ᾈ"]={ "Ἀ","ͅ" }, + ["ᾉ"]={ "Ἁ","ͅ" }, + ["ᾊ"]={ "Ἂ","ͅ" }, + ["ᾋ"]={ "Ἃ","ͅ" }, + ["ᾌ"]={ "Ἄ","ͅ" }, + ["ᾍ"]={ "Ἅ","ͅ" }, + ["ᾎ"]={ "Ἆ","ͅ" }, + ["ᾏ"]={ "Ἇ","ͅ" }, + ["ᾐ"]={ "ἠ","ͅ" }, + ["ᾑ"]={ "ἡ","ͅ" }, + ["ᾒ"]={ "ἢ","ͅ" }, + ["ᾓ"]={ "ἣ","ͅ" }, + ["ᾔ"]={ "ἤ","ͅ" }, + ["ᾕ"]={ "ἥ","ͅ" }, + ["ᾖ"]={ "ἦ","ͅ" }, + ["ᾗ"]={ "ἧ","ͅ" }, + ["ᾘ"]={ "Ἠ","ͅ" }, + ["ᾙ"]={ "Ἡ","ͅ" }, + ["ᾚ"]={ "Ἢ","ͅ" }, + ["ᾛ"]={ "Ἣ","ͅ" }, + ["ᾜ"]={ "Ἤ","ͅ" }, + ["ᾝ"]={ "Ἥ","ͅ" }, + ["ᾞ"]={ "Ἦ","ͅ" }, + ["ᾟ"]={ "Ἧ","ͅ" }, + ["ᾠ"]={ "ὠ","ͅ" }, + ["ᾡ"]={ "ὡ","ͅ" }, + ["ᾢ"]={ "ὢ","ͅ" }, + ["ᾣ"]={ "ὣ","ͅ" }, + ["ᾤ"]={ "ὤ","ͅ" }, + ["ᾥ"]={ "ὥ","ͅ" }, + ["ᾦ"]={ "ὦ","ͅ" }, + ["ᾧ"]={ "ὧ","ͅ" }, + ["ᾨ"]={ "Ὠ","ͅ" }, + ["ᾩ"]={ "Ὡ","ͅ" }, + ["ᾪ"]={ "Ὢ","ͅ" }, + ["ᾫ"]={ "Ὣ","ͅ" }, + ["ᾬ"]={ "Ὤ","ͅ" }, + ["ᾭ"]={ "Ὥ","ͅ" }, + ["ᾮ"]={ "Ὦ","ͅ" }, + ["ᾯ"]={ "Ὧ","ͅ" }, + ["ᾰ"]={ "α","̆" }, + ["ᾱ"]={ "α","̄" }, + ["ᾲ"]={ "ὰ","ͅ" }, + ["ᾳ"]={ "α","ͅ" }, + ["ᾴ"]={ "ά","ͅ" }, + ["ᾶ"]={ "α","͂" }, + ["ᾷ"]={ "ᾶ","ͅ" }, + ["Ᾰ"]={ "Α","̆" }, + ["Ᾱ"]={ "Α","̄" }, + ["Ὰ"]={ "Α","̀" }, + ["ᾼ"]={ "Α","ͅ" }, + ["῁"]={ "¨","͂" }, + ["ῂ"]={ "ὴ","ͅ" }, + ["ῃ"]={ "η","ͅ" }, + ["ῄ"]={ "ή","ͅ" }, + ["ῆ"]={ "η","͂" }, + ["ῇ"]={ "ῆ","ͅ" }, + ["Ὲ"]={ "Ε","̀" }, + ["Ὴ"]={ "Η","̀" }, + ["ῌ"]={ "Η","ͅ" }, + ["῍"]={ "᾿","̀" }, + ["῎"]={ "᾿","́" }, + ["῏"]={ "᾿","͂" }, + ["ῐ"]={ "ι","̆" }, + ["ῑ"]={ "ι","̄" }, + ["ῒ"]={ "ϊ","̀" }, + ["ῖ"]={ "ι","͂" }, + ["ῗ"]={ "ϊ","͂" }, + ["Ῐ"]={ "Ι","̆" }, + ["Ῑ"]={ "Ι","̄" }, + ["Ὶ"]={ "Ι","̀" }, + ["῝"]={ "῾","̀" }, + ["῞"]={ "῾","́" }, + ["῟"]={ "῾","͂" }, + ["ῠ"]={ "υ","̆" }, + ["ῡ"]={ "υ","̄" }, + ["ῢ"]={ "ϋ","̀" }, + ["ῤ"]={ "ρ","̓" }, + ["ῥ"]={ "ρ","̔" }, + ["ῦ"]={ "υ","͂" }, + ["ῧ"]={ "ϋ","͂" }, + ["Ῠ"]={ "Υ","̆" }, + ["Ῡ"]={ "Υ","̄" }, + ["Ὺ"]={ "Υ","̀" }, + ["Ῥ"]={ "Ρ","̔" }, + ["῭"]={ "¨","̀" }, + ["ῲ"]={ "ὼ","ͅ" }, + ["ῳ"]={ "ω","ͅ" }, + ["ῴ"]={ "ώ","ͅ" }, + ["ῶ"]={ "ω","͂" }, + ["ῷ"]={ "ῶ","ͅ" }, + ["Ὸ"]={ "Ο","̀" }, + ["Ὼ"]={ "Ω","̀" }, + ["ῼ"]={ "Ω","ͅ" }, + ["↚"]={ "←","̸" }, + ["↛"]={ "→","̸" }, + ["↮"]={ "↔","̸" }, + ["⇍"]={ "⇐","̸" }, + ["⇎"]={ "⇔","̸" }, + ["⇏"]={ "⇒","̸" }, + ["∄"]={ "∃","̸" }, + ["∉"]={ "∈","̸" }, + ["∌"]={ "∋","̸" }, + ["∤"]={ "∣","̸" }, + ["∦"]={ "∥","̸" }, + ["≁"]={ "∼","̸" }, + ["≄"]={ "≃","̸" }, + ["≇"]={ "≅","̸" }, + ["≉"]={ "≈","̸" }, + ["≠"]={ "=","̸" }, + ["≢"]={ "≡","̸" }, + ["≭"]={ "≍","̸" }, + ["≮"]={ "<","̸" }, + ["≯"]={ ">","̸" }, + ["≰"]={ "≤","̸" }, + ["≱"]={ "≥","̸" }, + ["≴"]={ "≲","̸" }, + ["≵"]={ "≳","̸" }, + ["≸"]={ "≶","̸" }, + ["≹"]={ "≷","̸" }, + ["⊀"]={ "≺","̸" }, + ["⊁"]={ "≻","̸" }, + ["⊄"]={ "⊂","̸" }, + ["⊅"]={ "⊃","̸" }, + ["⊈"]={ "⊆","̸" }, + ["⊉"]={ "⊇","̸" }, + ["⊬"]={ "⊢","̸" }, + ["⊭"]={ "⊨","̸" }, + ["⊮"]={ "⊩","̸" }, + ["⊯"]={ "⊫","̸" }, + ["⋠"]={ "≼","̸" }, + ["⋡"]={ "≽","̸" }, + ["⋢"]={ "⊑","̸" }, + ["⋣"]={ "⊒","̸" }, + ["⋪"]={ "⊲","̸" }, + ["⋫"]={ "⊳","̸" }, + ["⋬"]={ "⊴","̸" }, + ["⋭"]={ "⊵","̸" }, + ["⫝̸"]={ "⫝","̸" }, + ["が"]={ "か","゙" }, + ["ぎ"]={ "き","゙" }, + ["ぐ"]={ "く","゙" }, + ["げ"]={ "け","゙" }, + ["ご"]={ "こ","゙" }, + ["ざ"]={ "さ","゙" }, + ["じ"]={ "し","゙" }, + ["ず"]={ "す","゙" }, + ["ぜ"]={ "せ","゙" }, + ["ぞ"]={ "そ","゙" }, + ["だ"]={ "た","゙" }, + ["ぢ"]={ "ち","゙" }, + ["づ"]={ "つ","゙" }, + ["で"]={ "て","゙" }, + ["ど"]={ "と","゙" }, + ["ば"]={ "は","゙" }, + ["ぱ"]={ "は","゚" }, + ["び"]={ "ひ","゙" }, + ["ぴ"]={ "ひ","゚" }, + ["ぶ"]={ "ふ","゙" }, + ["ぷ"]={ "ふ","゚" }, + ["べ"]={ "へ","゙" }, + ["ぺ"]={ "へ","゚" }, + ["ぼ"]={ "ほ","゙" }, + ["ぽ"]={ "ほ","゚" }, + ["ゔ"]={ "う","゙" }, + ["ゞ"]={ "ゝ","゙" }, + ["ガ"]={ "カ","゙" }, + ["ギ"]={ "キ","゙" }, + ["グ"]={ "ク","゙" }, + ["ゲ"]={ "ケ","゙" }, + ["ゴ"]={ "コ","゙" }, + ["ザ"]={ "サ","゙" }, + ["ジ"]={ "シ","゙" }, + ["ズ"]={ "ス","゙" }, + ["ゼ"]={ "セ","゙" }, + ["ゾ"]={ "ソ","゙" }, + ["ダ"]={ "タ","゙" }, + ["ヂ"]={ "チ","゙" }, + ["ヅ"]={ "ツ","゙" }, + ["デ"]={ "テ","゙" }, + ["ド"]={ "ト","゙" }, + ["バ"]={ "ハ","゙" }, + ["パ"]={ "ハ","゚" }, + ["ビ"]={ "ヒ","゙" }, + ["ピ"]={ "ヒ","゚" }, + ["ブ"]={ "フ","゙" }, + ["プ"]={ "フ","゚" }, + ["ベ"]={ "ヘ","゙" }, + ["ペ"]={ "ヘ","゚" }, + ["ボ"]={ "ホ","゙" }, + ["ポ"]={ "ホ","゚" }, + ["ヴ"]={ "ウ","゙" }, + ["ヷ"]={ "ワ","゙" }, + ["ヸ"]={ "ヰ","゙" }, + ["ヹ"]={ "ヱ","゙" }, + ["ヺ"]={ "ヲ","゙" }, + ["ヾ"]={ "ヽ","゙" }, + ["יִ"]={ "י","ִ" }, + ["ײַ"]={ "ײ","ַ" }, + ["שׁ"]={ "ש","ׁ" }, + ["שׂ"]={ "ש","ׂ" }, + ["שּׁ"]={ "שּ","ׁ" }, + ["שּׂ"]={ "שּ","ׂ" }, + ["אַ"]={ "א","ַ" }, + ["אָ"]={ "א","ָ" }, + ["אּ"]={ "א","ּ" }, + ["בּ"]={ "ב","ּ" }, + ["גּ"]={ "ג","ּ" }, + ["דּ"]={ "ד","ּ" }, + ["הּ"]={ "ה","ּ" }, + ["וּ"]={ "ו","ּ" }, + ["זּ"]={ "ז","ּ" }, + ["טּ"]={ "ט","ּ" }, + ["יּ"]={ "י","ּ" }, + ["ךּ"]={ "ך","ּ" }, + ["כּ"]={ "כ","ּ" }, + ["לּ"]={ "ל","ּ" }, + ["מּ"]={ "מ","ּ" }, + ["נּ"]={ "נ","ּ" }, + ["סּ"]={ "ס","ּ" }, + ["ףּ"]={ "ף","ּ" }, + ["פּ"]={ "פ","ּ" }, + ["צּ"]={ "צ","ּ" }, + ["קּ"]={ "ק","ּ" }, + ["רּ"]={ "ר","ּ" }, + ["שּ"]={ "ש","ּ" }, + ["תּ"]={ "ת","ּ" }, + ["וֹ"]={ "ו","ֹ" }, + ["בֿ"]={ "ב","ֿ" }, + ["כֿ"]={ "כ","ֿ" }, + ["פֿ"]={ "פ","ֿ" }, + ["𑂚"]={ "𑂙","𑂺" }, + ["𑂜"]={ "𑂛","𑂺" }, + ["𑂫"]={ "𑂥","𑂺" }, + ["𑄮"]={ "𑄱","𑄧" }, + ["𑄯"]={ "𑄲","𑄧" }, + ["𑍋"]={ "𑍇","𑌾" }, + ["𑍌"]={ "𑍇","𑍗" }, + ["𑒻"]={ "𑒹","𑒺" }, + ["𑒼"]={ "𑒹","𑒰" }, + ["𑒾"]={ "𑒹","𑒽" }, + ["𑖺"]={ "𑖸","𑖯" }, + ["𑖻"]={ "𑖹","𑖯" }, + ["𝅗𝅥"]={ "𝅗","𝅥" }, + ["𝅘𝅥"]={ "𝅘","𝅥" }, + ["𝅘𝅥𝅮"]={ "𝅘𝅥","𝅮" }, + ["𝅘𝅥𝅯"]={ "𝅘𝅥","𝅯" }, + ["𝅘𝅥𝅰"]={ "𝅘𝅥","𝅰" }, + ["𝅘𝅥𝅱"]={ "𝅘𝅥","𝅱" }, + ["𝅘𝅥𝅲"]={ "𝅘𝅥","𝅲" }, + ["𝆹𝅥"]={ "𝆹","𝅥" }, + ["𝆺𝅥"]={ "𝆺","𝅥" }, + ["𝆹𝅥𝅮"]={ "𝆹𝅥","𝅮" }, + ["𝆺𝅥𝅮"]={ "𝆺𝅥","𝅮" }, + ["𝆹𝅥𝅯"]={ "𝆹𝅥","𝅯" }, + ["𝆺𝅥𝅯"]={ "𝆺𝅥","𝅯" }, + }, + }, + { + ["data"]={ + ["À"]={ "A","̀" }, + ["Á"]={ "A","́" }, + ["Â"]={ "A","̂" }, + ["Ã"]={ "A","̃" }, + ["Ä"]={ "A","̈" }, + ["Å"]={ "A","̊" }, + ["Ç"]={ "C","̧" }, + ["È"]={ "E","̀" }, + ["É"]={ "E","́" }, + ["Ê"]={ "E","̂" }, + ["Ë"]={ "E","̈" }, + ["Ì"]={ "I","̀" }, + ["Í"]={ "I","́" }, + ["Î"]={ "I","̂" }, + ["Ï"]={ "I","̈" }, + ["Ñ"]={ "N","̃" }, + ["Ò"]={ "O","̀" }, + ["Ó"]={ "O","́" }, + ["Ô"]={ "O","̂" }, + ["Õ"]={ "O","̃" }, + ["Ö"]={ "O","̈" }, + ["Ù"]={ "U","̀" }, + ["Ú"]={ "U","́" }, + ["Û"]={ "U","̂" }, + ["Ü"]={ "U","̈" }, + ["Ý"]={ "Y","́" }, + ["à"]={ "a","̀" }, + ["á"]={ "a","́" }, + ["â"]={ "a","̂" }, + ["ã"]={ "a","̃" }, + ["ä"]={ "a","̈" }, + ["å"]={ "a","̊" }, + ["ç"]={ "c","̧" }, + ["è"]={ "e","̀" }, + ["é"]={ "e","́" }, + ["ê"]={ "e","̂" }, + ["ë"]={ "e","̈" }, + ["ì"]={ "i","̀" }, + ["í"]={ "i","́" }, + ["î"]={ "i","̂" }, + ["ï"]={ "i","̈" }, + ["ñ"]={ "n","̃" }, + ["ò"]={ "o","̀" }, + ["ó"]={ "o","́" }, + ["ô"]={ "o","̂" }, + ["õ"]={ "o","̃" }, + ["ö"]={ "o","̈" }, + ["ù"]={ "u","̀" }, + ["ú"]={ "u","́" }, + ["û"]={ "u","̂" }, + ["ü"]={ "u","̈" }, + ["ý"]={ "y","́" }, + ["ÿ"]={ "y","̈" }, + ["Ā"]={ "A","̄" }, + ["ā"]={ "a","̄" }, + ["Ă"]={ "A","̆" }, + ["ă"]={ "a","̆" }, + ["Ą"]={ "A","̨" }, + ["ą"]={ "a","̨" }, + ["Ć"]={ "C","́" }, + ["ć"]={ "c","́" }, + ["Ĉ"]={ "C","̂" }, + ["ĉ"]={ "c","̂" }, + ["Ċ"]={ "C","̇" }, + ["ċ"]={ "c","̇" }, + ["Č"]={ "C","̌" }, + ["č"]={ "c","̌" }, + ["Ď"]={ "D","̌" }, + ["ď"]={ "d","̌" }, + ["Ē"]={ "E","̄" }, + ["ē"]={ "e","̄" }, + ["Ĕ"]={ "E","̆" }, + ["ĕ"]={ "e","̆" }, + ["Ė"]={ "E","̇" }, + ["ė"]={ "e","̇" }, + ["Ę"]={ "E","̨" }, + ["ę"]={ "e","̨" }, + ["Ě"]={ "E","̌" }, + ["ě"]={ "e","̌" }, + ["Ĝ"]={ "G","̂" }, + ["ĝ"]={ "g","̂" }, + ["Ğ"]={ "G","̆" }, + ["ğ"]={ "g","̆" }, + ["Ġ"]={ "G","̇" }, + ["ġ"]={ "g","̇" }, + ["Ģ"]={ "G","̧" }, + ["ģ"]={ "g","̧" }, + ["Ĥ"]={ "H","̂" }, + ["ĥ"]={ "h","̂" }, + ["Ĩ"]={ "I","̃" }, + ["ĩ"]={ "i","̃" }, + ["Ī"]={ "I","̄" }, + ["ī"]={ "i","̄" }, + ["Ĭ"]={ "I","̆" }, + ["ĭ"]={ "i","̆" }, + ["Į"]={ "I","̨" }, + ["į"]={ "i","̨" }, + ["İ"]={ "I","̇" }, + ["Ĵ"]={ "J","̂" }, + ["ĵ"]={ "j","̂" }, + ["Ķ"]={ "K","̧" }, + ["ķ"]={ "k","̧" }, + ["Ĺ"]={ "L","́" }, + ["ĺ"]={ "l","́" }, + ["Ļ"]={ "L","̧" }, + ["ļ"]={ "l","̧" }, + ["Ľ"]={ "L","̌" }, + ["ľ"]={ "l","̌" }, + ["Ń"]={ "N","́" }, + ["ń"]={ "n","́" }, + ["Ņ"]={ "N","̧" }, + ["ņ"]={ "n","̧" }, + ["Ň"]={ "N","̌" }, + ["ň"]={ "n","̌" }, + ["Ō"]={ "O","̄" }, + ["ō"]={ "o","̄" }, + ["Ŏ"]={ "O","̆" }, + ["ŏ"]={ "o","̆" }, + ["Ő"]={ "O","̋" }, + ["ő"]={ "o","̋" }, + ["Ŕ"]={ "R","́" }, + ["ŕ"]={ "r","́" }, + ["Ŗ"]={ "R","̧" }, + ["ŗ"]={ "r","̧" }, + ["Ř"]={ "R","̌" }, + ["ř"]={ "r","̌" }, + ["Ś"]={ "S","́" }, + ["ś"]={ "s","́" }, + ["Ŝ"]={ "S","̂" }, + ["ŝ"]={ "s","̂" }, + ["Ş"]={ "S","̧" }, + ["ş"]={ "s","̧" }, + ["Š"]={ "S","̌" }, + ["š"]={ "s","̌" }, + ["Ţ"]={ "T","̧" }, + ["ţ"]={ "t","̧" }, + ["Ť"]={ "T","̌" }, + ["ť"]={ "t","̌" }, + ["Ũ"]={ "U","̃" }, + ["ũ"]={ "u","̃" }, + ["Ū"]={ "U","̄" }, + ["ū"]={ "u","̄" }, + ["Ŭ"]={ "U","̆" }, + ["ŭ"]={ "u","̆" }, + ["Ů"]={ "U","̊" }, + ["ů"]={ "u","̊" }, + ["Ű"]={ "U","̋" }, + ["ű"]={ "u","̋" }, + ["Ų"]={ "U","̨" }, + ["ų"]={ "u","̨" }, + ["Ŵ"]={ "W","̂" }, + ["ŵ"]={ "w","̂" }, + ["Ŷ"]={ "Y","̂" }, + ["ŷ"]={ "y","̂" }, + ["Ÿ"]={ "Y","̈" }, + ["Ź"]={ "Z","́" }, + ["ź"]={ "z","́" }, + ["Ż"]={ "Z","̇" }, + ["ż"]={ "z","̇" }, + ["Ž"]={ "Z","̌" }, + ["ž"]={ "z","̌" }, + ["Ơ"]={ "O","̛" }, + ["ơ"]={ "o","̛" }, + ["Ư"]={ "U","̛" }, + ["ư"]={ "u","̛" }, + ["Ǎ"]={ "A","̌" }, + ["ǎ"]={ "a","̌" }, + ["Ǐ"]={ "I","̌" }, + ["ǐ"]={ "i","̌" }, + ["Ǒ"]={ "O","̌" }, + ["ǒ"]={ "o","̌" }, + ["Ǔ"]={ "U","̌" }, + ["ǔ"]={ "u","̌" }, + ["Ǖ"]={ "Ü","̄" }, + ["ǖ"]={ "ü","̄" }, + ["Ǘ"]={ "Ü","́" }, + ["ǘ"]={ "ü","́" }, + ["Ǚ"]={ "Ü","̌" }, + ["ǚ"]={ "ü","̌" }, + ["Ǜ"]={ "Ü","̀" }, + ["ǜ"]={ "ü","̀" }, + ["Ǟ"]={ "Ä","̄" }, + ["ǟ"]={ "ä","̄" }, + ["Ǡ"]={ "Ȧ","̄" }, + ["ǡ"]={ "ȧ","̄" }, + ["Ǣ"]={ "Æ","̄" }, + ["ǣ"]={ "æ","̄" }, + ["Ǧ"]={ "G","̌" }, + ["ǧ"]={ "g","̌" }, + ["Ǩ"]={ "K","̌" }, + ["ǩ"]={ "k","̌" }, + ["Ǫ"]={ "O","̨" }, + ["ǫ"]={ "o","̨" }, + ["Ǭ"]={ "Ǫ","̄" }, + ["ǭ"]={ "ǫ","̄" }, + ["Ǯ"]={ "Ʒ","̌" }, + ["ǯ"]={ "ʒ","̌" }, + ["ǰ"]={ "j","̌" }, + ["Ǵ"]={ "G","́" }, + ["ǵ"]={ "g","́" }, + ["Ǹ"]={ "N","̀" }, + ["ǹ"]={ "n","̀" }, + ["Ǻ"]={ "Å","́" }, + ["ǻ"]={ "å","́" }, + ["Ǽ"]={ "Æ","́" }, + ["ǽ"]={ "æ","́" }, + ["Ǿ"]={ "Ø","́" }, + ["ǿ"]={ "ø","́" }, + ["Ȁ"]={ "A","̏" }, + ["ȁ"]={ "a","̏" }, + ["Ȃ"]={ "A","̑" }, + ["ȃ"]={ "a","̑" }, + ["Ȅ"]={ "E","̏" }, + ["ȅ"]={ "e","̏" }, + ["Ȇ"]={ "E","̑" }, + ["ȇ"]={ "e","̑" }, + ["Ȉ"]={ "I","̏" }, + ["ȉ"]={ "i","̏" }, + ["Ȋ"]={ "I","̑" }, + ["ȋ"]={ "i","̑" }, + ["Ȍ"]={ "O","̏" }, + ["ȍ"]={ "o","̏" }, + ["Ȏ"]={ "O","̑" }, + ["ȏ"]={ "o","̑" }, + ["Ȑ"]={ "R","̏" }, + ["ȑ"]={ "r","̏" }, + ["Ȓ"]={ "R","̑" }, + ["ȓ"]={ "r","̑" }, + ["Ȕ"]={ "U","̏" }, + ["ȕ"]={ "u","̏" }, + ["Ȗ"]={ "U","̑" }, + ["ȗ"]={ "u","̑" }, + ["Ș"]={ "S","̦" }, + ["ș"]={ "s","̦" }, + ["Ț"]={ "T","̦" }, + ["ț"]={ "t","̦" }, + ["Ȟ"]={ "H","̌" }, + ["ȟ"]={ "h","̌" }, + ["Ȧ"]={ "A","̇" }, + ["ȧ"]={ "a","̇" }, + ["Ȩ"]={ "E","̧" }, + ["ȩ"]={ "e","̧" }, + ["Ȫ"]={ "Ö","̄" }, + ["ȫ"]={ "ö","̄" }, + ["Ȭ"]={ "Õ","̄" }, + ["ȭ"]={ "õ","̄" }, + ["Ȯ"]={ "O","̇" }, + ["ȯ"]={ "o","̇" }, + ["Ȱ"]={ "Ȯ","̄" }, + ["ȱ"]={ "ȯ","̄" }, + ["Ȳ"]={ "Y","̄" }, + ["ȳ"]={ "y","̄" }, + ["̈́"]={ "̈","́" }, + ["΅"]={ "¨","́" }, + ["Ά"]={ "Α","́" }, + ["Έ"]={ "Ε","́" }, + ["Ή"]={ "Η","́" }, + ["Ί"]={ "Ι","́" }, + ["Ό"]={ "Ο","́" }, + ["Ύ"]={ "Υ","́" }, + ["Ώ"]={ "Ω","́" }, + ["ΐ"]={ "ϊ","́" }, + ["Ϊ"]={ "Ι","̈" }, + ["Ϋ"]={ "Υ","̈" }, + ["ά"]={ "α","́" }, + ["έ"]={ "ε","́" }, + ["ή"]={ "η","́" }, + ["ί"]={ "ι","́" }, + ["ΰ"]={ "ϋ","́" }, + ["ϊ"]={ "ι","̈" }, + ["ϋ"]={ "υ","̈" }, + ["ό"]={ "ο","́" }, + ["ύ"]={ "υ","́" }, + ["ώ"]={ "ω","́" }, + ["ϓ"]={ "ϒ","́" }, + ["ϔ"]={ "ϒ","̈" }, + ["Ѐ"]={ "Е","̀" }, + ["Ё"]={ "Е","̈" }, + ["Ѓ"]={ "Г","́" }, + ["Ї"]={ "І","̈" }, + ["Ќ"]={ "К","́" }, + ["Ѝ"]={ "И","̀" }, + ["Ў"]={ "У","̆" }, + ["Й"]={ "И","̆" }, + ["й"]={ "и","̆" }, + ["ѐ"]={ "е","̀" }, + ["ё"]={ "е","̈" }, + ["ѓ"]={ "г","́" }, + ["ї"]={ "і","̈" }, + ["ќ"]={ "к","́" }, + ["ѝ"]={ "и","̀" }, + ["ў"]={ "у","̆" }, + ["Ѷ"]={ "Ѵ","̏" }, + ["ѷ"]={ "ѵ","̏" }, + ["Ӂ"]={ "Ж","̆" }, + ["ӂ"]={ "ж","̆" }, + ["Ӑ"]={ "А","̆" }, + ["ӑ"]={ "а","̆" }, + ["Ӓ"]={ "А","̈" }, + ["ӓ"]={ "а","̈" }, + ["Ӗ"]={ "Е","̆" }, + ["ӗ"]={ "е","̆" }, + ["Ӛ"]={ "Ә","̈" }, + ["ӛ"]={ "ә","̈" }, + ["Ӝ"]={ "Ж","̈" }, + ["ӝ"]={ "ж","̈" }, + ["Ӟ"]={ "З","̈" }, + ["ӟ"]={ "з","̈" }, + ["Ӣ"]={ "И","̄" }, + ["ӣ"]={ "и","̄" }, + ["Ӥ"]={ "И","̈" }, + ["ӥ"]={ "и","̈" }, + ["Ӧ"]={ "О","̈" }, + ["ӧ"]={ "о","̈" }, + ["Ӫ"]={ "Ө","̈" }, + ["ӫ"]={ "ө","̈" }, + ["Ӭ"]={ "Э","̈" }, + ["ӭ"]={ "э","̈" }, + ["Ӯ"]={ "У","̄" }, + ["ӯ"]={ "у","̄" }, + ["Ӱ"]={ "У","̈" }, + ["ӱ"]={ "у","̈" }, + ["Ӳ"]={ "У","̋" }, + ["ӳ"]={ "у","̋" }, + ["Ӵ"]={ "Ч","̈" }, + ["ӵ"]={ "ч","̈" }, + ["Ӹ"]={ "Ы","̈" }, + ["ӹ"]={ "ы","̈" }, + ["آ"]={ "ا","ٓ" }, + ["أ"]={ "ا","ٔ" }, + ["ؤ"]={ "و","ٔ" }, + ["إ"]={ "ا","ٕ" }, + ["ئ"]={ "ي","ٔ" }, + ["ۀ"]={ "ە","ٔ" }, + ["ۂ"]={ "ہ","ٔ" }, + ["ۓ"]={ "ے","ٔ" }, + ["ऩ"]={ "न","़" }, + ["ऱ"]={ "र","़" }, + ["ऴ"]={ "ळ","़" }, + ["क़"]={ "क","़" }, + ["ख़"]={ "ख","़" }, + ["ग़"]={ "ग","़" }, + ["ज़"]={ "ज","़" }, + ["ड़"]={ "ड","़" }, + ["ढ़"]={ "ढ","़" }, + ["फ़"]={ "फ","़" }, + ["य़"]={ "य","़" }, + ["ো"]={ "ে","া" }, + ["ৌ"]={ "ে","ৗ" }, + ["ড়"]={ "ড","়" }, + ["ঢ়"]={ "ঢ","়" }, + ["য়"]={ "য","়" }, + ["ਲ਼"]={ "ਲ","਼" }, + ["ਸ਼"]={ "ਸ","਼" }, + ["ਖ਼"]={ "ਖ","਼" }, + ["ਗ਼"]={ "ਗ","਼" }, + ["ਜ਼"]={ "ਜ","਼" }, + ["ਫ਼"]={ "ਫ","਼" }, + ["ୈ"]={ "େ","ୖ" }, + ["ୋ"]={ "େ","ା" }, + ["ୌ"]={ "େ","ୗ" }, + ["ଡ଼"]={ "ଡ","଼" }, + ["ଢ଼"]={ "ଢ","଼" }, + ["ஔ"]={ "ஒ","ௗ" }, + ["ொ"]={ "ெ","ா" }, + ["ோ"]={ "ே","ா" }, + ["ௌ"]={ "ெ","ௗ" }, + ["ై"]={ "ె","ౖ" }, + ["ೀ"]={ "ಿ","ೕ" }, + ["ೇ"]={ "ೆ","ೕ" }, + ["ೈ"]={ "ೆ","ೖ" }, + ["ೊ"]={ "ೆ","ೂ" }, + ["ೋ"]={ "ೊ","ೕ" }, + ["ൊ"]={ "െ","ാ" }, + ["ോ"]={ "േ","ാ" }, + ["ൌ"]={ "െ","ൗ" }, + ["ේ"]={ "ෙ","්" }, + ["ො"]={ "ෙ","ා" }, + ["ෝ"]={ "ො","්" }, + ["ෞ"]={ "ෙ","ෟ" }, + ["གྷ"]={ "ག","ྷ" }, + ["ཌྷ"]={ "ཌ","ྷ" }, + ["དྷ"]={ "ད","ྷ" }, + ["བྷ"]={ "བ","ྷ" }, + ["ཛྷ"]={ "ཛ","ྷ" }, + ["ཀྵ"]={ "ཀ","ྵ" }, + ["ཱི"]={ "ཱ","ི" }, + ["ཱུ"]={ "ཱ","ུ" }, + ["ྲྀ"]={ "ྲ","ྀ" }, + ["ླྀ"]={ "ླ","ྀ" }, + ["ཱྀ"]={ "ཱ","ྀ" }, + ["ྒྷ"]={ "ྒ","ྷ" }, + ["ྜྷ"]={ "ྜ","ྷ" }, + ["ྡྷ"]={ "ྡ","ྷ" }, + ["ྦྷ"]={ "ྦ","ྷ" }, + ["ྫྷ"]={ "ྫ","ྷ" }, + ["ྐྵ"]={ "ྐ","ྵ" }, + ["ဦ"]={ "ဥ","ီ" }, + ["ᬆ"]={ "ᬅ","ᬵ" }, + ["ᬈ"]={ "ᬇ","ᬵ" }, + ["ᬊ"]={ "ᬉ","ᬵ" }, + ["ᬌ"]={ "ᬋ","ᬵ" }, + ["ᬎ"]={ "ᬍ","ᬵ" }, + ["ᬒ"]={ "ᬑ","ᬵ" }, + ["ᬻ"]={ "ᬺ","ᬵ" }, + ["ᬽ"]={ "ᬼ","ᬵ" }, + ["ᭀ"]={ "ᬾ","ᬵ" }, + ["ᭁ"]={ "ᬿ","ᬵ" }, + ["ᭃ"]={ "ᭂ","ᬵ" }, + ["Ḁ"]={ "A","̥" }, + ["ḁ"]={ "a","̥" }, + ["Ḃ"]={ "B","̇" }, + ["ḃ"]={ "b","̇" }, + ["Ḅ"]={ "B","̣" }, + ["ḅ"]={ "b","̣" }, + ["Ḇ"]={ "B","̱" }, + ["ḇ"]={ "b","̱" }, + ["Ḉ"]={ "Ç","́" }, + ["ḉ"]={ "ç","́" }, + ["Ḋ"]={ "D","̇" }, + ["ḋ"]={ "d","̇" }, + ["Ḍ"]={ "D","̣" }, + ["ḍ"]={ "d","̣" }, + ["Ḏ"]={ "D","̱" }, + ["ḏ"]={ "d","̱" }, + ["Ḑ"]={ "D","̧" }, + ["ḑ"]={ "d","̧" }, + ["Ḓ"]={ "D","̭" }, + ["ḓ"]={ "d","̭" }, + ["Ḕ"]={ "Ē","̀" }, + ["ḕ"]={ "ē","̀" }, + ["Ḗ"]={ "Ē","́" }, + ["ḗ"]={ "ē","́" }, + ["Ḙ"]={ "E","̭" }, + ["ḙ"]={ "e","̭" }, + ["Ḛ"]={ "E","̰" }, + ["ḛ"]={ "e","̰" }, + ["Ḝ"]={ "Ȩ","̆" }, + ["ḝ"]={ "ȩ","̆" }, + ["Ḟ"]={ "F","̇" }, + ["ḟ"]={ "f","̇" }, + ["Ḡ"]={ "G","̄" }, + ["ḡ"]={ "g","̄" }, + ["Ḣ"]={ "H","̇" }, + ["ḣ"]={ "h","̇" }, + ["Ḥ"]={ "H","̣" }, + ["ḥ"]={ "h","̣" }, + ["Ḧ"]={ "H","̈" }, + ["ḧ"]={ "h","̈" }, + ["Ḩ"]={ "H","̧" }, + ["ḩ"]={ "h","̧" }, + ["Ḫ"]={ "H","̮" }, + ["ḫ"]={ "h","̮" }, + ["Ḭ"]={ "I","̰" }, + ["ḭ"]={ "i","̰" }, + ["Ḯ"]={ "Ï","́" }, + ["ḯ"]={ "ï","́" }, + ["Ḱ"]={ "K","́" }, + ["ḱ"]={ "k","́" }, + ["Ḳ"]={ "K","̣" }, + ["ḳ"]={ "k","̣" }, + ["Ḵ"]={ "K","̱" }, + ["ḵ"]={ "k","̱" }, + ["Ḷ"]={ "L","̣" }, + ["ḷ"]={ "l","̣" }, + ["Ḹ"]={ "Ḷ","̄" }, + ["ḹ"]={ "ḷ","̄" }, + ["Ḻ"]={ "L","̱" }, + ["ḻ"]={ "l","̱" }, + ["Ḽ"]={ "L","̭" }, + ["ḽ"]={ "l","̭" }, + ["Ḿ"]={ "M","́" }, + ["ḿ"]={ "m","́" }, + ["Ṁ"]={ "M","̇" }, + ["ṁ"]={ "m","̇" }, + ["Ṃ"]={ "M","̣" }, + ["ṃ"]={ "m","̣" }, + ["Ṅ"]={ "N","̇" }, + ["ṅ"]={ "n","̇" }, + ["Ṇ"]={ "N","̣" }, + ["ṇ"]={ "n","̣" }, + ["Ṉ"]={ "N","̱" }, + ["ṉ"]={ "n","̱" }, + ["Ṋ"]={ "N","̭" }, + ["ṋ"]={ "n","̭" }, + ["Ṍ"]={ "Õ","́" }, + ["ṍ"]={ "õ","́" }, + ["Ṏ"]={ "Õ","̈" }, + ["ṏ"]={ "õ","̈" }, + ["Ṑ"]={ "Ō","̀" }, + ["ṑ"]={ "ō","̀" }, + ["Ṓ"]={ "Ō","́" }, + ["ṓ"]={ "ō","́" }, + ["Ṕ"]={ "P","́" }, + ["ṕ"]={ "p","́" }, + ["Ṗ"]={ "P","̇" }, + ["ṗ"]={ "p","̇" }, + ["Ṙ"]={ "R","̇" }, + ["ṙ"]={ "r","̇" }, + ["Ṛ"]={ "R","̣" }, + ["ṛ"]={ "r","̣" }, + ["Ṝ"]={ "Ṛ","̄" }, + ["ṝ"]={ "ṛ","̄" }, + ["Ṟ"]={ "R","̱" }, + ["ṟ"]={ "r","̱" }, + ["Ṡ"]={ "S","̇" }, + ["ṡ"]={ "s","̇" }, + ["Ṣ"]={ "S","̣" }, + ["ṣ"]={ "s","̣" }, + ["Ṥ"]={ "Ś","̇" }, + ["ṥ"]={ "ś","̇" }, + ["Ṧ"]={ "Š","̇" }, + ["ṧ"]={ "š","̇" }, + ["Ṩ"]={ "Ṣ","̇" }, + ["ṩ"]={ "ṣ","̇" }, + ["Ṫ"]={ "T","̇" }, + ["ṫ"]={ "t","̇" }, + ["Ṭ"]={ "T","̣" }, + ["ṭ"]={ "t","̣" }, + ["Ṯ"]={ "T","̱" }, + ["ṯ"]={ "t","̱" }, + ["Ṱ"]={ "T","̭" }, + ["ṱ"]={ "t","̭" }, + ["Ṳ"]={ "U","̤" }, + ["ṳ"]={ "u","̤" }, + ["Ṵ"]={ "U","̰" }, + ["ṵ"]={ "u","̰" }, + ["Ṷ"]={ "U","̭" }, + ["ṷ"]={ "u","̭" }, + ["Ṹ"]={ "Ũ","́" }, + ["ṹ"]={ "ũ","́" }, + ["Ṻ"]={ "Ū","̈" }, + ["ṻ"]={ "ū","̈" }, + ["Ṽ"]={ "V","̃" }, + ["ṽ"]={ "v","̃" }, + ["Ṿ"]={ "V","̣" }, + ["ṿ"]={ "v","̣" }, + ["Ẁ"]={ "W","̀" }, + ["ẁ"]={ "w","̀" }, + ["Ẃ"]={ "W","́" }, + ["ẃ"]={ "w","́" }, + ["Ẅ"]={ "W","̈" }, + ["ẅ"]={ "w","̈" }, + ["Ẇ"]={ "W","̇" }, + ["ẇ"]={ "w","̇" }, + ["Ẉ"]={ "W","̣" }, + ["ẉ"]={ "w","̣" }, + ["Ẋ"]={ "X","̇" }, + ["ẋ"]={ "x","̇" }, + ["Ẍ"]={ "X","̈" }, + ["ẍ"]={ "x","̈" }, + ["Ẏ"]={ "Y","̇" }, + ["ẏ"]={ "y","̇" }, + ["Ẑ"]={ "Z","̂" }, + ["ẑ"]={ "z","̂" }, + ["Ẓ"]={ "Z","̣" }, + ["ẓ"]={ "z","̣" }, + ["Ẕ"]={ "Z","̱" }, + ["ẕ"]={ "z","̱" }, + ["ẖ"]={ "h","̱" }, + ["ẗ"]={ "t","̈" }, + ["ẘ"]={ "w","̊" }, + ["ẙ"]={ "y","̊" }, + ["ẛ"]={ "ſ","̇" }, + ["Ạ"]={ "A","̣" }, + ["ạ"]={ "a","̣" }, + ["Ả"]={ "A","̉" }, + ["ả"]={ "a","̉" }, + ["Ấ"]={ "Â","́" }, + ["ấ"]={ "â","́" }, + ["Ầ"]={ "Â","̀" }, + ["ầ"]={ "â","̀" }, + ["Ẩ"]={ "Â","̉" }, + ["ẩ"]={ "â","̉" }, + ["Ẫ"]={ "Â","̃" }, + ["ẫ"]={ "â","̃" }, + ["Ậ"]={ "Ạ","̂" }, + ["ậ"]={ "ạ","̂" }, + ["Ắ"]={ "Ă","́" }, + ["ắ"]={ "ă","́" }, + ["Ằ"]={ "Ă","̀" }, + ["ằ"]={ "ă","̀" }, + ["Ẳ"]={ "Ă","̉" }, + ["ẳ"]={ "ă","̉" }, + ["Ẵ"]={ "Ă","̃" }, + ["ẵ"]={ "ă","̃" }, + ["Ặ"]={ "Ạ","̆" }, + ["ặ"]={ "ạ","̆" }, + ["Ẹ"]={ "E","̣" }, + ["ẹ"]={ "e","̣" }, + ["Ẻ"]={ "E","̉" }, + ["ẻ"]={ "e","̉" }, + ["Ẽ"]={ "E","̃" }, + ["ẽ"]={ "e","̃" }, + ["Ế"]={ "Ê","́" }, + ["ế"]={ "ê","́" }, + ["Ề"]={ "Ê","̀" }, + ["ề"]={ "ê","̀" }, + ["Ể"]={ "Ê","̉" }, + ["ể"]={ "ê","̉" }, + ["Ễ"]={ "Ê","̃" }, + ["ễ"]={ "ê","̃" }, + ["Ệ"]={ "Ẹ","̂" }, + ["ệ"]={ "ẹ","̂" }, + ["Ỉ"]={ "I","̉" }, + ["ỉ"]={ "i","̉" }, + ["Ị"]={ "I","̣" }, + ["ị"]={ "i","̣" }, + ["Ọ"]={ "O","̣" }, + ["ọ"]={ "o","̣" }, + ["Ỏ"]={ "O","̉" }, + ["ỏ"]={ "o","̉" }, + ["Ố"]={ "Ô","́" }, + ["ố"]={ "ô","́" }, + ["Ồ"]={ "Ô","̀" }, + ["ồ"]={ "ô","̀" }, + ["Ổ"]={ "Ô","̉" }, + ["ổ"]={ "ô","̉" }, + ["Ỗ"]={ "Ô","̃" }, + ["ỗ"]={ "ô","̃" }, + ["Ộ"]={ "Ọ","̂" }, + ["ộ"]={ "ọ","̂" }, + ["Ớ"]={ "Ơ","́" }, + ["ớ"]={ "ơ","́" }, + ["Ờ"]={ "Ơ","̀" }, + ["ờ"]={ "ơ","̀" }, + ["Ở"]={ "Ơ","̉" }, + ["ở"]={ "ơ","̉" }, + ["Ỡ"]={ "Ơ","̃" }, + ["ỡ"]={ "ơ","̃" }, + ["Ợ"]={ "Ơ","̣" }, + ["ợ"]={ "ơ","̣" }, + ["Ụ"]={ "U","̣" }, + ["ụ"]={ "u","̣" }, + ["Ủ"]={ "U","̉" }, + ["ủ"]={ "u","̉" }, + ["Ứ"]={ "Ư","́" }, + ["ứ"]={ "ư","́" }, + ["Ừ"]={ "Ư","̀" }, + ["ừ"]={ "ư","̀" }, + ["Ử"]={ "Ư","̉" }, + ["ử"]={ "ư","̉" }, + ["Ữ"]={ "Ư","̃" }, + ["ữ"]={ "ư","̃" }, + ["Ự"]={ "Ư","̣" }, + ["ự"]={ "ư","̣" }, + ["Ỳ"]={ "Y","̀" }, + ["ỳ"]={ "y","̀" }, + ["Ỵ"]={ "Y","̣" }, + ["ỵ"]={ "y","̣" }, + ["Ỷ"]={ "Y","̉" }, + ["ỷ"]={ "y","̉" }, + ["Ỹ"]={ "Y","̃" }, + ["ỹ"]={ "y","̃" }, + ["ἀ"]={ "α","̓" }, + ["ἁ"]={ "α","̔" }, + ["ἂ"]={ "ἀ","̀" }, + ["ἃ"]={ "ἁ","̀" }, + ["ἄ"]={ "ἀ","́" }, + ["ἅ"]={ "ἁ","́" }, + ["ἆ"]={ "ἀ","͂" }, + ["ἇ"]={ "ἁ","͂" }, + ["Ἀ"]={ "Α","̓" }, + ["Ἁ"]={ "Α","̔" }, + ["Ἂ"]={ "Ἀ","̀" }, + ["Ἃ"]={ "Ἁ","̀" }, + ["Ἄ"]={ "Ἀ","́" }, + ["Ἅ"]={ "Ἁ","́" }, + ["Ἆ"]={ "Ἀ","͂" }, + ["Ἇ"]={ "Ἁ","͂" }, + ["ἐ"]={ "ε","̓" }, + ["ἑ"]={ "ε","̔" }, + ["ἒ"]={ "ἐ","̀" }, + ["ἓ"]={ "ἑ","̀" }, + ["ἔ"]={ "ἐ","́" }, + ["ἕ"]={ "ἑ","́" }, + ["Ἐ"]={ "Ε","̓" }, + ["Ἑ"]={ "Ε","̔" }, + ["Ἒ"]={ "Ἐ","̀" }, + ["Ἓ"]={ "Ἑ","̀" }, + ["Ἔ"]={ "Ἐ","́" }, + ["Ἕ"]={ "Ἑ","́" }, + ["ἠ"]={ "η","̓" }, + ["ἡ"]={ "η","̔" }, + ["ἢ"]={ "ἠ","̀" }, + ["ἣ"]={ "ἡ","̀" }, + ["ἤ"]={ "ἠ","́" }, + ["ἥ"]={ "ἡ","́" }, + ["ἦ"]={ "ἠ","͂" }, + ["ἧ"]={ "ἡ","͂" }, + ["Ἠ"]={ "Η","̓" }, + ["Ἡ"]={ "Η","̔" }, + ["Ἢ"]={ "Ἠ","̀" }, + ["Ἣ"]={ "Ἡ","̀" }, + ["Ἤ"]={ "Ἠ","́" }, + ["Ἥ"]={ "Ἡ","́" }, + ["Ἦ"]={ "Ἠ","͂" }, + ["Ἧ"]={ "Ἡ","͂" }, + ["ἰ"]={ "ι","̓" }, + ["ἱ"]={ "ι","̔" }, + ["ἲ"]={ "ἰ","̀" }, + ["ἳ"]={ "ἱ","̀" }, + ["ἴ"]={ "ἰ","́" }, + ["ἵ"]={ "ἱ","́" }, + ["ἶ"]={ "ἰ","͂" }, + ["ἷ"]={ "ἱ","͂" }, + ["Ἰ"]={ "Ι","̓" }, + ["Ἱ"]={ "Ι","̔" }, + ["Ἲ"]={ "Ἰ","̀" }, + ["Ἳ"]={ "Ἱ","̀" }, + ["Ἴ"]={ "Ἰ","́" }, + ["Ἵ"]={ "Ἱ","́" }, + ["Ἶ"]={ "Ἰ","͂" }, + ["Ἷ"]={ "Ἱ","͂" }, + ["ὀ"]={ "ο","̓" }, + ["ὁ"]={ "ο","̔" }, + ["ὂ"]={ "ὀ","̀" }, + ["ὃ"]={ "ὁ","̀" }, + ["ὄ"]={ "ὀ","́" }, + ["ὅ"]={ "ὁ","́" }, + ["Ὀ"]={ "Ο","̓" }, + ["Ὁ"]={ "Ο","̔" }, + ["Ὂ"]={ "Ὀ","̀" }, + ["Ὃ"]={ "Ὁ","̀" }, + ["Ὄ"]={ "Ὀ","́" }, + ["Ὅ"]={ "Ὁ","́" }, + ["ὐ"]={ "υ","̓" }, + ["ὑ"]={ "υ","̔" }, + ["ὒ"]={ "ὐ","̀" }, + ["ὓ"]={ "ὑ","̀" }, + ["ὔ"]={ "ὐ","́" }, + ["ὕ"]={ "ὑ","́" }, + ["ὖ"]={ "ὐ","͂" }, + ["ὗ"]={ "ὑ","͂" }, + ["Ὑ"]={ "Υ","̔" }, + ["Ὓ"]={ "Ὑ","̀" }, + ["Ὕ"]={ "Ὑ","́" }, + ["Ὗ"]={ "Ὑ","͂" }, + ["ὠ"]={ "ω","̓" }, + ["ὡ"]={ "ω","̔" }, + ["ὢ"]={ "ὠ","̀" }, + ["ὣ"]={ "ὡ","̀" }, + ["ὤ"]={ "ὠ","́" }, + ["ὥ"]={ "ὡ","́" }, + ["ὦ"]={ "ὠ","͂" }, + ["ὧ"]={ "ὡ","͂" }, + ["Ὠ"]={ "Ω","̓" }, + ["Ὡ"]={ "Ω","̔" }, + ["Ὢ"]={ "Ὠ","̀" }, + ["Ὣ"]={ "Ὡ","̀" }, + ["Ὤ"]={ "Ὠ","́" }, + ["Ὥ"]={ "Ὡ","́" }, + ["Ὦ"]={ "Ὠ","͂" }, + ["Ὧ"]={ "Ὡ","͂" }, + ["ὰ"]={ "α","̀" }, + ["ὲ"]={ "ε","̀" }, + ["ὴ"]={ "η","̀" }, + ["ὶ"]={ "ι","̀" }, + ["ὸ"]={ "ο","̀" }, + ["ὺ"]={ "υ","̀" }, + ["ὼ"]={ "ω","̀" }, + ["ᾀ"]={ "ἀ","ͅ" }, + ["ᾁ"]={ "ἁ","ͅ" }, + ["ᾂ"]={ "ἂ","ͅ" }, + ["ᾃ"]={ "ἃ","ͅ" }, + ["ᾄ"]={ "ἄ","ͅ" }, + ["ᾅ"]={ "ἅ","ͅ" }, + ["ᾆ"]={ "ἆ","ͅ" }, + ["ᾇ"]={ "ἇ","ͅ" }, + ["ᾈ"]={ "Ἀ","ͅ" }, + ["ᾉ"]={ "Ἁ","ͅ" }, + ["ᾊ"]={ "Ἂ","ͅ" }, + ["ᾋ"]={ "Ἃ","ͅ" }, + ["ᾌ"]={ "Ἄ","ͅ" }, + ["ᾍ"]={ "Ἅ","ͅ" }, + ["ᾎ"]={ "Ἆ","ͅ" }, + ["ᾏ"]={ "Ἇ","ͅ" }, + ["ᾐ"]={ "ἠ","ͅ" }, + ["ᾑ"]={ "ἡ","ͅ" }, + ["ᾒ"]={ "ἢ","ͅ" }, + ["ᾓ"]={ "ἣ","ͅ" }, + ["ᾔ"]={ "ἤ","ͅ" }, + ["ᾕ"]={ "ἥ","ͅ" }, + ["ᾖ"]={ "ἦ","ͅ" }, + ["ᾗ"]={ "ἧ","ͅ" }, + ["ᾘ"]={ "Ἠ","ͅ" }, + ["ᾙ"]={ "Ἡ","ͅ" }, + ["ᾚ"]={ "Ἢ","ͅ" }, + ["ᾛ"]={ "Ἣ","ͅ" }, + ["ᾜ"]={ "Ἤ","ͅ" }, + ["ᾝ"]={ "Ἥ","ͅ" }, + ["ᾞ"]={ "Ἦ","ͅ" }, + ["ᾟ"]={ "Ἧ","ͅ" }, + ["ᾠ"]={ "ὠ","ͅ" }, + ["ᾡ"]={ "ὡ","ͅ" }, + ["ᾢ"]={ "ὢ","ͅ" }, + ["ᾣ"]={ "ὣ","ͅ" }, + ["ᾤ"]={ "ὤ","ͅ" }, + ["ᾥ"]={ "ὥ","ͅ" }, + ["ᾦ"]={ "ὦ","ͅ" }, + ["ᾧ"]={ "ὧ","ͅ" }, + ["ᾨ"]={ "Ὠ","ͅ" }, + ["ᾩ"]={ "Ὡ","ͅ" }, + ["ᾪ"]={ "Ὢ","ͅ" }, + ["ᾫ"]={ "Ὣ","ͅ" }, + ["ᾬ"]={ "Ὤ","ͅ" }, + ["ᾭ"]={ "Ὥ","ͅ" }, + ["ᾮ"]={ "Ὦ","ͅ" }, + ["ᾯ"]={ "Ὧ","ͅ" }, + ["ᾰ"]={ "α","̆" }, + ["ᾱ"]={ "α","̄" }, + ["ᾲ"]={ "ὰ","ͅ" }, + ["ᾳ"]={ "α","ͅ" }, + ["ᾴ"]={ "ά","ͅ" }, + ["ᾶ"]={ "α","͂" }, + ["ᾷ"]={ "ᾶ","ͅ" }, + ["Ᾰ"]={ "Α","̆" }, + ["Ᾱ"]={ "Α","̄" }, + ["Ὰ"]={ "Α","̀" }, + ["ᾼ"]={ "Α","ͅ" }, + ["῁"]={ "¨","͂" }, + ["ῂ"]={ "ὴ","ͅ" }, + ["ῃ"]={ "η","ͅ" }, + ["ῄ"]={ "ή","ͅ" }, + ["ῆ"]={ "η","͂" }, + ["ῇ"]={ "ῆ","ͅ" }, + ["Ὲ"]={ "Ε","̀" }, + ["Ὴ"]={ "Η","̀" }, + ["ῌ"]={ "Η","ͅ" }, + ["῍"]={ "᾿","̀" }, + ["῎"]={ "᾿","́" }, + ["῏"]={ "᾿","͂" }, + ["ῐ"]={ "ι","̆" }, + ["ῑ"]={ "ι","̄" }, + ["ῒ"]={ "ϊ","̀" }, + ["ῖ"]={ "ι","͂" }, + ["ῗ"]={ "ϊ","͂" }, + ["Ῐ"]={ "Ι","̆" }, + ["Ῑ"]={ "Ι","̄" }, + ["Ὶ"]={ "Ι","̀" }, + ["῝"]={ "῾","̀" }, + ["῞"]={ "῾","́" }, + ["῟"]={ "῾","͂" }, + ["ῠ"]={ "υ","̆" }, + ["ῡ"]={ "υ","̄" }, + ["ῢ"]={ "ϋ","̀" }, + ["ῤ"]={ "ρ","̓" }, + ["ῥ"]={ "ρ","̔" }, + ["ῦ"]={ "υ","͂" }, + ["ῧ"]={ "ϋ","͂" }, + ["Ῠ"]={ "Υ","̆" }, + ["Ῡ"]={ "Υ","̄" }, + ["Ὺ"]={ "Υ","̀" }, + ["Ῥ"]={ "Ρ","̔" }, + ["῭"]={ "¨","̀" }, + ["ῲ"]={ "ὼ","ͅ" }, + ["ῳ"]={ "ω","ͅ" }, + ["ῴ"]={ "ώ","ͅ" }, + ["ῶ"]={ "ω","͂" }, + ["ῷ"]={ "ῶ","ͅ" }, + ["Ὸ"]={ "Ο","̀" }, + ["Ὼ"]={ "Ω","̀" }, + ["ῼ"]={ "Ω","ͅ" }, + ["↚"]={ "←","̸" }, + ["↛"]={ "→","̸" }, + ["↮"]={ "↔","̸" }, + ["⇍"]={ "⇐","̸" }, + ["⇎"]={ "⇔","̸" }, + ["⇏"]={ "⇒","̸" }, + ["∄"]={ "∃","̸" }, + ["∉"]={ "∈","̸" }, + ["∌"]={ "∋","̸" }, + ["∤"]={ "∣","̸" }, + ["∦"]={ "∥","̸" }, + ["≁"]={ "∼","̸" }, + ["≄"]={ "≃","̸" }, + ["≇"]={ "≅","̸" }, + ["≉"]={ "≈","̸" }, + ["≠"]={ "=","̸" }, + ["≢"]={ "≡","̸" }, + ["≭"]={ "≍","̸" }, + ["≮"]={ "<","̸" }, + ["≯"]={ ">","̸" }, + ["≰"]={ "≤","̸" }, + ["≱"]={ "≥","̸" }, + ["≴"]={ "≲","̸" }, + ["≵"]={ "≳","̸" }, + ["≸"]={ "≶","̸" }, + ["≹"]={ "≷","̸" }, + ["⊀"]={ "≺","̸" }, + ["⊁"]={ "≻","̸" }, + ["⊄"]={ "⊂","̸" }, + ["⊅"]={ "⊃","̸" }, + ["⊈"]={ "⊆","̸" }, + ["⊉"]={ "⊇","̸" }, + ["⊬"]={ "⊢","̸" }, + ["⊭"]={ "⊨","̸" }, + ["⊮"]={ "⊩","̸" }, + ["⊯"]={ "⊫","̸" }, + ["⋠"]={ "≼","̸" }, + ["⋡"]={ "≽","̸" }, + ["⋢"]={ "⊑","̸" }, + ["⋣"]={ "⊒","̸" }, + ["⋪"]={ "⊲","̸" }, + ["⋫"]={ "⊳","̸" }, + ["⋬"]={ "⊴","̸" }, + ["⋭"]={ "⊵","̸" }, + ["⫝̸"]={ "⫝","̸" }, + ["が"]={ "か","゙" }, + ["ぎ"]={ "き","゙" }, + ["ぐ"]={ "く","゙" }, + ["げ"]={ "け","゙" }, + ["ご"]={ "こ","゙" }, + ["ざ"]={ "さ","゙" }, + ["じ"]={ "し","゙" }, + ["ず"]={ "す","゙" }, + ["ぜ"]={ "せ","゙" }, + ["ぞ"]={ "そ","゙" }, + ["だ"]={ "た","゙" }, + ["ぢ"]={ "ち","゙" }, + ["づ"]={ "つ","゙" }, + ["で"]={ "て","゙" }, + ["ど"]={ "と","゙" }, + ["ば"]={ "は","゙" }, + ["ぱ"]={ "は","゚" }, + ["び"]={ "ひ","゙" }, + ["ぴ"]={ "ひ","゚" }, + ["ぶ"]={ "ふ","゙" }, + ["ぷ"]={ "ふ","゚" }, + ["べ"]={ "へ","゙" }, + ["ぺ"]={ "へ","゚" }, + ["ぼ"]={ "ほ","゙" }, + ["ぽ"]={ "ほ","゚" }, + ["ゔ"]={ "う","゙" }, + ["ゞ"]={ "ゝ","゙" }, + ["ガ"]={ "カ","゙" }, + ["ギ"]={ "キ","゙" }, + ["グ"]={ "ク","゙" }, + ["ゲ"]={ "ケ","゙" }, + ["ゴ"]={ "コ","゙" }, + ["ザ"]={ "サ","゙" }, + ["ジ"]={ "シ","゙" }, + ["ズ"]={ "ス","゙" }, + ["ゼ"]={ "セ","゙" }, + ["ゾ"]={ "ソ","゙" }, + ["ダ"]={ "タ","゙" }, + ["ヂ"]={ "チ","゙" }, + ["ヅ"]={ "ツ","゙" }, + ["デ"]={ "テ","゙" }, + ["ド"]={ "ト","゙" }, + ["バ"]={ "ハ","゙" }, + ["パ"]={ "ハ","゚" }, + ["ビ"]={ "ヒ","゙" }, + ["ピ"]={ "ヒ","゚" }, + ["ブ"]={ "フ","゙" }, + ["プ"]={ "フ","゚" }, + ["ベ"]={ "ヘ","゙" }, + ["ペ"]={ "ヘ","゚" }, + ["ボ"]={ "ホ","゙" }, + ["ポ"]={ "ホ","゚" }, + ["ヴ"]={ "ウ","゙" }, + ["ヷ"]={ "ワ","゙" }, + ["ヸ"]={ "ヰ","゙" }, + ["ヹ"]={ "ヱ","゙" }, + ["ヺ"]={ "ヲ","゙" }, + ["ヾ"]={ "ヽ","゙" }, + ["יִ"]={ "י","ִ" }, + ["ײַ"]={ "ײ","ַ" }, + ["שׁ"]={ "ש","ׁ" }, + ["שׂ"]={ "ש","ׂ" }, + ["שּׁ"]={ "שּ","ׁ" }, + ["שּׂ"]={ "שּ","ׂ" }, + ["אַ"]={ "א","ַ" }, + ["אָ"]={ "א","ָ" }, + ["אּ"]={ "א","ּ" }, + ["בּ"]={ "ב","ּ" }, + ["גּ"]={ "ג","ּ" }, + ["דּ"]={ "ד","ּ" }, + ["הּ"]={ "ה","ּ" }, + ["וּ"]={ "ו","ּ" }, + ["זּ"]={ "ז","ּ" }, + ["טּ"]={ "ט","ּ" }, + ["יּ"]={ "י","ּ" }, + ["ךּ"]={ "ך","ּ" }, + ["כּ"]={ "כ","ּ" }, + ["לּ"]={ "ל","ּ" }, + ["מּ"]={ "מ","ּ" }, + ["נּ"]={ "נ","ּ" }, + ["סּ"]={ "ס","ּ" }, + ["ףּ"]={ "ף","ּ" }, + ["פּ"]={ "פ","ּ" }, + ["צּ"]={ "צ","ּ" }, + ["קּ"]={ "ק","ּ" }, + ["רּ"]={ "ר","ּ" }, + ["שּ"]={ "ש","ּ" }, + ["תּ"]={ "ת","ּ" }, + ["וֹ"]={ "ו","ֹ" }, + ["בֿ"]={ "ב","ֿ" }, + ["כֿ"]={ "כ","ֿ" }, + ["פֿ"]={ "פ","ֿ" }, + ["𑂚"]={ "𑂙","𑂺" }, + ["𑂜"]={ "𑂛","𑂺" }, + ["𑂫"]={ "𑂥","𑂺" }, + ["𑄮"]={ "𑄱","𑄧" }, + ["𑄯"]={ "𑄲","𑄧" }, + ["𑍋"]={ "𑍇","𑌾" }, + ["𑍌"]={ "𑍇","𑍗" }, + ["𑒻"]={ "𑒹","𑒺" }, + ["𑒼"]={ "𑒹","𑒰" }, + ["𑒾"]={ "𑒹","𑒽" }, + ["𑖺"]={ "𑖸","𑖯" }, + ["𑖻"]={ "𑖹","𑖯" }, + ["𝅗𝅥"]={ "𝅗","𝅥" }, + ["𝅘𝅥"]={ "𝅘","𝅥" }, + ["𝅘𝅥𝅮"]={ "𝅘𝅥","𝅮" }, + ["𝅘𝅥𝅯"]={ "𝅘𝅥","𝅯" }, + ["𝅘𝅥𝅰"]={ "𝅘𝅥","𝅰" }, + ["𝅘𝅥𝅱"]={ "𝅘𝅥","𝅱" }, + ["𝅘𝅥𝅲"]={ "𝅘𝅥","𝅲" }, + ["𝆹𝅥"]={ "𝆹","𝅥" }, + ["𝆺𝅥"]={ "𝆺","𝅥" }, + ["𝆹𝅥𝅮"]={ "𝆹𝅥","𝅮" }, + ["𝆺𝅥𝅮"]={ "𝆺𝅥","𝅮" }, + ["𝆹𝅥𝅯"]={ "𝆹𝅥","𝅯" }, + ["𝆺𝅥𝅯"]={ "𝆺𝅥","𝅯" }, + }, + }, + }, + ["name"]="collapse", + ["prepend"]=true, + ["type"]="ligature", +} end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules={} end modules ['font-gbn']={ +if not modules then modules={} end modules ['luatex-fonts-gbn']={ version=1.001, comment="companion to luatex-*.tex", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", @@ -27032,9 +37407,10 @@ if not modules then modules={} end modules ['font-gbn']={ license="see context related readme files" } if context then - texio.write_nl("fatal error: this module is not for context") - os.exit() +--removed + end +local next=next local fonts=fonts local nodes=nodes local nuts=nodes.nuts @@ -27056,11 +37432,11 @@ local setlink=nuts.setlink local setprev=nuts.setprev local n_ligaturing=node.ligaturing local n_kerning=node.kerning -local ligaturing=nuts.ligaturing -local kerning=nuts.kerning +local d_ligaturing=nuts.ligaturing +local d_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 +local function l_warning() logs.report("fonts","don't call 'node.ligaturing' directly") l_warning=nil end +local function k_warning() logs.report("fonts","don't call 'node.kerning' directly") k_warning=nil end function node.ligaturing(...) if basemodepass and l_warning then l_warning() @@ -27073,20 +37449,32 @@ function node.kerning(...) end return n_kerning(...) end +function nuts.ligaturing(...) + if basemodepass and l_warning then + l_warning() + end + return d_ligaturing(...) +end +function nuts.kerning(...) + if basemodepass and k_warning then + k_warning() + end + return d_kerning(...) +end function nodes.handlers.setbasemodepass(v) basemodepass=v end -function nodes.handlers.nodepass(head) +local function nodepass(head,groupcode,size,packtype,direction) 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 nofused=0 + for n in traverse_id(glyph_code,head) do local font=getfont(n) if font~=prevfont then if basefont then @@ -27102,6 +37490,7 @@ function nodes.handlers.nodepass(head) local processors=shared.processes if processors and #processors>0 then usedfonts[font]=processors + nofused=nofused+1 elseif basemodepass then basefont={ n,nil } basefonts[#basefonts+1]=basefont @@ -27122,7 +37511,7 @@ function nodes.handlers.nodepass(head) end if variants then local char=getchar(n) - if char>=0xFE00 and (char<=0xFE0F or (char>=0xE0100 and char<=0xE01EF)) then + if (char>=0xFE00 and char<=0xFE0F) or (char>=0xE0100 and char<=0xE01EF) then local hash=variants[char] if hash then local p=getprev(n) @@ -27146,8 +37535,8 @@ function nodes.handlers.nodepass(head) for i=1,#redundant do local r=redundant[i] local p,n=getboth(r) - if r==nuthead then - nuthead=n + if r==head then + head=n setprev(n) else setlink(p,n) @@ -27166,7 +37555,7 @@ function nodes.handlers.nodepass(head) flush_node(r) end end - for d in traverse_id(disc_code,nuthead) do + for d in traverse_id(disc_code,head) do local _,_,r=getdisc(d) if r then for n in traverse_id(glyph_code,r) do @@ -27182,6 +37571,7 @@ function nodes.handlers.nodepass(head) local processors=shared.processes if processors and #processors>0 then usedfonts[font]=processors + nofused=nofused+1 end end end @@ -27193,7 +37583,7 @@ function nodes.handlers.nodepass(head) if next(usedfonts) then for font,processors in next,usedfonts do for i=1,#processors do - head=processors[i](head,font,0) or head + head=processors[i](head,font,0,direction,nofused) or head end end end @@ -27203,16 +37593,16 @@ function nodes.handlers.nodepass(head) local start=range[1] local stop=range[2] if start then - local front=nuthead==start + local front=head==start local prev,next if stop then next=getnext(stop) - start,stop=ligaturing(start,stop) - start,stop=kerning(start,stop) + start,stop=d_ligaturing(start,stop) + start,stop=d_kerning(start,stop) else prev=getprev(start) - start=ligaturing(start) - start=kerning(start) + start=d_ligaturing(start) + start=d_kerning(start) end if prev then setlink(prev,start) @@ -27220,40 +37610,57 @@ function nodes.handlers.nodepass(head) if next then setlink(stop,next) end - if front and nuthead~=start then - head=tonode(start) + if front and head~=start then + head=start end end end end - return head,true - else - return head,false end + return head end -function nodes.handlers.basepass(head) - if not basemodepass then - head=n_ligaturing(head) - head=n_kerning(head) +local function basepass(head) + if basemodepass then + head=d_ligaturing(head) + head=d_kerning(head) end - return head,true + return head end -local nodepass=nodes.handlers.nodepass -local basepass=nodes.handlers.basepass +local protectpass=node.direct.protect_glyphs local injectpass=nodes.injections.handler -local protectpass=nodes.handlers.protectglyphs -function nodes.simple_font_handler(head) +function nodes.handlers.nodepass(head,...) + if head then + return tonode(nodepass(tonut(head),...)) + end +end +function nodes.handlers.basepass(head) + if head then + return tonode(basepass(tonut(head))) + end +end +function nodes.handlers.injectpass(head) + if head then + return tonode(injectpass(tonut(head))) + end +end +function nodes.handlers.protectpass(head) + if head then + protectpass(tonut(head)) + return head + end +end +function nodes.simple_font_handler(head,groupcode,size,packtype,direction) if head then - head=nodepass(head) + head=tonut(head) + head=nodepass(head,groupcode,size,packtype,direction) head=injectpass(head) if not basemodepass then head=basepass(head) end protectpass(head) - return head,true - else - return head,false + head=tonode(head) end + return head end end -- closure diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib-test.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib-test.lua deleted file mode 100644 index 00d7c5a4efe..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib-test.lua +++ /dev/null @@ -1,743 +0,0 @@ - - --- local gm = require("swiglib.gmwand.core") -- local gm = swiglib("gmwand.core") --- --- gm.InitializeMagick(".") --- --- local magick_wand = gm.NewMagickWand() --- local drawing_wand = gm.NewDrawingWand() --- --- gm.MagickSetSize(magick_wand,800,600) --- gm.MagickReadImage(magick_wand,"xc:red") --- --- gm.DrawPushGraphicContext(drawing_wand) --- --- gm.DrawSetFillColor(drawing_wand,gm.NewPixelWand()) --- --- -- gm.DrawSetFont(drawing_wand, kpse.findfile("DejaVuSerifBold.ttf")) --- -- gm.DrawSetFontSize(drawing_wand, 96) --- -- gm.DrawAnnotation(drawing_wand,300,200, "LuaTeX") --- --- gm.DrawPopGraphicContext(drawing_wand) --- gm.MagickDrawImage(magick_wand,drawing_wand) --- --- gm.MagickWriteImages(magick_wand,"./luatex-swiglib-test.jpg",1) --- --- gm.DestroyDrawingWand(drawing_wand) --- gm.DestroyMagickWand(magick_wand) - -local swighelpers = require("swiglib.helpers.core") -local sqlite = require("swiglib.sqlite.core") - --- s.sqlite3_create_function_v2_lua_callback(function() end) - - -local t = [[ - CREATE TABLE IF NOT EXISTS loggers ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `time` int(11) NOT NULL, - `type` int(11) NOT NULL, - `action` varchar(15) NOT NULL, - `data` longtext, - PRIMARY KEY (`id`), - UNIQUE KEY `id_unique_key` (`id`) - ) - DEFAULT CHARSET = utf8 ; -]] - -local t = [[ -CREATE TABLE IF NOT EXISTS loggers ( - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `time` int(11) NOT NULL, - `type` int(11) NOT NULL, - `action` varchar(15) NOT NULL, - `data` longtext - ); -]] - -local function execute(name,query) - local db = sqlite.new_sqlite3_p_array(1) - local okay = sqlite.sqlite3_open(name,db) - local dbh = sqlite.sqlite3_p_array_getitem(db,0) - if okay ~= sqlite.SQLITE_OK then - return false, sqlite.sqlite3_errmsg(dbh) - end - local result = { } - local callback = function(data,nofcolumns,values,fields) - local r = { } - for i=0,nofcolumns-1 do - local field = sqlite.char_p_array_getitem(fields,i) - local value = sqlite.char_p_array_getitem(values,i) - r[field] = value - end - result[#result+1] = r - return sqlite.SQLITE_OK - end - local data = nil - local message = nil - local okay = sqlite.sqlite3_exec_lua_callback(dbh,query,callback,data,message) - if okay ~= sqlite.SQLITE_OK then - return false, sqlite.sqlite3_errmsg(dbh) - end - sqlite.sqlite3_close(dbh) - sqlite.delete_sqlite3_p_array(db) - return result -end - -execute("foo.db",t) --- execute("test.db",t) - -local template =[[ - INSERT INTO loggers ( - `time`, - `type`, - `action`, - `data` - ) VALUES ( - %s, - %s, - '%s', - '%s' - ) ; -]] - --- for i=1,100 do --- local t = string.format(template,os.time(),1,"oeps " .. i,"more") --- execute("foo.db",t) --- end - - -local r = execute("foo.db","select * from loggers;") -inspect(r) - - --- inspect(table.sortedkeys(sqlite)) --- inspect(table.sortedkeys(swighelpers)) - - - -t={ - "FULLY_WITHIN", - "NOT_WITHIN", - "PARTLY_WITHIN", - "SQLITE3_TEXT", - "SQLITE_ABORT", - "SQLITE_ABORT_ROLLBACK", - "SQLITE_ACCESS_EXISTS", - "SQLITE_ACCESS_READ", - "SQLITE_ACCESS_READWRITE", - "SQLITE_ALTER_TABLE", - "SQLITE_ANALYZE", - "SQLITE_ANY", - "SQLITE_ATTACH", - "SQLITE_AUTH", - "SQLITE_AUTH_USER", - "SQLITE_BLOB", - "SQLITE_BUSY", - "SQLITE_BUSY_RECOVERY", - "SQLITE_BUSY_SNAPSHOT", - "SQLITE_CANTOPEN", - "SQLITE_CANTOPEN_CONVPATH", - "SQLITE_CANTOPEN_FULLPATH", - "SQLITE_CANTOPEN_ISDIR", - "SQLITE_CANTOPEN_NOTEMPDIR", - "SQLITE_CHECKPOINT_FULL", - "SQLITE_CHECKPOINT_PASSIVE", - "SQLITE_CHECKPOINT_RESTART", - "SQLITE_CONFIG_COVERING_INDEX_SCAN", - "SQLITE_CONFIG_GETMALLOC", - "SQLITE_CONFIG_GETMUTEX", - "SQLITE_CONFIG_GETPCACHE", - "SQLITE_CONFIG_GETPCACHE2", - "SQLITE_CONFIG_HEAP", - "SQLITE_CONFIG_LOG", - "SQLITE_CONFIG_LOOKASIDE", - "SQLITE_CONFIG_MALLOC", - "SQLITE_CONFIG_MEMSTATUS", - "SQLITE_CONFIG_MMAP_SIZE", - "SQLITE_CONFIG_MULTITHREAD", - "SQLITE_CONFIG_MUTEX", - "SQLITE_CONFIG_PAGECACHE", - "SQLITE_CONFIG_PCACHE", - "SQLITE_CONFIG_PCACHE2", - "SQLITE_CONFIG_SCRATCH", - "SQLITE_CONFIG_SERIALIZED", - "SQLITE_CONFIG_SINGLETHREAD", - "SQLITE_CONFIG_SQLLOG", - "SQLITE_CONFIG_URI", - "SQLITE_CONFIG_WIN32_HEAPSIZE", - "SQLITE_CONSTRAINT", - "SQLITE_CONSTRAINT_CHECK", - "SQLITE_CONSTRAINT_COMMITHOOK", - "SQLITE_CONSTRAINT_FOREIGNKEY", - "SQLITE_CONSTRAINT_FUNCTION", - "SQLITE_CONSTRAINT_NOTNULL", - "SQLITE_CONSTRAINT_PRIMARYKEY", - "SQLITE_CONSTRAINT_ROWID", - "SQLITE_CONSTRAINT_TRIGGER", - "SQLITE_CONSTRAINT_UNIQUE", - "SQLITE_CONSTRAINT_VTAB", - "SQLITE_COPY", - "SQLITE_CORRUPT", - "SQLITE_CORRUPT_VTAB", - "SQLITE_CREATE_INDEX", - "SQLITE_CREATE_TABLE", - "SQLITE_CREATE_TEMP_INDEX", - "SQLITE_CREATE_TEMP_TABLE", - "SQLITE_CREATE_TEMP_TRIGGER", - "SQLITE_CREATE_TEMP_VIEW", - "SQLITE_CREATE_TRIGGER", - "SQLITE_CREATE_VIEW", - "SQLITE_CREATE_VTABLE", - "SQLITE_DBCONFIG_ENABLE_FKEY", - "SQLITE_DBCONFIG_ENABLE_TRIGGER", - "SQLITE_DBCONFIG_LOOKASIDE", - "SQLITE_DBSTATUS_CACHE_HIT", - "SQLITE_DBSTATUS_CACHE_MISS", - "SQLITE_DBSTATUS_CACHE_USED", - "SQLITE_DBSTATUS_CACHE_WRITE", - "SQLITE_DBSTATUS_DEFERRED_FKS", - "SQLITE_DBSTATUS_LOOKASIDE_HIT", - "SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL", - "SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE", - "SQLITE_DBSTATUS_LOOKASIDE_USED", - "SQLITE_DBSTATUS_MAX", - "SQLITE_DBSTATUS_SCHEMA_USED", - "SQLITE_DBSTATUS_STMT_USED", - "SQLITE_DELETE", - "SQLITE_DENY", - "SQLITE_DETACH", - "SQLITE_DETERMINISTIC", - "SQLITE_DONE", - "SQLITE_DROP_INDEX", - "SQLITE_DROP_TABLE", - "SQLITE_DROP_TEMP_INDEX", - "SQLITE_DROP_TEMP_TABLE", - "SQLITE_DROP_TEMP_TRIGGER", - "SQLITE_DROP_TEMP_VIEW", - "SQLITE_DROP_TRIGGER", - "SQLITE_DROP_VIEW", - "SQLITE_DROP_VTABLE", - "SQLITE_EMPTY", - "SQLITE_ERROR", - "SQLITE_FAIL", - "SQLITE_FCNTL_BUSYHANDLER", - "SQLITE_FCNTL_CHUNK_SIZE", - "SQLITE_FCNTL_COMMIT_PHASETWO", - "SQLITE_FCNTL_FILE_POINTER", - "SQLITE_FCNTL_HAS_MOVED", - "SQLITE_FCNTL_LOCKSTATE", - "SQLITE_FCNTL_MMAP_SIZE", - "SQLITE_FCNTL_OVERWRITE", - "SQLITE_FCNTL_PERSIST_WAL", - "SQLITE_FCNTL_POWERSAFE_OVERWRITE", - "SQLITE_FCNTL_PRAGMA", - "SQLITE_FCNTL_SIZE_HINT", - "SQLITE_FCNTL_SYNC", - "SQLITE_FCNTL_SYNC_OMITTED", - "SQLITE_FCNTL_TEMPFILENAME", - "SQLITE_FCNTL_TRACE", - "SQLITE_FCNTL_VFSNAME", - "SQLITE_FCNTL_WIN32_AV_RETRY", - "SQLITE_FCNTL_WIN32_SET_HANDLE", - "SQLITE_FLOAT", - "SQLITE_FORMAT", - "SQLITE_FULL", - "SQLITE_FUNCTION", - "SQLITE_GET_LOCKPROXYFILE", - "SQLITE_IGNORE", - "SQLITE_INDEX_CONSTRAINT_EQ", - "SQLITE_INDEX_CONSTRAINT_GE", - "SQLITE_INDEX_CONSTRAINT_GT", - "SQLITE_INDEX_CONSTRAINT_LE", - "SQLITE_INDEX_CONSTRAINT_LT", - "SQLITE_INDEX_CONSTRAINT_MATCH", - "SQLITE_INSERT", - "SQLITE_INTEGER", - "SQLITE_INTERNAL", - "SQLITE_INTERRUPT", - "SQLITE_IOCAP_ATOMIC", - "SQLITE_IOCAP_ATOMIC16K", - "SQLITE_IOCAP_ATOMIC1K", - "SQLITE_IOCAP_ATOMIC2K", - "SQLITE_IOCAP_ATOMIC32K", - "SQLITE_IOCAP_ATOMIC4K", - "SQLITE_IOCAP_ATOMIC512", - "SQLITE_IOCAP_ATOMIC64K", - "SQLITE_IOCAP_ATOMIC8K", - "SQLITE_IOCAP_IMMUTABLE", - "SQLITE_IOCAP_POWERSAFE_OVERWRITE", - "SQLITE_IOCAP_SAFE_APPEND", - "SQLITE_IOCAP_SEQUENTIAL", - "SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN", - "SQLITE_IOERR", - "SQLITE_IOERR_ACCESS", - "SQLITE_IOERR_BLOCKED", - "SQLITE_IOERR_CHECKRESERVEDLOCK", - "SQLITE_IOERR_CLOSE", - "SQLITE_IOERR_CONVPATH", - "SQLITE_IOERR_DELETE", - "SQLITE_IOERR_DELETE_NOENT", - "SQLITE_IOERR_DIR_CLOSE", - "SQLITE_IOERR_DIR_FSYNC", - "SQLITE_IOERR_FSTAT", - "SQLITE_IOERR_FSYNC", - "SQLITE_IOERR_GETTEMPPATH", - "SQLITE_IOERR_LOCK", - "SQLITE_IOERR_MMAP", - "SQLITE_IOERR_NOMEM", - "SQLITE_IOERR_RDLOCK", - "SQLITE_IOERR_READ", - "SQLITE_IOERR_SEEK", - "SQLITE_IOERR_SHMLOCK", - "SQLITE_IOERR_SHMMAP", - "SQLITE_IOERR_SHMOPEN", - "SQLITE_IOERR_SHMSIZE", - "SQLITE_IOERR_SHORT_READ", - "SQLITE_IOERR_TRUNCATE", - "SQLITE_IOERR_UNLOCK", - "SQLITE_IOERR_WRITE", - "SQLITE_LAST_ERRNO", - "SQLITE_LIMIT_ATTACHED", - "SQLITE_LIMIT_COLUMN", - "SQLITE_LIMIT_COMPOUND_SELECT", - "SQLITE_LIMIT_EXPR_DEPTH", - "SQLITE_LIMIT_FUNCTION_ARG", - "SQLITE_LIMIT_LENGTH", - "SQLITE_LIMIT_LIKE_PATTERN_LENGTH", - "SQLITE_LIMIT_SQL_LENGTH", - "SQLITE_LIMIT_TRIGGER_DEPTH", - "SQLITE_LIMIT_VARIABLE_NUMBER", - "SQLITE_LIMIT_VDBE_OP", - "SQLITE_LIMIT_WORKER_THREADS", - "SQLITE_LOCKED", - "SQLITE_LOCKED_SHAREDCACHE", - "SQLITE_LOCK_EXCLUSIVE", - "SQLITE_LOCK_NONE", - "SQLITE_LOCK_PENDING", - "SQLITE_LOCK_RESERVED", - "SQLITE_LOCK_SHARED", - "SQLITE_MISMATCH", - "SQLITE_MISUSE", - "SQLITE_MUTEX_FAST", - "SQLITE_MUTEX_RECURSIVE", - "SQLITE_MUTEX_STATIC_APP1", - "SQLITE_MUTEX_STATIC_APP2", - "SQLITE_MUTEX_STATIC_APP3", - "SQLITE_MUTEX_STATIC_LRU", - "SQLITE_MUTEX_STATIC_LRU2", - "SQLITE_MUTEX_STATIC_MASTER", - "SQLITE_MUTEX_STATIC_MEM", - "SQLITE_MUTEX_STATIC_MEM2", - "SQLITE_MUTEX_STATIC_OPEN", - "SQLITE_MUTEX_STATIC_PMEM", - "SQLITE_MUTEX_STATIC_PRNG", - "SQLITE_NOLFS", - "SQLITE_NOMEM", - "SQLITE_NOTADB", - "SQLITE_NOTFOUND", - "SQLITE_NOTICE", - "SQLITE_NOTICE_RECOVER_ROLLBACK", - "SQLITE_NOTICE_RECOVER_WAL", - "SQLITE_NULL", - "SQLITE_OK", - "SQLITE_OPEN_AUTOPROXY", - "SQLITE_OPEN_CREATE", - "SQLITE_OPEN_DELETEONCLOSE", - "SQLITE_OPEN_EXCLUSIVE", - "SQLITE_OPEN_FULLMUTEX", - "SQLITE_OPEN_MAIN_DB", - "SQLITE_OPEN_MAIN_JOURNAL", - "SQLITE_OPEN_MASTER_JOURNAL", - "SQLITE_OPEN_MEMORY", - "SQLITE_OPEN_NOMUTEX", - "SQLITE_OPEN_PRIVATECACHE", - "SQLITE_OPEN_READONLY", - "SQLITE_OPEN_READWRITE", - "SQLITE_OPEN_SHAREDCACHE", - "SQLITE_OPEN_SUBJOURNAL", - "SQLITE_OPEN_TEMP_DB", - "SQLITE_OPEN_TEMP_JOURNAL", - "SQLITE_OPEN_TRANSIENT_DB", - "SQLITE_OPEN_URI", - "SQLITE_OPEN_WAL", - "SQLITE_PERM", - "SQLITE_PRAGMA", - "SQLITE_PROTOCOL", - "SQLITE_RANGE", - "SQLITE_READ", - "SQLITE_READONLY", - "SQLITE_READONLY_CANTLOCK", - "SQLITE_READONLY_DBMOVED", - "SQLITE_READONLY_RECOVERY", - "SQLITE_READONLY_ROLLBACK", - "SQLITE_RECURSIVE", - "SQLITE_REINDEX", - "SQLITE_REPLACE", - "SQLITE_ROLLBACK", - "SQLITE_ROW", - "SQLITE_SAVEPOINT", - "SQLITE_SCHEMA", - "SQLITE_SELECT", - "SQLITE_SET_LOCKPROXYFILE", - "SQLITE_SHM_EXCLUSIVE", - "SQLITE_SHM_LOCK", - "SQLITE_SHM_NLOCK", - "SQLITE_SHM_SHARED", - "SQLITE_SHM_UNLOCK", - "SQLITE_SOURCE_ID", - "SQLITE_STATUS_MALLOC_COUNT", - "SQLITE_STATUS_MALLOC_SIZE", - "SQLITE_STATUS_MEMORY_USED", - "SQLITE_STATUS_PAGECACHE_OVERFLOW", - "SQLITE_STATUS_PAGECACHE_SIZE", - "SQLITE_STATUS_PAGECACHE_USED", - "SQLITE_STATUS_PARSER_STACK", - "SQLITE_STATUS_SCRATCH_OVERFLOW", - "SQLITE_STATUS_SCRATCH_SIZE", - "SQLITE_STATUS_SCRATCH_USED", - "SQLITE_STMTSTATUS_AUTOINDEX", - "SQLITE_STMTSTATUS_FULLSCAN_STEP", - "SQLITE_STMTSTATUS_SORT", - "SQLITE_STMTSTATUS_VM_STEP", - "SQLITE_SYNC_DATAONLY", - "SQLITE_SYNC_FULL", - "SQLITE_SYNC_NORMAL", - "SQLITE_TESTCTRL_ALWAYS", - "SQLITE_TESTCTRL_ASSERT", - "SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS", - "SQLITE_TESTCTRL_BITVEC_TEST", - "SQLITE_TESTCTRL_BYTEORDER", - "SQLITE_TESTCTRL_EXPLAIN_STMT", - "SQLITE_TESTCTRL_FAULT_INSTALL", - "SQLITE_TESTCTRL_FIRST", - "SQLITE_TESTCTRL_ISINIT", - "SQLITE_TESTCTRL_ISKEYWORD", - "SQLITE_TESTCTRL_LAST", - "SQLITE_TESTCTRL_LOCALTIME_FAULT", - "SQLITE_TESTCTRL_NEVER_CORRUPT", - "SQLITE_TESTCTRL_OPTIMIZATIONS", - "SQLITE_TESTCTRL_PENDING_BYTE", - "SQLITE_TESTCTRL_PRNG_RESET", - "SQLITE_TESTCTRL_PRNG_RESTORE", - "SQLITE_TESTCTRL_PRNG_SAVE", - "SQLITE_TESTCTRL_RESERVE", - "SQLITE_TESTCTRL_SCRATCHMALLOC", - "SQLITE_TESTCTRL_SORTER_MMAP", - "SQLITE_TESTCTRL_VDBE_COVERAGE", - "SQLITE_TEXT", - "SQLITE_TOOBIG", - "SQLITE_TRANSACTION", - "SQLITE_UPDATE", - "SQLITE_UTF16", - "SQLITE_UTF16BE", - "SQLITE_UTF16LE", - "SQLITE_UTF16_ALIGNED", - "SQLITE_UTF8", - "SQLITE_VERSION", - "SQLITE_VERSION_NUMBER", - "SQLITE_VTAB_CONSTRAINT_SUPPORT", - "SQLITE_WARNING", - "SQLITE_WARNING_AUTOINDEX", - "call_callback_sqlite3_create_collation_v2_1", - "call_callback_sqlite3_create_collation_v2_2", - "call_callback_sqlite3_create_function16_1", - "call_callback_sqlite3_create_function16_2", - "call_callback_sqlite3_create_function16_3", - "call_callback_sqlite3_create_function_1", - "call_callback_sqlite3_create_function_2", - "call_callback_sqlite3_create_function_3", - "call_callback_sqlite3_create_function_v2_1", - "call_callback_sqlite3_create_function_v2_2", - "call_callback_sqlite3_create_function_v2_3", - "call_callback_sqlite3_create_function_v2_4", - "call_callback_sqlite3_rtree_query_callback_1", - "call_callback_sqlite3_rtree_query_callback_2", - "char_p_array_getitem", - "char_p_array_setitem", - "delete_char_p_array", - "delete_sqlite3_p_array", - "delete_sqlite3_rtree_dbl_array", - "delete_sqlite3_stmt_p_array", - "delete_sqlite3_value_p_array", - "delete_sqlite3_vtab_cursor_p_array", - "delete_sqlite3_vtab_p_array", - "delete_void_p_array", - "new_char_p_array", - "new_sqlite3_p_array", - "new_sqlite3_rtree_dbl_array", - "new_sqlite3_stmt_p_array", - "new_sqlite3_value_p_array", - "new_sqlite3_vtab_cursor_p_array", - "new_sqlite3_vtab_p_array", - "new_void_p_array", - "sqlite3_aggregate_context", - "sqlite3_aggregate_count", - "sqlite3_auto_extension", - "sqlite3_auto_extension_lua_callback", - "sqlite3_backup_finish", - "sqlite3_backup_init", - "sqlite3_backup_pagecount", - "sqlite3_backup_remaining", - "sqlite3_backup_step", - "sqlite3_bind_blob", - "sqlite3_bind_blob64", - "sqlite3_bind_blob64_lua_callback", - "sqlite3_bind_blob_lua_callback", - "sqlite3_bind_double", - "sqlite3_bind_int", - "sqlite3_bind_int64", - "sqlite3_bind_null", - "sqlite3_bind_parameter_count", - "sqlite3_bind_parameter_index", - "sqlite3_bind_parameter_name", - "sqlite3_bind_text", - "sqlite3_bind_text16", - "sqlite3_bind_text16_lua_callback", - "sqlite3_bind_text64", - "sqlite3_bind_text64_lua_callback", - "sqlite3_bind_text_lua_callback", - "sqlite3_bind_value", - "sqlite3_bind_zeroblob", - "sqlite3_blob_bytes", - "sqlite3_blob_close", - "sqlite3_blob_open", - "sqlite3_blob_read", - "sqlite3_blob_reopen", - "sqlite3_blob_write", - "sqlite3_busy_handler", - "sqlite3_busy_handler_lua_callback", - "sqlite3_busy_timeout", - "sqlite3_cancel_auto_extension", - "sqlite3_cancel_auto_extension_lua_callback", - "sqlite3_changes", - "sqlite3_clear_bindings", - "sqlite3_close", - "sqlite3_close_v2", - "sqlite3_collation_needed", - "sqlite3_collation_needed16", - "sqlite3_collation_needed16_lua_callback", - "sqlite3_collation_needed_lua_callback", - "sqlite3_column_blob", - "sqlite3_column_bytes", - "sqlite3_column_bytes16", - "sqlite3_column_count", - "sqlite3_column_decltype", - "sqlite3_column_decltype16", - "sqlite3_column_double", - "sqlite3_column_int", - "sqlite3_column_int64", - "sqlite3_column_name", - "sqlite3_column_name16", - "sqlite3_column_text", - "sqlite3_column_text16", - "sqlite3_column_type", - "sqlite3_column_value", - "sqlite3_commit_hook", - "sqlite3_commit_hook_lua_callback", - "sqlite3_compileoption_get", - "sqlite3_compileoption_used", - "sqlite3_complete", - "sqlite3_complete16", - "sqlite3_config", - "sqlite3_context_db_handle", - "sqlite3_create_collation", - "sqlite3_create_collation16", - "sqlite3_create_collation16_lua_callback", - "sqlite3_create_collation_lua_callback", - "sqlite3_create_collation_v2", - "sqlite3_create_collation_v2_lua_callback", - "sqlite3_create_function", - "sqlite3_create_function16", - "sqlite3_create_function16_lua_callback", - "sqlite3_create_function_lua_callback", - "sqlite3_create_function_v2", - "sqlite3_create_function_v2_lua_callback", - "sqlite3_create_module", - "sqlite3_create_module_v2", - "sqlite3_create_module_v2_lua_callback", - "sqlite3_data_count", - "sqlite3_db_config", - "sqlite3_db_filename", - "sqlite3_db_handle", - "sqlite3_db_mutex", - "sqlite3_db_readonly", - "sqlite3_db_release_memory", - "sqlite3_db_status", - "sqlite3_declare_vtab", - "sqlite3_enable_load_extension", - "sqlite3_enable_shared_cache", - "sqlite3_errcode", - "sqlite3_errmsg", - "sqlite3_errmsg16", - "sqlite3_errstr", - "sqlite3_exec", - "sqlite3_exec_lua_callback", - "sqlite3_expired", - "sqlite3_extended_errcode", - "sqlite3_extended_result_codes", - "sqlite3_file", - "sqlite3_file_control", - "sqlite3_finalize", - "sqlite3_free", - "sqlite3_free_table", - "sqlite3_get_autocommit", - "sqlite3_get_auxdata", - "sqlite3_get_table", - "sqlite3_global_recover", - "sqlite3_index_info", - "sqlite3_index_info_aConstraint", - "sqlite3_index_info_aConstraintUsage", - "sqlite3_index_info_aOrderBy", - "sqlite3_initialize", - "sqlite3_interrupt", - "sqlite3_io_methods", - "sqlite3_last_insert_rowid", - "sqlite3_libversion", - "sqlite3_libversion_number", - "sqlite3_limit", - "sqlite3_load_extension", - "sqlite3_log", - "sqlite3_malloc", - "sqlite3_malloc64", - "sqlite3_mem_methods", - "sqlite3_memory_alarm", - "sqlite3_memory_highwater", - "sqlite3_memory_used", - "sqlite3_module", - "sqlite3_mprintf", - "sqlite3_msize", - "sqlite3_mutex_alloc", - "sqlite3_mutex_enter", - "sqlite3_mutex_free", - "sqlite3_mutex_leave", - "sqlite3_mutex_methods", - "sqlite3_mutex_try", - "sqlite3_next_stmt", - "sqlite3_open", - "sqlite3_open16", - "sqlite3_open_v2", - "sqlite3_os_end", - "sqlite3_os_init", - "sqlite3_overload_function", - "sqlite3_p_array_getitem", - "sqlite3_p_array_setitem", - "sqlite3_pcache_methods", - "sqlite3_pcache_methods2", - "sqlite3_pcache_page", - "sqlite3_prepare", - "sqlite3_prepare16", - "sqlite3_prepare16_v2", - "sqlite3_prepare_v2", - "sqlite3_profile", - "sqlite3_progress_handler", - "sqlite3_progress_handler_lua_callback", - "sqlite3_randomness", - "sqlite3_realloc", - "sqlite3_realloc64", - "sqlite3_release_memory", - "sqlite3_reset", - "sqlite3_reset_auto_extension", - "sqlite3_result_blob", - "sqlite3_result_blob64", - "sqlite3_result_blob64_lua_callback", - "sqlite3_result_blob_lua_callback", - "sqlite3_result_double", - "sqlite3_result_error", - "sqlite3_result_error16", - "sqlite3_result_error_code", - "sqlite3_result_error_nomem", - "sqlite3_result_error_toobig", - "sqlite3_result_int", - "sqlite3_result_int64", - "sqlite3_result_null", - "sqlite3_result_text", - "sqlite3_result_text16", - "sqlite3_result_text16_lua_callback", - "sqlite3_result_text16be", - "sqlite3_result_text16be_lua_callback", - "sqlite3_result_text16le", - "sqlite3_result_text16le_lua_callback", - "sqlite3_result_text64", - "sqlite3_result_text64_lua_callback", - "sqlite3_result_text_lua_callback", - "sqlite3_result_value", - "sqlite3_result_zeroblob", - "sqlite3_rollback_hook", - "sqlite3_rollback_hook_lua_callback", - "sqlite3_rtree_dbl_array_getitem", - "sqlite3_rtree_dbl_array_setitem", - "sqlite3_rtree_geometry", - "sqlite3_rtree_geometry_callback", - "sqlite3_rtree_geometry_callback_lua_callback", - "sqlite3_rtree_query_callback", - "sqlite3_rtree_query_callback_lua_callback", - "sqlite3_rtree_query_info", - "sqlite3_set_authorizer", - "sqlite3_set_authorizer_lua_callback", - "sqlite3_set_auxdata", - "sqlite3_set_auxdata_lua_callback", - "sqlite3_shutdown", - "sqlite3_sleep", - "sqlite3_snprintf", - "sqlite3_soft_heap_limit", - "sqlite3_soft_heap_limit64", - "sqlite3_sourceid", - "sqlite3_sql", - "sqlite3_status", - "sqlite3_step", - "sqlite3_stmt_busy", - "sqlite3_stmt_p_array_getitem", - "sqlite3_stmt_p_array_setitem", - "sqlite3_stmt_readonly", - "sqlite3_stmt_status", - "sqlite3_strglob", - "sqlite3_stricmp", - "sqlite3_strnicmp", - "sqlite3_test_control", - "sqlite3_thread_cleanup", - "sqlite3_threadsafe", - "sqlite3_total_changes", - "sqlite3_trace", - "sqlite3_trace_lua_callback", - "sqlite3_transfer_bindings", - "sqlite3_update_hook", - "sqlite3_update_hook_lua_callback", - "sqlite3_uri_boolean", - "sqlite3_uri_int64", - "sqlite3_uri_parameter", - "sqlite3_user_data", - "sqlite3_value_blob", - "sqlite3_value_bytes", - "sqlite3_value_bytes16", - "sqlite3_value_double", - "sqlite3_value_int", - "sqlite3_value_int64", - "sqlite3_value_numeric_type", - "sqlite3_value_p_array_getitem", - "sqlite3_value_p_array_setitem", - "sqlite3_value_text", - "sqlite3_value_text16", - "sqlite3_value_text16be", - "sqlite3_value_text16le", - "sqlite3_value_type", - "sqlite3_vfs", - "sqlite3_vfs_find", - "sqlite3_vfs_register", - "sqlite3_vfs_unregister", - "sqlite3_vtab", - "sqlite3_vtab_config", - "sqlite3_vtab_cursor", - "sqlite3_vtab_cursor_p_array_getitem", - "sqlite3_vtab_cursor_p_array_setitem", - "sqlite3_vtab_on_conflict", - "sqlite3_vtab_p_array_getitem", - "sqlite3_vtab_p_array_setitem", - "sqlite3_wal_autocheckpoint", - "sqlite3_wal_checkpoint", - "sqlite3_wal_checkpoint_v2", - "sqlite3_wal_hook", - "sqlite3_wal_hook_lua_callback", - "void_p_array_getitem", - "void_p_array_setitem", -} diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib-test.tex b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib-test.tex deleted file mode 100644 index d26bb6ffbb3..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib-test.tex +++ /dev/null @@ -1,11 +0,0 @@ -% luatex --fmt=luatex=plain luatex-swiglib-test.tex - -\input luatex-swiglib.tex - -\directlua { - dofile("luatex-swiglib-test.lua") -} - -\pdfximage {luatex-swiglib-test.jpg} \pdfrefximage\pdflastximage - -\end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib.lua deleted file mode 100644 index 3108dd43fb8..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib.lua +++ /dev/null @@ -1,63 +0,0 @@ -if not modules then modules = { } end modules ['luatex-swiglib'] = { - version = 1.001, - comment = "companion to luatex-swiglib.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -local savedrequire = require - -local libsuffix = os.type == "windows" and ".dll" or ".so" -local pathsplit = "([^" .. io.pathseparator .. "]+)" - -function requireswiglib(required,version) - local library = package.loaded[required] - if library then - return library - else - local name = string.gsub(required,"%.","/") .. libsuffix - local list = kpse.show_path("clua") - for root in string.gmatch(list,pathsplit) do - local full = false - if type(version) == "string" and version ~= "" then - full = root .. "/" .. version .. "/" .. name - full = lfs.isfile(full) and full - end - if not full then - full = root .. "/" .. name - full = lfs.isfile(full) and full - end - if full then - local path, base = string.match(full,"^(.-)([^\\/]+)" .. libsuffix .."$") - local savedlibrary = package.loaded[base] - package.loaded[base] = nil - local savedpath = lfs.currentdir() - lfs.chdir(path) - library = package.loadlib(full,"luaopen_" .. base) - if type(library) == "function" then - library = library() - texio.write("") - end - lfs.chdir(savedpath) - package.loaded[base] = savedlibrary - package.loaded[required] = library - return library - end - end - texio.write("") -end - -function require(name) - if string.find(name,"^swiglib%.") then - return requireswiglib(name) - else - return savedrequire(name) - end -end - -function swiglib(name,version) - return requireswiglib("swiglib." .. name,version) -end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib.tex b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib.tex deleted file mode 100644 index 7c437751587..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-swiglib.tex +++ /dev/null @@ -1,20 +0,0 @@ -%D \module -%D [ file=luatex-swiglib, -%D version=2013.03.30, -%D title=\LUATEX\ Support Macros, -%D subtitle=Generic \SWIGLIB\ Font Handler, -%D author=Hans Hagen, -%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] - -%D This is an experimental setup. Usage: -%D -%D \starttyping -%D local gm = swiglib("gmwand.core") -%D local gm = require("swiglib.gmwand.core") -%D local sq = swiglib("mysql.core") -%D local sq = swiglib("mysql.core","5.6") -%D \stoptyping - -\directlua { - dofile(kpse.find_file("luatex-swiglib.lua","tex")) -} diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-test.tex b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-test.tex deleted file mode 100644 index 2aa4f22d975..00000000000 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-test.tex +++ /dev/null @@ -1,173 +0,0 @@ -% texformat=luatex-plain - -%D \module -%D [ file=luatex-test, -%D version=2009.12.01, -%D title=\LUATEX\ Support Macros, -%D subtitle=Simple Test File, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] - -%D See \type {luatex-plain.tex} (or on my machine \type {luatex.tex} -%D for how to make a format. - -% You can generate a font database with: -% -% mtxrun --script fonts --reload --save -% -% The file luatex-fonts-names.lua has to be moved to a place -% where kpse can find it. - -\pdfoutput=1 - -\font\testa=file:lmroman10-regular at 12pt \testa \input tufte \par -\font\testb=file:lmroman12-regular:+liga; at 24pt \testb effe flink fietsen \par -\font\testc=file:lmroman12-regular:mode=node;+liga; at 24pt \testc effe flink fietsen \par -\font\testd=name:lmroman10bold at 12pt \testd a bit bold \par - -\font\oeps=cmr10 - -\font\oeps=[lmroman12-regular]:+liga at 30pt \oeps crap -\font\oeps=[lmroman12-regular] at 40pt \oeps more crap - -\font\cidtest=adobesongstd-light - -\font\mathtest=cambria(math) {\mathtest 123} - -% \font\gothic=msgothic(ms-gothic) {\gothic whatever} % no longer in windows 10 - -\bgroup - - \ifdefined\pdfprotrudechars \pdfprotrudechars \else \protrudechars \fi 2 \relax - \ifdefined\pdfadjustspacing \pdfadjustspacing \else \adjustspacing \fi 2 \relax - - \font\testb=file:lmroman12-regular:+liga;extend=1.5 at 12pt \testb \input tufte \par - \font\testb=file:lmroman12-regular:+liga;slant=0.8 at 12pt \testb \input tufte \par - \font\testb=file:lmroman12-regular:+liga;protrusion=default at 12pt \testb \input tufte \par - -\egroup - -\setmplibformat{plain} - -\directlua { - function MpTest() - metapost.print("fullcircle scaled 3cm") - end -} - -\mplibcode - beginfig(1) ; - draw fullcircle - scaled 10cm - withcolor red - withpen pencircle xscaled 4mm yscaled 2mm rotated 30 ; - draw "test" infont defaultfont scaled 4 ; - verbatimtex \sl etex; - draw btex some more test etex scaled 2 ; - currentpicture := currentpicture shifted (0,1cm) ; - verbatimtex \bf etex; - draw btex another test etex scaled 2 ; - currentpicture := currentpicture shifted (0,1cm) ; - draw btex another test etex scaled 2 ; - draw - runscript("MpTest()") - withcolor green - withpen pencircle xscaled 2mm yscaled 1mm rotated 20 ; - endfig ; -\endmplibcode - -\font\mine=file:luatex-fonts-demo-vf-1.lua at 12pt - -\mine \input tufte \par - -% \font\mine=file:luatex-fonts-demo-vf-2.lua at 12pt \mine [abab] \par -% \font\mine=file:luatex-fonts-demo-vf-3.lua at 12pt \mine [abab] \par - -\font\test=dejavuserif:+kern at 10pt \test - - -\bgroup \hsize 1mm \noindent Циолковский \par \egroup - -\loadpatterns{ru} - -\bgroup \hsize 1mm \noindent Циолковский \par \egroup - -a bit of math - -$\it e=mc^2 \bf e=mc^2 \Uchar"1D49D$ - -$$\left( { {1} \over { {1} \over {x} } } \right) $$ - -$$\sqrt {2} { { {1} \over { {1} \over {x} } } } $$ - -\font\cows=file:koeieletters.afm at 50pt - -\cows Hello World! - -% math test - -\latinmodern - -\def\sqrt{\Uroot "0 "221A{}} - -\def\root#1\of{\Uroot "0 "221A{#1}} - -Inline $\sqrt{x}{1.2}$ math. % same for $\root n of x$ - -$\root3\of x$ - -$\sin{x}$ - -\lucidabright - -\def\sqrt{\Uroot "0 "221A{}} - -\def\root#1\of{\Uroot "0 "221A{#1}} - -Inline $\sqrt{x}{1.2}$ math. % same for $\root n of x$ - -$\root3\of x$ - -$\sin{x}$ - -\bgroup - - % drawback: no features (so use basemode) - - \font\crapa=lmroman12-regular:mode=base;liga=yes;kern=yes; at 12pt - \font\crapb=lmsans12-regular:mode=base;liga=yes;kern=yes; at 30pt - %\font\mine=file:luatex-fonts-demo-vf-4.lua:1=lmroman12-regular;2=lmsans12-regular{0x41-0x5A+0x30-0x39+0x21}; at 12pt - \font\mine=file:luatex-fonts-demo-vf-4.lua:1=\fontid\crapa;2=\fontid\crapb{0x41-0x5A+0x30-0x39+0x21}; at 12pt - - \crapa Test\par - \crapb Test\par - - \mine Zomaar een eindje fiets! En dan weer terug. - -\egroup - -% \font\amiri=file:amiri-regular.ttf:% -% mode=node;analyze=yes;language=dflt;script=arab;ccmp=yes;% -% init=yes;medi=yes;fina=yes;isol=yes;% -% mark=yes;mkmk=yes;kern=yes;curs=yes;% -% liga=yes;dlig=yes;rlig=yes;clig=yes;calt=yes % -% at 32pt - -% \bgroup -% \textdir TRT\amiri بِسْمِ اللَّـهِ الرَّ‌حْمَـٰنِ الرَّ‌حِيمِ -% \egroup - -% assumes csr10.tfm csr10.pfb csr.enc to be present - -% \font\foo=file:luatex-plain-tfm.lua:tfm=csr10;enc=csr;pfb=csr10 at 12pt -% -% \foo áäčďěíĺľňóôŕřšťúýž ff ffi - -% \font\foo=file:csr10.tfm:reencode=csr -% \font\foo=file:csr10.tfm:reencode=csr;bitmap=yes % use map file -% \font\foo=file:csr10.tfm:reencode=auto -% -% \foo áäčďěíĺľňóôŕřšťúýž ff ffi \input tufte\par - -\end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-fil.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-fil.lua index 0f9731a2656..d568a2e1fd0 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-fil.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-fil.lua @@ -6,10 +6,8 @@ if not modules then modules = { } end modules ['util-fil'] = { license = "see context related readme files" } -local byte = string.byte -local char = string.char -local extract = bit32 and bit32.extract -local floor = math.floor +local byte = string.byte +local char = string.char -- Here are a few helpers (the starting point were old ones I used for parsing -- flac files). In Lua 5.3 we can probably do this better. Some code will move @@ -35,7 +33,10 @@ function files.close(f) end function files.size(f) - return f:seek("end") + local current = f:seek() + local size = f:seek("end") + f:seek("set",current) + return size end files.getsize = files.size @@ -83,6 +84,12 @@ function files.readbytes(f,n) return byte(f:read(n),1,n) end +function files.readbytetable(f,n) + -- return { byte(f:read(n),1,n) } + local s = f:read(n or 1) + return { byte(s,1,#s) } -- best use the real length +end + function files.readchar(f) return f:read(1) end @@ -93,22 +100,23 @@ end function files.readinteger1(f) -- one byte local n = byte(f:read(1)) - if n >= 0x80 then - -- return n - 0xFF - 1 + if n >= 0x80 then return n - 0x100 else return n end end -files.readcardinal1 = files.readbyte -- one byte -files.readcardinal = files.readcardinal1 -files.readinteger = files.readinteger1 +files.readcardinal1 = files.readbyte -- one byte +files.readcardinal = files.readcardinal1 +files.readinteger = files.readinteger1 +files.readsignedbyte = files.readinteger1 function files.readcardinal2(f) local a, b = byte(f:read(2),1,2) return 0x100 * a + b end + function files.readcardinal2le(f) local b, a = byte(f:read(2),1,2) return 0x100 * a + b @@ -116,22 +124,19 @@ end function files.readinteger2(f) local a, b = byte(f:read(2),1,2) - local n = 0x100 * a + b - if n >= 0x8000 then - -- return n - 0xFFFF - 1 - return n - 0x10000 + if a >= 0x80 then + return 0x100 * a + b - 0x10000 else - return n + return 0x100 * a + b end end + function files.readinteger2le(f) local b, a = byte(f:read(2),1,2) - local n = 0x100 * a + b - if n >= 0x8000 then - -- return n - 0xFFFF - 1 - return n - 0x10000 + if a >= 0x80 then + return 0x100 * a + b - 0x10000 else - return n + return 0x100 * a + b end end @@ -139,6 +144,7 @@ function files.readcardinal3(f) local a, b, c = byte(f:read(3),1,3) return 0x10000 * a + 0x100 * b + c end + function files.readcardinal3le(f) local c, b, a = byte(f:read(3),1,3) return 0x10000 * a + 0x100 * b + c @@ -146,22 +152,19 @@ end function files.readinteger3(f) local a, b, c = byte(f:read(3),1,3) - local n = 0x10000 * a + 0x100 * b + c - if n >= 0x80000 then - -- return n - 0xFFFFFF - 1 - return n - 0x1000000 + if a >= 0x80 then + return 0x10000 * a + 0x100 * b + c - 0x1000000 else - return n + return 0x10000 * a + 0x100 * b + c end end + function files.readinteger3le(f) local c, b, a = byte(f:read(3),1,3) - local n = 0x10000 * a + 0x100 * b + c - if n >= 0x80000 then - -- return n - 0xFFFFFF - 1 - return n - 0x1000000 + if a >= 0x80 then + return 0x10000 * a + 0x100 * b + c - 0x1000000 else - return n + return 0x10000 * a + 0x100 * b + c end end @@ -169,6 +172,7 @@ function files.readcardinal4(f) local a, b, c, d = byte(f:read(4),1,4) return 0x1000000 * a + 0x10000 * b + 0x100 * c + d end + function files.readcardinal4le(f) local d, c, b, a = byte(f:read(4),1,4) return 0x1000000 * a + 0x10000 * b + 0x100 * c + d @@ -176,48 +180,75 @@ end function files.readinteger4(f) local a, b, c, d = byte(f:read(4),1,4) - local n = 0x1000000 * a + 0x10000 * b + 0x100 * c + d - if n >= 0x8000000 then - -- return n - 0xFFFFFFFF - 1 - return n - 0x100000000 + if a >= 0x80 then + return 0x1000000 * a + 0x10000 * b + 0x100 * c + d - 0x100000000 else - return n + return 0x1000000 * a + 0x10000 * b + 0x100 * c + d end end + function files.readinteger4le(f) local d, c, b, a = byte(f:read(4),1,4) - local n = 0x1000000 * a + 0x10000 * b + 0x100 * c + d - if n >= 0x8000000 then - -- return n - 0xFFFFFFFF - 1 - return n - 0x100000000 + if a >= 0x80 then + return 0x1000000 * a + 0x10000 * b + 0x100 * c + d - 0x100000000 else - return n + return 0x1000000 * a + 0x10000 * b + 0x100 * c + d end end +-- function files.readfixed2(f) +-- local a, b = byte(f:read(2),1,2) +-- if a >= 0x80 then +-- return (0x100 * a + b - 0x10000)/256.0 +-- else +-- return (0x100 * a + b)/256.0 +-- end +-- end + +function files.readfixed2(f) + local a, b = byte(f:read(2),1,2) + if a >= 0x80 then + return (a - 0x100) + b/0x100 + else + return (a ) + b/0x100 + end +end + +-- (real) (n>>16) + ((n&0xffff)/65536.0)) + +-- function files.readfixed4(f) +-- local a, b, c, d = byte(f:read(4),1,4) +-- if a >= 0x80 then +-- return (0x1000000 * a + 0x10000 * b + 0x100 * c + d - 0x100000000)/65536.0 +-- else +-- return (0x1000000 * a + 0x10000 * b + 0x100 * c + d)/65536.0 +-- end +-- end + function files.readfixed4(f) local a, b, c, d = byte(f:read(4),1,4) - local n = 0x100 * a + b - if n >= 0x8000 then - -- return n - 0xFFFF - 1 + (0x100 * c + d)/0xFFFF - return n - 0x10000 + (0x100 * c + d)/0xFFFF + if a >= 0x80 then + return (0x100 * a + b - 0x10000) + (0x100 * c + d)/0x10000 else - return n + (0x100 * c + d)/0xFFFF + return (0x100 * a + b ) + (0x100 * c + d)/0x10000 end end -if extract then +-- (real) ((n<<16)>>(16+14)) + ((n&0x3fff)/16384.0)) + +if bit32 then + + local extract = bit32.extract + local band = bit32.band function files.read2dot14(f) local a, b = byte(f:read(2),1,2) - local n = 0x100 * a + b - local m = extract(n,0,30) - if n > 0x7FFF then - n = extract(n,30,2) - return m/0x4000 - 4 + if a >= 0x80 then + local n = -(0x100 * a + b) + return - (extract(n,14,2) + (band(n,0x3FFF) / 16384.0)) else - n = extract(n,30,2) - return n + m/0x4000 + local n = 0x100 * a + b + return (extract(n,14,2) + (band(n,0x3FFF) / 16384.0)) end end @@ -233,20 +264,37 @@ end -- writers (kind of slow) -function files.writecardinal2(f,n) - local a = char(n % 256) - n = floor(n/256) - local b = char(n % 256) - f:write(b,a) +if bit32 then + + local rshift = bit32.rshift + + function files.writecardinal2(f,n) + local a = char(n % 256) + n = rshift(n,8) + local b = char(n % 256) + f:write(b,a) + end + +else + + local floor = math.floor + + function files.writecardinal2(f,n) + local a = char(n % 256) + n = floor(n/256) + local b = char(n % 256) + f:write(b,a) + end + end function files.writecardinal4(f,n) local a = char(n % 256) - n = floor(n/256) + n = rshift(n,8) local b = char(n % 256) - n = floor(n/256) + n = rshift(n,8) local c = char(n % 256) - n = floor(n/256) + n = rshift(n,8) local d = char(n % 256) f:write(d,c,b,a) end @@ -259,3 +307,76 @@ function files.writebyte(f,b) f:write(char(b)) end +if fio and fio.readcardinal1 then + + files.readcardinal1 = fio.readcardinal1 + files.readcardinal2 = fio.readcardinal2 + files.readcardinal3 = fio.readcardinal3 + files.readcardinal4 = fio.readcardinal4 + files.readinteger1 = fio.readinteger1 + files.readinteger2 = fio.readinteger2 + files.readinteger3 = fio.readinteger3 + files.readinteger4 = fio.readinteger4 + files.readfixed2 = fio.readfixed2 + files.readfixed4 = fio.readfixed4 + files.read2dot14 = fio.read2dot14 + files.setposition = fio.setposition + files.getposition = fio.getposition + + files.readbyte = files.readcardinal1 + files.readsignedbyte = files.readinteger1 + files.readcardinal = files.readcardinal1 + files.readinteger = files.readinteger1 + + local skipposition = fio.skipposition + files.skipposition = skipposition + + files.readbytes = fio.readbytes + files.readbytetable = fio.readbytetable + + function files.skipshort(f,n) + skipposition(f,2*(n or 1)) + end + + function files.skiplong(f,n) + skipposition(f,4*(n or 1)) + end + +end + +if fio and fio.readcardinaltable then + + files.readcardinaltable = fio.readcardinaltable + files.readintegertable = fio.readintegertable + +else + + local readcardinal1 = files.readcardinal1 + local readcardinal2 = files.readcardinal2 + local readcardinal3 = files.readcardinal3 + local readcardinal4 = files.readcardinal4 + + function files.readcardinaltable(f,n,b) + local t = { } + if b == 1 then for i=1,n do t[i] = readcardinal1(f) end + elseif b == 2 then for i=1,n do t[i] = readcardinal2(f) end + elseif b == 3 then for i=1,n do t[i] = readcardinal3(f) end + elseif b == 4 then for i=1,n do t[i] = readcardinal4(f) end end + return t + end + + local readinteger1 = files.readinteger1 + local readinteger2 = files.readinteger2 + local readinteger3 = files.readinteger3 + local readinteger4 = files.readinteger4 + + function files.readintegertable(f,n,b) + local t = { } + if b == 1 then for i=1,n do t[i] = readinteger1(f) end + elseif b == 2 then for i=1,n do t[i] = readinteger2(f) end + elseif b == 3 then for i=1,n do t[i] = readinteger3(f) end + elseif b == 4 then for i=1,n do t[i] = readinteger4(f) end end + return t + end + +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-str.lua b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-str.lua index fb510257a2f..f090bce55cc 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-str.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/fontloader-util-str.lua @@ -12,17 +12,20 @@ local strings = utilities.strings local format, gsub, rep, sub, find = string.format, string.gsub, string.rep, string.sub, string.find local load, dump = load, string.dump -local tonumber, type, tostring = tonumber, type, tostring +local tonumber, type, tostring, next, setmetatable = tonumber, type, tostring, next, setmetatable +local unpack, concat = table.unpack, table.concat local unpack, concat = table.unpack, table.concat local P, V, C, S, R, Ct, Cs, Cp, Carg, Cc = lpeg.P, lpeg.V, lpeg.C, lpeg.S, lpeg.R, lpeg.Ct, lpeg.Cs, lpeg.Cp, lpeg.Carg, lpeg.Cc local patterns, lpegmatch = lpeg.patterns, lpeg.match -local utfchar, utfbyte = utf.char, utf.byte +local utfchar, utfbyte, utflen = utf.char, utf.byte, utf.len + ----- loadstripped = utilities.lua.loadstripped ----- setmetatableindex = table.setmetatableindex local loadstripped = nil +local oldfashioned = LUAVERSION < 5.2 -if _LUAVERSION < 5.2 then +if oldfashioned then loadstripped = function(str,shortcuts) return load(str) @@ -44,21 +47,60 @@ end if not number then number = { } end -- temp hack for luatex-fonts -local stripper = patterns.stripzeros +local stripzero = patterns.stripzero +local stripzeros = patterns.stripzeros local newline = patterns.newline local endofstring = patterns.endofstring +local anything = patterns.anything local whitespace = patterns.whitespace +local space = patterns.space local spacer = patterns.spacer local spaceortab = patterns.spaceortab +local digit = patterns.digit +local sign = patterns.sign +local period = patterns.period + +-- local function points(n) +-- n = tonumber(n) +-- return (not n or n == 0) and "0pt" or lpegmatch(stripzeros,format("%.5fpt",n/65536)) +-- end + +-- local function basepoints(n) +-- n = tonumber(n) +-- return (not n or n == 0) and "0bp" or lpegmatch(stripzeros,format("%.5fbp", n*(7200/7227)/65536)) +-- end + +local ptf = 1 / 65536 +local bpf = (7200/7227) / 65536 local function points(n) + if n == 0 then + return "0pt" + end n = tonumber(n) - return (not n or n == 0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536)) + if not n or n == 0 then + return "0pt" + end + n = n * ptf + if n % 1 == 0 then + return format("%ipt",n) + end + return lpegmatch(stripzeros,format("%.5fpt",n)) -- plural as we need to keep the pt end local function basepoints(n) + if n == 0 then + return "0pt" + end n = tonumber(n) - return (not n or n == 0) and "0bp" or lpegmatch(stripper,format("%.5fbp", n*(7200/7227)/65536)) + if not n or n == 0 then + return "0pt" + end + n = n * bpf + if n % 1 == 0 then + return format("%ibp",n) + end + return lpegmatch(stripzeros,format("%.5fbp",n)) -- plural as we need to keep the pt end number.points = points @@ -69,7 +111,6 @@ number.basepoints = basepoints local rubish = spaceortab^0 * newline local anyrubish = spaceortab + newline -local anything = patterns.anything local stripped = (spaceortab^1 / "") * newline local leading = rubish^0 / "" local trailing = (anyrubish^1 * endofstring) / "" @@ -137,13 +178,26 @@ local pattern = + newline * Cp() / function(position) extra, start = 0, position end - + patterns.anything + + anything )^1) function strings.tabtospace(str,tab) + -- no real gain in first checking if a \t is there return lpegmatch(pattern,str,1,tab or 7) end +function string.utfpadding(s,n) + if not n or n == 0 then + return "" + end + local l = utflen(s) + if n > 0 then + return nspaces[n-l] + else + return nspaces[-n-l] + end +end + -- local t = { -- "1234567123456712345671234567", -- "\tb\tc", @@ -167,27 +221,31 @@ end -- return str -- end -local space = spacer^0 -local nospace = space/"" -local endofline = nospace * newline +local optionalspace = spacer^0 +local nospace = optionalspace/"" +local endofline = nospace * newline -local stripend = (whitespace^1 * endofstring)/"" +local stripend = (whitespace^1 * endofstring)/"" -local normalline = (nospace * ((1-space*(newline+endofstring))^1) * nospace) +local normalline = (nospace * ((1-optionalspace*(newline+endofstring))^1) * nospace) -local stripempty = endofline^1/"" -local normalempty = endofline^1 -local singleempty = endofline * (endofline^0/"") -local doubleempty = endofline * endofline^-1 * (endofline^0/"") +local stripempty = endofline^1/"" +local normalempty = endofline^1 +local singleempty = endofline * (endofline^0/"") +local doubleempty = endofline * endofline^-1 * (endofline^0/"") +local stripstart = stripempty^0 -local stripstart = stripempty^0 +local intospace = whitespace^1/" " +local noleading = whitespace^1/"" +local notrailing = noleading * endofstring -local p_prune_normal = Cs ( stripstart * ( stripend + normalline + normalempty )^0 ) -local p_prune_collapse = Cs ( stripstart * ( stripend + normalline + doubleempty )^0 ) -local p_prune_noempty = Cs ( stripstart * ( stripend + normalline + singleempty )^0 ) -local p_retain_normal = Cs ( ( normalline + normalempty )^0 ) -local p_retain_collapse = Cs ( ( normalline + doubleempty )^0 ) -local p_retain_noempty = Cs ( ( normalline + singleempty )^0 ) +local p_prune_normal = Cs ( stripstart * ( stripend + normalline + normalempty )^0 ) +local p_prune_collapse = Cs ( stripstart * ( stripend + normalline + doubleempty )^0 ) +local p_prune_noempty = Cs ( stripstart * ( stripend + normalline + singleempty )^0 ) +local p_prune_intospace = Cs ( noleading * ( notrailing + intospace + 1 )^0 ) +local p_retain_normal = Cs ( ( normalline + normalempty )^0 ) +local p_retain_collapse = Cs ( ( normalline + doubleempty )^0 ) +local p_retain_noempty = Cs ( ( normalline + singleempty )^0 ) -- function striplines(str,prune,collapse,noempty) -- if prune then @@ -213,10 +271,11 @@ local striplinepatterns = { ["prune"] = p_prune_normal, ["prune and collapse"] = p_prune_collapse, -- default ["prune and no empty"] = p_prune_noempty, + ["prune and to space"] = p_prune_intospace, ["retain"] = p_retain_normal, ["retain and collapse"] = p_retain_collapse, ["retain and no empty"] = p_retain_noempty, - ["collapse"] = patterns.collapser, -- how about: stripper fullstripper + ["collapse"] = patterns.collapser, } setmetatable(striplinepatterns,{ __index = function(t,k) return p_prune_collapse end }) @@ -227,6 +286,10 @@ function strings.striplines(str,how) return str and lpegmatch(striplinepatterns[how],str) or str end +function strings.collapse(str) -- maybe also in strings + return str and lpegmatch(p_prune_intospace,str) or str +end + -- also see: string.collapsespaces strings.striplong = strings.striplines -- for old times sake @@ -242,13 +305,14 @@ strings.striplong = strings.striplines -- for old times sake -- " zus wim jet", -- " ", -- }, "\n") - +-- -- local str = table.concat( { -- " aaaa", -- " bb", -- " cccccc", +-- " ", -- }, "\n") - +-- -- for k, v in table.sortedhash(utilities.strings.striplinepatterns) do -- logs.report("stripper","method: %s, result: [[%s]]",k,utilities.strings.striplines(str,k)) -- end @@ -280,41 +344,49 @@ end -- -- More info can be found in cld-mkiv.pdf so here I stick to a simple list. -- --- integer %...i number --- integer %...d number --- unsigned %...u number --- character %...c number --- hexadecimal %...x number --- HEXADECIMAL %...X number --- octal %...o number --- string %...s string number --- float %...f number --- checked float %...F number --- exponential %...e number --- exponential %...E number --- autofloat %...g number --- autofloat %...G number --- utf character %...c number --- force tostring %...S any --- force tostring %Q any --- force tonumber %N number (strip leading zeros) --- signed number %I number --- rounded number %r number --- 0xhexadecimal %...h character number --- 0xHEXADECIMAL %...H character number --- U+hexadecimal %...u character number --- U+HEXADECIMAL %...U character number --- points %p number (scaled points) --- basepoints %b number (scaled points) --- table concat %...t table --- table concat %{.}t table --- serialize %...T sequenced (no nested tables) --- serialize %{.}T sequenced (no nested tables) --- boolean (logic) %l boolean --- BOOLEAN %L boolean --- whitespace %...w --- automatic %...a 'whatever' (string, table, ...) --- automatic %...A "whatever" (string, table, ...) +-- integer %...i number +-- integer %...d number +-- unsigned %...u number -- not used +-- character %...c number +-- hexadecimal %...x number +-- HEXADECIMAL %...X number +-- octal %...o number +-- string %...s string number +-- float %...f number +-- checked float %...F number +-- exponential %...e number +-- exponential %...E number +-- stripped e %...j number +-- stripped E %...J number +-- autofloat %...g number +-- autofloat %...G number +-- utf character %...c number +-- force tostring %...S any +-- force tostring %Q any +-- force tonumber %N number (strip leading zeros) +-- signed number %I number +-- rounded number %r number +-- 0xhexadecimal %...h character number +-- 0xHEXADECIMAL %...H character number +-- U+hexadecimal %...u character number +-- U+HEXADECIMAL %...U character number +-- points %p number (scaled points) +-- basepoints %b number (scaled points) +-- table concat %...t table +-- table concat %{.}t table +-- serialize %...T sequenced (no nested tables) +-- serialize %{.}T sequenced (no nested tables) +-- boolean (logic) %l boolean +-- BOOLEAN %L boolean +-- whitespace %...w number +-- whitespace %...W (fixed) +-- automatic %...a 'whatever' (string, table, ...) +-- automatic %...A "whatever" (string, table, ...) +-- zap %...z skip +-- stripped %...N %...N +-- comma/period real %...m +-- period/comma real %...M +-- formatted float %...k n.m local n = 0 @@ -387,27 +459,45 @@ end -- maybe to util-num -local digit = patterns.digit -local period = patterns.period -local three = digit * digit * digit +local two = digit * digit +local three = two * digit +local prefix = (Carg(1) * three)^1 local splitter = Cs ( - (((1 - (three^1 * period))^1 + C(three)) * (Carg(1) * three)^1 + C((1-period)^1)) - * (P(1)/"" * Carg(2)) * C(2) + (((1 - (three^1 * period))^1 + C(three)) * prefix + C((1-period)^1)) + * (anything/"" * Carg(2)) * C(2) +) + +local splitter3 = Cs ( + three * prefix * endofstring + + two * prefix * endofstring + + digit * prefix * endofstring + + three + + two + + digit ) patterns.formattednumber = splitter function number.formatted(n,sep1,sep2) - local s = type(s) == "string" and n or format("%0.2f",n) - if sep1 == true then - return lpegmatch(splitter,s,1,".",",") - elseif sep1 == "." then - return lpegmatch(splitter,s,1,sep1,sep2 or ",") - elseif sep1 == "," then - return lpegmatch(splitter,s,1,sep1,sep2 or ".") + if sep1 == false then + if type(n) == "number" then + n = tostring(n) + end + return lpegmatch(splitter3,n,1,sep2 or ".") else - return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") + if type(n) == "number" then + n = format("%0.2f",n) + end + if sep1 == true then + return lpegmatch(splitter,n,1,".",",") + elseif sep1 == "." then + return lpegmatch(splitter,n,1,sep1,sep2 or ",") + elseif sep1 == "," then + return lpegmatch(splitter,n,1,sep1,sep2 or ".") + else + return lpegmatch(splitter,n,1,sep1 or ",",sep2 or ".") + end end end @@ -420,17 +510,45 @@ end -- print(number.formatted(1234567)) -- print(number.formatted(12345678)) -- print(number.formatted(12345678,true)) +-- print(number.formatted(1,false)) +-- print(number.formatted(12,false)) +-- print(number.formatted(123,false)) +-- print(number.formatted(1234,false)) +-- print(number.formatted(12345,false)) +-- print(number.formatted(123456,false)) +-- print(number.formatted(1234567,false)) +-- print(number.formatted(12345678,false)) -- print(number.formatted(1234.56,"!","?")) +local p = Cs( + P("-")^0 + * (P("0")^1/"")^0 + * (1-period)^0 + * (period * P("0")^1 * endofstring/"" + period^0) + * P(1-P("0")^1*endofstring)^0 + ) + +function number.compactfloat(n,fmt) + if n == 0 then + return "0" + elseif n == 1 then + return "1" + end + n = lpegmatch(p,format(fmt or "%0.3f",n)) + if n == "." or n == "" or n == "-" then + return "0" + end + return n +end + local zero = P("0")^1 / "" local plus = P("+") / "" local minus = P("-") -local separator = S(".") -local digit = R("09") +local separator = period local trailing = zero^1 * #S("eE") -local exponent = (S("eE") * (plus + Cs((minus * zero^0 * P(-1))/"") + minus) * zero^0 * (P(-1) * Cc("0") + P(1)^1)) +local exponent = (S("eE") * (plus + Cs((minus * zero^0 * endofstring)/"") + minus) * zero^0 * (endofstring * Cc("0") + anything^1)) local pattern_a = Cs(minus^0 * digit^1 * (separator/"" * trailing + separator * (trailing + digit)^0) * exponent) -local pattern_b = Cs((exponent + P(1))^0) +local pattern_b = Cs((exponent + anything)^0) function number.sparseexponent(f,n) if not n then @@ -449,6 +567,31 @@ function number.sparseexponent(f,n) return tostring(n) end +local hf = { } +local hs = { } + +setmetatable(hf, { __index = function(t,k) + local v = "%." .. k .. "f" + t[k] = v + return v +end } ) + +setmetatable(hs, { __index = function(t,k) + local v = "%" .. k .. "s" + t[k] = v + return v +end } ) + +function number.formattedfloat(n,b,a) + local s = format(hf[a],n) + local l = (b or 0) + (a or 0) + 1 + if #s < l then + return format(hs[l],s) + else + return s + end +end + local template = [[ %s %s @@ -457,7 +600,7 @@ return function(%s) return %s end local preamble, environment = "", { } -if _LUAVERSION < 5.2 then +if oldfashioned then preamble = [[ local lpeg=lpeg @@ -473,12 +616,16 @@ local utfchar=utf.char local utfbyte=utf.byte local lpegmatch=lpeg.match local nspaces=string.nspaces +local utfpadding=string.utfpadding local tracedchar=string.tracedchar local autosingle=string.autosingle local autodouble=string.autodouble local sequenced=table.sequenced local formattednumber=number.formatted local sparseexponent=number.sparseexponent +local formattedfloat=number.formattedfloat +local stripzero=lpeg.patterns.stripzero +local stripzeros=lpeg.patterns.stripzeros ]] else @@ -498,12 +645,16 @@ else utfbyte = utf.byte, lpegmatch = lpeg.match, nspaces = string.nspaces, + utfpadding = string.utfpadding, tracedchar = string.tracedchar, autosingle = string.autosingle, autodouble = string.autodouble, sequenced = table.sequenced, formattednumber = number.formatted, sparseexponent = number.sparseexponent, + formattedfloat = number.formattedfloat, + stripzero = lpeg.patterns.stripzero, + stripzeros = lpeg.patterns.stripzeros, } end @@ -520,7 +671,10 @@ setmetatable(arguments, { __index = end }) -local prefix_any = C((S("+- .") + R("09"))^0) +local prefix_any = C((sign + space + period + digit)^0) +local prefix_sub = (C((sign + digit)^0) + Cc(0)) + * period + * (C((sign + digit)^0) + Cc(0)) local prefix_tab = P("{") * C((1-P("}"))^0) * P("}") + C((1-R("az","AZ","09","%%"))^0) -- we've split all cases as then we can optimize them (let's omit the fuzzy u) @@ -545,9 +699,36 @@ local format_S = function(f) -- can be optimized end end +local format_right = function(f) + n = n + 1 + f = tonumber(f) + if not f or f == 0 then + return format("(a%s or '')",n) + elseif f > 0 then + return format("utfpadding(a%s,%i)..a%s",n,f,n) + else + return format("a%s..utfpadding(a%s,%i)",n,n,f) + end +end + +local format_left = function(f) + n = n + 1 + f = tonumber(f) + if not f or f == 0 then + return format("(a%s or '')",n) + end + if f < 0 then + return format("utfpadding(a%s,%i)..a%s",n,-f,n) + else + return format("a%s..utfpadding(a%s,%i)",n,n,-f) + end +end + local format_q = function() n = n + 1 - return format("(a%s and format('%%q',a%s) or '')",n,n) -- goodie: nil check (maybe separate lpeg, not faster) + -- lua 5.3 has a different q than lua 5.2 (which does a tostring on numbers) + -- return format("(a%s ~= nil and format('%%q',a%s) or '')",n,n) + return format("(a%s ~= nil and format('%%q',tostring(a%s)) or '')",n,n) end local format_Q = function() -- can be optimized @@ -594,6 +775,11 @@ local format_F = function(f) -- beware, no cast to number end end +local format_k = function(b,a) -- slow + n = n + 1 + return format("formattedfloat(a%s,%i,%i)",n,b or 0, a or 0) +end + local format_g = function(f) n = n + 1 return format("format('%%%sg',a%s)",f,n) @@ -732,9 +918,43 @@ local format_L = function() return format("(a%s and 'TRUE' or 'FALSE')",n) end -local format_N = function() -- strips leading zeros +local format_n = function() -- strips leading and trailing zeros and removes .0 + n = n + 1 + return format("((a%s %% 1 == 0) and format('%%i',a%s) or tostring(a%s))",n,n,n) +end + +-- local format_N = function() -- strips leading and trailing zeros (also accepts string) +-- n = n + 1 +-- return format("tostring(tonumber(a%s) or a%s)",n,n) +-- end + +-- local format_N = function(f) -- strips leading and trailing zeros +-- n = n + 1 +-- -- stripzero (singular) as we only have a number +-- if not f or f == "" then +-- return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or ((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%.9f',a%s)))",n,n,n,n,n) +-- else +-- return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%%sf',a%s)))",n,n,f,n) +-- end +-- end + +-- local format_N = function(f) -- strips leading and trailing zeros +-- n = n + 1 +-- -- stripzero (singular) as we only have a number +-- if not f or f == "" then +-- return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or ((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or lpegmatch(stripzero,format('%%.9f',a%s)))",n,n,n,n,n) +-- else +-- return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%%sf',a%s)))",n,n,f,n) +-- end +-- end + +local format_N = function(f) -- strips leading and trailing zeros n = n + 1 - return format("tostring(tonumber(a%s) or a%s)",n,n) + -- stripzero (singular) as we only have a number + if not f or f == "" then + f = ".9" + end -- always a leading number ! + return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%%sf',a%s)))",n,n,f,n) end local format_a = function(f) @@ -774,7 +994,11 @@ local format_m = function(f) if not f or f == "" then f = "," end - return format([[formattednumber(a%s,%q,".")]],n,f) + if f == "0" then + return format([[formattednumber(a%s,false)]],n) + else + return format([[formattednumber(a%s,%q,".")]],n,f) + end end local format_M = function(f) @@ -782,7 +1006,11 @@ local format_M = function(f) if not f or f == "" then f = "." end - return format([[formattednumber(a%s,%q,",")]],n,f) + if f == "0" then + return format([[formattednumber(a%s,false)]],n) + else + return format([[formattednumber(a%s,%q,",")]],n,f) + end end -- @@ -794,6 +1022,22 @@ end -- +-- local strip +-- +-- local format_Z = function(f) +-- n = n + 1 +-- if not f or f == "" then +-- f = ".9" +-- end +-- return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or (strip and lpegmatch(stripzero,format('%%%sf',a%s))) or format('%%%sf',a%s))",n,n,f,n,f,n) +-- end +-- +-- function strings.stripformatterzeros() +-- strip = true +-- end + +-- + local format_rest = function(s) return format("%q",s) -- catches " and \n and such end @@ -854,7 +1098,9 @@ local builder = Cs { "start", + V("C") + V("S") -- new + V("Q") -- new + + V("n") -- new + V("N") -- new + + V("k") -- new -- + V("r") + V("h") + V("H") + V("u") + V("U") @@ -870,11 +1116,14 @@ local builder = Cs { "start", + V("m") + V("M") -- new (formatted number) + V("z") -- new -- + + V(">") -- left padding + + V("<") -- right padding + -- -- + V("?") -- ignored, probably messed up % ) + V("*") ) - * (P(-1) + Carg(1)) + * (endofstring + Carg(1)) )^0, -- ["s"] = (prefix_any * P("s")) / format_s, -- %s => regular %s (string) @@ -892,8 +1141,10 @@ local builder = Cs { "start", ["o"] = (prefix_any * P("o")) / format_o, -- %o => regular %o (octal) -- ["S"] = (prefix_any * P("S")) / format_S, -- %S => %s (tostring) - ["Q"] = (prefix_any * P("Q")) / format_S, -- %Q => %q (tostring) - ["N"] = (prefix_any * P("N")) / format_N, -- %N => tonumber (strips leading zeros) + ["Q"] = (prefix_any * P("Q")) / format_Q, -- %Q => %q (tostring) + ["n"] = (prefix_any * P("n")) / format_n, -- %n => tonumber (strips leading and trailing zeros, as well as .0, expects number) + ["N"] = (prefix_any * P("N")) / format_N, -- %N => tonumber (strips leading and trailing zeros, also takes string) + ["k"] = (prefix_sub * P("k")) / format_k, -- %k => like f but with n.m ["c"] = (prefix_any * P("c")) / format_c, -- %c => utf character (extension to regular) ["C"] = (prefix_any * P("C")) / format_C, -- %c => U+.... utf character -- @@ -916,48 +1167,49 @@ local builder = Cs { "start", ["j"] = (prefix_any * P("j")) / format_j, -- %j => %e (float) stripped exponent (irrational) ["J"] = (prefix_any * P("J")) / format_J, -- %J => %E (float) stripped exponent (irrational) -- - ["m"] = (prefix_tab * P("m")) / format_m, -- %m => xxx.xxx.xxx,xx (optional prefix instead of .) - ["M"] = (prefix_tab * P("M")) / format_M, -- %M => xxx,xxx,xxx.xx (optional prefix instead of ,) + ["m"] = (prefix_any * P("m")) / format_m, -- %m => xxx.xxx.xxx,xx (optional prefix instead of .) + ["M"] = (prefix_any * P("M")) / format_M, -- %M => xxx,xxx,xxx.xx (optional prefix instead of ,) -- - ["z"] = (prefix_any * P("z")) / format_z, -- %M => xxx,xxx,xxx.xx (optional prefix instead of ,) + ["z"] = (prefix_any * P("z")) / format_z, -- %z => skip n arguments + -- ["Z"] = (prefix_any * P("Z")) / format_Z, -- %Z => optionally strip zeros -- ["a"] = (prefix_any * P("a")) / format_a, -- %a => '...' (forces tostring) ["A"] = (prefix_any * P("A")) / format_A, -- %A => "..." (forces tostring) -- + ["<"] = (prefix_any * P("<")) / format_left, + [">"] = (prefix_any * P(">")) / format_right, + -- ["*"] = Cs(((1-P("%"))^1 + P("%%")/"%%")^1) / format_rest, -- rest (including %%) ["?"] = Cs(((1-P("%"))^1 )^1) / format_rest, -- rest (including %%) -- ["!"] = Carg(2) * prefix_any * P("!") * C((1-P("!"))^1) * P("!") / format_extension, } --- we can be clever and only alias what is needed - --- local direct = Cs ( --- P("%")/"" --- * Cc([[local format = string.format return function(str) return format("%]]) --- * (S("+- .") + R("09"))^0 --- * S("sqidfgGeExXo") --- * Cc([[",str) end]]) --- * P(-1) --- ) - -local direct = Cs ( - P("%") - * (S("+- .") + R("09"))^0 - * S("sqidfgGeExXo") - * P(-1) / [[local format = string.format return function(str) return format("%0",str) end]] -) +-- We can be clever and only alias what is needed: + +local xx = setmetatable({ }, { __index = function(t,k) local v = format("%02x",k) t[k] = v return v end }) +local XX = setmetatable({ }, { __index = function(t,k) local v = format("%02X",k) t[k] = v return v end }) + +local preset = { + ["%02x"] = function(n) return xx[n] end, + ["%02X"] = function(n) return XX[n] end, +} + +local direct = + P("%") * (sign + space + period + digit)^0 * S("sqidfgGeExXo") * endofstring + / [[local format = string.format return function(str) return format("%0",str) end]] local function make(t,str) - local f - local p + local f = preset[str] + if f then + return f + end local p = lpegmatch(direct,str) if p then - -- f = loadstripped(p)() -- print("builder 1 >",p) f = loadstripped(p)() else - n = 0 + n = 0 -- used in patterns -- p = lpegmatch(builder,str,1,"..",t._extensions_) -- after this we know n p = lpegmatch(builder,str,1,t._connector_,t._extensions_) -- after this we know n if n > 0 then @@ -968,6 +1220,7 @@ local function make(t,str) f = function() return str end end end + -- if jit then jit.on(f,true) end t[str] = f return f end @@ -1020,7 +1273,7 @@ strings.formatters = { } -- _connector_ is an experiment -if _LUAVERSION < 5.2 then +if oldfashioned then function strings.formatters.new(noconcat) local t = { _type_ = "formatter", _connector_ = noconcat and "," or "..", _extensions_ = { }, _preamble_ = preamble, _environment_ = { } } @@ -1072,19 +1325,15 @@ strings.formatters.add = add -- registered in the default instance (should we fall back on this one?) -patterns.xmlescape = Cs((P("<")/"<" + P(">")/">" + P("&")/"&" + P('"')/""" + P(1))^0) -patterns.texescape = Cs((C(S("#$%\\{}"))/"\\%1" + P(1))^0) +patterns.xmlescape = Cs((P("<")/"<" + P(">")/">" + P("&")/"&" + P('"')/""" + anything)^0) +patterns.texescape = Cs((C(S("#$%\\{}"))/"\\%1" + anything)^0) patterns.luaescape = Cs(((1-S('"\n'))^1 + P('"')/'\\"' + P('\n')/'\\n"')^0) -- maybe also \0 patterns.luaquoted = Cs(Cc('"') * ((1-S('"\n'))^1 + P('"')/'\\"' + P('\n')/'\\n"')^0 * Cc('"')) -- escaping by lpeg is faster for strings without quotes, slower on a string with quotes, but -- faster again when other q-escapables are found (the ones we don't need to escape) --- add(formatters,"xml", [[lpegmatch(xmlescape,%s)]],[[local xmlescape = lpeg.patterns.xmlescape]]) --- add(formatters,"tex", [[lpegmatch(texescape,%s)]],[[local texescape = lpeg.patterns.texescape]]) --- add(formatters,"lua", [[lpegmatch(luaescape,%s)]],[[local luaescape = lpeg.patterns.luaescape]]) - -if _LUAVERSION < 5.2 then +if oldfashioned then add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape") add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape") @@ -1133,7 +1382,6 @@ end local dquote = patterns.dquote -- P('"') local equote = patterns.escaped + dquote / '\\"' + 1 -local space = patterns.space local cquote = Cc('"') local pattern = @@ -1149,3 +1397,21 @@ local pattern = Cs((newline / (os.newline or "\r") + 1)^0) function string.replacenewlines(str) return lpegmatch(pattern,str) end + +-- + +function strings.newcollector() + local result, r = { }, 0 + return + function(fmt,str,...) -- write + r = r + 1 + result[r] = str == nil and fmt or formatters[fmt](str,...) + end, + function(connector) -- flush + if result then + local str = concat(result,connector) + result, r = { }, 0 + return str + end + end +end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua index 395f9b5b519..03027e760e3 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua @@ -2,7 +2,7 @@ ----------------------------------------------------------------------- -- FILE: luaotfload-auxiliary.lua -- DESCRIPTION: part of luaotfload --- REQUIREMENTS: luaotfload 2.8 +-- REQUIREMENTS: luaotfload 2.9 -- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang ----------------------------------------------------------------------- -- diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-characters.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-characters.lua index 258a5aee649..552759811dd 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-characters.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-characters.lua @@ -5475,6 +5475,10 @@ return { category="po", direction="l", }, + [0x560]={ + category="ll", + direction="l", + }, [0x561]={ category="ll", direction="l", @@ -5631,6 +5635,10 @@ return { category="ll", direction="l", }, + [0x588]={ + category="ll", + direction="l", + }, [0x589]={ category="po", direction="l", @@ -5979,6 +5987,10 @@ return { category="lo", direction="r", }, + [0x5EF]={ + category="lo", + direction="r", + }, [0x5F0]={ category="lo", direction="r", @@ -7955,6 +7967,18 @@ return { category="lm", direction="r", }, + [0x7FD]={ + category="mn", + direction="nsm", + }, + [0x7FE]={ + category="sc", + direction="r", + }, + [0x7FF]={ + category="sc", + direction="r", + }, [0x800]={ category="lo", direction="r", @@ -8315,6 +8339,50 @@ return { category="po", direction="r", }, + [0x860]={ + category="lo", + direction="al", + }, + [0x861]={ + category="lo", + direction="al", + }, + [0x862]={ + category="lo", + direction="al", + }, + [0x863]={ + category="lo", + direction="al", + }, + [0x864]={ + category="lo", + direction="al", + }, + [0x865]={ + category="lo", + direction="al", + }, + [0x866]={ + category="lo", + direction="al", + }, + [0x867]={ + category="lo", + direction="al", + }, + [0x868]={ + category="lo", + direction="al", + }, + [0x869]={ + category="lo", + direction="al", + }, + [0x86A]={ + category="lo", + direction="al", + }, [0x8A0]={ category="lo", direction="al", @@ -8431,6 +8499,10 @@ return { category="lo", direction="al", }, + [0x8D3]={ + category="mn", + direction="nsm", + }, [0x8D4]={ category="mn", direction="nsm", @@ -9491,6 +9563,18 @@ return { category="sc", direction="et", }, + [0x9FC]={ + category="lo", + direction="l", + }, + [0x9FD]={ + category="po", + direction="l", + }, + [0x9FE]={ + category="mn", + direction="nsm", + }, [0xA01]={ category="mn", direction="nsm", @@ -9807,6 +9891,10 @@ return { category="mn", direction="nsm", }, + [0xA76]={ + category="po", + direction="l", + }, [0xA81]={ category="mn", direction="nsm", @@ -10147,6 +10235,30 @@ return { category="lo", direction="l", }, + [0xAFA]={ + category="mn", + direction="nsm", + }, + [0xAFB]={ + category="mn", + direction="nsm", + }, + [0xAFC]={ + category="mn", + direction="nsm", + }, + [0xAFD]={ + category="mn", + direction="nsm", + }, + [0xAFE]={ + category="mn", + direction="nsm", + }, + [0xAFF]={ + category="mn", + direction="nsm", + }, [0xB01]={ category="mn", direction="nsm", @@ -10811,6 +10923,10 @@ return { category="mc", direction="l", }, + [0xC04]={ + category="mn", + direction="nsm", + }, [0xC05]={ category="lo", direction="l", @@ -11195,6 +11311,10 @@ return { category="mc", direction="l", }, + [0xC84]={ + category="po", + direction="l", + }, [0xC85]={ category="lo", direction="l", @@ -11531,6 +11651,10 @@ return { category="so", direction="l", }, + [0xD00]={ + category="mn", + direction="nsm", + }, [0xD01]={ category="mn", direction="nsm", @@ -11751,6 +11875,14 @@ return { category="lo", direction="l", }, + [0xD3B]={ + category="mn", + direction="nsm", + }, + [0xD3C]={ + category="mn", + direction="nsm", + }, [0xD3D]={ category="lo", direction="l", @@ -21997,6 +22129,10 @@ return { category="lo", direction="l", }, + [0x1878]={ + category="lo", + direction="l", + }, [0x1880]={ category="lo", direction="l", @@ -25497,6 +25633,190 @@ return { category="ll", direction="l", }, + [0x1C90]={ + category="lu", + direction="l", + }, + [0x1C91]={ + category="lu", + direction="l", + }, + [0x1C92]={ + category="lu", + direction="l", + }, + [0x1C93]={ + category="lu", + direction="l", + }, + [0x1C94]={ + category="lu", + direction="l", + }, + [0x1C95]={ + category="lu", + direction="l", + }, + [0x1C96]={ + category="lu", + direction="l", + }, + [0x1C97]={ + category="lu", + direction="l", + }, + [0x1C98]={ + category="lu", + direction="l", + }, + [0x1C99]={ + category="lu", + direction="l", + }, + [0x1C9A]={ + category="lu", + direction="l", + }, + [0x1C9B]={ + category="lu", + direction="l", + }, + [0x1C9C]={ + category="lu", + direction="l", + }, + [0x1C9D]={ + category="lu", + direction="l", + }, + [0x1C9E]={ + category="lu", + direction="l", + }, + [0x1C9F]={ + category="lu", + direction="l", + }, + [0x1CA0]={ + category="lu", + direction="l", + }, + [0x1CA1]={ + category="lu", + direction="l", + }, + [0x1CA2]={ + category="lu", + direction="l", + }, + [0x1CA3]={ + category="lu", + direction="l", + }, + [0x1CA4]={ + category="lu", + direction="l", + }, + [0x1CA5]={ + category="lu", + direction="l", + }, + [0x1CA6]={ + category="lu", + direction="l", + }, + [0x1CA7]={ + category="lu", + direction="l", + }, + [0x1CA8]={ + category="lu", + direction="l", + }, + [0x1CA9]={ + category="lu", + direction="l", + }, + [0x1CAA]={ + category="lu", + direction="l", + }, + [0x1CAB]={ + category="lu", + direction="l", + }, + [0x1CAC]={ + category="lu", + direction="l", + }, + [0x1CAD]={ + category="lu", + direction="l", + }, + [0x1CAE]={ + category="lu", + direction="l", + }, + [0x1CAF]={ + category="lu", + direction="l", + }, + [0x1CB0]={ + category="lu", + direction="l", + }, + [0x1CB1]={ + category="lu", + direction="l", + }, + [0x1CB2]={ + category="lu", + direction="l", + }, + [0x1CB3]={ + category="lu", + direction="l", + }, + [0x1CB4]={ + category="lu", + direction="l", + }, + [0x1CB5]={ + category="lu", + direction="l", + }, + [0x1CB6]={ + category="lu", + direction="l", + }, + [0x1CB7]={ + category="lu", + direction="l", + }, + [0x1CB8]={ + category="lu", + direction="l", + }, + [0x1CB9]={ + category="lu", + direction="l", + }, + [0x1CBA]={ + category="lu", + direction="l", + }, + [0x1CBD]={ + category="lu", + direction="l", + }, + [0x1CBE]={ + category="lu", + direction="l", + }, + [0x1CBF]={ + category="lu", + direction="l", + }, [0x1CC0]={ category="po", direction="l", @@ -25685,6 +26005,10 @@ return { category="lo", direction="l", }, + [0x1CF7]={ + category="mc", + direction="l", + }, [0x1CF8]={ category="mn", direction="nsm", @@ -26677,6 +27001,22 @@ return { category="mn", direction="nsm", }, + [0x1DF6]={ + category="mn", + direction="nsm", + }, + [0x1DF7]={ + category="mn", + direction="nsm", + }, + [0x1DF8]={ + category="mn", + direction="nsm", + }, + [0x1DF9]={ + category="mn", + direction="nsm", + }, [0x1DFB]={ category="mn", direction="nsm", @@ -29403,6 +29743,10 @@ return { category="sc", direction="et", }, + [0x20BF]={ + category="sc", + direction="et", + }, [0x20D0]={ category="mn", direction="nsm", @@ -30677,18 +31021,22 @@ return { [0x221F]={ category="sm", direction="on", + mirror=0x2BFE, }, [0x2220]={ category="sm", direction="on", + mirror=0x29A3, }, [0x2221]={ category="sm", direction="on", + mirror=0x299B, }, [0x2222]={ category="sm", direction="on", + mirror=0x29A0, }, [0x2223]={ category="sm", @@ -30697,6 +31045,7 @@ return { [0x2224]={ category="sm", direction="on", + mirror=0x2AEE, }, [0x2225]={ category="sm", @@ -30832,6 +31181,7 @@ return { [0x2245]={ category="sm", direction="on", + mirror=0x224C, }, [0x2246]={ category="sm", @@ -30860,6 +31210,7 @@ return { [0x224C]={ category="sm", direction="on", + mirror=0x2245, }, [0x224D]={ category="sm", @@ -31353,6 +31704,7 @@ return { [0x22B8]={ category="sm", direction="on", + mirror=0x27DC, }, [0x22B9]={ category="sm", @@ -32707,6 +33059,10 @@ return { category="so", direction="on", }, + [0x23FF]={ + category="so", + direction="on", + }, [0x2400]={ category="so", direction="on", @@ -36502,6 +36858,7 @@ return { [0x27DC]={ category="sm", direction="on", + mirror=0x22B8, }, [0x27DD]={ category="sm", @@ -38330,6 +38687,7 @@ return { [0x299B]={ category="sm", direction="on", + mirror=0x2221, }, [0x299C]={ category="sm", @@ -38350,6 +38708,7 @@ return { [0x29A0]={ category="sm", direction="on", + mirror=0x2222, }, [0x29A1]={ category="sm", @@ -38362,14 +38721,17 @@ return { [0x29A3]={ category="sm", direction="on", + mirror=0x2220, }, [0x29A4]={ category="sm", direction="on", + mirror=0x29A5, }, [0x29A5]={ category="sm", direction="on", + mirror=0x29A4, }, [0x29A6]={ category="sm", @@ -38382,34 +38744,42 @@ return { [0x29A8]={ category="sm", direction="on", + mirror=0x29A9, }, [0x29A9]={ category="sm", direction="on", + mirror=0x29A8, }, [0x29AA]={ category="sm", direction="on", + mirror=0x29AB, }, [0x29AB]={ category="sm", direction="on", + mirror=0x29AA, }, [0x29AC]={ category="sm", direction="on", + mirror=0x29AD, }, [0x29AD]={ category="sm", direction="on", + mirror=0x29AC, }, [0x29AE]={ category="sm", direction="on", + mirror=0x29AF, }, [0x29AF]={ category="sm", direction="on", + mirror=0x29AE, }, [0x29B0]={ category="sm", @@ -38653,10 +39023,12 @@ return { [0x29E8]={ category="sm", direction="on", + mirror=0x29E9, }, [0x29E9]={ category="sm", direction="on", + mirror=0x29E8, }, [0x29EA]={ category="sm", @@ -39258,10 +39630,12 @@ return { [0x2A7B]={ category="sm", direction="on", + mirror=0x2A7C, }, [0x2A7C]={ category="sm", direction="on", + mirror=0x2A7B, }, [0x2A7D]={ category="sm", @@ -39306,26 +39680,32 @@ return { [0x2A85]={ category="sm", direction="on", + mirror=0x2A86, }, [0x2A86]={ category="sm", direction="on", + mirror=0x2A85, }, [0x2A87]={ category="sm", direction="on", + mirror=0x2A88, }, [0x2A88]={ category="sm", direction="on", + mirror=0x2A87, }, [0x2A89]={ category="sm", direction="on", + mirror=0x2A8A, }, [0x2A8A]={ category="sm", direction="on", + mirror=0x2A89, }, [0x2A8B]={ category="sm", @@ -39340,18 +39720,22 @@ return { [0x2A8D]={ category="sm", direction="on", + mirror=0x2A8E, }, [0x2A8E]={ category="sm", direction="on", + mirror=0x2A8D, }, [0x2A8F]={ category="sm", direction="on", + mirror=0x2A90, }, [0x2A90]={ category="sm", direction="on", + mirror=0x2A8F, }, [0x2A91]={ category="sm", @@ -39416,18 +39800,22 @@ return { [0x2A9D]={ category="sm", direction="on", + mirror=0x2A9E, }, [0x2A9E]={ category="sm", direction="on", + mirror=0x2A9D, }, [0x2A9F]={ category="sm", direction="on", + mirror=0x2AA0, }, [0x2AA0]={ category="sm", direction="on", + mirror=0x2A9F, }, [0x2AA1]={ category="sm", @@ -39508,10 +39896,12 @@ return { [0x2AB1]={ category="sm", direction="on", + mirror=0x2AB2, }, [0x2AB2]={ category="sm", direction="on", + mirror=0x2AB1, }, [0x2AB3]={ category="sm", @@ -39526,26 +39916,32 @@ return { [0x2AB5]={ category="sm", direction="on", + mirror=0x2AB6, }, [0x2AB6]={ category="sm", direction="on", + mirror=0x2AB5, }, [0x2AB7]={ category="sm", direction="on", + mirror=0x2AB8, }, [0x2AB8]={ category="sm", direction="on", + mirror=0x2AB7, }, [0x2AB9]={ category="sm", direction="on", + mirror=0x2ABA, }, [0x2ABA]={ category="sm", direction="on", + mirror=0x2AB9, }, [0x2ABB]={ category="sm", @@ -39610,26 +40006,32 @@ return { [0x2AC7]={ category="sm", direction="on", + mirror=0x2AC8, }, [0x2AC8]={ category="sm", direction="on", + mirror=0x2AC7, }, [0x2AC9]={ category="sm", direction="on", + mirror=0x2ACA, }, [0x2ACA]={ category="sm", direction="on", + mirror=0x2AC9, }, [0x2ACB]={ category="sm", direction="on", + mirror=0x2ACC, }, [0x2ACC]={ category="sm", direction="on", + mirror=0x2ACB, }, [0x2ACD]={ category="sm", @@ -39782,6 +40184,7 @@ return { [0x2AEE]={ category="sm", direction="on", + mirror=0x2224, }, [0x2AEF]={ category="sm", @@ -40583,6 +40986,18 @@ return { category="so", direction="on", }, + [0x2BBA]={ + category="so", + direction="on", + }, + [0x2BBB]={ + category="so", + direction="on", + }, + [0x2BBC]={ + category="so", + direction="on", + }, [0x2BBD]={ category="so", direction="on", @@ -40663,6 +41078,110 @@ return { category="so", direction="on", }, + [0x2BD2]={ + category="so", + direction="on", + }, + [0x2BD3]={ + category="so", + direction="on", + }, + [0x2BD4]={ + category="so", + direction="on", + }, + [0x2BD5]={ + category="so", + direction="on", + }, + [0x2BD6]={ + category="so", + direction="on", + }, + [0x2BD7]={ + category="so", + direction="on", + }, + [0x2BD8]={ + category="so", + direction="on", + }, + [0x2BD9]={ + category="so", + direction="on", + }, + [0x2BDA]={ + category="so", + direction="on", + }, + [0x2BDB]={ + category="so", + direction="on", + }, + [0x2BDC]={ + category="so", + direction="on", + }, + [0x2BDD]={ + category="so", + direction="on", + }, + [0x2BDE]={ + category="so", + direction="on", + }, + [0x2BDF]={ + category="so", + direction="on", + }, + [0x2BE0]={ + category="so", + direction="on", + }, + [0x2BE1]={ + category="so", + direction="on", + }, + [0x2BE2]={ + category="so", + direction="on", + }, + [0x2BE3]={ + category="so", + direction="on", + }, + [0x2BE4]={ + category="so", + direction="on", + }, + [0x2BE5]={ + category="so", + direction="on", + }, + [0x2BE6]={ + category="so", + direction="on", + }, + [0x2BE7]={ + category="so", + direction="on", + }, + [0x2BE8]={ + category="so", + direction="on", + }, + [0x2BE9]={ + category="so", + direction="on", + }, + [0x2BEA]={ + category="so", + direction="on", + }, + [0x2BEB]={ + category="so", + direction="on", + }, [0x2BEC]={ category="so", direction="on", @@ -40679,6 +41198,67 @@ return { category="so", direction="on", }, + [0x2BF0]={ + category="so", + direction="on", + }, + [0x2BF1]={ + category="so", + direction="on", + }, + [0x2BF2]={ + category="so", + direction="on", + }, + [0x2BF3]={ + category="so", + direction="on", + }, + [0x2BF4]={ + category="so", + direction="on", + }, + [0x2BF5]={ + category="so", + direction="on", + }, + [0x2BF6]={ + category="so", + direction="on", + }, + [0x2BF7]={ + category="so", + direction="on", + }, + [0x2BF8]={ + category="so", + direction="on", + }, + [0x2BF9]={ + category="so", + direction="on", + }, + [0x2BFA]={ + category="so", + direction="on", + }, + [0x2BFB]={ + category="so", + direction="on", + }, + [0x2BFC]={ + category="so", + direction="on", + }, + [0x2BFD]={ + category="so", + direction="on", + }, + [0x2BFE]={ + category="so", + direction="on", + mirror=0x221F, + }, [0x2C00]={ category="lu", direction="l", @@ -42811,6 +43391,46 @@ return { category="po", direction="on", }, + [0x2E45]={ + category="po", + direction="on", + }, + [0x2E46]={ + category="po", + direction="on", + }, + [0x2E47]={ + category="po", + direction="on", + }, + [0x2E48]={ + category="po", + direction="on", + }, + [0x2E49]={ + category="po", + direction="on", + }, + [0x2E4A]={ + category="po", + direction="on", + }, + [0x2E4B]={ + category="po", + direction="on", + }, + [0x2E4C]={ + category="po", + direction="on", + }, + [0x2E4D]={ + category="po", + direction="on", + }, + [0x2E4E]={ + category="po", + direction="on", + }, [0x2E80]={ category="so", direction="on", @@ -45369,6 +45989,14 @@ return { category="lo", direction="l", }, + [0x312E]={ + category="lo", + direction="l", + }, + [0x312F]={ + category="lo", + direction="l", + }, [0x3131]={ category="lo", direction="l", @@ -56129,6 +56757,10 @@ return { category="lu", direction="l", }, + [0xA7AF]={ + category="ll", + direction="l", + }, [0xA7B0]={ category="lu", direction="l", @@ -56161,6 +56793,14 @@ return { category="ll", direction="l", }, + [0xA7B8]={ + category="lu", + direction="l", + }, + [0xA7B9]={ + category="ll", + direction="l", + }, [0xA7F7]={ category="lo", direction="l", @@ -57085,6 +57725,14 @@ return { category="lo", direction="l", }, + [0xA8FE]={ + category="lo", + direction="l", + }, + [0xA8FF]={ + category="mn", + direction="nsm", + }, [0xA900]={ category="nd", direction="l", @@ -64641,6 +65289,7 @@ return { category="so", direction="on", }, + [0xFE00]={}, [0xFE10]={ category="po", direction="on", @@ -68693,6 +69342,18 @@ return { category="no", direction="l", }, + [0x1032D]={ + category="lo", + direction="l", + }, + [0x1032E]={ + category="lo", + direction="l", + }, + [0x1032F]={ + category="lo", + direction="l", + }, [0x10330]={ category="lo", direction="l", @@ -73617,6 +74278,14 @@ return { category="lo", direction="r", }, + [0x10A34]={ + category="lo", + direction="r", + }, + [0x10A35]={ + category="lo", + direction="r", + }, [0x10A38]={ category="mn", direction="nsm", @@ -73665,6 +74334,10 @@ return { category="no", direction="r", }, + [0x10A48]={ + category="no", + direction="r", + }, [0x10A50]={ category="po", direction="r", @@ -75473,6 +76146,206 @@ return { category="no", direction="r", }, + [0x10D00]={ + category="lo", + direction="al", + }, + [0x10D01]={ + category="lo", + direction="al", + }, + [0x10D02]={ + category="lo", + direction="al", + }, + [0x10D03]={ + category="lo", + direction="al", + }, + [0x10D04]={ + category="lo", + direction="al", + }, + [0x10D05]={ + category="lo", + direction="al", + }, + [0x10D06]={ + category="lo", + direction="al", + }, + [0x10D07]={ + category="lo", + direction="al", + }, + [0x10D08]={ + category="lo", + direction="al", + }, + [0x10D09]={ + category="lo", + direction="al", + }, + [0x10D0A]={ + category="lo", + direction="al", + }, + [0x10D0B]={ + category="lo", + direction="al", + }, + [0x10D0C]={ + category="lo", + direction="al", + }, + [0x10D0D]={ + category="lo", + direction="al", + }, + [0x10D0E]={ + category="lo", + direction="al", + }, + [0x10D0F]={ + category="lo", + direction="al", + }, + [0x10D10]={ + category="lo", + direction="al", + }, + [0x10D11]={ + category="lo", + direction="al", + }, + [0x10D12]={ + category="lo", + direction="al", + }, + [0x10D13]={ + category="lo", + direction="al", + }, + [0x10D14]={ + category="lo", + direction="al", + }, + [0x10D15]={ + category="lo", + direction="al", + }, + [0x10D16]={ + category="lo", + direction="al", + }, + [0x10D17]={ + category="lo", + direction="al", + }, + [0x10D18]={ + category="lo", + direction="al", + }, + [0x10D19]={ + category="lo", + direction="al", + }, + [0x10D1A]={ + category="lo", + direction="al", + }, + [0x10D1B]={ + category="lo", + direction="al", + }, + [0x10D1C]={ + category="lo", + direction="al", + }, + [0x10D1D]={ + category="lo", + direction="al", + }, + [0x10D1E]={ + category="lo", + direction="al", + }, + [0x10D1F]={ + category="lo", + direction="al", + }, + [0x10D20]={ + category="lo", + direction="al", + }, + [0x10D21]={ + category="lo", + direction="al", + }, + [0x10D22]={ + category="lo", + direction="al", + }, + [0x10D23]={ + category="lo", + direction="al", + }, + [0x10D24]={ + category="mn", + direction="nsm", + }, + [0x10D25]={ + category="mn", + direction="nsm", + }, + [0x10D26]={ + category="mn", + direction="nsm", + }, + [0x10D27]={ + category="mn", + direction="nsm", + }, + [0x10D30]={ + category="nd", + direction="an", + }, + [0x10D31]={ + category="nd", + direction="an", + }, + [0x10D32]={ + category="nd", + direction="an", + }, + [0x10D33]={ + category="nd", + direction="an", + }, + [0x10D34]={ + category="nd", + direction="an", + }, + [0x10D35]={ + category="nd", + direction="an", + }, + [0x10D36]={ + category="nd", + direction="an", + }, + [0x10D37]={ + category="nd", + direction="an", + }, + [0x10D38]={ + category="nd", + direction="an", + }, + [0x10D39]={ + category="nd", + direction="an", + }, [0x10E60]={ category="no", direction="an", @@ -75597,6 +76470,334 @@ return { category="no", direction="an", }, + [0x10F00]={ + category="lo", + direction="r", + }, + [0x10F01]={ + category="lo", + direction="r", + }, + [0x10F02]={ + category="lo", + direction="r", + }, + [0x10F03]={ + category="lo", + direction="r", + }, + [0x10F04]={ + category="lo", + direction="r", + }, + [0x10F05]={ + category="lo", + direction="r", + }, + [0x10F06]={ + category="lo", + direction="r", + }, + [0x10F07]={ + category="lo", + direction="r", + }, + [0x10F08]={ + category="lo", + direction="r", + }, + [0x10F09]={ + category="lo", + direction="r", + }, + [0x10F0A]={ + category="lo", + direction="r", + }, + [0x10F0B]={ + category="lo", + direction="r", + }, + [0x10F0C]={ + category="lo", + direction="r", + }, + [0x10F0D]={ + category="lo", + direction="r", + }, + [0x10F0E]={ + category="lo", + direction="r", + }, + [0x10F0F]={ + category="lo", + direction="r", + }, + [0x10F10]={ + category="lo", + direction="r", + }, + [0x10F11]={ + category="lo", + direction="r", + }, + [0x10F12]={ + category="lo", + direction="r", + }, + [0x10F13]={ + category="lo", + direction="r", + }, + [0x10F14]={ + category="lo", + direction="r", + }, + [0x10F15]={ + category="lo", + direction="r", + }, + [0x10F16]={ + category="lo", + direction="r", + }, + [0x10F17]={ + category="lo", + direction="r", + }, + [0x10F18]={ + category="lo", + direction="r", + }, + [0x10F19]={ + category="lo", + direction="r", + }, + [0x10F1A]={ + category="lo", + direction="r", + }, + [0x10F1B]={ + category="lo", + direction="r", + }, + [0x10F1C]={ + category="lo", + direction="r", + }, + [0x10F1D]={ + category="no", + direction="r", + }, + [0x10F1E]={ + category="no", + direction="r", + }, + [0x10F1F]={ + category="no", + direction="r", + }, + [0x10F20]={ + category="no", + direction="r", + }, + [0x10F21]={ + category="no", + direction="r", + }, + [0x10F22]={ + category="no", + direction="r", + }, + [0x10F23]={ + category="no", + direction="r", + }, + [0x10F24]={ + category="no", + direction="r", + }, + [0x10F25]={ + category="no", + direction="r", + }, + [0x10F26]={ + category="no", + direction="r", + }, + [0x10F27]={ + category="lo", + direction="r", + }, + [0x10F30]={ + category="lo", + direction="al", + }, + [0x10F31]={ + category="lo", + direction="al", + }, + [0x10F32]={ + category="lo", + direction="al", + }, + [0x10F33]={ + category="lo", + direction="al", + }, + [0x10F34]={ + category="lo", + direction="al", + }, + [0x10F35]={ + category="lo", + direction="al", + }, + [0x10F36]={ + category="lo", + direction="al", + }, + [0x10F37]={ + category="lo", + direction="al", + }, + [0x10F38]={ + category="lo", + direction="al", + }, + [0x10F39]={ + category="lo", + direction="al", + }, + [0x10F3A]={ + category="lo", + direction="al", + }, + [0x10F3B]={ + category="lo", + direction="al", + }, + [0x10F3C]={ + category="lo", + direction="al", + }, + [0x10F3D]={ + category="lo", + direction="al", + }, + [0x10F3E]={ + category="lo", + direction="al", + }, + [0x10F3F]={ + category="lo", + direction="al", + }, + [0x10F40]={ + category="lo", + direction="al", + }, + [0x10F41]={ + category="lo", + direction="al", + }, + [0x10F42]={ + category="lo", + direction="al", + }, + [0x10F43]={ + category="lo", + direction="al", + }, + [0x10F44]={ + category="lo", + direction="al", + }, + [0x10F45]={ + category="lo", + direction="al", + }, + [0x10F46]={ + category="mn", + direction="nsm", + }, + [0x10F47]={ + category="mn", + direction="nsm", + }, + [0x10F48]={ + category="mn", + direction="nsm", + }, + [0x10F49]={ + category="mn", + direction="nsm", + }, + [0x10F4A]={ + category="mn", + direction="nsm", + }, + [0x10F4B]={ + category="mn", + direction="nsm", + }, + [0x10F4C]={ + category="mn", + direction="nsm", + }, + [0x10F4D]={ + category="mn", + direction="nsm", + }, + [0x10F4E]={ + category="mn", + direction="nsm", + }, + [0x10F4F]={ + category="mn", + direction="nsm", + }, + [0x10F50]={ + category="mn", + direction="nsm", + }, + [0x10F51]={ + category="no", + direction="al", + }, + [0x10F52]={ + category="no", + direction="al", + }, + [0x10F53]={ + category="no", + direction="al", + }, + [0x10F54]={ + category="no", + direction="al", + }, + [0x10F55]={ + category="po", + direction="al", + }, + [0x10F56]={ + category="po", + direction="al", + }, + [0x10F57]={ + category="po", + direction="al", + }, + [0x10F58]={ + category="po", + direction="al", + }, + [0x10F59]={ + category="po", + direction="al", + }, [0x11000]={ category="mc", direction="l", @@ -76297,6 +77498,10 @@ return { category="po", direction="l", }, + [0x110CD]={ + category="cf", + direction="l", + }, [0x110D0]={ category="lo", direction="l", @@ -76705,6 +77910,18 @@ return { category="po", direction="l", }, + [0x11144]={ + category="lo", + direction="l", + }, + [0x11145]={ + category="mc", + direction="l", + }, + [0x11146]={ + category="mc", + direction="l", + }, [0x11150]={ category="lo", direction="l", @@ -77155,7 +78372,7 @@ return { }, [0x111C9]={ category="po", - direction="l", + direction="nsm", }, [0x111CA]={ category="mn", @@ -78193,6 +79410,10 @@ return { category="lo", direction="l", }, + [0x1133B]={ + category="mn", + direction="nsm", + }, [0x1133C]={ category="mn", direction="nsm", @@ -78701,6 +79922,10 @@ return { category="po", direction="l", }, + [0x1145E]={ + category="mn", + direction="nsm", + }, [0x11480]={ category="lo", direction="l", @@ -80133,6 +81358,10 @@ return { category="lo", direction="l", }, + [0x1171A]={ + category="lo", + direction="l", + }, [0x1171D]={ category="mn", direction="nsm", @@ -80257,6 +81486,246 @@ return { category="so", direction="l", }, + [0x11800]={ + category="lo", + direction="l", + }, + [0x11801]={ + category="lo", + direction="l", + }, + [0x11802]={ + category="lo", + direction="l", + }, + [0x11803]={ + category="lo", + direction="l", + }, + [0x11804]={ + category="lo", + direction="l", + }, + [0x11805]={ + category="lo", + direction="l", + }, + [0x11806]={ + category="lo", + direction="l", + }, + [0x11807]={ + category="lo", + direction="l", + }, + [0x11808]={ + category="lo", + direction="l", + }, + [0x11809]={ + category="lo", + direction="l", + }, + [0x1180A]={ + category="lo", + direction="l", + }, + [0x1180B]={ + category="lo", + direction="l", + }, + [0x1180C]={ + category="lo", + direction="l", + }, + [0x1180D]={ + category="lo", + direction="l", + }, + [0x1180E]={ + category="lo", + direction="l", + }, + [0x1180F]={ + category="lo", + direction="l", + }, + [0x11810]={ + category="lo", + direction="l", + }, + [0x11811]={ + category="lo", + direction="l", + }, + [0x11812]={ + category="lo", + direction="l", + }, + [0x11813]={ + category="lo", + direction="l", + }, + [0x11814]={ + category="lo", + direction="l", + }, + [0x11815]={ + category="lo", + direction="l", + }, + [0x11816]={ + category="lo", + direction="l", + }, + [0x11817]={ + category="lo", + direction="l", + }, + [0x11818]={ + category="lo", + direction="l", + }, + [0x11819]={ + category="lo", + direction="l", + }, + [0x1181A]={ + category="lo", + direction="l", + }, + [0x1181B]={ + category="lo", + direction="l", + }, + [0x1181C]={ + category="lo", + direction="l", + }, + [0x1181D]={ + category="lo", + direction="l", + }, + [0x1181E]={ + category="lo", + direction="l", + }, + [0x1181F]={ + category="lo", + direction="l", + }, + [0x11820]={ + category="lo", + direction="l", + }, + [0x11821]={ + category="lo", + direction="l", + }, + [0x11822]={ + category="lo", + direction="l", + }, + [0x11823]={ + category="lo", + direction="l", + }, + [0x11824]={ + category="lo", + direction="l", + }, + [0x11825]={ + category="lo", + direction="l", + }, + [0x11826]={ + category="lo", + direction="l", + }, + [0x11827]={ + category="lo", + direction="l", + }, + [0x11828]={ + category="lo", + direction="l", + }, + [0x11829]={ + category="lo", + direction="l", + }, + [0x1182A]={ + category="lo", + direction="l", + }, + [0x1182B]={ + category="lo", + direction="l", + }, + [0x1182C]={ + category="mc", + direction="l", + }, + [0x1182D]={ + category="mc", + direction="l", + }, + [0x1182E]={ + category="mc", + direction="l", + }, + [0x1182F]={ + category="mn", + direction="nsm", + }, + [0x11830]={ + category="mn", + direction="nsm", + }, + [0x11831]={ + category="mn", + direction="nsm", + }, + [0x11832]={ + category="mn", + direction="nsm", + }, + [0x11833]={ + category="mn", + direction="nsm", + }, + [0x11834]={ + category="mn", + direction="nsm", + }, + [0x11835]={ + category="mn", + direction="nsm", + }, + [0x11836]={ + category="mn", + direction="nsm", + }, + [0x11837]={ + category="mn", + direction="nsm", + }, + [0x11838]={ + category="mc", + direction="l", + }, + [0x11839]={ + category="mn", + direction="nsm", + }, + [0x1183A]={ + category="mn", + direction="nsm", + }, + [0x1183B]={ + category="po", + direction="l", + }, [0x118A0]={ category="lu", direction="l", @@ -80593,19107 +82062,23463 @@ return { category="lo", direction="l", }, - [0x11AC0]={ + [0x11A00]={ category="lo", direction="l", }, - [0x11AC1]={ - category="lo", - direction="l", + [0x11A01]={ + category="mn", + direction="nsm", }, - [0x11AC2]={ - category="lo", - direction="l", + [0x11A02]={ + category="mn", + direction="nsm", }, - [0x11AC3]={ - category="lo", - direction="l", + [0x11A03]={ + category="mn", + direction="nsm", }, - [0x11AC4]={ - category="lo", - direction="l", + [0x11A04]={ + category="mn", + direction="nsm", }, - [0x11AC5]={ - category="lo", + [0x11A05]={ + category="mn", + direction="nsm", + }, + [0x11A06]={ + category="mn", + direction="nsm", + }, + [0x11A07]={ + category="mc", direction="l", }, - [0x11AC6]={ - category="lo", + [0x11A08]={ + category="mc", direction="l", }, - [0x11AC7]={ + [0x11A09]={ + category="mn", + direction="nsm", + }, + [0x11A0A]={ + category="mn", + direction="nsm", + }, + [0x11A0B]={ category="lo", direction="l", }, - [0x11AC8]={ + [0x11A0C]={ category="lo", direction="l", }, - [0x11AC9]={ + [0x11A0D]={ category="lo", direction="l", }, - [0x11ACA]={ + [0x11A0E]={ category="lo", direction="l", }, - [0x11ACB]={ + [0x11A0F]={ category="lo", direction="l", }, - [0x11ACC]={ + [0x11A10]={ category="lo", direction="l", }, - [0x11ACD]={ + [0x11A11]={ category="lo", direction="l", }, - [0x11ACE]={ + [0x11A12]={ category="lo", direction="l", }, - [0x11ACF]={ + [0x11A13]={ category="lo", direction="l", }, - [0x11AD0]={ + [0x11A14]={ category="lo", direction="l", }, - [0x11AD1]={ + [0x11A15]={ category="lo", direction="l", }, - [0x11AD2]={ + [0x11A16]={ category="lo", direction="l", }, - [0x11AD3]={ + [0x11A17]={ category="lo", direction="l", }, - [0x11AD4]={ + [0x11A18]={ category="lo", direction="l", }, - [0x11AD5]={ + [0x11A19]={ category="lo", direction="l", }, - [0x11AD6]={ + [0x11A1A]={ category="lo", direction="l", }, - [0x11AD7]={ + [0x11A1B]={ category="lo", direction="l", }, - [0x11AD8]={ + [0x11A1C]={ category="lo", direction="l", }, - [0x11AD9]={ + [0x11A1D]={ category="lo", direction="l", }, - [0x11ADA]={ + [0x11A1E]={ category="lo", direction="l", }, - [0x11ADB]={ + [0x11A1F]={ category="lo", direction="l", }, - [0x11ADC]={ + [0x11A20]={ category="lo", direction="l", }, - [0x11ADD]={ + [0x11A21]={ category="lo", direction="l", }, - [0x11ADE]={ + [0x11A22]={ category="lo", direction="l", }, - [0x11ADF]={ + [0x11A23]={ category="lo", direction="l", }, - [0x11AE0]={ + [0x11A24]={ category="lo", direction="l", }, - [0x11AE1]={ + [0x11A25]={ category="lo", direction="l", }, - [0x11AE2]={ + [0x11A26]={ category="lo", direction="l", }, - [0x11AE3]={ + [0x11A27]={ category="lo", direction="l", }, - [0x11AE4]={ + [0x11A28]={ category="lo", direction="l", }, - [0x11AE5]={ + [0x11A29]={ category="lo", direction="l", }, - [0x11AE6]={ + [0x11A2A]={ category="lo", direction="l", }, - [0x11AE7]={ + [0x11A2B]={ category="lo", direction="l", }, - [0x11AE8]={ + [0x11A2C]={ category="lo", direction="l", }, - [0x11AE9]={ + [0x11A2D]={ category="lo", direction="l", }, - [0x11AEA]={ + [0x11A2E]={ category="lo", direction="l", }, - [0x11AEB]={ + [0x11A2F]={ category="lo", direction="l", }, - [0x11AEC]={ + [0x11A30]={ category="lo", direction="l", }, - [0x11AED]={ + [0x11A31]={ category="lo", direction="l", }, - [0x11AEE]={ + [0x11A32]={ category="lo", direction="l", }, - [0x11AEF]={ - category="lo", + [0x11A33]={ + category="mn", + direction="nsm", + }, + [0x11A34]={ + category="mn", + direction="nsm", + }, + [0x11A35]={ + category="mn", + direction="nsm", + }, + [0x11A36]={ + category="mn", + direction="nsm", + }, + [0x11A37]={ + category="mn", + direction="nsm", + }, + [0x11A38]={ + category="mn", + direction="nsm", + }, + [0x11A39]={ + category="mc", direction="l", }, - [0x11AF0]={ + [0x11A3A]={ category="lo", direction="l", }, - [0x11AF1]={ - category="lo", + [0x11A3B]={ + category="mn", + direction="nsm", + }, + [0x11A3C]={ + category="mn", + direction="nsm", + }, + [0x11A3D]={ + category="mn", + direction="nsm", + }, + [0x11A3E]={ + category="mn", + direction="nsm", + }, + [0x11A3F]={ + category="po", direction="l", }, - [0x11AF2]={ - category="lo", + [0x11A40]={ + category="po", direction="l", }, - [0x11AF3]={ - category="lo", + [0x11A41]={ + category="po", direction="l", }, - [0x11AF4]={ - category="lo", + [0x11A42]={ + category="po", direction="l", }, - [0x11AF5]={ - category="lo", + [0x11A43]={ + category="po", direction="l", }, - [0x11AF6]={ - category="lo", + [0x11A44]={ + category="po", direction="l", }, - [0x11AF7]={ - category="lo", + [0x11A45]={ + category="po", direction="l", }, - [0x11AF8]={ - category="lo", + [0x11A46]={ + category="po", direction="l", }, - [0x11C00]={ + [0x11A47]={ + category="mn", + direction="nsm", + }, + [0x11A50]={ category="lo", direction="l", }, - [0x11C01]={ - category="lo", + [0x11A51]={ + category="mn", + direction="nsm", + }, + [0x11A52]={ + category="mn", + direction="nsm", + }, + [0x11A53]={ + category="mn", + direction="nsm", + }, + [0x11A54]={ + category="mn", + direction="nsm", + }, + [0x11A55]={ + category="mn", + direction="nsm", + }, + [0x11A56]={ + category="mn", + direction="nsm", + }, + [0x11A57]={ + category="mc", direction="l", }, - [0x11C02]={ - category="lo", + [0x11A58]={ + category="mc", direction="l", }, - [0x11C03]={ + [0x11A59]={ + category="mn", + direction="nsm", + }, + [0x11A5A]={ + category="mn", + direction="nsm", + }, + [0x11A5B]={ + category="mn", + direction="nsm", + }, + [0x11A5C]={ category="lo", direction="l", }, - [0x11C04]={ + [0x11A5D]={ category="lo", direction="l", }, - [0x11C05]={ + [0x11A5E]={ category="lo", direction="l", }, - [0x11C06]={ + [0x11A5F]={ category="lo", direction="l", }, - [0x11C07]={ + [0x11A60]={ category="lo", direction="l", }, - [0x11C08]={ + [0x11A61]={ category="lo", direction="l", }, - [0x11C0A]={ + [0x11A62]={ category="lo", direction="l", }, - [0x11C0B]={ + [0x11A63]={ category="lo", direction="l", }, - [0x11C0C]={ + [0x11A64]={ category="lo", direction="l", }, - [0x11C0D]={ + [0x11A65]={ category="lo", direction="l", }, - [0x11C0E]={ + [0x11A66]={ category="lo", direction="l", }, - [0x11C0F]={ + [0x11A67]={ category="lo", direction="l", }, - [0x11C10]={ + [0x11A68]={ category="lo", direction="l", }, - [0x11C11]={ + [0x11A69]={ category="lo", direction="l", }, - [0x11C12]={ + [0x11A6A]={ category="lo", direction="l", }, - [0x11C13]={ + [0x11A6B]={ category="lo", direction="l", }, - [0x11C14]={ + [0x11A6C]={ category="lo", direction="l", }, - [0x11C15]={ + [0x11A6D]={ category="lo", direction="l", }, - [0x11C16]={ + [0x11A6E]={ category="lo", direction="l", }, - [0x11C17]={ + [0x11A6F]={ category="lo", direction="l", }, - [0x11C18]={ + [0x11A70]={ category="lo", direction="l", }, - [0x11C19]={ + [0x11A71]={ category="lo", direction="l", }, - [0x11C1A]={ + [0x11A72]={ category="lo", direction="l", }, - [0x11C1B]={ + [0x11A73]={ category="lo", direction="l", }, - [0x11C1C]={ + [0x11A74]={ category="lo", direction="l", }, - [0x11C1D]={ + [0x11A75]={ category="lo", direction="l", }, - [0x11C1E]={ + [0x11A76]={ category="lo", direction="l", }, - [0x11C1F]={ + [0x11A77]={ category="lo", direction="l", }, - [0x11C20]={ + [0x11A78]={ category="lo", direction="l", }, - [0x11C21]={ + [0x11A79]={ category="lo", direction="l", }, - [0x11C22]={ + [0x11A7A]={ category="lo", direction="l", }, - [0x11C23]={ + [0x11A7B]={ category="lo", direction="l", }, - [0x11C24]={ + [0x11A7C]={ category="lo", direction="l", }, - [0x11C25]={ + [0x11A7D]={ category="lo", direction="l", }, - [0x11C26]={ + [0x11A7E]={ category="lo", direction="l", }, - [0x11C27]={ + [0x11A7F]={ category="lo", direction="l", }, - [0x11C28]={ + [0x11A80]={ category="lo", direction="l", }, - [0x11C29]={ + [0x11A81]={ category="lo", direction="l", }, - [0x11C2A]={ + [0x11A82]={ category="lo", direction="l", }, - [0x11C2B]={ + [0x11A83]={ category="lo", direction="l", }, - [0x11C2C]={ + [0x11A86]={ category="lo", direction="l", }, - [0x11C2D]={ + [0x11A87]={ category="lo", direction="l", }, - [0x11C2E]={ + [0x11A88]={ category="lo", direction="l", }, - [0x11C2F]={ - category="mc", + [0x11A89]={ + category="lo", direction="l", }, - [0x11C30]={ + [0x11A8A]={ category="mn", direction="nsm", }, - [0x11C31]={ + [0x11A8B]={ category="mn", direction="nsm", }, - [0x11C32]={ + [0x11A8C]={ category="mn", direction="nsm", }, - [0x11C33]={ + [0x11A8D]={ category="mn", direction="nsm", }, - [0x11C34]={ + [0x11A8E]={ category="mn", direction="nsm", }, - [0x11C35]={ + [0x11A8F]={ category="mn", direction="nsm", }, - [0x11C36]={ + [0x11A90]={ category="mn", direction="nsm", }, - [0x11C38]={ + [0x11A91]={ category="mn", direction="nsm", }, - [0x11C39]={ + [0x11A92]={ category="mn", direction="nsm", }, - [0x11C3A]={ + [0x11A93]={ category="mn", direction="nsm", }, - [0x11C3B]={ + [0x11A94]={ category="mn", direction="nsm", }, - [0x11C3C]={ + [0x11A95]={ category="mn", direction="nsm", }, - [0x11C3D]={ + [0x11A96]={ category="mn", direction="nsm", }, - [0x11C3E]={ + [0x11A97]={ category="mc", direction="l", }, - [0x11C3F]={ + [0x11A98]={ category="mn", - direction="l", + direction="nsm", }, - [0x11C40]={ - category="lo", - direction="l", + [0x11A99]={ + category="mn", + direction="nsm", }, - [0x11C41]={ + [0x11A9A]={ category="po", direction="l", }, - [0x11C42]={ + [0x11A9B]={ category="po", direction="l", }, - [0x11C43]={ + [0x11A9C]={ category="po", direction="l", }, - [0x11C44]={ - category="po", + [0x11A9D]={ + category="lo", direction="l", }, - [0x11C45]={ + [0x11A9E]={ category="po", direction="l", }, - [0x11C50]={ - category="nd", + [0x11A9F]={ + category="po", direction="l", }, - [0x11C51]={ - category="nd", + [0x11AA0]={ + category="po", direction="l", }, - [0x11C52]={ - category="nd", + [0x11AA1]={ + category="po", direction="l", }, - [0x11C53]={ - category="nd", + [0x11AA2]={ + category="po", direction="l", }, - [0x11C54]={ - category="nd", + [0x11AC0]={ + category="lo", direction="l", }, - [0x11C55]={ - category="nd", + [0x11AC1]={ + category="lo", direction="l", }, - [0x11C56]={ - category="nd", + [0x11AC2]={ + category="lo", direction="l", }, - [0x11C57]={ - category="nd", + [0x11AC3]={ + category="lo", direction="l", }, - [0x11C58]={ - category="nd", + [0x11AC4]={ + category="lo", direction="l", }, - [0x11C59]={ - category="nd", + [0x11AC5]={ + category="lo", direction="l", }, - [0x11C5A]={ - category="no", + [0x11AC6]={ + category="lo", direction="l", }, - [0x11C5B]={ - category="no", + [0x11AC7]={ + category="lo", direction="l", }, - [0x11C5C]={ - category="no", + [0x11AC8]={ + category="lo", direction="l", }, - [0x11C5D]={ - category="no", + [0x11AC9]={ + category="lo", direction="l", }, - [0x11C5E]={ - category="no", + [0x11ACA]={ + category="lo", direction="l", }, - [0x11C5F]={ - category="no", + [0x11ACB]={ + category="lo", direction="l", }, - [0x11C60]={ - category="no", + [0x11ACC]={ + category="lo", direction="l", }, - [0x11C61]={ - category="no", + [0x11ACD]={ + category="lo", direction="l", }, - [0x11C62]={ - category="no", + [0x11ACE]={ + category="lo", direction="l", }, - [0x11C63]={ - category="no", + [0x11ACF]={ + category="lo", direction="l", }, - [0x11C64]={ - category="no", + [0x11AD0]={ + category="lo", direction="l", }, - [0x11C65]={ - category="no", + [0x11AD1]={ + category="lo", direction="l", }, - [0x11C66]={ - category="no", + [0x11AD2]={ + category="lo", direction="l", }, - [0x11C67]={ - category="no", + [0x11AD3]={ + category="lo", direction="l", }, - [0x11C68]={ - category="no", + [0x11AD4]={ + category="lo", direction="l", }, - [0x11C69]={ - category="no", + [0x11AD5]={ + category="lo", direction="l", }, - [0x11C6A]={ - category="no", + [0x11AD6]={ + category="lo", direction="l", }, - [0x11C6B]={ - category="no", + [0x11AD7]={ + category="lo", direction="l", }, - [0x11C6C]={ - category="no", + [0x11AD8]={ + category="lo", direction="l", }, - [0x11C70]={ - category="po", + [0x11AD9]={ + category="lo", direction="l", }, - [0x11C71]={ - category="po", + [0x11ADA]={ + category="lo", direction="l", }, - [0x11C72]={ + [0x11ADB]={ category="lo", direction="l", }, - [0x11C73]={ + [0x11ADC]={ category="lo", direction="l", }, - [0x11C74]={ + [0x11ADD]={ category="lo", direction="l", }, - [0x11C75]={ + [0x11ADE]={ category="lo", direction="l", }, - [0x11C76]={ + [0x11ADF]={ category="lo", direction="l", }, - [0x11C77]={ + [0x11AE0]={ category="lo", direction="l", }, - [0x11C78]={ + [0x11AE1]={ category="lo", direction="l", }, - [0x11C79]={ + [0x11AE2]={ category="lo", direction="l", }, - [0x11C7A]={ + [0x11AE3]={ category="lo", direction="l", }, - [0x11C7B]={ + [0x11AE4]={ category="lo", direction="l", }, - [0x11C7C]={ + [0x11AE5]={ category="lo", direction="l", }, - [0x11C7D]={ + [0x11AE6]={ category="lo", direction="l", }, - [0x11C7E]={ + [0x11AE7]={ category="lo", direction="l", }, - [0x11C7F]={ + [0x11AE8]={ category="lo", direction="l", }, - [0x11C80]={ + [0x11AE9]={ category="lo", direction="l", }, - [0x11C81]={ + [0x11AEA]={ category="lo", direction="l", }, - [0x11C82]={ + [0x11AEB]={ category="lo", direction="l", }, - [0x11C83]={ + [0x11AEC]={ category="lo", direction="l", }, - [0x11C84]={ + [0x11AED]={ category="lo", direction="l", }, - [0x11C85]={ + [0x11AEE]={ category="lo", direction="l", }, - [0x11C86]={ + [0x11AEF]={ category="lo", direction="l", }, - [0x11C87]={ + [0x11AF0]={ category="lo", direction="l", }, - [0x11C88]={ + [0x11AF1]={ category="lo", direction="l", }, - [0x11C89]={ + [0x11AF2]={ category="lo", direction="l", }, - [0x11C8A]={ + [0x11AF3]={ category="lo", direction="l", }, - [0x11C8B]={ + [0x11AF4]={ category="lo", direction="l", }, - [0x11C8C]={ + [0x11AF5]={ category="lo", direction="l", }, - [0x11C8D]={ + [0x11AF6]={ category="lo", direction="l", }, - [0x11C8E]={ + [0x11AF7]={ category="lo", direction="l", }, - [0x11C8F]={ + [0x11AF8]={ category="lo", direction="l", }, - [0x11C92]={ + [0x11C00]={ + category="lo", + direction="l", + }, + [0x11C01]={ + category="lo", + direction="l", + }, + [0x11C02]={ + category="lo", + direction="l", + }, + [0x11C03]={ + category="lo", + direction="l", + }, + [0x11C04]={ + category="lo", + direction="l", + }, + [0x11C05]={ + category="lo", + direction="l", + }, + [0x11C06]={ + category="lo", + direction="l", + }, + [0x11C07]={ + category="lo", + direction="l", + }, + [0x11C08]={ + category="lo", + direction="l", + }, + [0x11C0A]={ + category="lo", + direction="l", + }, + [0x11C0B]={ + category="lo", + direction="l", + }, + [0x11C0C]={ + category="lo", + direction="l", + }, + [0x11C0D]={ + category="lo", + direction="l", + }, + [0x11C0E]={ + category="lo", + direction="l", + }, + [0x11C0F]={ + category="lo", + direction="l", + }, + [0x11C10]={ + category="lo", + direction="l", + }, + [0x11C11]={ + category="lo", + direction="l", + }, + [0x11C12]={ + category="lo", + direction="l", + }, + [0x11C13]={ + category="lo", + direction="l", + }, + [0x11C14]={ + category="lo", + direction="l", + }, + [0x11C15]={ + category="lo", + direction="l", + }, + [0x11C16]={ + category="lo", + direction="l", + }, + [0x11C17]={ + category="lo", + direction="l", + }, + [0x11C18]={ + category="lo", + direction="l", + }, + [0x11C19]={ + category="lo", + direction="l", + }, + [0x11C1A]={ + category="lo", + direction="l", + }, + [0x11C1B]={ + category="lo", + direction="l", + }, + [0x11C1C]={ + category="lo", + direction="l", + }, + [0x11C1D]={ + category="lo", + direction="l", + }, + [0x11C1E]={ + category="lo", + direction="l", + }, + [0x11C1F]={ + category="lo", + direction="l", + }, + [0x11C20]={ + category="lo", + direction="l", + }, + [0x11C21]={ + category="lo", + direction="l", + }, + [0x11C22]={ + category="lo", + direction="l", + }, + [0x11C23]={ + category="lo", + direction="l", + }, + [0x11C24]={ + category="lo", + direction="l", + }, + [0x11C25]={ + category="lo", + direction="l", + }, + [0x11C26]={ + category="lo", + direction="l", + }, + [0x11C27]={ + category="lo", + direction="l", + }, + [0x11C28]={ + category="lo", + direction="l", + }, + [0x11C29]={ + category="lo", + direction="l", + }, + [0x11C2A]={ + category="lo", + direction="l", + }, + [0x11C2B]={ + category="lo", + direction="l", + }, + [0x11C2C]={ + category="lo", + direction="l", + }, + [0x11C2D]={ + category="lo", + direction="l", + }, + [0x11C2E]={ + category="lo", + direction="l", + }, + [0x11C2F]={ + category="mc", + direction="l", + }, + [0x11C30]={ category="mn", direction="nsm", }, - [0x11C93]={ + [0x11C31]={ category="mn", direction="nsm", }, - [0x11C94]={ + [0x11C32]={ category="mn", direction="nsm", }, - [0x11C95]={ + [0x11C33]={ category="mn", direction="nsm", }, - [0x11C96]={ + [0x11C34]={ category="mn", direction="nsm", }, - [0x11C97]={ + [0x11C35]={ category="mn", direction="nsm", }, - [0x11C98]={ + [0x11C36]={ category="mn", direction="nsm", }, - [0x11C99]={ + [0x11C38]={ category="mn", direction="nsm", }, - [0x11C9A]={ + [0x11C39]={ category="mn", direction="nsm", }, - [0x11C9B]={ + [0x11C3A]={ category="mn", direction="nsm", }, - [0x11C9C]={ + [0x11C3B]={ category="mn", direction="nsm", }, - [0x11C9D]={ + [0x11C3C]={ category="mn", direction="nsm", }, - [0x11C9E]={ + [0x11C3D]={ category="mn", direction="nsm", }, - [0x11C9F]={ - category="mn", - direction="nsm", + [0x11C3E]={ + category="mc", + direction="l", + }, + [0x11C3F]={ + category="mn", + direction="l", + }, + [0x11C40]={ + category="lo", + direction="l", + }, + [0x11C41]={ + category="po", + direction="l", + }, + [0x11C42]={ + category="po", + direction="l", + }, + [0x11C43]={ + category="po", + direction="l", + }, + [0x11C44]={ + category="po", + direction="l", + }, + [0x11C45]={ + category="po", + direction="l", + }, + [0x11C50]={ + category="nd", + direction="l", + }, + [0x11C51]={ + category="nd", + direction="l", + }, + [0x11C52]={ + category="nd", + direction="l", + }, + [0x11C53]={ + category="nd", + direction="l", + }, + [0x11C54]={ + category="nd", + direction="l", + }, + [0x11C55]={ + category="nd", + direction="l", + }, + [0x11C56]={ + category="nd", + direction="l", + }, + [0x11C57]={ + category="nd", + direction="l", + }, + [0x11C58]={ + category="nd", + direction="l", + }, + [0x11C59]={ + category="nd", + direction="l", + }, + [0x11C5A]={ + category="no", + direction="l", + }, + [0x11C5B]={ + category="no", + direction="l", + }, + [0x11C5C]={ + category="no", + direction="l", + }, + [0x11C5D]={ + category="no", + direction="l", + }, + [0x11C5E]={ + category="no", + direction="l", + }, + [0x11C5F]={ + category="no", + direction="l", + }, + [0x11C60]={ + category="no", + direction="l", + }, + [0x11C61]={ + category="no", + direction="l", + }, + [0x11C62]={ + category="no", + direction="l", + }, + [0x11C63]={ + category="no", + direction="l", + }, + [0x11C64]={ + category="no", + direction="l", + }, + [0x11C65]={ + category="no", + direction="l", + }, + [0x11C66]={ + category="no", + direction="l", + }, + [0x11C67]={ + category="no", + direction="l", + }, + [0x11C68]={ + category="no", + direction="l", + }, + [0x11C69]={ + category="no", + direction="l", + }, + [0x11C6A]={ + category="no", + direction="l", + }, + [0x11C6B]={ + category="no", + direction="l", + }, + [0x11C6C]={ + category="no", + direction="l", + }, + [0x11C70]={ + category="po", + direction="l", + }, + [0x11C71]={ + category="po", + direction="l", + }, + [0x11C72]={ + category="lo", + direction="l", + }, + [0x11C73]={ + category="lo", + direction="l", + }, + [0x11C74]={ + category="lo", + direction="l", + }, + [0x11C75]={ + category="lo", + direction="l", + }, + [0x11C76]={ + category="lo", + direction="l", + }, + [0x11C77]={ + category="lo", + direction="l", + }, + [0x11C78]={ + category="lo", + direction="l", + }, + [0x11C79]={ + category="lo", + direction="l", + }, + [0x11C7A]={ + category="lo", + direction="l", + }, + [0x11C7B]={ + category="lo", + direction="l", + }, + [0x11C7C]={ + category="lo", + direction="l", + }, + [0x11C7D]={ + category="lo", + direction="l", + }, + [0x11C7E]={ + category="lo", + direction="l", + }, + [0x11C7F]={ + category="lo", + direction="l", + }, + [0x11C80]={ + category="lo", + direction="l", + }, + [0x11C81]={ + category="lo", + direction="l", + }, + [0x11C82]={ + category="lo", + direction="l", + }, + [0x11C83]={ + category="lo", + direction="l", + }, + [0x11C84]={ + category="lo", + direction="l", + }, + [0x11C85]={ + category="lo", + direction="l", + }, + [0x11C86]={ + category="lo", + direction="l", + }, + [0x11C87]={ + category="lo", + direction="l", + }, + [0x11C88]={ + category="lo", + direction="l", + }, + [0x11C89]={ + category="lo", + direction="l", + }, + [0x11C8A]={ + category="lo", + direction="l", + }, + [0x11C8B]={ + category="lo", + direction="l", + }, + [0x11C8C]={ + category="lo", + direction="l", + }, + [0x11C8D]={ + category="lo", + direction="l", + }, + [0x11C8E]={ + category="lo", + direction="l", + }, + [0x11C8F]={ + category="lo", + direction="l", + }, + [0x11C92]={ + category="mn", + direction="nsm", + }, + [0x11C93]={ + category="mn", + direction="nsm", + }, + [0x11C94]={ + category="mn", + direction="nsm", + }, + [0x11C95]={ + category="mn", + direction="nsm", + }, + [0x11C96]={ + category="mn", + direction="nsm", + }, + [0x11C97]={ + category="mn", + direction="nsm", + }, + [0x11C98]={ + category="mn", + direction="nsm", + }, + [0x11C99]={ + category="mn", + direction="nsm", + }, + [0x11C9A]={ + category="mn", + direction="nsm", + }, + [0x11C9B]={ + category="mn", + direction="nsm", + }, + [0x11C9C]={ + category="mn", + direction="nsm", + }, + [0x11C9D]={ + category="mn", + direction="nsm", + }, + [0x11C9E]={ + category="mn", + direction="nsm", + }, + [0x11C9F]={ + category="mn", + direction="nsm", + }, + [0x11CA0]={ + category="mn", + direction="nsm", + }, + [0x11CA1]={ + category="mn", + direction="nsm", + }, + [0x11CA2]={ + category="mn", + direction="nsm", + }, + [0x11CA3]={ + category="mn", + direction="nsm", + }, + [0x11CA4]={ + category="mn", + direction="nsm", + }, + [0x11CA5]={ + category="mn", + direction="nsm", + }, + [0x11CA6]={ + category="mn", + direction="nsm", + }, + [0x11CA7]={ + category="mn", + direction="nsm", + }, + [0x11CA9]={ + category="mc", + direction="l", + }, + [0x11CAA]={ + category="mn", + direction="nsm", + }, + [0x11CAB]={ + category="mn", + direction="nsm", + }, + [0x11CAC]={ + category="mn", + direction="nsm", + }, + [0x11CAD]={ + category="mn", + direction="nsm", + }, + [0x11CAE]={ + category="mn", + direction="nsm", + }, + [0x11CAF]={ + category="mn", + direction="nsm", + }, + [0x11CB0]={ + category="mn", + direction="nsm", + }, + [0x11CB1]={ + category="mc", + direction="l", + }, + [0x11CB2]={ + category="mn", + direction="nsm", + }, + [0x11CB3]={ + category="mn", + direction="nsm", + }, + [0x11CB4]={ + category="mc", + direction="l", + }, + [0x11CB5]={ + category="mn", + direction="nsm", + }, + [0x11CB6]={ + category="mn", + direction="nsm", + }, + [0x11D00]={ + category="lo", + direction="l", + }, + [0x11D01]={ + category="lo", + direction="l", + }, + [0x11D02]={ + category="lo", + direction="l", + }, + [0x11D03]={ + category="lo", + direction="l", + }, + [0x11D04]={ + category="lo", + direction="l", + }, + [0x11D05]={ + category="lo", + direction="l", + }, + [0x11D06]={ + category="lo", + direction="l", + }, + [0x11D08]={ + category="lo", + direction="l", + }, + [0x11D09]={ + category="lo", + direction="l", + }, + [0x11D0B]={ + category="lo", + direction="l", + }, + [0x11D0C]={ + category="lo", + direction="l", + }, + [0x11D0D]={ + category="lo", + direction="l", + }, + [0x11D0E]={ + category="lo", + direction="l", + }, + [0x11D0F]={ + category="lo", + direction="l", + }, + [0x11D10]={ + category="lo", + direction="l", + }, + [0x11D11]={ + category="lo", + direction="l", + }, + [0x11D12]={ + category="lo", + direction="l", + }, + [0x11D13]={ + category="lo", + direction="l", + }, + [0x11D14]={ + category="lo", + direction="l", + }, + [0x11D15]={ + category="lo", + direction="l", + }, + [0x11D16]={ + category="lo", + direction="l", + }, + [0x11D17]={ + category="lo", + direction="l", + }, + [0x11D18]={ + category="lo", + direction="l", + }, + [0x11D19]={ + category="lo", + direction="l", + }, + [0x11D1A]={ + category="lo", + direction="l", + }, + [0x11D1B]={ + category="lo", + direction="l", + }, + [0x11D1C]={ + category="lo", + direction="l", + }, + [0x11D1D]={ + category="lo", + direction="l", + }, + [0x11D1E]={ + category="lo", + direction="l", + }, + [0x11D1F]={ + category="lo", + direction="l", + }, + [0x11D20]={ + category="lo", + direction="l", + }, + [0x11D21]={ + category="lo", + direction="l", + }, + [0x11D22]={ + category="lo", + direction="l", + }, + [0x11D23]={ + category="lo", + direction="l", + }, + [0x11D24]={ + category="lo", + direction="l", + }, + [0x11D25]={ + category="lo", + direction="l", + }, + [0x11D26]={ + category="lo", + direction="l", + }, + [0x11D27]={ + category="lo", + direction="l", + }, + [0x11D28]={ + category="lo", + direction="l", + }, + [0x11D29]={ + category="lo", + direction="l", + }, + [0x11D2A]={ + category="lo", + direction="l", + }, + [0x11D2B]={ + category="lo", + direction="l", + }, + [0x11D2C]={ + category="lo", + direction="l", + }, + [0x11D2D]={ + category="lo", + direction="l", + }, + [0x11D2E]={ + category="lo", + direction="l", + }, + [0x11D2F]={ + category="lo", + direction="l", + }, + [0x11D30]={ + category="lo", + direction="l", + }, + [0x11D31]={ + category="mn", + direction="nsm", + }, + [0x11D32]={ + category="mn", + direction="nsm", + }, + [0x11D33]={ + category="mn", + direction="nsm", + }, + [0x11D34]={ + category="mn", + direction="nsm", + }, + [0x11D35]={ + category="mn", + direction="nsm", + }, + [0x11D36]={ + category="mn", + direction="nsm", + }, + [0x11D3A]={ + category="mn", + direction="nsm", + }, + [0x11D3C]={ + category="mn", + direction="nsm", + }, + [0x11D3D]={ + category="mn", + direction="nsm", + }, + [0x11D3F]={ + category="mn", + direction="nsm", + }, + [0x11D40]={ + category="mn", + direction="nsm", + }, + [0x11D41]={ + category="mn", + direction="nsm", + }, + [0x11D42]={ + category="mn", + direction="nsm", + }, + [0x11D43]={ + category="mn", + direction="nsm", + }, + [0x11D44]={ + category="mn", + direction="nsm", + }, + [0x11D45]={ + category="mn", + direction="nsm", + }, + [0x11D46]={ + category="lo", + direction="l", + }, + [0x11D47]={ + category="mn", + direction="nsm", + }, + [0x11D50]={ + category="nd", + direction="l", + }, + [0x11D51]={ + category="nd", + direction="l", + }, + [0x11D52]={ + category="nd", + direction="l", + }, + [0x11D53]={ + category="nd", + direction="l", + }, + [0x11D54]={ + category="nd", + direction="l", + }, + [0x11D55]={ + category="nd", + direction="l", + }, + [0x11D56]={ + category="nd", + direction="l", + }, + [0x11D57]={ + category="nd", + direction="l", + }, + [0x11D58]={ + category="nd", + direction="l", + }, + [0x11D59]={ + category="nd", + direction="l", + }, + [0x11D60]={ + category="lo", + direction="l", + }, + [0x11D61]={ + category="lo", + direction="l", + }, + [0x11D62]={ + category="lo", + direction="l", + }, + [0x11D63]={ + category="lo", + direction="l", + }, + [0x11D64]={ + category="lo", + direction="l", + }, + [0x11D65]={ + category="lo", + direction="l", + }, + [0x11D67]={ + category="lo", + direction="l", + }, + [0x11D68]={ + category="lo", + direction="l", + }, + [0x11D6A]={ + category="lo", + direction="l", + }, + [0x11D6B]={ + category="lo", + direction="l", + }, + [0x11D6C]={ + category="lo", + direction="l", + }, + [0x11D6D]={ + category="lo", + direction="l", + }, + [0x11D6E]={ + category="lo", + direction="l", + }, + [0x11D6F]={ + category="lo", + direction="l", + }, + [0x11D70]={ + category="lo", + direction="l", + }, + [0x11D71]={ + category="lo", + direction="l", + }, + [0x11D72]={ + category="lo", + direction="l", + }, + [0x11D73]={ + category="lo", + direction="l", + }, + [0x11D74]={ + category="lo", + direction="l", + }, + [0x11D75]={ + category="lo", + direction="l", + }, + [0x11D76]={ + category="lo", + direction="l", + }, + [0x11D77]={ + category="lo", + direction="l", + }, + [0x11D78]={ + category="lo", + direction="l", + }, + [0x11D79]={ + category="lo", + direction="l", + }, + [0x11D7A]={ + category="lo", + direction="l", + }, + [0x11D7B]={ + category="lo", + direction="l", + }, + [0x11D7C]={ + category="lo", + direction="l", + }, + [0x11D7D]={ + category="lo", + direction="l", + }, + [0x11D7E]={ + category="lo", + direction="l", + }, + [0x11D7F]={ + category="lo", + direction="l", + }, + [0x11D80]={ + category="lo", + direction="l", + }, + [0x11D81]={ + category="lo", + direction="l", + }, + [0x11D82]={ + category="lo", + direction="l", + }, + [0x11D83]={ + category="lo", + direction="l", + }, + [0x11D84]={ + category="lo", + direction="l", + }, + [0x11D85]={ + category="lo", + direction="l", + }, + [0x11D86]={ + category="lo", + direction="l", + }, + [0x11D87]={ + category="lo", + direction="l", + }, + [0x11D88]={ + category="lo", + direction="l", + }, + [0x11D89]={ + category="lo", + direction="l", + }, + [0x11D8A]={ + category="mc", + direction="l", + }, + [0x11D8B]={ + category="mc", + direction="l", + }, + [0x11D8C]={ + category="mc", + direction="l", + }, + [0x11D8D]={ + category="mc", + direction="l", + }, + [0x11D8E]={ + category="mc", + direction="l", + }, + [0x11D90]={ + category="mn", + direction="nsm", + }, + [0x11D91]={ + category="mn", + direction="nsm", + }, + [0x11D93]={ + category="mc", + direction="l", + }, + [0x11D94]={ + category="mc", + direction="l", + }, + [0x11D95]={ + category="mn", + direction="nsm", + }, + [0x11D96]={ + category="mc", + direction="l", + }, + [0x11D97]={ + category="mn", + direction="nsm", + }, + [0x11D98]={ + category="lo", + direction="l", + }, + [0x11DA0]={ + category="nd", + direction="l", + }, + [0x11DA1]={ + category="nd", + direction="l", + }, + [0x11DA2]={ + category="nd", + direction="l", + }, + [0x11DA3]={ + category="nd", + direction="l", + }, + [0x11DA4]={ + category="nd", + direction="l", + }, + [0x11DA5]={ + category="nd", + direction="l", + }, + [0x11DA6]={ + category="nd", + direction="l", + }, + [0x11DA7]={ + category="nd", + direction="l", + }, + [0x11DA8]={ + category="nd", + direction="l", + }, + [0x11DA9]={ + category="nd", + direction="l", + }, + [0x11EE0]={ + category="lo", + direction="l", + }, + [0x11EE1]={ + category="lo", + direction="l", + }, + [0x11EE2]={ + category="lo", + direction="l", + }, + [0x11EE3]={ + category="lo", + direction="l", + }, + [0x11EE4]={ + category="lo", + direction="l", + }, + [0x11EE5]={ + category="lo", + direction="l", + }, + [0x11EE6]={ + category="lo", + direction="l", + }, + [0x11EE7]={ + category="lo", + direction="l", + }, + [0x11EE8]={ + category="lo", + direction="l", + }, + [0x11EE9]={ + category="lo", + direction="l", + }, + [0x11EEA]={ + category="lo", + direction="l", + }, + [0x11EEB]={ + category="lo", + direction="l", + }, + [0x11EEC]={ + category="lo", + direction="l", + }, + [0x11EED]={ + category="lo", + direction="l", + }, + [0x11EEE]={ + category="lo", + direction="l", + }, + [0x11EEF]={ + category="lo", + direction="l", + }, + [0x11EF0]={ + category="lo", + direction="l", + }, + [0x11EF1]={ + category="lo", + direction="l", + }, + [0x11EF2]={ + category="lo", + direction="l", + }, + [0x11EF3]={ + category="mn", + direction="nsm", + }, + [0x11EF4]={ + category="mn", + direction="nsm", + }, + [0x11EF5]={ + category="mc", + direction="l", + }, + [0x11EF6]={ + category="mc", + direction="l", + }, + [0x11EF7]={ + category="po", + direction="l", + }, + [0x11EF8]={ + category="po", + direction="l", + }, + [0x12000]={ + category="lo", + direction="l", + }, + [0x12001]={ + category="lo", + direction="l", + }, + [0x12002]={ + category="lo", + direction="l", + }, + [0x12003]={ + category="lo", + direction="l", + }, + [0x12004]={ + category="lo", + direction="l", + }, + [0x12005]={ + category="lo", + direction="l", + }, + [0x12006]={ + category="lo", + direction="l", + }, + [0x12007]={ + category="lo", + direction="l", + }, + [0x12008]={ + category="lo", + direction="l", + }, + [0x12009]={ + category="lo", + direction="l", + }, + [0x1200A]={ + category="lo", + direction="l", + }, + [0x1200B]={ + category="lo", + direction="l", + }, + [0x1200C]={ + category="lo", + direction="l", + }, + [0x1200D]={ + category="lo", + direction="l", + }, + [0x1200E]={ + category="lo", + direction="l", + }, + [0x1200F]={ + category="lo", + direction="l", + }, + [0x12010]={ + category="lo", + direction="l", + }, + [0x12011]={ + category="lo", + direction="l", + }, + [0x12012]={ + category="lo", + direction="l", + }, + [0x12013]={ + category="lo", + direction="l", + }, + [0x12014]={ + category="lo", + direction="l", + }, + [0x12015]={ + category="lo", + direction="l", + }, + [0x12016]={ + category="lo", + direction="l", + }, + [0x12017]={ + category="lo", + direction="l", + }, + [0x12018]={ + category="lo", + direction="l", + }, + [0x12019]={ + category="lo", + direction="l", + }, + [0x1201A]={ + category="lo", + direction="l", + }, + [0x1201B]={ + category="lo", + direction="l", + }, + [0x1201C]={ + category="lo", + direction="l", + }, + [0x1201D]={ + category="lo", + direction="l", + }, + [0x1201E]={ + category="lo", + direction="l", + }, + [0x1201F]={ + category="lo", + direction="l", + }, + [0x12020]={ + category="lo", + direction="l", + }, + [0x12021]={ + category="lo", + direction="l", + }, + [0x12022]={ + category="lo", + direction="l", + }, + [0x12023]={ + category="lo", + direction="l", + }, + [0x12024]={ + category="lo", + direction="l", + }, + [0x12025]={ + category="lo", + direction="l", + }, + [0x12026]={ + category="lo", + direction="l", + }, + [0x12027]={ + category="lo", + direction="l", + }, + [0x12028]={ + category="lo", + direction="l", + }, + [0x12029]={ + category="lo", + direction="l", + }, + [0x1202A]={ + category="lo", + direction="l", + }, + [0x1202B]={ + category="lo", + direction="l", + }, + [0x1202C]={ + category="lo", + direction="l", + }, + [0x1202D]={ + category="lo", + direction="l", + }, + [0x1202E]={ + category="lo", + direction="l", + }, + [0x1202F]={ + category="lo", + direction="l", + }, + [0x12030]={ + category="lo", + direction="l", + }, + [0x12031]={ + category="lo", + direction="l", + }, + [0x12032]={ + category="lo", + direction="l", + }, + [0x12033]={ + category="lo", + direction="l", + }, + [0x12034]={ + category="lo", + direction="l", + }, + [0x12035]={ + category="lo", + direction="l", + }, + [0x12036]={ + category="lo", + direction="l", + }, + [0x12037]={ + category="lo", + direction="l", + }, + [0x12038]={ + category="lo", + direction="l", + }, + [0x12039]={ + category="lo", + direction="l", + }, + [0x1203A]={ + category="lo", + direction="l", + }, + [0x1203B]={ + category="lo", + direction="l", + }, + [0x1203C]={ + category="lo", + direction="l", + }, + [0x1203D]={ + category="lo", + direction="l", + }, + [0x1203E]={ + category="lo", + direction="l", + }, + [0x1203F]={ + category="lo", + direction="l", + }, + [0x12040]={ + category="lo", + direction="l", + }, + [0x12041]={ + category="lo", + direction="l", + }, + [0x12042]={ + category="lo", + direction="l", + }, + [0x12043]={ + category="lo", + direction="l", + }, + [0x12044]={ + category="lo", + direction="l", + }, + [0x12045]={ + category="lo", + direction="l", + }, + [0x12046]={ + category="lo", + direction="l", + }, + [0x12047]={ + category="lo", + direction="l", + }, + [0x12048]={ + category="lo", + direction="l", + }, + [0x12049]={ + category="lo", + direction="l", + }, + [0x1204A]={ + category="lo", + direction="l", + }, + [0x1204B]={ + category="lo", + direction="l", + }, + [0x1204C]={ + category="lo", + direction="l", + }, + [0x1204D]={ + category="lo", + direction="l", + }, + [0x1204E]={ + category="lo", + direction="l", + }, + [0x1204F]={ + category="lo", + direction="l", + }, + [0x12050]={ + category="lo", + direction="l", + }, + [0x12051]={ + category="lo", + direction="l", + }, + [0x12052]={ + category="lo", + direction="l", + }, + [0x12053]={ + category="lo", + direction="l", + }, + [0x12054]={ + category="lo", + direction="l", + }, + [0x12055]={ + category="lo", + direction="l", + }, + [0x12056]={ + category="lo", + direction="l", + }, + [0x12057]={ + category="lo", + direction="l", + }, + [0x12058]={ + category="lo", + direction="l", + }, + [0x12059]={ + category="lo", + direction="l", + }, + [0x1205A]={ + category="lo", + direction="l", + }, + [0x1205B]={ + category="lo", + direction="l", + }, + [0x1205C]={ + category="lo", + direction="l", + }, + [0x1205D]={ + category="lo", + direction="l", + }, + [0x1205E]={ + category="lo", + direction="l", + }, + [0x1205F]={ + category="lo", + direction="l", + }, + [0x12060]={ + category="lo", + direction="l", + }, + [0x12061]={ + category="lo", + direction="l", + }, + [0x12062]={ + category="lo", + direction="l", + }, + [0x12063]={ + category="lo", + direction="l", + }, + [0x12064]={ + category="lo", + direction="l", + }, + [0x12065]={ + category="lo", + direction="l", + }, + [0x12066]={ + category="lo", + direction="l", + }, + [0x12067]={ + category="lo", + direction="l", + }, + [0x12068]={ + category="lo", + direction="l", + }, + [0x12069]={ + category="lo", + direction="l", + }, + [0x1206A]={ + category="lo", + direction="l", + }, + [0x1206B]={ + category="lo", + direction="l", + }, + [0x1206C]={ + category="lo", + direction="l", + }, + [0x1206D]={ + category="lo", + direction="l", + }, + [0x1206E]={ + category="lo", + direction="l", + }, + [0x1206F]={ + category="lo", + direction="l", + }, + [0x12070]={ + category="lo", + direction="l", + }, + [0x12071]={ + category="lo", + direction="l", + }, + [0x12072]={ + category="lo", + direction="l", + }, + [0x12073]={ + category="lo", + direction="l", + }, + [0x12074]={ + category="lo", + direction="l", + }, + [0x12075]={ + category="lo", + direction="l", + }, + [0x12076]={ + category="lo", + direction="l", + }, + [0x12077]={ + category="lo", + direction="l", + }, + [0x12078]={ + category="lo", + direction="l", + }, + [0x12079]={ + category="lo", + direction="l", + }, + [0x1207A]={ + category="lo", + direction="l", + }, + [0x1207B]={ + category="lo", + direction="l", + }, + [0x1207C]={ + category="lo", + direction="l", + }, + [0x1207D]={ + category="lo", + direction="l", + }, + [0x1207E]={ + category="lo", + direction="l", + }, + [0x1207F]={ + category="lo", + direction="l", + }, + [0x12080]={ + category="lo", + direction="l", + }, + [0x12081]={ + category="lo", + direction="l", + }, + [0x12082]={ + category="lo", + direction="l", + }, + [0x12083]={ + category="lo", + direction="l", + }, + [0x12084]={ + category="lo", + direction="l", + }, + [0x12085]={ + category="lo", + direction="l", + }, + [0x12086]={ + category="lo", + direction="l", + }, + [0x12087]={ + category="lo", + direction="l", + }, + [0x12088]={ + category="lo", + direction="l", + }, + [0x12089]={ + category="lo", + direction="l", + }, + [0x1208A]={ + category="lo", + direction="l", + }, + [0x1208B]={ + category="lo", + direction="l", + }, + [0x1208C]={ + category="lo", + direction="l", + }, + [0x1208D]={ + category="lo", + direction="l", + }, + [0x1208E]={ + category="lo", + direction="l", + }, + [0x1208F]={ + category="lo", + direction="l", + }, + [0x12090]={ + category="lo", + direction="l", + }, + [0x12091]={ + category="lo", + direction="l", + }, + [0x12092]={ + category="lo", + direction="l", + }, + [0x12093]={ + category="lo", + direction="l", + }, + [0x12094]={ + category="lo", + direction="l", + }, + [0x12095]={ + category="lo", + direction="l", + }, + [0x12096]={ + category="lo", + direction="l", + }, + [0x12097]={ + category="lo", + direction="l", + }, + [0x12098]={ + category="lo", + direction="l", + }, + [0x12099]={ + category="lo", + direction="l", + }, + [0x1209A]={ + category="lo", + direction="l", + }, + [0x1209B]={ + category="lo", + direction="l", + }, + [0x1209C]={ + category="lo", + direction="l", + }, + [0x1209D]={ + category="lo", + direction="l", + }, + [0x1209E]={ + category="lo", + direction="l", + }, + [0x1209F]={ + category="lo", + direction="l", + }, + [0x120A0]={ + category="lo", + direction="l", + }, + [0x120A1]={ + category="lo", + direction="l", + }, + [0x120A2]={ + category="lo", + direction="l", + }, + [0x120A3]={ + category="lo", + direction="l", + }, + [0x120A4]={ + category="lo", + direction="l", + }, + [0x120A5]={ + category="lo", + direction="l", + }, + [0x120A6]={ + category="lo", + direction="l", + }, + [0x120A7]={ + category="lo", + direction="l", + }, + [0x120A8]={ + category="lo", + direction="l", + }, + [0x120A9]={ + category="lo", + direction="l", + }, + [0x120AA]={ + category="lo", + direction="l", + }, + [0x120AB]={ + category="lo", + direction="l", + }, + [0x120AC]={ + category="lo", + direction="l", + }, + [0x120AD]={ + category="lo", + direction="l", + }, + [0x120AE]={ + category="lo", + direction="l", + }, + [0x120AF]={ + category="lo", + direction="l", + }, + [0x120B0]={ + category="lo", + direction="l", + }, + [0x120B1]={ + category="lo", + direction="l", + }, + [0x120B2]={ + category="lo", + direction="l", + }, + [0x120B3]={ + category="lo", + direction="l", + }, + [0x120B4]={ + category="lo", + direction="l", + }, + [0x120B5]={ + category="lo", + direction="l", + }, + [0x120B6]={ + category="lo", + direction="l", + }, + [0x120B7]={ + category="lo", + direction="l", + }, + [0x120B8]={ + category="lo", + direction="l", + }, + [0x120B9]={ + category="lo", + direction="l", + }, + [0x120BA]={ + category="lo", + direction="l", + }, + [0x120BB]={ + category="lo", + direction="l", + }, + [0x120BC]={ + category="lo", + direction="l", + }, + [0x120BD]={ + category="lo", + direction="l", + }, + [0x120BE]={ + category="lo", + direction="l", + }, + [0x120BF]={ + category="lo", + direction="l", + }, + [0x120C0]={ + category="lo", + direction="l", + }, + [0x120C1]={ + category="lo", + direction="l", + }, + [0x120C2]={ + category="lo", + direction="l", + }, + [0x120C3]={ + category="lo", + direction="l", + }, + [0x120C4]={ + category="lo", + direction="l", + }, + [0x120C5]={ + category="lo", + direction="l", + }, + [0x120C6]={ + category="lo", + direction="l", + }, + [0x120C7]={ + category="lo", + direction="l", + }, + [0x120C8]={ + category="lo", + direction="l", + }, + [0x120C9]={ + category="lo", + direction="l", + }, + [0x120CA]={ + category="lo", + direction="l", + }, + [0x120CB]={ + category="lo", + direction="l", + }, + [0x120CC]={ + category="lo", + direction="l", + }, + [0x120CD]={ + category="lo", + direction="l", + }, + [0x120CE]={ + category="lo", + direction="l", + }, + [0x120CF]={ + category="lo", + direction="l", + }, + [0x120D0]={ + category="lo", + direction="l", + }, + [0x120D1]={ + category="lo", + direction="l", + }, + [0x120D2]={ + category="lo", + direction="l", + }, + [0x120D3]={ + category="lo", + direction="l", + }, + [0x120D4]={ + category="lo", + direction="l", + }, + [0x120D5]={ + category="lo", + direction="l", + }, + [0x120D6]={ + category="lo", + direction="l", + }, + [0x120D7]={ + category="lo", + direction="l", + }, + [0x120D8]={ + category="lo", + direction="l", + }, + [0x120D9]={ + category="lo", + direction="l", + }, + [0x120DA]={ + category="lo", + direction="l", + }, + [0x120DB]={ + category="lo", + direction="l", + }, + [0x120DC]={ + category="lo", + direction="l", + }, + [0x120DD]={ + category="lo", + direction="l", + }, + [0x120DE]={ + category="lo", + direction="l", + }, + [0x120DF]={ + category="lo", + direction="l", + }, + [0x120E0]={ + category="lo", + direction="l", + }, + [0x120E1]={ + category="lo", + direction="l", + }, + [0x120E2]={ + category="lo", + direction="l", + }, + [0x120E3]={ + category="lo", + direction="l", + }, + [0x120E4]={ + category="lo", + direction="l", + }, + [0x120E5]={ + category="lo", + direction="l", + }, + [0x120E6]={ + category="lo", + direction="l", + }, + [0x120E7]={ + category="lo", + direction="l", + }, + [0x120E8]={ + category="lo", + direction="l", + }, + [0x120E9]={ + category="lo", + direction="l", + }, + [0x120EA]={ + category="lo", + direction="l", + }, + [0x120EB]={ + category="lo", + direction="l", + }, + [0x120EC]={ + category="lo", + direction="l", + }, + [0x120ED]={ + category="lo", + direction="l", + }, + [0x120EE]={ + category="lo", + direction="l", + }, + [0x120EF]={ + category="lo", + direction="l", + }, + [0x120F0]={ + category="lo", + direction="l", + }, + [0x120F1]={ + category="lo", + direction="l", + }, + [0x120F2]={ + category="lo", + direction="l", + }, + [0x120F3]={ + category="lo", + direction="l", + }, + [0x120F4]={ + category="lo", + direction="l", + }, + [0x120F5]={ + category="lo", + direction="l", + }, + [0x120F6]={ + category="lo", + direction="l", + }, + [0x120F7]={ + category="lo", + direction="l", + }, + [0x120F8]={ + category="lo", + direction="l", + }, + [0x120F9]={ + category="lo", + direction="l", + }, + [0x120FA]={ + category="lo", + direction="l", + }, + [0x120FB]={ + category="lo", + direction="l", + }, + [0x120FC]={ + category="lo", + direction="l", + }, + [0x120FD]={ + category="lo", + direction="l", + }, + [0x120FE]={ + category="lo", + direction="l", + }, + [0x120FF]={ + category="lo", + direction="l", + }, + [0x12100]={ + category="lo", + direction="l", + }, + [0x12101]={ + category="lo", + direction="l", + }, + [0x12102]={ + category="lo", + direction="l", + }, + [0x12103]={ + category="lo", + direction="l", + }, + [0x12104]={ + category="lo", + direction="l", + }, + [0x12105]={ + category="lo", + direction="l", + }, + [0x12106]={ + category="lo", + direction="l", + }, + [0x12107]={ + category="lo", + direction="l", + }, + [0x12108]={ + category="lo", + direction="l", + }, + [0x12109]={ + category="lo", + direction="l", + }, + [0x1210A]={ + category="lo", + direction="l", + }, + [0x1210B]={ + category="lo", + direction="l", + }, + [0x1210C]={ + category="lo", + direction="l", + }, + [0x1210D]={ + category="lo", + direction="l", + }, + [0x1210E]={ + category="lo", + direction="l", + }, + [0x1210F]={ + category="lo", + direction="l", + }, + [0x12110]={ + category="lo", + direction="l", + }, + [0x12111]={ + category="lo", + direction="l", + }, + [0x12112]={ + category="lo", + direction="l", + }, + [0x12113]={ + category="lo", + direction="l", + }, + [0x12114]={ + category="lo", + direction="l", + }, + [0x12115]={ + category="lo", + direction="l", + }, + [0x12116]={ + category="lo", + direction="l", + }, + [0x12117]={ + category="lo", + direction="l", + }, + [0x12118]={ + category="lo", + direction="l", + }, + [0x12119]={ + category="lo", + direction="l", + }, + [0x1211A]={ + category="lo", + direction="l", + }, + [0x1211B]={ + category="lo", + direction="l", + }, + [0x1211C]={ + category="lo", + direction="l", + }, + [0x1211D]={ + category="lo", + direction="l", + }, + [0x1211E]={ + category="lo", + direction="l", + }, + [0x1211F]={ + category="lo", + direction="l", + }, + [0x12120]={ + category="lo", + direction="l", + }, + [0x12121]={ + category="lo", + direction="l", + }, + [0x12122]={ + category="lo", + direction="l", + }, + [0x12123]={ + category="lo", + direction="l", + }, + [0x12124]={ + category="lo", + direction="l", + }, + [0x12125]={ + category="lo", + direction="l", + }, + [0x12126]={ + category="lo", + direction="l", + }, + [0x12127]={ + category="lo", + direction="l", + }, + [0x12128]={ + category="lo", + direction="l", + }, + [0x12129]={ + category="lo", + direction="l", + }, + [0x1212A]={ + category="lo", + direction="l", + }, + [0x1212B]={ + category="lo", + direction="l", + }, + [0x1212C]={ + category="lo", + direction="l", + }, + [0x1212D]={ + category="lo", + direction="l", + }, + [0x1212E]={ + category="lo", + direction="l", + }, + [0x1212F]={ + category="lo", + direction="l", + }, + [0x12130]={ + category="lo", + direction="l", + }, + [0x12131]={ + category="lo", + direction="l", + }, + [0x12132]={ + category="lo", + direction="l", + }, + [0x12133]={ + category="lo", + direction="l", + }, + [0x12134]={ + category="lo", + direction="l", + }, + [0x12135]={ + category="lo", + direction="l", + }, + [0x12136]={ + category="lo", + direction="l", + }, + [0x12137]={ + category="lo", + direction="l", + }, + [0x12138]={ + category="lo", + direction="l", + }, + [0x12139]={ + category="lo", + direction="l", + }, + [0x1213A]={ + category="lo", + direction="l", + }, + [0x1213B]={ + category="lo", + direction="l", + }, + [0x1213C]={ + category="lo", + direction="l", + }, + [0x1213D]={ + category="lo", + direction="l", + }, + [0x1213E]={ + category="lo", + direction="l", + }, + [0x1213F]={ + category="lo", + direction="l", + }, + [0x12140]={ + category="lo", + direction="l", + }, + [0x12141]={ + category="lo", + direction="l", + }, + [0x12142]={ + category="lo", + direction="l", + }, + [0x12143]={ + category="lo", + direction="l", + }, + [0x12144]={ + category="lo", + direction="l", + }, + [0x12145]={ + category="lo", + direction="l", + }, + [0x12146]={ + category="lo", + direction="l", + }, + [0x12147]={ + category="lo", + direction="l", + }, + [0x12148]={ + category="lo", + direction="l", + }, + [0x12149]={ + category="lo", + direction="l", + }, + [0x1214A]={ + category="lo", + direction="l", + }, + [0x1214B]={ + category="lo", + direction="l", + }, + [0x1214C]={ + category="lo", + direction="l", + }, + [0x1214D]={ + category="lo", + direction="l", + }, + [0x1214E]={ + category="lo", + direction="l", + }, + [0x1214F]={ + category="lo", + direction="l", + }, + [0x12150]={ + category="lo", + direction="l", + }, + [0x12151]={ + category="lo", + direction="l", + }, + [0x12152]={ + category="lo", + direction="l", + }, + [0x12153]={ + category="lo", + direction="l", + }, + [0x12154]={ + category="lo", + direction="l", + }, + [0x12155]={ + category="lo", + direction="l", + }, + [0x12156]={ + category="lo", + direction="l", + }, + [0x12157]={ + category="lo", + direction="l", + }, + [0x12158]={ + category="lo", + direction="l", + }, + [0x12159]={ + category="lo", + direction="l", + }, + [0x1215A]={ + category="lo", + direction="l", + }, + [0x1215B]={ + category="lo", + direction="l", + }, + [0x1215C]={ + category="lo", + direction="l", + }, + [0x1215D]={ + category="lo", + direction="l", + }, + [0x1215E]={ + category="lo", + direction="l", + }, + [0x1215F]={ + category="lo", + direction="l", + }, + [0x12160]={ + category="lo", + direction="l", + }, + [0x12161]={ + category="lo", + direction="l", + }, + [0x12162]={ + category="lo", + direction="l", + }, + [0x12163]={ + category="lo", + direction="l", + }, + [0x12164]={ + category="lo", + direction="l", + }, + [0x12165]={ + category="lo", + direction="l", + }, + [0x12166]={ + category="lo", + direction="l", + }, + [0x12167]={ + category="lo", + direction="l", + }, + [0x12168]={ + category="lo", + direction="l", + }, + [0x12169]={ + category="lo", + direction="l", + }, + [0x1216A]={ + category="lo", + direction="l", + }, + [0x1216B]={ + category="lo", + direction="l", + }, + [0x1216C]={ + category="lo", + direction="l", + }, + [0x1216D]={ + category="lo", + direction="l", + }, + [0x1216E]={ + category="lo", + direction="l", + }, + [0x1216F]={ + category="lo", + direction="l", + }, + [0x12170]={ + category="lo", + direction="l", + }, + [0x12171]={ + category="lo", + direction="l", + }, + [0x12172]={ + category="lo", + direction="l", + }, + [0x12173]={ + category="lo", + direction="l", + }, + [0x12174]={ + category="lo", + direction="l", + }, + [0x12175]={ + category="lo", + direction="l", + }, + [0x12176]={ + category="lo", + direction="l", + }, + [0x12177]={ + category="lo", + direction="l", + }, + [0x12178]={ + category="lo", + direction="l", + }, + [0x12179]={ + category="lo", + direction="l", + }, + [0x1217A]={ + category="lo", + direction="l", + }, + [0x1217B]={ + category="lo", + direction="l", + }, + [0x1217C]={ + category="lo", + direction="l", + }, + [0x1217D]={ + category="lo", + direction="l", + }, + [0x1217E]={ + category="lo", + direction="l", + }, + [0x1217F]={ + category="lo", + direction="l", + }, + [0x12180]={ + category="lo", + direction="l", + }, + [0x12181]={ + category="lo", + direction="l", + }, + [0x12182]={ + category="lo", + direction="l", + }, + [0x12183]={ + category="lo", + direction="l", + }, + [0x12184]={ + category="lo", + direction="l", + }, + [0x12185]={ + category="lo", + direction="l", + }, + [0x12186]={ + category="lo", + direction="l", + }, + [0x12187]={ + category="lo", + direction="l", + }, + [0x12188]={ + category="lo", + direction="l", + }, + [0x12189]={ + category="lo", + direction="l", + }, + [0x1218A]={ + category="lo", + direction="l", + }, + [0x1218B]={ + category="lo", + direction="l", + }, + [0x1218C]={ + category="lo", + direction="l", + }, + [0x1218D]={ + category="lo", + direction="l", + }, + [0x1218E]={ + category="lo", + direction="l", + }, + [0x1218F]={ + category="lo", + direction="l", + }, + [0x12190]={ + category="lo", + direction="l", + }, + [0x12191]={ + category="lo", + direction="l", + }, + [0x12192]={ + category="lo", + direction="l", + }, + [0x12193]={ + category="lo", + direction="l", + }, + [0x12194]={ + category="lo", + direction="l", + }, + [0x12195]={ + category="lo", + direction="l", + }, + [0x12196]={ + category="lo", + direction="l", + }, + [0x12197]={ + category="lo", + direction="l", + }, + [0x12198]={ + category="lo", + direction="l", + }, + [0x12199]={ + category="lo", + direction="l", + }, + [0x1219A]={ + category="lo", + direction="l", + }, + [0x1219B]={ + category="lo", + direction="l", + }, + [0x1219C]={ + category="lo", + direction="l", + }, + [0x1219D]={ + category="lo", + direction="l", + }, + [0x1219E]={ + category="lo", + direction="l", + }, + [0x1219F]={ + category="lo", + direction="l", + }, + [0x121A0]={ + category="lo", + direction="l", + }, + [0x121A1]={ + category="lo", + direction="l", + }, + [0x121A2]={ + category="lo", + direction="l", + }, + [0x121A3]={ + category="lo", + direction="l", + }, + [0x121A4]={ + category="lo", + direction="l", + }, + [0x121A5]={ + category="lo", + direction="l", + }, + [0x121A6]={ + category="lo", + direction="l", + }, + [0x121A7]={ + category="lo", + direction="l", + }, + [0x121A8]={ + category="lo", + direction="l", + }, + [0x121A9]={ + category="lo", + direction="l", + }, + [0x121AA]={ + category="lo", + direction="l", + }, + [0x121AB]={ + category="lo", + direction="l", + }, + [0x121AC]={ + category="lo", + direction="l", + }, + [0x121AD]={ + category="lo", + direction="l", + }, + [0x121AE]={ + category="lo", + direction="l", + }, + [0x121AF]={ + category="lo", + direction="l", + }, + [0x121B0]={ + category="lo", + direction="l", + }, + [0x121B1]={ + category="lo", + direction="l", + }, + [0x121B2]={ + category="lo", + direction="l", + }, + [0x121B3]={ + category="lo", + direction="l", + }, + [0x121B4]={ + category="lo", + direction="l", + }, + [0x121B5]={ + category="lo", + direction="l", + }, + [0x121B6]={ + category="lo", + direction="l", + }, + [0x121B7]={ + category="lo", + direction="l", + }, + [0x121B8]={ + category="lo", + direction="l", + }, + [0x121B9]={ + category="lo", + direction="l", + }, + [0x121BA]={ + category="lo", + direction="l", + }, + [0x121BB]={ + category="lo", + direction="l", + }, + [0x121BC]={ + category="lo", + direction="l", + }, + [0x121BD]={ + category="lo", + direction="l", + }, + [0x121BE]={ + category="lo", + direction="l", + }, + [0x121BF]={ + category="lo", + direction="l", + }, + [0x121C0]={ + category="lo", + direction="l", + }, + [0x121C1]={ + category="lo", + direction="l", + }, + [0x121C2]={ + category="lo", + direction="l", + }, + [0x121C3]={ + category="lo", + direction="l", + }, + [0x121C4]={ + category="lo", + direction="l", + }, + [0x121C5]={ + category="lo", + direction="l", + }, + [0x121C6]={ + category="lo", + direction="l", + }, + [0x121C7]={ + category="lo", + direction="l", + }, + [0x121C8]={ + category="lo", + direction="l", + }, + [0x121C9]={ + category="lo", + direction="l", + }, + [0x121CA]={ + category="lo", + direction="l", + }, + [0x121CB]={ + category="lo", + direction="l", + }, + [0x121CC]={ + category="lo", + direction="l", + }, + [0x121CD]={ + category="lo", + direction="l", + }, + [0x121CE]={ + category="lo", + direction="l", + }, + [0x121CF]={ + category="lo", + direction="l", + }, + [0x121D0]={ + category="lo", + direction="l", + }, + [0x121D1]={ + category="lo", + direction="l", + }, + [0x121D2]={ + category="lo", + direction="l", + }, + [0x121D3]={ + category="lo", + direction="l", + }, + [0x121D4]={ + category="lo", + direction="l", + }, + [0x121D5]={ + category="lo", + direction="l", + }, + [0x121D6]={ + category="lo", + direction="l", + }, + [0x121D7]={ + category="lo", + direction="l", + }, + [0x121D8]={ + category="lo", + direction="l", + }, + [0x121D9]={ + category="lo", + direction="l", + }, + [0x121DA]={ + category="lo", + direction="l", + }, + [0x121DB]={ + category="lo", + direction="l", + }, + [0x121DC]={ + category="lo", + direction="l", + }, + [0x121DD]={ + category="lo", + direction="l", + }, + [0x121DE]={ + category="lo", + direction="l", + }, + [0x121DF]={ + category="lo", + direction="l", + }, + [0x121E0]={ + category="lo", + direction="l", + }, + [0x121E1]={ + category="lo", + direction="l", + }, + [0x121E2]={ + category="lo", + direction="l", + }, + [0x121E3]={ + category="lo", + direction="l", + }, + [0x121E4]={ + category="lo", + direction="l", + }, + [0x121E5]={ + category="lo", + direction="l", + }, + [0x121E6]={ + category="lo", + direction="l", + }, + [0x121E7]={ + category="lo", + direction="l", + }, + [0x121E8]={ + category="lo", + direction="l", + }, + [0x121E9]={ + category="lo", + direction="l", + }, + [0x121EA]={ + category="lo", + direction="l", + }, + [0x121EB]={ + category="lo", + direction="l", + }, + [0x121EC]={ + category="lo", + direction="l", + }, + [0x121ED]={ + category="lo", + direction="l", + }, + [0x121EE]={ + category="lo", + direction="l", + }, + [0x121EF]={ + category="lo", + direction="l", + }, + [0x121F0]={ + category="lo", + direction="l", + }, + [0x121F1]={ + category="lo", + direction="l", + }, + [0x121F2]={ + category="lo", + direction="l", + }, + [0x121F3]={ + category="lo", + direction="l", + }, + [0x121F4]={ + category="lo", + direction="l", + }, + [0x121F5]={ + category="lo", + direction="l", + }, + [0x121F6]={ + category="lo", + direction="l", + }, + [0x121F7]={ + category="lo", + direction="l", + }, + [0x121F8]={ + category="lo", + direction="l", + }, + [0x121F9]={ + category="lo", + direction="l", + }, + [0x121FA]={ + category="lo", + direction="l", + }, + [0x121FB]={ + category="lo", + direction="l", + }, + [0x121FC]={ + category="lo", + direction="l", + }, + [0x121FD]={ + category="lo", + direction="l", + }, + [0x121FE]={ + category="lo", + direction="l", + }, + [0x121FF]={ + category="lo", + direction="l", + }, + [0x12200]={ + category="lo", + direction="l", + }, + [0x12201]={ + category="lo", + direction="l", + }, + [0x12202]={ + category="lo", + direction="l", + }, + [0x12203]={ + category="lo", + direction="l", + }, + [0x12204]={ + category="lo", + direction="l", + }, + [0x12205]={ + category="lo", + direction="l", + }, + [0x12206]={ + category="lo", + direction="l", + }, + [0x12207]={ + category="lo", + direction="l", + }, + [0x12208]={ + category="lo", + direction="l", + }, + [0x12209]={ + category="lo", + direction="l", + }, + [0x1220A]={ + category="lo", + direction="l", + }, + [0x1220B]={ + category="lo", + direction="l", + }, + [0x1220C]={ + category="lo", + direction="l", + }, + [0x1220D]={ + category="lo", + direction="l", + }, + [0x1220E]={ + category="lo", + direction="l", + }, + [0x1220F]={ + category="lo", + direction="l", + }, + [0x12210]={ + category="lo", + direction="l", + }, + [0x12211]={ + category="lo", + direction="l", + }, + [0x12212]={ + category="lo", + direction="l", + }, + [0x12213]={ + category="lo", + direction="l", + }, + [0x12214]={ + category="lo", + direction="l", + }, + [0x12215]={ + category="lo", + direction="l", + }, + [0x12216]={ + category="lo", + direction="l", + }, + [0x12217]={ + category="lo", + direction="l", + }, + [0x12218]={ + category="lo", + direction="l", + }, + [0x12219]={ + category="lo", + direction="l", + }, + [0x1221A]={ + category="lo", + direction="l", + }, + [0x1221B]={ + category="lo", + direction="l", + }, + [0x1221C]={ + category="lo", + direction="l", + }, + [0x1221D]={ + category="lo", + direction="l", + }, + [0x1221E]={ + category="lo", + direction="l", + }, + [0x1221F]={ + category="lo", + direction="l", + }, + [0x12220]={ + category="lo", + direction="l", + }, + [0x12221]={ + category="lo", + direction="l", + }, + [0x12222]={ + category="lo", + direction="l", + }, + [0x12223]={ + category="lo", + direction="l", + }, + [0x12224]={ + category="lo", + direction="l", + }, + [0x12225]={ + category="lo", + direction="l", + }, + [0x12226]={ + category="lo", + direction="l", + }, + [0x12227]={ + category="lo", + direction="l", + }, + [0x12228]={ + category="lo", + direction="l", + }, + [0x12229]={ + category="lo", + direction="l", + }, + [0x1222A]={ + category="lo", + direction="l", + }, + [0x1222B]={ + category="lo", + direction="l", + }, + [0x1222C]={ + category="lo", + direction="l", + }, + [0x1222D]={ + category="lo", + direction="l", + }, + [0x1222E]={ + category="lo", + direction="l", + }, + [0x1222F]={ + category="lo", + direction="l", + }, + [0x12230]={ + category="lo", + direction="l", + }, + [0x12231]={ + category="lo", + direction="l", + }, + [0x12232]={ + category="lo", + direction="l", + }, + [0x12233]={ + category="lo", + direction="l", + }, + [0x12234]={ + category="lo", + direction="l", + }, + [0x12235]={ + category="lo", + direction="l", + }, + [0x12236]={ + category="lo", + direction="l", + }, + [0x12237]={ + category="lo", + direction="l", + }, + [0x12238]={ + category="lo", + direction="l", + }, + [0x12239]={ + category="lo", + direction="l", + }, + [0x1223A]={ + category="lo", + direction="l", + }, + [0x1223B]={ + category="lo", + direction="l", + }, + [0x1223C]={ + category="lo", + direction="l", + }, + [0x1223D]={ + category="lo", + direction="l", + }, + [0x1223E]={ + category="lo", + direction="l", + }, + [0x1223F]={ + category="lo", + direction="l", + }, + [0x12240]={ + category="lo", + direction="l", + }, + [0x12241]={ + category="lo", + direction="l", + }, + [0x12242]={ + category="lo", + direction="l", + }, + [0x12243]={ + category="lo", + direction="l", + }, + [0x12244]={ + category="lo", + direction="l", + }, + [0x12245]={ + category="lo", + direction="l", + }, + [0x12246]={ + category="lo", + direction="l", + }, + [0x12247]={ + category="lo", + direction="l", + }, + [0x12248]={ + category="lo", + direction="l", + }, + [0x12249]={ + category="lo", + direction="l", + }, + [0x1224A]={ + category="lo", + direction="l", + }, + [0x1224B]={ + category="lo", + direction="l", + }, + [0x1224C]={ + category="lo", + direction="l", + }, + [0x1224D]={ + category="lo", + direction="l", + }, + [0x1224E]={ + category="lo", + direction="l", + }, + [0x1224F]={ + category="lo", + direction="l", + }, + [0x12250]={ + category="lo", + direction="l", + }, + [0x12251]={ + category="lo", + direction="l", + }, + [0x12252]={ + category="lo", + direction="l", + }, + [0x12253]={ + category="lo", + direction="l", + }, + [0x12254]={ + category="lo", + direction="l", + }, + [0x12255]={ + category="lo", + direction="l", + }, + [0x12256]={ + category="lo", + direction="l", + }, + [0x12257]={ + category="lo", + direction="l", + }, + [0x12258]={ + category="lo", + direction="l", + }, + [0x12259]={ + category="lo", + direction="l", + }, + [0x1225A]={ + category="lo", + direction="l", + }, + [0x1225B]={ + category="lo", + direction="l", + }, + [0x1225C]={ + category="lo", + direction="l", + }, + [0x1225D]={ + category="lo", + direction="l", + }, + [0x1225E]={ + category="lo", + direction="l", + }, + [0x1225F]={ + category="lo", + direction="l", + }, + [0x12260]={ + category="lo", + direction="l", + }, + [0x12261]={ + category="lo", + direction="l", + }, + [0x12262]={ + category="lo", + direction="l", + }, + [0x12263]={ + category="lo", + direction="l", + }, + [0x12264]={ + category="lo", + direction="l", + }, + [0x12265]={ + category="lo", + direction="l", + }, + [0x12266]={ + category="lo", + direction="l", + }, + [0x12267]={ + category="lo", + direction="l", + }, + [0x12268]={ + category="lo", + direction="l", + }, + [0x12269]={ + category="lo", + direction="l", + }, + [0x1226A]={ + category="lo", + direction="l", + }, + [0x1226B]={ + category="lo", + direction="l", + }, + [0x1226C]={ + category="lo", + direction="l", + }, + [0x1226D]={ + category="lo", + direction="l", + }, + [0x1226E]={ + category="lo", + direction="l", + }, + [0x1226F]={ + category="lo", + direction="l", + }, + [0x12270]={ + category="lo", + direction="l", + }, + [0x12271]={ + category="lo", + direction="l", + }, + [0x12272]={ + category="lo", + direction="l", + }, + [0x12273]={ + category="lo", + direction="l", + }, + [0x12274]={ + category="lo", + direction="l", + }, + [0x12275]={ + category="lo", + direction="l", + }, + [0x12276]={ + category="lo", + direction="l", + }, + [0x12277]={ + category="lo", + direction="l", + }, + [0x12278]={ + category="lo", + direction="l", + }, + [0x12279]={ + category="lo", + direction="l", + }, + [0x1227A]={ + category="lo", + direction="l", + }, + [0x1227B]={ + category="lo", + direction="l", + }, + [0x1227C]={ + category="lo", + direction="l", + }, + [0x1227D]={ + category="lo", + direction="l", + }, + [0x1227E]={ + category="lo", + direction="l", + }, + [0x1227F]={ + category="lo", + direction="l", + }, + [0x12280]={ + category="lo", + direction="l", + }, + [0x12281]={ + category="lo", + direction="l", + }, + [0x12282]={ + category="lo", + direction="l", + }, + [0x12283]={ + category="lo", + direction="l", + }, + [0x12284]={ + category="lo", + direction="l", + }, + [0x12285]={ + category="lo", + direction="l", + }, + [0x12286]={ + category="lo", + direction="l", + }, + [0x12287]={ + category="lo", + direction="l", + }, + [0x12288]={ + category="lo", + direction="l", + }, + [0x12289]={ + category="lo", + direction="l", + }, + [0x1228A]={ + category="lo", + direction="l", + }, + [0x1228B]={ + category="lo", + direction="l", + }, + [0x1228C]={ + category="lo", + direction="l", + }, + [0x1228D]={ + category="lo", + direction="l", + }, + [0x1228E]={ + category="lo", + direction="l", + }, + [0x1228F]={ + category="lo", + direction="l", + }, + [0x12290]={ + category="lo", + direction="l", + }, + [0x12291]={ + category="lo", + direction="l", + }, + [0x12292]={ + category="lo", + direction="l", + }, + [0x12293]={ + category="lo", + direction="l", + }, + [0x12294]={ + category="lo", + direction="l", + }, + [0x12295]={ + category="lo", + direction="l", + }, + [0x12296]={ + category="lo", + direction="l", + }, + [0x12297]={ + category="lo", + direction="l", + }, + [0x12298]={ + category="lo", + direction="l", + }, + [0x12299]={ + category="lo", + direction="l", + }, + [0x1229A]={ + category="lo", + direction="l", + }, + [0x1229B]={ + category="lo", + direction="l", + }, + [0x1229C]={ + category="lo", + direction="l", + }, + [0x1229D]={ + category="lo", + direction="l", + }, + [0x1229E]={ + category="lo", + direction="l", + }, + [0x1229F]={ + category="lo", + direction="l", + }, + [0x122A0]={ + category="lo", + direction="l", + }, + [0x122A1]={ + category="lo", + direction="l", + }, + [0x122A2]={ + category="lo", + direction="l", + }, + [0x122A3]={ + category="lo", + direction="l", + }, + [0x122A4]={ + category="lo", + direction="l", + }, + [0x122A5]={ + category="lo", + direction="l", + }, + [0x122A6]={ + category="lo", + direction="l", + }, + [0x122A7]={ + category="lo", + direction="l", + }, + [0x122A8]={ + category="lo", + direction="l", + }, + [0x122A9]={ + category="lo", + direction="l", + }, + [0x122AA]={ + category="lo", + direction="l", + }, + [0x122AB]={ + category="lo", + direction="l", + }, + [0x122AC]={ + category="lo", + direction="l", + }, + [0x122AD]={ + category="lo", + direction="l", + }, + [0x122AE]={ + category="lo", + direction="l", + }, + [0x122AF]={ + category="lo", + direction="l", + }, + [0x122B0]={ + category="lo", + direction="l", + }, + [0x122B1]={ + category="lo", + direction="l", + }, + [0x122B2]={ + category="lo", + direction="l", + }, + [0x122B3]={ + category="lo", + direction="l", + }, + [0x122B4]={ + category="lo", + direction="l", + }, + [0x122B5]={ + category="lo", + direction="l", + }, + [0x122B6]={ + category="lo", + direction="l", + }, + [0x122B7]={ + category="lo", + direction="l", + }, + [0x122B8]={ + category="lo", + direction="l", + }, + [0x122B9]={ + category="lo", + direction="l", + }, + [0x122BA]={ + category="lo", + direction="l", + }, + [0x122BB]={ + category="lo", + direction="l", + }, + [0x122BC]={ + category="lo", + direction="l", + }, + [0x122BD]={ + category="lo", + direction="l", + }, + [0x122BE]={ + category="lo", + direction="l", + }, + [0x122BF]={ + category="lo", + direction="l", + }, + [0x122C0]={ + category="lo", + direction="l", + }, + [0x122C1]={ + category="lo", + direction="l", + }, + [0x122C2]={ + category="lo", + direction="l", + }, + [0x122C3]={ + category="lo", + direction="l", + }, + [0x122C4]={ + category="lo", + direction="l", + }, + [0x122C5]={ + category="lo", + direction="l", + }, + [0x122C6]={ + category="lo", + direction="l", + }, + [0x122C7]={ + category="lo", + direction="l", + }, + [0x122C8]={ + category="lo", + direction="l", + }, + [0x122C9]={ + category="lo", + direction="l", + }, + [0x122CA]={ + category="lo", + direction="l", + }, + [0x122CB]={ + category="lo", + direction="l", + }, + [0x122CC]={ + category="lo", + direction="l", + }, + [0x122CD]={ + category="lo", + direction="l", + }, + [0x122CE]={ + category="lo", + direction="l", + }, + [0x122CF]={ + category="lo", + direction="l", + }, + [0x122D0]={ + category="lo", + direction="l", + }, + [0x122D1]={ + category="lo", + direction="l", + }, + [0x122D2]={ + category="lo", + direction="l", + }, + [0x122D3]={ + category="lo", + direction="l", + }, + [0x122D4]={ + category="lo", + direction="l", + }, + [0x122D5]={ + category="lo", + direction="l", + }, + [0x122D6]={ + category="lo", + direction="l", + }, + [0x122D7]={ + category="lo", + direction="l", + }, + [0x122D8]={ + category="lo", + direction="l", + }, + [0x122D9]={ + category="lo", + direction="l", + }, + [0x122DA]={ + category="lo", + direction="l", + }, + [0x122DB]={ + category="lo", + direction="l", + }, + [0x122DC]={ + category="lo", + direction="l", + }, + [0x122DD]={ + category="lo", + direction="l", + }, + [0x122DE]={ + category="lo", + direction="l", }, - [0x11CA0]={ - category="mn", - direction="nsm", + [0x122DF]={ + category="lo", + direction="l", }, - [0x11CA1]={ - category="mn", - direction="nsm", + [0x122E0]={ + category="lo", + direction="l", }, - [0x11CA2]={ - category="mn", - direction="nsm", + [0x122E1]={ + category="lo", + direction="l", }, - [0x11CA3]={ - category="mn", - direction="nsm", + [0x122E2]={ + category="lo", + direction="l", }, - [0x11CA4]={ - category="mn", - direction="nsm", + [0x122E3]={ + category="lo", + direction="l", }, - [0x11CA5]={ - category="mn", - direction="nsm", + [0x122E4]={ + category="lo", + direction="l", }, - [0x11CA6]={ - category="mn", - direction="nsm", + [0x122E5]={ + category="lo", + direction="l", }, - [0x11CA7]={ - category="mn", - direction="nsm", + [0x122E6]={ + category="lo", + direction="l", }, - [0x11CA9]={ - category="mc", + [0x122E7]={ + category="lo", direction="l", }, - [0x11CAA]={ - category="mn", - direction="nsm", + [0x122E8]={ + category="lo", + direction="l", }, - [0x11CAB]={ - category="mn", - direction="nsm", + [0x122E9]={ + category="lo", + direction="l", }, - [0x11CAC]={ - category="mn", - direction="nsm", + [0x122EA]={ + category="lo", + direction="l", }, - [0x11CAD]={ - category="mn", - direction="nsm", + [0x122EB]={ + category="lo", + direction="l", }, - [0x11CAE]={ - category="mn", - direction="nsm", + [0x122EC]={ + category="lo", + direction="l", }, - [0x11CAF]={ - category="mn", - direction="nsm", + [0x122ED]={ + category="lo", + direction="l", }, - [0x11CB0]={ - category="mn", - direction="nsm", + [0x122EE]={ + category="lo", + direction="l", }, - [0x11CB1]={ - category="mc", + [0x122EF]={ + category="lo", direction="l", }, - [0x11CB2]={ - category="mn", - direction="nsm", + [0x122F0]={ + category="lo", + direction="l", }, - [0x11CB3]={ - category="mn", - direction="nsm", + [0x122F1]={ + category="lo", + direction="l", }, - [0x11CB4]={ - category="mc", + [0x122F2]={ + category="lo", direction="l", }, - [0x11CB5]={ - category="mn", - direction="nsm", + [0x122F3]={ + category="lo", + direction="l", }, - [0x11CB6]={ - category="mn", - direction="nsm", + [0x122F4]={ + category="lo", + direction="l", }, - [0x12000]={ + [0x122F5]={ category="lo", direction="l", }, - [0x12001]={ + [0x122F6]={ category="lo", direction="l", }, - [0x12002]={ + [0x122F7]={ category="lo", direction="l", }, - [0x12003]={ + [0x122F8]={ category="lo", direction="l", }, - [0x12004]={ + [0x122F9]={ category="lo", direction="l", }, - [0x12005]={ + [0x122FA]={ category="lo", direction="l", }, - [0x12006]={ + [0x122FB]={ category="lo", direction="l", }, - [0x12007]={ + [0x122FC]={ category="lo", direction="l", }, - [0x12008]={ + [0x122FD]={ category="lo", direction="l", }, - [0x12009]={ + [0x122FE]={ category="lo", direction="l", }, - [0x1200A]={ + [0x122FF]={ category="lo", direction="l", }, - [0x1200B]={ + [0x12300]={ category="lo", direction="l", }, - [0x1200C]={ + [0x12301]={ category="lo", direction="l", }, - [0x1200D]={ + [0x12302]={ category="lo", direction="l", }, - [0x1200E]={ + [0x12303]={ category="lo", direction="l", }, - [0x1200F]={ + [0x12304]={ category="lo", direction="l", }, - [0x12010]={ + [0x12305]={ category="lo", direction="l", }, - [0x12011]={ + [0x12306]={ category="lo", direction="l", }, - [0x12012]={ + [0x12307]={ category="lo", direction="l", }, - [0x12013]={ + [0x12308]={ category="lo", direction="l", }, - [0x12014]={ + [0x12309]={ category="lo", direction="l", }, - [0x12015]={ + [0x1230A]={ category="lo", direction="l", }, - [0x12016]={ + [0x1230B]={ category="lo", direction="l", }, - [0x12017]={ + [0x1230C]={ category="lo", direction="l", }, - [0x12018]={ + [0x1230D]={ category="lo", direction="l", }, - [0x12019]={ + [0x1230E]={ category="lo", direction="l", }, - [0x1201A]={ + [0x1230F]={ category="lo", direction="l", }, - [0x1201B]={ + [0x12310]={ category="lo", direction="l", }, - [0x1201C]={ + [0x12311]={ category="lo", direction="l", }, - [0x1201D]={ + [0x12312]={ category="lo", direction="l", }, - [0x1201E]={ + [0x12313]={ category="lo", direction="l", }, - [0x1201F]={ + [0x12314]={ category="lo", direction="l", }, - [0x12020]={ + [0x12315]={ category="lo", direction="l", }, - [0x12021]={ + [0x12316]={ category="lo", direction="l", }, - [0x12022]={ + [0x12317]={ category="lo", direction="l", }, - [0x12023]={ + [0x12318]={ category="lo", direction="l", }, - [0x12024]={ + [0x12319]={ category="lo", direction="l", }, - [0x12025]={ + [0x1231A]={ category="lo", direction="l", }, - [0x12026]={ + [0x1231B]={ category="lo", direction="l", }, - [0x12027]={ + [0x1231C]={ category="lo", direction="l", }, - [0x12028]={ + [0x1231D]={ category="lo", direction="l", }, - [0x12029]={ + [0x1231E]={ category="lo", direction="l", }, - [0x1202A]={ + [0x1231F]={ category="lo", direction="l", }, - [0x1202B]={ + [0x12320]={ category="lo", direction="l", }, - [0x1202C]={ + [0x12321]={ category="lo", direction="l", }, - [0x1202D]={ + [0x12322]={ category="lo", direction="l", }, - [0x1202E]={ + [0x12323]={ category="lo", direction="l", }, - [0x1202F]={ + [0x12324]={ category="lo", direction="l", }, - [0x12030]={ + [0x12325]={ category="lo", direction="l", }, - [0x12031]={ + [0x12326]={ category="lo", direction="l", }, - [0x12032]={ + [0x12327]={ category="lo", direction="l", }, - [0x12033]={ + [0x12328]={ category="lo", direction="l", }, - [0x12034]={ + [0x12329]={ category="lo", direction="l", }, - [0x12035]={ + [0x1232A]={ category="lo", direction="l", }, - [0x12036]={ + [0x1232B]={ category="lo", direction="l", }, - [0x12037]={ + [0x1232C]={ category="lo", direction="l", }, - [0x12038]={ + [0x1232D]={ category="lo", direction="l", }, - [0x12039]={ + [0x1232E]={ category="lo", direction="l", }, - [0x1203A]={ + [0x1232F]={ category="lo", direction="l", }, - [0x1203B]={ + [0x12330]={ category="lo", direction="l", }, - [0x1203C]={ + [0x12331]={ category="lo", direction="l", }, - [0x1203D]={ + [0x12332]={ category="lo", direction="l", }, - [0x1203E]={ + [0x12333]={ category="lo", direction="l", }, - [0x1203F]={ + [0x12334]={ category="lo", direction="l", }, - [0x12040]={ + [0x12335]={ category="lo", direction="l", }, - [0x12041]={ + [0x12336]={ category="lo", direction="l", }, - [0x12042]={ + [0x12337]={ category="lo", direction="l", }, - [0x12043]={ + [0x12338]={ category="lo", direction="l", }, - [0x12044]={ + [0x12339]={ category="lo", direction="l", }, - [0x12045]={ + [0x1233A]={ category="lo", direction="l", }, - [0x12046]={ + [0x1233B]={ category="lo", direction="l", }, - [0x12047]={ + [0x1233C]={ category="lo", direction="l", }, - [0x12048]={ + [0x1233D]={ category="lo", direction="l", }, - [0x12049]={ + [0x1233E]={ category="lo", direction="l", }, - [0x1204A]={ + [0x1233F]={ category="lo", direction="l", }, - [0x1204B]={ + [0x12340]={ category="lo", direction="l", }, - [0x1204C]={ + [0x12341]={ category="lo", direction="l", }, - [0x1204D]={ + [0x12342]={ category="lo", direction="l", }, - [0x1204E]={ + [0x12343]={ category="lo", direction="l", }, - [0x1204F]={ + [0x12344]={ category="lo", direction="l", }, - [0x12050]={ + [0x12345]={ category="lo", direction="l", }, - [0x12051]={ + [0x12346]={ category="lo", direction="l", }, - [0x12052]={ + [0x12347]={ category="lo", direction="l", }, - [0x12053]={ + [0x12348]={ category="lo", direction="l", }, - [0x12054]={ + [0x12349]={ category="lo", direction="l", }, - [0x12055]={ + [0x1234A]={ category="lo", direction="l", }, - [0x12056]={ + [0x1234B]={ category="lo", direction="l", }, - [0x12057]={ + [0x1234C]={ category="lo", direction="l", }, - [0x12058]={ + [0x1234D]={ category="lo", direction="l", }, - [0x12059]={ + [0x1234E]={ category="lo", direction="l", }, - [0x1205A]={ + [0x1234F]={ category="lo", direction="l", }, - [0x1205B]={ + [0x12350]={ category="lo", direction="l", }, - [0x1205C]={ + [0x12351]={ category="lo", direction="l", }, - [0x1205D]={ + [0x12352]={ category="lo", direction="l", }, - [0x1205E]={ + [0x12353]={ category="lo", direction="l", }, - [0x1205F]={ + [0x12354]={ category="lo", direction="l", }, - [0x12060]={ + [0x12355]={ category="lo", direction="l", }, - [0x12061]={ + [0x12356]={ category="lo", direction="l", }, - [0x12062]={ + [0x12357]={ category="lo", direction="l", }, - [0x12063]={ + [0x12358]={ category="lo", direction="l", }, - [0x12064]={ + [0x12359]={ category="lo", direction="l", }, - [0x12065]={ + [0x1235A]={ category="lo", direction="l", }, - [0x12066]={ + [0x1235B]={ category="lo", direction="l", }, - [0x12067]={ + [0x1235C]={ category="lo", direction="l", }, - [0x12068]={ + [0x1235D]={ category="lo", direction="l", }, - [0x12069]={ + [0x1235E]={ category="lo", direction="l", }, - [0x1206A]={ + [0x1235F]={ category="lo", direction="l", }, - [0x1206B]={ + [0x12360]={ category="lo", direction="l", }, - [0x1206C]={ + [0x12361]={ category="lo", direction="l", }, - [0x1206D]={ + [0x12362]={ category="lo", direction="l", }, - [0x1206E]={ + [0x12363]={ category="lo", direction="l", }, - [0x1206F]={ + [0x12364]={ category="lo", direction="l", }, - [0x12070]={ + [0x12365]={ category="lo", direction="l", }, - [0x12071]={ + [0x12366]={ category="lo", direction="l", }, - [0x12072]={ + [0x12367]={ category="lo", direction="l", }, - [0x12073]={ + [0x12368]={ category="lo", direction="l", }, - [0x12074]={ + [0x12369]={ category="lo", direction="l", }, - [0x12075]={ + [0x1236A]={ category="lo", direction="l", }, - [0x12076]={ + [0x1236B]={ category="lo", direction="l", }, - [0x12077]={ + [0x1236C]={ category="lo", direction="l", }, - [0x12078]={ + [0x1236D]={ category="lo", direction="l", }, - [0x12079]={ + [0x1236E]={ category="lo", direction="l", }, - [0x1207A]={ + [0x1236F]={ category="lo", direction="l", }, - [0x1207B]={ + [0x12370]={ category="lo", direction="l", }, - [0x1207C]={ + [0x12371]={ category="lo", direction="l", }, - [0x1207D]={ + [0x12372]={ category="lo", direction="l", }, - [0x1207E]={ + [0x12373]={ category="lo", direction="l", }, - [0x1207F]={ + [0x12374]={ category="lo", direction="l", }, - [0x12080]={ + [0x12375]={ category="lo", direction="l", }, - [0x12081]={ + [0x12376]={ category="lo", direction="l", }, - [0x12082]={ + [0x12377]={ category="lo", direction="l", }, - [0x12083]={ + [0x12378]={ category="lo", direction="l", }, - [0x12084]={ + [0x12379]={ category="lo", direction="l", }, - [0x12085]={ + [0x1237A]={ category="lo", direction="l", }, - [0x12086]={ + [0x1237B]={ category="lo", direction="l", }, - [0x12087]={ + [0x1237C]={ category="lo", direction="l", }, - [0x12088]={ + [0x1237D]={ category="lo", direction="l", }, - [0x12089]={ + [0x1237E]={ category="lo", direction="l", }, - [0x1208A]={ + [0x1237F]={ category="lo", direction="l", }, - [0x1208B]={ + [0x12380]={ category="lo", direction="l", }, - [0x1208C]={ + [0x12381]={ category="lo", direction="l", }, - [0x1208D]={ + [0x12382]={ category="lo", direction="l", }, - [0x1208E]={ + [0x12383]={ category="lo", direction="l", }, - [0x1208F]={ + [0x12384]={ category="lo", direction="l", }, - [0x12090]={ + [0x12385]={ category="lo", direction="l", }, - [0x12091]={ + [0x12386]={ category="lo", direction="l", }, - [0x12092]={ + [0x12387]={ category="lo", direction="l", }, - [0x12093]={ + [0x12388]={ category="lo", direction="l", }, - [0x12094]={ + [0x12389]={ category="lo", direction="l", }, - [0x12095]={ + [0x1238A]={ category="lo", direction="l", }, - [0x12096]={ + [0x1238B]={ category="lo", direction="l", }, - [0x12097]={ + [0x1238C]={ category="lo", direction="l", }, - [0x12098]={ + [0x1238D]={ category="lo", direction="l", }, - [0x12099]={ + [0x1238E]={ category="lo", direction="l", }, - [0x1209A]={ + [0x1238F]={ category="lo", direction="l", }, - [0x1209B]={ + [0x12390]={ category="lo", direction="l", }, - [0x1209C]={ + [0x12391]={ category="lo", direction="l", }, - [0x1209D]={ + [0x12392]={ category="lo", direction="l", }, - [0x1209E]={ + [0x12393]={ category="lo", direction="l", }, - [0x1209F]={ + [0x12394]={ category="lo", direction="l", }, - [0x120A0]={ + [0x12395]={ category="lo", direction="l", }, - [0x120A1]={ + [0x12396]={ category="lo", direction="l", }, - [0x120A2]={ + [0x12397]={ category="lo", direction="l", }, - [0x120A3]={ + [0x12398]={ + category="lo", + direction="l", + }, + [0x12399]={ category="lo", direction="l", }, - [0x120A4]={ - category="lo", + [0x12400]={ + category="nl", + direction="l", + }, + [0x12401]={ + category="nl", + direction="l", + }, + [0x12402]={ + category="nl", + direction="l", + }, + [0x12403]={ + category="nl", + direction="l", + }, + [0x12404]={ + category="nl", + direction="l", + }, + [0x12405]={ + category="nl", + direction="l", + }, + [0x12406]={ + category="nl", + direction="l", + }, + [0x12407]={ + category="nl", + direction="l", + }, + [0x12408]={ + category="nl", + direction="l", + }, + [0x12409]={ + category="nl", + direction="l", + }, + [0x1240A]={ + category="nl", + direction="l", + }, + [0x1240B]={ + category="nl", + direction="l", + }, + [0x1240C]={ + category="nl", direction="l", }, - [0x120A5]={ - category="lo", + [0x1240D]={ + category="nl", direction="l", }, - [0x120A6]={ - category="lo", + [0x1240E]={ + category="nl", direction="l", }, - [0x120A7]={ - category="lo", + [0x1240F]={ + category="nl", direction="l", }, - [0x120A8]={ - category="lo", + [0x12410]={ + category="nl", direction="l", }, - [0x120A9]={ - category="lo", + [0x12411]={ + category="nl", direction="l", }, - [0x120AA]={ - category="lo", + [0x12412]={ + category="nl", direction="l", }, - [0x120AB]={ - category="lo", + [0x12413]={ + category="nl", direction="l", }, - [0x120AC]={ - category="lo", + [0x12414]={ + category="nl", direction="l", }, - [0x120AD]={ - category="lo", + [0x12415]={ + category="nl", direction="l", }, - [0x120AE]={ - category="lo", + [0x12416]={ + category="nl", direction="l", }, - [0x120AF]={ - category="lo", + [0x12417]={ + category="nl", direction="l", }, - [0x120B0]={ - category="lo", + [0x12418]={ + category="nl", direction="l", }, - [0x120B1]={ - category="lo", + [0x12419]={ + category="nl", direction="l", }, - [0x120B2]={ - category="lo", + [0x1241A]={ + category="nl", direction="l", }, - [0x120B3]={ - category="lo", + [0x1241B]={ + category="nl", direction="l", }, - [0x120B4]={ - category="lo", + [0x1241C]={ + category="nl", direction="l", }, - [0x120B5]={ - category="lo", + [0x1241D]={ + category="nl", direction="l", }, - [0x120B6]={ - category="lo", + [0x1241E]={ + category="nl", direction="l", }, - [0x120B7]={ - category="lo", + [0x1241F]={ + category="nl", direction="l", }, - [0x120B8]={ - category="lo", + [0x12420]={ + category="nl", direction="l", }, - [0x120B9]={ - category="lo", + [0x12421]={ + category="nl", direction="l", }, - [0x120BA]={ - category="lo", + [0x12422]={ + category="nl", direction="l", }, - [0x120BB]={ - category="lo", + [0x12423]={ + category="nl", direction="l", }, - [0x120BC]={ - category="lo", + [0x12424]={ + category="nl", direction="l", }, - [0x120BD]={ - category="lo", + [0x12425]={ + category="nl", direction="l", }, - [0x120BE]={ - category="lo", + [0x12426]={ + category="nl", direction="l", }, - [0x120BF]={ - category="lo", + [0x12427]={ + category="nl", direction="l", }, - [0x120C0]={ - category="lo", + [0x12428]={ + category="nl", direction="l", }, - [0x120C1]={ - category="lo", + [0x12429]={ + category="nl", direction="l", }, - [0x120C2]={ - category="lo", + [0x1242A]={ + category="nl", direction="l", }, - [0x120C3]={ - category="lo", + [0x1242B]={ + category="nl", direction="l", }, - [0x120C4]={ - category="lo", + [0x1242C]={ + category="nl", direction="l", }, - [0x120C5]={ - category="lo", + [0x1242D]={ + category="nl", direction="l", }, - [0x120C6]={ - category="lo", + [0x1242E]={ + category="nl", direction="l", }, - [0x120C7]={ - category="lo", + [0x1242F]={ + category="nl", direction="l", }, - [0x120C8]={ - category="lo", + [0x12430]={ + category="nl", direction="l", }, - [0x120C9]={ - category="lo", + [0x12431]={ + category="nl", direction="l", }, - [0x120CA]={ - category="lo", + [0x12432]={ + category="nl", direction="l", }, - [0x120CB]={ - category="lo", + [0x12433]={ + category="nl", direction="l", }, - [0x120CC]={ - category="lo", + [0x12434]={ + category="nl", direction="l", }, - [0x120CD]={ - category="lo", + [0x12435]={ + category="nl", direction="l", }, - [0x120CE]={ - category="lo", + [0x12436]={ + category="nl", direction="l", }, - [0x120CF]={ - category="lo", + [0x12437]={ + category="nl", direction="l", }, - [0x120D0]={ - category="lo", + [0x12438]={ + category="nl", direction="l", }, - [0x120D1]={ - category="lo", + [0x12439]={ + category="nl", direction="l", }, - [0x120D2]={ - category="lo", + [0x1243A]={ + category="nl", direction="l", }, - [0x120D3]={ - category="lo", + [0x1243B]={ + category="nl", direction="l", }, - [0x120D4]={ - category="lo", + [0x1243C]={ + category="nl", direction="l", }, - [0x120D5]={ - category="lo", + [0x1243D]={ + category="nl", direction="l", }, - [0x120D6]={ - category="lo", + [0x1243E]={ + category="nl", direction="l", }, - [0x120D7]={ - category="lo", + [0x1243F]={ + category="nl", direction="l", }, - [0x120D8]={ - category="lo", + [0x12440]={ + category="nl", direction="l", }, - [0x120D9]={ - category="lo", + [0x12441]={ + category="nl", direction="l", }, - [0x120DA]={ - category="lo", + [0x12442]={ + category="nl", direction="l", }, - [0x120DB]={ - category="lo", + [0x12443]={ + category="nl", direction="l", }, - [0x120DC]={ - category="lo", + [0x12444]={ + category="nl", direction="l", }, - [0x120DD]={ - category="lo", + [0x12445]={ + category="nl", direction="l", }, - [0x120DE]={ - category="lo", + [0x12446]={ + category="nl", direction="l", }, - [0x120DF]={ - category="lo", + [0x12447]={ + category="nl", direction="l", }, - [0x120E0]={ - category="lo", + [0x12448]={ + category="nl", direction="l", }, - [0x120E1]={ - category="lo", + [0x12449]={ + category="nl", direction="l", }, - [0x120E2]={ - category="lo", + [0x1244A]={ + category="nl", direction="l", }, - [0x120E3]={ - category="lo", + [0x1244B]={ + category="nl", direction="l", }, - [0x120E4]={ - category="lo", + [0x1244C]={ + category="nl", direction="l", }, - [0x120E5]={ - category="lo", + [0x1244D]={ + category="nl", direction="l", }, - [0x120E6]={ - category="lo", + [0x1244E]={ + category="nl", direction="l", }, - [0x120E7]={ - category="lo", + [0x1244F]={ + category="nl", direction="l", }, - [0x120E8]={ - category="lo", + [0x12450]={ + category="nl", direction="l", }, - [0x120E9]={ - category="lo", + [0x12451]={ + category="nl", direction="l", }, - [0x120EA]={ - category="lo", + [0x12452]={ + category="nl", direction="l", }, - [0x120EB]={ - category="lo", + [0x12453]={ + category="nl", direction="l", }, - [0x120EC]={ - category="lo", + [0x12454]={ + category="nl", direction="l", }, - [0x120ED]={ - category="lo", + [0x12455]={ + category="nl", direction="l", }, - [0x120EE]={ - category="lo", + [0x12456]={ + category="nl", direction="l", }, - [0x120EF]={ - category="lo", + [0x12457]={ + category="nl", direction="l", }, - [0x120F0]={ - category="lo", + [0x12458]={ + category="nl", direction="l", }, - [0x120F1]={ - category="lo", + [0x12459]={ + category="nl", direction="l", }, - [0x120F2]={ - category="lo", + [0x1245A]={ + category="nl", direction="l", }, - [0x120F3]={ - category="lo", + [0x1245B]={ + category="nl", direction="l", }, - [0x120F4]={ - category="lo", + [0x1245C]={ + category="nl", direction="l", }, - [0x120F5]={ - category="lo", + [0x1245D]={ + category="nl", direction="l", }, - [0x120F6]={ - category="lo", + [0x1245E]={ + category="nl", direction="l", }, - [0x120F7]={ - category="lo", + [0x1245F]={ + category="nl", direction="l", }, - [0x120F8]={ - category="lo", + [0x12460]={ + category="nl", direction="l", }, - [0x120F9]={ - category="lo", + [0x12461]={ + category="nl", direction="l", }, - [0x120FA]={ - category="lo", + [0x12462]={ + category="nl", direction="l", }, - [0x120FB]={ - category="lo", + [0x12463]={ + category="nl", direction="l", }, - [0x120FC]={ - category="lo", + [0x12464]={ + category="nl", direction="l", }, - [0x120FD]={ - category="lo", + [0x12465]={ + category="nl", direction="l", }, - [0x120FE]={ - category="lo", + [0x12466]={ + category="nl", direction="l", }, - [0x120FF]={ - category="lo", + [0x12467]={ + category="nl", direction="l", }, - [0x12100]={ - category="lo", + [0x12468]={ + category="nl", direction="l", }, - [0x12101]={ - category="lo", + [0x12469]={ + category="nl", direction="l", }, - [0x12102]={ - category="lo", + [0x1246A]={ + category="nl", direction="l", }, - [0x12103]={ - category="lo", + [0x1246B]={ + category="nl", direction="l", }, - [0x12104]={ - category="lo", + [0x1246C]={ + category="nl", direction="l", }, - [0x12105]={ - category="lo", + [0x1246D]={ + category="nl", direction="l", }, - [0x12106]={ - category="lo", + [0x1246E]={ + category="nl", direction="l", }, - [0x12107]={ - category="lo", + [0x12470]={ + category="po", direction="l", }, - [0x12108]={ - category="lo", + [0x12471]={ + category="po", direction="l", }, - [0x12109]={ - category="lo", + [0x12472]={ + category="po", direction="l", }, - [0x1210A]={ - category="lo", + [0x12473]={ + category="po", direction="l", }, - [0x1210B]={ - category="lo", + [0x12474]={ + category="po", direction="l", }, - [0x1210C]={ + [0x12480]={ category="lo", direction="l", }, - [0x1210D]={ + [0x12481]={ category="lo", direction="l", }, - [0x1210E]={ + [0x12482]={ category="lo", direction="l", }, - [0x1210F]={ + [0x12483]={ category="lo", direction="l", }, - [0x12110]={ + [0x12484]={ category="lo", direction="l", }, - [0x12111]={ + [0x12485]={ category="lo", direction="l", }, - [0x12112]={ + [0x12486]={ category="lo", direction="l", }, - [0x12113]={ + [0x12487]={ category="lo", direction="l", }, - [0x12114]={ + [0x12488]={ category="lo", direction="l", }, - [0x12115]={ + [0x12489]={ category="lo", direction="l", }, - [0x12116]={ + [0x1248A]={ category="lo", direction="l", }, - [0x12117]={ + [0x1248B]={ category="lo", direction="l", }, - [0x12118]={ + [0x1248C]={ category="lo", direction="l", }, - [0x12119]={ + [0x1248D]={ category="lo", direction="l", }, - [0x1211A]={ + [0x1248E]={ category="lo", direction="l", }, - [0x1211B]={ + [0x1248F]={ category="lo", direction="l", }, - [0x1211C]={ + [0x12490]={ category="lo", direction="l", }, - [0x1211D]={ + [0x12491]={ category="lo", direction="l", }, - [0x1211E]={ + [0x12492]={ category="lo", direction="l", }, - [0x1211F]={ + [0x12493]={ category="lo", direction="l", }, - [0x12120]={ + [0x12494]={ category="lo", direction="l", }, - [0x12121]={ + [0x12495]={ category="lo", direction="l", }, - [0x12122]={ + [0x12496]={ category="lo", direction="l", }, - [0x12123]={ + [0x12497]={ category="lo", direction="l", }, - [0x12124]={ + [0x12498]={ category="lo", direction="l", }, - [0x12125]={ + [0x12499]={ category="lo", direction="l", }, - [0x12126]={ + [0x1249A]={ category="lo", direction="l", }, - [0x12127]={ + [0x1249B]={ category="lo", direction="l", }, - [0x12128]={ + [0x1249C]={ category="lo", direction="l", }, - [0x12129]={ + [0x1249D]={ category="lo", direction="l", }, - [0x1212A]={ + [0x1249E]={ category="lo", direction="l", }, - [0x1212B]={ + [0x1249F]={ category="lo", direction="l", }, - [0x1212C]={ + [0x124A0]={ category="lo", direction="l", }, - [0x1212D]={ + [0x124A1]={ category="lo", direction="l", }, - [0x1212E]={ + [0x124A2]={ category="lo", direction="l", }, - [0x1212F]={ + [0x124A3]={ category="lo", direction="l", }, - [0x12130]={ + [0x124A4]={ category="lo", direction="l", }, - [0x12131]={ + [0x124A5]={ category="lo", direction="l", }, - [0x12132]={ + [0x124A6]={ category="lo", direction="l", }, - [0x12133]={ + [0x124A7]={ category="lo", direction="l", }, - [0x12134]={ + [0x124A8]={ category="lo", direction="l", }, - [0x12135]={ + [0x124A9]={ category="lo", direction="l", }, - [0x12136]={ + [0x124AA]={ category="lo", direction="l", }, - [0x12137]={ + [0x124AB]={ category="lo", direction="l", }, - [0x12138]={ + [0x124AC]={ category="lo", direction="l", }, - [0x12139]={ + [0x124AD]={ category="lo", direction="l", }, - [0x1213A]={ + [0x124AE]={ category="lo", direction="l", }, - [0x1213B]={ + [0x124AF]={ category="lo", direction="l", }, - [0x1213C]={ + [0x124B0]={ category="lo", direction="l", }, - [0x1213D]={ + [0x124B1]={ category="lo", direction="l", }, - [0x1213E]={ + [0x124B2]={ category="lo", direction="l", }, - [0x1213F]={ + [0x124B3]={ category="lo", direction="l", }, - [0x12140]={ + [0x124B4]={ category="lo", direction="l", }, - [0x12141]={ + [0x124B5]={ category="lo", direction="l", }, - [0x12142]={ + [0x124B6]={ category="lo", direction="l", }, - [0x12143]={ + [0x124B7]={ category="lo", direction="l", }, - [0x12144]={ + [0x124B8]={ category="lo", direction="l", }, - [0x12145]={ + [0x124B9]={ category="lo", direction="l", }, - [0x12146]={ + [0x124BA]={ category="lo", direction="l", }, - [0x12147]={ + [0x124BB]={ category="lo", direction="l", }, - [0x12148]={ + [0x124BC]={ category="lo", direction="l", }, - [0x12149]={ + [0x124BD]={ category="lo", direction="l", }, - [0x1214A]={ + [0x124BE]={ category="lo", direction="l", }, - [0x1214B]={ + [0x124BF]={ category="lo", direction="l", }, - [0x1214C]={ + [0x124C0]={ category="lo", direction="l", }, - [0x1214D]={ + [0x124C1]={ category="lo", direction="l", }, - [0x1214E]={ + [0x124C2]={ category="lo", direction="l", }, - [0x1214F]={ + [0x124C3]={ category="lo", direction="l", }, - [0x12150]={ + [0x124C4]={ category="lo", direction="l", }, - [0x12151]={ + [0x124C5]={ category="lo", direction="l", }, - [0x12152]={ + [0x124C6]={ category="lo", direction="l", }, - [0x12153]={ + [0x124C7]={ category="lo", direction="l", }, - [0x12154]={ + [0x124C8]={ category="lo", direction="l", }, - [0x12155]={ + [0x124C9]={ category="lo", direction="l", }, - [0x12156]={ + [0x124CA]={ category="lo", direction="l", }, - [0x12157]={ + [0x124CB]={ category="lo", direction="l", }, - [0x12158]={ + [0x124CC]={ category="lo", direction="l", }, - [0x12159]={ + [0x124CD]={ category="lo", direction="l", }, - [0x1215A]={ + [0x124CE]={ category="lo", direction="l", }, - [0x1215B]={ + [0x124CF]={ category="lo", direction="l", }, - [0x1215C]={ + [0x124D0]={ category="lo", direction="l", }, - [0x1215D]={ + [0x124D1]={ category="lo", direction="l", }, - [0x1215E]={ + [0x124D2]={ category="lo", direction="l", }, - [0x1215F]={ + [0x124D3]={ category="lo", direction="l", }, - [0x12160]={ + [0x124D4]={ category="lo", direction="l", }, - [0x12161]={ + [0x124D5]={ category="lo", direction="l", }, - [0x12162]={ + [0x124D6]={ category="lo", direction="l", }, - [0x12163]={ + [0x124D7]={ category="lo", direction="l", }, - [0x12164]={ + [0x124D8]={ category="lo", direction="l", }, - [0x12165]={ + [0x124D9]={ category="lo", direction="l", }, - [0x12166]={ + [0x124DA]={ category="lo", direction="l", }, - [0x12167]={ + [0x124DB]={ category="lo", direction="l", }, - [0x12168]={ + [0x124DC]={ category="lo", direction="l", }, - [0x12169]={ + [0x124DD]={ category="lo", direction="l", }, - [0x1216A]={ + [0x124DE]={ category="lo", direction="l", }, - [0x1216B]={ + [0x124DF]={ category="lo", direction="l", }, - [0x1216C]={ + [0x124E0]={ category="lo", direction="l", }, - [0x1216D]={ + [0x124E1]={ category="lo", direction="l", }, - [0x1216E]={ + [0x124E2]={ category="lo", direction="l", }, - [0x1216F]={ + [0x124E3]={ category="lo", direction="l", }, - [0x12170]={ + [0x124E4]={ category="lo", direction="l", }, - [0x12171]={ + [0x124E5]={ category="lo", direction="l", }, - [0x12172]={ + [0x124E6]={ category="lo", direction="l", }, - [0x12173]={ + [0x124E7]={ category="lo", direction="l", }, - [0x12174]={ + [0x124E8]={ category="lo", direction="l", }, - [0x12175]={ + [0x124E9]={ category="lo", direction="l", }, - [0x12176]={ + [0x124EA]={ category="lo", direction="l", }, - [0x12177]={ + [0x124EB]={ category="lo", direction="l", }, - [0x12178]={ + [0x124EC]={ category="lo", direction="l", }, - [0x12179]={ + [0x124ED]={ category="lo", direction="l", }, - [0x1217A]={ + [0x124EE]={ category="lo", direction="l", }, - [0x1217B]={ + [0x124EF]={ category="lo", direction="l", }, - [0x1217C]={ + [0x124F0]={ category="lo", direction="l", }, - [0x1217D]={ + [0x124F1]={ category="lo", direction="l", }, - [0x1217E]={ + [0x124F2]={ category="lo", direction="l", }, - [0x1217F]={ + [0x124F3]={ category="lo", direction="l", }, - [0x12180]={ + [0x124F4]={ category="lo", direction="l", }, - [0x12181]={ + [0x124F5]={ category="lo", direction="l", }, - [0x12182]={ + [0x124F6]={ category="lo", direction="l", }, - [0x12183]={ + [0x124F7]={ category="lo", direction="l", }, - [0x12184]={ + [0x124F8]={ category="lo", direction="l", }, - [0x12185]={ + [0x124F9]={ category="lo", direction="l", }, - [0x12186]={ + [0x124FA]={ category="lo", direction="l", }, - [0x12187]={ + [0x124FB]={ category="lo", direction="l", }, - [0x12188]={ + [0x124FC]={ category="lo", direction="l", }, - [0x12189]={ + [0x124FD]={ category="lo", direction="l", }, - [0x1218A]={ + [0x124FE]={ category="lo", direction="l", }, - [0x1218B]={ + [0x124FF]={ category="lo", direction="l", }, - [0x1218C]={ + [0x12500]={ category="lo", direction="l", }, - [0x1218D]={ + [0x12501]={ category="lo", direction="l", }, - [0x1218E]={ + [0x12502]={ category="lo", direction="l", }, - [0x1218F]={ + [0x12503]={ category="lo", direction="l", }, - [0x12190]={ + [0x12504]={ category="lo", direction="l", }, - [0x12191]={ + [0x12505]={ category="lo", direction="l", }, - [0x12192]={ + [0x12506]={ category="lo", direction="l", }, - [0x12193]={ + [0x12507]={ category="lo", direction="l", }, - [0x12194]={ + [0x12508]={ category="lo", direction="l", }, - [0x12195]={ + [0x12509]={ category="lo", direction="l", }, - [0x12196]={ + [0x1250A]={ category="lo", direction="l", }, - [0x12197]={ + [0x1250B]={ category="lo", direction="l", }, - [0x12198]={ + [0x1250C]={ category="lo", direction="l", }, - [0x12199]={ + [0x1250D]={ category="lo", direction="l", }, - [0x1219A]={ + [0x1250E]={ category="lo", direction="l", }, - [0x1219B]={ + [0x1250F]={ category="lo", direction="l", }, - [0x1219C]={ + [0x12510]={ category="lo", direction="l", }, - [0x1219D]={ + [0x12511]={ category="lo", direction="l", }, - [0x1219E]={ + [0x12512]={ category="lo", direction="l", }, - [0x1219F]={ + [0x12513]={ category="lo", direction="l", }, - [0x121A0]={ + [0x12514]={ category="lo", direction="l", }, - [0x121A1]={ + [0x12515]={ category="lo", direction="l", }, - [0x121A2]={ + [0x12516]={ category="lo", direction="l", }, - [0x121A3]={ + [0x12517]={ category="lo", direction="l", }, - [0x121A4]={ + [0x12518]={ category="lo", direction="l", }, - [0x121A5]={ + [0x12519]={ category="lo", direction="l", }, - [0x121A6]={ + [0x1251A]={ category="lo", direction="l", }, - [0x121A7]={ + [0x1251B]={ category="lo", direction="l", }, - [0x121A8]={ + [0x1251C]={ category="lo", direction="l", }, - [0x121A9]={ + [0x1251D]={ category="lo", direction="l", }, - [0x121AA]={ + [0x1251E]={ category="lo", direction="l", }, - [0x121AB]={ + [0x1251F]={ category="lo", direction="l", }, - [0x121AC]={ + [0x12520]={ category="lo", direction="l", }, - [0x121AD]={ + [0x12521]={ category="lo", direction="l", }, - [0x121AE]={ + [0x12522]={ category="lo", direction="l", }, - [0x121AF]={ + [0x12523]={ category="lo", direction="l", }, - [0x121B0]={ + [0x12524]={ category="lo", direction="l", }, - [0x121B1]={ + [0x12525]={ category="lo", direction="l", }, - [0x121B2]={ + [0x12526]={ category="lo", direction="l", }, - [0x121B3]={ + [0x12527]={ category="lo", direction="l", }, - [0x121B4]={ + [0x12528]={ category="lo", direction="l", }, - [0x121B5]={ + [0x12529]={ category="lo", direction="l", }, - [0x121B6]={ + [0x1252A]={ category="lo", direction="l", }, - [0x121B7]={ + [0x1252B]={ category="lo", direction="l", }, - [0x121B8]={ + [0x1252C]={ category="lo", direction="l", }, - [0x121B9]={ + [0x1252D]={ category="lo", direction="l", }, - [0x121BA]={ + [0x1252E]={ category="lo", direction="l", }, - [0x121BB]={ + [0x1252F]={ category="lo", direction="l", }, - [0x121BC]={ + [0x12530]={ category="lo", direction="l", }, - [0x121BD]={ + [0x12531]={ category="lo", direction="l", }, - [0x121BE]={ + [0x12532]={ category="lo", direction="l", }, - [0x121BF]={ + [0x12533]={ category="lo", direction="l", }, - [0x121C0]={ + [0x12534]={ category="lo", direction="l", }, - [0x121C1]={ + [0x12535]={ category="lo", direction="l", }, - [0x121C2]={ + [0x12536]={ category="lo", direction="l", }, - [0x121C3]={ + [0x12537]={ category="lo", direction="l", }, - [0x121C4]={ + [0x12538]={ category="lo", direction="l", }, - [0x121C5]={ + [0x12539]={ category="lo", direction="l", }, - [0x121C6]={ + [0x1253A]={ category="lo", direction="l", }, - [0x121C7]={ + [0x1253B]={ category="lo", direction="l", }, - [0x121C8]={ + [0x1253C]={ category="lo", direction="l", }, - [0x121C9]={ + [0x1253D]={ category="lo", direction="l", }, - [0x121CA]={ + [0x1253E]={ category="lo", direction="l", }, - [0x121CB]={ + [0x1253F]={ category="lo", direction="l", }, - [0x121CC]={ + [0x12540]={ category="lo", direction="l", }, - [0x121CD]={ + [0x12541]={ category="lo", direction="l", }, - [0x121CE]={ + [0x12542]={ category="lo", direction="l", }, - [0x121CF]={ + [0x12543]={ category="lo", direction="l", }, - [0x121D0]={ + [0x13000]={ category="lo", direction="l", }, - [0x121D1]={ + [0x13001]={ category="lo", direction="l", }, - [0x121D2]={ + [0x13002]={ category="lo", direction="l", }, - [0x121D3]={ + [0x13003]={ category="lo", direction="l", }, - [0x121D4]={ + [0x13004]={ category="lo", direction="l", }, - [0x121D5]={ + [0x13005]={ category="lo", direction="l", }, - [0x121D6]={ + [0x13006]={ category="lo", direction="l", }, - [0x121D7]={ + [0x13007]={ category="lo", direction="l", }, - [0x121D8]={ + [0x13008]={ category="lo", direction="l", }, - [0x121D9]={ + [0x13009]={ category="lo", direction="l", }, - [0x121DA]={ + [0x1300A]={ category="lo", direction="l", }, - [0x121DB]={ + [0x1300B]={ category="lo", direction="l", }, - [0x121DC]={ + [0x1300C]={ category="lo", direction="l", }, - [0x121DD]={ + [0x1300D]={ category="lo", direction="l", }, - [0x121DE]={ + [0x1300E]={ category="lo", direction="l", }, - [0x121DF]={ + [0x1300F]={ category="lo", direction="l", }, - [0x121E0]={ + [0x13010]={ category="lo", direction="l", }, - [0x121E1]={ + [0x13011]={ category="lo", direction="l", }, - [0x121E2]={ + [0x13012]={ category="lo", direction="l", }, - [0x121E3]={ + [0x13013]={ category="lo", direction="l", }, - [0x121E4]={ + [0x13014]={ category="lo", direction="l", }, - [0x121E5]={ + [0x13015]={ category="lo", direction="l", }, - [0x121E6]={ + [0x13016]={ category="lo", direction="l", }, - [0x121E7]={ + [0x13017]={ category="lo", direction="l", }, - [0x121E8]={ + [0x13018]={ category="lo", direction="l", }, - [0x121E9]={ + [0x13019]={ category="lo", direction="l", }, - [0x121EA]={ + [0x1301A]={ category="lo", direction="l", }, - [0x121EB]={ + [0x1301B]={ category="lo", direction="l", }, - [0x121EC]={ + [0x1301C]={ category="lo", direction="l", }, - [0x121ED]={ + [0x1301D]={ category="lo", direction="l", }, - [0x121EE]={ + [0x1301E]={ category="lo", direction="l", }, - [0x121EF]={ + [0x1301F]={ category="lo", direction="l", }, - [0x121F0]={ + [0x13020]={ category="lo", direction="l", }, - [0x121F1]={ + [0x13021]={ category="lo", direction="l", }, - [0x121F2]={ + [0x13022]={ category="lo", direction="l", }, - [0x121F3]={ + [0x13023]={ category="lo", direction="l", }, - [0x121F4]={ + [0x13024]={ category="lo", direction="l", }, - [0x121F5]={ + [0x13025]={ category="lo", direction="l", }, - [0x121F6]={ + [0x13026]={ category="lo", direction="l", }, - [0x121F7]={ + [0x13027]={ category="lo", direction="l", }, - [0x121F8]={ + [0x13028]={ category="lo", direction="l", }, - [0x121F9]={ + [0x13029]={ category="lo", direction="l", }, - [0x121FA]={ + [0x1302A]={ category="lo", direction="l", }, - [0x121FB]={ + [0x1302B]={ category="lo", direction="l", }, - [0x121FC]={ + [0x1302C]={ category="lo", direction="l", }, - [0x121FD]={ + [0x1302D]={ category="lo", direction="l", }, - [0x121FE]={ + [0x1302E]={ category="lo", direction="l", }, - [0x121FF]={ + [0x1302F]={ category="lo", direction="l", }, - [0x12200]={ + [0x13030]={ category="lo", direction="l", }, - [0x12201]={ + [0x13031]={ category="lo", direction="l", }, - [0x12202]={ + [0x13032]={ category="lo", direction="l", }, - [0x12203]={ + [0x13033]={ category="lo", direction="l", }, - [0x12204]={ + [0x13034]={ category="lo", direction="l", }, - [0x12205]={ + [0x13035]={ category="lo", direction="l", }, - [0x12206]={ + [0x13036]={ category="lo", direction="l", }, - [0x12207]={ + [0x13037]={ category="lo", direction="l", }, - [0x12208]={ + [0x13038]={ category="lo", direction="l", }, - [0x12209]={ + [0x13039]={ category="lo", direction="l", }, - [0x1220A]={ + [0x1303A]={ category="lo", direction="l", }, - [0x1220B]={ + [0x1303B]={ category="lo", direction="l", }, - [0x1220C]={ + [0x1303C]={ category="lo", direction="l", }, - [0x1220D]={ + [0x1303D]={ category="lo", direction="l", }, - [0x1220E]={ + [0x1303E]={ category="lo", direction="l", }, - [0x1220F]={ + [0x1303F]={ category="lo", direction="l", }, - [0x12210]={ + [0x13040]={ category="lo", direction="l", }, - [0x12211]={ + [0x13041]={ category="lo", direction="l", }, - [0x12212]={ + [0x13042]={ category="lo", direction="l", }, - [0x12213]={ + [0x13043]={ category="lo", direction="l", }, - [0x12214]={ + [0x13044]={ category="lo", direction="l", }, - [0x12215]={ + [0x13045]={ category="lo", direction="l", }, - [0x12216]={ + [0x13046]={ category="lo", direction="l", }, - [0x12217]={ + [0x13047]={ category="lo", direction="l", }, - [0x12218]={ + [0x13048]={ category="lo", direction="l", }, - [0x12219]={ + [0x13049]={ category="lo", direction="l", }, - [0x1221A]={ + [0x1304A]={ category="lo", direction="l", }, - [0x1221B]={ + [0x1304B]={ category="lo", direction="l", }, - [0x1221C]={ + [0x1304C]={ category="lo", direction="l", }, - [0x1221D]={ + [0x1304D]={ category="lo", direction="l", }, - [0x1221E]={ + [0x1304E]={ category="lo", direction="l", }, - [0x1221F]={ + [0x1304F]={ category="lo", direction="l", }, - [0x12220]={ + [0x13050]={ category="lo", direction="l", }, - [0x12221]={ + [0x13051]={ category="lo", direction="l", }, - [0x12222]={ + [0x13052]={ category="lo", direction="l", }, - [0x12223]={ + [0x13053]={ category="lo", direction="l", }, - [0x12224]={ + [0x13054]={ category="lo", direction="l", }, - [0x12225]={ + [0x13055]={ category="lo", direction="l", }, - [0x12226]={ + [0x13056]={ category="lo", direction="l", }, - [0x12227]={ + [0x13057]={ category="lo", direction="l", }, - [0x12228]={ + [0x13058]={ category="lo", direction="l", }, - [0x12229]={ + [0x13059]={ category="lo", direction="l", }, - [0x1222A]={ + [0x1305A]={ category="lo", direction="l", }, - [0x1222B]={ + [0x1305B]={ category="lo", direction="l", }, - [0x1222C]={ + [0x1305C]={ category="lo", direction="l", }, - [0x1222D]={ + [0x1305D]={ category="lo", direction="l", }, - [0x1222E]={ + [0x1305E]={ category="lo", direction="l", }, - [0x1222F]={ + [0x1305F]={ category="lo", direction="l", }, - [0x12230]={ + [0x13060]={ category="lo", direction="l", }, - [0x12231]={ + [0x13061]={ category="lo", direction="l", }, - [0x12232]={ + [0x13062]={ category="lo", direction="l", }, - [0x12233]={ + [0x13063]={ category="lo", direction="l", }, - [0x12234]={ + [0x13064]={ category="lo", direction="l", }, - [0x12235]={ + [0x13065]={ category="lo", direction="l", }, - [0x12236]={ + [0x13066]={ category="lo", direction="l", }, - [0x12237]={ + [0x13067]={ category="lo", direction="l", }, - [0x12238]={ + [0x13068]={ category="lo", direction="l", }, - [0x12239]={ + [0x13069]={ category="lo", direction="l", }, - [0x1223A]={ + [0x1306A]={ category="lo", direction="l", }, - [0x1223B]={ + [0x1306B]={ category="lo", direction="l", }, - [0x1223C]={ + [0x1306C]={ category="lo", direction="l", }, - [0x1223D]={ + [0x1306D]={ category="lo", direction="l", }, - [0x1223E]={ + [0x1306E]={ category="lo", direction="l", }, - [0x1223F]={ + [0x1306F]={ category="lo", direction="l", }, - [0x12240]={ + [0x13070]={ category="lo", direction="l", }, - [0x12241]={ + [0x13071]={ category="lo", direction="l", }, - [0x12242]={ + [0x13072]={ category="lo", direction="l", }, - [0x12243]={ + [0x13073]={ category="lo", direction="l", }, - [0x12244]={ + [0x13074]={ category="lo", direction="l", }, - [0x12245]={ + [0x13075]={ category="lo", direction="l", }, - [0x12246]={ + [0x13076]={ category="lo", direction="l", }, - [0x12247]={ + [0x13077]={ category="lo", direction="l", }, - [0x12248]={ + [0x13078]={ category="lo", direction="l", }, - [0x12249]={ + [0x13079]={ category="lo", direction="l", }, - [0x1224A]={ + [0x1307A]={ category="lo", direction="l", }, - [0x1224B]={ + [0x1307B]={ category="lo", direction="l", }, - [0x1224C]={ + [0x1307C]={ category="lo", direction="l", }, - [0x1224D]={ + [0x1307D]={ category="lo", direction="l", }, - [0x1224E]={ + [0x1307E]={ category="lo", direction="l", }, - [0x1224F]={ + [0x1307F]={ category="lo", direction="l", }, - [0x12250]={ + [0x13080]={ category="lo", direction="l", }, - [0x12251]={ + [0x13081]={ category="lo", direction="l", }, - [0x12252]={ + [0x13082]={ category="lo", direction="l", }, - [0x12253]={ + [0x13083]={ category="lo", direction="l", }, - [0x12254]={ + [0x13084]={ category="lo", direction="l", }, - [0x12255]={ + [0x13085]={ category="lo", direction="l", }, - [0x12256]={ + [0x13086]={ category="lo", direction="l", }, - [0x12257]={ + [0x13087]={ category="lo", direction="l", }, - [0x12258]={ + [0x13088]={ category="lo", direction="l", }, - [0x12259]={ + [0x13089]={ category="lo", direction="l", }, - [0x1225A]={ + [0x1308A]={ category="lo", direction="l", }, - [0x1225B]={ + [0x1308B]={ category="lo", direction="l", }, - [0x1225C]={ + [0x1308C]={ category="lo", direction="l", }, - [0x1225D]={ + [0x1308D]={ category="lo", direction="l", }, - [0x1225E]={ + [0x1308E]={ category="lo", direction="l", }, - [0x1225F]={ + [0x1308F]={ category="lo", direction="l", }, - [0x12260]={ + [0x13090]={ category="lo", direction="l", }, - [0x12261]={ + [0x13091]={ category="lo", direction="l", }, - [0x12262]={ + [0x13092]={ category="lo", direction="l", }, - [0x12263]={ + [0x13093]={ category="lo", direction="l", }, - [0x12264]={ + [0x13094]={ category="lo", direction="l", }, - [0x12265]={ + [0x13095]={ category="lo", direction="l", }, - [0x12266]={ + [0x13096]={ category="lo", direction="l", }, - [0x12267]={ + [0x13097]={ category="lo", direction="l", }, - [0x12268]={ + [0x13098]={ category="lo", direction="l", }, - [0x12269]={ + [0x13099]={ category="lo", direction="l", }, - [0x1226A]={ + [0x1309A]={ category="lo", direction="l", }, - [0x1226B]={ + [0x1309B]={ category="lo", direction="l", }, - [0x1226C]={ + [0x1309C]={ category="lo", direction="l", }, - [0x1226D]={ + [0x1309D]={ category="lo", direction="l", }, - [0x1226E]={ + [0x1309E]={ category="lo", direction="l", }, - [0x1226F]={ + [0x1309F]={ category="lo", direction="l", }, - [0x12270]={ + [0x130A0]={ category="lo", direction="l", }, - [0x12271]={ + [0x130A1]={ category="lo", direction="l", }, - [0x12272]={ + [0x130A2]={ category="lo", direction="l", }, - [0x12273]={ + [0x130A3]={ category="lo", direction="l", }, - [0x12274]={ + [0x130A4]={ category="lo", direction="l", }, - [0x12275]={ + [0x130A5]={ category="lo", direction="l", }, - [0x12276]={ + [0x130A6]={ category="lo", direction="l", }, - [0x12277]={ + [0x130A7]={ category="lo", direction="l", }, - [0x12278]={ + [0x130A8]={ category="lo", direction="l", }, - [0x12279]={ + [0x130A9]={ category="lo", direction="l", }, - [0x1227A]={ + [0x130AA]={ category="lo", direction="l", }, - [0x1227B]={ + [0x130AB]={ category="lo", direction="l", }, - [0x1227C]={ + [0x130AC]={ category="lo", direction="l", }, - [0x1227D]={ + [0x130AD]={ category="lo", direction="l", }, - [0x1227E]={ + [0x130AE]={ category="lo", direction="l", }, - [0x1227F]={ + [0x130AF]={ category="lo", direction="l", }, - [0x12280]={ + [0x130B0]={ category="lo", direction="l", }, - [0x12281]={ + [0x130B1]={ category="lo", direction="l", }, - [0x12282]={ + [0x130B2]={ category="lo", direction="l", }, - [0x12283]={ + [0x130B3]={ category="lo", direction="l", }, - [0x12284]={ + [0x130B4]={ category="lo", direction="l", }, - [0x12285]={ + [0x130B5]={ category="lo", direction="l", }, - [0x12286]={ + [0x130B6]={ category="lo", direction="l", }, - [0x12287]={ + [0x130B7]={ category="lo", direction="l", }, - [0x12288]={ + [0x130B8]={ category="lo", direction="l", }, - [0x12289]={ + [0x130B9]={ category="lo", direction="l", }, - [0x1228A]={ + [0x130BA]={ category="lo", direction="l", }, - [0x1228B]={ + [0x130BB]={ category="lo", direction="l", }, - [0x1228C]={ + [0x130BC]={ category="lo", direction="l", }, - [0x1228D]={ + [0x130BD]={ category="lo", direction="l", }, - [0x1228E]={ + [0x130BE]={ category="lo", direction="l", }, - [0x1228F]={ + [0x130BF]={ category="lo", direction="l", }, - [0x12290]={ + [0x130C0]={ category="lo", direction="l", }, - [0x12291]={ + [0x130C1]={ category="lo", direction="l", }, - [0x12292]={ + [0x130C2]={ category="lo", direction="l", }, - [0x12293]={ + [0x130C3]={ category="lo", direction="l", }, - [0x12294]={ + [0x130C4]={ category="lo", direction="l", }, - [0x12295]={ + [0x130C5]={ category="lo", direction="l", }, - [0x12296]={ + [0x130C6]={ category="lo", direction="l", }, - [0x12297]={ + [0x130C7]={ category="lo", direction="l", }, - [0x12298]={ + [0x130C8]={ category="lo", direction="l", }, - [0x12299]={ + [0x130C9]={ category="lo", direction="l", }, - [0x1229A]={ + [0x130CA]={ category="lo", direction="l", }, - [0x1229B]={ + [0x130CB]={ category="lo", direction="l", }, - [0x1229C]={ + [0x130CC]={ category="lo", direction="l", }, - [0x1229D]={ + [0x130CD]={ category="lo", direction="l", }, - [0x1229E]={ + [0x130CE]={ category="lo", direction="l", }, - [0x1229F]={ + [0x130CF]={ category="lo", direction="l", }, - [0x122A0]={ + [0x130D0]={ category="lo", direction="l", }, - [0x122A1]={ + [0x130D1]={ category="lo", direction="l", }, - [0x122A2]={ + [0x130D2]={ category="lo", direction="l", }, - [0x122A3]={ + [0x130D3]={ category="lo", direction="l", }, - [0x122A4]={ + [0x130D4]={ category="lo", direction="l", }, - [0x122A5]={ + [0x130D5]={ category="lo", direction="l", }, - [0x122A6]={ + [0x130D6]={ category="lo", direction="l", }, - [0x122A7]={ + [0x130D7]={ category="lo", direction="l", }, - [0x122A8]={ + [0x130D8]={ category="lo", direction="l", }, - [0x122A9]={ + [0x130D9]={ category="lo", direction="l", }, - [0x122AA]={ + [0x130DA]={ category="lo", direction="l", }, - [0x122AB]={ + [0x130DB]={ category="lo", direction="l", }, - [0x122AC]={ + [0x130DC]={ category="lo", direction="l", }, - [0x122AD]={ + [0x130DD]={ category="lo", direction="l", }, - [0x122AE]={ + [0x130DE]={ category="lo", direction="l", }, - [0x122AF]={ + [0x130DF]={ category="lo", direction="l", }, - [0x122B0]={ + [0x130E0]={ category="lo", direction="l", }, - [0x122B1]={ + [0x130E1]={ category="lo", direction="l", }, - [0x122B2]={ + [0x130E2]={ category="lo", direction="l", }, - [0x122B3]={ + [0x130E3]={ category="lo", direction="l", }, - [0x122B4]={ + [0x130E4]={ category="lo", direction="l", }, - [0x122B5]={ + [0x130E5]={ category="lo", direction="l", }, - [0x122B6]={ + [0x130E6]={ category="lo", direction="l", }, - [0x122B7]={ + [0x130E7]={ category="lo", direction="l", }, - [0x122B8]={ + [0x130E8]={ category="lo", direction="l", }, - [0x122B9]={ + [0x130E9]={ category="lo", direction="l", }, - [0x122BA]={ + [0x130EA]={ category="lo", direction="l", }, - [0x122BB]={ + [0x130EB]={ category="lo", direction="l", }, - [0x122BC]={ + [0x130EC]={ category="lo", direction="l", }, - [0x122BD]={ + [0x130ED]={ category="lo", direction="l", }, - [0x122BE]={ + [0x130EE]={ category="lo", direction="l", }, - [0x122BF]={ + [0x130EF]={ category="lo", direction="l", }, - [0x122C0]={ + [0x130F0]={ category="lo", direction="l", }, - [0x122C1]={ + [0x130F1]={ category="lo", direction="l", }, - [0x122C2]={ + [0x130F2]={ category="lo", direction="l", }, - [0x122C3]={ + [0x130F3]={ category="lo", direction="l", }, - [0x122C4]={ + [0x130F4]={ category="lo", direction="l", }, - [0x122C5]={ + [0x130F5]={ category="lo", direction="l", }, - [0x122C6]={ + [0x130F6]={ category="lo", direction="l", }, - [0x122C7]={ + [0x130F7]={ category="lo", direction="l", }, - [0x122C8]={ + [0x130F8]={ category="lo", direction="l", }, - [0x122C9]={ + [0x130F9]={ category="lo", direction="l", }, - [0x122CA]={ + [0x130FA]={ category="lo", direction="l", }, - [0x122CB]={ + [0x130FB]={ category="lo", direction="l", }, - [0x122CC]={ + [0x130FC]={ category="lo", direction="l", }, - [0x122CD]={ + [0x130FD]={ category="lo", direction="l", }, - [0x122CE]={ + [0x130FE]={ category="lo", direction="l", }, - [0x122CF]={ + [0x130FF]={ category="lo", direction="l", }, - [0x122D0]={ + [0x13100]={ category="lo", direction="l", }, - [0x122D1]={ + [0x13101]={ category="lo", direction="l", }, - [0x122D2]={ + [0x13102]={ category="lo", direction="l", }, - [0x122D3]={ + [0x13103]={ category="lo", direction="l", }, - [0x122D4]={ + [0x13104]={ category="lo", direction="l", }, - [0x122D5]={ + [0x13105]={ category="lo", direction="l", }, - [0x122D6]={ + [0x13106]={ category="lo", direction="l", }, - [0x122D7]={ + [0x13107]={ category="lo", direction="l", }, - [0x122D8]={ + [0x13108]={ category="lo", direction="l", }, - [0x122D9]={ + [0x13109]={ category="lo", direction="l", }, - [0x122DA]={ + [0x1310A]={ category="lo", direction="l", }, - [0x122DB]={ + [0x1310B]={ category="lo", direction="l", }, - [0x122DC]={ + [0x1310C]={ category="lo", direction="l", }, - [0x122DD]={ + [0x1310D]={ category="lo", direction="l", }, - [0x122DE]={ + [0x1310E]={ category="lo", direction="l", }, - [0x122DF]={ + [0x1310F]={ category="lo", direction="l", }, - [0x122E0]={ + [0x13110]={ category="lo", direction="l", }, - [0x122E1]={ + [0x13111]={ category="lo", direction="l", }, - [0x122E2]={ + [0x13112]={ category="lo", direction="l", }, - [0x122E3]={ + [0x13113]={ category="lo", direction="l", }, - [0x122E4]={ + [0x13114]={ category="lo", direction="l", }, - [0x122E5]={ + [0x13115]={ category="lo", direction="l", }, - [0x122E6]={ + [0x13116]={ category="lo", direction="l", }, - [0x122E7]={ + [0x13117]={ category="lo", direction="l", }, - [0x122E8]={ + [0x13118]={ category="lo", direction="l", }, - [0x122E9]={ + [0x13119]={ category="lo", direction="l", }, - [0x122EA]={ + [0x1311A]={ category="lo", direction="l", }, - [0x122EB]={ + [0x1311B]={ category="lo", direction="l", }, - [0x122EC]={ + [0x1311C]={ category="lo", direction="l", }, - [0x122ED]={ + [0x1311D]={ category="lo", direction="l", }, - [0x122EE]={ + [0x1311E]={ category="lo", direction="l", }, - [0x122EF]={ + [0x1311F]={ category="lo", direction="l", }, - [0x122F0]={ + [0x13120]={ category="lo", direction="l", }, - [0x122F1]={ + [0x13121]={ category="lo", direction="l", }, - [0x122F2]={ + [0x13122]={ category="lo", direction="l", }, - [0x122F3]={ + [0x13123]={ category="lo", direction="l", }, - [0x122F4]={ + [0x13124]={ category="lo", direction="l", }, - [0x122F5]={ + [0x13125]={ category="lo", direction="l", }, - [0x122F6]={ + [0x13126]={ category="lo", direction="l", }, - [0x122F7]={ + [0x13127]={ category="lo", direction="l", }, - [0x122F8]={ + [0x13128]={ category="lo", direction="l", }, - [0x122F9]={ + [0x13129]={ category="lo", direction="l", }, - [0x122FA]={ + [0x1312A]={ category="lo", direction="l", }, - [0x122FB]={ + [0x1312B]={ category="lo", direction="l", }, - [0x122FC]={ + [0x1312C]={ category="lo", direction="l", }, - [0x122FD]={ + [0x1312D]={ category="lo", direction="l", }, - [0x122FE]={ + [0x1312E]={ category="lo", direction="l", }, - [0x122FF]={ + [0x1312F]={ category="lo", direction="l", }, - [0x12300]={ + [0x13130]={ category="lo", direction="l", }, - [0x12301]={ + [0x13131]={ category="lo", direction="l", }, - [0x12302]={ + [0x13132]={ category="lo", direction="l", }, - [0x12303]={ + [0x13133]={ category="lo", direction="l", }, - [0x12304]={ + [0x13134]={ category="lo", direction="l", }, - [0x12305]={ + [0x13135]={ category="lo", direction="l", }, - [0x12306]={ + [0x13136]={ category="lo", direction="l", }, - [0x12307]={ + [0x13137]={ category="lo", direction="l", }, - [0x12308]={ + [0x13138]={ category="lo", direction="l", }, - [0x12309]={ + [0x13139]={ category="lo", direction="l", }, - [0x1230A]={ + [0x1313A]={ category="lo", direction="l", }, - [0x1230B]={ + [0x1313B]={ category="lo", direction="l", }, - [0x1230C]={ + [0x1313C]={ category="lo", direction="l", }, - [0x1230D]={ + [0x1313D]={ category="lo", direction="l", }, - [0x1230E]={ + [0x1313E]={ category="lo", direction="l", }, - [0x1230F]={ + [0x1313F]={ category="lo", direction="l", }, - [0x12310]={ + [0x13140]={ category="lo", direction="l", }, - [0x12311]={ + [0x13141]={ category="lo", direction="l", }, - [0x12312]={ + [0x13142]={ category="lo", direction="l", }, - [0x12313]={ + [0x13143]={ category="lo", direction="l", }, - [0x12314]={ + [0x13144]={ category="lo", direction="l", }, - [0x12315]={ + [0x13145]={ category="lo", direction="l", }, - [0x12316]={ + [0x13146]={ category="lo", direction="l", }, - [0x12317]={ + [0x13147]={ category="lo", direction="l", }, - [0x12318]={ + [0x13148]={ category="lo", direction="l", }, - [0x12319]={ + [0x13149]={ category="lo", direction="l", }, - [0x1231A]={ + [0x1314A]={ category="lo", direction="l", }, - [0x1231B]={ + [0x1314B]={ category="lo", direction="l", }, - [0x1231C]={ + [0x1314C]={ category="lo", direction="l", }, - [0x1231D]={ + [0x1314D]={ category="lo", direction="l", }, - [0x1231E]={ + [0x1314E]={ category="lo", direction="l", }, - [0x1231F]={ + [0x1314F]={ category="lo", direction="l", }, - [0x12320]={ + [0x13150]={ category="lo", direction="l", }, - [0x12321]={ + [0x13151]={ category="lo", direction="l", }, - [0x12322]={ + [0x13152]={ category="lo", direction="l", }, - [0x12323]={ + [0x13153]={ category="lo", direction="l", }, - [0x12324]={ + [0x13154]={ category="lo", direction="l", }, - [0x12325]={ + [0x13155]={ category="lo", direction="l", }, - [0x12326]={ + [0x13156]={ category="lo", direction="l", }, - [0x12327]={ + [0x13157]={ category="lo", direction="l", }, - [0x12328]={ + [0x13158]={ category="lo", direction="l", }, - [0x12329]={ + [0x13159]={ category="lo", direction="l", }, - [0x1232A]={ + [0x1315A]={ category="lo", direction="l", }, - [0x1232B]={ + [0x1315B]={ category="lo", direction="l", }, - [0x1232C]={ + [0x1315C]={ category="lo", direction="l", }, - [0x1232D]={ + [0x1315D]={ category="lo", direction="l", }, - [0x1232E]={ + [0x1315E]={ category="lo", direction="l", }, - [0x1232F]={ + [0x1315F]={ category="lo", direction="l", }, - [0x12330]={ + [0x13160]={ category="lo", direction="l", }, - [0x12331]={ + [0x13161]={ category="lo", direction="l", }, - [0x12332]={ + [0x13162]={ category="lo", direction="l", }, - [0x12333]={ + [0x13163]={ category="lo", direction="l", }, - [0x12334]={ + [0x13164]={ category="lo", direction="l", }, - [0x12335]={ + [0x13165]={ category="lo", direction="l", }, - [0x12336]={ + [0x13166]={ category="lo", direction="l", }, - [0x12337]={ + [0x13167]={ category="lo", direction="l", }, - [0x12338]={ + [0x13168]={ category="lo", direction="l", }, - [0x12339]={ + [0x13169]={ category="lo", direction="l", }, - [0x1233A]={ + [0x1316A]={ category="lo", direction="l", }, - [0x1233B]={ + [0x1316B]={ category="lo", direction="l", }, - [0x1233C]={ + [0x1316C]={ category="lo", direction="l", }, - [0x1233D]={ + [0x1316D]={ category="lo", direction="l", }, - [0x1233E]={ + [0x1316E]={ category="lo", direction="l", }, - [0x1233F]={ + [0x1316F]={ category="lo", direction="l", }, - [0x12340]={ + [0x13170]={ category="lo", direction="l", }, - [0x12341]={ + [0x13171]={ category="lo", direction="l", }, - [0x12342]={ + [0x13172]={ category="lo", direction="l", }, - [0x12343]={ + [0x13173]={ category="lo", direction="l", }, - [0x12344]={ + [0x13174]={ category="lo", direction="l", }, - [0x12345]={ + [0x13175]={ category="lo", direction="l", }, - [0x12346]={ + [0x13176]={ category="lo", direction="l", }, - [0x12347]={ + [0x13177]={ category="lo", direction="l", }, - [0x12348]={ + [0x13178]={ category="lo", direction="l", }, - [0x12349]={ + [0x13179]={ category="lo", direction="l", }, - [0x1234A]={ + [0x1317A]={ category="lo", direction="l", }, - [0x1234B]={ + [0x1317B]={ category="lo", direction="l", }, - [0x1234C]={ + [0x1317C]={ category="lo", direction="l", }, - [0x1234D]={ + [0x1317D]={ category="lo", direction="l", }, - [0x1234E]={ + [0x1317E]={ category="lo", direction="l", }, - [0x1234F]={ + [0x1317F]={ category="lo", direction="l", }, - [0x12350]={ + [0x13180]={ category="lo", direction="l", }, - [0x12351]={ + [0x13181]={ category="lo", direction="l", }, - [0x12352]={ + [0x13182]={ category="lo", direction="l", }, - [0x12353]={ + [0x13183]={ category="lo", direction="l", }, - [0x12354]={ + [0x13184]={ category="lo", direction="l", }, - [0x12355]={ + [0x13185]={ category="lo", direction="l", }, - [0x12356]={ + [0x13186]={ category="lo", direction="l", }, - [0x12357]={ + [0x13187]={ category="lo", direction="l", }, - [0x12358]={ + [0x13188]={ category="lo", direction="l", }, - [0x12359]={ + [0x13189]={ category="lo", direction="l", }, - [0x1235A]={ + [0x1318A]={ category="lo", direction="l", }, - [0x1235B]={ + [0x1318B]={ category="lo", direction="l", }, - [0x1235C]={ + [0x1318C]={ category="lo", direction="l", }, - [0x1235D]={ + [0x1318D]={ category="lo", direction="l", }, - [0x1235E]={ + [0x1318E]={ category="lo", direction="l", }, - [0x1235F]={ + [0x1318F]={ category="lo", direction="l", }, - [0x12360]={ + [0x13190]={ category="lo", direction="l", }, - [0x12361]={ + [0x13191]={ category="lo", direction="l", }, - [0x12362]={ + [0x13192]={ category="lo", direction="l", }, - [0x12363]={ + [0x13193]={ category="lo", direction="l", }, - [0x12364]={ + [0x13194]={ category="lo", direction="l", }, - [0x12365]={ + [0x13195]={ category="lo", direction="l", }, - [0x12366]={ + [0x13196]={ category="lo", direction="l", }, - [0x12367]={ + [0x13197]={ category="lo", direction="l", }, - [0x12368]={ + [0x13198]={ category="lo", direction="l", }, - [0x12369]={ + [0x13199]={ category="lo", direction="l", }, - [0x1236A]={ + [0x1319A]={ category="lo", direction="l", }, - [0x1236B]={ + [0x1319B]={ category="lo", direction="l", }, - [0x1236C]={ + [0x1319C]={ category="lo", direction="l", }, - [0x1236D]={ + [0x1319D]={ category="lo", direction="l", }, - [0x1236E]={ + [0x1319E]={ category="lo", direction="l", }, - [0x1236F]={ + [0x1319F]={ category="lo", direction="l", }, - [0x12370]={ + [0x131A0]={ category="lo", direction="l", }, - [0x12371]={ + [0x131A1]={ category="lo", direction="l", }, - [0x12372]={ + [0x131A2]={ category="lo", direction="l", }, - [0x12373]={ + [0x131A3]={ category="lo", direction="l", }, - [0x12374]={ + [0x131A4]={ category="lo", direction="l", }, - [0x12375]={ + [0x131A5]={ category="lo", direction="l", }, - [0x12376]={ + [0x131A6]={ category="lo", direction="l", }, - [0x12377]={ + [0x131A7]={ category="lo", direction="l", }, - [0x12378]={ + [0x131A8]={ category="lo", direction="l", }, - [0x12379]={ + [0x131A9]={ category="lo", direction="l", }, - [0x1237A]={ + [0x131AA]={ category="lo", direction="l", }, - [0x1237B]={ + [0x131AB]={ category="lo", direction="l", }, - [0x1237C]={ + [0x131AC]={ category="lo", direction="l", }, - [0x1237D]={ + [0x131AD]={ category="lo", direction="l", }, - [0x1237E]={ + [0x131AE]={ category="lo", direction="l", }, - [0x1237F]={ + [0x131AF]={ category="lo", direction="l", }, - [0x12380]={ + [0x131B0]={ category="lo", direction="l", }, - [0x12381]={ + [0x131B1]={ category="lo", direction="l", }, - [0x12382]={ + [0x131B2]={ category="lo", direction="l", }, - [0x12383]={ + [0x131B3]={ category="lo", direction="l", }, - [0x12384]={ + [0x131B4]={ category="lo", direction="l", }, - [0x12385]={ + [0x131B5]={ category="lo", direction="l", }, - [0x12386]={ + [0x131B6]={ category="lo", direction="l", }, - [0x12387]={ + [0x131B7]={ category="lo", direction="l", }, - [0x12388]={ + [0x131B8]={ category="lo", direction="l", }, - [0x12389]={ + [0x131B9]={ category="lo", direction="l", }, - [0x1238A]={ + [0x131BA]={ category="lo", direction="l", }, - [0x1238B]={ + [0x131BB]={ category="lo", direction="l", }, - [0x1238C]={ + [0x131BC]={ category="lo", direction="l", }, - [0x1238D]={ + [0x131BD]={ category="lo", direction="l", }, - [0x1238E]={ + [0x131BE]={ category="lo", direction="l", }, - [0x1238F]={ + [0x131BF]={ category="lo", direction="l", }, - [0x12390]={ + [0x131C0]={ category="lo", direction="l", }, - [0x12391]={ + [0x131C1]={ category="lo", direction="l", }, - [0x12392]={ + [0x131C2]={ category="lo", direction="l", }, - [0x12393]={ + [0x131C3]={ category="lo", direction="l", }, - [0x12394]={ + [0x131C4]={ category="lo", direction="l", }, - [0x12395]={ + [0x131C5]={ category="lo", direction="l", }, - [0x12396]={ + [0x131C6]={ category="lo", direction="l", }, - [0x12397]={ + [0x131C7]={ category="lo", direction="l", }, - [0x12398]={ + [0x131C8]={ category="lo", direction="l", }, - [0x12399]={ + [0x131C9]={ category="lo", direction="l", }, - [0x12400]={ - category="nl", + [0x131CA]={ + category="lo", direction="l", }, - [0x12401]={ - category="nl", + [0x131CB]={ + category="lo", direction="l", }, - [0x12402]={ - category="nl", + [0x131CC]={ + category="lo", direction="l", }, - [0x12403]={ - category="nl", + [0x131CD]={ + category="lo", direction="l", }, - [0x12404]={ - category="nl", + [0x131CE]={ + category="lo", direction="l", }, - [0x12405]={ - category="nl", + [0x131CF]={ + category="lo", direction="l", }, - [0x12406]={ - category="nl", + [0x131D0]={ + category="lo", direction="l", }, - [0x12407]={ - category="nl", + [0x131D1]={ + category="lo", direction="l", }, - [0x12408]={ - category="nl", + [0x131D2]={ + category="lo", direction="l", }, - [0x12409]={ - category="nl", + [0x131D3]={ + category="lo", direction="l", }, - [0x1240A]={ - category="nl", + [0x131D4]={ + category="lo", direction="l", }, - [0x1240B]={ - category="nl", + [0x131D5]={ + category="lo", direction="l", }, - [0x1240C]={ - category="nl", + [0x131D6]={ + category="lo", direction="l", }, - [0x1240D]={ - category="nl", + [0x131D7]={ + category="lo", direction="l", }, - [0x1240E]={ - category="nl", + [0x131D8]={ + category="lo", direction="l", }, - [0x1240F]={ - category="nl", + [0x131D9]={ + category="lo", direction="l", }, - [0x12410]={ - category="nl", + [0x131DA]={ + category="lo", direction="l", }, - [0x12411]={ - category="nl", + [0x131DB]={ + category="lo", direction="l", }, - [0x12412]={ - category="nl", + [0x131DC]={ + category="lo", direction="l", }, - [0x12413]={ - category="nl", + [0x131DD]={ + category="lo", direction="l", }, - [0x12414]={ - category="nl", + [0x131DE]={ + category="lo", direction="l", }, - [0x12415]={ - category="nl", + [0x131DF]={ + category="lo", direction="l", }, - [0x12416]={ - category="nl", + [0x131E0]={ + category="lo", direction="l", }, - [0x12417]={ - category="nl", + [0x131E1]={ + category="lo", direction="l", }, - [0x12418]={ - category="nl", + [0x131E2]={ + category="lo", direction="l", }, - [0x12419]={ - category="nl", + [0x131E3]={ + category="lo", direction="l", }, - [0x1241A]={ - category="nl", + [0x131E4]={ + category="lo", direction="l", }, - [0x1241B]={ - category="nl", + [0x131E5]={ + category="lo", direction="l", }, - [0x1241C]={ - category="nl", + [0x131E6]={ + category="lo", direction="l", }, - [0x1241D]={ - category="nl", + [0x131E7]={ + category="lo", direction="l", }, - [0x1241E]={ - category="nl", + [0x131E8]={ + category="lo", direction="l", }, - [0x1241F]={ - category="nl", + [0x131E9]={ + category="lo", direction="l", }, - [0x12420]={ - category="nl", + [0x131EA]={ + category="lo", direction="l", }, - [0x12421]={ - category="nl", + [0x131EB]={ + category="lo", direction="l", }, - [0x12422]={ - category="nl", + [0x131EC]={ + category="lo", direction="l", }, - [0x12423]={ - category="nl", + [0x131ED]={ + category="lo", direction="l", }, - [0x12424]={ - category="nl", + [0x131EE]={ + category="lo", direction="l", }, - [0x12425]={ - category="nl", + [0x131EF]={ + category="lo", direction="l", }, - [0x12426]={ - category="nl", + [0x131F0]={ + category="lo", direction="l", }, - [0x12427]={ - category="nl", + [0x131F1]={ + category="lo", direction="l", }, - [0x12428]={ - category="nl", + [0x131F2]={ + category="lo", direction="l", }, - [0x12429]={ - category="nl", + [0x131F3]={ + category="lo", direction="l", }, - [0x1242A]={ - category="nl", + [0x131F4]={ + category="lo", direction="l", }, - [0x1242B]={ - category="nl", + [0x131F5]={ + category="lo", direction="l", }, - [0x1242C]={ - category="nl", + [0x131F6]={ + category="lo", direction="l", }, - [0x1242D]={ - category="nl", + [0x131F7]={ + category="lo", direction="l", }, - [0x1242E]={ - category="nl", + [0x131F8]={ + category="lo", direction="l", }, - [0x1242F]={ - category="nl", + [0x131F9]={ + category="lo", direction="l", }, - [0x12430]={ - category="nl", + [0x131FA]={ + category="lo", direction="l", }, - [0x12431]={ - category="nl", + [0x131FB]={ + category="lo", direction="l", }, - [0x12432]={ - category="nl", + [0x131FC]={ + category="lo", direction="l", }, - [0x12433]={ - category="nl", + [0x131FD]={ + category="lo", direction="l", }, - [0x12434]={ - category="nl", + [0x131FE]={ + category="lo", direction="l", }, - [0x12435]={ - category="nl", + [0x131FF]={ + category="lo", direction="l", }, - [0x12436]={ - category="nl", + [0x13200]={ + category="lo", direction="l", }, - [0x12437]={ - category="nl", + [0x13201]={ + category="lo", direction="l", }, - [0x12438]={ - category="nl", + [0x13202]={ + category="lo", direction="l", }, - [0x12439]={ - category="nl", + [0x13203]={ + category="lo", direction="l", }, - [0x1243A]={ - category="nl", + [0x13204]={ + category="lo", direction="l", }, - [0x1243B]={ - category="nl", + [0x13205]={ + category="lo", direction="l", }, - [0x1243C]={ - category="nl", + [0x13206]={ + category="lo", direction="l", }, - [0x1243D]={ - category="nl", + [0x13207]={ + category="lo", direction="l", }, - [0x1243E]={ - category="nl", + [0x13208]={ + category="lo", direction="l", }, - [0x1243F]={ - category="nl", + [0x13209]={ + category="lo", direction="l", }, - [0x12440]={ - category="nl", + [0x1320A]={ + category="lo", direction="l", }, - [0x12441]={ - category="nl", + [0x1320B]={ + category="lo", direction="l", }, - [0x12442]={ - category="nl", + [0x1320C]={ + category="lo", direction="l", }, - [0x12443]={ - category="nl", + [0x1320D]={ + category="lo", direction="l", }, - [0x12444]={ - category="nl", + [0x1320E]={ + category="lo", direction="l", }, - [0x12445]={ - category="nl", + [0x1320F]={ + category="lo", direction="l", }, - [0x12446]={ - category="nl", + [0x13210]={ + category="lo", direction="l", }, - [0x12447]={ - category="nl", + [0x13211]={ + category="lo", direction="l", }, - [0x12448]={ - category="nl", + [0x13212]={ + category="lo", direction="l", }, - [0x12449]={ - category="nl", + [0x13213]={ + category="lo", direction="l", }, - [0x1244A]={ - category="nl", + [0x13214]={ + category="lo", direction="l", }, - [0x1244B]={ - category="nl", + [0x13215]={ + category="lo", direction="l", }, - [0x1244C]={ - category="nl", + [0x13216]={ + category="lo", direction="l", }, - [0x1244D]={ - category="nl", + [0x13217]={ + category="lo", direction="l", }, - [0x1244E]={ - category="nl", + [0x13218]={ + category="lo", direction="l", }, - [0x1244F]={ - category="nl", + [0x13219]={ + category="lo", direction="l", - }, - [0x12450]={ - category="nl", + }, + [0x1321A]={ + category="lo", direction="l", }, - [0x12451]={ - category="nl", + [0x1321B]={ + category="lo", direction="l", }, - [0x12452]={ - category="nl", + [0x1321C]={ + category="lo", direction="l", }, - [0x12453]={ - category="nl", + [0x1321D]={ + category="lo", direction="l", }, - [0x12454]={ - category="nl", + [0x1321E]={ + category="lo", direction="l", }, - [0x12455]={ - category="nl", + [0x1321F]={ + category="lo", direction="l", }, - [0x12456]={ - category="nl", + [0x13220]={ + category="lo", direction="l", }, - [0x12457]={ - category="nl", + [0x13221]={ + category="lo", direction="l", }, - [0x12458]={ - category="nl", + [0x13222]={ + category="lo", direction="l", }, - [0x12459]={ - category="nl", + [0x13223]={ + category="lo", direction="l", }, - [0x1245A]={ - category="nl", + [0x13224]={ + category="lo", direction="l", }, - [0x1245B]={ - category="nl", + [0x13225]={ + category="lo", direction="l", }, - [0x1245C]={ - category="nl", + [0x13226]={ + category="lo", direction="l", }, - [0x1245D]={ - category="nl", + [0x13227]={ + category="lo", direction="l", }, - [0x1245E]={ - category="nl", + [0x13228]={ + category="lo", direction="l", }, - [0x1245F]={ - category="nl", + [0x13229]={ + category="lo", direction="l", }, - [0x12460]={ - category="nl", + [0x1322A]={ + category="lo", direction="l", }, - [0x12461]={ - category="nl", + [0x1322B]={ + category="lo", direction="l", }, - [0x12462]={ - category="nl", + [0x1322C]={ + category="lo", direction="l", }, - [0x12463]={ - category="nl", + [0x1322D]={ + category="lo", direction="l", }, - [0x12464]={ - category="nl", + [0x1322E]={ + category="lo", direction="l", }, - [0x12465]={ - category="nl", + [0x1322F]={ + category="lo", direction="l", }, - [0x12466]={ - category="nl", + [0x13230]={ + category="lo", direction="l", }, - [0x12467]={ - category="nl", + [0x13231]={ + category="lo", direction="l", }, - [0x12468]={ - category="nl", + [0x13232]={ + category="lo", direction="l", }, - [0x12469]={ - category="nl", + [0x13233]={ + category="lo", direction="l", }, - [0x1246A]={ - category="nl", + [0x13234]={ + category="lo", direction="l", }, - [0x1246B]={ - category="nl", + [0x13235]={ + category="lo", direction="l", }, - [0x1246C]={ - category="nl", + [0x13236]={ + category="lo", direction="l", }, - [0x1246D]={ - category="nl", + [0x13237]={ + category="lo", direction="l", }, - [0x1246E]={ - category="nl", + [0x13238]={ + category="lo", direction="l", }, - [0x12470]={ - category="po", + [0x13239]={ + category="lo", direction="l", }, - [0x12471]={ - category="po", + [0x1323A]={ + category="lo", direction="l", }, - [0x12472]={ - category="po", + [0x1323B]={ + category="lo", direction="l", }, - [0x12473]={ - category="po", + [0x1323C]={ + category="lo", direction="l", }, - [0x12474]={ - category="po", + [0x1323D]={ + category="lo", direction="l", }, - [0x12480]={ + [0x1323E]={ category="lo", direction="l", }, - [0x12481]={ + [0x1323F]={ category="lo", direction="l", }, - [0x12482]={ + [0x13240]={ category="lo", direction="l", }, - [0x12483]={ + [0x13241]={ category="lo", direction="l", }, - [0x12484]={ + [0x13242]={ category="lo", direction="l", }, - [0x12485]={ + [0x13243]={ category="lo", direction="l", }, - [0x12486]={ + [0x13244]={ category="lo", direction="l", }, - [0x12487]={ + [0x13245]={ category="lo", direction="l", }, - [0x12488]={ + [0x13246]={ category="lo", direction="l", }, - [0x12489]={ + [0x13247]={ category="lo", direction="l", }, - [0x1248A]={ + [0x13248]={ category="lo", direction="l", }, - [0x1248B]={ + [0x13249]={ category="lo", direction="l", }, - [0x1248C]={ + [0x1324A]={ category="lo", direction="l", }, - [0x1248D]={ + [0x1324B]={ category="lo", direction="l", }, - [0x1248E]={ + [0x1324C]={ category="lo", direction="l", }, - [0x1248F]={ + [0x1324D]={ category="lo", direction="l", }, - [0x12490]={ + [0x1324E]={ category="lo", direction="l", }, - [0x12491]={ + [0x1324F]={ category="lo", direction="l", }, - [0x12492]={ + [0x13250]={ category="lo", direction="l", }, - [0x12493]={ + [0x13251]={ category="lo", direction="l", }, - [0x12494]={ + [0x13252]={ category="lo", direction="l", }, - [0x12495]={ + [0x13253]={ category="lo", direction="l", }, - [0x12496]={ + [0x13254]={ category="lo", direction="l", }, - [0x12497]={ + [0x13255]={ category="lo", direction="l", }, - [0x12498]={ + [0x13256]={ category="lo", direction="l", }, - [0x12499]={ + [0x13257]={ category="lo", direction="l", }, - [0x1249A]={ + [0x13258]={ category="lo", direction="l", }, - [0x1249B]={ + [0x13259]={ category="lo", direction="l", }, - [0x1249C]={ + [0x1325A]={ category="lo", direction="l", }, - [0x1249D]={ + [0x1325B]={ category="lo", direction="l", }, - [0x1249E]={ + [0x1325C]={ category="lo", direction="l", }, - [0x1249F]={ + [0x1325D]={ category="lo", direction="l", }, - [0x124A0]={ + [0x1325E]={ category="lo", direction="l", }, - [0x124A1]={ + [0x1325F]={ category="lo", direction="l", }, - [0x124A2]={ + [0x13260]={ category="lo", direction="l", }, - [0x124A3]={ + [0x13261]={ category="lo", direction="l", }, - [0x124A4]={ + [0x13262]={ category="lo", direction="l", }, - [0x124A5]={ + [0x13263]={ category="lo", direction="l", }, - [0x124A6]={ + [0x13264]={ category="lo", direction="l", }, - [0x124A7]={ + [0x13265]={ category="lo", direction="l", }, - [0x124A8]={ + [0x13266]={ category="lo", direction="l", }, - [0x124A9]={ + [0x13267]={ category="lo", direction="l", }, - [0x124AA]={ + [0x13268]={ category="lo", direction="l", }, - [0x124AB]={ + [0x13269]={ category="lo", direction="l", }, - [0x124AC]={ + [0x1326A]={ category="lo", direction="l", }, - [0x124AD]={ + [0x1326B]={ category="lo", direction="l", }, - [0x124AE]={ + [0x1326C]={ category="lo", direction="l", }, - [0x124AF]={ + [0x1326D]={ category="lo", direction="l", }, - [0x124B0]={ + [0x1326E]={ category="lo", direction="l", }, - [0x124B1]={ + [0x1326F]={ category="lo", direction="l", }, - [0x124B2]={ + [0x13270]={ category="lo", direction="l", }, - [0x124B3]={ + [0x13271]={ category="lo", direction="l", }, - [0x124B4]={ + [0x13272]={ category="lo", direction="l", }, - [0x124B5]={ + [0x13273]={ category="lo", direction="l", }, - [0x124B6]={ + [0x13274]={ category="lo", direction="l", }, - [0x124B7]={ + [0x13275]={ category="lo", direction="l", }, - [0x124B8]={ + [0x13276]={ category="lo", direction="l", }, - [0x124B9]={ + [0x13277]={ category="lo", direction="l", }, - [0x124BA]={ + [0x13278]={ category="lo", direction="l", }, - [0x124BB]={ + [0x13279]={ category="lo", direction="l", }, - [0x124BC]={ + [0x1327A]={ category="lo", direction="l", }, - [0x124BD]={ + [0x1327B]={ category="lo", direction="l", }, - [0x124BE]={ + [0x1327C]={ category="lo", direction="l", }, - [0x124BF]={ + [0x1327D]={ category="lo", direction="l", }, - [0x124C0]={ + [0x1327E]={ category="lo", direction="l", }, - [0x124C1]={ + [0x1327F]={ category="lo", direction="l", }, - [0x124C2]={ + [0x13280]={ category="lo", direction="l", }, - [0x124C3]={ + [0x13281]={ category="lo", direction="l", }, - [0x124C4]={ + [0x13282]={ category="lo", direction="l", }, - [0x124C5]={ + [0x13283]={ category="lo", direction="l", }, - [0x124C6]={ + [0x13284]={ category="lo", direction="l", }, - [0x124C7]={ + [0x13285]={ category="lo", direction="l", }, - [0x124C8]={ + [0x13286]={ category="lo", direction="l", }, - [0x124C9]={ + [0x13287]={ category="lo", direction="l", }, - [0x124CA]={ + [0x13288]={ category="lo", direction="l", }, - [0x124CB]={ + [0x13289]={ category="lo", direction="l", }, - [0x124CC]={ + [0x1328A]={ category="lo", direction="l", }, - [0x124CD]={ + [0x1328B]={ category="lo", direction="l", }, - [0x124CE]={ + [0x1328C]={ category="lo", direction="l", }, - [0x124CF]={ + [0x1328D]={ category="lo", direction="l", }, - [0x124D0]={ + [0x1328E]={ category="lo", direction="l", }, - [0x124D1]={ + [0x1328F]={ category="lo", direction="l", }, - [0x124D2]={ + [0x13290]={ category="lo", direction="l", }, - [0x124D3]={ + [0x13291]={ category="lo", direction="l", }, - [0x124D4]={ + [0x13292]={ category="lo", direction="l", }, - [0x124D5]={ + [0x13293]={ category="lo", direction="l", }, - [0x124D6]={ + [0x13294]={ category="lo", direction="l", }, - [0x124D7]={ + [0x13295]={ category="lo", direction="l", }, - [0x124D8]={ + [0x13296]={ category="lo", direction="l", }, - [0x124D9]={ + [0x13297]={ category="lo", direction="l", }, - [0x124DA]={ + [0x13298]={ category="lo", direction="l", }, - [0x124DB]={ + [0x13299]={ category="lo", direction="l", }, - [0x124DC]={ + [0x1329A]={ category="lo", direction="l", }, - [0x124DD]={ + [0x1329B]={ category="lo", direction="l", }, - [0x124DE]={ + [0x1329C]={ category="lo", direction="l", }, - [0x124DF]={ + [0x1329D]={ category="lo", direction="l", }, - [0x124E0]={ + [0x1329E]={ category="lo", direction="l", }, - [0x124E1]={ + [0x1329F]={ category="lo", direction="l", }, - [0x124E2]={ + [0x132A0]={ category="lo", direction="l", }, - [0x124E3]={ + [0x132A1]={ category="lo", direction="l", }, - [0x124E4]={ + [0x132A2]={ category="lo", direction="l", }, - [0x124E5]={ + [0x132A3]={ category="lo", direction="l", }, - [0x124E6]={ + [0x132A4]={ category="lo", direction="l", }, - [0x124E7]={ + [0x132A5]={ category="lo", direction="l", }, - [0x124E8]={ + [0x132A6]={ category="lo", direction="l", }, - [0x124E9]={ + [0x132A7]={ category="lo", direction="l", }, - [0x124EA]={ + [0x132A8]={ category="lo", direction="l", }, - [0x124EB]={ + [0x132A9]={ category="lo", direction="l", }, - [0x124EC]={ + [0x132AA]={ category="lo", direction="l", }, - [0x124ED]={ + [0x132AB]={ category="lo", direction="l", }, - [0x124EE]={ + [0x132AC]={ category="lo", direction="l", }, - [0x124EF]={ + [0x132AD]={ category="lo", direction="l", }, - [0x124F0]={ + [0x132AE]={ category="lo", direction="l", }, - [0x124F1]={ + [0x132AF]={ category="lo", direction="l", }, - [0x124F2]={ + [0x132B0]={ category="lo", direction="l", }, - [0x124F3]={ + [0x132B1]={ category="lo", direction="l", }, - [0x124F4]={ + [0x132B2]={ category="lo", direction="l", }, - [0x124F5]={ + [0x132B3]={ category="lo", direction="l", }, - [0x124F6]={ + [0x132B4]={ category="lo", direction="l", }, - [0x124F7]={ + [0x132B5]={ category="lo", direction="l", }, - [0x124F8]={ + [0x132B6]={ category="lo", direction="l", }, - [0x124F9]={ + [0x132B7]={ category="lo", direction="l", }, - [0x124FA]={ + [0x132B8]={ category="lo", direction="l", }, - [0x124FB]={ + [0x132B9]={ category="lo", direction="l", }, - [0x124FC]={ + [0x132BA]={ category="lo", direction="l", }, - [0x124FD]={ + [0x132BB]={ category="lo", direction="l", }, - [0x124FE]={ + [0x132BC]={ category="lo", direction="l", }, - [0x124FF]={ + [0x132BD]={ category="lo", direction="l", }, - [0x12500]={ + [0x132BE]={ category="lo", direction="l", }, - [0x12501]={ + [0x132BF]={ category="lo", direction="l", }, - [0x12502]={ + [0x132C0]={ category="lo", direction="l", }, - [0x12503]={ + [0x132C1]={ category="lo", direction="l", }, - [0x12504]={ + [0x132C2]={ category="lo", direction="l", }, - [0x12505]={ + [0x132C3]={ category="lo", direction="l", }, - [0x12506]={ + [0x132C4]={ category="lo", direction="l", }, - [0x12507]={ + [0x132C5]={ category="lo", direction="l", }, - [0x12508]={ + [0x132C6]={ category="lo", direction="l", }, - [0x12509]={ + [0x132C7]={ category="lo", direction="l", }, - [0x1250A]={ + [0x132C8]={ category="lo", direction="l", }, - [0x1250B]={ + [0x132C9]={ category="lo", direction="l", }, - [0x1250C]={ + [0x132CA]={ category="lo", direction="l", }, - [0x1250D]={ + [0x132CB]={ category="lo", direction="l", }, - [0x1250E]={ + [0x132CC]={ category="lo", direction="l", }, - [0x1250F]={ + [0x132CD]={ category="lo", direction="l", }, - [0x12510]={ + [0x132CE]={ category="lo", direction="l", }, - [0x12511]={ + [0x132CF]={ category="lo", direction="l", }, - [0x12512]={ + [0x132D0]={ category="lo", direction="l", }, - [0x12513]={ + [0x132D1]={ category="lo", direction="l", }, - [0x12514]={ + [0x132D2]={ category="lo", direction="l", }, - [0x12515]={ + [0x132D3]={ category="lo", direction="l", }, - [0x12516]={ + [0x132D4]={ category="lo", direction="l", }, - [0x12517]={ + [0x132D5]={ category="lo", direction="l", }, - [0x12518]={ + [0x132D6]={ category="lo", direction="l", }, - [0x12519]={ + [0x132D7]={ category="lo", direction="l", }, - [0x1251A]={ + [0x132D8]={ category="lo", direction="l", }, - [0x1251B]={ + [0x132D9]={ category="lo", direction="l", }, - [0x1251C]={ + [0x132DA]={ category="lo", direction="l", }, - [0x1251D]={ + [0x132DB]={ category="lo", direction="l", }, - [0x1251E]={ + [0x132DC]={ category="lo", direction="l", }, - [0x1251F]={ + [0x132DD]={ category="lo", direction="l", }, - [0x12520]={ + [0x132DE]={ category="lo", direction="l", }, - [0x12521]={ + [0x132DF]={ category="lo", direction="l", }, - [0x12522]={ + [0x132E0]={ category="lo", direction="l", }, - [0x12523]={ + [0x132E1]={ category="lo", direction="l", }, - [0x12524]={ + [0x132E2]={ category="lo", direction="l", }, - [0x12525]={ + [0x132E3]={ category="lo", direction="l", }, - [0x12526]={ + [0x132E4]={ category="lo", direction="l", }, - [0x12527]={ + [0x132E5]={ category="lo", direction="l", }, - [0x12528]={ + [0x132E6]={ category="lo", direction="l", }, - [0x12529]={ + [0x132E7]={ category="lo", direction="l", }, - [0x1252A]={ + [0x132E8]={ category="lo", direction="l", }, - [0x1252B]={ + [0x132E9]={ category="lo", direction="l", }, - [0x1252C]={ + [0x132EA]={ category="lo", direction="l", }, - [0x1252D]={ + [0x132EB]={ category="lo", direction="l", }, - [0x1252E]={ + [0x132EC]={ category="lo", direction="l", }, - [0x1252F]={ + [0x132ED]={ category="lo", direction="l", }, - [0x12530]={ + [0x132EE]={ category="lo", direction="l", }, - [0x12531]={ + [0x132EF]={ category="lo", direction="l", }, - [0x12532]={ + [0x132F0]={ category="lo", direction="l", }, - [0x12533]={ + [0x132F1]={ category="lo", direction="l", }, - [0x12534]={ + [0x132F2]={ category="lo", direction="l", }, - [0x12535]={ + [0x132F3]={ category="lo", direction="l", }, - [0x12536]={ + [0x132F4]={ category="lo", direction="l", }, - [0x12537]={ + [0x132F5]={ category="lo", direction="l", }, - [0x12538]={ + [0x132F6]={ category="lo", direction="l", }, - [0x12539]={ + [0x132F7]={ category="lo", direction="l", }, - [0x1253A]={ + [0x132F8]={ category="lo", direction="l", }, - [0x1253B]={ + [0x132F9]={ category="lo", direction="l", }, - [0x1253C]={ + [0x132FA]={ category="lo", direction="l", }, - [0x1253D]={ + [0x132FB]={ category="lo", direction="l", }, - [0x1253E]={ + [0x132FC]={ category="lo", direction="l", }, - [0x1253F]={ + [0x132FD]={ category="lo", direction="l", }, - [0x12540]={ + [0x132FE]={ category="lo", direction="l", }, - [0x12541]={ + [0x132FF]={ category="lo", direction="l", }, - [0x12542]={ + [0x13300]={ category="lo", direction="l", }, - [0x12543]={ + [0x13301]={ category="lo", direction="l", }, - [0x13000]={ + [0x13302]={ category="lo", direction="l", }, - [0x13001]={ + [0x13303]={ category="lo", direction="l", }, - [0x13002]={ + [0x13304]={ category="lo", direction="l", }, - [0x13003]={ + [0x13305]={ category="lo", direction="l", }, - [0x13004]={ + [0x13306]={ category="lo", direction="l", }, - [0x13005]={ + [0x13307]={ category="lo", direction="l", }, - [0x13006]={ + [0x13308]={ category="lo", direction="l", }, - [0x13007]={ + [0x13309]={ category="lo", direction="l", }, - [0x13008]={ + [0x1330A]={ category="lo", direction="l", }, - [0x13009]={ + [0x1330B]={ category="lo", direction="l", }, - [0x1300A]={ + [0x1330C]={ category="lo", direction="l", }, - [0x1300B]={ + [0x1330D]={ category="lo", direction="l", }, - [0x1300C]={ + [0x1330E]={ category="lo", direction="l", }, - [0x1300D]={ + [0x1330F]={ category="lo", direction="l", }, - [0x1300E]={ + [0x13310]={ category="lo", direction="l", }, - [0x1300F]={ + [0x13311]={ category="lo", direction="l", }, - [0x13010]={ + [0x13312]={ category="lo", direction="l", }, - [0x13011]={ + [0x13313]={ category="lo", direction="l", }, - [0x13012]={ + [0x13314]={ category="lo", direction="l", }, - [0x13013]={ + [0x13315]={ category="lo", direction="l", }, - [0x13014]={ + [0x13316]={ category="lo", direction="l", }, - [0x13015]={ + [0x13317]={ category="lo", direction="l", }, - [0x13016]={ + [0x13318]={ category="lo", direction="l", }, - [0x13017]={ + [0x13319]={ category="lo", direction="l", }, - [0x13018]={ + [0x1331A]={ category="lo", direction="l", }, - [0x13019]={ + [0x1331B]={ category="lo", direction="l", }, - [0x1301A]={ + [0x1331C]={ category="lo", direction="l", }, - [0x1301B]={ + [0x1331D]={ category="lo", direction="l", }, - [0x1301C]={ + [0x1331E]={ category="lo", direction="l", }, - [0x1301D]={ + [0x1331F]={ category="lo", direction="l", }, - [0x1301E]={ + [0x13320]={ category="lo", direction="l", }, - [0x1301F]={ + [0x13321]={ category="lo", direction="l", }, - [0x13020]={ + [0x13322]={ category="lo", direction="l", }, - [0x13021]={ + [0x13323]={ category="lo", direction="l", }, - [0x13022]={ + [0x13324]={ category="lo", direction="l", }, - [0x13023]={ + [0x13325]={ category="lo", direction="l", }, - [0x13024]={ + [0x13326]={ category="lo", direction="l", }, - [0x13025]={ + [0x13327]={ category="lo", direction="l", }, - [0x13026]={ + [0x13328]={ category="lo", direction="l", }, - [0x13027]={ + [0x13329]={ category="lo", direction="l", }, - [0x13028]={ + [0x1332A]={ category="lo", direction="l", }, - [0x13029]={ + [0x1332B]={ category="lo", direction="l", }, - [0x1302A]={ + [0x1332C]={ category="lo", direction="l", }, - [0x1302B]={ + [0x1332D]={ category="lo", direction="l", }, - [0x1302C]={ + [0x1332E]={ category="lo", direction="l", }, - [0x1302D]={ + [0x1332F]={ category="lo", direction="l", }, - [0x1302E]={ + [0x13330]={ category="lo", direction="l", }, - [0x1302F]={ + [0x13331]={ category="lo", direction="l", }, - [0x13030]={ + [0x13332]={ category="lo", direction="l", }, - [0x13031]={ + [0x13333]={ category="lo", direction="l", }, - [0x13032]={ + [0x13334]={ category="lo", direction="l", }, - [0x13033]={ + [0x13335]={ category="lo", direction="l", }, - [0x13034]={ + [0x13336]={ category="lo", direction="l", }, - [0x13035]={ + [0x13337]={ category="lo", direction="l", }, - [0x13036]={ + [0x13338]={ category="lo", direction="l", }, - [0x13037]={ + [0x13339]={ category="lo", direction="l", }, - [0x13038]={ + [0x1333A]={ category="lo", direction="l", }, - [0x13039]={ + [0x1333B]={ category="lo", direction="l", }, - [0x1303A]={ + [0x1333C]={ category="lo", direction="l", }, - [0x1303B]={ + [0x1333D]={ category="lo", direction="l", }, - [0x1303C]={ + [0x1333E]={ category="lo", direction="l", }, - [0x1303D]={ + [0x1333F]={ category="lo", direction="l", }, - [0x1303E]={ + [0x13340]={ category="lo", direction="l", }, - [0x1303F]={ + [0x13341]={ category="lo", direction="l", }, - [0x13040]={ + [0x13342]={ category="lo", direction="l", }, - [0x13041]={ + [0x13343]={ category="lo", direction="l", }, - [0x13042]={ + [0x13344]={ category="lo", direction="l", }, - [0x13043]={ + [0x13345]={ category="lo", direction="l", }, - [0x13044]={ + [0x13346]={ category="lo", direction="l", }, - [0x13045]={ + [0x13347]={ category="lo", direction="l", }, - [0x13046]={ + [0x13348]={ category="lo", direction="l", }, - [0x13047]={ + [0x13349]={ category="lo", direction="l", }, - [0x13048]={ + [0x1334A]={ category="lo", direction="l", }, - [0x13049]={ + [0x1334B]={ category="lo", direction="l", }, - [0x1304A]={ + [0x1334C]={ category="lo", direction="l", }, - [0x1304B]={ + [0x1334D]={ category="lo", direction="l", }, - [0x1304C]={ + [0x1334E]={ category="lo", direction="l", }, - [0x1304D]={ + [0x1334F]={ category="lo", direction="l", }, - [0x1304E]={ + [0x13350]={ category="lo", direction="l", }, - [0x1304F]={ + [0x13351]={ category="lo", direction="l", }, - [0x13050]={ + [0x13352]={ category="lo", direction="l", }, - [0x13051]={ + [0x13353]={ category="lo", direction="l", }, - [0x13052]={ + [0x13354]={ category="lo", direction="l", }, - [0x13053]={ + [0x13355]={ category="lo", direction="l", }, - [0x13054]={ + [0x13356]={ category="lo", direction="l", }, - [0x13055]={ + [0x13357]={ category="lo", direction="l", }, - [0x13056]={ + [0x13358]={ category="lo", direction="l", }, - [0x13057]={ + [0x13359]={ category="lo", direction="l", }, - [0x13058]={ + [0x1335A]={ category="lo", direction="l", }, - [0x13059]={ + [0x1335B]={ category="lo", direction="l", }, - [0x1305A]={ + [0x1335C]={ category="lo", direction="l", }, - [0x1305B]={ + [0x1335D]={ category="lo", direction="l", }, - [0x1305C]={ + [0x1335E]={ category="lo", direction="l", }, - [0x1305D]={ + [0x1335F]={ category="lo", direction="l", }, - [0x1305E]={ + [0x13360]={ category="lo", direction="l", }, - [0x1305F]={ + [0x13361]={ category="lo", direction="l", }, - [0x13060]={ + [0x13362]={ category="lo", direction="l", }, - [0x13061]={ + [0x13363]={ category="lo", direction="l", }, - [0x13062]={ + [0x13364]={ category="lo", direction="l", }, - [0x13063]={ + [0x13365]={ category="lo", direction="l", }, - [0x13064]={ + [0x13366]={ category="lo", direction="l", }, - [0x13065]={ + [0x13367]={ category="lo", direction="l", }, - [0x13066]={ + [0x13368]={ category="lo", direction="l", }, - [0x13067]={ + [0x13369]={ category="lo", direction="l", }, - [0x13068]={ + [0x1336A]={ category="lo", direction="l", }, - [0x13069]={ + [0x1336B]={ category="lo", direction="l", }, - [0x1306A]={ + [0x1336C]={ category="lo", direction="l", }, - [0x1306B]={ + [0x1336D]={ category="lo", direction="l", }, - [0x1306C]={ + [0x1336E]={ category="lo", direction="l", }, - [0x1306D]={ + [0x1336F]={ category="lo", direction="l", }, - [0x1306E]={ + [0x13370]={ category="lo", direction="l", }, - [0x1306F]={ + [0x13371]={ category="lo", direction="l", }, - [0x13070]={ + [0x13372]={ category="lo", direction="l", }, - [0x13071]={ + [0x13373]={ category="lo", direction="l", }, - [0x13072]={ + [0x13374]={ category="lo", direction="l", }, - [0x13073]={ + [0x13375]={ category="lo", direction="l", }, - [0x13074]={ + [0x13376]={ category="lo", direction="l", }, - [0x13075]={ + [0x13377]={ category="lo", direction="l", }, - [0x13076]={ + [0x13378]={ category="lo", direction="l", }, - [0x13077]={ + [0x13379]={ category="lo", direction="l", }, - [0x13078]={ + [0x1337A]={ category="lo", direction="l", }, - [0x13079]={ + [0x1337B]={ category="lo", direction="l", }, - [0x1307A]={ + [0x1337C]={ category="lo", direction="l", }, - [0x1307B]={ + [0x1337D]={ category="lo", direction="l", }, - [0x1307C]={ + [0x1337E]={ category="lo", direction="l", }, - [0x1307D]={ + [0x1337F]={ category="lo", direction="l", }, - [0x1307E]={ + [0x13380]={ category="lo", direction="l", }, - [0x1307F]={ + [0x13381]={ category="lo", direction="l", }, - [0x13080]={ + [0x13382]={ category="lo", direction="l", }, - [0x13081]={ + [0x13383]={ category="lo", direction="l", }, - [0x13082]={ + [0x13384]={ category="lo", direction="l", }, - [0x13083]={ + [0x13385]={ category="lo", direction="l", }, - [0x13084]={ + [0x13386]={ category="lo", direction="l", }, - [0x13085]={ + [0x13387]={ category="lo", direction="l", }, - [0x13086]={ + [0x13388]={ category="lo", direction="l", }, - [0x13087]={ + [0x13389]={ category="lo", direction="l", }, - [0x13088]={ + [0x1338A]={ category="lo", direction="l", }, - [0x13089]={ + [0x1338B]={ category="lo", direction="l", }, - [0x1308A]={ + [0x1338C]={ category="lo", direction="l", }, - [0x1308B]={ + [0x1338D]={ category="lo", direction="l", }, - [0x1308C]={ + [0x1338E]={ category="lo", direction="l", }, - [0x1308D]={ + [0x1338F]={ category="lo", direction="l", }, - [0x1308E]={ + [0x13390]={ category="lo", direction="l", }, - [0x1308F]={ + [0x13391]={ category="lo", direction="l", }, - [0x13090]={ + [0x13392]={ category="lo", direction="l", }, - [0x13091]={ + [0x13393]={ category="lo", direction="l", }, - [0x13092]={ + [0x13394]={ category="lo", direction="l", }, - [0x13093]={ + [0x13395]={ category="lo", direction="l", }, - [0x13094]={ + [0x13396]={ category="lo", direction="l", }, - [0x13095]={ + [0x13397]={ category="lo", direction="l", }, - [0x13096]={ + [0x13398]={ category="lo", direction="l", }, - [0x13097]={ + [0x13399]={ category="lo", direction="l", }, - [0x13098]={ + [0x1339A]={ category="lo", direction="l", }, - [0x13099]={ + [0x1339B]={ category="lo", direction="l", }, - [0x1309A]={ + [0x1339C]={ category="lo", direction="l", }, - [0x1309B]={ + [0x1339D]={ category="lo", direction="l", }, - [0x1309C]={ + [0x1339E]={ category="lo", direction="l", }, - [0x1309D]={ + [0x1339F]={ category="lo", direction="l", }, - [0x1309E]={ + [0x133A0]={ category="lo", direction="l", }, - [0x1309F]={ + [0x133A1]={ category="lo", direction="l", }, - [0x130A0]={ + [0x133A2]={ category="lo", direction="l", }, - [0x130A1]={ + [0x133A3]={ category="lo", direction="l", }, - [0x130A2]={ + [0x133A4]={ category="lo", direction="l", }, - [0x130A3]={ + [0x133A5]={ category="lo", direction="l", }, - [0x130A4]={ + [0x133A6]={ category="lo", direction="l", }, - [0x130A5]={ + [0x133A7]={ category="lo", direction="l", }, - [0x130A6]={ + [0x133A8]={ category="lo", direction="l", }, - [0x130A7]={ + [0x133A9]={ category="lo", direction="l", }, - [0x130A8]={ + [0x133AA]={ category="lo", direction="l", }, - [0x130A9]={ + [0x133AB]={ category="lo", direction="l", }, - [0x130AA]={ + [0x133AC]={ category="lo", direction="l", }, - [0x130AB]={ + [0x133AD]={ category="lo", direction="l", }, - [0x130AC]={ + [0x133AE]={ category="lo", direction="l", }, - [0x130AD]={ + [0x133AF]={ category="lo", direction="l", }, - [0x130AE]={ + [0x133B0]={ category="lo", direction="l", }, - [0x130AF]={ + [0x133B1]={ category="lo", direction="l", }, - [0x130B0]={ + [0x133B2]={ category="lo", direction="l", }, - [0x130B1]={ + [0x133B3]={ category="lo", direction="l", }, - [0x130B2]={ + [0x133B4]={ category="lo", direction="l", }, - [0x130B3]={ + [0x133B5]={ category="lo", direction="l", }, - [0x130B4]={ + [0x133B6]={ category="lo", direction="l", }, - [0x130B5]={ + [0x133B7]={ category="lo", direction="l", }, - [0x130B6]={ + [0x133B8]={ category="lo", direction="l", }, - [0x130B7]={ + [0x133B9]={ category="lo", direction="l", }, - [0x130B8]={ + [0x133BA]={ category="lo", direction="l", }, - [0x130B9]={ + [0x133BB]={ category="lo", direction="l", }, - [0x130BA]={ + [0x133BC]={ category="lo", direction="l", }, - [0x130BB]={ + [0x133BD]={ category="lo", direction="l", }, - [0x130BC]={ + [0x133BE]={ category="lo", direction="l", }, - [0x130BD]={ + [0x133BF]={ category="lo", direction="l", }, - [0x130BE]={ + [0x133C0]={ category="lo", direction="l", }, - [0x130BF]={ + [0x133C1]={ category="lo", direction="l", }, - [0x130C0]={ + [0x133C2]={ category="lo", direction="l", }, - [0x130C1]={ + [0x133C3]={ category="lo", direction="l", }, - [0x130C2]={ + [0x133C4]={ category="lo", direction="l", }, - [0x130C3]={ + [0x133C5]={ category="lo", direction="l", }, - [0x130C4]={ + [0x133C6]={ category="lo", direction="l", }, - [0x130C5]={ + [0x133C7]={ category="lo", direction="l", }, - [0x130C6]={ + [0x133C8]={ category="lo", direction="l", }, - [0x130C7]={ + [0x133C9]={ category="lo", direction="l", }, - [0x130C8]={ + [0x133CA]={ category="lo", direction="l", }, - [0x130C9]={ + [0x133CB]={ category="lo", direction="l", }, - [0x130CA]={ + [0x133CC]={ category="lo", direction="l", }, - [0x130CB]={ + [0x133CD]={ category="lo", direction="l", }, - [0x130CC]={ + [0x133CE]={ category="lo", direction="l", }, - [0x130CD]={ + [0x133CF]={ category="lo", direction="l", }, - [0x130CE]={ + [0x133D0]={ category="lo", direction="l", }, - [0x130CF]={ + [0x133D1]={ category="lo", direction="l", }, - [0x130D0]={ + [0x133D2]={ category="lo", direction="l", }, - [0x130D1]={ + [0x133D3]={ category="lo", direction="l", }, - [0x130D2]={ + [0x133D4]={ category="lo", direction="l", }, - [0x130D3]={ + [0x133D5]={ category="lo", direction="l", }, - [0x130D4]={ + [0x133D6]={ category="lo", direction="l", }, - [0x130D5]={ + [0x133D7]={ category="lo", direction="l", }, - [0x130D6]={ + [0x133D8]={ category="lo", direction="l", }, - [0x130D7]={ + [0x133D9]={ category="lo", direction="l", }, - [0x130D8]={ + [0x133DA]={ category="lo", direction="l", }, - [0x130D9]={ + [0x133DB]={ category="lo", direction="l", }, - [0x130DA]={ + [0x133DC]={ category="lo", direction="l", }, - [0x130DB]={ + [0x133DD]={ category="lo", direction="l", }, - [0x130DC]={ + [0x133DE]={ category="lo", direction="l", }, - [0x130DD]={ + [0x133DF]={ category="lo", direction="l", }, - [0x130DE]={ + [0x133E0]={ category="lo", direction="l", }, - [0x130DF]={ + [0x133E1]={ category="lo", direction="l", }, - [0x130E0]={ + [0x133E2]={ category="lo", direction="l", }, - [0x130E1]={ + [0x133E3]={ category="lo", direction="l", }, - [0x130E2]={ + [0x133E4]={ category="lo", direction="l", }, - [0x130E3]={ + [0x133E5]={ category="lo", direction="l", }, - [0x130E4]={ + [0x133E6]={ category="lo", direction="l", }, - [0x130E5]={ + [0x133E7]={ category="lo", direction="l", }, - [0x130E6]={ + [0x133E8]={ category="lo", direction="l", }, - [0x130E7]={ + [0x133E9]={ category="lo", direction="l", }, - [0x130E8]={ + [0x133EA]={ category="lo", direction="l", }, - [0x130E9]={ + [0x133EB]={ category="lo", direction="l", }, - [0x130EA]={ + [0x133EC]={ category="lo", direction="l", }, - [0x130EB]={ + [0x133ED]={ category="lo", direction="l", }, - [0x130EC]={ + [0x133EE]={ category="lo", direction="l", }, - [0x130ED]={ + [0x133EF]={ category="lo", direction="l", }, - [0x130EE]={ + [0x133F0]={ category="lo", direction="l", }, - [0x130EF]={ + [0x133F1]={ category="lo", direction="l", }, - [0x130F0]={ + [0x133F2]={ category="lo", direction="l", }, - [0x130F1]={ + [0x133F3]={ category="lo", direction="l", }, - [0x130F2]={ + [0x133F4]={ category="lo", direction="l", }, - [0x130F3]={ + [0x133F5]={ category="lo", direction="l", }, - [0x130F4]={ + [0x133F6]={ category="lo", direction="l", }, - [0x130F5]={ + [0x133F7]={ category="lo", direction="l", }, - [0x130F6]={ + [0x133F8]={ category="lo", direction="l", }, - [0x130F7]={ + [0x133F9]={ category="lo", direction="l", }, - [0x130F8]={ + [0x133FA]={ category="lo", direction="l", }, - [0x130F9]={ + [0x133FB]={ category="lo", direction="l", }, - [0x130FA]={ + [0x133FC]={ category="lo", direction="l", }, - [0x130FB]={ + [0x133FD]={ category="lo", direction="l", }, - [0x130FC]={ + [0x133FE]={ category="lo", direction="l", }, - [0x130FD]={ + [0x133FF]={ category="lo", direction="l", }, - [0x130FE]={ + [0x13400]={ category="lo", direction="l", }, - [0x130FF]={ + [0x13401]={ category="lo", direction="l", }, - [0x13100]={ + [0x13402]={ category="lo", direction="l", }, - [0x13101]={ + [0x13403]={ category="lo", direction="l", }, - [0x13102]={ + [0x13404]={ category="lo", direction="l", }, - [0x13103]={ + [0x13405]={ category="lo", direction="l", }, - [0x13104]={ + [0x13406]={ category="lo", direction="l", }, - [0x13105]={ + [0x13407]={ category="lo", direction="l", }, - [0x13106]={ + [0x13408]={ category="lo", direction="l", }, - [0x13107]={ + [0x13409]={ category="lo", direction="l", }, - [0x13108]={ + [0x1340A]={ category="lo", direction="l", }, - [0x13109]={ + [0x1340B]={ category="lo", direction="l", }, - [0x1310A]={ + [0x1340C]={ category="lo", direction="l", }, - [0x1310B]={ + [0x1340D]={ category="lo", direction="l", }, - [0x1310C]={ + [0x1340E]={ category="lo", direction="l", }, - [0x1310D]={ + [0x1340F]={ category="lo", direction="l", }, - [0x1310E]={ + [0x13410]={ category="lo", direction="l", }, - [0x1310F]={ + [0x13411]={ category="lo", direction="l", }, - [0x13110]={ + [0x13412]={ category="lo", direction="l", }, - [0x13111]={ + [0x13413]={ category="lo", direction="l", }, - [0x13112]={ + [0x13414]={ category="lo", direction="l", }, - [0x13113]={ + [0x13415]={ category="lo", direction="l", }, - [0x13114]={ + [0x13416]={ category="lo", direction="l", }, - [0x13115]={ + [0x13417]={ category="lo", direction="l", }, - [0x13116]={ + [0x13418]={ category="lo", direction="l", }, - [0x13117]={ + [0x13419]={ category="lo", direction="l", }, - [0x13118]={ + [0x1341A]={ category="lo", direction="l", }, - [0x13119]={ + [0x1341B]={ category="lo", direction="l", }, - [0x1311A]={ + [0x1341C]={ category="lo", direction="l", }, - [0x1311B]={ + [0x1341D]={ category="lo", direction="l", }, - [0x1311C]={ + [0x1341E]={ category="lo", direction="l", }, - [0x1311D]={ + [0x1341F]={ category="lo", direction="l", }, - [0x1311E]={ + [0x13420]={ category="lo", direction="l", }, - [0x1311F]={ + [0x13421]={ category="lo", direction="l", }, - [0x13120]={ + [0x13422]={ category="lo", direction="l", }, - [0x13121]={ + [0x13423]={ category="lo", direction="l", }, - [0x13122]={ + [0x13424]={ category="lo", direction="l", }, - [0x13123]={ + [0x13425]={ category="lo", direction="l", }, - [0x13124]={ + [0x13426]={ category="lo", direction="l", }, - [0x13125]={ + [0x13427]={ category="lo", direction="l", }, - [0x13126]={ + [0x13428]={ category="lo", direction="l", }, - [0x13127]={ + [0x13429]={ category="lo", direction="l", }, - [0x13128]={ + [0x1342A]={ category="lo", direction="l", }, - [0x13129]={ + [0x1342B]={ category="lo", direction="l", }, - [0x1312A]={ + [0x1342C]={ category="lo", direction="l", }, - [0x1312B]={ + [0x1342D]={ category="lo", direction="l", }, - [0x1312C]={ + [0x1342E]={ category="lo", direction="l", }, - [0x1312D]={ + [0x14400]={ category="lo", direction="l", }, - [0x1312E]={ + [0x14401]={ category="lo", direction="l", }, - [0x1312F]={ + [0x14402]={ category="lo", direction="l", }, - [0x13130]={ + [0x14403]={ category="lo", direction="l", }, - [0x13131]={ + [0x14404]={ category="lo", direction="l", }, - [0x13132]={ + [0x14405]={ category="lo", direction="l", }, - [0x13133]={ + [0x14406]={ category="lo", direction="l", }, - [0x13134]={ + [0x14407]={ category="lo", direction="l", }, - [0x13135]={ + [0x14408]={ category="lo", direction="l", }, - [0x13136]={ + [0x14409]={ category="lo", direction="l", }, - [0x13137]={ + [0x1440A]={ category="lo", direction="l", }, - [0x13138]={ + [0x1440B]={ category="lo", direction="l", }, - [0x13139]={ + [0x1440C]={ category="lo", direction="l", }, - [0x1313A]={ + [0x1440D]={ category="lo", direction="l", }, - [0x1313B]={ + [0x1440E]={ category="lo", direction="l", }, - [0x1313C]={ + [0x1440F]={ category="lo", direction="l", }, - [0x1313D]={ + [0x14410]={ category="lo", direction="l", }, - [0x1313E]={ + [0x14411]={ category="lo", direction="l", }, - [0x1313F]={ + [0x14412]={ category="lo", direction="l", }, - [0x13140]={ + [0x14413]={ category="lo", direction="l", }, - [0x13141]={ + [0x14414]={ category="lo", direction="l", }, - [0x13142]={ + [0x14415]={ category="lo", direction="l", }, - [0x13143]={ + [0x14416]={ category="lo", direction="l", }, - [0x13144]={ + [0x14417]={ category="lo", direction="l", }, - [0x13145]={ + [0x14418]={ category="lo", direction="l", }, - [0x13146]={ + [0x14419]={ category="lo", direction="l", }, - [0x13147]={ + [0x1441A]={ category="lo", direction="l", }, - [0x13148]={ + [0x1441B]={ category="lo", direction="l", }, - [0x13149]={ + [0x1441C]={ category="lo", direction="l", }, - [0x1314A]={ + [0x1441D]={ category="lo", direction="l", }, - [0x1314B]={ + [0x1441E]={ category="lo", direction="l", }, - [0x1314C]={ + [0x1441F]={ category="lo", direction="l", }, - [0x1314D]={ + [0x14420]={ category="lo", direction="l", }, - [0x1314E]={ + [0x14421]={ category="lo", direction="l", }, - [0x1314F]={ + [0x14422]={ category="lo", direction="l", }, - [0x13150]={ + [0x14423]={ category="lo", direction="l", }, - [0x13151]={ + [0x14424]={ category="lo", direction="l", }, - [0x13152]={ + [0x14425]={ category="lo", direction="l", }, - [0x13153]={ + [0x14426]={ category="lo", direction="l", }, - [0x13154]={ + [0x14427]={ category="lo", direction="l", }, - [0x13155]={ + [0x14428]={ category="lo", direction="l", }, - [0x13156]={ + [0x14429]={ category="lo", direction="l", }, - [0x13157]={ + [0x1442A]={ category="lo", direction="l", }, - [0x13158]={ + [0x1442B]={ category="lo", direction="l", }, - [0x13159]={ + [0x1442C]={ category="lo", direction="l", }, - [0x1315A]={ + [0x1442D]={ category="lo", direction="l", }, - [0x1315B]={ + [0x1442E]={ category="lo", direction="l", }, - [0x1315C]={ + [0x1442F]={ category="lo", direction="l", }, - [0x1315D]={ + [0x14430]={ category="lo", direction="l", }, - [0x1315E]={ + [0x14431]={ category="lo", direction="l", }, - [0x1315F]={ + [0x14432]={ category="lo", direction="l", }, - [0x13160]={ + [0x14433]={ category="lo", direction="l", }, - [0x13161]={ + [0x14434]={ category="lo", direction="l", }, - [0x13162]={ + [0x14435]={ category="lo", direction="l", }, - [0x13163]={ + [0x14436]={ category="lo", direction="l", }, - [0x13164]={ + [0x14437]={ category="lo", direction="l", }, - [0x13165]={ + [0x14438]={ category="lo", direction="l", }, - [0x13166]={ + [0x14439]={ category="lo", direction="l", }, - [0x13167]={ + [0x1443A]={ category="lo", direction="l", }, - [0x13168]={ + [0x1443B]={ category="lo", direction="l", }, - [0x13169]={ + [0x1443C]={ category="lo", direction="l", }, - [0x1316A]={ + [0x1443D]={ category="lo", direction="l", }, - [0x1316B]={ + [0x1443E]={ category="lo", direction="l", }, - [0x1316C]={ + [0x1443F]={ category="lo", direction="l", }, - [0x1316D]={ + [0x14440]={ category="lo", direction="l", }, - [0x1316E]={ + [0x14441]={ category="lo", direction="l", }, - [0x1316F]={ + [0x14442]={ category="lo", direction="l", }, - [0x13170]={ + [0x14443]={ category="lo", direction="l", }, - [0x13171]={ + [0x14444]={ category="lo", direction="l", }, - [0x13172]={ + [0x14445]={ category="lo", direction="l", }, - [0x13173]={ + [0x14446]={ category="lo", direction="l", }, - [0x13174]={ + [0x14447]={ category="lo", direction="l", }, - [0x13175]={ + [0x14448]={ category="lo", direction="l", }, - [0x13176]={ + [0x14449]={ category="lo", direction="l", }, - [0x13177]={ + [0x1444A]={ category="lo", direction="l", }, - [0x13178]={ + [0x1444B]={ category="lo", direction="l", }, - [0x13179]={ + [0x1444C]={ category="lo", direction="l", }, - [0x1317A]={ + [0x1444D]={ category="lo", direction="l", }, - [0x1317B]={ + [0x1444E]={ category="lo", direction="l", }, - [0x1317C]={ + [0x1444F]={ category="lo", direction="l", }, - [0x1317D]={ + [0x14450]={ category="lo", direction="l", }, - [0x1317E]={ + [0x14451]={ category="lo", direction="l", }, - [0x1317F]={ + [0x14452]={ category="lo", direction="l", }, - [0x13180]={ + [0x14453]={ category="lo", direction="l", }, - [0x13181]={ + [0x14454]={ category="lo", direction="l", }, - [0x13182]={ + [0x14455]={ category="lo", direction="l", }, - [0x13183]={ + [0x14456]={ category="lo", direction="l", }, - [0x13184]={ + [0x14457]={ category="lo", direction="l", }, - [0x13185]={ + [0x14458]={ category="lo", direction="l", }, - [0x13186]={ + [0x14459]={ category="lo", direction="l", }, - [0x13187]={ + [0x1445A]={ category="lo", direction="l", }, - [0x13188]={ + [0x1445B]={ category="lo", direction="l", }, - [0x13189]={ + [0x1445C]={ category="lo", direction="l", }, - [0x1318A]={ + [0x1445D]={ category="lo", direction="l", }, - [0x1318B]={ + [0x1445E]={ category="lo", direction="l", }, - [0x1318C]={ + [0x1445F]={ category="lo", direction="l", }, - [0x1318D]={ + [0x14460]={ category="lo", direction="l", }, - [0x1318E]={ + [0x14461]={ category="lo", direction="l", }, - [0x1318F]={ + [0x14462]={ category="lo", direction="l", }, - [0x13190]={ + [0x14463]={ category="lo", direction="l", }, - [0x13191]={ + [0x14464]={ category="lo", direction="l", }, - [0x13192]={ + [0x14465]={ category="lo", direction="l", }, - [0x13193]={ + [0x14466]={ category="lo", direction="l", }, - [0x13194]={ + [0x14467]={ category="lo", direction="l", }, - [0x13195]={ + [0x14468]={ category="lo", direction="l", }, - [0x13196]={ + [0x14469]={ category="lo", direction="l", }, - [0x13197]={ + [0x1446A]={ category="lo", direction="l", }, - [0x13198]={ + [0x1446B]={ category="lo", direction="l", }, - [0x13199]={ + [0x1446C]={ category="lo", direction="l", }, - [0x1319A]={ + [0x1446D]={ category="lo", direction="l", }, - [0x1319B]={ + [0x1446E]={ category="lo", direction="l", }, - [0x1319C]={ + [0x1446F]={ category="lo", direction="l", }, - [0x1319D]={ + [0x14470]={ category="lo", direction="l", }, - [0x1319E]={ + [0x14471]={ category="lo", direction="l", }, - [0x1319F]={ + [0x14472]={ category="lo", direction="l", }, - [0x131A0]={ + [0x14473]={ category="lo", direction="l", }, - [0x131A1]={ + [0x14474]={ category="lo", direction="l", }, - [0x131A2]={ + [0x14475]={ category="lo", direction="l", }, - [0x131A3]={ + [0x14476]={ category="lo", direction="l", }, - [0x131A4]={ + [0x14477]={ category="lo", direction="l", }, - [0x131A5]={ + [0x14478]={ category="lo", direction="l", }, - [0x131A6]={ + [0x14479]={ category="lo", direction="l", }, - [0x131A7]={ + [0x1447A]={ category="lo", direction="l", }, - [0x131A8]={ + [0x1447B]={ category="lo", direction="l", }, - [0x131A9]={ + [0x1447C]={ category="lo", direction="l", }, - [0x131AA]={ + [0x1447D]={ category="lo", direction="l", }, - [0x131AB]={ + [0x1447E]={ category="lo", direction="l", }, - [0x131AC]={ + [0x1447F]={ category="lo", direction="l", }, - [0x131AD]={ + [0x14480]={ category="lo", direction="l", }, - [0x131AE]={ + [0x14481]={ category="lo", direction="l", }, - [0x131AF]={ + [0x14482]={ category="lo", direction="l", }, - [0x131B0]={ + [0x14483]={ category="lo", direction="l", }, - [0x131B1]={ + [0x14484]={ category="lo", direction="l", }, - [0x131B2]={ + [0x14485]={ category="lo", direction="l", }, - [0x131B3]={ + [0x14486]={ category="lo", direction="l", }, - [0x131B4]={ + [0x14487]={ category="lo", direction="l", }, - [0x131B5]={ + [0x14488]={ category="lo", direction="l", }, - [0x131B6]={ + [0x14489]={ category="lo", direction="l", }, - [0x131B7]={ + [0x1448A]={ category="lo", direction="l", }, - [0x131B8]={ + [0x1448B]={ category="lo", direction="l", }, - [0x131B9]={ + [0x1448C]={ category="lo", direction="l", }, - [0x131BA]={ + [0x1448D]={ category="lo", direction="l", }, - [0x131BB]={ + [0x1448E]={ category="lo", direction="l", }, - [0x131BC]={ + [0x1448F]={ category="lo", direction="l", }, - [0x131BD]={ + [0x14490]={ category="lo", direction="l", }, - [0x131BE]={ + [0x14491]={ category="lo", direction="l", }, - [0x131BF]={ + [0x14492]={ category="lo", direction="l", }, - [0x131C0]={ + [0x14493]={ category="lo", direction="l", }, - [0x131C1]={ + [0x14494]={ category="lo", direction="l", }, - [0x131C2]={ + [0x14495]={ category="lo", direction="l", }, - [0x131C3]={ + [0x14496]={ category="lo", direction="l", }, - [0x131C4]={ + [0x14497]={ category="lo", direction="l", }, - [0x131C5]={ + [0x14498]={ category="lo", direction="l", }, - [0x131C6]={ + [0x14499]={ category="lo", direction="l", }, - [0x131C7]={ + [0x1449A]={ category="lo", direction="l", }, - [0x131C8]={ + [0x1449B]={ category="lo", direction="l", }, - [0x131C9]={ + [0x1449C]={ category="lo", direction="l", }, - [0x131CA]={ + [0x1449D]={ category="lo", direction="l", }, - [0x131CB]={ + [0x1449E]={ category="lo", direction="l", }, - [0x131CC]={ + [0x1449F]={ category="lo", direction="l", }, - [0x131CD]={ + [0x144A0]={ category="lo", direction="l", }, - [0x131CE]={ + [0x144A1]={ category="lo", direction="l", }, - [0x131CF]={ + [0x144A2]={ category="lo", direction="l", }, - [0x131D0]={ + [0x144A3]={ category="lo", direction="l", }, - [0x131D1]={ + [0x144A4]={ category="lo", direction="l", }, - [0x131D2]={ + [0x144A5]={ category="lo", direction="l", }, - [0x131D3]={ + [0x144A6]={ category="lo", direction="l", }, - [0x131D4]={ + [0x144A7]={ category="lo", direction="l", }, - [0x131D5]={ + [0x144A8]={ category="lo", direction="l", }, - [0x131D6]={ + [0x144A9]={ category="lo", direction="l", }, - [0x131D7]={ + [0x144AA]={ category="lo", direction="l", }, - [0x131D8]={ + [0x144AB]={ category="lo", direction="l", }, - [0x131D9]={ + [0x144AC]={ category="lo", direction="l", }, - [0x131DA]={ + [0x144AD]={ category="lo", direction="l", }, - [0x131DB]={ + [0x144AE]={ category="lo", direction="l", }, - [0x131DC]={ + [0x144AF]={ category="lo", direction="l", }, - [0x131DD]={ + [0x144B0]={ category="lo", direction="l", }, - [0x131DE]={ + [0x144B1]={ category="lo", direction="l", }, - [0x131DF]={ + [0x144B2]={ category="lo", direction="l", }, - [0x131E0]={ + [0x144B3]={ category="lo", direction="l", }, - [0x131E1]={ + [0x144B4]={ category="lo", direction="l", }, - [0x131E2]={ + [0x144B5]={ category="lo", direction="l", }, - [0x131E3]={ + [0x144B6]={ category="lo", direction="l", }, - [0x131E4]={ + [0x144B7]={ category="lo", direction="l", }, - [0x131E5]={ + [0x144B8]={ category="lo", direction="l", }, - [0x131E6]={ + [0x144B9]={ category="lo", direction="l", }, - [0x131E7]={ + [0x144BA]={ category="lo", direction="l", }, - [0x131E8]={ + [0x144BB]={ category="lo", direction="l", }, - [0x131E9]={ + [0x144BC]={ category="lo", direction="l", }, - [0x131EA]={ + [0x144BD]={ category="lo", direction="l", }, - [0x131EB]={ + [0x144BE]={ category="lo", direction="l", }, - [0x131EC]={ + [0x144BF]={ category="lo", direction="l", }, - [0x131ED]={ + [0x144C0]={ category="lo", direction="l", }, - [0x131EE]={ + [0x144C1]={ category="lo", direction="l", }, - [0x131EF]={ + [0x144C2]={ category="lo", direction="l", }, - [0x131F0]={ + [0x144C3]={ category="lo", direction="l", }, - [0x131F1]={ + [0x144C4]={ category="lo", direction="l", }, - [0x131F2]={ + [0x144C5]={ category="lo", direction="l", }, - [0x131F3]={ + [0x144C6]={ category="lo", direction="l", }, - [0x131F4]={ + [0x144C7]={ category="lo", direction="l", }, - [0x131F5]={ + [0x144C8]={ category="lo", direction="l", }, - [0x131F6]={ + [0x144C9]={ category="lo", direction="l", }, - [0x131F7]={ + [0x144CA]={ category="lo", direction="l", }, - [0x131F8]={ + [0x144CB]={ category="lo", direction="l", }, - [0x131F9]={ + [0x144CC]={ category="lo", direction="l", }, - [0x131FA]={ + [0x144CD]={ category="lo", direction="l", }, - [0x131FB]={ + [0x144CE]={ category="lo", direction="l", }, - [0x131FC]={ + [0x144CF]={ category="lo", direction="l", }, - [0x131FD]={ + [0x144D0]={ category="lo", direction="l", }, - [0x131FE]={ + [0x144D1]={ category="lo", direction="l", }, - [0x131FF]={ + [0x144D2]={ category="lo", direction="l", }, - [0x13200]={ + [0x144D3]={ category="lo", direction="l", }, - [0x13201]={ + [0x144D4]={ category="lo", direction="l", }, - [0x13202]={ + [0x144D5]={ category="lo", direction="l", }, - [0x13203]={ + [0x144D6]={ category="lo", direction="l", }, - [0x13204]={ + [0x144D7]={ category="lo", direction="l", }, - [0x13205]={ + [0x144D8]={ category="lo", direction="l", }, - [0x13206]={ + [0x144D9]={ category="lo", direction="l", }, - [0x13207]={ + [0x144DA]={ category="lo", direction="l", }, - [0x13208]={ + [0x144DB]={ category="lo", direction="l", }, - [0x13209]={ + [0x144DC]={ category="lo", direction="l", }, - [0x1320A]={ + [0x144DD]={ category="lo", direction="l", }, - [0x1320B]={ + [0x144DE]={ category="lo", direction="l", }, - [0x1320C]={ + [0x144DF]={ category="lo", direction="l", }, - [0x1320D]={ + [0x144E0]={ category="lo", direction="l", }, - [0x1320E]={ + [0x144E1]={ category="lo", direction="l", }, - [0x1320F]={ + [0x144E2]={ category="lo", direction="l", }, - [0x13210]={ + [0x144E3]={ category="lo", direction="l", }, - [0x13211]={ + [0x144E4]={ category="lo", direction="l", }, - [0x13212]={ + [0x144E5]={ category="lo", direction="l", }, - [0x13213]={ + [0x144E6]={ category="lo", direction="l", }, - [0x13214]={ + [0x144E7]={ category="lo", direction="l", }, - [0x13215]={ + [0x144E8]={ category="lo", direction="l", }, - [0x13216]={ + [0x144E9]={ category="lo", direction="l", }, - [0x13217]={ + [0x144EA]={ category="lo", direction="l", }, - [0x13218]={ + [0x144EB]={ category="lo", direction="l", }, - [0x13219]={ + [0x144EC]={ category="lo", direction="l", }, - [0x1321A]={ + [0x144ED]={ category="lo", direction="l", }, - [0x1321B]={ + [0x144EE]={ category="lo", direction="l", }, - [0x1321C]={ + [0x144EF]={ category="lo", direction="l", }, - [0x1321D]={ + [0x144F0]={ category="lo", direction="l", }, - [0x1321E]={ + [0x144F1]={ category="lo", direction="l", }, - [0x1321F]={ + [0x144F2]={ category="lo", direction="l", }, - [0x13220]={ + [0x144F3]={ category="lo", direction="l", }, - [0x13221]={ + [0x144F4]={ category="lo", direction="l", }, - [0x13222]={ + [0x144F5]={ category="lo", direction="l", }, - [0x13223]={ + [0x144F6]={ category="lo", direction="l", }, - [0x13224]={ + [0x144F7]={ category="lo", direction="l", }, - [0x13225]={ + [0x144F8]={ category="lo", direction="l", }, - [0x13226]={ + [0x144F9]={ category="lo", direction="l", }, - [0x13227]={ + [0x144FA]={ category="lo", direction="l", }, - [0x13228]={ + [0x144FB]={ category="lo", direction="l", }, - [0x13229]={ + [0x144FC]={ category="lo", direction="l", }, - [0x1322A]={ + [0x144FD]={ category="lo", direction="l", }, - [0x1322B]={ + [0x144FE]={ category="lo", direction="l", }, - [0x1322C]={ + [0x144FF]={ category="lo", direction="l", }, - [0x1322D]={ + [0x14500]={ category="lo", direction="l", }, - [0x1322E]={ + [0x14501]={ category="lo", direction="l", }, - [0x1322F]={ + [0x14502]={ category="lo", direction="l", }, - [0x13230]={ + [0x14503]={ category="lo", direction="l", }, - [0x13231]={ + [0x14504]={ category="lo", direction="l", }, - [0x13232]={ + [0x14505]={ category="lo", direction="l", }, - [0x13233]={ + [0x14506]={ category="lo", direction="l", }, - [0x13234]={ + [0x14507]={ category="lo", direction="l", }, - [0x13235]={ + [0x14508]={ category="lo", direction="l", }, - [0x13236]={ + [0x14509]={ category="lo", direction="l", }, - [0x13237]={ + [0x1450A]={ category="lo", direction="l", }, - [0x13238]={ + [0x1450B]={ category="lo", direction="l", }, - [0x13239]={ + [0x1450C]={ category="lo", direction="l", }, - [0x1323A]={ + [0x1450D]={ category="lo", direction="l", }, - [0x1323B]={ + [0x1450E]={ category="lo", direction="l", }, - [0x1323C]={ + [0x1450F]={ category="lo", direction="l", }, - [0x1323D]={ + [0x14510]={ category="lo", direction="l", }, - [0x1323E]={ + [0x14511]={ category="lo", direction="l", }, - [0x1323F]={ + [0x14512]={ category="lo", direction="l", }, - [0x13240]={ + [0x14513]={ category="lo", direction="l", }, - [0x13241]={ + [0x14514]={ category="lo", direction="l", }, - [0x13242]={ + [0x14515]={ category="lo", direction="l", }, - [0x13243]={ + [0x14516]={ category="lo", direction="l", }, - [0x13244]={ + [0x14517]={ category="lo", direction="l", }, - [0x13245]={ + [0x14518]={ category="lo", direction="l", }, - [0x13246]={ + [0x14519]={ category="lo", direction="l", }, - [0x13247]={ + [0x1451A]={ category="lo", direction="l", }, - [0x13248]={ + [0x1451B]={ category="lo", direction="l", }, - [0x13249]={ + [0x1451C]={ category="lo", direction="l", }, - [0x1324A]={ + [0x1451D]={ category="lo", direction="l", }, - [0x1324B]={ + [0x1451E]={ category="lo", direction="l", }, - [0x1324C]={ + [0x1451F]={ category="lo", direction="l", }, - [0x1324D]={ + [0x14520]={ category="lo", direction="l", }, - [0x1324E]={ + [0x14521]={ category="lo", direction="l", }, - [0x1324F]={ + [0x14522]={ category="lo", direction="l", }, - [0x13250]={ + [0x14523]={ category="lo", direction="l", }, - [0x13251]={ + [0x14524]={ category="lo", direction="l", }, - [0x13252]={ + [0x14525]={ category="lo", direction="l", }, - [0x13253]={ + [0x14526]={ category="lo", direction="l", }, - [0x13254]={ + [0x14527]={ category="lo", direction="l", }, - [0x13255]={ + [0x14528]={ category="lo", direction="l", }, - [0x13256]={ + [0x14529]={ category="lo", direction="l", }, - [0x13257]={ + [0x1452A]={ category="lo", direction="l", }, - [0x13258]={ + [0x1452B]={ category="lo", direction="l", }, - [0x13259]={ + [0x1452C]={ category="lo", direction="l", }, - [0x1325A]={ + [0x1452D]={ category="lo", direction="l", }, - [0x1325B]={ + [0x1452E]={ category="lo", direction="l", }, - [0x1325C]={ + [0x1452F]={ category="lo", direction="l", }, - [0x1325D]={ + [0x14530]={ category="lo", direction="l", }, - [0x1325E]={ + [0x14531]={ category="lo", direction="l", }, - [0x1325F]={ + [0x14532]={ category="lo", direction="l", }, - [0x13260]={ + [0x14533]={ category="lo", direction="l", }, - [0x13261]={ + [0x14534]={ category="lo", direction="l", }, - [0x13262]={ + [0x14535]={ category="lo", direction="l", }, - [0x13263]={ + [0x14536]={ category="lo", direction="l", }, - [0x13264]={ + [0x14537]={ category="lo", direction="l", }, - [0x13265]={ + [0x14538]={ category="lo", direction="l", }, - [0x13266]={ + [0x14539]={ category="lo", direction="l", }, - [0x13267]={ + [0x1453A]={ category="lo", direction="l", }, - [0x13268]={ + [0x1453B]={ category="lo", direction="l", }, - [0x13269]={ + [0x1453C]={ category="lo", direction="l", }, - [0x1326A]={ + [0x1453D]={ category="lo", direction="l", }, - [0x1326B]={ + [0x1453E]={ category="lo", direction="l", }, - [0x1326C]={ + [0x1453F]={ category="lo", direction="l", }, - [0x1326D]={ + [0x14540]={ category="lo", direction="l", }, - [0x1326E]={ + [0x14541]={ category="lo", direction="l", }, - [0x1326F]={ + [0x14542]={ category="lo", direction="l", }, - [0x13270]={ + [0x14543]={ category="lo", direction="l", }, - [0x13271]={ + [0x14544]={ category="lo", direction="l", }, - [0x13272]={ + [0x14545]={ category="lo", direction="l", }, - [0x13273]={ + [0x14546]={ category="lo", direction="l", }, - [0x13274]={ + [0x14547]={ category="lo", direction="l", }, - [0x13275]={ + [0x14548]={ category="lo", direction="l", }, - [0x13276]={ + [0x14549]={ category="lo", direction="l", }, - [0x13277]={ + [0x1454A]={ category="lo", direction="l", }, - [0x13278]={ + [0x1454B]={ category="lo", direction="l", }, - [0x13279]={ + [0x1454C]={ category="lo", direction="l", }, - [0x1327A]={ + [0x1454D]={ category="lo", direction="l", }, - [0x1327B]={ + [0x1454E]={ category="lo", direction="l", }, - [0x1327C]={ + [0x1454F]={ category="lo", direction="l", }, - [0x1327D]={ + [0x14550]={ category="lo", direction="l", }, - [0x1327E]={ + [0x14551]={ category="lo", direction="l", }, - [0x1327F]={ + [0x14552]={ category="lo", direction="l", }, - [0x13280]={ + [0x14553]={ category="lo", direction="l", }, - [0x13281]={ + [0x14554]={ category="lo", direction="l", }, - [0x13282]={ + [0x14555]={ category="lo", direction="l", }, - [0x13283]={ + [0x14556]={ category="lo", direction="l", }, - [0x13284]={ + [0x14557]={ category="lo", direction="l", }, - [0x13285]={ + [0x14558]={ category="lo", direction="l", }, - [0x13286]={ + [0x14559]={ category="lo", direction="l", }, - [0x13287]={ + [0x1455A]={ category="lo", direction="l", }, - [0x13288]={ + [0x1455B]={ category="lo", direction="l", }, - [0x13289]={ + [0x1455C]={ category="lo", direction="l", }, - [0x1328A]={ + [0x1455D]={ category="lo", direction="l", }, - [0x1328B]={ + [0x1455E]={ category="lo", direction="l", }, - [0x1328C]={ + [0x1455F]={ category="lo", direction="l", }, - [0x1328D]={ + [0x14560]={ category="lo", direction="l", }, - [0x1328E]={ + [0x14561]={ category="lo", direction="l", }, - [0x1328F]={ + [0x14562]={ category="lo", direction="l", }, - [0x13290]={ + [0x14563]={ category="lo", direction="l", }, - [0x13291]={ + [0x14564]={ category="lo", direction="l", }, - [0x13292]={ + [0x14565]={ category="lo", direction="l", }, - [0x13293]={ + [0x14566]={ category="lo", direction="l", }, - [0x13294]={ + [0x14567]={ category="lo", direction="l", }, - [0x13295]={ + [0x14568]={ category="lo", direction="l", }, - [0x13296]={ + [0x14569]={ category="lo", direction="l", }, - [0x13297]={ + [0x1456A]={ category="lo", direction="l", }, - [0x13298]={ + [0x1456B]={ category="lo", direction="l", }, - [0x13299]={ + [0x1456C]={ category="lo", direction="l", }, - [0x1329A]={ + [0x1456D]={ category="lo", direction="l", }, - [0x1329B]={ + [0x1456E]={ category="lo", direction="l", }, - [0x1329C]={ + [0x1456F]={ category="lo", direction="l", }, - [0x1329D]={ + [0x14570]={ category="lo", direction="l", }, - [0x1329E]={ + [0x14571]={ category="lo", direction="l", }, - [0x1329F]={ + [0x14572]={ category="lo", direction="l", }, - [0x132A0]={ + [0x14573]={ category="lo", direction="l", }, - [0x132A1]={ + [0x14574]={ category="lo", direction="l", }, - [0x132A2]={ + [0x14575]={ category="lo", direction="l", }, - [0x132A3]={ + [0x14576]={ category="lo", direction="l", }, - [0x132A4]={ + [0x14577]={ category="lo", direction="l", }, - [0x132A5]={ + [0x14578]={ category="lo", direction="l", }, - [0x132A6]={ + [0x14579]={ category="lo", direction="l", }, - [0x132A7]={ + [0x1457A]={ category="lo", direction="l", }, - [0x132A8]={ + [0x1457B]={ category="lo", direction="l", }, - [0x132A9]={ + [0x1457C]={ category="lo", direction="l", }, - [0x132AA]={ + [0x1457D]={ category="lo", direction="l", }, - [0x132AB]={ + [0x1457E]={ category="lo", direction="l", }, - [0x132AC]={ + [0x1457F]={ category="lo", direction="l", }, - [0x132AD]={ + [0x14580]={ category="lo", direction="l", }, - [0x132AE]={ + [0x14581]={ category="lo", direction="l", }, - [0x132AF]={ + [0x14582]={ category="lo", direction="l", }, - [0x132B0]={ + [0x14583]={ category="lo", direction="l", }, - [0x132B1]={ + [0x14584]={ category="lo", direction="l", }, - [0x132B2]={ + [0x14585]={ category="lo", direction="l", }, - [0x132B3]={ + [0x14586]={ category="lo", direction="l", }, - [0x132B4]={ + [0x14587]={ category="lo", direction="l", }, - [0x132B5]={ + [0x14588]={ category="lo", direction="l", }, - [0x132B6]={ + [0x14589]={ category="lo", direction="l", }, - [0x132B7]={ + [0x1458A]={ category="lo", direction="l", }, - [0x132B8]={ + [0x1458B]={ category="lo", direction="l", }, - [0x132B9]={ + [0x1458C]={ category="lo", direction="l", }, - [0x132BA]={ + [0x1458D]={ category="lo", direction="l", }, - [0x132BB]={ + [0x1458E]={ category="lo", direction="l", }, - [0x132BC]={ + [0x1458F]={ category="lo", direction="l", }, - [0x132BD]={ + [0x14590]={ category="lo", direction="l", }, - [0x132BE]={ + [0x14591]={ category="lo", direction="l", }, - [0x132BF]={ + [0x14592]={ category="lo", direction="l", }, - [0x132C0]={ + [0x14593]={ category="lo", direction="l", }, - [0x132C1]={ + [0x14594]={ category="lo", direction="l", }, - [0x132C2]={ + [0x14595]={ category="lo", direction="l", }, - [0x132C3]={ + [0x14596]={ category="lo", direction="l", }, - [0x132C4]={ + [0x14597]={ category="lo", direction="l", }, - [0x132C5]={ + [0x14598]={ category="lo", direction="l", }, - [0x132C6]={ + [0x14599]={ category="lo", direction="l", }, - [0x132C7]={ + [0x1459A]={ category="lo", direction="l", }, - [0x132C8]={ + [0x1459B]={ category="lo", direction="l", }, - [0x132C9]={ + [0x1459C]={ category="lo", direction="l", }, - [0x132CA]={ + [0x1459D]={ category="lo", direction="l", }, - [0x132CB]={ + [0x1459E]={ category="lo", direction="l", }, - [0x132CC]={ + [0x1459F]={ category="lo", direction="l", }, - [0x132CD]={ + [0x145A0]={ category="lo", direction="l", }, - [0x132CE]={ + [0x145A1]={ category="lo", direction="l", }, - [0x132CF]={ + [0x145A2]={ category="lo", direction="l", }, - [0x132D0]={ + [0x145A3]={ category="lo", direction="l", }, - [0x132D1]={ + [0x145A4]={ category="lo", direction="l", }, - [0x132D2]={ + [0x145A5]={ category="lo", direction="l", }, - [0x132D3]={ + [0x145A6]={ category="lo", direction="l", }, - [0x132D4]={ + [0x145A7]={ category="lo", direction="l", }, - [0x132D5]={ + [0x145A8]={ category="lo", direction="l", }, - [0x132D6]={ + [0x145A9]={ category="lo", direction="l", }, - [0x132D7]={ + [0x145AA]={ category="lo", direction="l", }, - [0x132D8]={ + [0x145AB]={ category="lo", direction="l", }, - [0x132D9]={ + [0x145AC]={ category="lo", direction="l", }, - [0x132DA]={ + [0x145AD]={ category="lo", direction="l", }, - [0x132DB]={ + [0x145AE]={ category="lo", direction="l", }, - [0x132DC]={ + [0x145AF]={ category="lo", direction="l", }, - [0x132DD]={ + [0x145B0]={ category="lo", direction="l", }, - [0x132DE]={ + [0x145B1]={ category="lo", direction="l", }, - [0x132DF]={ + [0x145B2]={ category="lo", direction="l", }, - [0x132E0]={ + [0x145B3]={ category="lo", direction="l", }, - [0x132E1]={ + [0x145B4]={ category="lo", direction="l", }, - [0x132E2]={ + [0x145B5]={ category="lo", direction="l", }, - [0x132E3]={ + [0x145B6]={ category="lo", direction="l", }, - [0x132E4]={ + [0x145B7]={ category="lo", direction="l", }, - [0x132E5]={ + [0x145B8]={ category="lo", direction="l", }, - [0x132E6]={ + [0x145B9]={ category="lo", direction="l", }, - [0x132E7]={ + [0x145BA]={ category="lo", direction="l", }, - [0x132E8]={ + [0x145BB]={ category="lo", direction="l", }, - [0x132E9]={ + [0x145BC]={ category="lo", direction="l", }, - [0x132EA]={ + [0x145BD]={ category="lo", direction="l", }, - [0x132EB]={ + [0x145BE]={ category="lo", direction="l", }, - [0x132EC]={ + [0x145BF]={ category="lo", direction="l", }, - [0x132ED]={ + [0x145C0]={ category="lo", direction="l", }, - [0x132EE]={ + [0x145C1]={ category="lo", direction="l", }, - [0x132EF]={ + [0x145C2]={ category="lo", direction="l", }, - [0x132F0]={ + [0x145C3]={ category="lo", direction="l", }, - [0x132F1]={ + [0x145C4]={ category="lo", direction="l", }, - [0x132F2]={ + [0x145C5]={ category="lo", direction="l", }, - [0x132F3]={ + [0x145C6]={ category="lo", direction="l", }, - [0x132F4]={ + [0x145C7]={ category="lo", direction="l", }, - [0x132F5]={ + [0x145C8]={ category="lo", direction="l", }, - [0x132F6]={ + [0x145C9]={ category="lo", direction="l", }, - [0x132F7]={ + [0x145CA]={ category="lo", direction="l", }, - [0x132F8]={ + [0x145CB]={ category="lo", direction="l", }, - [0x132F9]={ + [0x145CC]={ category="lo", direction="l", }, - [0x132FA]={ + [0x145CD]={ category="lo", direction="l", }, - [0x132FB]={ + [0x145CE]={ category="lo", direction="l", }, - [0x132FC]={ + [0x145CF]={ category="lo", direction="l", }, - [0x132FD]={ + [0x145D0]={ category="lo", direction="l", }, - [0x132FE]={ + [0x145D1]={ category="lo", direction="l", }, - [0x132FF]={ + [0x145D2]={ category="lo", direction="l", }, - [0x13300]={ + [0x145D3]={ category="lo", direction="l", }, - [0x13301]={ + [0x145D4]={ category="lo", direction="l", }, - [0x13302]={ + [0x145D5]={ category="lo", direction="l", }, - [0x13303]={ + [0x145D6]={ category="lo", direction="l", }, - [0x13304]={ + [0x145D7]={ category="lo", direction="l", }, - [0x13305]={ + [0x145D8]={ category="lo", direction="l", }, - [0x13306]={ + [0x145D9]={ category="lo", direction="l", }, - [0x13307]={ + [0x145DA]={ category="lo", direction="l", }, - [0x13308]={ + [0x145DB]={ category="lo", direction="l", }, - [0x13309]={ + [0x145DC]={ category="lo", direction="l", }, - [0x1330A]={ + [0x145DD]={ category="lo", direction="l", }, - [0x1330B]={ + [0x145DE]={ category="lo", direction="l", }, - [0x1330C]={ + [0x145DF]={ category="lo", direction="l", }, - [0x1330D]={ + [0x145E0]={ category="lo", direction="l", }, - [0x1330E]={ + [0x145E1]={ category="lo", direction="l", }, - [0x1330F]={ + [0x145E2]={ category="lo", direction="l", }, - [0x13310]={ + [0x145E3]={ category="lo", direction="l", }, - [0x13311]={ + [0x145E4]={ category="lo", direction="l", }, - [0x13312]={ + [0x145E5]={ category="lo", direction="l", }, - [0x13313]={ + [0x145E6]={ category="lo", direction="l", }, - [0x13314]={ + [0x145E7]={ category="lo", direction="l", }, - [0x13315]={ + [0x145E8]={ category="lo", direction="l", }, - [0x13316]={ + [0x145E9]={ category="lo", direction="l", }, - [0x13317]={ + [0x145EA]={ category="lo", direction="l", }, - [0x13318]={ + [0x145EB]={ category="lo", direction="l", }, - [0x13319]={ + [0x145EC]={ category="lo", direction="l", }, - [0x1331A]={ + [0x145ED]={ category="lo", direction="l", }, - [0x1331B]={ + [0x145EE]={ category="lo", direction="l", }, - [0x1331C]={ + [0x145EF]={ category="lo", direction="l", }, - [0x1331D]={ + [0x145F0]={ category="lo", direction="l", }, - [0x1331E]={ + [0x145F1]={ category="lo", direction="l", }, - [0x1331F]={ + [0x145F2]={ category="lo", direction="l", }, - [0x13320]={ + [0x145F3]={ category="lo", direction="l", }, - [0x13321]={ + [0x145F4]={ category="lo", direction="l", }, - [0x13322]={ + [0x145F5]={ category="lo", direction="l", }, - [0x13323]={ + [0x145F6]={ category="lo", direction="l", }, - [0x13324]={ + [0x145F7]={ category="lo", direction="l", }, - [0x13325]={ + [0x145F8]={ category="lo", direction="l", }, - [0x13326]={ + [0x145F9]={ category="lo", direction="l", }, - [0x13327]={ + [0x145FA]={ category="lo", direction="l", }, - [0x13328]={ + [0x145FB]={ category="lo", direction="l", }, - [0x13329]={ + [0x145FC]={ category="lo", direction="l", }, - [0x1332A]={ + [0x145FD]={ category="lo", direction="l", }, - [0x1332B]={ + [0x145FE]={ category="lo", direction="l", }, - [0x1332C]={ + [0x145FF]={ category="lo", direction="l", }, - [0x1332D]={ + [0x14600]={ category="lo", direction="l", }, - [0x1332E]={ + [0x14601]={ category="lo", direction="l", }, - [0x1332F]={ + [0x14602]={ category="lo", direction="l", }, - [0x13330]={ + [0x14603]={ category="lo", direction="l", }, - [0x13331]={ + [0x14604]={ category="lo", direction="l", }, - [0x13332]={ + [0x14605]={ category="lo", direction="l", }, - [0x13333]={ + [0x14606]={ category="lo", direction="l", }, - [0x13334]={ + [0x14607]={ category="lo", direction="l", }, - [0x13335]={ + [0x14608]={ category="lo", direction="l", }, - [0x13336]={ + [0x14609]={ category="lo", direction="l", }, - [0x13337]={ + [0x1460A]={ category="lo", direction="l", }, - [0x13338]={ + [0x1460B]={ category="lo", direction="l", }, - [0x13339]={ + [0x1460C]={ category="lo", direction="l", }, - [0x1333A]={ + [0x1460D]={ category="lo", direction="l", }, - [0x1333B]={ + [0x1460E]={ category="lo", direction="l", }, - [0x1333C]={ + [0x1460F]={ category="lo", direction="l", }, - [0x1333D]={ + [0x14610]={ category="lo", direction="l", }, - [0x1333E]={ + [0x14611]={ category="lo", direction="l", }, - [0x1333F]={ + [0x14612]={ category="lo", direction="l", }, - [0x13340]={ + [0x14613]={ category="lo", direction="l", }, - [0x13341]={ + [0x14614]={ category="lo", direction="l", }, - [0x13342]={ + [0x14615]={ category="lo", direction="l", }, - [0x13343]={ + [0x14616]={ category="lo", direction="l", }, - [0x13344]={ + [0x14617]={ category="lo", direction="l", }, - [0x13345]={ + [0x14618]={ category="lo", direction="l", }, - [0x13346]={ + [0x14619]={ category="lo", direction="l", }, - [0x13347]={ + [0x1461A]={ category="lo", direction="l", }, - [0x13348]={ + [0x1461B]={ category="lo", direction="l", }, - [0x13349]={ + [0x1461C]={ category="lo", direction="l", }, - [0x1334A]={ + [0x1461D]={ category="lo", direction="l", }, - [0x1334B]={ + [0x1461E]={ category="lo", direction="l", }, - [0x1334C]={ + [0x1461F]={ category="lo", direction="l", }, - [0x1334D]={ + [0x14620]={ category="lo", direction="l", }, - [0x1334E]={ + [0x14621]={ category="lo", direction="l", }, - [0x1334F]={ + [0x14622]={ category="lo", direction="l", }, - [0x13350]={ + [0x14623]={ category="lo", direction="l", }, - [0x13351]={ + [0x14624]={ category="lo", direction="l", }, - [0x13352]={ + [0x14625]={ category="lo", direction="l", }, - [0x13353]={ + [0x14626]={ category="lo", direction="l", }, - [0x13354]={ + [0x14627]={ category="lo", direction="l", }, - [0x13355]={ + [0x14628]={ category="lo", direction="l", }, - [0x13356]={ + [0x14629]={ category="lo", direction="l", }, - [0x13357]={ + [0x1462A]={ category="lo", direction="l", }, - [0x13358]={ + [0x1462B]={ category="lo", direction="l", }, - [0x13359]={ + [0x1462C]={ category="lo", direction="l", }, - [0x1335A]={ + [0x1462D]={ category="lo", direction="l", }, - [0x1335B]={ + [0x1462E]={ category="lo", direction="l", }, - [0x1335C]={ + [0x1462F]={ category="lo", direction="l", }, - [0x1335D]={ + [0x14630]={ category="lo", direction="l", }, - [0x1335E]={ + [0x14631]={ category="lo", direction="l", }, - [0x1335F]={ + [0x14632]={ category="lo", direction="l", }, - [0x13360]={ + [0x14633]={ category="lo", direction="l", }, - [0x13361]={ + [0x14634]={ category="lo", direction="l", }, - [0x13362]={ + [0x14635]={ category="lo", direction="l", }, - [0x13363]={ + [0x14636]={ category="lo", direction="l", }, - [0x13364]={ + [0x14637]={ category="lo", direction="l", }, - [0x13365]={ + [0x14638]={ category="lo", direction="l", }, - [0x13366]={ + [0x14639]={ category="lo", direction="l", }, - [0x13367]={ + [0x1463A]={ category="lo", direction="l", }, - [0x13368]={ + [0x1463B]={ category="lo", direction="l", }, - [0x13369]={ + [0x1463C]={ category="lo", direction="l", }, - [0x1336A]={ + [0x1463D]={ category="lo", direction="l", }, - [0x1336B]={ + [0x1463E]={ category="lo", direction="l", }, - [0x1336C]={ + [0x1463F]={ category="lo", direction="l", }, - [0x1336D]={ + [0x14640]={ category="lo", direction="l", }, - [0x1336E]={ + [0x14641]={ category="lo", direction="l", }, - [0x1336F]={ + [0x14642]={ category="lo", direction="l", }, - [0x13370]={ + [0x14643]={ category="lo", direction="l", }, - [0x13371]={ + [0x14644]={ category="lo", direction="l", }, - [0x13372]={ + [0x14645]={ category="lo", direction="l", }, - [0x13373]={ + [0x14646]={ category="lo", direction="l", }, - [0x13374]={ + [0x16800]={ category="lo", direction="l", }, - [0x13375]={ + [0x16801]={ category="lo", direction="l", }, - [0x13376]={ + [0x16802]={ category="lo", direction="l", }, - [0x13377]={ + [0x16803]={ category="lo", direction="l", }, - [0x13378]={ + [0x16804]={ category="lo", direction="l", }, - [0x13379]={ + [0x16805]={ category="lo", direction="l", }, - [0x1337A]={ + [0x16806]={ category="lo", direction="l", }, - [0x1337B]={ + [0x16807]={ category="lo", direction="l", }, - [0x1337C]={ + [0x16808]={ category="lo", direction="l", }, - [0x1337D]={ + [0x16809]={ category="lo", direction="l", }, - [0x1337E]={ + [0x1680A]={ category="lo", direction="l", }, - [0x1337F]={ + [0x1680B]={ category="lo", direction="l", }, - [0x13380]={ + [0x1680C]={ category="lo", direction="l", }, - [0x13381]={ + [0x1680D]={ category="lo", direction="l", }, - [0x13382]={ + [0x1680E]={ category="lo", direction="l", }, - [0x13383]={ + [0x1680F]={ category="lo", direction="l", }, - [0x13384]={ + [0x16810]={ category="lo", direction="l", }, - [0x13385]={ + [0x16811]={ category="lo", direction="l", }, - [0x13386]={ + [0x16812]={ category="lo", direction="l", }, - [0x13387]={ + [0x16813]={ category="lo", direction="l", }, - [0x13388]={ + [0x16814]={ category="lo", direction="l", }, - [0x13389]={ + [0x16815]={ category="lo", direction="l", }, - [0x1338A]={ + [0x16816]={ category="lo", direction="l", }, - [0x1338B]={ + [0x16817]={ category="lo", direction="l", }, - [0x1338C]={ + [0x16818]={ category="lo", direction="l", }, - [0x1338D]={ + [0x16819]={ category="lo", direction="l", }, - [0x1338E]={ + [0x1681A]={ category="lo", direction="l", }, - [0x1338F]={ + [0x1681B]={ category="lo", direction="l", }, - [0x13390]={ + [0x1681C]={ category="lo", direction="l", }, - [0x13391]={ + [0x1681D]={ category="lo", direction="l", }, - [0x13392]={ + [0x1681E]={ category="lo", direction="l", }, - [0x13393]={ + [0x1681F]={ category="lo", direction="l", }, - [0x13394]={ + [0x16820]={ category="lo", direction="l", }, - [0x13395]={ + [0x16821]={ category="lo", direction="l", }, - [0x13396]={ + [0x16822]={ category="lo", direction="l", }, - [0x13397]={ + [0x16823]={ category="lo", direction="l", }, - [0x13398]={ + [0x16824]={ category="lo", direction="l", }, - [0x13399]={ + [0x16825]={ category="lo", direction="l", }, - [0x1339A]={ + [0x16826]={ category="lo", direction="l", }, - [0x1339B]={ + [0x16827]={ category="lo", direction="l", }, - [0x1339C]={ + [0x16828]={ category="lo", direction="l", }, - [0x1339D]={ + [0x16829]={ category="lo", direction="l", }, - [0x1339E]={ + [0x1682A]={ category="lo", direction="l", }, - [0x1339F]={ + [0x1682B]={ category="lo", direction="l", }, - [0x133A0]={ + [0x1682C]={ category="lo", direction="l", }, - [0x133A1]={ + [0x1682D]={ category="lo", direction="l", }, - [0x133A2]={ + [0x1682E]={ category="lo", direction="l", }, - [0x133A3]={ + [0x1682F]={ category="lo", direction="l", }, - [0x133A4]={ + [0x16830]={ category="lo", direction="l", }, - [0x133A5]={ + [0x16831]={ category="lo", direction="l", }, - [0x133A6]={ + [0x16832]={ category="lo", direction="l", }, - [0x133A7]={ + [0x16833]={ category="lo", direction="l", }, - [0x133A8]={ + [0x16834]={ category="lo", direction="l", }, - [0x133A9]={ + [0x16835]={ category="lo", direction="l", }, - [0x133AA]={ + [0x16836]={ category="lo", direction="l", }, - [0x133AB]={ + [0x16837]={ category="lo", direction="l", }, - [0x133AC]={ + [0x16838]={ category="lo", direction="l", }, - [0x133AD]={ + [0x16839]={ category="lo", direction="l", }, - [0x133AE]={ + [0x1683A]={ category="lo", direction="l", }, - [0x133AF]={ + [0x1683B]={ category="lo", direction="l", }, - [0x133B0]={ + [0x1683C]={ category="lo", direction="l", }, - [0x133B1]={ + [0x1683D]={ category="lo", direction="l", }, - [0x133B2]={ + [0x1683E]={ category="lo", direction="l", }, - [0x133B3]={ + [0x1683F]={ category="lo", direction="l", }, - [0x133B4]={ + [0x16840]={ category="lo", direction="l", }, - [0x133B5]={ + [0x16841]={ category="lo", direction="l", }, - [0x133B6]={ + [0x16842]={ category="lo", direction="l", }, - [0x133B7]={ + [0x16843]={ category="lo", direction="l", }, - [0x133B8]={ + [0x16844]={ category="lo", direction="l", }, - [0x133B9]={ + [0x16845]={ category="lo", direction="l", }, - [0x133BA]={ + [0x16846]={ category="lo", direction="l", }, - [0x133BB]={ + [0x16847]={ category="lo", direction="l", }, - [0x133BC]={ + [0x16848]={ category="lo", direction="l", }, - [0x133BD]={ + [0x16849]={ category="lo", direction="l", }, - [0x133BE]={ + [0x1684A]={ category="lo", direction="l", }, - [0x133BF]={ + [0x1684B]={ category="lo", direction="l", }, - [0x133C0]={ + [0x1684C]={ category="lo", direction="l", }, - [0x133C1]={ + [0x1684D]={ category="lo", direction="l", }, - [0x133C2]={ + [0x1684E]={ category="lo", direction="l", }, - [0x133C3]={ + [0x1684F]={ category="lo", direction="l", }, - [0x133C4]={ + [0x16850]={ category="lo", direction="l", }, - [0x133C5]={ + [0x16851]={ category="lo", direction="l", }, - [0x133C6]={ + [0x16852]={ category="lo", direction="l", }, - [0x133C7]={ + [0x16853]={ category="lo", direction="l", }, - [0x133C8]={ + [0x16854]={ category="lo", direction="l", }, - [0x133C9]={ + [0x16855]={ category="lo", direction="l", }, - [0x133CA]={ + [0x16856]={ category="lo", direction="l", }, - [0x133CB]={ + [0x16857]={ category="lo", direction="l", }, - [0x133CC]={ + [0x16858]={ category="lo", direction="l", }, - [0x133CD]={ + [0x16859]={ category="lo", direction="l", }, - [0x133CE]={ + [0x1685A]={ category="lo", direction="l", }, - [0x133CF]={ + [0x1685B]={ category="lo", direction="l", }, - [0x133D0]={ + [0x1685C]={ category="lo", direction="l", }, - [0x133D1]={ + [0x1685D]={ category="lo", direction="l", }, - [0x133D2]={ + [0x1685E]={ category="lo", direction="l", }, - [0x133D3]={ + [0x1685F]={ category="lo", direction="l", }, - [0x133D4]={ + [0x16860]={ category="lo", direction="l", }, - [0x133D5]={ + [0x16861]={ category="lo", direction="l", }, - [0x133D6]={ + [0x16862]={ category="lo", direction="l", }, - [0x133D7]={ + [0x16863]={ category="lo", direction="l", }, - [0x133D8]={ + [0x16864]={ category="lo", direction="l", }, - [0x133D9]={ + [0x16865]={ category="lo", direction="l", }, - [0x133DA]={ + [0x16866]={ category="lo", direction="l", }, - [0x133DB]={ + [0x16867]={ category="lo", direction="l", }, - [0x133DC]={ + [0x16868]={ category="lo", direction="l", }, - [0x133DD]={ + [0x16869]={ category="lo", direction="l", }, - [0x133DE]={ + [0x1686A]={ category="lo", direction="l", }, - [0x133DF]={ + [0x1686B]={ category="lo", direction="l", }, - [0x133E0]={ + [0x1686C]={ category="lo", direction="l", }, - [0x133E1]={ + [0x1686D]={ category="lo", direction="l", }, - [0x133E2]={ + [0x1686E]={ category="lo", direction="l", }, - [0x133E3]={ + [0x1686F]={ category="lo", direction="l", }, - [0x133E4]={ + [0x16870]={ category="lo", direction="l", }, - [0x133E5]={ + [0x16871]={ category="lo", direction="l", }, - [0x133E6]={ + [0x16872]={ category="lo", direction="l", }, - [0x133E7]={ + [0x16873]={ category="lo", direction="l", }, - [0x133E8]={ + [0x16874]={ category="lo", direction="l", }, - [0x133E9]={ + [0x16875]={ category="lo", direction="l", }, - [0x133EA]={ + [0x16876]={ category="lo", direction="l", }, - [0x133EB]={ + [0x16877]={ category="lo", direction="l", }, - [0x133EC]={ + [0x16878]={ category="lo", direction="l", }, - [0x133ED]={ + [0x16879]={ category="lo", direction="l", }, - [0x133EE]={ + [0x1687A]={ category="lo", direction="l", }, - [0x133EF]={ + [0x1687B]={ category="lo", direction="l", }, - [0x133F0]={ + [0x1687C]={ category="lo", direction="l", }, - [0x133F1]={ + [0x1687D]={ category="lo", direction="l", }, - [0x133F2]={ + [0x1687E]={ category="lo", direction="l", }, - [0x133F3]={ + [0x1687F]={ category="lo", direction="l", }, - [0x133F4]={ + [0x16880]={ category="lo", direction="l", }, - [0x133F5]={ + [0x16881]={ category="lo", direction="l", }, - [0x133F6]={ + [0x16882]={ category="lo", direction="l", }, - [0x133F7]={ + [0x16883]={ category="lo", direction="l", }, - [0x133F8]={ + [0x16884]={ category="lo", direction="l", }, - [0x133F9]={ + [0x16885]={ category="lo", direction="l", }, - [0x133FA]={ + [0x16886]={ category="lo", direction="l", }, - [0x133FB]={ + [0x16887]={ category="lo", direction="l", }, - [0x133FC]={ + [0x16888]={ category="lo", direction="l", }, - [0x133FD]={ + [0x16889]={ category="lo", direction="l", }, - [0x133FE]={ + [0x1688A]={ category="lo", direction="l", }, - [0x133FF]={ + [0x1688B]={ category="lo", direction="l", }, - [0x13400]={ + [0x1688C]={ category="lo", direction="l", }, - [0x13401]={ + [0x1688D]={ category="lo", direction="l", }, - [0x13402]={ + [0x1688E]={ category="lo", direction="l", }, - [0x13403]={ + [0x1688F]={ category="lo", direction="l", }, - [0x13404]={ + [0x16890]={ category="lo", direction="l", }, - [0x13405]={ + [0x16891]={ category="lo", direction="l", }, - [0x13406]={ + [0x16892]={ category="lo", direction="l", }, - [0x13407]={ + [0x16893]={ category="lo", direction="l", }, - [0x13408]={ + [0x16894]={ category="lo", direction="l", }, - [0x13409]={ + [0x16895]={ category="lo", direction="l", }, - [0x1340A]={ + [0x16896]={ category="lo", direction="l", }, - [0x1340B]={ + [0x16897]={ category="lo", direction="l", }, - [0x1340C]={ + [0x16898]={ category="lo", direction="l", }, - [0x1340D]={ + [0x16899]={ category="lo", direction="l", }, - [0x1340E]={ + [0x1689A]={ category="lo", direction="l", }, - [0x1340F]={ + [0x1689B]={ category="lo", direction="l", }, - [0x13410]={ + [0x1689C]={ category="lo", direction="l", }, - [0x13411]={ + [0x1689D]={ category="lo", direction="l", }, - [0x13412]={ + [0x1689E]={ category="lo", direction="l", }, - [0x13413]={ + [0x1689F]={ category="lo", direction="l", }, - [0x13414]={ + [0x168A0]={ category="lo", direction="l", }, - [0x13415]={ + [0x168A1]={ category="lo", direction="l", }, - [0x13416]={ + [0x168A2]={ category="lo", direction="l", }, - [0x13417]={ + [0x168A3]={ category="lo", direction="l", }, - [0x13418]={ + [0x168A4]={ category="lo", direction="l", }, - [0x13419]={ + [0x168A5]={ category="lo", direction="l", }, - [0x1341A]={ + [0x168A6]={ category="lo", direction="l", }, - [0x1341B]={ + [0x168A7]={ category="lo", direction="l", }, - [0x1341C]={ + [0x168A8]={ category="lo", direction="l", }, - [0x1341D]={ + [0x168A9]={ category="lo", direction="l", }, - [0x1341E]={ + [0x168AA]={ category="lo", direction="l", }, - [0x1341F]={ + [0x168AB]={ category="lo", direction="l", }, - [0x13420]={ + [0x168AC]={ category="lo", direction="l", }, - [0x13421]={ + [0x168AD]={ category="lo", direction="l", }, - [0x13422]={ + [0x168AE]={ category="lo", direction="l", }, - [0x13423]={ + [0x168AF]={ category="lo", direction="l", }, - [0x13424]={ + [0x168B0]={ category="lo", direction="l", }, - [0x13425]={ + [0x168B1]={ category="lo", direction="l", }, - [0x13426]={ + [0x168B2]={ category="lo", direction="l", }, - [0x13427]={ + [0x168B3]={ category="lo", direction="l", }, - [0x13428]={ + [0x168B4]={ category="lo", direction="l", }, - [0x13429]={ + [0x168B5]={ category="lo", direction="l", }, - [0x1342A]={ + [0x168B6]={ category="lo", direction="l", }, - [0x1342B]={ + [0x168B7]={ category="lo", direction="l", }, - [0x1342C]={ + [0x168B8]={ category="lo", direction="l", }, - [0x1342D]={ + [0x168B9]={ category="lo", direction="l", }, - [0x1342E]={ + [0x168BA]={ category="lo", direction="l", }, - [0x14400]={ + [0x168BB]={ category="lo", direction="l", }, - [0x14401]={ + [0x168BC]={ category="lo", direction="l", }, - [0x14402]={ + [0x168BD]={ category="lo", direction="l", }, - [0x14403]={ + [0x168BE]={ category="lo", direction="l", }, - [0x14404]={ + [0x168BF]={ category="lo", direction="l", }, - [0x14405]={ + [0x168C0]={ category="lo", direction="l", }, - [0x14406]={ + [0x168C1]={ category="lo", direction="l", }, - [0x14407]={ + [0x168C2]={ category="lo", direction="l", }, - [0x14408]={ + [0x168C3]={ category="lo", direction="l", }, - [0x14409]={ + [0x168C4]={ category="lo", direction="l", }, - [0x1440A]={ + [0x168C5]={ category="lo", direction="l", }, - [0x1440B]={ + [0x168C6]={ category="lo", direction="l", }, - [0x1440C]={ + [0x168C7]={ category="lo", direction="l", }, - [0x1440D]={ + [0x168C8]={ category="lo", direction="l", }, - [0x1440E]={ + [0x168C9]={ category="lo", direction="l", }, - [0x1440F]={ + [0x168CA]={ category="lo", direction="l", }, - [0x14410]={ + [0x168CB]={ category="lo", direction="l", }, - [0x14411]={ + [0x168CC]={ category="lo", direction="l", }, - [0x14412]={ + [0x168CD]={ category="lo", direction="l", }, - [0x14413]={ + [0x168CE]={ category="lo", direction="l", }, - [0x14414]={ + [0x168CF]={ category="lo", direction="l", }, - [0x14415]={ + [0x168D0]={ category="lo", direction="l", }, - [0x14416]={ + [0x168D1]={ category="lo", direction="l", }, - [0x14417]={ + [0x168D2]={ category="lo", direction="l", }, - [0x14418]={ + [0x168D3]={ category="lo", direction="l", }, - [0x14419]={ + [0x168D4]={ category="lo", direction="l", }, - [0x1441A]={ + [0x168D5]={ category="lo", direction="l", }, - [0x1441B]={ + [0x168D6]={ category="lo", direction="l", }, - [0x1441C]={ + [0x168D7]={ category="lo", direction="l", }, - [0x1441D]={ + [0x168D8]={ category="lo", direction="l", }, - [0x1441E]={ + [0x168D9]={ category="lo", direction="l", }, - [0x1441F]={ + [0x168DA]={ category="lo", direction="l", }, - [0x14420]={ + [0x168DB]={ category="lo", direction="l", }, - [0x14421]={ + [0x168DC]={ category="lo", direction="l", }, - [0x14422]={ + [0x168DD]={ category="lo", direction="l", }, - [0x14423]={ + [0x168DE]={ category="lo", direction="l", }, - [0x14424]={ + [0x168DF]={ category="lo", direction="l", }, - [0x14425]={ + [0x168E0]={ category="lo", direction="l", }, - [0x14426]={ + [0x168E1]={ category="lo", direction="l", }, - [0x14427]={ + [0x168E2]={ category="lo", direction="l", }, - [0x14428]={ + [0x168E3]={ category="lo", direction="l", }, - [0x14429]={ + [0x168E4]={ category="lo", direction="l", }, - [0x1442A]={ + [0x168E5]={ category="lo", direction="l", }, - [0x1442B]={ + [0x168E6]={ category="lo", direction="l", }, - [0x1442C]={ + [0x168E7]={ category="lo", direction="l", }, - [0x1442D]={ + [0x168E8]={ category="lo", direction="l", }, - [0x1442E]={ + [0x168E9]={ category="lo", direction="l", }, - [0x1442F]={ + [0x168EA]={ category="lo", direction="l", }, - [0x14430]={ + [0x168EB]={ category="lo", direction="l", }, - [0x14431]={ + [0x168EC]={ category="lo", direction="l", }, - [0x14432]={ + [0x168ED]={ category="lo", direction="l", }, - [0x14433]={ + [0x168EE]={ category="lo", direction="l", }, - [0x14434]={ + [0x168EF]={ category="lo", direction="l", }, - [0x14435]={ + [0x168F0]={ category="lo", direction="l", }, - [0x14436]={ + [0x168F1]={ category="lo", direction="l", }, - [0x14437]={ + [0x168F2]={ category="lo", direction="l", }, - [0x14438]={ + [0x168F3]={ category="lo", direction="l", }, - [0x14439]={ + [0x168F4]={ category="lo", direction="l", }, - [0x1443A]={ + [0x168F5]={ category="lo", direction="l", }, - [0x1443B]={ + [0x168F6]={ category="lo", direction="l", }, - [0x1443C]={ + [0x168F7]={ category="lo", direction="l", }, - [0x1443D]={ + [0x168F8]={ category="lo", direction="l", }, - [0x1443E]={ + [0x168F9]={ category="lo", direction="l", }, - [0x1443F]={ + [0x168FA]={ category="lo", direction="l", }, - [0x14440]={ + [0x168FB]={ category="lo", direction="l", }, - [0x14441]={ + [0x168FC]={ category="lo", direction="l", }, - [0x14442]={ + [0x168FD]={ category="lo", direction="l", }, - [0x14443]={ + [0x168FE]={ category="lo", direction="l", }, - [0x14444]={ + [0x168FF]={ category="lo", direction="l", }, - [0x14445]={ + [0x16900]={ category="lo", direction="l", }, - [0x14446]={ + [0x16901]={ category="lo", direction="l", }, - [0x14447]={ + [0x16902]={ category="lo", direction="l", }, - [0x14448]={ + [0x16903]={ category="lo", direction="l", }, - [0x14449]={ + [0x16904]={ category="lo", direction="l", }, - [0x1444A]={ + [0x16905]={ category="lo", direction="l", }, - [0x1444B]={ + [0x16906]={ category="lo", direction="l", }, - [0x1444C]={ + [0x16907]={ category="lo", direction="l", }, - [0x1444D]={ + [0x16908]={ category="lo", direction="l", }, - [0x1444E]={ + [0x16909]={ category="lo", direction="l", }, - [0x1444F]={ + [0x1690A]={ category="lo", direction="l", }, - [0x14450]={ + [0x1690B]={ category="lo", direction="l", }, - [0x14451]={ + [0x1690C]={ category="lo", direction="l", }, - [0x14452]={ + [0x1690D]={ category="lo", direction="l", }, - [0x14453]={ + [0x1690E]={ category="lo", direction="l", }, - [0x14454]={ + [0x1690F]={ category="lo", direction="l", }, - [0x14455]={ + [0x16910]={ category="lo", direction="l", }, - [0x14456]={ + [0x16911]={ category="lo", direction="l", }, - [0x14457]={ + [0x16912]={ category="lo", direction="l", }, - [0x14458]={ + [0x16913]={ category="lo", direction="l", }, - [0x14459]={ + [0x16914]={ category="lo", direction="l", }, - [0x1445A]={ + [0x16915]={ category="lo", direction="l", }, - [0x1445B]={ + [0x16916]={ category="lo", direction="l", }, - [0x1445C]={ + [0x16917]={ category="lo", direction="l", }, - [0x1445D]={ + [0x16918]={ category="lo", direction="l", }, - [0x1445E]={ + [0x16919]={ category="lo", direction="l", }, - [0x1445F]={ + [0x1691A]={ category="lo", direction="l", }, - [0x14460]={ + [0x1691B]={ category="lo", direction="l", }, - [0x14461]={ + [0x1691C]={ category="lo", direction="l", }, - [0x14462]={ + [0x1691D]={ category="lo", direction="l", }, - [0x14463]={ + [0x1691E]={ category="lo", direction="l", }, - [0x14464]={ + [0x1691F]={ category="lo", direction="l", }, - [0x14465]={ + [0x16920]={ category="lo", direction="l", }, - [0x14466]={ + [0x16921]={ category="lo", direction="l", }, - [0x14467]={ + [0x16922]={ category="lo", direction="l", }, - [0x14468]={ + [0x16923]={ category="lo", direction="l", }, - [0x14469]={ + [0x16924]={ category="lo", direction="l", }, - [0x1446A]={ + [0x16925]={ category="lo", direction="l", }, - [0x1446B]={ + [0x16926]={ category="lo", direction="l", }, - [0x1446C]={ + [0x16927]={ category="lo", direction="l", }, - [0x1446D]={ + [0x16928]={ category="lo", direction="l", }, - [0x1446E]={ + [0x16929]={ category="lo", direction="l", }, - [0x1446F]={ + [0x1692A]={ category="lo", direction="l", }, - [0x14470]={ + [0x1692B]={ category="lo", direction="l", }, - [0x14471]={ + [0x1692C]={ category="lo", direction="l", }, - [0x14472]={ + [0x1692D]={ category="lo", direction="l", }, - [0x14473]={ + [0x1692E]={ category="lo", direction="l", }, - [0x14474]={ + [0x1692F]={ category="lo", direction="l", }, - [0x14475]={ + [0x16930]={ category="lo", direction="l", }, - [0x14476]={ + [0x16931]={ category="lo", direction="l", }, - [0x14477]={ + [0x16932]={ category="lo", direction="l", }, - [0x14478]={ + [0x16933]={ category="lo", direction="l", }, - [0x14479]={ + [0x16934]={ category="lo", direction="l", }, - [0x1447A]={ + [0x16935]={ category="lo", direction="l", }, - [0x1447B]={ + [0x16936]={ category="lo", direction="l", }, - [0x1447C]={ + [0x16937]={ category="lo", direction="l", }, - [0x1447D]={ + [0x16938]={ category="lo", direction="l", }, - [0x1447E]={ + [0x16939]={ category="lo", direction="l", }, - [0x1447F]={ + [0x1693A]={ category="lo", direction="l", }, - [0x14480]={ + [0x1693B]={ category="lo", direction="l", }, - [0x14481]={ + [0x1693C]={ category="lo", direction="l", }, - [0x14482]={ + [0x1693D]={ category="lo", direction="l", }, - [0x14483]={ + [0x1693E]={ category="lo", direction="l", }, - [0x14484]={ + [0x1693F]={ category="lo", direction="l", }, - [0x14485]={ + [0x16940]={ category="lo", direction="l", }, - [0x14486]={ + [0x16941]={ category="lo", direction="l", }, - [0x14487]={ + [0x16942]={ category="lo", direction="l", }, - [0x14488]={ + [0x16943]={ category="lo", direction="l", }, - [0x14489]={ + [0x16944]={ category="lo", direction="l", }, - [0x1448A]={ + [0x16945]={ category="lo", direction="l", }, - [0x1448B]={ + [0x16946]={ category="lo", direction="l", }, - [0x1448C]={ + [0x16947]={ category="lo", direction="l", }, - [0x1448D]={ + [0x16948]={ category="lo", direction="l", }, - [0x1448E]={ + [0x16949]={ category="lo", direction="l", }, - [0x1448F]={ + [0x1694A]={ category="lo", direction="l", }, - [0x14490]={ + [0x1694B]={ category="lo", direction="l", }, - [0x14491]={ + [0x1694C]={ category="lo", direction="l", }, - [0x14492]={ + [0x1694D]={ category="lo", direction="l", }, - [0x14493]={ + [0x1694E]={ category="lo", direction="l", }, - [0x14494]={ + [0x1694F]={ category="lo", direction="l", }, - [0x14495]={ + [0x16950]={ category="lo", direction="l", }, - [0x14496]={ + [0x16951]={ category="lo", direction="l", }, - [0x14497]={ + [0x16952]={ category="lo", direction="l", }, - [0x14498]={ + [0x16953]={ category="lo", direction="l", }, - [0x14499]={ + [0x16954]={ category="lo", direction="l", }, - [0x1449A]={ + [0x16955]={ category="lo", direction="l", }, - [0x1449B]={ + [0x16956]={ category="lo", direction="l", }, - [0x1449C]={ + [0x16957]={ category="lo", direction="l", }, - [0x1449D]={ + [0x16958]={ category="lo", direction="l", }, - [0x1449E]={ + [0x16959]={ category="lo", direction="l", }, - [0x1449F]={ + [0x1695A]={ category="lo", direction="l", }, - [0x144A0]={ + [0x1695B]={ category="lo", direction="l", }, - [0x144A1]={ + [0x1695C]={ category="lo", direction="l", }, - [0x144A2]={ + [0x1695D]={ category="lo", direction="l", }, - [0x144A3]={ + [0x1695E]={ category="lo", direction="l", }, - [0x144A4]={ + [0x1695F]={ category="lo", direction="l", }, - [0x144A5]={ + [0x16960]={ category="lo", direction="l", }, - [0x144A6]={ + [0x16961]={ category="lo", direction="l", }, - [0x144A7]={ + [0x16962]={ category="lo", direction="l", }, - [0x144A8]={ + [0x16963]={ category="lo", direction="l", }, - [0x144A9]={ + [0x16964]={ category="lo", direction="l", }, - [0x144AA]={ + [0x16965]={ category="lo", direction="l", }, - [0x144AB]={ + [0x16966]={ category="lo", direction="l", }, - [0x144AC]={ + [0x16967]={ category="lo", direction="l", }, - [0x144AD]={ + [0x16968]={ category="lo", direction="l", }, - [0x144AE]={ + [0x16969]={ category="lo", direction="l", }, - [0x144AF]={ + [0x1696A]={ category="lo", direction="l", }, - [0x144B0]={ + [0x1696B]={ category="lo", direction="l", }, - [0x144B1]={ + [0x1696C]={ category="lo", direction="l", }, - [0x144B2]={ + [0x1696D]={ category="lo", direction="l", }, - [0x144B3]={ + [0x1696E]={ category="lo", direction="l", }, - [0x144B4]={ + [0x1696F]={ category="lo", direction="l", }, - [0x144B5]={ + [0x16970]={ category="lo", direction="l", }, - [0x144B6]={ + [0x16971]={ category="lo", direction="l", }, - [0x144B7]={ + [0x16972]={ category="lo", direction="l", }, - [0x144B8]={ + [0x16973]={ category="lo", direction="l", }, - [0x144B9]={ + [0x16974]={ category="lo", direction="l", }, - [0x144BA]={ + [0x16975]={ category="lo", direction="l", }, - [0x144BB]={ + [0x16976]={ category="lo", direction="l", }, - [0x144BC]={ + [0x16977]={ category="lo", direction="l", }, - [0x144BD]={ + [0x16978]={ category="lo", direction="l", }, - [0x144BE]={ + [0x16979]={ category="lo", direction="l", }, - [0x144BF]={ + [0x1697A]={ category="lo", direction="l", }, - [0x144C0]={ + [0x1697B]={ category="lo", direction="l", }, - [0x144C1]={ + [0x1697C]={ category="lo", direction="l", }, - [0x144C2]={ + [0x1697D]={ category="lo", direction="l", }, - [0x144C3]={ + [0x1697E]={ category="lo", direction="l", }, - [0x144C4]={ + [0x1697F]={ category="lo", direction="l", }, - [0x144C5]={ + [0x16980]={ category="lo", direction="l", }, - [0x144C6]={ + [0x16981]={ category="lo", direction="l", }, - [0x144C7]={ + [0x16982]={ category="lo", direction="l", }, - [0x144C8]={ + [0x16983]={ category="lo", direction="l", }, - [0x144C9]={ + [0x16984]={ category="lo", direction="l", }, - [0x144CA]={ + [0x16985]={ category="lo", direction="l", }, - [0x144CB]={ + [0x16986]={ category="lo", direction="l", }, - [0x144CC]={ + [0x16987]={ category="lo", direction="l", }, - [0x144CD]={ + [0x16988]={ category="lo", direction="l", }, - [0x144CE]={ + [0x16989]={ category="lo", direction="l", }, - [0x144CF]={ + [0x1698A]={ category="lo", direction="l", }, - [0x144D0]={ + [0x1698B]={ category="lo", direction="l", }, - [0x144D1]={ + [0x1698C]={ category="lo", direction="l", }, - [0x144D2]={ + [0x1698D]={ category="lo", direction="l", }, - [0x144D3]={ + [0x1698E]={ category="lo", direction="l", }, - [0x144D4]={ + [0x1698F]={ category="lo", direction="l", }, - [0x144D5]={ + [0x16990]={ category="lo", direction="l", }, - [0x144D6]={ + [0x16991]={ category="lo", direction="l", }, - [0x144D7]={ + [0x16992]={ category="lo", direction="l", }, - [0x144D8]={ + [0x16993]={ category="lo", direction="l", }, - [0x144D9]={ + [0x16994]={ category="lo", direction="l", }, - [0x144DA]={ + [0x16995]={ category="lo", direction="l", }, - [0x144DB]={ + [0x16996]={ category="lo", direction="l", }, - [0x144DC]={ + [0x16997]={ category="lo", direction="l", }, - [0x144DD]={ + [0x16998]={ category="lo", direction="l", }, - [0x144DE]={ + [0x16999]={ category="lo", direction="l", }, - [0x144DF]={ + [0x1699A]={ category="lo", direction="l", }, - [0x144E0]={ + [0x1699B]={ category="lo", direction="l", }, - [0x144E1]={ + [0x1699C]={ category="lo", direction="l", }, - [0x144E2]={ + [0x1699D]={ category="lo", direction="l", }, - [0x144E3]={ + [0x1699E]={ category="lo", direction="l", }, - [0x144E4]={ + [0x1699F]={ category="lo", direction="l", }, - [0x144E5]={ + [0x169A0]={ category="lo", direction="l", }, - [0x144E6]={ + [0x169A1]={ category="lo", direction="l", }, - [0x144E7]={ + [0x169A2]={ category="lo", direction="l", }, - [0x144E8]={ + [0x169A3]={ category="lo", direction="l", }, - [0x144E9]={ + [0x169A4]={ category="lo", direction="l", }, - [0x144EA]={ + [0x169A5]={ category="lo", direction="l", }, - [0x144EB]={ + [0x169A6]={ category="lo", direction="l", }, - [0x144EC]={ + [0x169A7]={ category="lo", direction="l", }, - [0x144ED]={ + [0x169A8]={ category="lo", direction="l", }, - [0x144EE]={ + [0x169A9]={ category="lo", direction="l", }, - [0x144EF]={ + [0x169AA]={ category="lo", direction="l", }, - [0x144F0]={ + [0x169AB]={ category="lo", direction="l", }, - [0x144F1]={ + [0x169AC]={ category="lo", direction="l", }, - [0x144F2]={ + [0x169AD]={ category="lo", direction="l", }, - [0x144F3]={ + [0x169AE]={ category="lo", direction="l", }, - [0x144F4]={ + [0x169AF]={ category="lo", direction="l", }, - [0x144F5]={ + [0x169B0]={ category="lo", direction="l", }, - [0x144F6]={ + [0x169B1]={ category="lo", direction="l", }, - [0x144F7]={ + [0x169B2]={ category="lo", direction="l", }, - [0x144F8]={ + [0x169B3]={ category="lo", direction="l", }, - [0x144F9]={ + [0x169B4]={ category="lo", direction="l", }, - [0x144FA]={ + [0x169B5]={ category="lo", direction="l", }, - [0x144FB]={ + [0x169B6]={ category="lo", direction="l", }, - [0x144FC]={ + [0x169B7]={ category="lo", direction="l", }, - [0x144FD]={ + [0x169B8]={ category="lo", direction="l", }, - [0x144FE]={ + [0x169B9]={ category="lo", direction="l", }, - [0x144FF]={ + [0x169BA]={ category="lo", direction="l", }, - [0x14500]={ + [0x169BB]={ category="lo", direction="l", }, - [0x14501]={ + [0x169BC]={ category="lo", direction="l", }, - [0x14502]={ + [0x169BD]={ category="lo", direction="l", }, - [0x14503]={ + [0x169BE]={ category="lo", direction="l", }, - [0x14504]={ + [0x169BF]={ category="lo", direction="l", }, - [0x14505]={ + [0x169C0]={ category="lo", direction="l", }, - [0x14506]={ + [0x169C1]={ category="lo", direction="l", }, - [0x14507]={ + [0x169C2]={ category="lo", direction="l", }, - [0x14508]={ + [0x169C3]={ category="lo", direction="l", }, - [0x14509]={ + [0x169C4]={ category="lo", direction="l", }, - [0x1450A]={ + [0x169C5]={ category="lo", direction="l", }, - [0x1450B]={ + [0x169C6]={ category="lo", direction="l", }, - [0x1450C]={ + [0x169C7]={ category="lo", direction="l", }, - [0x1450D]={ + [0x169C8]={ category="lo", direction="l", }, - [0x1450E]={ + [0x169C9]={ category="lo", direction="l", }, - [0x1450F]={ + [0x169CA]={ category="lo", direction="l", }, - [0x14510]={ + [0x169CB]={ category="lo", direction="l", }, - [0x14511]={ + [0x169CC]={ category="lo", direction="l", }, - [0x14512]={ + [0x169CD]={ category="lo", direction="l", }, - [0x14513]={ + [0x169CE]={ category="lo", direction="l", }, - [0x14514]={ + [0x169CF]={ category="lo", direction="l", }, - [0x14515]={ + [0x169D0]={ category="lo", direction="l", }, - [0x14516]={ + [0x169D1]={ category="lo", direction="l", }, - [0x14517]={ + [0x169D2]={ category="lo", direction="l", }, - [0x14518]={ + [0x169D3]={ category="lo", direction="l", }, - [0x14519]={ + [0x169D4]={ category="lo", direction="l", }, - [0x1451A]={ + [0x169D5]={ category="lo", direction="l", }, - [0x1451B]={ + [0x169D6]={ category="lo", direction="l", }, - [0x1451C]={ + [0x169D7]={ category="lo", direction="l", }, - [0x1451D]={ + [0x169D8]={ category="lo", direction="l", }, - [0x1451E]={ + [0x169D9]={ category="lo", direction="l", }, - [0x1451F]={ + [0x169DA]={ category="lo", direction="l", }, - [0x14520]={ + [0x169DB]={ category="lo", direction="l", }, - [0x14521]={ + [0x169DC]={ category="lo", direction="l", }, - [0x14522]={ + [0x169DD]={ category="lo", direction="l", }, - [0x14523]={ + [0x169DE]={ category="lo", direction="l", }, - [0x14524]={ + [0x169DF]={ category="lo", direction="l", }, - [0x14525]={ + [0x169E0]={ category="lo", direction="l", }, - [0x14526]={ + [0x169E1]={ category="lo", direction="l", }, - [0x14527]={ + [0x169E2]={ category="lo", direction="l", }, - [0x14528]={ + [0x169E3]={ category="lo", direction="l", }, - [0x14529]={ + [0x169E4]={ category="lo", direction="l", }, - [0x1452A]={ + [0x169E5]={ category="lo", direction="l", }, - [0x1452B]={ + [0x169E6]={ category="lo", direction="l", }, - [0x1452C]={ + [0x169E7]={ category="lo", direction="l", }, - [0x1452D]={ + [0x169E8]={ category="lo", direction="l", }, - [0x1452E]={ + [0x169E9]={ category="lo", direction="l", }, - [0x1452F]={ + [0x169EA]={ category="lo", direction="l", }, - [0x14530]={ + [0x169EB]={ category="lo", direction="l", }, - [0x14531]={ + [0x169EC]={ category="lo", direction="l", }, - [0x14532]={ + [0x169ED]={ category="lo", direction="l", }, - [0x14533]={ + [0x169EE]={ category="lo", direction="l", }, - [0x14534]={ + [0x169EF]={ category="lo", direction="l", }, - [0x14535]={ + [0x169F0]={ category="lo", direction="l", }, - [0x14536]={ + [0x169F1]={ category="lo", direction="l", }, - [0x14537]={ + [0x169F2]={ category="lo", direction="l", }, - [0x14538]={ + [0x169F3]={ category="lo", direction="l", }, - [0x14539]={ + [0x169F4]={ category="lo", direction="l", }, - [0x1453A]={ + [0x169F5]={ category="lo", direction="l", }, - [0x1453B]={ + [0x169F6]={ category="lo", direction="l", }, - [0x1453C]={ + [0x169F7]={ category="lo", direction="l", }, - [0x1453D]={ + [0x169F8]={ category="lo", direction="l", }, - [0x1453E]={ + [0x169F9]={ category="lo", direction="l", }, - [0x1453F]={ + [0x169FA]={ category="lo", direction="l", }, - [0x14540]={ + [0x169FB]={ category="lo", direction="l", }, - [0x14541]={ + [0x169FC]={ category="lo", direction="l", }, - [0x14542]={ + [0x169FD]={ category="lo", direction="l", }, - [0x14543]={ + [0x169FE]={ category="lo", direction="l", }, - [0x14544]={ + [0x169FF]={ category="lo", direction="l", }, - [0x14545]={ + [0x16A00]={ category="lo", direction="l", }, - [0x14546]={ + [0x16A01]={ category="lo", direction="l", }, - [0x14547]={ + [0x16A02]={ category="lo", direction="l", }, - [0x14548]={ + [0x16A03]={ category="lo", direction="l", }, - [0x14549]={ + [0x16A04]={ category="lo", direction="l", }, - [0x1454A]={ + [0x16A05]={ category="lo", direction="l", }, - [0x1454B]={ + [0x16A06]={ category="lo", direction="l", }, - [0x1454C]={ + [0x16A07]={ category="lo", direction="l", }, - [0x1454D]={ + [0x16A08]={ category="lo", direction="l", }, - [0x1454E]={ + [0x16A09]={ category="lo", direction="l", }, - [0x1454F]={ + [0x16A0A]={ category="lo", direction="l", }, - [0x14550]={ + [0x16A0B]={ category="lo", direction="l", }, - [0x14551]={ + [0x16A0C]={ category="lo", direction="l", }, - [0x14552]={ + [0x16A0D]={ category="lo", direction="l", }, - [0x14553]={ + [0x16A0E]={ category="lo", direction="l", }, - [0x14554]={ + [0x16A0F]={ category="lo", direction="l", }, - [0x14555]={ + [0x16A10]={ category="lo", direction="l", }, - [0x14556]={ + [0x16A11]={ category="lo", direction="l", }, - [0x14557]={ + [0x16A12]={ category="lo", direction="l", }, - [0x14558]={ + [0x16A13]={ category="lo", direction="l", }, - [0x14559]={ + [0x16A14]={ category="lo", direction="l", }, - [0x1455A]={ + [0x16A15]={ category="lo", direction="l", }, - [0x1455B]={ + [0x16A16]={ category="lo", direction="l", }, - [0x1455C]={ + [0x16A17]={ category="lo", direction="l", }, - [0x1455D]={ + [0x16A18]={ category="lo", direction="l", }, - [0x1455E]={ + [0x16A19]={ category="lo", direction="l", }, - [0x1455F]={ + [0x16A1A]={ category="lo", direction="l", }, - [0x14560]={ + [0x16A1B]={ category="lo", direction="l", }, - [0x14561]={ + [0x16A1C]={ category="lo", direction="l", }, - [0x14562]={ + [0x16A1D]={ category="lo", direction="l", }, - [0x14563]={ + [0x16A1E]={ category="lo", direction="l", }, - [0x14564]={ + [0x16A1F]={ category="lo", direction="l", }, - [0x14565]={ + [0x16A20]={ category="lo", direction="l", }, - [0x14566]={ + [0x16A21]={ category="lo", direction="l", }, - [0x14567]={ + [0x16A22]={ category="lo", direction="l", }, - [0x14568]={ + [0x16A23]={ category="lo", direction="l", }, - [0x14569]={ + [0x16A24]={ category="lo", direction="l", }, - [0x1456A]={ + [0x16A25]={ category="lo", direction="l", }, - [0x1456B]={ + [0x16A26]={ category="lo", direction="l", }, - [0x1456C]={ + [0x16A27]={ category="lo", direction="l", }, - [0x1456D]={ + [0x16A28]={ category="lo", direction="l", }, - [0x1456E]={ + [0x16A29]={ category="lo", direction="l", }, - [0x1456F]={ + [0x16A2A]={ category="lo", direction="l", }, - [0x14570]={ + [0x16A2B]={ category="lo", direction="l", }, - [0x14571]={ + [0x16A2C]={ category="lo", direction="l", }, - [0x14572]={ + [0x16A2D]={ category="lo", direction="l", }, - [0x14573]={ + [0x16A2E]={ category="lo", direction="l", }, - [0x14574]={ + [0x16A2F]={ category="lo", direction="l", }, - [0x14575]={ + [0x16A30]={ category="lo", direction="l", }, - [0x14576]={ + [0x16A31]={ category="lo", direction="l", }, - [0x14577]={ + [0x16A32]={ category="lo", direction="l", }, - [0x14578]={ + [0x16A33]={ category="lo", direction="l", }, - [0x14579]={ + [0x16A34]={ category="lo", direction="l", }, - [0x1457A]={ + [0x16A35]={ category="lo", direction="l", }, - [0x1457B]={ + [0x16A36]={ category="lo", direction="l", }, - [0x1457C]={ + [0x16A37]={ category="lo", direction="l", }, - [0x1457D]={ + [0x16A38]={ category="lo", direction="l", }, - [0x1457E]={ + [0x16A40]={ category="lo", direction="l", }, - [0x1457F]={ + [0x16A41]={ category="lo", direction="l", }, - [0x14580]={ + [0x16A42]={ category="lo", direction="l", }, - [0x14581]={ + [0x16A43]={ category="lo", direction="l", }, - [0x14582]={ + [0x16A44]={ category="lo", direction="l", }, - [0x14583]={ + [0x16A45]={ category="lo", direction="l", }, - [0x14584]={ + [0x16A46]={ category="lo", direction="l", }, - [0x14585]={ + [0x16A47]={ category="lo", direction="l", }, - [0x14586]={ + [0x16A48]={ category="lo", direction="l", }, - [0x14587]={ + [0x16A49]={ category="lo", direction="l", }, - [0x14588]={ + [0x16A4A]={ category="lo", direction="l", }, - [0x14589]={ + [0x16A4B]={ category="lo", direction="l", }, - [0x1458A]={ + [0x16A4C]={ category="lo", direction="l", }, - [0x1458B]={ + [0x16A4D]={ category="lo", direction="l", }, - [0x1458C]={ + [0x16A4E]={ category="lo", direction="l", }, - [0x1458D]={ + [0x16A4F]={ category="lo", direction="l", }, - [0x1458E]={ + [0x16A50]={ category="lo", direction="l", }, - [0x1458F]={ + [0x16A51]={ category="lo", direction="l", }, - [0x14590]={ + [0x16A52]={ category="lo", direction="l", }, - [0x14591]={ + [0x16A53]={ category="lo", direction="l", }, - [0x14592]={ + [0x16A54]={ category="lo", direction="l", }, - [0x14593]={ + [0x16A55]={ category="lo", direction="l", }, - [0x14594]={ + [0x16A56]={ category="lo", direction="l", }, - [0x14595]={ + [0x16A57]={ category="lo", direction="l", }, - [0x14596]={ + [0x16A58]={ category="lo", direction="l", }, - [0x14597]={ + [0x16A59]={ category="lo", direction="l", }, - [0x14598]={ + [0x16A5A]={ category="lo", direction="l", }, - [0x14599]={ + [0x16A5B]={ category="lo", direction="l", }, - [0x1459A]={ + [0x16A5C]={ category="lo", direction="l", }, - [0x1459B]={ + [0x16A5D]={ category="lo", direction="l", }, - [0x1459C]={ + [0x16A5E]={ category="lo", direction="l", }, - [0x1459D]={ - category="lo", + [0x16A60]={ + category="nd", direction="l", }, - [0x1459E]={ - category="lo", + [0x16A61]={ + category="nd", direction="l", }, - [0x1459F]={ - category="lo", + [0x16A62]={ + category="nd", direction="l", }, - [0x145A0]={ - category="lo", + [0x16A63]={ + category="nd", direction="l", }, - [0x145A1]={ - category="lo", + [0x16A64]={ + category="nd", direction="l", }, - [0x145A2]={ - category="lo", + [0x16A65]={ + category="nd", direction="l", }, - [0x145A3]={ - category="lo", + [0x16A66]={ + category="nd", direction="l", }, - [0x145A4]={ - category="lo", + [0x16A67]={ + category="nd", direction="l", }, - [0x145A5]={ - category="lo", + [0x16A68]={ + category="nd", direction="l", }, - [0x145A6]={ - category="lo", + [0x16A69]={ + category="nd", direction="l", }, - [0x145A7]={ - category="lo", + [0x16A6E]={ + category="po", direction="l", }, - [0x145A8]={ - category="lo", + [0x16A6F]={ + category="po", direction="l", }, - [0x145A9]={ + [0x16AD0]={ category="lo", direction="l", }, - [0x145AA]={ + [0x16AD1]={ category="lo", direction="l", }, - [0x145AB]={ + [0x16AD2]={ category="lo", direction="l", }, - [0x145AC]={ + [0x16AD3]={ category="lo", direction="l", }, - [0x145AD]={ + [0x16AD4]={ category="lo", direction="l", }, - [0x145AE]={ + [0x16AD5]={ category="lo", direction="l", }, - [0x145AF]={ + [0x16AD6]={ category="lo", direction="l", }, - [0x145B0]={ + [0x16AD7]={ category="lo", direction="l", }, - [0x145B1]={ + [0x16AD8]={ category="lo", direction="l", }, - [0x145B2]={ + [0x16AD9]={ category="lo", direction="l", }, - [0x145B3]={ + [0x16ADA]={ category="lo", direction="l", }, - [0x145B4]={ + [0x16ADB]={ category="lo", direction="l", }, - [0x145B5]={ + [0x16ADC]={ category="lo", direction="l", }, - [0x145B6]={ + [0x16ADD]={ category="lo", direction="l", }, - [0x145B7]={ + [0x16ADE]={ category="lo", direction="l", }, - [0x145B8]={ + [0x16ADF]={ category="lo", direction="l", }, - [0x145B9]={ + [0x16AE0]={ category="lo", direction="l", }, - [0x145BA]={ + [0x16AE1]={ category="lo", direction="l", }, - [0x145BB]={ + [0x16AE2]={ category="lo", direction="l", }, - [0x145BC]={ + [0x16AE3]={ category="lo", direction="l", }, - [0x145BD]={ + [0x16AE4]={ category="lo", direction="l", }, - [0x145BE]={ + [0x16AE5]={ category="lo", direction="l", }, - [0x145BF]={ + [0x16AE6]={ category="lo", direction="l", }, - [0x145C0]={ + [0x16AE7]={ category="lo", direction="l", }, - [0x145C1]={ + [0x16AE8]={ category="lo", direction="l", }, - [0x145C2]={ + [0x16AE9]={ category="lo", direction="l", }, - [0x145C3]={ + [0x16AEA]={ category="lo", direction="l", }, - [0x145C4]={ + [0x16AEB]={ category="lo", direction="l", }, - [0x145C5]={ + [0x16AEC]={ category="lo", direction="l", }, - [0x145C6]={ + [0x16AED]={ category="lo", direction="l", }, - [0x145C7]={ - category="lo", + [0x16AF0]={ + category="mn", + direction="nsm", + }, + [0x16AF1]={ + category="mn", + direction="nsm", + }, + [0x16AF2]={ + category="mn", + direction="nsm", + }, + [0x16AF3]={ + category="mn", + direction="nsm", + }, + [0x16AF4]={ + category="mn", + direction="nsm", + }, + [0x16AF5]={ + category="po", direction="l", }, - [0x145C8]={ + [0x16B00]={ category="lo", direction="l", }, - [0x145C9]={ + [0x16B01]={ category="lo", direction="l", }, - [0x145CA]={ + [0x16B02]={ category="lo", direction="l", }, - [0x145CB]={ + [0x16B03]={ category="lo", direction="l", }, - [0x145CC]={ + [0x16B04]={ category="lo", direction="l", }, - [0x145CD]={ + [0x16B05]={ category="lo", direction="l", }, - [0x145CE]={ + [0x16B06]={ category="lo", direction="l", }, - [0x145CF]={ + [0x16B07]={ category="lo", direction="l", }, - [0x145D0]={ + [0x16B08]={ category="lo", direction="l", }, - [0x145D1]={ + [0x16B09]={ category="lo", direction="l", }, - [0x145D2]={ + [0x16B0A]={ category="lo", direction="l", }, - [0x145D3]={ + [0x16B0B]={ category="lo", direction="l", }, - [0x145D4]={ + [0x16B0C]={ category="lo", direction="l", }, - [0x145D5]={ + [0x16B0D]={ category="lo", direction="l", }, - [0x145D6]={ + [0x16B0E]={ category="lo", direction="l", }, - [0x145D7]={ + [0x16B0F]={ category="lo", direction="l", }, - [0x145D8]={ + [0x16B10]={ category="lo", direction="l", }, - [0x145D9]={ + [0x16B11]={ category="lo", direction="l", }, - [0x145DA]={ + [0x16B12]={ category="lo", direction="l", }, - [0x145DB]={ + [0x16B13]={ category="lo", direction="l", }, - [0x145DC]={ + [0x16B14]={ category="lo", direction="l", }, - [0x145DD]={ + [0x16B15]={ category="lo", direction="l", }, - [0x145DE]={ + [0x16B16]={ category="lo", direction="l", }, - [0x145DF]={ + [0x16B17]={ category="lo", direction="l", }, - [0x145E0]={ + [0x16B18]={ category="lo", direction="l", }, - [0x145E1]={ + [0x16B19]={ category="lo", direction="l", }, - [0x145E2]={ + [0x16B1A]={ category="lo", direction="l", }, - [0x145E3]={ + [0x16B1B]={ category="lo", direction="l", }, - [0x145E4]={ + [0x16B1C]={ category="lo", direction="l", }, - [0x145E5]={ + [0x16B1D]={ category="lo", direction="l", }, - [0x145E6]={ + [0x16B1E]={ category="lo", direction="l", }, - [0x145E7]={ + [0x16B1F]={ category="lo", direction="l", }, - [0x145E8]={ + [0x16B20]={ category="lo", direction="l", }, - [0x145E9]={ + [0x16B21]={ category="lo", direction="l", }, - [0x145EA]={ + [0x16B22]={ category="lo", direction="l", }, - [0x145EB]={ + [0x16B23]={ category="lo", direction="l", }, - [0x145EC]={ + [0x16B24]={ category="lo", direction="l", }, - [0x145ED]={ + [0x16B25]={ category="lo", direction="l", }, - [0x145EE]={ + [0x16B26]={ category="lo", direction="l", }, - [0x145EF]={ + [0x16B27]={ category="lo", direction="l", }, - [0x145F0]={ + [0x16B28]={ category="lo", direction="l", }, - [0x145F1]={ + [0x16B29]={ category="lo", direction="l", }, - [0x145F2]={ + [0x16B2A]={ category="lo", direction="l", }, - [0x145F3]={ + [0x16B2B]={ category="lo", direction="l", }, - [0x145F4]={ + [0x16B2C]={ category="lo", direction="l", }, - [0x145F5]={ + [0x16B2D]={ category="lo", direction="l", }, - [0x145F6]={ + [0x16B2E]={ category="lo", direction="l", }, - [0x145F7]={ + [0x16B2F]={ category="lo", direction="l", }, - [0x145F8]={ - category="lo", + [0x16B30]={ + category="mn", + direction="nsm", + }, + [0x16B31]={ + category="mn", + direction="nsm", + }, + [0x16B32]={ + category="mn", + direction="nsm", + }, + [0x16B33]={ + category="mn", + direction="nsm", + }, + [0x16B34]={ + category="mn", + direction="nsm", + }, + [0x16B35]={ + category="mn", + direction="nsm", + }, + [0x16B36]={ + category="mn", + direction="nsm", + }, + [0x16B37]={ + category="po", direction="l", }, - [0x145F9]={ - category="lo", + [0x16B38]={ + category="po", direction="l", }, - [0x145FA]={ - category="lo", + [0x16B39]={ + category="po", direction="l", }, - [0x145FB]={ - category="lo", + [0x16B3A]={ + category="po", direction="l", }, - [0x145FC]={ - category="lo", + [0x16B3B]={ + category="po", direction="l", }, - [0x145FD]={ - category="lo", + [0x16B3C]={ + category="so", direction="l", }, - [0x145FE]={ - category="lo", + [0x16B3D]={ + category="so", direction="l", }, - [0x145FF]={ - category="lo", + [0x16B3E]={ + category="so", direction="l", }, - [0x14600]={ - category="lo", + [0x16B3F]={ + category="so", direction="l", }, - [0x14601]={ - category="lo", + [0x16B40]={ + category="lm", direction="l", }, - [0x14602]={ - category="lo", + [0x16B41]={ + category="lm", direction="l", }, - [0x14603]={ - category="lo", + [0x16B42]={ + category="lm", direction="l", }, - [0x14604]={ - category="lo", + [0x16B43]={ + category="lm", direction="l", }, - [0x14605]={ - category="lo", + [0x16B44]={ + category="po", direction="l", }, - [0x14606]={ - category="lo", + [0x16B45]={ + category="so", direction="l", }, - [0x14607]={ - category="lo", + [0x16B50]={ + category="nd", direction="l", }, - [0x14608]={ - category="lo", + [0x16B51]={ + category="nd", direction="l", }, - [0x14609]={ - category="lo", + [0x16B52]={ + category="nd", direction="l", }, - [0x1460A]={ - category="lo", + [0x16B53]={ + category="nd", direction="l", }, - [0x1460B]={ - category="lo", + [0x16B54]={ + category="nd", direction="l", }, - [0x1460C]={ - category="lo", + [0x16B55]={ + category="nd", direction="l", }, - [0x1460D]={ - category="lo", + [0x16B56]={ + category="nd", direction="l", }, - [0x1460E]={ - category="lo", + [0x16B57]={ + category="nd", direction="l", }, - [0x1460F]={ - category="lo", + [0x16B58]={ + category="nd", direction="l", }, - [0x14610]={ - category="lo", + [0x16B59]={ + category="nd", direction="l", }, - [0x14611]={ - category="lo", + [0x16B5B]={ + category="no", direction="l", }, - [0x14612]={ - category="lo", + [0x16B5C]={ + category="no", direction="l", }, - [0x14613]={ - category="lo", + [0x16B5D]={ + category="no", direction="l", }, - [0x14614]={ - category="lo", + [0x16B5E]={ + category="no", direction="l", }, - [0x14615]={ - category="lo", + [0x16B5F]={ + category="no", direction="l", }, - [0x14616]={ - category="lo", + [0x16B60]={ + category="no", direction="l", }, - [0x14617]={ - category="lo", + [0x16B61]={ + category="no", direction="l", }, - [0x14618]={ + [0x16B63]={ category="lo", direction="l", }, - [0x14619]={ + [0x16B64]={ category="lo", direction="l", }, - [0x1461A]={ + [0x16B65]={ category="lo", direction="l", }, - [0x1461B]={ + [0x16B66]={ category="lo", direction="l", }, - [0x1461C]={ + [0x16B67]={ category="lo", direction="l", }, - [0x1461D]={ + [0x16B68]={ category="lo", direction="l", }, - [0x1461E]={ + [0x16B69]={ category="lo", direction="l", }, - [0x1461F]={ + [0x16B6A]={ category="lo", direction="l", }, - [0x14620]={ + [0x16B6B]={ category="lo", direction="l", }, - [0x14621]={ + [0x16B6C]={ category="lo", direction="l", }, - [0x14622]={ + [0x16B6D]={ category="lo", direction="l", }, - [0x14623]={ + [0x16B6E]={ category="lo", direction="l", }, - [0x14624]={ + [0x16B6F]={ category="lo", direction="l", }, - [0x14625]={ + [0x16B70]={ category="lo", direction="l", }, - [0x14626]={ + [0x16B71]={ category="lo", direction="l", }, - [0x14627]={ + [0x16B72]={ category="lo", direction="l", }, - [0x14628]={ + [0x16B73]={ category="lo", direction="l", }, - [0x14629]={ + [0x16B74]={ category="lo", direction="l", }, - [0x1462A]={ + [0x16B75]={ category="lo", direction="l", }, - [0x1462B]={ + [0x16B76]={ category="lo", direction="l", }, - [0x1462C]={ + [0x16B77]={ category="lo", direction="l", }, - [0x1462D]={ + [0x16B7D]={ category="lo", direction="l", }, - [0x1462E]={ + [0x16B7E]={ category="lo", direction="l", }, - [0x1462F]={ + [0x16B7F]={ category="lo", direction="l", }, - [0x14630]={ + [0x16B80]={ category="lo", direction="l", }, - [0x14631]={ + [0x16B81]={ category="lo", direction="l", }, - [0x14632]={ + [0x16B82]={ category="lo", direction="l", }, - [0x14633]={ + [0x16B83]={ category="lo", direction="l", }, - [0x14634]={ + [0x16B84]={ category="lo", direction="l", }, - [0x14635]={ + [0x16B85]={ category="lo", direction="l", }, - [0x14636]={ + [0x16B86]={ category="lo", direction="l", }, - [0x14637]={ + [0x16B87]={ category="lo", direction="l", }, - [0x14638]={ + [0x16B88]={ category="lo", direction="l", }, - [0x14639]={ + [0x16B89]={ category="lo", direction="l", }, - [0x1463A]={ + [0x16B8A]={ category="lo", direction="l", }, - [0x1463B]={ + [0x16B8B]={ category="lo", direction="l", }, - [0x1463C]={ + [0x16B8C]={ category="lo", direction="l", }, - [0x1463D]={ + [0x16B8D]={ category="lo", direction="l", }, - [0x1463E]={ + [0x16B8E]={ category="lo", direction="l", }, - [0x1463F]={ + [0x16B8F]={ category="lo", direction="l", }, - [0x14640]={ - category="lo", + [0x16E40]={ + category="lu", direction="l", }, - [0x14641]={ - category="lo", + [0x16E41]={ + category="lu", direction="l", }, - [0x14642]={ - category="lo", + [0x16E42]={ + category="lu", direction="l", }, - [0x14643]={ - category="lo", + [0x16E43]={ + category="lu", direction="l", }, - [0x14644]={ - category="lo", + [0x16E44]={ + category="lu", direction="l", }, - [0x14645]={ - category="lo", + [0x16E45]={ + category="lu", direction="l", }, - [0x14646]={ - category="lo", + [0x16E46]={ + category="lu", direction="l", }, - [0x16800]={ - category="lo", + [0x16E47]={ + category="lu", direction="l", }, - [0x16801]={ - category="lo", + [0x16E48]={ + category="lu", direction="l", }, - [0x16802]={ - category="lo", + [0x16E49]={ + category="lu", direction="l", }, - [0x16803]={ - category="lo", + [0x16E4A]={ + category="lu", direction="l", }, - [0x16804]={ - category="lo", + [0x16E4B]={ + category="lu", direction="l", }, - [0x16805]={ - category="lo", + [0x16E4C]={ + category="lu", direction="l", }, - [0x16806]={ - category="lo", + [0x16E4D]={ + category="lu", direction="l", }, - [0x16807]={ - category="lo", + [0x16E4E]={ + category="lu", direction="l", }, - [0x16808]={ - category="lo", + [0x16E4F]={ + category="lu", direction="l", }, - [0x16809]={ - category="lo", + [0x16E50]={ + category="lu", direction="l", }, - [0x1680A]={ - category="lo", + [0x16E51]={ + category="lu", direction="l", }, - [0x1680B]={ - category="lo", + [0x16E52]={ + category="lu", direction="l", }, - [0x1680C]={ - category="lo", + [0x16E53]={ + category="lu", direction="l", }, - [0x1680D]={ - category="lo", + [0x16E54]={ + category="lu", direction="l", }, - [0x1680E]={ - category="lo", + [0x16E55]={ + category="lu", direction="l", }, - [0x1680F]={ - category="lo", + [0x16E56]={ + category="lu", direction="l", }, - [0x16810]={ - category="lo", + [0x16E57]={ + category="lu", direction="l", }, - [0x16811]={ - category="lo", + [0x16E58]={ + category="lu", direction="l", }, - [0x16812]={ - category="lo", + [0x16E59]={ + category="lu", direction="l", }, - [0x16813]={ - category="lo", + [0x16E5A]={ + category="lu", direction="l", }, - [0x16814]={ - category="lo", + [0x16E5B]={ + category="lu", direction="l", }, - [0x16815]={ - category="lo", + [0x16E5C]={ + category="lu", direction="l", }, - [0x16816]={ - category="lo", + [0x16E5D]={ + category="lu", direction="l", }, - [0x16817]={ - category="lo", + [0x16E5E]={ + category="lu", direction="l", }, - [0x16818]={ - category="lo", + [0x16E5F]={ + category="lu", direction="l", }, - [0x16819]={ - category="lo", + [0x16E60]={ + category="ll", direction="l", }, - [0x1681A]={ - category="lo", + [0x16E61]={ + category="ll", direction="l", }, - [0x1681B]={ - category="lo", + [0x16E62]={ + category="ll", direction="l", }, - [0x1681C]={ - category="lo", + [0x16E63]={ + category="ll", direction="l", }, - [0x1681D]={ - category="lo", + [0x16E64]={ + category="ll", direction="l", }, - [0x1681E]={ - category="lo", + [0x16E65]={ + category="ll", direction="l", }, - [0x1681F]={ - category="lo", + [0x16E66]={ + category="ll", direction="l", }, - [0x16820]={ - category="lo", + [0x16E67]={ + category="ll", direction="l", }, - [0x16821]={ - category="lo", + [0x16E68]={ + category="ll", direction="l", }, - [0x16822]={ - category="lo", + [0x16E69]={ + category="ll", direction="l", }, - [0x16823]={ - category="lo", + [0x16E6A]={ + category="ll", direction="l", }, - [0x16824]={ - category="lo", + [0x16E6B]={ + category="ll", direction="l", }, - [0x16825]={ - category="lo", + [0x16E6C]={ + category="ll", direction="l", }, - [0x16826]={ - category="lo", + [0x16E6D]={ + category="ll", direction="l", }, - [0x16827]={ - category="lo", + [0x16E6E]={ + category="ll", direction="l", }, - [0x16828]={ - category="lo", + [0x16E6F]={ + category="ll", direction="l", }, - [0x16829]={ - category="lo", + [0x16E70]={ + category="ll", direction="l", }, - [0x1682A]={ - category="lo", + [0x16E71]={ + category="ll", direction="l", }, - [0x1682B]={ - category="lo", + [0x16E72]={ + category="ll", direction="l", }, - [0x1682C]={ - category="lo", + [0x16E73]={ + category="ll", direction="l", }, - [0x1682D]={ - category="lo", + [0x16E74]={ + category="ll", direction="l", }, - [0x1682E]={ - category="lo", + [0x16E75]={ + category="ll", direction="l", }, - [0x1682F]={ - category="lo", + [0x16E76]={ + category="ll", direction="l", }, - [0x16830]={ - category="lo", + [0x16E77]={ + category="ll", direction="l", }, - [0x16831]={ - category="lo", + [0x16E78]={ + category="ll", direction="l", }, - [0x16832]={ - category="lo", + [0x16E79]={ + category="ll", direction="l", }, - [0x16833]={ - category="lo", + [0x16E7A]={ + category="ll", direction="l", }, - [0x16834]={ - category="lo", + [0x16E7B]={ + category="ll", direction="l", }, - [0x16835]={ - category="lo", + [0x16E7C]={ + category="ll", direction="l", }, - [0x16836]={ - category="lo", + [0x16E7D]={ + category="ll", direction="l", }, - [0x16837]={ - category="lo", + [0x16E7E]={ + category="ll", direction="l", }, - [0x16838]={ - category="lo", + [0x16E7F]={ + category="ll", direction="l", }, - [0x16839]={ - category="lo", + [0x16E80]={ + category="no", direction="l", }, - [0x1683A]={ - category="lo", + [0x16E81]={ + category="no", direction="l", }, - [0x1683B]={ - category="lo", + [0x16E82]={ + category="no", direction="l", }, - [0x1683C]={ - category="lo", + [0x16E83]={ + category="no", direction="l", }, - [0x1683D]={ - category="lo", + [0x16E84]={ + category="no", direction="l", }, - [0x1683E]={ - category="lo", + [0x16E85]={ + category="no", direction="l", }, - [0x1683F]={ - category="lo", + [0x16E86]={ + category="no", direction="l", }, - [0x16840]={ - category="lo", + [0x16E87]={ + category="no", direction="l", }, - [0x16841]={ - category="lo", + [0x16E88]={ + category="no", direction="l", }, - [0x16842]={ - category="lo", + [0x16E89]={ + category="no", direction="l", }, - [0x16843]={ - category="lo", + [0x16E8A]={ + category="no", direction="l", }, - [0x16844]={ - category="lo", + [0x16E8B]={ + category="no", direction="l", }, - [0x16845]={ - category="lo", + [0x16E8C]={ + category="no", direction="l", }, - [0x16846]={ - category="lo", + [0x16E8D]={ + category="no", direction="l", }, - [0x16847]={ - category="lo", + [0x16E8E]={ + category="no", direction="l", }, - [0x16848]={ - category="lo", + [0x16E8F]={ + category="no", direction="l", }, - [0x16849]={ - category="lo", + [0x16E90]={ + category="no", direction="l", }, - [0x1684A]={ - category="lo", + [0x16E91]={ + category="no", direction="l", }, - [0x1684B]={ - category="lo", + [0x16E92]={ + category="no", direction="l", }, - [0x1684C]={ - category="lo", + [0x16E93]={ + category="no", direction="l", }, - [0x1684D]={ - category="lo", + [0x16E94]={ + category="no", direction="l", }, - [0x1684E]={ - category="lo", + [0x16E95]={ + category="no", direction="l", }, - [0x1684F]={ - category="lo", + [0x16E96]={ + category="no", direction="l", }, - [0x16850]={ - category="lo", + [0x16E97]={ + category="po", direction="l", }, - [0x16851]={ - category="lo", + [0x16E98]={ + category="po", direction="l", }, - [0x16852]={ - category="lo", + [0x16E99]={ + category="po", direction="l", }, - [0x16853]={ - category="lo", + [0x16E9A]={ + category="po", direction="l", }, - [0x16854]={ + [0x16F00]={ category="lo", direction="l", }, - [0x16855]={ + [0x16F01]={ category="lo", direction="l", }, - [0x16856]={ + [0x16F02]={ category="lo", direction="l", }, - [0x16857]={ + [0x16F03]={ category="lo", direction="l", }, - [0x16858]={ + [0x16F04]={ category="lo", direction="l", }, - [0x16859]={ + [0x16F05]={ category="lo", direction="l", }, - [0x1685A]={ + [0x16F06]={ category="lo", direction="l", }, - [0x1685B]={ + [0x16F07]={ category="lo", direction="l", }, - [0x1685C]={ + [0x16F08]={ category="lo", direction="l", }, - [0x1685D]={ + [0x16F09]={ category="lo", direction="l", }, - [0x1685E]={ + [0x16F0A]={ category="lo", direction="l", }, - [0x1685F]={ + [0x16F0B]={ category="lo", direction="l", }, - [0x16860]={ + [0x16F0C]={ category="lo", direction="l", }, - [0x16861]={ + [0x16F0D]={ category="lo", direction="l", }, - [0x16862]={ + [0x16F0E]={ category="lo", direction="l", }, - [0x16863]={ + [0x16F0F]={ category="lo", direction="l", }, - [0x16864]={ + [0x16F10]={ category="lo", direction="l", }, - [0x16865]={ + [0x16F11]={ category="lo", direction="l", }, - [0x16866]={ + [0x16F12]={ category="lo", direction="l", }, - [0x16867]={ + [0x16F13]={ category="lo", direction="l", }, - [0x16868]={ + [0x16F14]={ category="lo", direction="l", }, - [0x16869]={ + [0x16F15]={ category="lo", direction="l", }, - [0x1686A]={ + [0x16F16]={ category="lo", direction="l", }, - [0x1686B]={ + [0x16F17]={ category="lo", direction="l", }, - [0x1686C]={ + [0x16F18]={ category="lo", direction="l", }, - [0x1686D]={ + [0x16F19]={ category="lo", direction="l", }, - [0x1686E]={ + [0x16F1A]={ category="lo", direction="l", }, - [0x1686F]={ + [0x16F1B]={ category="lo", direction="l", }, - [0x16870]={ + [0x16F1C]={ category="lo", direction="l", }, - [0x16871]={ + [0x16F1D]={ category="lo", direction="l", }, - [0x16872]={ + [0x16F1E]={ category="lo", direction="l", }, - [0x16873]={ + [0x16F1F]={ category="lo", direction="l", }, - [0x16874]={ + [0x16F20]={ category="lo", direction="l", }, - [0x16875]={ + [0x16F21]={ category="lo", direction="l", }, - [0x16876]={ + [0x16F22]={ category="lo", direction="l", }, - [0x16877]={ + [0x16F23]={ category="lo", direction="l", }, - [0x16878]={ + [0x16F24]={ category="lo", direction="l", }, - [0x16879]={ + [0x16F25]={ category="lo", direction="l", }, - [0x1687A]={ + [0x16F26]={ category="lo", direction="l", }, - [0x1687B]={ + [0x16F27]={ category="lo", direction="l", }, - [0x1687C]={ + [0x16F28]={ category="lo", direction="l", }, - [0x1687D]={ + [0x16F29]={ category="lo", direction="l", }, - [0x1687E]={ + [0x16F2A]={ category="lo", direction="l", }, - [0x1687F]={ + [0x16F2B]={ category="lo", direction="l", }, - [0x16880]={ + [0x16F2C]={ category="lo", direction="l", }, - [0x16881]={ + [0x16F2D]={ category="lo", direction="l", }, - [0x16882]={ + [0x16F2E]={ category="lo", direction="l", }, - [0x16883]={ + [0x16F2F]={ category="lo", direction="l", }, - [0x16884]={ + [0x16F30]={ category="lo", direction="l", }, - [0x16885]={ + [0x16F31]={ category="lo", direction="l", }, - [0x16886]={ + [0x16F32]={ category="lo", direction="l", }, - [0x16887]={ + [0x16F33]={ category="lo", direction="l", }, - [0x16888]={ + [0x16F34]={ category="lo", direction="l", }, - [0x16889]={ + [0x16F35]={ category="lo", direction="l", }, - [0x1688A]={ + [0x16F36]={ category="lo", direction="l", }, - [0x1688B]={ + [0x16F37]={ category="lo", direction="l", }, - [0x1688C]={ + [0x16F38]={ category="lo", direction="l", }, - [0x1688D]={ + [0x16F39]={ category="lo", direction="l", }, - [0x1688E]={ + [0x16F3A]={ category="lo", direction="l", }, - [0x1688F]={ + [0x16F3B]={ category="lo", direction="l", }, - [0x16890]={ + [0x16F3C]={ category="lo", direction="l", }, - [0x16891]={ + [0x16F3D]={ category="lo", direction="l", }, - [0x16892]={ + [0x16F3E]={ category="lo", direction="l", }, - [0x16893]={ + [0x16F3F]={ category="lo", direction="l", }, - [0x16894]={ + [0x16F40]={ category="lo", direction="l", }, - [0x16895]={ + [0x16F41]={ category="lo", direction="l", }, - [0x16896]={ + [0x16F42]={ category="lo", direction="l", }, - [0x16897]={ + [0x16F43]={ category="lo", direction="l", }, - [0x16898]={ + [0x16F44]={ category="lo", direction="l", }, - [0x16899]={ + [0x16F50]={ category="lo", direction="l", }, - [0x1689A]={ - category="lo", + [0x16F51]={ + category="mc", direction="l", }, - [0x1689B]={ - category="lo", + [0x16F52]={ + category="mc", direction="l", }, - [0x1689C]={ - category="lo", + [0x16F53]={ + category="mc", direction="l", }, - [0x1689D]={ - category="lo", + [0x16F54]={ + category="mc", direction="l", }, - [0x1689E]={ - category="lo", + [0x16F55]={ + category="mc", direction="l", }, - [0x1689F]={ - category="lo", + [0x16F56]={ + category="mc", direction="l", }, - [0x168A0]={ - category="lo", + [0x16F57]={ + category="mc", direction="l", }, - [0x168A1]={ - category="lo", + [0x16F58]={ + category="mc", direction="l", }, - [0x168A2]={ - category="lo", + [0x16F59]={ + category="mc", direction="l", }, - [0x168A3]={ - category="lo", + [0x16F5A]={ + category="mc", direction="l", }, - [0x168A4]={ - category="lo", + [0x16F5B]={ + category="mc", direction="l", }, - [0x168A5]={ - category="lo", + [0x16F5C]={ + category="mc", direction="l", }, - [0x168A6]={ - category="lo", + [0x16F5D]={ + category="mc", direction="l", }, - [0x168A7]={ - category="lo", + [0x16F5E]={ + category="mc", direction="l", }, - [0x168A8]={ - category="lo", + [0x16F5F]={ + category="mc", direction="l", }, - [0x168A9]={ - category="lo", + [0x16F60]={ + category="mc", direction="l", }, - [0x168AA]={ - category="lo", + [0x16F61]={ + category="mc", direction="l", }, - [0x168AB]={ - category="lo", + [0x16F62]={ + category="mc", direction="l", }, - [0x168AC]={ - category="lo", + [0x16F63]={ + category="mc", direction="l", }, - [0x168AD]={ - category="lo", + [0x16F64]={ + category="mc", direction="l", }, - [0x168AE]={ - category="lo", + [0x16F65]={ + category="mc", direction="l", }, - [0x168AF]={ - category="lo", + [0x16F66]={ + category="mc", direction="l", }, - [0x168B0]={ - category="lo", + [0x16F67]={ + category="mc", direction="l", }, - [0x168B1]={ - category="lo", + [0x16F68]={ + category="mc", direction="l", }, - [0x168B2]={ - category="lo", + [0x16F69]={ + category="mc", direction="l", }, - [0x168B3]={ - category="lo", + [0x16F6A]={ + category="mc", direction="l", }, - [0x168B4]={ - category="lo", + [0x16F6B]={ + category="mc", direction="l", }, - [0x168B5]={ - category="lo", + [0x16F6C]={ + category="mc", direction="l", }, - [0x168B6]={ - category="lo", + [0x16F6D]={ + category="mc", direction="l", }, - [0x168B7]={ - category="lo", + [0x16F6E]={ + category="mc", direction="l", }, - [0x168B8]={ - category="lo", + [0x16F6F]={ + category="mc", direction="l", }, - [0x168B9]={ - category="lo", + [0x16F70]={ + category="mc", direction="l", }, - [0x168BA]={ - category="lo", + [0x16F71]={ + category="mc", direction="l", }, - [0x168BB]={ - category="lo", + [0x16F72]={ + category="mc", direction="l", }, - [0x168BC]={ - category="lo", + [0x16F73]={ + category="mc", direction="l", }, - [0x168BD]={ - category="lo", + [0x16F74]={ + category="mc", direction="l", }, - [0x168BE]={ - category="lo", + [0x16F75]={ + category="mc", direction="l", }, - [0x168BF]={ - category="lo", + [0x16F76]={ + category="mc", direction="l", }, - [0x168C0]={ - category="lo", + [0x16F77]={ + category="mc", direction="l", }, - [0x168C1]={ - category="lo", + [0x16F78]={ + category="mc", direction="l", }, - [0x168C2]={ - category="lo", + [0x16F79]={ + category="mc", direction="l", }, - [0x168C3]={ - category="lo", + [0x16F7A]={ + category="mc", direction="l", }, - [0x168C4]={ - category="lo", + [0x16F7B]={ + category="mc", direction="l", }, - [0x168C5]={ - category="lo", + [0x16F7C]={ + category="mc", direction="l", }, - [0x168C6]={ - category="lo", + [0x16F7D]={ + category="mc", direction="l", }, - [0x168C7]={ - category="lo", + [0x16F7E]={ + category="mc", direction="l", }, - [0x168C8]={ - category="lo", + [0x16F8F]={ + category="mn", + direction="nsm", + }, + [0x16F90]={ + category="mn", + direction="nsm", + }, + [0x16F91]={ + category="mn", + direction="nsm", + }, + [0x16F92]={ + category="mn", + direction="nsm", + }, + [0x16F93]={ + category="lm", direction="l", }, - [0x168C9]={ - category="lo", + [0x16F94]={ + category="lm", direction="l", }, - [0x168CA]={ - category="lo", + [0x16F95]={ + category="lm", direction="l", }, - [0x168CB]={ - category="lo", + [0x16F96]={ + category="lm", direction="l", }, - [0x168CC]={ - category="lo", + [0x16F97]={ + category="lm", direction="l", }, - [0x168CD]={ - category="lo", + [0x16F98]={ + category="lm", direction="l", }, - [0x168CE]={ - category="lo", + [0x16F99]={ + category="lm", direction="l", }, - [0x168CF]={ - category="lo", + [0x16F9A]={ + category="lm", direction="l", }, - [0x168D0]={ - category="lo", + [0x16F9B]={ + category="lm", direction="l", }, - [0x168D1]={ - category="lo", + [0x16F9C]={ + category="lm", direction="l", }, - [0x168D2]={ - category="lo", + [0x16F9D]={ + category="lm", direction="l", }, - [0x168D3]={ - category="lo", + [0x16F9E]={ + category="lm", direction="l", }, - [0x168D4]={ - category="lo", + [0x16F9F]={ + category="lm", direction="l", }, - [0x168D5]={ - category="lo", + [0x16FE0]={ + category="lm", direction="l", }, - [0x168D6]={ - category="lo", + [0x16FE1]={ + category="lm", direction="l", }, - [0x168D7]={ + [0x18800]={ category="lo", direction="l", }, - [0x168D8]={ + [0x18801]={ category="lo", direction="l", }, - [0x168D9]={ + [0x18802]={ category="lo", direction="l", }, - [0x168DA]={ + [0x18803]={ category="lo", direction="l", }, - [0x168DB]={ + [0x18804]={ category="lo", direction="l", }, - [0x168DC]={ + [0x18805]={ category="lo", direction="l", }, - [0x168DD]={ + [0x18806]={ category="lo", direction="l", }, - [0x168DE]={ + [0x18807]={ category="lo", direction="l", }, - [0x168DF]={ + [0x18808]={ category="lo", direction="l", }, - [0x168E0]={ + [0x18809]={ category="lo", direction="l", }, - [0x168E1]={ + [0x1880A]={ category="lo", direction="l", }, - [0x168E2]={ + [0x1880B]={ category="lo", direction="l", }, - [0x168E3]={ + [0x1880C]={ category="lo", direction="l", }, - [0x168E4]={ + [0x1880D]={ category="lo", direction="l", }, - [0x168E5]={ + [0x1880E]={ category="lo", direction="l", }, - [0x168E6]={ + [0x1880F]={ category="lo", direction="l", }, - [0x168E7]={ + [0x18810]={ category="lo", direction="l", }, - [0x168E8]={ + [0x18811]={ category="lo", direction="l", }, - [0x168E9]={ + [0x18812]={ category="lo", direction="l", }, - [0x168EA]={ + [0x18813]={ category="lo", direction="l", }, - [0x168EB]={ + [0x18814]={ category="lo", direction="l", }, - [0x168EC]={ + [0x18815]={ category="lo", direction="l", }, - [0x168ED]={ + [0x18816]={ category="lo", direction="l", }, - [0x168EE]={ + [0x18817]={ category="lo", direction="l", }, - [0x168EF]={ + [0x18818]={ category="lo", direction="l", }, - [0x168F0]={ + [0x18819]={ category="lo", direction="l", }, - [0x168F1]={ + [0x1881A]={ category="lo", direction="l", }, - [0x168F2]={ + [0x1881B]={ category="lo", direction="l", }, - [0x168F3]={ + [0x1881C]={ category="lo", direction="l", }, - [0x168F4]={ + [0x1881D]={ category="lo", direction="l", }, - [0x168F5]={ + [0x1881E]={ category="lo", direction="l", }, - [0x168F6]={ + [0x1881F]={ category="lo", direction="l", }, - [0x168F7]={ + [0x18820]={ category="lo", direction="l", }, - [0x168F8]={ + [0x18821]={ category="lo", direction="l", }, - [0x168F9]={ + [0x18822]={ category="lo", direction="l", }, - [0x168FA]={ + [0x18823]={ category="lo", direction="l", }, - [0x168FB]={ + [0x18824]={ category="lo", direction="l", }, - [0x168FC]={ + [0x18825]={ category="lo", direction="l", }, - [0x168FD]={ + [0x18826]={ category="lo", direction="l", }, - [0x168FE]={ + [0x18827]={ category="lo", direction="l", }, - [0x168FF]={ + [0x18828]={ category="lo", direction="l", }, - [0x16900]={ + [0x18829]={ category="lo", direction="l", }, - [0x16901]={ + [0x1882A]={ category="lo", direction="l", }, - [0x16902]={ + [0x1882B]={ category="lo", direction="l", }, - [0x16903]={ + [0x1882C]={ category="lo", direction="l", }, - [0x16904]={ + [0x1882D]={ category="lo", direction="l", }, - [0x16905]={ + [0x1882E]={ category="lo", direction="l", }, - [0x16906]={ + [0x1882F]={ category="lo", direction="l", }, - [0x16907]={ + [0x18830]={ category="lo", direction="l", }, - [0x16908]={ + [0x18831]={ category="lo", direction="l", }, - [0x16909]={ + [0x18832]={ category="lo", direction="l", }, - [0x1690A]={ + [0x18833]={ category="lo", direction="l", }, - [0x1690B]={ + [0x18834]={ category="lo", direction="l", }, - [0x1690C]={ + [0x18835]={ category="lo", direction="l", }, - [0x1690D]={ + [0x18836]={ category="lo", direction="l", }, - [0x1690E]={ + [0x18837]={ category="lo", direction="l", }, - [0x1690F]={ + [0x18838]={ category="lo", direction="l", }, - [0x16910]={ + [0x18839]={ category="lo", direction="l", }, - [0x16911]={ + [0x1883A]={ category="lo", direction="l", }, - [0x16912]={ + [0x1883B]={ category="lo", direction="l", }, - [0x16913]={ + [0x1883C]={ category="lo", direction="l", }, - [0x16914]={ + [0x1883D]={ category="lo", direction="l", }, - [0x16915]={ + [0x1883E]={ category="lo", direction="l", }, - [0x16916]={ + [0x1883F]={ category="lo", direction="l", }, - [0x16917]={ + [0x18840]={ category="lo", direction="l", }, - [0x16918]={ + [0x18841]={ category="lo", direction="l", }, - [0x16919]={ + [0x18842]={ category="lo", direction="l", }, - [0x1691A]={ + [0x18843]={ category="lo", direction="l", }, - [0x1691B]={ + [0x18844]={ category="lo", direction="l", }, - [0x1691C]={ + [0x18845]={ category="lo", direction="l", }, - [0x1691D]={ + [0x18846]={ category="lo", direction="l", }, - [0x1691E]={ + [0x18847]={ category="lo", direction="l", }, - [0x1691F]={ + [0x18848]={ category="lo", direction="l", }, - [0x16920]={ + [0x18849]={ category="lo", direction="l", }, - [0x16921]={ + [0x1884A]={ category="lo", direction="l", }, - [0x16922]={ + [0x1884B]={ category="lo", direction="l", }, - [0x16923]={ + [0x1884C]={ category="lo", direction="l", }, - [0x16924]={ + [0x1884D]={ category="lo", direction="l", }, - [0x16925]={ + [0x1884E]={ category="lo", direction="l", }, - [0x16926]={ + [0x1884F]={ category="lo", direction="l", }, - [0x16927]={ + [0x18850]={ category="lo", direction="l", }, - [0x16928]={ + [0x18851]={ category="lo", direction="l", }, - [0x16929]={ + [0x18852]={ category="lo", direction="l", }, - [0x1692A]={ + [0x18853]={ category="lo", direction="l", }, - [0x1692B]={ + [0x18854]={ category="lo", direction="l", }, - [0x1692C]={ + [0x18855]={ category="lo", direction="l", }, - [0x1692D]={ + [0x18856]={ category="lo", direction="l", }, - [0x1692E]={ + [0x18857]={ category="lo", direction="l", }, - [0x1692F]={ + [0x18858]={ category="lo", direction="l", }, - [0x16930]={ + [0x18859]={ category="lo", direction="l", }, - [0x16931]={ + [0x1885A]={ category="lo", direction="l", }, - [0x16932]={ + [0x1885B]={ category="lo", direction="l", }, - [0x16933]={ + [0x1885C]={ category="lo", direction="l", }, - [0x16934]={ + [0x1885D]={ category="lo", direction="l", }, - [0x16935]={ + [0x1885E]={ category="lo", direction="l", }, - [0x16936]={ + [0x1885F]={ category="lo", direction="l", }, - [0x16937]={ + [0x18860]={ category="lo", direction="l", }, - [0x16938]={ + [0x18861]={ category="lo", direction="l", }, - [0x16939]={ + [0x18862]={ category="lo", direction="l", }, - [0x1693A]={ + [0x18863]={ category="lo", direction="l", }, - [0x1693B]={ + [0x18864]={ category="lo", direction="l", }, - [0x1693C]={ + [0x18865]={ category="lo", direction="l", }, - [0x1693D]={ + [0x18866]={ category="lo", direction="l", }, - [0x1693E]={ + [0x18867]={ category="lo", direction="l", }, - [0x1693F]={ + [0x18868]={ category="lo", direction="l", }, - [0x16940]={ + [0x18869]={ category="lo", direction="l", }, - [0x16941]={ + [0x1886A]={ category="lo", direction="l", }, - [0x16942]={ + [0x1886B]={ category="lo", direction="l", }, - [0x16943]={ + [0x1886C]={ category="lo", direction="l", }, - [0x16944]={ + [0x1886D]={ category="lo", direction="l", }, - [0x16945]={ + [0x1886E]={ category="lo", direction="l", }, - [0x16946]={ + [0x1886F]={ category="lo", direction="l", }, - [0x16947]={ + [0x18870]={ category="lo", direction="l", }, - [0x16948]={ + [0x18871]={ category="lo", direction="l", }, - [0x16949]={ + [0x18872]={ category="lo", direction="l", }, - [0x1694A]={ + [0x18873]={ category="lo", direction="l", }, - [0x1694B]={ + [0x18874]={ category="lo", direction="l", }, - [0x1694C]={ + [0x18875]={ category="lo", direction="l", }, - [0x1694D]={ + [0x18876]={ category="lo", direction="l", }, - [0x1694E]={ + [0x18877]={ category="lo", direction="l", }, - [0x1694F]={ + [0x18878]={ category="lo", direction="l", }, - [0x16950]={ + [0x18879]={ category="lo", direction="l", }, - [0x16951]={ + [0x1887A]={ category="lo", direction="l", }, - [0x16952]={ + [0x1887B]={ category="lo", direction="l", }, - [0x16953]={ + [0x1887C]={ category="lo", direction="l", }, - [0x16954]={ + [0x1887D]={ category="lo", direction="l", }, - [0x16955]={ + [0x1887E]={ category="lo", direction="l", }, - [0x16956]={ + [0x1887F]={ category="lo", direction="l", }, - [0x16957]={ + [0x18880]={ category="lo", direction="l", }, - [0x16958]={ + [0x18881]={ category="lo", direction="l", }, - [0x16959]={ + [0x18882]={ category="lo", direction="l", }, - [0x1695A]={ + [0x18883]={ category="lo", direction="l", }, - [0x1695B]={ + [0x18884]={ category="lo", direction="l", }, - [0x1695C]={ + [0x18885]={ category="lo", direction="l", }, - [0x1695D]={ + [0x18886]={ category="lo", direction="l", }, - [0x1695E]={ + [0x18887]={ category="lo", direction="l", }, - [0x1695F]={ + [0x18888]={ category="lo", direction="l", }, - [0x16960]={ + [0x18889]={ category="lo", direction="l", }, - [0x16961]={ + [0x1888A]={ category="lo", direction="l", }, - [0x16962]={ + [0x1888B]={ category="lo", direction="l", }, - [0x16963]={ + [0x1888C]={ category="lo", direction="l", }, - [0x16964]={ + [0x1888D]={ category="lo", direction="l", }, - [0x16965]={ + [0x1888E]={ category="lo", direction="l", }, - [0x16966]={ + [0x1888F]={ category="lo", direction="l", }, - [0x16967]={ + [0x18890]={ category="lo", direction="l", }, - [0x16968]={ + [0x18891]={ category="lo", direction="l", }, - [0x16969]={ + [0x18892]={ category="lo", direction="l", }, - [0x1696A]={ + [0x18893]={ category="lo", direction="l", }, - [0x1696B]={ + [0x18894]={ category="lo", direction="l", }, - [0x1696C]={ + [0x18895]={ category="lo", direction="l", }, - [0x1696D]={ + [0x18896]={ category="lo", direction="l", }, - [0x1696E]={ + [0x18897]={ category="lo", direction="l", }, - [0x1696F]={ + [0x18898]={ category="lo", direction="l", }, - [0x16970]={ + [0x18899]={ category="lo", direction="l", }, - [0x16971]={ + [0x1889A]={ category="lo", direction="l", }, - [0x16972]={ + [0x1889B]={ category="lo", direction="l", }, - [0x16973]={ + [0x1889C]={ category="lo", direction="l", }, - [0x16974]={ + [0x1889D]={ category="lo", direction="l", }, - [0x16975]={ + [0x1889E]={ category="lo", direction="l", }, - [0x16976]={ + [0x1889F]={ category="lo", direction="l", }, - [0x16977]={ + [0x188A0]={ category="lo", direction="l", }, - [0x16978]={ + [0x188A1]={ category="lo", direction="l", }, - [0x16979]={ + [0x188A2]={ category="lo", direction="l", }, - [0x1697A]={ + [0x188A3]={ category="lo", direction="l", }, - [0x1697B]={ + [0x188A4]={ category="lo", direction="l", }, - [0x1697C]={ + [0x188A5]={ category="lo", direction="l", }, - [0x1697D]={ + [0x188A6]={ category="lo", direction="l", }, - [0x1697E]={ + [0x188A7]={ category="lo", direction="l", }, - [0x1697F]={ + [0x188A8]={ category="lo", direction="l", }, - [0x16980]={ + [0x188A9]={ category="lo", direction="l", }, - [0x16981]={ + [0x188AA]={ category="lo", direction="l", }, - [0x16982]={ + [0x188AB]={ category="lo", direction="l", }, - [0x16983]={ + [0x188AC]={ category="lo", direction="l", }, - [0x16984]={ + [0x188AD]={ category="lo", direction="l", }, - [0x16985]={ + [0x188AE]={ category="lo", direction="l", }, - [0x16986]={ + [0x188AF]={ category="lo", direction="l", }, - [0x16987]={ + [0x188B0]={ category="lo", direction="l", }, - [0x16988]={ + [0x188B1]={ category="lo", direction="l", }, - [0x16989]={ + [0x188B2]={ category="lo", direction="l", }, - [0x1698A]={ + [0x188B3]={ category="lo", direction="l", }, - [0x1698B]={ + [0x188B4]={ category="lo", direction="l", }, - [0x1698C]={ + [0x188B5]={ category="lo", direction="l", }, - [0x1698D]={ + [0x188B6]={ category="lo", direction="l", }, - [0x1698E]={ + [0x188B7]={ category="lo", direction="l", }, - [0x1698F]={ + [0x188B8]={ category="lo", direction="l", }, - [0x16990]={ + [0x188B9]={ category="lo", direction="l", }, - [0x16991]={ + [0x188BA]={ category="lo", direction="l", }, - [0x16992]={ + [0x188BB]={ category="lo", direction="l", }, - [0x16993]={ + [0x188BC]={ category="lo", direction="l", }, - [0x16994]={ + [0x188BD]={ category="lo", direction="l", }, - [0x16995]={ + [0x188BE]={ category="lo", direction="l", }, - [0x16996]={ + [0x188BF]={ category="lo", direction="l", }, - [0x16997]={ + [0x188C0]={ category="lo", direction="l", }, - [0x16998]={ + [0x188C1]={ category="lo", direction="l", }, - [0x16999]={ + [0x188C2]={ category="lo", direction="l", }, - [0x1699A]={ + [0x188C3]={ category="lo", direction="l", }, - [0x1699B]={ + [0x188C4]={ category="lo", direction="l", }, - [0x1699C]={ + [0x188C5]={ category="lo", direction="l", }, - [0x1699D]={ + [0x188C6]={ category="lo", direction="l", }, - [0x1699E]={ + [0x188C7]={ category="lo", direction="l", }, - [0x1699F]={ + [0x188C8]={ category="lo", direction="l", }, - [0x169A0]={ + [0x188C9]={ category="lo", direction="l", }, - [0x169A1]={ + [0x188CA]={ category="lo", direction="l", }, - [0x169A2]={ + [0x188CB]={ category="lo", direction="l", }, - [0x169A3]={ + [0x188CC]={ category="lo", direction="l", }, - [0x169A4]={ + [0x188CD]={ category="lo", direction="l", }, - [0x169A5]={ + [0x188CE]={ category="lo", direction="l", }, - [0x169A6]={ + [0x188CF]={ category="lo", direction="l", }, - [0x169A7]={ + [0x188D0]={ category="lo", direction="l", }, - [0x169A8]={ + [0x188D1]={ category="lo", direction="l", }, - [0x169A9]={ + [0x188D2]={ category="lo", direction="l", }, - [0x169AA]={ + [0x188D3]={ category="lo", direction="l", }, - [0x169AB]={ + [0x188D4]={ category="lo", direction="l", }, - [0x169AC]={ + [0x188D5]={ category="lo", direction="l", }, - [0x169AD]={ + [0x188D6]={ category="lo", direction="l", }, - [0x169AE]={ + [0x188D7]={ category="lo", direction="l", }, - [0x169AF]={ + [0x188D8]={ category="lo", direction="l", }, - [0x169B0]={ + [0x188D9]={ category="lo", direction="l", }, - [0x169B1]={ + [0x188DA]={ category="lo", direction="l", }, - [0x169B2]={ + [0x188DB]={ category="lo", direction="l", }, - [0x169B3]={ + [0x188DC]={ category="lo", direction="l", }, - [0x169B4]={ + [0x188DD]={ category="lo", direction="l", }, - [0x169B5]={ + [0x188DE]={ category="lo", direction="l", }, - [0x169B6]={ + [0x188DF]={ category="lo", direction="l", }, - [0x169B7]={ + [0x188E0]={ category="lo", direction="l", }, - [0x169B8]={ + [0x188E1]={ category="lo", direction="l", }, - [0x169B9]={ + [0x188E2]={ category="lo", direction="l", }, - [0x169BA]={ + [0x188E3]={ category="lo", direction="l", }, - [0x169BB]={ + [0x188E4]={ category="lo", direction="l", }, - [0x169BC]={ + [0x188E5]={ category="lo", direction="l", }, - [0x169BD]={ + [0x188E6]={ category="lo", direction="l", }, - [0x169BE]={ + [0x188E7]={ category="lo", direction="l", }, - [0x169BF]={ + [0x188E8]={ category="lo", direction="l", }, - [0x169C0]={ + [0x188E9]={ category="lo", direction="l", }, - [0x169C1]={ + [0x188EA]={ category="lo", direction="l", }, - [0x169C2]={ + [0x188EB]={ category="lo", direction="l", }, - [0x169C3]={ + [0x188EC]={ category="lo", direction="l", }, - [0x169C4]={ + [0x188ED]={ category="lo", direction="l", }, - [0x169C5]={ + [0x188EE]={ category="lo", direction="l", }, - [0x169C6]={ + [0x188EF]={ category="lo", direction="l", }, - [0x169C7]={ + [0x188F0]={ category="lo", direction="l", }, - [0x169C8]={ + [0x188F1]={ category="lo", direction="l", }, - [0x169C9]={ + [0x188F2]={ category="lo", direction="l", }, - [0x169CA]={ + [0x188F3]={ category="lo", direction="l", }, - [0x169CB]={ + [0x188F4]={ category="lo", direction="l", }, - [0x169CC]={ + [0x188F5]={ category="lo", direction="l", }, - [0x169CD]={ + [0x188F6]={ category="lo", direction="l", }, - [0x169CE]={ + [0x188F7]={ category="lo", direction="l", }, - [0x169CF]={ + [0x188F8]={ category="lo", direction="l", }, - [0x169D0]={ + [0x188F9]={ category="lo", direction="l", }, - [0x169D1]={ + [0x188FA]={ category="lo", direction="l", }, - [0x169D2]={ + [0x188FB]={ category="lo", direction="l", }, - [0x169D3]={ + [0x188FC]={ category="lo", direction="l", }, - [0x169D4]={ + [0x188FD]={ category="lo", direction="l", }, - [0x169D5]={ + [0x188FE]={ category="lo", direction="l", }, - [0x169D6]={ + [0x188FF]={ category="lo", direction="l", }, - [0x169D7]={ + [0x18900]={ category="lo", direction="l", }, - [0x169D8]={ + [0x18901]={ category="lo", direction="l", }, - [0x169D9]={ + [0x18902]={ category="lo", direction="l", }, - [0x169DA]={ + [0x18903]={ category="lo", direction="l", }, - [0x169DB]={ + [0x18904]={ category="lo", direction="l", }, - [0x169DC]={ + [0x18905]={ category="lo", direction="l", }, - [0x169DD]={ + [0x18906]={ category="lo", direction="l", }, - [0x169DE]={ + [0x18907]={ category="lo", direction="l", }, - [0x169DF]={ + [0x18908]={ category="lo", direction="l", }, - [0x169E0]={ + [0x18909]={ category="lo", direction="l", }, - [0x169E1]={ + [0x1890A]={ category="lo", direction="l", }, - [0x169E2]={ + [0x1890B]={ category="lo", direction="l", }, - [0x169E3]={ + [0x1890C]={ category="lo", direction="l", }, - [0x169E4]={ + [0x1890D]={ category="lo", direction="l", }, - [0x169E5]={ + [0x1890E]={ category="lo", direction="l", }, - [0x169E6]={ + [0x1890F]={ category="lo", direction="l", }, - [0x169E7]={ + [0x18910]={ category="lo", direction="l", }, - [0x169E8]={ + [0x18911]={ category="lo", direction="l", }, - [0x169E9]={ + [0x18912]={ category="lo", direction="l", }, - [0x169EA]={ + [0x18913]={ category="lo", direction="l", }, - [0x169EB]={ + [0x18914]={ category="lo", direction="l", }, - [0x169EC]={ + [0x18915]={ category="lo", direction="l", }, - [0x169ED]={ + [0x18916]={ category="lo", direction="l", }, - [0x169EE]={ + [0x18917]={ category="lo", direction="l", }, - [0x169EF]={ + [0x18918]={ category="lo", direction="l", }, - [0x169F0]={ + [0x18919]={ category="lo", direction="l", }, - [0x169F1]={ + [0x1891A]={ category="lo", direction="l", }, - [0x169F2]={ + [0x1891B]={ category="lo", direction="l", }, - [0x169F3]={ + [0x1891C]={ category="lo", direction="l", }, - [0x169F4]={ + [0x1891D]={ category="lo", direction="l", }, - [0x169F5]={ + [0x1891E]={ category="lo", direction="l", }, - [0x169F6]={ + [0x1891F]={ category="lo", direction="l", }, - [0x169F7]={ + [0x18920]={ category="lo", direction="l", }, - [0x169F8]={ + [0x18921]={ category="lo", direction="l", }, - [0x169F9]={ + [0x18922]={ category="lo", direction="l", }, - [0x169FA]={ + [0x18923]={ category="lo", direction="l", }, - [0x169FB]={ + [0x18924]={ category="lo", direction="l", }, - [0x169FC]={ + [0x18925]={ category="lo", direction="l", }, - [0x169FD]={ + [0x18926]={ category="lo", direction="l", }, - [0x169FE]={ + [0x18927]={ category="lo", direction="l", }, - [0x169FF]={ + [0x18928]={ category="lo", direction="l", }, - [0x16A00]={ + [0x18929]={ category="lo", direction="l", }, - [0x16A01]={ + [0x1892A]={ category="lo", direction="l", }, - [0x16A02]={ + [0x1892B]={ category="lo", direction="l", }, - [0x16A03]={ + [0x1892C]={ category="lo", direction="l", }, - [0x16A04]={ + [0x1892D]={ category="lo", direction="l", }, - [0x16A05]={ + [0x1892E]={ category="lo", direction="l", }, - [0x16A06]={ + [0x1892F]={ category="lo", direction="l", }, - [0x16A07]={ + [0x18930]={ category="lo", direction="l", }, - [0x16A08]={ + [0x18931]={ category="lo", direction="l", }, - [0x16A09]={ + [0x18932]={ category="lo", direction="l", }, - [0x16A0A]={ + [0x18933]={ category="lo", direction="l", }, - [0x16A0B]={ + [0x18934]={ category="lo", direction="l", }, - [0x16A0C]={ + [0x18935]={ category="lo", direction="l", }, - [0x16A0D]={ + [0x18936]={ category="lo", direction="l", }, - [0x16A0E]={ + [0x18937]={ category="lo", direction="l", }, - [0x16A0F]={ + [0x18938]={ category="lo", direction="l", }, - [0x16A10]={ + [0x18939]={ category="lo", direction="l", }, - [0x16A11]={ + [0x1893A]={ category="lo", direction="l", }, - [0x16A12]={ + [0x1893B]={ category="lo", direction="l", }, - [0x16A13]={ + [0x1893C]={ category="lo", direction="l", }, - [0x16A14]={ + [0x1893D]={ category="lo", direction="l", }, - [0x16A15]={ + [0x1893E]={ category="lo", direction="l", }, - [0x16A16]={ + [0x1893F]={ category="lo", direction="l", }, - [0x16A17]={ + [0x18940]={ category="lo", direction="l", }, - [0x16A18]={ + [0x18941]={ category="lo", direction="l", }, - [0x16A19]={ + [0x18942]={ category="lo", direction="l", }, - [0x16A1A]={ + [0x18943]={ category="lo", direction="l", }, - [0x16A1B]={ + [0x18944]={ category="lo", direction="l", }, - [0x16A1C]={ + [0x18945]={ category="lo", direction="l", }, - [0x16A1D]={ + [0x18946]={ category="lo", direction="l", }, - [0x16A1E]={ + [0x18947]={ category="lo", direction="l", }, - [0x16A1F]={ + [0x18948]={ category="lo", direction="l", }, - [0x16A20]={ + [0x18949]={ category="lo", direction="l", }, - [0x16A21]={ + [0x1894A]={ category="lo", direction="l", }, - [0x16A22]={ + [0x1894B]={ category="lo", direction="l", }, - [0x16A23]={ + [0x1894C]={ category="lo", direction="l", }, - [0x16A24]={ + [0x1894D]={ category="lo", direction="l", }, - [0x16A25]={ + [0x1894E]={ category="lo", direction="l", }, - [0x16A26]={ + [0x1894F]={ category="lo", direction="l", }, - [0x16A27]={ + [0x18950]={ category="lo", direction="l", }, - [0x16A28]={ + [0x18951]={ category="lo", direction="l", }, - [0x16A29]={ + [0x18952]={ category="lo", direction="l", }, - [0x16A2A]={ + [0x18953]={ category="lo", direction="l", }, - [0x16A2B]={ + [0x18954]={ category="lo", direction="l", }, - [0x16A2C]={ + [0x18955]={ category="lo", direction="l", }, - [0x16A2D]={ + [0x18956]={ category="lo", direction="l", }, - [0x16A2E]={ + [0x18957]={ category="lo", direction="l", }, - [0x16A2F]={ + [0x18958]={ category="lo", direction="l", }, - [0x16A30]={ + [0x18959]={ category="lo", direction="l", }, - [0x16A31]={ + [0x1895A]={ category="lo", direction="l", }, - [0x16A32]={ + [0x1895B]={ category="lo", direction="l", }, - [0x16A33]={ + [0x1895C]={ category="lo", direction="l", }, - [0x16A34]={ + [0x1895D]={ category="lo", direction="l", }, - [0x16A35]={ + [0x1895E]={ category="lo", direction="l", }, - [0x16A36]={ + [0x1895F]={ category="lo", direction="l", }, - [0x16A37]={ + [0x18960]={ category="lo", direction="l", }, - [0x16A38]={ + [0x18961]={ category="lo", direction="l", }, - [0x16A40]={ + [0x18962]={ category="lo", direction="l", }, - [0x16A41]={ + [0x18963]={ category="lo", direction="l", }, - [0x16A42]={ + [0x18964]={ category="lo", direction="l", }, - [0x16A43]={ + [0x18965]={ category="lo", direction="l", }, - [0x16A44]={ + [0x18966]={ category="lo", direction="l", }, - [0x16A45]={ + [0x18967]={ category="lo", direction="l", }, - [0x16A46]={ + [0x18968]={ category="lo", direction="l", }, - [0x16A47]={ + [0x18969]={ category="lo", direction="l", }, - [0x16A48]={ + [0x1896A]={ category="lo", direction="l", }, - [0x16A49]={ + [0x1896B]={ category="lo", direction="l", }, - [0x16A4A]={ + [0x1896C]={ category="lo", direction="l", }, - [0x16A4B]={ + [0x1896D]={ category="lo", direction="l", }, - [0x16A4C]={ + [0x1896E]={ category="lo", direction="l", }, - [0x16A4D]={ + [0x1896F]={ category="lo", direction="l", }, - [0x16A4E]={ + [0x18970]={ category="lo", direction="l", }, - [0x16A4F]={ + [0x18971]={ category="lo", direction="l", }, - [0x16A50]={ + [0x18972]={ category="lo", direction="l", }, - [0x16A51]={ + [0x18973]={ category="lo", direction="l", }, - [0x16A52]={ + [0x18974]={ category="lo", direction="l", }, - [0x16A53]={ + [0x18975]={ category="lo", direction="l", }, - [0x16A54]={ + [0x18976]={ category="lo", direction="l", }, - [0x16A55]={ + [0x18977]={ category="lo", direction="l", }, - [0x16A56]={ + [0x18978]={ category="lo", direction="l", }, - [0x16A57]={ + [0x18979]={ category="lo", direction="l", }, - [0x16A58]={ + [0x1897A]={ category="lo", direction="l", }, - [0x16A59]={ + [0x1897B]={ category="lo", direction="l", }, - [0x16A5A]={ + [0x1897C]={ category="lo", direction="l", }, - [0x16A5B]={ + [0x1897D]={ category="lo", direction="l", }, - [0x16A5C]={ + [0x1897E]={ category="lo", direction="l", }, - [0x16A5D]={ + [0x1897F]={ category="lo", direction="l", }, - [0x16A5E]={ + [0x18980]={ category="lo", direction="l", }, - [0x16A60]={ - category="nd", + [0x18981]={ + category="lo", direction="l", }, - [0x16A61]={ - category="nd", + [0x18982]={ + category="lo", direction="l", }, - [0x16A62]={ - category="nd", + [0x18983]={ + category="lo", direction="l", }, - [0x16A63]={ - category="nd", + [0x18984]={ + category="lo", direction="l", }, - [0x16A64]={ - category="nd", + [0x18985]={ + category="lo", direction="l", }, - [0x16A65]={ - category="nd", + [0x18986]={ + category="lo", direction="l", }, - [0x16A66]={ - category="nd", + [0x18987]={ + category="lo", direction="l", }, - [0x16A67]={ - category="nd", + [0x18988]={ + category="lo", direction="l", }, - [0x16A68]={ - category="nd", + [0x18989]={ + category="lo", direction="l", }, - [0x16A69]={ - category="nd", + [0x1898A]={ + category="lo", direction="l", }, - [0x16A6E]={ - category="po", + [0x1898B]={ + category="lo", direction="l", }, - [0x16A6F]={ - category="po", + [0x1898C]={ + category="lo", direction="l", }, - [0x16AD0]={ + [0x1898D]={ category="lo", direction="l", }, - [0x16AD1]={ + [0x1898E]={ category="lo", direction="l", }, - [0x16AD2]={ + [0x1898F]={ category="lo", direction="l", }, - [0x16AD3]={ + [0x18990]={ category="lo", direction="l", }, - [0x16AD4]={ + [0x18991]={ category="lo", direction="l", }, - [0x16AD5]={ + [0x18992]={ category="lo", direction="l", }, - [0x16AD6]={ + [0x18993]={ category="lo", direction="l", }, - [0x16AD7]={ + [0x18994]={ category="lo", direction="l", }, - [0x16AD8]={ + [0x18995]={ category="lo", direction="l", }, - [0x16AD9]={ + [0x18996]={ category="lo", direction="l", }, - [0x16ADA]={ + [0x18997]={ category="lo", direction="l", }, - [0x16ADB]={ + [0x18998]={ category="lo", direction="l", }, - [0x16ADC]={ + [0x18999]={ category="lo", direction="l", }, - [0x16ADD]={ + [0x1899A]={ category="lo", direction="l", }, - [0x16ADE]={ + [0x1899B]={ category="lo", direction="l", }, - [0x16ADF]={ + [0x1899C]={ category="lo", direction="l", }, - [0x16AE0]={ + [0x1899D]={ category="lo", direction="l", }, - [0x16AE1]={ + [0x1899E]={ category="lo", direction="l", }, - [0x16AE2]={ + [0x1899F]={ category="lo", direction="l", }, - [0x16AE3]={ + [0x189A0]={ category="lo", direction="l", }, - [0x16AE4]={ + [0x189A1]={ category="lo", direction="l", }, - [0x16AE5]={ + [0x189A2]={ category="lo", direction="l", }, - [0x16AE6]={ + [0x189A3]={ category="lo", direction="l", }, - [0x16AE7]={ + [0x189A4]={ category="lo", direction="l", }, - [0x16AE8]={ + [0x189A5]={ category="lo", direction="l", }, - [0x16AE9]={ + [0x189A6]={ category="lo", direction="l", }, - [0x16AEA]={ + [0x189A7]={ category="lo", direction="l", }, - [0x16AEB]={ + [0x189A8]={ category="lo", direction="l", }, - [0x16AEC]={ + [0x189A9]={ category="lo", direction="l", }, - [0x16AED]={ + [0x189AA]={ category="lo", direction="l", }, - [0x16AF0]={ - category="mn", - direction="nsm", - }, - [0x16AF1]={ - category="mn", - direction="nsm", - }, - [0x16AF2]={ - category="mn", - direction="nsm", - }, - [0x16AF3]={ - category="mn", - direction="nsm", - }, - [0x16AF4]={ - category="mn", - direction="nsm", - }, - [0x16AF5]={ - category="po", + [0x189AB]={ + category="lo", direction="l", }, - [0x16B00]={ + [0x189AC]={ category="lo", direction="l", }, - [0x16B01]={ + [0x189AD]={ category="lo", direction="l", }, - [0x16B02]={ + [0x189AE]={ category="lo", direction="l", }, - [0x16B03]={ + [0x189AF]={ category="lo", direction="l", }, - [0x16B04]={ + [0x189B0]={ category="lo", direction="l", }, - [0x16B05]={ + [0x189B1]={ category="lo", direction="l", }, - [0x16B06]={ + [0x189B2]={ category="lo", direction="l", }, - [0x16B07]={ + [0x189B3]={ category="lo", direction="l", }, - [0x16B08]={ + [0x189B4]={ category="lo", direction="l", }, - [0x16B09]={ + [0x189B5]={ category="lo", direction="l", }, - [0x16B0A]={ + [0x189B6]={ category="lo", direction="l", }, - [0x16B0B]={ + [0x189B7]={ category="lo", direction="l", }, - [0x16B0C]={ + [0x189B8]={ category="lo", direction="l", }, - [0x16B0D]={ + [0x189B9]={ category="lo", direction="l", }, - [0x16B0E]={ + [0x189BA]={ category="lo", direction="l", }, - [0x16B0F]={ + [0x189BB]={ category="lo", direction="l", }, - [0x16B10]={ + [0x189BC]={ category="lo", direction="l", }, - [0x16B11]={ + [0x189BD]={ category="lo", direction="l", }, - [0x16B12]={ + [0x189BE]={ category="lo", direction="l", }, - [0x16B13]={ + [0x189BF]={ category="lo", direction="l", }, - [0x16B14]={ + [0x189C0]={ category="lo", direction="l", }, - [0x16B15]={ + [0x189C1]={ category="lo", direction="l", }, - [0x16B16]={ + [0x189C2]={ category="lo", direction="l", }, - [0x16B17]={ + [0x189C3]={ category="lo", direction="l", }, - [0x16B18]={ + [0x189C4]={ category="lo", direction="l", }, - [0x16B19]={ + [0x189C5]={ category="lo", direction="l", }, - [0x16B1A]={ + [0x189C6]={ category="lo", direction="l", }, - [0x16B1B]={ + [0x189C7]={ category="lo", direction="l", }, - [0x16B1C]={ + [0x189C8]={ category="lo", direction="l", }, - [0x16B1D]={ + [0x189C9]={ category="lo", direction="l", }, - [0x16B1E]={ + [0x189CA]={ category="lo", direction="l", }, - [0x16B1F]={ + [0x189CB]={ category="lo", direction="l", }, - [0x16B20]={ + [0x189CC]={ category="lo", direction="l", }, - [0x16B21]={ + [0x189CD]={ category="lo", direction="l", }, - [0x16B22]={ + [0x189CE]={ category="lo", direction="l", }, - [0x16B23]={ + [0x189CF]={ category="lo", direction="l", }, - [0x16B24]={ + [0x189D0]={ category="lo", direction="l", }, - [0x16B25]={ + [0x189D1]={ category="lo", direction="l", }, - [0x16B26]={ + [0x189D2]={ category="lo", direction="l", }, - [0x16B27]={ + [0x189D3]={ category="lo", direction="l", }, - [0x16B28]={ + [0x189D4]={ category="lo", direction="l", }, - [0x16B29]={ + [0x189D5]={ category="lo", direction="l", }, - [0x16B2A]={ + [0x189D6]={ category="lo", direction="l", }, - [0x16B2B]={ + [0x189D7]={ category="lo", direction="l", }, - [0x16B2C]={ + [0x189D8]={ category="lo", direction="l", }, - [0x16B2D]={ + [0x189D9]={ category="lo", direction="l", }, - [0x16B2E]={ + [0x189DA]={ category="lo", direction="l", }, - [0x16B2F]={ + [0x189DB]={ category="lo", direction="l", }, - [0x16B30]={ - category="mn", - direction="nsm", - }, - [0x16B31]={ - category="mn", - direction="nsm", - }, - [0x16B32]={ - category="mn", - direction="nsm", - }, - [0x16B33]={ - category="mn", - direction="nsm", - }, - [0x16B34]={ - category="mn", - direction="nsm", - }, - [0x16B35]={ - category="mn", - direction="nsm", - }, - [0x16B36]={ - category="mn", - direction="nsm", - }, - [0x16B37]={ - category="po", + [0x189DC]={ + category="lo", direction="l", }, - [0x16B38]={ - category="po", + [0x189DD]={ + category="lo", direction="l", }, - [0x16B39]={ - category="po", + [0x189DE]={ + category="lo", direction="l", }, - [0x16B3A]={ - category="po", + [0x189DF]={ + category="lo", direction="l", }, - [0x16B3B]={ - category="po", + [0x189E0]={ + category="lo", direction="l", }, - [0x16B3C]={ - category="so", + [0x189E1]={ + category="lo", direction="l", }, - [0x16B3D]={ - category="so", + [0x189E2]={ + category="lo", direction="l", }, - [0x16B3E]={ - category="so", + [0x189E3]={ + category="lo", direction="l", }, - [0x16B3F]={ - category="so", + [0x189E4]={ + category="lo", direction="l", }, - [0x16B40]={ - category="lm", + [0x189E5]={ + category="lo", direction="l", }, - [0x16B41]={ - category="lm", + [0x189E6]={ + category="lo", direction="l", }, - [0x16B42]={ - category="lm", + [0x189E7]={ + category="lo", direction="l", }, - [0x16B43]={ - category="lm", + [0x189E8]={ + category="lo", direction="l", }, - [0x16B44]={ - category="po", + [0x189E9]={ + category="lo", direction="l", }, - [0x16B45]={ - category="so", + [0x189EA]={ + category="lo", direction="l", }, - [0x16B50]={ - category="nd", + [0x189EB]={ + category="lo", direction="l", }, - [0x16B51]={ - category="nd", + [0x189EC]={ + category="lo", direction="l", }, - [0x16B52]={ - category="nd", + [0x189ED]={ + category="lo", direction="l", }, - [0x16B53]={ - category="nd", + [0x189EE]={ + category="lo", direction="l", }, - [0x16B54]={ - category="nd", + [0x189EF]={ + category="lo", direction="l", }, - [0x16B55]={ - category="nd", + [0x189F0]={ + category="lo", direction="l", }, - [0x16B56]={ - category="nd", + [0x189F1]={ + category="lo", direction="l", }, - [0x16B57]={ - category="nd", + [0x189F2]={ + category="lo", direction="l", }, - [0x16B58]={ - category="nd", + [0x189F3]={ + category="lo", direction="l", }, - [0x16B59]={ - category="nd", + [0x189F4]={ + category="lo", direction="l", }, - [0x16B5B]={ - category="no", + [0x189F5]={ + category="lo", direction="l", }, - [0x16B5C]={ - category="no", + [0x189F6]={ + category="lo", direction="l", }, - [0x16B5D]={ - category="no", + [0x189F7]={ + category="lo", direction="l", }, - [0x16B5E]={ - category="no", + [0x189F8]={ + category="lo", direction="l", }, - [0x16B5F]={ - category="no", + [0x189F9]={ + category="lo", direction="l", }, - [0x16B60]={ - category="no", + [0x189FA]={ + category="lo", direction="l", }, - [0x16B61]={ - category="no", + [0x189FB]={ + category="lo", direction="l", }, - [0x16B63]={ + [0x189FC]={ category="lo", direction="l", }, - [0x16B64]={ + [0x189FD]={ category="lo", direction="l", }, - [0x16B65]={ + [0x189FE]={ category="lo", direction="l", }, - [0x16B66]={ + [0x189FF]={ category="lo", direction="l", }, - [0x16B67]={ + [0x18A00]={ category="lo", direction="l", }, - [0x16B68]={ + [0x18A01]={ category="lo", direction="l", }, - [0x16B69]={ + [0x18A02]={ category="lo", direction="l", }, - [0x16B6A]={ + [0x18A03]={ category="lo", direction="l", }, - [0x16B6B]={ + [0x18A04]={ category="lo", direction="l", }, - [0x16B6C]={ + [0x18A05]={ category="lo", direction="l", }, - [0x16B6D]={ + [0x18A06]={ category="lo", direction="l", }, - [0x16B6E]={ + [0x18A07]={ category="lo", direction="l", }, - [0x16B6F]={ + [0x18A08]={ category="lo", direction="l", }, - [0x16B70]={ + [0x18A09]={ category="lo", direction="l", }, - [0x16B71]={ + [0x18A0A]={ category="lo", direction="l", }, - [0x16B72]={ + [0x18A0B]={ category="lo", direction="l", }, - [0x16B73]={ + [0x18A0C]={ category="lo", direction="l", }, - [0x16B74]={ + [0x18A0D]={ category="lo", direction="l", }, - [0x16B75]={ + [0x18A0E]={ category="lo", direction="l", }, - [0x16B76]={ + [0x18A0F]={ category="lo", direction="l", }, - [0x16B77]={ + [0x18A10]={ category="lo", direction="l", }, - [0x16B7D]={ + [0x18A11]={ category="lo", direction="l", }, - [0x16B7E]={ + [0x18A12]={ category="lo", direction="l", }, - [0x16B7F]={ + [0x18A13]={ category="lo", direction="l", }, - [0x16B80]={ + [0x18A14]={ category="lo", direction="l", }, - [0x16B81]={ + [0x18A15]={ category="lo", direction="l", }, - [0x16B82]={ + [0x18A16]={ category="lo", direction="l", }, - [0x16B83]={ + [0x18A17]={ category="lo", direction="l", }, - [0x16B84]={ + [0x18A18]={ category="lo", direction="l", }, - [0x16B85]={ + [0x18A19]={ category="lo", direction="l", }, - [0x16B86]={ + [0x18A1A]={ category="lo", direction="l", }, - [0x16B87]={ + [0x18A1B]={ category="lo", direction="l", }, - [0x16B88]={ + [0x18A1C]={ category="lo", direction="l", }, - [0x16B89]={ + [0x18A1D]={ category="lo", direction="l", }, - [0x16B8A]={ + [0x18A1E]={ category="lo", direction="l", }, - [0x16B8B]={ + [0x18A1F]={ category="lo", direction="l", }, - [0x16B8C]={ + [0x18A20]={ category="lo", direction="l", }, - [0x16B8D]={ + [0x18A21]={ category="lo", direction="l", }, - [0x16B8E]={ + [0x18A22]={ category="lo", direction="l", }, - [0x16B8F]={ + [0x18A23]={ category="lo", direction="l", }, - [0x16F00]={ + [0x18A24]={ category="lo", direction="l", }, - [0x16F01]={ + [0x18A25]={ category="lo", direction="l", }, - [0x16F02]={ + [0x18A26]={ category="lo", direction="l", }, - [0x16F03]={ + [0x18A27]={ category="lo", direction="l", }, - [0x16F04]={ + [0x18A28]={ category="lo", direction="l", }, - [0x16F05]={ + [0x18A29]={ category="lo", direction="l", }, - [0x16F06]={ + [0x18A2A]={ category="lo", direction="l", }, - [0x16F07]={ + [0x18A2B]={ category="lo", direction="l", }, - [0x16F08]={ + [0x18A2C]={ category="lo", direction="l", }, - [0x16F09]={ + [0x18A2D]={ category="lo", direction="l", }, - [0x16F0A]={ + [0x18A2E]={ category="lo", direction="l", }, - [0x16F0B]={ + [0x18A2F]={ category="lo", direction="l", }, - [0x16F0C]={ + [0x18A30]={ category="lo", direction="l", }, - [0x16F0D]={ + [0x18A31]={ category="lo", direction="l", }, - [0x16F0E]={ + [0x18A32]={ category="lo", direction="l", }, - [0x16F0F]={ + [0x18A33]={ category="lo", direction="l", }, - [0x16F10]={ + [0x18A34]={ category="lo", direction="l", }, - [0x16F11]={ + [0x18A35]={ category="lo", direction="l", }, - [0x16F12]={ + [0x18A36]={ category="lo", direction="l", }, - [0x16F13]={ + [0x18A37]={ category="lo", direction="l", }, - [0x16F14]={ + [0x18A38]={ category="lo", direction="l", }, - [0x16F15]={ + [0x18A39]={ category="lo", direction="l", }, - [0x16F16]={ + [0x18A3A]={ category="lo", direction="l", }, - [0x16F17]={ + [0x18A3B]={ category="lo", direction="l", }, - [0x16F18]={ + [0x18A3C]={ category="lo", direction="l", }, - [0x16F19]={ + [0x18A3D]={ category="lo", direction="l", }, - [0x16F1A]={ + [0x18A3E]={ category="lo", direction="l", }, - [0x16F1B]={ + [0x18A3F]={ category="lo", direction="l", }, - [0x16F1C]={ + [0x18A40]={ category="lo", direction="l", }, - [0x16F1D]={ + [0x18A41]={ category="lo", direction="l", }, - [0x16F1E]={ + [0x18A42]={ category="lo", direction="l", }, - [0x16F1F]={ + [0x18A43]={ category="lo", direction="l", }, - [0x16F20]={ + [0x18A44]={ category="lo", direction="l", }, - [0x16F21]={ + [0x18A45]={ category="lo", direction="l", }, - [0x16F22]={ + [0x18A46]={ category="lo", direction="l", }, - [0x16F23]={ + [0x18A47]={ category="lo", direction="l", }, - [0x16F24]={ + [0x18A48]={ category="lo", direction="l", }, - [0x16F25]={ + [0x18A49]={ category="lo", direction="l", }, - [0x16F26]={ + [0x18A4A]={ category="lo", direction="l", }, - [0x16F27]={ + [0x18A4B]={ category="lo", direction="l", }, - [0x16F28]={ + [0x18A4C]={ category="lo", direction="l", }, - [0x16F29]={ + [0x18A4D]={ category="lo", direction="l", }, - [0x16F2A]={ + [0x18A4E]={ category="lo", direction="l", }, - [0x16F2B]={ + [0x18A4F]={ category="lo", direction="l", }, - [0x16F2C]={ + [0x18A50]={ category="lo", direction="l", }, - [0x16F2D]={ + [0x18A51]={ category="lo", direction="l", }, - [0x16F2E]={ + [0x18A52]={ category="lo", direction="l", }, - [0x16F2F]={ + [0x18A53]={ category="lo", direction="l", }, - [0x16F30]={ + [0x18A54]={ category="lo", direction="l", }, - [0x16F31]={ + [0x18A55]={ category="lo", direction="l", }, - [0x16F32]={ + [0x18A56]={ category="lo", direction="l", }, - [0x16F33]={ + [0x18A57]={ category="lo", direction="l", }, - [0x16F34]={ + [0x18A58]={ category="lo", direction="l", }, - [0x16F35]={ + [0x18A59]={ category="lo", direction="l", }, - [0x16F36]={ + [0x18A5A]={ category="lo", direction="l", }, - [0x16F37]={ + [0x18A5B]={ category="lo", direction="l", }, - [0x16F38]={ + [0x18A5C]={ category="lo", direction="l", }, - [0x16F39]={ + [0x18A5D]={ category="lo", direction="l", }, - [0x16F3A]={ + [0x18A5E]={ category="lo", direction="l", }, - [0x16F3B]={ + [0x18A5F]={ category="lo", direction="l", }, - [0x16F3C]={ + [0x18A60]={ category="lo", direction="l", }, - [0x16F3D]={ + [0x18A61]={ category="lo", direction="l", }, - [0x16F3E]={ + [0x18A62]={ category="lo", direction="l", }, - [0x16F3F]={ + [0x18A63]={ category="lo", direction="l", }, - [0x16F40]={ + [0x18A64]={ category="lo", direction="l", }, - [0x16F41]={ + [0x18A65]={ category="lo", direction="l", }, - [0x16F42]={ + [0x18A66]={ category="lo", direction="l", }, - [0x16F43]={ + [0x18A67]={ category="lo", direction="l", }, - [0x16F44]={ + [0x18A68]={ category="lo", direction="l", }, - [0x16F50]={ + [0x18A69]={ category="lo", direction="l", }, - [0x16F51]={ - category="mc", + [0x18A6A]={ + category="lo", direction="l", }, - [0x16F52]={ - category="mc", + [0x18A6B]={ + category="lo", direction="l", }, - [0x16F53]={ - category="mc", + [0x18A6C]={ + category="lo", direction="l", }, - [0x16F54]={ - category="mc", + [0x18A6D]={ + category="lo", direction="l", }, - [0x16F55]={ - category="mc", + [0x18A6E]={ + category="lo", direction="l", }, - [0x16F56]={ - category="mc", + [0x18A6F]={ + category="lo", direction="l", }, - [0x16F57]={ - category="mc", + [0x18A70]={ + category="lo", direction="l", }, - [0x16F58]={ - category="mc", + [0x18A71]={ + category="lo", direction="l", }, - [0x16F59]={ - category="mc", + [0x18A72]={ + category="lo", direction="l", }, - [0x16F5A]={ - category="mc", + [0x18A73]={ + category="lo", direction="l", }, - [0x16F5B]={ - category="mc", + [0x18A74]={ + category="lo", direction="l", }, - [0x16F5C]={ - category="mc", + [0x18A75]={ + category="lo", direction="l", }, - [0x16F5D]={ - category="mc", + [0x18A76]={ + category="lo", direction="l", }, - [0x16F5E]={ - category="mc", + [0x18A77]={ + category="lo", direction="l", }, - [0x16F5F]={ - category="mc", + [0x18A78]={ + category="lo", direction="l", }, - [0x16F60]={ - category="mc", + [0x18A79]={ + category="lo", direction="l", }, - [0x16F61]={ - category="mc", + [0x18A7A]={ + category="lo", direction="l", }, - [0x16F62]={ - category="mc", + [0x18A7B]={ + category="lo", direction="l", }, - [0x16F63]={ - category="mc", + [0x18A7C]={ + category="lo", direction="l", }, - [0x16F64]={ - category="mc", + [0x18A7D]={ + category="lo", direction="l", }, - [0x16F65]={ - category="mc", + [0x18A7E]={ + category="lo", direction="l", }, - [0x16F66]={ - category="mc", + [0x18A7F]={ + category="lo", direction="l", }, - [0x16F67]={ - category="mc", + [0x18A80]={ + category="lo", direction="l", }, - [0x16F68]={ - category="mc", + [0x18A81]={ + category="lo", direction="l", }, - [0x16F69]={ - category="mc", + [0x18A82]={ + category="lo", direction="l", }, - [0x16F6A]={ - category="mc", + [0x18A83]={ + category="lo", direction="l", }, - [0x16F6B]={ - category="mc", + [0x18A84]={ + category="lo", direction="l", }, - [0x16F6C]={ - category="mc", + [0x18A85]={ + category="lo", direction="l", }, - [0x16F6D]={ - category="mc", + [0x18A86]={ + category="lo", direction="l", }, - [0x16F6E]={ - category="mc", + [0x18A87]={ + category="lo", direction="l", }, - [0x16F6F]={ - category="mc", + [0x18A88]={ + category="lo", direction="l", }, - [0x16F70]={ - category="mc", + [0x18A89]={ + category="lo", direction="l", }, - [0x16F71]={ - category="mc", + [0x18A8A]={ + category="lo", direction="l", }, - [0x16F72]={ - category="mc", + [0x18A8B]={ + category="lo", direction="l", }, - [0x16F73]={ - category="mc", + [0x18A8C]={ + category="lo", direction="l", }, - [0x16F74]={ - category="mc", + [0x18A8D]={ + category="lo", direction="l", }, - [0x16F75]={ - category="mc", + [0x18A8E]={ + category="lo", direction="l", }, - [0x16F76]={ - category="mc", + [0x18A8F]={ + category="lo", direction="l", }, - [0x16F77]={ - category="mc", + [0x18A90]={ + category="lo", direction="l", }, - [0x16F78]={ - category="mc", + [0x18A91]={ + category="lo", direction="l", }, - [0x16F79]={ - category="mc", + [0x18A92]={ + category="lo", direction="l", }, - [0x16F7A]={ - category="mc", + [0x18A93]={ + category="lo", direction="l", }, - [0x16F7B]={ - category="mc", + [0x18A94]={ + category="lo", direction="l", }, - [0x16F7C]={ - category="mc", + [0x18A95]={ + category="lo", direction="l", }, - [0x16F7D]={ - category="mc", + [0x18A96]={ + category="lo", direction="l", }, - [0x16F7E]={ - category="mc", + [0x18A97]={ + category="lo", direction="l", }, - [0x16F8F]={ - category="mn", - direction="nsm", - }, - [0x16F90]={ - category="mn", - direction="nsm", + [0x18A98]={ + category="lo", + direction="l", }, - [0x16F91]={ - category="mn", - direction="nsm", + [0x18A99]={ + category="lo", + direction="l", }, - [0x16F92]={ - category="mn", - direction="nsm", + [0x18A9A]={ + category="lo", + direction="l", }, - [0x16F93]={ - category="lm", + [0x18A9B]={ + category="lo", direction="l", }, - [0x16F94]={ - category="lm", + [0x18A9C]={ + category="lo", direction="l", }, - [0x16F95]={ - category="lm", + [0x18A9D]={ + category="lo", direction="l", }, - [0x16F96]={ - category="lm", + [0x18A9E]={ + category="lo", direction="l", }, - [0x16F97]={ - category="lm", + [0x18A9F]={ + category="lo", direction="l", }, - [0x16F98]={ - category="lm", + [0x18AA0]={ + category="lo", direction="l", }, - [0x16F99]={ - category="lm", + [0x18AA1]={ + category="lo", direction="l", }, - [0x16F9A]={ - category="lm", + [0x18AA2]={ + category="lo", direction="l", }, - [0x16F9B]={ - category="lm", + [0x18AA3]={ + category="lo", direction="l", }, - [0x16F9C]={ - category="lm", + [0x18AA4]={ + category="lo", direction="l", }, - [0x16F9D]={ - category="lm", + [0x18AA5]={ + category="lo", direction="l", }, - [0x16F9E]={ - category="lm", + [0x18AA6]={ + category="lo", direction="l", }, - [0x16F9F]={ - category="lm", + [0x18AA7]={ + category="lo", direction="l", }, - [0x16FE0]={ - category="lm", + [0x18AA8]={ + category="lo", direction="l", }, - [0x18800]={ + [0x18AA9]={ category="lo", direction="l", }, - [0x18801]={ + [0x18AAA]={ category="lo", direction="l", }, - [0x18802]={ + [0x18AAB]={ category="lo", direction="l", }, - [0x18803]={ + [0x18AAC]={ category="lo", direction="l", }, - [0x18804]={ + [0x18AAD]={ category="lo", direction="l", }, - [0x18805]={ + [0x18AAE]={ category="lo", direction="l", }, - [0x18806]={ + [0x18AAF]={ category="lo", direction="l", }, - [0x18807]={ + [0x18AB0]={ category="lo", direction="l", }, - [0x18808]={ + [0x18AB1]={ category="lo", direction="l", }, - [0x18809]={ + [0x18AB2]={ category="lo", direction="l", }, - [0x1880A]={ + [0x18AB3]={ category="lo", direction="l", }, - [0x1880B]={ + [0x18AB4]={ category="lo", direction="l", }, - [0x1880C]={ + [0x18AB5]={ category="lo", direction="l", }, - [0x1880D]={ + [0x18AB6]={ category="lo", direction="l", }, - [0x1880E]={ + [0x18AB7]={ category="lo", direction="l", }, - [0x1880F]={ + [0x18AB8]={ category="lo", direction="l", }, - [0x18810]={ + [0x18AB9]={ category="lo", direction="l", }, - [0x18811]={ + [0x18ABA]={ category="lo", direction="l", }, - [0x18812]={ + [0x18ABB]={ category="lo", direction="l", }, - [0x18813]={ + [0x18ABC]={ category="lo", direction="l", }, - [0x18814]={ + [0x18ABD]={ category="lo", direction="l", }, - [0x18815]={ + [0x18ABE]={ category="lo", direction="l", }, - [0x18816]={ + [0x18ABF]={ category="lo", direction="l", }, - [0x18817]={ + [0x18AC0]={ category="lo", direction="l", }, - [0x18818]={ + [0x18AC1]={ category="lo", direction="l", }, - [0x18819]={ + [0x18AC2]={ category="lo", direction="l", }, - [0x1881A]={ + [0x18AC3]={ category="lo", direction="l", }, - [0x1881B]={ + [0x18AC4]={ category="lo", direction="l", }, - [0x1881C]={ + [0x18AC5]={ category="lo", direction="l", }, - [0x1881D]={ + [0x18AC6]={ category="lo", direction="l", }, - [0x1881E]={ + [0x18AC7]={ category="lo", direction="l", }, - [0x1881F]={ + [0x18AC8]={ category="lo", direction="l", }, - [0x18820]={ + [0x18AC9]={ category="lo", direction="l", }, - [0x18821]={ + [0x18ACA]={ category="lo", direction="l", }, - [0x18822]={ + [0x18ACB]={ category="lo", direction="l", }, - [0x18823]={ + [0x18ACC]={ category="lo", direction="l", }, - [0x18824]={ + [0x18ACD]={ category="lo", direction="l", }, - [0x18825]={ + [0x18ACE]={ category="lo", direction="l", }, - [0x18826]={ + [0x18ACF]={ category="lo", direction="l", }, - [0x18827]={ + [0x18AD0]={ category="lo", direction="l", }, - [0x18828]={ + [0x18AD1]={ category="lo", direction="l", }, - [0x18829]={ + [0x18AD2]={ category="lo", direction="l", }, - [0x1882A]={ + [0x18AD3]={ category="lo", direction="l", }, - [0x1882B]={ + [0x18AD4]={ category="lo", direction="l", }, - [0x1882C]={ + [0x18AD5]={ category="lo", direction="l", }, - [0x1882D]={ + [0x18AD6]={ category="lo", direction="l", }, - [0x1882E]={ + [0x18AD7]={ category="lo", direction="l", }, - [0x1882F]={ + [0x18AD8]={ category="lo", direction="l", }, - [0x18830]={ + [0x18AD9]={ category="lo", direction="l", }, - [0x18831]={ + [0x18ADA]={ category="lo", direction="l", }, - [0x18832]={ + [0x18ADB]={ category="lo", direction="l", }, - [0x18833]={ + [0x18ADC]={ category="lo", direction="l", }, - [0x18834]={ + [0x18ADD]={ category="lo", direction="l", }, - [0x18835]={ + [0x18ADE]={ category="lo", direction="l", }, - [0x18836]={ + [0x18ADF]={ category="lo", direction="l", }, - [0x18837]={ + [0x18AE0]={ category="lo", direction="l", }, - [0x18838]={ + [0x18AE1]={ category="lo", direction="l", }, - [0x18839]={ + [0x18AE2]={ category="lo", direction="l", }, - [0x1883A]={ + [0x18AE3]={ category="lo", direction="l", }, - [0x1883B]={ + [0x18AE4]={ category="lo", direction="l", }, - [0x1883C]={ + [0x18AE5]={ category="lo", direction="l", }, - [0x1883D]={ + [0x18AE6]={ category="lo", direction="l", }, - [0x1883E]={ + [0x18AE7]={ category="lo", direction="l", }, - [0x1883F]={ + [0x18AE8]={ category="lo", direction="l", }, - [0x18840]={ + [0x18AE9]={ category="lo", direction="l", }, - [0x18841]={ + [0x18AEA]={ category="lo", direction="l", }, - [0x18842]={ + [0x18AEB]={ category="lo", direction="l", }, - [0x18843]={ + [0x18AEC]={ category="lo", direction="l", }, - [0x18844]={ + [0x18AED]={ category="lo", direction="l", }, - [0x18845]={ + [0x18AEE]={ category="lo", direction="l", }, - [0x18846]={ + [0x18AEF]={ category="lo", direction="l", }, - [0x18847]={ + [0x18AF0]={ category="lo", direction="l", }, - [0x18848]={ + [0x18AF1]={ category="lo", direction="l", }, - [0x18849]={ + [0x18AF2]={ category="lo", direction="l", }, - [0x1884A]={ + [0x1B000]={ category="lo", direction="l", }, - [0x1884B]={ + [0x1B001]={ category="lo", direction="l", }, - [0x1884C]={ + [0x1B002]={ category="lo", direction="l", }, - [0x1884D]={ + [0x1B003]={ category="lo", direction="l", }, - [0x1884E]={ + [0x1B004]={ category="lo", direction="l", }, - [0x1884F]={ + [0x1B005]={ category="lo", direction="l", }, - [0x18850]={ + [0x1B006]={ category="lo", direction="l", }, - [0x18851]={ + [0x1B007]={ category="lo", direction="l", }, - [0x18852]={ + [0x1B008]={ category="lo", direction="l", }, - [0x18853]={ + [0x1B009]={ category="lo", direction="l", }, - [0x18854]={ + [0x1B00A]={ category="lo", direction="l", }, - [0x18855]={ + [0x1B00B]={ category="lo", direction="l", }, - [0x18856]={ + [0x1B00C]={ category="lo", direction="l", }, - [0x18857]={ + [0x1B00D]={ category="lo", direction="l", }, - [0x18858]={ + [0x1B00E]={ category="lo", direction="l", }, - [0x18859]={ + [0x1B00F]={ category="lo", direction="l", }, - [0x1885A]={ + [0x1B010]={ category="lo", direction="l", }, - [0x1885B]={ + [0x1B011]={ category="lo", direction="l", }, - [0x1885C]={ + [0x1B012]={ category="lo", direction="l", }, - [0x1885D]={ + [0x1B013]={ category="lo", direction="l", }, - [0x1885E]={ + [0x1B014]={ category="lo", direction="l", }, - [0x1885F]={ + [0x1B015]={ category="lo", direction="l", }, - [0x18860]={ + [0x1B016]={ category="lo", direction="l", }, - [0x18861]={ + [0x1B017]={ category="lo", direction="l", }, - [0x18862]={ + [0x1B018]={ category="lo", direction="l", }, - [0x18863]={ + [0x1B019]={ category="lo", direction="l", }, - [0x18864]={ + [0x1B01A]={ category="lo", direction="l", }, - [0x18865]={ + [0x1B01B]={ category="lo", direction="l", }, - [0x18866]={ + [0x1B01C]={ category="lo", direction="l", }, - [0x18867]={ + [0x1B01D]={ category="lo", direction="l", }, - [0x18868]={ + [0x1B01E]={ category="lo", direction="l", }, - [0x18869]={ + [0x1B01F]={ category="lo", direction="l", }, - [0x1886A]={ + [0x1B020]={ category="lo", direction="l", }, - [0x1886B]={ + [0x1B021]={ category="lo", direction="l", }, - [0x1886C]={ + [0x1B022]={ category="lo", direction="l", }, - [0x1886D]={ + [0x1B023]={ category="lo", direction="l", }, - [0x1886E]={ + [0x1B024]={ category="lo", direction="l", }, - [0x1886F]={ + [0x1B025]={ category="lo", direction="l", }, - [0x18870]={ + [0x1B026]={ category="lo", direction="l", }, - [0x18871]={ + [0x1B027]={ category="lo", direction="l", }, - [0x18872]={ + [0x1B028]={ category="lo", direction="l", }, - [0x18873]={ + [0x1B029]={ category="lo", direction="l", }, - [0x18874]={ + [0x1B02A]={ category="lo", direction="l", }, - [0x18875]={ + [0x1B02B]={ category="lo", direction="l", }, - [0x18876]={ + [0x1B02C]={ category="lo", direction="l", }, - [0x18877]={ + [0x1B02D]={ category="lo", direction="l", }, - [0x18878]={ + [0x1B02E]={ category="lo", direction="l", }, - [0x18879]={ + [0x1B02F]={ category="lo", direction="l", }, - [0x1887A]={ + [0x1B030]={ category="lo", direction="l", }, - [0x1887B]={ + [0x1B031]={ category="lo", direction="l", }, - [0x1887C]={ + [0x1B032]={ category="lo", direction="l", }, - [0x1887D]={ + [0x1B033]={ category="lo", direction="l", }, - [0x1887E]={ + [0x1B034]={ category="lo", direction="l", }, - [0x1887F]={ + [0x1B035]={ category="lo", direction="l", }, - [0x18880]={ + [0x1B036]={ category="lo", direction="l", }, - [0x18881]={ + [0x1B037]={ category="lo", direction="l", }, - [0x18882]={ + [0x1B038]={ category="lo", direction="l", }, - [0x18883]={ + [0x1B039]={ category="lo", direction="l", }, - [0x18884]={ + [0x1B03A]={ category="lo", direction="l", }, - [0x18885]={ + [0x1B03B]={ category="lo", direction="l", }, - [0x18886]={ + [0x1B03C]={ category="lo", direction="l", }, - [0x18887]={ + [0x1B03D]={ category="lo", direction="l", }, - [0x18888]={ + [0x1B03E]={ category="lo", direction="l", }, - [0x18889]={ + [0x1B03F]={ category="lo", direction="l", }, - [0x1888A]={ + [0x1B040]={ category="lo", direction="l", }, - [0x1888B]={ + [0x1B041]={ category="lo", direction="l", }, - [0x1888C]={ + [0x1B042]={ category="lo", direction="l", }, - [0x1888D]={ + [0x1B043]={ category="lo", direction="l", }, - [0x1888E]={ + [0x1B044]={ category="lo", direction="l", }, - [0x1888F]={ + [0x1B045]={ category="lo", direction="l", }, - [0x18890]={ + [0x1B046]={ category="lo", direction="l", }, - [0x18891]={ + [0x1B047]={ category="lo", direction="l", }, - [0x18892]={ + [0x1B048]={ category="lo", direction="l", }, - [0x18893]={ + [0x1B049]={ category="lo", direction="l", }, - [0x18894]={ + [0x1B04A]={ category="lo", direction="l", }, - [0x18895]={ + [0x1B04B]={ category="lo", direction="l", }, - [0x18896]={ + [0x1B04C]={ category="lo", direction="l", }, - [0x18897]={ + [0x1B04D]={ category="lo", direction="l", }, - [0x18898]={ + [0x1B04E]={ category="lo", direction="l", }, - [0x18899]={ + [0x1B04F]={ category="lo", direction="l", }, - [0x1889A]={ + [0x1B050]={ category="lo", direction="l", }, - [0x1889B]={ + [0x1B051]={ category="lo", direction="l", }, - [0x1889C]={ + [0x1B052]={ category="lo", direction="l", }, - [0x1889D]={ + [0x1B053]={ category="lo", direction="l", }, - [0x1889E]={ + [0x1B054]={ category="lo", direction="l", }, - [0x1889F]={ + [0x1B055]={ category="lo", direction="l", }, - [0x188A0]={ + [0x1B056]={ category="lo", direction="l", }, - [0x188A1]={ + [0x1B057]={ category="lo", direction="l", }, - [0x188A2]={ + [0x1B058]={ category="lo", direction="l", }, - [0x188A3]={ + [0x1B059]={ category="lo", direction="l", }, - [0x188A4]={ + [0x1B05A]={ category="lo", direction="l", }, - [0x188A5]={ + [0x1B05B]={ category="lo", direction="l", }, - [0x188A6]={ + [0x1B05C]={ category="lo", direction="l", }, - [0x188A7]={ + [0x1B05D]={ category="lo", direction="l", }, - [0x188A8]={ + [0x1B05E]={ category="lo", direction="l", }, - [0x188A9]={ + [0x1B05F]={ category="lo", direction="l", }, - [0x188AA]={ + [0x1B060]={ category="lo", direction="l", }, - [0x188AB]={ + [0x1B061]={ category="lo", direction="l", }, - [0x188AC]={ + [0x1B062]={ category="lo", direction="l", }, - [0x188AD]={ + [0x1B063]={ category="lo", direction="l", }, - [0x188AE]={ + [0x1B064]={ category="lo", direction="l", }, - [0x188AF]={ + [0x1B065]={ category="lo", direction="l", }, - [0x188B0]={ + [0x1B066]={ category="lo", direction="l", }, - [0x188B1]={ + [0x1B067]={ category="lo", direction="l", }, - [0x188B2]={ + [0x1B068]={ category="lo", direction="l", }, - [0x188B3]={ + [0x1B069]={ category="lo", direction="l", }, - [0x188B4]={ + [0x1B06A]={ category="lo", direction="l", }, - [0x188B5]={ + [0x1B06B]={ category="lo", direction="l", }, - [0x188B6]={ + [0x1B06C]={ category="lo", direction="l", }, - [0x188B7]={ + [0x1B06D]={ category="lo", direction="l", }, - [0x188B8]={ + [0x1B06E]={ category="lo", direction="l", }, - [0x188B9]={ + [0x1B06F]={ category="lo", direction="l", }, - [0x188BA]={ + [0x1B070]={ category="lo", direction="l", }, - [0x188BB]={ + [0x1B071]={ category="lo", direction="l", }, - [0x188BC]={ + [0x1B072]={ category="lo", direction="l", }, - [0x188BD]={ + [0x1B073]={ category="lo", direction="l", }, - [0x188BE]={ + [0x1B074]={ category="lo", direction="l", }, - [0x188BF]={ + [0x1B075]={ category="lo", direction="l", }, - [0x188C0]={ + [0x1B076]={ category="lo", direction="l", }, - [0x188C1]={ + [0x1B077]={ category="lo", direction="l", }, - [0x188C2]={ + [0x1B078]={ category="lo", direction="l", }, - [0x188C3]={ + [0x1B079]={ category="lo", direction="l", }, - [0x188C4]={ + [0x1B07A]={ category="lo", direction="l", }, - [0x188C5]={ + [0x1B07B]={ category="lo", direction="l", }, - [0x188C6]={ + [0x1B07C]={ category="lo", direction="l", }, - [0x188C7]={ + [0x1B07D]={ category="lo", direction="l", }, - [0x188C8]={ + [0x1B07E]={ category="lo", direction="l", }, - [0x188C9]={ + [0x1B07F]={ category="lo", direction="l", }, - [0x188CA]={ + [0x1B080]={ category="lo", direction="l", }, - [0x188CB]={ + [0x1B081]={ category="lo", direction="l", }, - [0x188CC]={ + [0x1B082]={ category="lo", direction="l", }, - [0x188CD]={ + [0x1B083]={ category="lo", direction="l", }, - [0x188CE]={ + [0x1B084]={ category="lo", direction="l", }, - [0x188CF]={ + [0x1B085]={ category="lo", direction="l", }, - [0x188D0]={ + [0x1B086]={ category="lo", direction="l", }, - [0x188D1]={ + [0x1B087]={ category="lo", direction="l", }, - [0x188D2]={ + [0x1B088]={ category="lo", direction="l", }, - [0x188D3]={ + [0x1B089]={ category="lo", direction="l", }, - [0x188D4]={ + [0x1B08A]={ category="lo", direction="l", }, - [0x188D5]={ + [0x1B08B]={ category="lo", direction="l", }, - [0x188D6]={ + [0x1B08C]={ category="lo", direction="l", }, - [0x188D7]={ + [0x1B08D]={ category="lo", direction="l", }, - [0x188D8]={ + [0x1B08E]={ category="lo", direction="l", }, - [0x188D9]={ + [0x1B08F]={ category="lo", direction="l", }, - [0x188DA]={ + [0x1B090]={ category="lo", direction="l", }, - [0x188DB]={ + [0x1B091]={ category="lo", direction="l", }, - [0x188DC]={ + [0x1B092]={ category="lo", direction="l", }, - [0x188DD]={ + [0x1B093]={ category="lo", direction="l", }, - [0x188DE]={ + [0x1B094]={ category="lo", direction="l", }, - [0x188DF]={ + [0x1B095]={ category="lo", direction="l", }, - [0x188E0]={ + [0x1B096]={ category="lo", direction="l", }, - [0x188E1]={ + [0x1B097]={ category="lo", direction="l", }, - [0x188E2]={ + [0x1B098]={ category="lo", direction="l", }, - [0x188E3]={ + [0x1B099]={ category="lo", direction="l", }, - [0x188E4]={ + [0x1B09A]={ category="lo", direction="l", }, - [0x188E5]={ + [0x1B09B]={ category="lo", direction="l", }, - [0x188E6]={ + [0x1B09C]={ category="lo", direction="l", }, - [0x188E7]={ + [0x1B09D]={ category="lo", direction="l", }, - [0x188E8]={ + [0x1B09E]={ category="lo", direction="l", }, - [0x188E9]={ + [0x1B09F]={ category="lo", direction="l", }, - [0x188EA]={ + [0x1B0A0]={ category="lo", direction="l", }, - [0x188EB]={ + [0x1B0A1]={ category="lo", direction="l", }, - [0x188EC]={ + [0x1B0A2]={ category="lo", direction="l", }, - [0x188ED]={ + [0x1B0A3]={ category="lo", direction="l", }, - [0x188EE]={ + [0x1B0A4]={ category="lo", direction="l", }, - [0x188EF]={ + [0x1B0A5]={ category="lo", direction="l", }, - [0x188F0]={ + [0x1B0A6]={ category="lo", direction="l", }, - [0x188F1]={ + [0x1B0A7]={ category="lo", direction="l", }, - [0x188F2]={ + [0x1B0A8]={ category="lo", direction="l", }, - [0x188F3]={ + [0x1B0A9]={ category="lo", direction="l", }, - [0x188F4]={ + [0x1B0AA]={ category="lo", direction="l", }, - [0x188F5]={ + [0x1B0AB]={ category="lo", direction="l", }, - [0x188F6]={ + [0x1B0AC]={ category="lo", direction="l", }, - [0x188F7]={ + [0x1B0AD]={ category="lo", direction="l", }, - [0x188F8]={ + [0x1B0AE]={ category="lo", direction="l", }, - [0x188F9]={ + [0x1B0AF]={ category="lo", direction="l", }, - [0x188FA]={ + [0x1B0B0]={ category="lo", direction="l", }, - [0x188FB]={ + [0x1B0B1]={ category="lo", direction="l", }, - [0x188FC]={ + [0x1B0B2]={ category="lo", direction="l", }, - [0x188FD]={ + [0x1B0B3]={ category="lo", direction="l", }, - [0x188FE]={ + [0x1B0B4]={ category="lo", direction="l", }, - [0x188FF]={ + [0x1B0B5]={ category="lo", direction="l", }, - [0x18900]={ + [0x1B0B6]={ category="lo", direction="l", }, - [0x18901]={ + [0x1B0B7]={ category="lo", direction="l", }, - [0x18902]={ + [0x1B0B8]={ category="lo", direction="l", }, - [0x18903]={ + [0x1B0B9]={ category="lo", direction="l", }, - [0x18904]={ + [0x1B0BA]={ category="lo", direction="l", }, - [0x18905]={ + [0x1B0BB]={ category="lo", direction="l", }, - [0x18906]={ + [0x1B0BC]={ category="lo", direction="l", }, - [0x18907]={ + [0x1B0BD]={ category="lo", direction="l", }, - [0x18908]={ + [0x1B0BE]={ category="lo", direction="l", }, - [0x18909]={ + [0x1B0BF]={ category="lo", direction="l", }, - [0x1890A]={ + [0x1B0C0]={ category="lo", direction="l", }, - [0x1890B]={ + [0x1B0C1]={ category="lo", direction="l", }, - [0x1890C]={ + [0x1B0C2]={ category="lo", direction="l", }, - [0x1890D]={ + [0x1B0C3]={ category="lo", direction="l", }, - [0x1890E]={ + [0x1B0C4]={ category="lo", direction="l", }, - [0x1890F]={ + [0x1B0C5]={ category="lo", direction="l", }, - [0x18910]={ + [0x1B0C6]={ category="lo", direction="l", }, - [0x18911]={ + [0x1B0C7]={ category="lo", direction="l", }, - [0x18912]={ + [0x1B0C8]={ category="lo", direction="l", }, - [0x18913]={ + [0x1B0C9]={ category="lo", direction="l", }, - [0x18914]={ + [0x1B0CA]={ category="lo", direction="l", }, - [0x18915]={ + [0x1B0CB]={ category="lo", direction="l", }, - [0x18916]={ + [0x1B0CC]={ category="lo", direction="l", }, - [0x18917]={ + [0x1B0CD]={ category="lo", direction="l", }, - [0x18918]={ + [0x1B0CE]={ category="lo", direction="l", }, - [0x18919]={ + [0x1B0CF]={ category="lo", direction="l", }, - [0x1891A]={ + [0x1B0D0]={ category="lo", direction="l", }, - [0x1891B]={ + [0x1B0D1]={ category="lo", direction="l", }, - [0x1891C]={ + [0x1B0D2]={ category="lo", direction="l", }, - [0x1891D]={ + [0x1B0D3]={ category="lo", direction="l", }, - [0x1891E]={ + [0x1B0D4]={ category="lo", direction="l", }, - [0x1891F]={ + [0x1B0D5]={ category="lo", direction="l", }, - [0x18920]={ + [0x1B0D6]={ category="lo", direction="l", }, - [0x18921]={ + [0x1B0D7]={ category="lo", direction="l", }, - [0x18922]={ + [0x1B0D8]={ category="lo", direction="l", }, - [0x18923]={ + [0x1B0D9]={ category="lo", direction="l", }, - [0x18924]={ + [0x1B0DA]={ category="lo", direction="l", }, - [0x18925]={ + [0x1B0DB]={ category="lo", direction="l", }, - [0x18926]={ + [0x1B0DC]={ category="lo", direction="l", }, - [0x18927]={ + [0x1B0DD]={ category="lo", direction="l", }, - [0x18928]={ + [0x1B0DE]={ category="lo", direction="l", }, - [0x18929]={ + [0x1B0DF]={ category="lo", direction="l", }, - [0x1892A]={ + [0x1B0E0]={ category="lo", direction="l", }, - [0x1892B]={ + [0x1B0E1]={ category="lo", direction="l", }, - [0x1892C]={ + [0x1B0E2]={ category="lo", direction="l", }, - [0x1892D]={ + [0x1B0E3]={ category="lo", direction="l", }, - [0x1892E]={ + [0x1B0E4]={ category="lo", direction="l", }, - [0x1892F]={ + [0x1B0E5]={ category="lo", direction="l", }, - [0x18930]={ + [0x1B0E6]={ category="lo", direction="l", }, - [0x18931]={ + [0x1B0E7]={ category="lo", direction="l", }, - [0x18932]={ + [0x1B0E8]={ category="lo", direction="l", }, - [0x18933]={ + [0x1B0E9]={ category="lo", direction="l", }, - [0x18934]={ + [0x1B0EA]={ category="lo", direction="l", }, - [0x18935]={ + [0x1B0EB]={ category="lo", direction="l", }, - [0x18936]={ + [0x1B0EC]={ category="lo", direction="l", }, - [0x18937]={ + [0x1B0ED]={ category="lo", direction="l", }, - [0x18938]={ + [0x1B0EE]={ category="lo", direction="l", }, - [0x18939]={ + [0x1B0EF]={ category="lo", direction="l", }, - [0x1893A]={ + [0x1B0F0]={ category="lo", direction="l", }, - [0x1893B]={ + [0x1B0F1]={ category="lo", direction="l", }, - [0x1893C]={ + [0x1B0F2]={ category="lo", direction="l", }, - [0x1893D]={ + [0x1B0F3]={ category="lo", direction="l", }, - [0x1893E]={ + [0x1B0F4]={ category="lo", direction="l", }, - [0x1893F]={ + [0x1B0F5]={ category="lo", direction="l", }, - [0x18940]={ + [0x1B0F6]={ category="lo", direction="l", }, - [0x18941]={ + [0x1B0F7]={ category="lo", direction="l", }, - [0x18942]={ + [0x1B0F8]={ category="lo", direction="l", }, - [0x18943]={ + [0x1B0F9]={ category="lo", direction="l", }, - [0x18944]={ + [0x1B0FA]={ category="lo", direction="l", }, - [0x18945]={ + [0x1B0FB]={ category="lo", direction="l", }, - [0x18946]={ + [0x1B0FC]={ category="lo", direction="l", }, - [0x18947]={ + [0x1B0FD]={ category="lo", direction="l", }, - [0x18948]={ + [0x1B0FE]={ category="lo", direction="l", }, - [0x18949]={ + [0x1B0FF]={ category="lo", direction="l", }, - [0x1894A]={ + [0x1B100]={ category="lo", direction="l", }, - [0x1894B]={ + [0x1B101]={ category="lo", direction="l", }, - [0x1894C]={ + [0x1B102]={ category="lo", direction="l", }, - [0x1894D]={ + [0x1B103]={ category="lo", direction="l", }, - [0x1894E]={ + [0x1B104]={ category="lo", direction="l", }, - [0x1894F]={ + [0x1B105]={ category="lo", direction="l", }, - [0x18950]={ + [0x1B106]={ category="lo", direction="l", }, - [0x18951]={ + [0x1B107]={ category="lo", direction="l", }, - [0x18952]={ + [0x1B108]={ category="lo", direction="l", }, - [0x18953]={ + [0x1B109]={ category="lo", direction="l", }, - [0x18954]={ + [0x1B10A]={ category="lo", direction="l", }, - [0x18955]={ + [0x1B10B]={ category="lo", direction="l", }, - [0x18956]={ + [0x1B10C]={ category="lo", direction="l", }, - [0x18957]={ + [0x1B10D]={ category="lo", direction="l", }, - [0x18958]={ + [0x1B10E]={ category="lo", direction="l", }, - [0x18959]={ + [0x1B10F]={ category="lo", direction="l", }, - [0x1895A]={ + [0x1B110]={ category="lo", direction="l", }, - [0x1895B]={ + [0x1B111]={ category="lo", direction="l", }, - [0x1895C]={ + [0x1B112]={ category="lo", direction="l", }, - [0x1895D]={ + [0x1B113]={ category="lo", direction="l", }, - [0x1895E]={ + [0x1B114]={ category="lo", direction="l", }, - [0x1895F]={ + [0x1B115]={ category="lo", direction="l", }, - [0x18960]={ + [0x1B116]={ category="lo", direction="l", }, - [0x18961]={ + [0x1B117]={ category="lo", direction="l", }, - [0x18962]={ + [0x1B118]={ category="lo", direction="l", }, - [0x18963]={ + [0x1B119]={ category="lo", direction="l", }, - [0x18964]={ + [0x1B11A]={ category="lo", direction="l", }, - [0x18965]={ + [0x1B11B]={ category="lo", direction="l", }, - [0x18966]={ + [0x1B11C]={ category="lo", direction="l", }, - [0x18967]={ + [0x1B11D]={ category="lo", direction="l", }, - [0x18968]={ + [0x1B11E]={ category="lo", direction="l", }, - [0x18969]={ + [0x1B170]={ category="lo", direction="l", }, - [0x1896A]={ + [0x1B171]={ category="lo", direction="l", }, - [0x1896B]={ + [0x1B172]={ category="lo", direction="l", }, - [0x1896C]={ + [0x1B173]={ category="lo", direction="l", }, - [0x1896D]={ + [0x1B174]={ category="lo", direction="l", }, - [0x1896E]={ + [0x1B175]={ category="lo", direction="l", }, - [0x1896F]={ + [0x1B176]={ category="lo", direction="l", }, - [0x18970]={ + [0x1B177]={ category="lo", direction="l", }, - [0x18971]={ + [0x1B178]={ category="lo", direction="l", }, - [0x18972]={ + [0x1B179]={ category="lo", direction="l", }, - [0x18973]={ + [0x1B17A]={ category="lo", direction="l", }, - [0x18974]={ + [0x1B17B]={ category="lo", direction="l", }, - [0x18975]={ + [0x1B17C]={ category="lo", direction="l", }, - [0x18976]={ + [0x1B17D]={ category="lo", direction="l", }, - [0x18977]={ + [0x1B17E]={ category="lo", direction="l", }, - [0x18978]={ + [0x1B17F]={ category="lo", direction="l", }, - [0x18979]={ + [0x1B180]={ category="lo", direction="l", }, - [0x1897A]={ + [0x1B181]={ category="lo", direction="l", }, - [0x1897B]={ + [0x1B182]={ category="lo", direction="l", }, - [0x1897C]={ + [0x1B183]={ category="lo", direction="l", }, - [0x1897D]={ + [0x1B184]={ category="lo", direction="l", }, - [0x1897E]={ + [0x1B185]={ category="lo", direction="l", }, - [0x1897F]={ + [0x1B186]={ category="lo", direction="l", }, - [0x18980]={ + [0x1B187]={ category="lo", direction="l", }, - [0x18981]={ + [0x1B188]={ category="lo", direction="l", }, - [0x18982]={ + [0x1B189]={ category="lo", direction="l", }, - [0x18983]={ + [0x1B18A]={ category="lo", direction="l", }, - [0x18984]={ + [0x1B18B]={ category="lo", direction="l", }, - [0x18985]={ + [0x1B18C]={ category="lo", direction="l", }, - [0x18986]={ + [0x1B18D]={ category="lo", direction="l", }, - [0x18987]={ + [0x1B18E]={ category="lo", direction="l", }, - [0x18988]={ + [0x1B18F]={ category="lo", direction="l", }, - [0x18989]={ + [0x1B190]={ category="lo", direction="l", }, - [0x1898A]={ + [0x1B191]={ category="lo", direction="l", }, - [0x1898B]={ + [0x1B192]={ category="lo", direction="l", }, - [0x1898C]={ + [0x1B193]={ category="lo", direction="l", }, - [0x1898D]={ + [0x1B194]={ category="lo", direction="l", }, - [0x1898E]={ + [0x1B195]={ category="lo", direction="l", }, - [0x1898F]={ + [0x1B196]={ category="lo", direction="l", }, - [0x18990]={ + [0x1B197]={ category="lo", direction="l", }, - [0x18991]={ + [0x1B198]={ category="lo", direction="l", }, - [0x18992]={ + [0x1B199]={ category="lo", direction="l", }, - [0x18993]={ + [0x1B19A]={ category="lo", direction="l", }, - [0x18994]={ + [0x1B19B]={ category="lo", direction="l", }, - [0x18995]={ + [0x1B19C]={ category="lo", direction="l", }, - [0x18996]={ + [0x1B19D]={ category="lo", direction="l", }, - [0x18997]={ + [0x1B19E]={ category="lo", direction="l", }, - [0x18998]={ + [0x1B19F]={ category="lo", direction="l", }, - [0x18999]={ + [0x1B1A0]={ category="lo", direction="l", }, - [0x1899A]={ + [0x1B1A1]={ category="lo", direction="l", }, - [0x1899B]={ + [0x1B1A2]={ category="lo", direction="l", }, - [0x1899C]={ + [0x1B1A3]={ category="lo", direction="l", }, - [0x1899D]={ + [0x1B1A4]={ category="lo", direction="l", }, - [0x1899E]={ + [0x1B1A5]={ category="lo", direction="l", }, - [0x1899F]={ + [0x1B1A6]={ category="lo", direction="l", }, - [0x189A0]={ + [0x1B1A7]={ category="lo", direction="l", }, - [0x189A1]={ + [0x1B1A8]={ category="lo", direction="l", }, - [0x189A2]={ + [0x1B1A9]={ category="lo", direction="l", }, - [0x189A3]={ + [0x1B1AA]={ category="lo", direction="l", }, - [0x189A4]={ + [0x1B1AB]={ category="lo", direction="l", }, - [0x189A5]={ + [0x1B1AC]={ category="lo", direction="l", }, - [0x189A6]={ + [0x1B1AD]={ category="lo", direction="l", }, - [0x189A7]={ + [0x1B1AE]={ category="lo", direction="l", }, - [0x189A8]={ + [0x1B1AF]={ category="lo", direction="l", }, - [0x189A9]={ + [0x1B1B0]={ category="lo", direction="l", }, - [0x189AA]={ + [0x1B1B1]={ category="lo", direction="l", }, - [0x189AB]={ + [0x1B1B2]={ category="lo", direction="l", }, - [0x189AC]={ + [0x1B1B3]={ category="lo", direction="l", }, - [0x189AD]={ + [0x1B1B4]={ category="lo", direction="l", }, - [0x189AE]={ + [0x1B1B5]={ category="lo", direction="l", }, - [0x189AF]={ + [0x1B1B6]={ category="lo", direction="l", }, - [0x189B0]={ + [0x1B1B7]={ category="lo", direction="l", }, - [0x189B1]={ + [0x1B1B8]={ category="lo", direction="l", }, - [0x189B2]={ + [0x1B1B9]={ category="lo", direction="l", }, - [0x189B3]={ + [0x1B1BA]={ category="lo", direction="l", }, - [0x189B4]={ + [0x1B1BB]={ category="lo", direction="l", }, - [0x189B5]={ + [0x1B1BC]={ category="lo", direction="l", }, - [0x189B6]={ + [0x1B1BD]={ category="lo", direction="l", }, - [0x189B7]={ + [0x1B1BE]={ category="lo", direction="l", }, - [0x189B8]={ + [0x1B1BF]={ category="lo", direction="l", }, - [0x189B9]={ + [0x1B1C0]={ category="lo", direction="l", }, - [0x189BA]={ + [0x1B1C1]={ category="lo", direction="l", }, - [0x189BB]={ + [0x1B1C2]={ category="lo", direction="l", }, - [0x189BC]={ + [0x1B1C3]={ category="lo", direction="l", }, - [0x189BD]={ + [0x1B1C4]={ category="lo", direction="l", }, - [0x189BE]={ + [0x1B1C5]={ category="lo", direction="l", }, - [0x189BF]={ + [0x1B1C6]={ category="lo", direction="l", }, - [0x189C0]={ + [0x1B1C7]={ category="lo", direction="l", }, - [0x189C1]={ + [0x1B1C8]={ category="lo", direction="l", }, - [0x189C2]={ + [0x1B1C9]={ category="lo", direction="l", }, - [0x189C3]={ + [0x1B1CA]={ category="lo", direction="l", }, - [0x189C4]={ + [0x1B1CB]={ category="lo", direction="l", }, - [0x189C5]={ + [0x1B1CC]={ category="lo", direction="l", }, - [0x189C6]={ + [0x1B1CD]={ category="lo", direction="l", }, - [0x189C7]={ + [0x1B1CE]={ category="lo", direction="l", }, - [0x189C8]={ + [0x1B1CF]={ category="lo", direction="l", }, - [0x189C9]={ + [0x1B1D0]={ category="lo", direction="l", }, - [0x189CA]={ + [0x1B1D1]={ category="lo", direction="l", }, - [0x189CB]={ + [0x1B1D2]={ category="lo", direction="l", }, - [0x189CC]={ + [0x1B1D3]={ category="lo", direction="l", }, - [0x189CD]={ + [0x1B1D4]={ category="lo", direction="l", }, - [0x189CE]={ + [0x1B1D5]={ category="lo", direction="l", }, - [0x189CF]={ + [0x1B1D6]={ category="lo", direction="l", }, - [0x189D0]={ + [0x1B1D7]={ category="lo", direction="l", }, - [0x189D1]={ + [0x1B1D8]={ category="lo", direction="l", }, - [0x189D2]={ + [0x1B1D9]={ category="lo", direction="l", }, - [0x189D3]={ + [0x1B1DA]={ category="lo", direction="l", }, - [0x189D4]={ + [0x1B1DB]={ category="lo", direction="l", }, - [0x189D5]={ + [0x1B1DC]={ category="lo", direction="l", }, - [0x189D6]={ + [0x1B1DD]={ category="lo", direction="l", }, - [0x189D7]={ + [0x1B1DE]={ category="lo", direction="l", }, - [0x189D8]={ + [0x1B1DF]={ category="lo", direction="l", }, - [0x189D9]={ + [0x1B1E0]={ category="lo", direction="l", }, - [0x189DA]={ + [0x1B1E1]={ category="lo", direction="l", }, - [0x189DB]={ + [0x1B1E2]={ category="lo", direction="l", }, - [0x189DC]={ + [0x1B1E3]={ category="lo", direction="l", }, - [0x189DD]={ + [0x1B1E4]={ category="lo", direction="l", }, - [0x189DE]={ + [0x1B1E5]={ category="lo", direction="l", }, - [0x189DF]={ + [0x1B1E6]={ category="lo", direction="l", }, - [0x189E0]={ + [0x1B1E7]={ category="lo", direction="l", }, - [0x189E1]={ + [0x1B1E8]={ category="lo", direction="l", }, - [0x189E2]={ + [0x1B1E9]={ category="lo", direction="l", }, - [0x189E3]={ + [0x1B1EA]={ category="lo", direction="l", }, - [0x189E4]={ + [0x1B1EB]={ category="lo", direction="l", }, - [0x189E5]={ + [0x1B1EC]={ category="lo", direction="l", }, - [0x189E6]={ + [0x1B1ED]={ category="lo", direction="l", }, - [0x189E7]={ + [0x1B1EE]={ category="lo", direction="l", }, - [0x189E8]={ + [0x1B1EF]={ category="lo", direction="l", }, - [0x189E9]={ + [0x1B1F0]={ category="lo", direction="l", }, - [0x189EA]={ + [0x1B1F1]={ category="lo", direction="l", }, - [0x189EB]={ + [0x1B1F2]={ category="lo", direction="l", }, - [0x189EC]={ + [0x1B1F3]={ category="lo", direction="l", }, - [0x189ED]={ + [0x1B1F4]={ category="lo", direction="l", }, - [0x189EE]={ + [0x1B1F5]={ category="lo", direction="l", }, - [0x189EF]={ + [0x1B1F6]={ category="lo", direction="l", }, - [0x189F0]={ + [0x1B1F7]={ category="lo", direction="l", }, - [0x189F1]={ + [0x1B1F8]={ category="lo", direction="l", }, - [0x189F2]={ + [0x1B1F9]={ category="lo", direction="l", }, - [0x189F3]={ + [0x1B1FA]={ category="lo", direction="l", }, - [0x189F4]={ + [0x1B1FB]={ category="lo", direction="l", }, - [0x189F5]={ + [0x1B1FC]={ category="lo", direction="l", }, - [0x189F6]={ + [0x1B1FD]={ category="lo", direction="l", }, - [0x189F7]={ + [0x1B1FE]={ category="lo", direction="l", }, - [0x189F8]={ + [0x1B1FF]={ category="lo", direction="l", }, - [0x189F9]={ + [0x1B200]={ category="lo", direction="l", }, - [0x189FA]={ + [0x1B201]={ category="lo", direction="l", }, - [0x189FB]={ + [0x1B202]={ category="lo", direction="l", }, - [0x189FC]={ + [0x1B203]={ category="lo", direction="l", }, - [0x189FD]={ + [0x1B204]={ category="lo", direction="l", }, - [0x189FE]={ + [0x1B205]={ category="lo", direction="l", }, - [0x189FF]={ + [0x1B206]={ category="lo", direction="l", }, - [0x18A00]={ + [0x1B207]={ category="lo", direction="l", }, - [0x18A01]={ + [0x1B208]={ category="lo", direction="l", }, - [0x18A02]={ + [0x1B209]={ category="lo", direction="l", }, - [0x18A03]={ + [0x1B20A]={ category="lo", direction="l", }, - [0x18A04]={ + [0x1B20B]={ category="lo", direction="l", }, - [0x18A05]={ + [0x1B20C]={ category="lo", direction="l", }, - [0x18A06]={ + [0x1B20D]={ category="lo", direction="l", }, - [0x18A07]={ + [0x1B20E]={ category="lo", direction="l", }, - [0x18A08]={ + [0x1B20F]={ category="lo", direction="l", }, - [0x18A09]={ + [0x1B210]={ category="lo", direction="l", }, - [0x18A0A]={ + [0x1B211]={ category="lo", direction="l", }, - [0x18A0B]={ + [0x1B212]={ category="lo", direction="l", }, - [0x18A0C]={ + [0x1B213]={ category="lo", direction="l", }, - [0x18A0D]={ + [0x1B214]={ category="lo", direction="l", }, - [0x18A0E]={ + [0x1B215]={ category="lo", direction="l", }, - [0x18A0F]={ + [0x1B216]={ category="lo", direction="l", }, - [0x18A10]={ + [0x1B217]={ category="lo", direction="l", }, - [0x18A11]={ + [0x1B218]={ category="lo", direction="l", }, - [0x18A12]={ + [0x1B219]={ category="lo", direction="l", }, - [0x18A13]={ + [0x1B21A]={ category="lo", direction="l", }, - [0x18A14]={ + [0x1B21B]={ category="lo", direction="l", }, - [0x18A15]={ + [0x1B21C]={ category="lo", direction="l", }, - [0x18A16]={ + [0x1B21D]={ category="lo", direction="l", }, - [0x18A17]={ + [0x1B21E]={ category="lo", direction="l", }, - [0x18A18]={ + [0x1B21F]={ category="lo", direction="l", }, - [0x18A19]={ + [0x1B220]={ category="lo", direction="l", }, - [0x18A1A]={ + [0x1B221]={ category="lo", direction="l", }, - [0x18A1B]={ + [0x1B222]={ category="lo", direction="l", }, - [0x18A1C]={ + [0x1B223]={ category="lo", direction="l", }, - [0x18A1D]={ + [0x1B224]={ category="lo", direction="l", }, - [0x18A1E]={ + [0x1B225]={ category="lo", direction="l", }, - [0x18A1F]={ + [0x1B226]={ category="lo", direction="l", }, - [0x18A20]={ + [0x1B227]={ category="lo", direction="l", }, - [0x18A21]={ + [0x1B228]={ category="lo", direction="l", }, - [0x18A22]={ + [0x1B229]={ category="lo", direction="l", }, - [0x18A23]={ + [0x1B22A]={ category="lo", direction="l", }, - [0x18A24]={ + [0x1B22B]={ category="lo", direction="l", }, - [0x18A25]={ + [0x1B22C]={ category="lo", direction="l", }, - [0x18A26]={ + [0x1B22D]={ category="lo", direction="l", }, - [0x18A27]={ + [0x1B22E]={ category="lo", direction="l", }, - [0x18A28]={ + [0x1B22F]={ category="lo", direction="l", }, - [0x18A29]={ + [0x1B230]={ category="lo", direction="l", }, - [0x18A2A]={ + [0x1B231]={ category="lo", direction="l", }, - [0x18A2B]={ + [0x1B232]={ category="lo", direction="l", }, - [0x18A2C]={ + [0x1B233]={ category="lo", direction="l", }, - [0x18A2D]={ + [0x1B234]={ category="lo", direction="l", }, - [0x18A2E]={ + [0x1B235]={ category="lo", direction="l", }, - [0x18A2F]={ + [0x1B236]={ category="lo", direction="l", }, - [0x18A30]={ + [0x1B237]={ category="lo", direction="l", }, - [0x18A31]={ + [0x1B238]={ category="lo", direction="l", }, - [0x18A32]={ + [0x1B239]={ category="lo", direction="l", }, - [0x18A33]={ + [0x1B23A]={ category="lo", direction="l", }, - [0x18A34]={ + [0x1B23B]={ category="lo", direction="l", }, - [0x18A35]={ + [0x1B23C]={ category="lo", direction="l", }, - [0x18A36]={ + [0x1B23D]={ category="lo", direction="l", }, - [0x18A37]={ + [0x1B23E]={ category="lo", direction="l", }, - [0x18A38]={ + [0x1B23F]={ category="lo", direction="l", }, - [0x18A39]={ + [0x1B240]={ category="lo", direction="l", }, - [0x18A3A]={ + [0x1B241]={ category="lo", direction="l", }, - [0x18A3B]={ + [0x1B242]={ category="lo", direction="l", }, - [0x18A3C]={ + [0x1B243]={ category="lo", direction="l", }, - [0x18A3D]={ + [0x1B244]={ category="lo", direction="l", }, - [0x18A3E]={ + [0x1B245]={ category="lo", direction="l", }, - [0x18A3F]={ + [0x1B246]={ category="lo", direction="l", }, - [0x18A40]={ + [0x1B247]={ category="lo", direction="l", }, - [0x18A41]={ + [0x1B248]={ category="lo", direction="l", }, - [0x18A42]={ + [0x1B249]={ category="lo", direction="l", }, - [0x18A43]={ + [0x1B24A]={ category="lo", direction="l", }, - [0x18A44]={ + [0x1B24B]={ category="lo", direction="l", }, - [0x18A45]={ + [0x1B24C]={ category="lo", direction="l", }, - [0x18A46]={ + [0x1B24D]={ category="lo", direction="l", }, - [0x18A47]={ + [0x1B24E]={ category="lo", direction="l", }, - [0x18A48]={ + [0x1B24F]={ category="lo", direction="l", }, - [0x18A49]={ + [0x1B250]={ category="lo", direction="l", }, - [0x18A4A]={ + [0x1B251]={ category="lo", direction="l", }, - [0x18A4B]={ + [0x1B252]={ category="lo", direction="l", }, - [0x18A4C]={ + [0x1B253]={ category="lo", direction="l", }, - [0x18A4D]={ + [0x1B254]={ category="lo", direction="l", }, - [0x18A4E]={ + [0x1B255]={ category="lo", direction="l", }, - [0x18A4F]={ + [0x1B256]={ category="lo", direction="l", }, - [0x18A50]={ + [0x1B257]={ category="lo", direction="l", }, - [0x18A51]={ + [0x1B258]={ category="lo", direction="l", }, - [0x18A52]={ + [0x1B259]={ category="lo", direction="l", }, - [0x18A53]={ + [0x1B25A]={ category="lo", direction="l", }, - [0x18A54]={ + [0x1B25B]={ category="lo", direction="l", }, - [0x18A55]={ + [0x1B25C]={ category="lo", direction="l", }, - [0x18A56]={ + [0x1B25D]={ category="lo", direction="l", }, - [0x18A57]={ + [0x1B25E]={ category="lo", direction="l", }, - [0x18A58]={ + [0x1B25F]={ category="lo", direction="l", }, - [0x18A59]={ + [0x1B260]={ category="lo", direction="l", }, - [0x18A5A]={ + [0x1B261]={ category="lo", direction="l", }, - [0x18A5B]={ + [0x1B262]={ category="lo", direction="l", }, - [0x18A5C]={ + [0x1B263]={ category="lo", direction="l", }, - [0x18A5D]={ + [0x1B264]={ category="lo", direction="l", }, - [0x18A5E]={ + [0x1B265]={ category="lo", direction="l", }, - [0x18A5F]={ + [0x1B266]={ category="lo", direction="l", }, - [0x18A60]={ + [0x1B267]={ category="lo", direction="l", }, - [0x18A61]={ + [0x1B268]={ category="lo", direction="l", }, - [0x18A62]={ + [0x1B269]={ category="lo", direction="l", }, - [0x18A63]={ + [0x1B26A]={ category="lo", direction="l", }, - [0x18A64]={ + [0x1B26B]={ category="lo", direction="l", }, - [0x18A65]={ + [0x1B26C]={ category="lo", direction="l", }, - [0x18A66]={ + [0x1B26D]={ category="lo", direction="l", }, - [0x18A67]={ + [0x1B26E]={ category="lo", direction="l", }, - [0x18A68]={ + [0x1B26F]={ category="lo", direction="l", }, - [0x18A69]={ + [0x1B270]={ category="lo", direction="l", }, - [0x18A6A]={ + [0x1B271]={ category="lo", direction="l", }, - [0x18A6B]={ + [0x1B272]={ category="lo", direction="l", }, - [0x18A6C]={ + [0x1B273]={ category="lo", direction="l", }, - [0x18A6D]={ + [0x1B274]={ category="lo", direction="l", }, - [0x18A6E]={ + [0x1B275]={ category="lo", direction="l", }, - [0x18A6F]={ + [0x1B276]={ category="lo", direction="l", }, - [0x18A70]={ + [0x1B277]={ category="lo", direction="l", }, - [0x18A71]={ + [0x1B278]={ category="lo", direction="l", }, - [0x18A72]={ + [0x1B279]={ category="lo", direction="l", }, - [0x18A73]={ + [0x1B27A]={ category="lo", direction="l", }, - [0x18A74]={ + [0x1B27B]={ category="lo", direction="l", }, - [0x18A75]={ + [0x1B27C]={ category="lo", direction="l", }, - [0x18A76]={ + [0x1B27D]={ category="lo", direction="l", }, - [0x18A77]={ + [0x1B27E]={ category="lo", direction="l", }, - [0x18A78]={ + [0x1B27F]={ category="lo", direction="l", }, - [0x18A79]={ + [0x1B280]={ category="lo", direction="l", }, - [0x18A7A]={ + [0x1B281]={ category="lo", direction="l", }, - [0x18A7B]={ + [0x1B282]={ category="lo", direction="l", }, - [0x18A7C]={ + [0x1B283]={ category="lo", direction="l", }, - [0x18A7D]={ + [0x1B284]={ category="lo", direction="l", }, - [0x18A7E]={ + [0x1B285]={ category="lo", direction="l", }, - [0x18A7F]={ + [0x1B286]={ category="lo", direction="l", }, - [0x18A80]={ + [0x1B287]={ category="lo", direction="l", }, - [0x18A81]={ + [0x1B288]={ category="lo", direction="l", }, - [0x18A82]={ + [0x1B289]={ category="lo", direction="l", }, - [0x18A83]={ + [0x1B28A]={ category="lo", direction="l", }, - [0x18A84]={ + [0x1B28B]={ category="lo", direction="l", }, - [0x18A85]={ + [0x1B28C]={ category="lo", direction="l", }, - [0x18A86]={ + [0x1B28D]={ category="lo", direction="l", }, - [0x18A87]={ + [0x1B28E]={ category="lo", direction="l", }, - [0x18A88]={ + [0x1B28F]={ category="lo", direction="l", }, - [0x18A89]={ + [0x1B290]={ category="lo", direction="l", }, - [0x18A8A]={ + [0x1B291]={ category="lo", direction="l", }, - [0x18A8B]={ + [0x1B292]={ category="lo", direction="l", }, - [0x18A8C]={ + [0x1B293]={ category="lo", direction="l", }, - [0x18A8D]={ + [0x1B294]={ category="lo", direction="l", }, - [0x18A8E]={ + [0x1B295]={ category="lo", direction="l", }, - [0x18A8F]={ + [0x1B296]={ category="lo", direction="l", }, - [0x18A90]={ + [0x1B297]={ category="lo", direction="l", }, - [0x18A91]={ + [0x1B298]={ category="lo", direction="l", }, - [0x18A92]={ + [0x1B299]={ category="lo", direction="l", }, - [0x18A93]={ + [0x1B29A]={ category="lo", direction="l", }, - [0x18A94]={ + [0x1B29B]={ category="lo", direction="l", }, - [0x18A95]={ + [0x1B29C]={ category="lo", direction="l", }, - [0x18A96]={ + [0x1B29D]={ category="lo", direction="l", }, - [0x18A97]={ + [0x1B29E]={ category="lo", direction="l", }, - [0x18A98]={ + [0x1B29F]={ category="lo", direction="l", }, - [0x18A99]={ + [0x1B2A0]={ category="lo", direction="l", }, - [0x18A9A]={ + [0x1B2A1]={ category="lo", direction="l", }, - [0x18A9B]={ + [0x1B2A2]={ category="lo", direction="l", }, - [0x18A9C]={ + [0x1B2A3]={ category="lo", direction="l", }, - [0x18A9D]={ + [0x1B2A4]={ category="lo", direction="l", }, - [0x18A9E]={ + [0x1B2A5]={ category="lo", direction="l", }, - [0x18A9F]={ + [0x1B2A6]={ category="lo", direction="l", }, - [0x18AA0]={ + [0x1B2A7]={ category="lo", direction="l", }, - [0x18AA1]={ + [0x1B2A8]={ category="lo", direction="l", }, - [0x18AA2]={ + [0x1B2A9]={ category="lo", direction="l", }, - [0x18AA3]={ + [0x1B2AA]={ category="lo", direction="l", }, - [0x18AA4]={ + [0x1B2AB]={ category="lo", direction="l", }, - [0x18AA5]={ + [0x1B2AC]={ category="lo", direction="l", }, - [0x18AA6]={ + [0x1B2AD]={ category="lo", direction="l", }, - [0x18AA7]={ + [0x1B2AE]={ category="lo", direction="l", }, - [0x18AA8]={ + [0x1B2AF]={ category="lo", direction="l", }, - [0x18AA9]={ + [0x1B2B0]={ category="lo", direction="l", }, - [0x18AAA]={ + [0x1B2B1]={ category="lo", direction="l", }, - [0x18AAB]={ + [0x1B2B2]={ category="lo", direction="l", }, - [0x18AAC]={ + [0x1B2B3]={ category="lo", direction="l", }, - [0x18AAD]={ + [0x1B2B4]={ category="lo", direction="l", }, - [0x18AAE]={ + [0x1B2B5]={ category="lo", direction="l", }, - [0x18AAF]={ + [0x1B2B6]={ category="lo", direction="l", }, - [0x18AB0]={ + [0x1B2B7]={ category="lo", direction="l", }, - [0x18AB1]={ + [0x1B2B8]={ category="lo", direction="l", }, - [0x18AB2]={ + [0x1B2B9]={ category="lo", direction="l", }, - [0x18AB3]={ + [0x1B2BA]={ category="lo", direction="l", }, - [0x18AB4]={ + [0x1B2BB]={ category="lo", direction="l", }, - [0x18AB5]={ + [0x1B2BC]={ category="lo", direction="l", }, - [0x18AB6]={ + [0x1B2BD]={ category="lo", direction="l", }, - [0x18AB7]={ + [0x1B2BE]={ category="lo", direction="l", }, - [0x18AB8]={ + [0x1B2BF]={ category="lo", direction="l", }, - [0x18AB9]={ + [0x1B2C0]={ category="lo", direction="l", }, - [0x18ABA]={ + [0x1B2C1]={ category="lo", direction="l", }, - [0x18ABB]={ + [0x1B2C2]={ category="lo", direction="l", }, - [0x18ABC]={ + [0x1B2C3]={ category="lo", direction="l", }, - [0x18ABD]={ + [0x1B2C4]={ category="lo", direction="l", }, - [0x18ABE]={ + [0x1B2C5]={ category="lo", direction="l", }, - [0x18ABF]={ + [0x1B2C6]={ category="lo", direction="l", }, - [0x18AC0]={ + [0x1B2C7]={ category="lo", direction="l", }, - [0x18AC1]={ + [0x1B2C8]={ category="lo", direction="l", }, - [0x18AC2]={ + [0x1B2C9]={ category="lo", direction="l", }, - [0x18AC3]={ + [0x1B2CA]={ category="lo", direction="l", }, - [0x18AC4]={ + [0x1B2CB]={ category="lo", direction="l", }, - [0x18AC5]={ + [0x1B2CC]={ category="lo", direction="l", }, - [0x18AC6]={ + [0x1B2CD]={ category="lo", direction="l", }, - [0x18AC7]={ + [0x1B2CE]={ category="lo", direction="l", }, - [0x18AC8]={ + [0x1B2CF]={ category="lo", direction="l", }, - [0x18AC9]={ + [0x1B2D0]={ category="lo", direction="l", }, - [0x18ACA]={ + [0x1B2D1]={ category="lo", direction="l", }, - [0x18ACB]={ + [0x1B2D2]={ category="lo", direction="l", }, - [0x18ACC]={ + [0x1B2D3]={ category="lo", direction="l", }, - [0x18ACD]={ + [0x1B2D4]={ category="lo", direction="l", }, - [0x18ACE]={ + [0x1B2D5]={ category="lo", direction="l", }, - [0x18ACF]={ + [0x1B2D6]={ category="lo", direction="l", }, - [0x18AD0]={ + [0x1B2D7]={ category="lo", direction="l", }, - [0x18AD1]={ + [0x1B2D8]={ category="lo", direction="l", }, - [0x18AD2]={ + [0x1B2D9]={ category="lo", direction="l", }, - [0x18AD3]={ + [0x1B2DA]={ category="lo", direction="l", }, - [0x18AD4]={ + [0x1B2DB]={ category="lo", direction="l", }, - [0x18AD5]={ + [0x1B2DC]={ category="lo", direction="l", }, - [0x18AD6]={ + [0x1B2DD]={ category="lo", direction="l", }, - [0x18AD7]={ + [0x1B2DE]={ category="lo", direction="l", }, - [0x18AD8]={ + [0x1B2DF]={ category="lo", direction="l", }, - [0x18AD9]={ + [0x1B2E0]={ category="lo", direction="l", }, - [0x18ADA]={ + [0x1B2E1]={ category="lo", direction="l", }, - [0x18ADB]={ + [0x1B2E2]={ category="lo", direction="l", }, - [0x18ADC]={ + [0x1B2E3]={ category="lo", direction="l", }, - [0x18ADD]={ + [0x1B2E4]={ category="lo", direction="l", }, - [0x18ADE]={ + [0x1B2E5]={ category="lo", direction="l", }, - [0x18ADF]={ + [0x1B2E6]={ category="lo", direction="l", }, - [0x18AE0]={ + [0x1B2E7]={ category="lo", direction="l", }, - [0x18AE1]={ + [0x1B2E8]={ category="lo", direction="l", }, - [0x18AE2]={ + [0x1B2E9]={ category="lo", direction="l", }, - [0x18AE3]={ + [0x1B2EA]={ category="lo", direction="l", }, - [0x18AE4]={ + [0x1B2EB]={ category="lo", direction="l", }, - [0x18AE5]={ + [0x1B2EC]={ category="lo", direction="l", }, - [0x18AE6]={ + [0x1B2ED]={ category="lo", direction="l", }, - [0x18AE7]={ + [0x1B2EE]={ category="lo", direction="l", }, - [0x18AE8]={ + [0x1B2EF]={ category="lo", direction="l", }, - [0x18AE9]={ + [0x1B2F0]={ category="lo", direction="l", }, - [0x18AEA]={ + [0x1B2F1]={ category="lo", direction="l", }, - [0x18AEB]={ + [0x1B2F2]={ category="lo", direction="l", }, - [0x18AEC]={ + [0x1B2F3]={ category="lo", direction="l", }, - [0x18AED]={ + [0x1B2F4]={ category="lo", direction="l", }, - [0x18AEE]={ + [0x1B2F5]={ category="lo", direction="l", }, - [0x18AEF]={ + [0x1B2F6]={ category="lo", direction="l", }, - [0x18AF0]={ + [0x1B2F7]={ category="lo", direction="l", }, - [0x18AF1]={ + [0x1B2F8]={ category="lo", direction="l", }, - [0x18AF2]={ + [0x1B2F9]={ category="lo", direction="l", }, - [0x1B000]={ + [0x1B2FA]={ category="lo", direction="l", }, - [0x1B001]={ + [0x1B2FB]={ category="lo", direction="l", }, @@ -102473,6 +108298,86 @@ return { category="so", direction="on", }, + [0x1D2E0]={ + category="no", + direction="l", + }, + [0x1D2E1]={ + category="no", + direction="l", + }, + [0x1D2E2]={ + category="no", + direction="l", + }, + [0x1D2E3]={ + category="no", + direction="l", + }, + [0x1D2E4]={ + category="no", + direction="l", + }, + [0x1D2E5]={ + category="no", + direction="l", + }, + [0x1D2E6]={ + category="no", + direction="l", + }, + [0x1D2E7]={ + category="no", + direction="l", + }, + [0x1D2E8]={ + category="no", + direction="l", + }, + [0x1D2E9]={ + category="no", + direction="l", + }, + [0x1D2EA]={ + category="no", + direction="l", + }, + [0x1D2EB]={ + category="no", + direction="l", + }, + [0x1D2EC]={ + category="no", + direction="l", + }, + [0x1D2ED]={ + category="no", + direction="l", + }, + [0x1D2EE]={ + category="no", + direction="l", + }, + [0x1D2EF]={ + category="no", + direction="l", + }, + [0x1D2F0]={ + category="no", + direction="l", + }, + [0x1D2F1]={ + category="no", + direction="l", + }, + [0x1D2F2]={ + category="no", + direction="l", + }, + [0x1D2F3]={ + category="no", + direction="l", + }, [0x1D300]={ category="so", direction="on", @@ -102893,6 +108798,34 @@ return { category="no", direction="l", }, + [0x1D372]={ + category="no", + direction="l", + }, + [0x1D373]={ + category="no", + direction="l", + }, + [0x1D374]={ + category="no", + direction="l", + }, + [0x1D375]={ + category="no", + direction="l", + }, + [0x1D376]={ + category="no", + direction="l", + }, + [0x1D377]={ + category="no", + direction="l", + }, + [0x1D378]={ + category="no", + direction="l", + }, [0x1D400]={ category="lu", direction="l", @@ -110917,6 +116850,278 @@ return { category="po", direction="r", }, + [0x1EC71]={ + category="no", + direction="al", + }, + [0x1EC72]={ + category="no", + direction="al", + }, + [0x1EC73]={ + category="no", + direction="al", + }, + [0x1EC74]={ + category="no", + direction="al", + }, + [0x1EC75]={ + category="no", + direction="al", + }, + [0x1EC76]={ + category="no", + direction="al", + }, + [0x1EC77]={ + category="no", + direction="al", + }, + [0x1EC78]={ + category="no", + direction="al", + }, + [0x1EC79]={ + category="no", + direction="al", + }, + [0x1EC7A]={ + category="no", + direction="al", + }, + [0x1EC7B]={ + category="no", + direction="al", + }, + [0x1EC7C]={ + category="no", + direction="al", + }, + [0x1EC7D]={ + category="no", + direction="al", + }, + [0x1EC7E]={ + category="no", + direction="al", + }, + [0x1EC7F]={ + category="no", + direction="al", + }, + [0x1EC80]={ + category="no", + direction="al", + }, + [0x1EC81]={ + category="no", + direction="al", + }, + [0x1EC82]={ + category="no", + direction="al", + }, + [0x1EC83]={ + category="no", + direction="al", + }, + [0x1EC84]={ + category="no", + direction="al", + }, + [0x1EC85]={ + category="no", + direction="al", + }, + [0x1EC86]={ + category="no", + direction="al", + }, + [0x1EC87]={ + category="no", + direction="al", + }, + [0x1EC88]={ + category="no", + direction="al", + }, + [0x1EC89]={ + category="no", + direction="al", + }, + [0x1EC8A]={ + category="no", + direction="al", + }, + [0x1EC8B]={ + category="no", + direction="al", + }, + [0x1EC8C]={ + category="no", + direction="al", + }, + [0x1EC8D]={ + category="no", + direction="al", + }, + [0x1EC8E]={ + category="no", + direction="al", + }, + [0x1EC8F]={ + category="no", + direction="al", + }, + [0x1EC90]={ + category="no", + direction="al", + }, + [0x1EC91]={ + category="no", + direction="al", + }, + [0x1EC92]={ + category="no", + direction="al", + }, + [0x1EC93]={ + category="no", + direction="al", + }, + [0x1EC94]={ + category="no", + direction="al", + }, + [0x1EC95]={ + category="no", + direction="al", + }, + [0x1EC96]={ + category="no", + direction="al", + }, + [0x1EC97]={ + category="no", + direction="al", + }, + [0x1EC98]={ + category="no", + direction="al", + }, + [0x1EC99]={ + category="no", + direction="al", + }, + [0x1EC9A]={ + category="no", + direction="al", + }, + [0x1EC9B]={ + category="no", + direction="al", + }, + [0x1EC9C]={ + category="no", + direction="al", + }, + [0x1EC9D]={ + category="no", + direction="al", + }, + [0x1EC9E]={ + category="no", + direction="al", + }, + [0x1EC9F]={ + category="no", + direction="al", + }, + [0x1ECA0]={ + category="no", + direction="al", + }, + [0x1ECA1]={ + category="no", + direction="al", + }, + [0x1ECA2]={ + category="no", + direction="al", + }, + [0x1ECA3]={ + category="no", + direction="al", + }, + [0x1ECA4]={ + category="no", + direction="al", + }, + [0x1ECA5]={ + category="no", + direction="al", + }, + [0x1ECA6]={ + category="no", + direction="al", + }, + [0x1ECA7]={ + category="no", + direction="al", + }, + [0x1ECA8]={ + category="no", + direction="al", + }, + [0x1ECA9]={ + category="no", + direction="al", + }, + [0x1ECAA]={ + category="no", + direction="al", + }, + [0x1ECAB]={ + category="no", + direction="al", + }, + [0x1ECAC]={ + category="so", + direction="al", + }, + [0x1ECAD]={ + category="no", + direction="al", + }, + [0x1ECAE]={ + category="no", + direction="al", + }, + [0x1ECAF]={ + category="no", + direction="al", + }, + [0x1ECB0]={ + category="sc", + direction="al", + }, + [0x1ECB1]={ + category="no", + direction="al", + }, + [0x1ECB2]={ + category="no", + direction="al", + }, + [0x1ECB3]={ + category="no", + direction="al", + }, + [0x1ECB4]={ + category="no", + direction="al", + }, [0x1EE00]={ category="lo", direction="al", @@ -112569,6 +118774,10 @@ return { category="so", direction="l", }, + [0x1F12F]={ + category="so", + direction="on", + }, [0x1F130]={ category="so", direction="l", @@ -113389,6 +119598,30 @@ return { category="so", direction="l", }, + [0x1F260]={ + category="so", + direction="on", + }, + [0x1F261]={ + category="so", + direction="on", + }, + [0x1F262]={ + category="so", + direction="on", + }, + [0x1F263]={ + category="so", + direction="on", + }, + [0x1F264]={ + category="so", + direction="on", + }, + [0x1F265]={ + category="so", + direction="on", + }, [0x1F300]={ category="so", direction="on", @@ -117305,6 +123538,14 @@ return { category="so", direction="on", }, + [0x1F6D3]={ + category="so", + direction="on", + }, + [0x1F6D4]={ + category="so", + direction="on", + }, [0x1F6E0]={ category="so", direction="on", @@ -117385,6 +123626,18 @@ return { category="so", direction="on", }, + [0x1F6F7]={ + category="so", + direction="on", + }, + [0x1F6F8]={ + category="so", + direction="on", + }, + [0x1F6F9]={ + category="so", + direction="on", + }, [0x1F700]={ category="so", direction="on", @@ -118189,6 +124442,22 @@ return { category="so", direction="on", }, + [0x1F7D5]={ + category="so", + direction="on", + }, + [0x1F7D6]={ + category="so", + direction="on", + }, + [0x1F7D7]={ + category="so", + direction="on", + }, + [0x1F7D8]={ + category="so", + direction="on", + }, [0x1F800]={ category="so", direction="on", @@ -118781,6 +125050,54 @@ return { category="so", direction="on", }, + [0x1F900]={ + category="so", + direction="on", + }, + [0x1F901]={ + category="so", + direction="on", + }, + [0x1F902]={ + category="so", + direction="on", + }, + [0x1F903]={ + category="so", + direction="on", + }, + [0x1F904]={ + category="so", + direction="on", + }, + [0x1F905]={ + category="so", + direction="on", + }, + [0x1F906]={ + category="so", + direction="on", + }, + [0x1F907]={ + category="so", + direction="on", + }, + [0x1F908]={ + category="so", + direction="on", + }, + [0x1F909]={ + category="so", + direction="on", + }, + [0x1F90A]={ + category="so", + direction="on", + }, + [0x1F90B]={ + category="so", + direction="on", + }, [0x1F910]={ category="so", direction="on", @@ -118841,6 +125158,10 @@ return { category="so", direction="on", }, + [0x1F91F]={ + category="so", + direction="on", + }, [0x1F920]={ category="so", direction="on", @@ -118873,10 +125194,50 @@ return { category="so", direction="on", }, + [0x1F928]={ + category="so", + direction="on", + }, + [0x1F929]={ + category="so", + direction="on", + }, + [0x1F92A]={ + category="so", + direction="on", + }, + [0x1F92B]={ + category="so", + direction="on", + }, + [0x1F92C]={ + category="so", + direction="on", + }, + [0x1F92D]={ + category="so", + direction="on", + }, + [0x1F92E]={ + category="so", + direction="on", + }, + [0x1F92F]={ + category="so", + direction="on", + }, [0x1F930]={ category="so", direction="on", }, + [0x1F931]={ + category="so", + direction="on", + }, + [0x1F932]={ + category="so", + direction="on", + }, [0x1F933]={ category="so", direction="on", @@ -118973,6 +125334,22 @@ return { category="so", direction="on", }, + [0x1F94C]={ + category="so", + direction="on", + }, + [0x1F94D]={ + category="so", + direction="on", + }, + [0x1F94E]={ + category="so", + direction="on", + }, + [0x1F94F]={ + category="so", + direction="on", + }, [0x1F950]={ category="so", direction="on", @@ -119033,6 +125410,114 @@ return { category="so", direction="on", }, + [0x1F95F]={ + category="so", + direction="on", + }, + [0x1F960]={ + category="so", + direction="on", + }, + [0x1F961]={ + category="so", + direction="on", + }, + [0x1F962]={ + category="so", + direction="on", + }, + [0x1F963]={ + category="so", + direction="on", + }, + [0x1F964]={ + category="so", + direction="on", + }, + [0x1F965]={ + category="so", + direction="on", + }, + [0x1F966]={ + category="so", + direction="on", + }, + [0x1F967]={ + category="so", + direction="on", + }, + [0x1F968]={ + category="so", + direction="on", + }, + [0x1F969]={ + category="so", + direction="on", + }, + [0x1F96A]={ + category="so", + direction="on", + }, + [0x1F96B]={ + category="so", + direction="on", + }, + [0x1F96C]={ + category="so", + direction="on", + }, + [0x1F96D]={ + category="so", + direction="on", + }, + [0x1F96E]={ + category="so", + direction="on", + }, + [0x1F96F]={ + category="so", + direction="on", + }, + [0x1F970]={ + category="so", + direction="on", + }, + [0x1F973]={ + category="so", + direction="on", + }, + [0x1F974]={ + category="so", + direction="on", + }, + [0x1F975]={ + category="so", + direction="on", + }, + [0x1F976]={ + category="so", + direction="on", + }, + [0x1F97A]={ + category="so", + direction="on", + }, + [0x1F97C]={ + category="so", + direction="on", + }, + [0x1F97D]={ + category="so", + direction="on", + }, + [0x1F97E]={ + category="so", + direction="on", + }, + [0x1F97F]={ + category="so", + direction="on", + }, [0x1F980]={ category="so", direction="on", @@ -119105,10 +125590,374 @@ return { category="so", direction="on", }, + [0x1F992]={ + category="so", + direction="on", + }, + [0x1F993]={ + category="so", + direction="on", + }, + [0x1F994]={ + category="so", + direction="on", + }, + [0x1F995]={ + category="so", + direction="on", + }, + [0x1F996]={ + category="so", + direction="on", + }, + [0x1F997]={ + category="so", + direction="on", + }, + [0x1F998]={ + category="so", + direction="on", + }, + [0x1F999]={ + category="so", + direction="on", + }, + [0x1F99A]={ + category="so", + direction="on", + }, + [0x1F99B]={ + category="so", + direction="on", + }, + [0x1F99C]={ + category="so", + direction="on", + }, + [0x1F99D]={ + category="so", + direction="on", + }, + [0x1F99E]={ + category="so", + direction="on", + }, + [0x1F99F]={ + category="so", + direction="on", + }, + [0x1F9A0]={ + category="so", + direction="on", + }, + [0x1F9A1]={ + category="so", + direction="on", + }, + [0x1F9A2]={ + category="so", + direction="on", + }, + [0x1F9B0]={ + category="so", + direction="on", + }, + [0x1F9B1]={ + category="so", + direction="on", + }, + [0x1F9B2]={ + category="so", + direction="on", + }, + [0x1F9B3]={ + category="so", + direction="on", + }, + [0x1F9B4]={ + category="so", + direction="on", + }, + [0x1F9B5]={ + category="so", + direction="on", + }, + [0x1F9B6]={ + category="so", + direction="on", + }, + [0x1F9B7]={ + category="so", + direction="on", + }, + [0x1F9B8]={ + category="so", + direction="on", + }, + [0x1F9B9]={ + category="so", + direction="on", + }, [0x1F9C0]={ category="so", direction="on", }, + [0x1F9C1]={ + category="so", + direction="on", + }, + [0x1F9C2]={ + category="so", + direction="on", + }, + [0x1F9D0]={ + category="so", + direction="on", + }, + [0x1F9D1]={ + category="so", + direction="on", + }, + [0x1F9D2]={ + category="so", + direction="on", + }, + [0x1F9D3]={ + category="so", + direction="on", + }, + [0x1F9D4]={ + category="so", + direction="on", + }, + [0x1F9D5]={ + category="so", + direction="on", + }, + [0x1F9D6]={ + category="so", + direction="on", + }, + [0x1F9D7]={ + category="so", + direction="on", + }, + [0x1F9D8]={ + category="so", + direction="on", + }, + [0x1F9D9]={ + category="so", + direction="on", + }, + [0x1F9DA]={ + category="so", + direction="on", + }, + [0x1F9DB]={ + category="so", + direction="on", + }, + [0x1F9DC]={ + category="so", + direction="on", + }, + [0x1F9DD]={ + category="so", + direction="on", + }, + [0x1F9DE]={ + category="so", + direction="on", + }, + [0x1F9DF]={ + category="so", + direction="on", + }, + [0x1F9E0]={ + category="so", + direction="on", + }, + [0x1F9E1]={ + category="so", + direction="on", + }, + [0x1F9E2]={ + category="so", + direction="on", + }, + [0x1F9E3]={ + category="so", + direction="on", + }, + [0x1F9E4]={ + category="so", + direction="on", + }, + [0x1F9E5]={ + category="so", + direction="on", + }, + [0x1F9E6]={ + category="so", + direction="on", + }, + [0x1F9E7]={ + category="so", + direction="on", + }, + [0x1F9E8]={ + category="so", + direction="on", + }, + [0x1F9E9]={ + category="so", + direction="on", + }, + [0x1F9EA]={ + category="so", + direction="on", + }, + [0x1F9EB]={ + category="so", + direction="on", + }, + [0x1F9EC]={ + category="so", + direction="on", + }, + [0x1F9ED]={ + category="so", + direction="on", + }, + [0x1F9EE]={ + category="so", + direction="on", + }, + [0x1F9EF]={ + category="so", + direction="on", + }, + [0x1F9F0]={ + category="so", + direction="on", + }, + [0x1F9F1]={ + category="so", + direction="on", + }, + [0x1F9F2]={ + category="so", + direction="on", + }, + [0x1F9F3]={ + category="so", + direction="on", + }, + [0x1F9F4]={ + category="so", + direction="on", + }, + [0x1F9F5]={ + category="so", + direction="on", + }, + [0x1F9F6]={ + category="so", + direction="on", + }, + [0x1F9F7]={ + category="so", + direction="on", + }, + [0x1F9F8]={ + category="so", + direction="on", + }, + [0x1F9F9]={ + category="so", + direction="on", + }, + [0x1F9FA]={ + category="so", + direction="on", + }, + [0x1F9FB]={ + category="so", + direction="on", + }, + [0x1F9FC]={ + category="so", + direction="on", + }, + [0x1F9FD]={ + category="so", + direction="on", + }, + [0x1F9FE]={ + category="so", + direction="on", + }, + [0x1F9FF]={ + category="so", + direction="on", + }, + [0x1FA60]={ + category="so", + direction="on", + }, + [0x1FA61]={ + category="so", + direction="on", + }, + [0x1FA62]={ + category="so", + direction="on", + }, + [0x1FA63]={ + category="so", + direction="on", + }, + [0x1FA64]={ + category="so", + direction="on", + }, + [0x1FA65]={ + category="so", + direction="on", + }, + [0x1FA66]={ + category="so", + direction="on", + }, + [0x1FA67]={ + category="so", + direction="on", + }, + [0x1FA68]={ + category="so", + direction="on", + }, + [0x1FA69]={ + category="so", + direction="on", + }, + [0x1FA6A]={ + category="so", + direction="on", + }, + [0x1FA6B]={ + category="so", + direction="on", + }, + [0x1FA6C]={ + category="so", + direction="on", + }, + [0x1FA6D]={ + category="so", + direction="on", + }, [0x2F800]={ category="lo", direction="l", @@ -121665,6 +128514,7 @@ return { category="cf", direction="bn", }, + [0xE0100]={}, characters={}, classifiers={ [0x600]=0x4, @@ -121936,7 +128786,6 @@ return { [0x70B]=0x6, [0x70C]=0x6, [0x70D]=0x6, - [0x70F]=0x6, [0x710]=0x3, [0x711]=0x5, [0x712]=0x2, @@ -122106,6 +128955,9 @@ return { [0x7F8]=0x6, [0x7F9]=0x6, [0x7FA]=0x2, + [0x7FD]=0x5, + [0x7FE]=0x6, + [0x7FF]=0x6, [0x840]=0x3, [0x841]=0x2, [0x842]=0x2, @@ -122131,6 +128983,17 @@ return { [0x856]=0x4, [0x857]=0x4, [0x858]=0x4, + [0x860]=0x2, + [0x861]=0x4, + [0x862]=0x2, + [0x863]=0x2, + [0x864]=0x2, + [0x865]=0x2, + [0x866]=0x4, + [0x867]=0x3, + [0x868]=0x2, + [0x869]=0x3, + [0x86A]=0x3, [0x8A0]=0x2, [0x8A1]=0x2, [0x8A2]=0x2, @@ -122253,6 +129116,7 @@ return { [0x1875]=0x2, [0x1876]=0x2, [0x1877]=0x2, + [0x1878]=0x2, [0x1880]=0x4, [0x1881]=0x4, [0x1882]=0x4, @@ -122419,6 +129283,70 @@ return { [0x10BAD]=0x2, [0x10BAE]=0x2, [0x10BAF]=0x4, + [0x10D00]=0x1, + [0x10D01]=0x2, + [0x10D02]=0x2, + [0x10D03]=0x2, + [0x10D04]=0x2, + [0x10D05]=0x2, + [0x10D06]=0x2, + [0x10D07]=0x2, + [0x10D08]=0x2, + [0x10D09]=0x2, + [0x10D0A]=0x2, + [0x10D0B]=0x2, + [0x10D0C]=0x2, + [0x10D0D]=0x2, + [0x10D0E]=0x2, + [0x10D0F]=0x2, + [0x10D10]=0x2, + [0x10D11]=0x2, + [0x10D12]=0x2, + [0x10D13]=0x2, + [0x10D14]=0x2, + [0x10D15]=0x2, + [0x10D16]=0x2, + [0x10D17]=0x2, + [0x10D18]=0x2, + [0x10D19]=0x2, + [0x10D1A]=0x2, + [0x10D1B]=0x2, + [0x10D1C]=0x2, + [0x10D1D]=0x2, + [0x10D1E]=0x2, + [0x10D1F]=0x2, + [0x10D20]=0x2, + [0x10D21]=0x2, + [0x10D22]=0x3, + [0x10D23]=0x2, + [0x10F30]=0x2, + [0x10F31]=0x2, + [0x10F32]=0x2, + [0x10F33]=0x3, + [0x10F34]=0x2, + [0x10F35]=0x2, + [0x10F36]=0x2, + [0x10F37]=0x2, + [0x10F38]=0x2, + [0x10F39]=0x2, + [0x10F3A]=0x2, + [0x10F3B]=0x2, + [0x10F3C]=0x2, + [0x10F3D]=0x2, + [0x10F3E]=0x2, + [0x10F3F]=0x2, + [0x10F40]=0x2, + [0x10F41]=0x2, + [0x10F42]=0x2, + [0x10F43]=0x2, + [0x10F44]=0x2, + [0x10F45]=0x4, + [0x10F51]=0x2, + [0x10F52]=0x2, + [0x10F53]=0x2, + [0x10F54]=0x3, + [0x110BD]=0x4, + [0x110CD]=0x4, [0x1E900]=0x2, [0x1E901]=0x2, [0x1E902]=0x2, diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua index f2fdceb2c50..13b2e05157b 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua @@ -1,5 +1,5 @@ if not modules then modules = { } end modules ['luaotfload-colors'] = { - version = "2.8", + version = "2.9", comment = "companion to luaotfload-main.lua (font color)", author = "Khaled Hosny, Elie Roux, Philipp Gesang, Dohyun Kim, David Carlisle", copyright = "Luaotfload Development Team", diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-configuration.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-configuration.lua index 2f4589b10d0..d7604241c4d 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-configuration.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-configuration.lua @@ -9,7 +9,7 @@ -- if not modules then modules = { } end modules ["luaotfload-configuration"] = { - version = "2.8", + version = "2.9", comment = "part of Luaotfload", author = "Philipp Gesang, Dohyun Kim", copyright = "Luaotfload Development Team", diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua index 9c8c298636a..a195eaae724 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua @@ -1,11 +1,13 @@ if not modules then modules = { } end modules ['luaotfload-database'] = { - version = "2.8", + version = "2.9", comment = "companion to luaotfload-main.lua", - author = "Khaled Hosny, Elie Roux, Philipp Gesang", + author = "Khaled Hosny, Elie Roux, Philipp Gesang, Marcel Krüger", copyright = "Luaotfload Development Team", license = "GNU GPL v2.0" } + + --[[doc-- With version 2.7 we killed of the Fontforge libraries in favor of @@ -617,7 +619,11 @@ local style_category = { i = "italic", } -local type1_metrics = { "tfm", "ofm", } +-- MK Determine if casefold search is requested +local casefold_search = + not ({['0'] = true, ['f'] = true, [''] = true}) + [(kpse.var_value'texmf_casefold_search' or '1'):sub(1,1)] +-- /MK local lookup_filename = function (filename) if not name_index then name_index = load_names () end @@ -630,7 +636,10 @@ local lookup_filename = function (filename) local barenames = baredata [location] local idx if basenames ~= nil then + -- MK Added fallback idx = basenames [filename] + or casefold_search and basenames [stringlower(filename)] + -- /MK if idx then goto done end @@ -640,7 +649,10 @@ local lookup_filename = function (filename) local format = format_precedence [j] local filemap = barenames [format] if filemap then + -- MK Added fallback idx = barenames [format] [filename] + or casefold_search and barenames [format] [stringlower(filename)] + -- /MK if idx then break end @@ -668,6 +680,12 @@ local dummy_findfile = resolvers.findfile -- from basics-gen --- string -> string * string * bool local lookup_font_file + +-- MK Added mini scope to avoid variable limit +do +local type1_metrics = { "tfm", "ofm", } +-- /MK + lookup_font_file = function (filename) local found = lookup_filename (filename) @@ -693,6 +711,9 @@ lookup_font_file = function (filename) end return filename, nil, false end +-- MK +end +-- /MK --[[doc-- @@ -1208,12 +1229,18 @@ lookup_fullpath = function (fontname, ext) --- getfilename() local basenames = basedata [location] local idx if basenames ~= nil then + -- MK Added fallback idx = basenames [fontname] + or casefold_search and basenames [stringlower(fontname)] + -- /MK end if ext then local barenames = baredata [location] [ext] if not idx and barenames ~= nil then + -- MK Added fallback idx = barenames [fontname] + or casefold_search and barenames [stringlower(fontname)] + -- /MK end end if idx then @@ -2567,6 +2594,7 @@ generate_filedata = function (mappings) local inbase = base [location] --- no format since the suffix is known + -- MK Added lowercase versions for case-insensitive fallback if inbase then local present = inbase [basename] if present then @@ -2586,11 +2614,38 @@ generate_filedata = function (mappings) end else + local lowerbasename = stringlower (basename) + if basename ~= lowerbasename then + present = inbase [lowerbasename] + if present then + logreport ("both", 4, "db", + "Conflicting basename: %q already indexed \z + as %s.", + barename, mappings[present].basename) + conflicts.basenames = conflicts.basenames + 1 + + --- track conflicts per font + local conflictdata = entry.conflicts + + if not conflictdata then + entry.conflicts = { basename = present } + else -- some conflicts already detected + conflictdata.basename = present + end + + else + inbase [lowerbasename] = index + end + end inbase [basename] = index end else inbase = { basename = index } base [location] = inbase + local lowerbasename = stringlower (basename) + if basename ~= lowerbasename then + inbase [lowerbasename] = index + end end --- 2) add to barename table @@ -2614,14 +2669,40 @@ generate_filedata = function (mappings) else -- some conflicts already detected conflictdata.barename = present end - else + local lowerbarename = stringlower (barename) + if barename ~= lowerbarename then + present = inbare [lowerbarename] + if present then + logreport ("both", 4, "db", + "Conflicting barename: %q already indexed \z + as %s.", + barename, mappings[present].basename) + conflicts.barenames = conflicts.barenames + 1 + + --- track conflicts per font + local conflictdata = entry.conflicts + + if not conflictdata then + entry.conflicts = { barename = present } + else -- some conflicts already detected + conflictdata.barename = present + end + else + inbare [lowerbarename] = index + end + end inbare [barename] = index end else inbare = { [barename] = index } bare [location] [format] = inbare + local lowerbarename = stringlower (barename) + if barename ~= lowerbarename then + inbare [lowerbarename] = index + end end + -- /MK --- 3) add to fullpath map @@ -3680,7 +3761,10 @@ return { fonts.definers = fonts.definers or { resolvers = { } } names.blacklist = blacklist - names.version = 5 --- increase monotonically + -- MK Changed to rebuild with case insensitive fallback. + -- Negative version to indicate generation by modified code. + names.version = -1 --- decrease monotonically + -- /MK names.data = nil --- contains the loaded database names.lookups = nil --- contains the lookup cache diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua index f11f05b9b0e..c69cab3a720 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua @@ -1,11 +1,12 @@ if not modules then modules = { } end modules ["features"] = { - version = "2.8", + version = "2.9", comment = "companion to luaotfload-main.lua", - author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang", + author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang, Marcel Krüger", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } + local type = type local next = next local tonumber = tonumber @@ -1259,12 +1260,17 @@ if as_script == true then --- skip the remainder of the file report ("log", 5, "features", "Exiting early from luaotfload-features.lua.") return -else - local registersplit = definers.registersplit - registersplit (":", handle_request, "common") - registersplit ("", handle_request, "xetex path style") -- catches \font\text=[names] end +-- MK: Added +function fonts.definers.analyze (spec_string, size) + return handle_request { + size = size, + specification = spec_string, + } +end +-- /MK + -- We assume that the other otf stuff is loaded already; though there’s -- another check below during the initialization phase. diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-filelist.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-filelist.lua new file mode 100644 index 00000000000..3446b79aff9 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-filelist.lua @@ -0,0 +1,417 @@ +------------------------------------------------------------------------------- +-- FILE: luaotfload-filelist.lua +-- DESCRIPTION: a table with the files of luaotfload +-- VERSION: 2.9 +-- AUTHOR: Ulrike Fischer, +-- DATE: 2018-09-23 +------------------------------------------------------------------------------- + + + +--[[doc-- + +luaotfload has tables with files list in many places: In the import scripts, +in the init file, in the documentation. This makes maintenance difficult. +This here is a try to get everything in one place. As the order how files are +loaded during the merge matters the table is an array. Lists with other +sorting should be created with functions. Ditto for subtables. + +Some redundancy can not be avoided for now if one want to avoid to have to change all sort of functions. + +## locations +files can reside in up to three location (with perhaps different names) +- context ctxdir +- git gitdir +- texmf texdir + +files don't need to exist in all locations but the direction of the flow is +always from context over git to texmf: + context-> git -> texmf +so the "stations" of a file and its source can be deduced by the dirs recorded in the entries + + +## "kind" +the first four entries are from the original mkimport code and are important for the imports. +the others are new. + + · *essential*: Files required at runtime. + · *merged*: Files merged into the fontloader package. + · *ignored*: Lua files not merged, but part of the format. + · *tex*: TeX code, i.e. format and examples. + · *lualibs*: Files imported, but also provided by the Lualibs package. + · *library*: native luaotfload-files of type library + · *core*: native core luaotfload files + · *generated*: generated luaotfload files + · *scripts*: scripts (mk-...) + · *docu*: documentation (should perhaps be more refined + +## names + +The "real" name of a file in a location can be build + +- in the context location: ctxpref + name + ext +- in the git location: (gitpref or "fontloader-") + (ours or name ) + ext +- in the tex location: (gitpref or "fontloader-") + (texname or ours or name ) + ext + +## fields + mkstatus: used to ignore some files in mkstatus + + +## files which use the file names + +### mkimport + + mkimports needs an + --> "import" table with two subtables: + --> fontloader, with the files to get from generic, cond: ctxtype = "ctxgene" + --> context, with the files to get from context, cond: ctxtype = "ctxbase" + entries are subtables with {name=, ours=, kind= } + +and a + --> "package" table with + --> optional (probably unused) + --> required = files of type kind_merged + entries are the values of name + +### mkstatus + +this script has been already changed to use luaotfload-filelist.lua + +### luaotfload-init.lua + +luaotfload-init.lua needs a table +--> context_modules +with the (ordered) entries + +{false, "name"} -- kind_lualibs +{ctxdir,"ctxprefix+name"} -- kind_essential or kind_merged + +The same list should be used in local init_main = function () +but only without the prefix. +it is unclear how fonts_syn should be handled!!!! + +### filegraph.tex +has been already adapted + +### luaotfload-latex.tex/luaotfload-main.tex +has been already adapted + +### build.lua +??? + +--doc]]-- + + +local kind_essential = 0 +local kind_merged = 1 +local kind_tex = 2 +local kind_ignored = 3 +local kind_lualibs = 4 +local kind_library = 5 +local kind_core = 6 +local kind_generated = 7 +local kind_script = 8 +local kind_docu = 9 + +local kind_name = { + [0] = "essential", + [1] = "merged" , + [2] = "tex" , + [3] = "ignored" , + [4] = "lualibs" , + [5] = "library" , + [6] = "core", + [7] = "generated", + [8] = "script", + [9] = "docu" +} + + +local ctxdirbas = "tex/context/base/mkiv/" +local ctxdirgen = "tex/generic/context/luatex/" + +local gitdirimp = "src/fontloader/misc/" -- imp=imported +local gitdirsrc = "src/" +local gitdiress = "src/fontloader/runtime/" +local gitdirgen = "src/fontloader/auto/" +local gitdirdoc = "doc/" +local gitdirscr = "scripts/" +local gitdirmain = "./" + +-- these here a not really pathes +local texdirtex = "tex" +local texdirscr = "scripts" +local texdirdoc = "doc" +local texdirman = "man" + +luaotfload = luaotfload or {} +luaotfload.filelist = luaotfload.filelist or {} + +luaotfload.filelist.data = + { + -- at first the source files from context + { name = "l-lua" , ours = "l-lua" , ext = ".lua", kind = kind_lualibs , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "l-lpeg" , ours = "l-lpeg" , ext = ".lua", kind = kind_lualibs , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "l-function" , ours = "l-function" , ext = ".lua", kind = kind_lualibs , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "l-string" , ours = "l-string" , ext = ".lua", kind = kind_lualibs , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "l-table" , ours = "l-table" , ext = ".lua", kind = kind_lualibs , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "l-io" , ours = "l-io" , ext = ".lua", kind = kind_lualibs , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "l-file" , ours = "l-file" , ext = ".lua", kind = kind_lualibs , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "l-boolean" , ours = "l-boolean" , ext = ".lua", kind = kind_lualibs , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "l-math" , ours = "l-math" , ext = ".lua", kind = kind_lualibs , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "l-unicode" , ours = "l-unicode" , ext = ".lua", kind = kind_lualibs , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + + { name = "util-str" , ours = "util-str" , ext = ".lua", kind = kind_lualibs , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "util-fil" , ours = "util-fil" , ext = ".lua", kind = kind_lualibs , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + + { name = "basics-gen" , ours = nil , ext = ".lua", kind = kind_essential , gitdir=gitdiress, texdir = texdirtex , ctxdir= ctxdirgen, ctxtype = "ctxgene" , ctxpref = "luatex-" }, +-- files merged in the fontloader. Two files are ignored + { name = "data-con" , ours = "data-con" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "basics-nod" , ours = nil , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirgen, ctxtype = "ctxgene" , ctxpref = "luatex-" }, + { name = "basics-chr" , ours = nil , ext = ".lua", kind = kind_ignored , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirgen, ctxtype = "ctxgene" , ctxpref = "luatex-" }, + { name = "font-ini" , ours = "font-ini" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "fonts-mis" , ours = nil , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirgen, ctxtype = "ctxgene" , ctxpref = "luatex-" }, + { name = "font-con" , ours = "font-con" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "fonts-enc" , ours = nil , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirgen, ctxtype = "ctxgene" , ctxpref = "luatex-" }, + { name = "font-cid" , ours = "font-cid" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-map" , ours = "font-map" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "fonts-syn" , ours = nil , ext = ".lua", kind = kind_ignored , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirgen, ctxtype = "ctxgene" , ctxpref = "luatex-" }, + { name = "font-vfc" , ours = "font-vfc" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-otr" , ours = "font-otr" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-oti" , ours = "font-oti" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-ott" , ours = "font-ott" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-cff" , ours = "font-cff" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-ttf" , ours = "font-ttf" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-dsp" , ours = "font-dsp" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-oup" , ours = "font-oup" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-otl" , ours = "font-otl" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-oto" , ours = "font-oto" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-otj" , ours = "font-otj" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-ota" , ours = "font-ota" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-ots" , ours = "font-ots" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-osd" , ours = "font-osd" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-ocl" , ours = "font-ocl" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-otc" , ours = "font-otc" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-onr" , ours = "font-onr" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-one" , ours = "font-one" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-afk" , ours = "font-afk" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-tfm" , ours = "font-tfm" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-lua" , ours = "font-lua" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-def" , ours = "font-def" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "fonts-def" , ours = nil , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirgen, ctxtype = "ctxgene" , ctxpref = "luatex-" }, + { name = "fonts-ext" , ours = nil , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirgen, ctxtype = "ctxgene" , ctxpref = "luatex-" }, + { name = "font-imp-tex" , ours = "font-imp-tex" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-imp-ligatures", ours = "font-imp-ligatures", ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-imp-italics" , ours = "font-imp-italics" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "font-imp-effects" , ours = "font-imp-effects" , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirbas, ctxtype = "ctxbase" }, + { name = "fonts-lig" , ours = nil , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirgen, ctxtype = "ctxgene" , ctxpref = "luatex-" }, + { name = "fonts-gbn" , ours = nil , ext = ".lua", kind = kind_merged , gitdir=gitdirimp, texdir = texdirtex , ctxdir= ctxdirgen, ctxtype = "ctxgene" , ctxpref = "luatex-" }, +-- end of files merged + + { name = "fonts-merged" , ours = "reference" , ext = ".lua", kind = kind_essential , gitdir=gitdiress, texdir = texdirtex , ctxdir= ctxdirgen ,ctxtype = "ctxgene" , ctxpref = "luatex-" }, + + + +-- this two files are useful as reference for the load order but should not be installed + { name = "fonts" , ours = "load-order-reference", ext = ".lua", kind = kind_ignored , gitdir=gitdirimp, ctxdir= ctxdirgen, ctxtype = "ctxgene" , ctxpref = "luatex-" }, + { name = "fonts" , ours = "load-order-reference", ext = ".tex", kind = kind_tex , gitdir=gitdirimp, ctxdir= ctxdirgen, ctxtype = "ctxgene" , ctxpref = "luatex-" }, + +-- the default fontloader. How to code the name?? + { name = "YYYY-MM-DD" , ext = ".lua", kind = kind_generated , gitdir = gitdirgen, texdir = texdirtex,mkstatus="auto" }, + +-- the luaotfload files + { name = "luaotfload" ,kind = kind_core, ext =".sty", gitdir=gitdirsrc, texdir=texdirtex, gitpref="",}, + { name = "main" ,kind = kind_core, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "init" ,kind = kind_core, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "log" ,kind = kind_core, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "diagnostics" ,kind = kind_core, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + + { name = "tool" ,kind = kind_core, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "blacklist" ,kind = kind_core, ext =".cnf", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + + { name = "filelist" ,kind = kind_library, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "auxiliary" ,kind = kind_library, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "colors" ,kind = kind_library, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "configuration" ,kind = kind_library, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "database" ,kind = kind_library, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "features" ,kind = kind_library, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "letterspace" ,kind = kind_library, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "loaders" ,kind = kind_library, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "parsers" ,kind = kind_library, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + { name = "resolvers" ,kind = kind_library, ext =".lua", gitdir=gitdirsrc, texdir=texdirtex, gitpref = "luaotfload-" }, + + { name = "characters" ,kind = kind_generated, ext =".lua", gitdir=gitdirgen, texdir=texdirtex, gitpref = "luaotfload-", script="mkcharacter" }, + { name = "glyphlist" ,kind = kind_generated, ext =".lua", gitdir=gitdirgen, texdir=texdirtex, gitpref = "luaotfload-", script="mkglyphlist" }, + { name = "status" ,kind = kind_generated, ext =".lua", gitdir=gitdirgen, texdir=texdirtex, gitpref = "luaotfload-", script="mkstatus",mkstatus="ignore" }, + + + +-- scripts + { name = "mkimport" ,kind = kind_script, gitdir = gitdirscr, gitpref = "", ext=""}, + { name = "mkglyphlist" ,kind = kind_script, gitdir = gitdirscr, gitpref = "", ext=""}, + { name = "mkcharacters" ,kind = kind_script, gitdir = gitdirscr, gitpref = "", ext=""}, + { name = "mkstatus" ,kind = kind_script, gitdir = gitdirscr, gitpref = "", ext=""}, + { name = "mktests" ,kind = kind_script, gitdir = gitdirscr, gitpref = "", ext=""}, + +-- documentation (source dirs need perhaps coding ...) but don't overdo for now + + { name = "latex" , kind= kind_docu, ext = ".tex", gitpref = "luaotfload-", gitdir = gitdirdoc, texdir= texdirdoc, typeset = true }, + { name = "main" , kind= kind_docu, ext = ".tex", gitpref = "luaotfload-", gitdir = gitdirdoc, texdir= texdirdoc, typeset = false }, + { name = "conf" , kind= kind_docu, ext = ".tex", gitpref = "luaotfload-", gitdir = gitdirdoc, texdir= texdirdoc, typeset = true , generated = true}, + { name = "tool" , kind= kind_docu, ext = ".tex", gitpref = "luaotfload-", gitdir = gitdirdoc, texdir= texdirdoc, typeset = true , generated = true}, + { name = "filegraph" , kind= kind_docu, ext = ".tex", gitpref ="", gitdir = gitdirdoc, texdir= texdirdoc, typeset = true , generated = true}, + { name = "conf" , kind= kind_docu, ext = ".rst", gitpref = "luaotfload.", gitdir = gitdirdoc, texdir= texdirdoc }, + { name = "conf" , kind= kind_docu, ext = ".5" , gitpref = "luaotfload.", gitdir = gitdirdoc, texdir= texdirman}, + { name = "tool" , kind= kind_docu, ext = ".rst", gitpref = "luaotfload-", gitdir = gitdirdoc, texdir= texdirdoc}, + { name = "tool" , kind= kind_docu, ext = ".1" , gitpref = "luaotfload-", gitdir = gitdirdoc, texdir= texdirman }, + { name = "README" , kind= kind_docu, texname = "README", gitpref="", ext = ".md" , gitdir = gitdirdoc, texdir= texdirdoc}, + { name = "COPYING" , kind= kind_docu, ext = "" , gitpref="", gitdir = gitdirmain, texdir= texdirdoc}, + { name = "NEWS" , kind= kind_docu, ext = "" , gitpref="", gitdir = gitdirmain , texdir= texdirdoc}, + { name = "README" , kind= kind_docu, gitpref="", ext = ".md" , gitdir = gitdirmain}, -- github readme + +} + + + +-- functions +-- list of kind: +--local kind_essential = 0 +--local kind_merged = 1 +--local kind_tex = 2 +--local kind_ignored = 3 +--local kind_lualibs = 4 +--local kind_library = 5 +--local kind_core = 6 +--local kind_generated = 7 +--local kind_script = 8 +--local kind_docu = 9 + + +-- some selections +-- due to the history and setup of the other files, there are not always simple "kind" selections. +-- ctx base files are splitted in two sets: +-- 1. font & node +function luaotfload.filelist.selectctxfontentries ( filetable ) + local result = {} + for i,v in ipairs (filetable) do + if v.ctxtype == "ctxbase" and v.kind==1 then + table.insert(result,v) + end + end + return result +end + +-- 2. lualibs libraries +function luaotfload.filelist.selectctxlibsentries ( filetable ) + local result = {} + for i,v in ipairs (filetable) do + if v.ctxtype == "ctxbase" and v.kind==4 then + table.insert(result,v) + end + end + return result +end + +-- ctx generic files +-- 1. ignored files are not in the list ... +function luaotfload.filelist.selectctxgeneusedentries ( filetable ) + local result = {} + for i,v in ipairs (filetable) do + if v.ctxtype == "ctxgene" and v.kind==1 then + table.insert(result,v) + end + end + return result +end + +-- 2. ignored files are in the list +function luaotfload.filelist.selectctxgeneentries ( filetable ) + local result = {} + for i,v in ipairs (filetable) do + if v.ctxtype == "ctxgene" and (v.kind==1 or v.kind== 3) then + table.insert(result,v) + end + end + return result +end + +-- luaotfload-files (lol) are splitted in essential (0), core (6), lib (5) and gene (7) and scr (scripts): +-- luaoftload libraries + +function luaotfload.filelist.selectlolessentries (filetable) + local result = {} + for i,v in ipairs (filetable) do + if v.kind == 0 then + table.insert (result,v) + end + end + return result +end + + +function luaotfload.filelist.selectlollibentries (filetable) + local result = {} + for i,v in ipairs (filetable) do + if v.kind == 5 then + table.insert (result,v) + end + end + return result +end + +-- luaoftload core + +function luaotfload.filelist.selectlolcoreentries (filetable) + local result = {} + for i,v in ipairs (filetable) do + if v.kind == 6 then + table.insert (result,v) + end + end + return result +end + +-- core and lib lua-files + +function luaotfload.filelist.selectlolsrcluaentries (filetable) + local result = {} + for i,v in ipairs (filetable) do + if (v.kind == 5 or v.kind==6) and v.ext==".lua" then + table.insert (result,v) + end + end + return result +end + + + +-- luaoftload generated + +function luaotfload.filelist.selectlolgeneentries (filetable) + local result = {} + for i,v in ipairs (filetable) do + if v.kind == 7 then + table.insert (result,v) + end + end + return result +end + + + +-- scripts +function luaotfload.filelist.selectlolscrentries ( filetable ) + local result = {} + for i,v in ipairs (filetable) do + if v.kind==8 then + table.insert(result,v) + end + end + return result +end + + + + + + diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-init.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-init.lua index ad18b321c5e..b50b5c2291d 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-init.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-init.lua @@ -3,10 +3,12 @@ -- FILE: luaotfload-init.lua -- DESCRIPTION: Luaotfload font loader initialization -- REQUIREMENTS: luatex v.0.80 or later; packages lualibs --- AUTHOR: Philipp Gesang (Phg), +-- VERSION: 2.9 2018-09-24 +-- AUTHOR: Philipp Gesang (Phg), , Marcel Krüger ----------------------------------------------------------------------- -- + local setmetatable = setmetatable local kpselookup = kpse.lookup local lfsisdir = lfs.isdir @@ -238,6 +240,7 @@ local context_modules = { { false, "l-file" }, { false, "l-boolean" }, { false, "l-math" }, + { false, "l-unicode" }, -- NEW UF 19.09.2018 { false, "util-str" }, { false, "util-fil" }, @@ -246,13 +249,16 @@ local context_modules = { { ctx, "data-con" }, { ltx, "luatex-basics-nod" }, { ctx, "font-ini" }, + { ltx, "luatex-fonts-mis" }, -- NEW UF 19.09.2018 { ctx, "font-con" }, { ltx, "luatex-fonts-enc" }, { ctx, "font-cid" }, { ctx, "font-map" }, - { ltx, "luatex-fonts-syn" }, +-- { ltx, "luatex-fonts-syn" }, -- ignore?? + { ctx, "font-vfc" }, -- NEW UF 19.09.2018 { ctx, "font-oti" }, { ctx, "font-otr" }, + { ctx, "font-ott" }, { ctx, "font-cff" }, { ctx, "font-ttf" }, { ctx, "font-dsp" }, @@ -271,8 +277,15 @@ local context_modules = { { ctx, "font-tfm" }, { ctx, "font-lua" }, { ctx, "font-def" }, + { ltx, "luatex-fonts-def" }, { ltx, "luatex-fonts-ext" }, - { ctx, "font-gbn" }, + { ctx, "font-imp-tex" }, + { ctx, "font-imp-ligatures"}, + { ctx, "font-imp-italics" }, + { ctx, "font-imp-effects" }, + { ltx, "luatex-fonts-lig" }, + { ltx, "luatex-fonts-gbn" }, + } --[[context_modules]] @@ -527,13 +540,16 @@ local init_main = function () load_fontloader_module "data-con" load_fontloader_module "basics-nod" load_fontloader_module "font-ini" + load_fontloader_module "fonts-mis" load_fontloader_module "font-con" load_fontloader_module "fonts-enc" load_fontloader_module "font-cid" load_fontloader_module "font-map" - load_fontloader_module "fonts-syn" - load_fontloader_module "font-oti" +-- load_fontloader_module "fonts-syn" -- ignore? + load_fontloader_module "font-vfc" load_fontloader_module "font-otr" + load_fontloader_module "font-oti" + load_fontloader_module "font-ott" load_fontloader_module "font-cff" load_fontloader_module "font-ttf" load_fontloader_module "font-dsp" @@ -545,14 +561,21 @@ local init_main = function () load_fontloader_module "font-ots" load_fontloader_module "font-osd" load_fontloader_module "font-ocl" + load_fontloader_module "font-otc" load_fontloader_module "font-onr" load_fontloader_module "font-one" load_fontloader_module "font-afk" load_fontloader_module "font-tfm" load_fontloader_module "font-lua" load_fontloader_module "font-def" + load_fontloader_module "fonts-def" load_fontloader_module "fonts-ext" - load_fontloader_module "font-gbn" + load_fontloader_module "font-imp-tex" + load_fontloader_module "font-imp-ligatures" + load_fontloader_module "font-imp-italics" + load_fontloader_module "font-imp-effects" + load_fontloader_module "fonts-lig" + load_fontloader_module "fonts-gbn" elseif fontloader == "context" then logreport ("log", 0, "init", @@ -648,14 +671,20 @@ local init_post_install_callbacks = function () --doc]]-- + -- MK Pass current text direction to simple_font_handler + local handler = nodes.simple_font_handler + local callback = function(head, groupcode, _, _, direction) + return handler(head, groupcode, nil, nil, direction or tex.get'textdir') + end luatexbase.add_to_callback("pre_linebreak_filter", - nodes.simple_font_handler, + callback, "luaotfload.node_processor", 1) luatexbase.add_to_callback("hpack_filter", - nodes.simple_font_handler, + callback, "luaotfload.node_processor", 1) + -- /MK end local init_post_load_agl = function () diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua index fd64c90a4b8..104047ba8ba 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua @@ -1,7 +1,7 @@ if not modules then modules = { } end modules ['letterspace'] = { - version = "2.8", + version = "2.9", comment = "companion to luaotfload-main.lua", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL; adapted by Philipp Gesang", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL; adapted by Philipp Gesang, Ulrike Fischer", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } @@ -70,7 +70,7 @@ local copy_node = nodedirect.copy local new_node = nodedirect.new local nodepool = nodedirect.pool -local new_kern = nodepool.kern +-- local new_kern = nodepool.kern -- UF removed 2017-07-14 local nodecodes = nodes.nodecodes @@ -236,16 +236,21 @@ end --- character kerning functionality ---=================================================================--- +-- UF changed 2017-07-14 +local nodedirectnew = node.direct.new + local kern_injector = function (fillup, kern) - if fillup then - local g = new_glue(kern) - local s = getfield(g, "spec") - setfield(s, "stretch", kern) - setfield(s, "stretch_order", 1) - return g - end - return new_kern(kern) + if fillup then + local g = nodedirectnew("glue") + setfield(g, "stretch", kern) + setfield(g, "stretch_order", 1) + return g + end + local g = nodedirectnew("kern") + setfield(g,"kern",kern) + return g end +-- /UF local kernable_skip = function (n) local st = getsubtype (n) diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-log.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-log.lua index ab38ffaac14..8ba3e1c474a 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-log.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-log.lua @@ -1,5 +1,5 @@ if not modules then modules = { } end modules ["luaotfload-log"] = { - version = "2.8", + version = "2.9", comment = "companion to Luaotfload", author = "Khaled Hosny, Elie Roux, Philipp Gesang", copyright = "Luaotfload Development Team", diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-main.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-main.lua index 14d531683a2..1ac0752c621 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-main.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-main.lua @@ -2,7 +2,7 @@ -- FILE: luaotfload-main.lua -- DESCRIPTION: Luaotfload entry point -- REQUIREMENTS: luatex v.0.95.0 or later; package lualibs --- AUTHOR: Élie Roux, Khaled Hosny, Philipp Gesang +-- AUTHOR: Élie Roux, Khaled Hosny, Philipp Gesang, Ulrike Fischer, Marcel Krüger ----------------------------------------------------------------------- -- @@ -11,7 +11,7 @@ config = config or { } luaotfload = luaotfload or { } local luaotfload = luaotfload luaotfload.log = luaotfload.log or { } -luaotfload.version = "2.8" +luaotfload.version = "2.9" luaotfload.loaders = { } luaotfload.min_luatex_version = { 0, 95, 0 } luaotfload.fontloader_package = "reference" --- default: from current Context @@ -52,8 +52,8 @@ local authors = "\z luaotfload.module = { name = "luaotfload-main", - version = 2.80001, - date = "2017/01/29", + version = 2.90001, + date = "2018/09/24", description = "OpenType layout system.", author = authors, copyright = authors, diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-parsers.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-parsers.lua index b188bb0d28c..b6e7acab7a6 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-parsers.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-parsers.lua @@ -3,10 +3,11 @@ -- FILE: luaotfload-parsers.lua -- DESCRIPTION: various lpeg-based parsers used in Luaotfload -- REQUIREMENTS: Luaotfload >= 2.8 --- AUTHOR: Philipp Gesang (Phg), +-- AUTHOR: Philipp Gesang (Phg), , Marcel Krüger ------------------------------------------------------------------------------- -- + local traversal_maxdepth = 42 --- prevent stack overflows local rawset = rawset @@ -238,6 +239,7 @@ end --- -> (string -> fun option -> string list) --- -> tab * tab * tab local read_fonts_conf_indeed +-- MK Made basedir an explicit parameter to fix relative paths read_fonts_conf_indeed = function (depth, start, home, @@ -246,7 +248,8 @@ read_fonts_conf_indeed = function (depth, acc, done, dirs_done, - find_files) + find_files, + basedir) logreport ("both", 4, "db", "Fontconfig scanner processing path %s.", @@ -303,7 +306,7 @@ read_fonts_conf_indeed = function (depth, elseif --- if the path is relative, we make it absolute not ( lfsisfile(path) or lfsisdir(path) ) then - path = filejoin(filedirname(start), path) + path = filejoin(basedir, path) end if lfsisfile(path) and kpsereadable_file(path) @@ -321,17 +324,22 @@ read_fonts_conf_indeed = function (depth, acc, done, dirs_done, - find_files) + find_files, + basedir) end elseif lfsisdir(path) then --- arrow code ahead - local config_files = find_files (path, conf_filter) - for _, filename in next, config_files do - if not done[filename] then - if done[path] then + if done[path] then + logreport ("log", 3, "db", + "Skipping directory at %s, already included.", path) + else + done[path] = true + local config_files = find_files (path, conf_filter) + for _, filename in next, config_files do + if done[filename] then logreport ("log", 3, "db", - "Skipping file at %s, already included.", path) + "Skipping file at %s, already included.", filename) else - done[path] = true + done[filename] = true acc = read_fonts_conf_indeed(depth + 1, filename, home, @@ -340,7 +348,8 @@ read_fonts_conf_indeed = function (depth, acc, done, dirs_done, - find_files) + find_files, + basedir) end end end @@ -352,6 +361,7 @@ read_fonts_conf_indeed = function (depth, --inspect(done) return acc, done, dirs_done end --- read_fonts_conf_indeed() +-- /MK --[[doc-- read_fonts_conf() sets up an accumulator and two sets @@ -376,6 +386,7 @@ local read_fonts_conf = function (path_list, find_files) local done = { } ---> set: files inspected local dirs_done = { } ---> set: dirs in list for i=1, #path_list do --- we keep the state between files + -- MK Pass the base directory acc, done, dirs_done = read_fonts_conf_indeed(0, path_list[i], home, @@ -384,7 +395,9 @@ local read_fonts_conf = function (path_list, find_files) acc, done, dirs_done, - find_files) + find_files, + filedirname(path_list[i])) + -- /MK end return acc end diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua index 5571e947cf0..cb3cdd57811 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua @@ -1,85 +1,86 @@ return { ["hashes"]={ - { "fontloader-2017-02-11.lua", "c2db29b75e1838c08ff5867778f79cc3" }, - { "fontloader-util-fil.lua", "0573e91847e722ffe0fcdb465ba89e6b" }, - { "fontloader-util-str.lua", "513836cfea6a8027c1451a95bc74649f" }, - { "fontloader-swiglib-test.lua", "2f0b431faf53d3e19bfae22035bdc77e" }, - { "fontloader-swiglib.lua", "c20138e09bfd743dba7de1d5421be4af" }, - { "fontloader-preprocessor.lua", "f87963c22d5523218f1beee7e315bae5" }, - { "fontloader-mplib.lua", "55ad1c7febc72c6700b49692c2384738" }, - { "fontloader-math.lua", "f7eda9d5922e4aa7bb858ba9943fdacd" }, - { "fontloader-l-table.lua", "92978e5ea9ca13dae04fb606f5be4a1c" }, - { "fontloader-l-string.lua", "2ca01eeb225c09e73ff4f3a2d101827a" }, - { "fontloader-l-math.lua", "51275e81c652e3fbd7d02b1c383d3049" }, - { "fontloader-l-lua.lua", "5466b2db6d8cc3b9786a398bb4230ea1" }, - { "fontloader-l-lpeg.lua", "93b90b22d7d3600ddd9def9f3ad9a7bb" }, - { "fontloader-l-io.lua", "ee77e357f5965196f9559b8fcccc6e71" }, - { "fontloader-l-function.lua", "a7e68a9703c35238729da41a474e951b" }, - { "fontloader-l-file.lua", "32d9ba0a5cdd3509a3847bb050bfb800" }, + { "fontloader-2018-09-24.lua", "9b44571b829ca5276deffe1d1a9c8673" }, + { "fontloader-util-fil.lua", "258d23db3b135c06ceb87faecbe785b4" }, + { "fontloader-util-str.lua", "98fd6915ad05564aed39dff72102b6db" }, + { "fontloader-l-unicode.lua", "c8dd70435ced1bd05e257557a05b718b" }, + { "fontloader-l-math.lua", "082add08e0bb79f39191ae9534a79849" }, { "fontloader-l-boolean.lua", "abe28515dd33e8f6c416c09bca351cf8" }, - { "fontloader-languages.lua", "4e5bbdad336fa4969ac21a5242643a40" }, - { "fontloader-font-otc.lua", "85d63e257c748c624768aa7c8ec7f0bc" }, - { "fontloader-font-ocl.lua", "fbc00782e4efb24a7569f99cd1574ffb" }, - { "fontloader-font-osd.lua", "10ecd4b375680b011e7c6a25e5ad74f7" }, - { "fontloader-font-ots.lua", "7e1e55f9f728474372665e4a64a43f5a" }, - { "fontloader-font-ota.lua", "c281d18dfc89a8ca18af64f55e9fa92b" }, - { "fontloader-font-otj.lua", "5ea70db9f1990dc1459425853c79f663" }, - { "fontloader-font-oto.lua", "5fbdd899624d4eef639f81b580afe9aa" }, - { "fontloader-font-otl.lua", "2e7c8d9a331c46826211bd507f8e488a" }, - { "fontloader-font-oup.lua", "f7237130b648a4c2b477dabedc7f90e8" }, - { "fontloader-font-dsp.lua", "4a5266ada979d5c2d48867dc3ffaefea" }, - { "fontloader-font-ttf.lua", "e0893de6d0f3f421ee4386fa90429db8" }, - { "fontloader-font-cff.lua", "f3fc74e8629f7a2825c34a34550c790d" }, - { "fontloader-font-otr.lua", "2bd0085b78027f261218d63034f43474" }, - { "fontloader-font-tfm.lua", "3d813578dbf6c447e4b859c2bf0618f7" }, - { "fontloader-fonts-syn.lua", "6753dfb9a28aad35266284bb00072dca" }, + { "fontloader-l-file.lua", "7496b10d145575310f3888ee0b22d04d" }, + { "fontloader-l-io.lua", "acbb79a157e9d141379ed4708bef1c04" }, + { "fontloader-l-table.lua", "d021aef90825ed5844f26395ee16916c" }, + { "fontloader-l-string.lua", "ce0b99ddb7749540105b13450971ca7e" }, + { "fontloader-l-function.lua", "a7e68a9703c35238729da41a474e951b" }, + { "fontloader-l-lpeg.lua", "42ea6d8dd58797f4d9531c42fb3b6147" }, + { "fontloader-l-lua.lua", "a5deb6c4d9f8b0ca85740880eef75063" }, + { "fontloader-fonts-gbn.lua", "10ecdf01e7c926e5128ad8a9dff4d677" }, + { "fontloader-fonts-lig.lua", "fbd251eea3810a43a8d5542319361d68" }, + { "fontloader-fonts-ext.lua", "32013cbc5d5d336be8b1d1e5879d86c4" }, + { "fontloader-fonts-def.lua", "f435e0875f203f343157baeff876ec9c" }, + { "fontloader-fonts-enc.lua", "5ff4ca50493d7c4ecea0e15c203099f0" }, + { "fontloader-fonts-mis.lua", "f8d0788d2554d71720dc543cf4ffafe1" }, + { "fontloader-basics-nod.lua", "78f56219685f3145b9393c2b688aad94" }, + { "fontloader-font-imp-effects.lua", "3327181ef3db6f44dd234ad66ccc3f38" }, + { "fontloader-font-imp-italics.lua", "c2e6d6c4096a2c30d68fbffd3d7d58a7" }, + { "fontloader-font-imp-ligatures.lua", "26ffcf089391445f7af59536c8814364" }, + { "fontloader-font-imp-tex.lua", "ceb087ef6fa2f89aed7179f60ddf8f35" }, + { "fontloader-font-def.lua", "8e2cb2552bf02246da2ac43334b91795" }, { "fontloader-font-lua.lua", "1fbfdf7b689b2bdfd0e3bb9bf74ce136" }, - { "fontloader-fonts.lua", "96e568c9b2ffa8108fb95d886bb2b645" }, - { "fontloader-fonts-ext.lua", "aff3846f4c1f15de0a9f4fd7081e0c68" }, - { "fontloader-fonts-enc.lua", "a7ace7c1969cd64a5ca9888838f3edb6" }, - { "fontloader-fonts-demo-vf-1.lua", "7a4cb26eda4f1090d195114c10a33a75" }, - { "fontloader-font-def.lua", "49fa2b50d8d2a1bb70b08b72f858ecd0" }, - { "fontloader-font-gbn.lua", "850f31ba73ff8de96371d0aed2b2b4cb" }, - { "fontloader-font-oti.lua", "8f48c06a1d632febd7231ad5dfadfc53" }, - { "fontloader-font-map.lua", "8708bde7467785c4d3b7afdaf2f9333a" }, - { "fontloader-font-ini.lua", "10cb9a563a98e06ff79c35a8751e13dc" }, - { "fontloader-font-def.lua", "49fa2b50d8d2a1bb70b08b72f858ecd0" }, - { "fontloader-font-con.lua", "7575a7b4e6d04816072945e27d7d0b33" }, - { "fontloader-font-cid.lua", "52421d1fdaa07ec4b1d936c6ff5079be" }, - { "fontloader-font-one.lua", "6fbf6b9e219a944cd1ad5933d77cc488" }, - { "fontloader-font-onr.lua", "205c8bc640715aecf3538a33b842f450" }, - { "fontloader-font-afk.lua", "b36a76ceb835f41f8c05b471000ddc14" }, + { "fontloader-font-tfm.lua", "c9797c1b4ccb8fdb7f041e19207109a2" }, + { "fontloader-font-afk.lua", "9da14e0fb22129c053acc599d1312544" }, + { "fontloader-font-one.lua", "52588c7c3aab714d95d03e91ee86e1ad" }, + { "fontloader-font-onr.lua", "66a0c352b5c36b9974aab1b5026152f2" }, + { "fontloader-font-otc.lua", "9dcd93d83bc61c90f0fb6161a1911a55" }, + { "fontloader-font-ocl.lua", "49cf3230228aa7f2d19cd491f55f5395" }, + { "fontloader-font-osd.lua", "d2b542031aa693bb423b6d3272820c9a" }, + { "fontloader-font-ots.lua", "624f23e63b7430a51212c8824d09924f" }, + { "fontloader-font-ota.lua", "d883cf661bb8c27bfa9b7cc66420c1ed" }, + { "fontloader-font-otj.lua", "578448ae37a167319dbccd5af04738da" }, + { "fontloader-font-oto.lua", "d78a6165b7ff6c21f4c3c4c3c0553648" }, + { "fontloader-font-otl.lua", "fcc55ae6cff5fd55b5054f923c21cafa" }, + { "fontloader-font-oup.lua", "020f2630f5a18d39e209f1cbf7a334ea" }, + { "fontloader-font-dsp.lua", "9cade9beb226c61b4323729aca1f4a80" }, + { "fontloader-font-ttf.lua", "ff587471f4297aa8ba0fa022609adc6e" }, + { "fontloader-font-cff.lua", "eefa156275ba0ac91aa651a944d35978" }, + { "fontloader-font-ott.lua", "a4dd9bc516bdb7669ee61d5207f899c6" }, + { "fontloader-font-oti.lua", "309a75f9c14b77d87e94eba827dc4e71" }, + { "fontloader-font-otr.lua", "4a1dae571a43d7cb8afce7e906df89c6" }, + { "fontloader-font-vfc.lua", "237aff1862009b7850653c2098473bd4" }, + { "fontloader-font-map.lua", "2c03dc61afabb5887ef18a88e6ec86bd" }, + { "fontloader-font-cid.lua", "22b0367742fb253deef84ef7ccf5e8de" }, + { "fontloader-font-con.lua", "3218bd21a24c2abb13b284085da8ced4" }, + { "fontloader-font-ini.lua", "6f615d3bc72fb29f409ed2b8f6364c35" }, { "fontloader-data-con.lua", "d8982c834ed9acc6193eee23067b9d5d" }, - { "fontloader-basics-nod.lua", "9288471b8395bfb683aba0ff3964d950" }, - { "fontloader-reference.lua", "12efa565c640e8583a8fde380dc0e1a0" }, - { "fontloader-basics-gen.lua", "b6c8f256b6c6d3350bf5bad2034857a0" }, - { "mktests", "918cb50be9ee8bd645ac1a27dc501e8c" }, - { "mkstatus", "d66d9237a9e18e64732f239e49e77633" }, - { "mkimport", "a90dbe00a99c7e77494b07fbe0e669af" }, - { "mkglyphlist", "b573c7a7645e5b84e776d7d0bb06c559" }, - { "mkcharacters", "1db01d8172c49c8a405897dc45340917" }, + { "fontloader-reference.lua", "78d6781759aa7a701282370a9c02bbb5" }, + { "fontloader-basics-gen.lua", "a1e8c7de52ec6a8afcce3b2105f0d355" }, + { "mktests", "c7cff7025962922944376acc1ffa9c47" }, + { "mkstatus", "66bd373bfc704d78084cfe95a6b9c192" }, + { "mkcharacters", "42bb2bec362979ffdccce9be69acc5fb" }, + { "mkglyphlist", "2ff6dcdec67b4853c6ad102a16fa8539" }, + { "mkimport", "2540d1920712e0b3777d0dc0aec52729" }, { "luaotfload-glyphlist.lua", "bcf89e1441be9f4ad29c0cf3a076bcea" }, - { "luaotfload-characters.lua", "99578c3eff8b68cae3da45a4a7cbfcf2" }, - { "luaotfload-tool.lua", "e6ca512e0620ccaac968ccc17239ee39" }, - { "luaotfload-resolvers.lua", "bcdde7940630c4b563f3884b64ae3c84" }, - { "luaotfload-parsers.lua", "0f40bde1938a5cb72b75fccbbaa43d75" }, - { "luaotfload-main.lua", "8411e662c9867c423dc0229b12eb174c" }, - { "luaotfload-log.lua", "cc79197903756cde068d3c197844be99" }, - { "luaotfload-loaders.lua", "26fef8c4436e02b03deab714716a0948" }, - { "luaotfload-letterspace.lua", "0f4bd9bc0365e96f099e68f4a8114d3e" }, - { "luaotfload-init.lua", "e5c4c96fc7e3bf150229013fb56e77b5" }, - { "luaotfload-features.lua", "dc9dcfb374577b47dafac01e19935050" }, - { "luaotfload-diagnostics.lua", "7b35c9f91e3e73fc5a61dbfe1f0e7ad9" }, - { "luaotfload-database.lua", "62fee5ba6afb4ee38aa1a2b183d59ff8" }, - { "luaotfload-configuration.lua", "64fa81f2f8fe6815f109873fb27f78aa" }, - { "luaotfload-colors.lua", "2582a42a68739087a72e5ea6440cf0d8" }, - { "luaotfload-auxiliary.lua", "a6c092c006660ee6e6fbdcddf104a99b" }, + { "luaotfload-characters.lua", "910152da2c9ae773c539284ac6cb0a90" }, + { "luaotfload-resolvers.lua", "6cd52af62c4726ba8fce657376de4579" }, + { "luaotfload-parsers.lua", "c6e627e3b157fc4004f7c9e715c01822" }, + { "luaotfload-loaders.lua", "a2bdea2fc644fbc31efec7377312b282" }, + { "luaotfload-letterspace.lua", "078b211a668ac6943141442b84062354" }, + { "luaotfload-features.lua", "14041df7943fb33d0d2493b14cf95706" }, + { "luaotfload-database.lua", "f4ee22158855438aa8a29623552ed82c" }, + { "luaotfload-configuration.lua", "12e69f5d83340c8fb810c590f16f60cf" }, + { "luaotfload-colors.lua", "d494389674e7fe39678f4819c7bd0d00" }, + { "luaotfload-auxiliary.lua", "6788b0f763d759933de8c1d493421210" }, + { "luaotfload-filelist.lua", "dae5349af86ec3b471f9a57eb6b5631b" }, + { "luaotfload-tool.lua", "e3cc9e50fb9fc5ce3428c0c8ac72bf25" }, + { "luaotfload-diagnostics.lua", "1ff35d5ef3adf4e9d92ee3f979672b9c" }, + { "luaotfload-log.lua", "7b9e8dd90047a06a8840d662f2b63739" }, + { "luaotfload-init.lua", "63d9efafb1a9f659f7f2cce355022899" }, + { "luaotfload-main.lua", "c4784393b85fb082a100ffaf100d5e74" }, }, ["notes"]={ - ["committer"]="Philipp Gesang ", - ["description"]="v2.8-fix-2", - ["loader"]="fontloader-2017-02-11.lua", - ["revision"]="ad480924393fffa2896156e1a32c22f5c61120dd", - ["timestamp"]="2017-02-11 00:21:52 +0100", + ["committer"]="Ulrike Fischer ", + ["description"]="2.9", + ["loader"]="fontloader-2018-09-24.lua", + ["revision"]="f9f554b2305fba2bc075b84380321104161e230d", + ["timestamp"]="2018-09-25 16:34:44 +0200", }, } \ No newline at end of file diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty index 45744d15053..312771474f0 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty @@ -1,4 +1,4 @@ -%% Copyright (C) 2009-2017 +%% Copyright (C) 2009-2018 %% %% by Elie Roux %% and Khaled Hosny @@ -8,7 +8,8 @@ %% %% This file is part of Luaotfload. %% -%% Home: https://github.com/lualatex/luaotfload +%% Home: https://github.com/lualatex/luaotfload +%%Current home: https://github.com/u-fischer/luaotfload %% Support: . %% %% Luaotfload is under the GPL v2.0 (exactly) license. @@ -41,10 +42,9 @@ \ProvidesPackage{luaotfload}% %% FIXME The date is meaningless, we need to find a way to %% use the git revision instead. - [2017/01/29 v2.8 OpenType layout system] + [2018/09/24 v2.9 OpenType layout system] \fi \directlua{ require('luaotfload-main') local _void = luaotfload.main () } - -- cgit v1.2.3