diff options
-rw-r--r-- | luaotfload.dtx | 104 | ||||
-rw-r--r-- | otfl-data-con.lua | 13 | ||||
-rw-r--r-- | otfl-font-age.lua | 3743 | ||||
-rw-r--r-- | otfl-font-cid.lua | 48 | ||||
-rw-r--r-- | otfl-font-clr.lua | 6 | ||||
-rw-r--r-- | otfl-font-def.lua | 467 | ||||
-rw-r--r-- | otfl-font-dum.lua | 202 | ||||
-rw-r--r-- | otfl-font-ini.lua | 66 | ||||
-rw-r--r-- | otfl-font-ltx.lua (renamed from otfl-font-xtx.lua) | 83 | ||||
-rw-r--r-- | otfl-font-lua.lua | 45 | ||||
-rw-r--r-- | otfl-font-map.lua | 186 | ||||
-rw-r--r-- | otfl-font-ota.lua | 62 | ||||
-rw-r--r-- | otfl-font-otb.lua | 59 | ||||
-rw-r--r-- | otfl-font-otc.lua | 173 | ||||
-rw-r--r-- | otfl-font-otd.lua | 42 | ||||
-rw-r--r-- | otfl-font-otf.lua | 2016 | ||||
-rw-r--r-- | otfl-font-oti.lua | 37 | ||||
-rw-r--r-- | otfl-font-otn.lua | 342 | ||||
-rw-r--r-- | otfl-font-ott.lua | 160 | ||||
-rw-r--r-- | otfl-font-tfm.lua | 302 | ||||
-rw-r--r-- | otfl-luat-dum.lua | 80 | ||||
-rw-r--r-- | otfl-node-dum.lua | 85 | ||||
-rw-r--r-- | otfl-node-inj.lua | 98 | ||||
-rwxr-xr-x | sync.sh | 9 |
24 files changed, 6198 insertions, 2230 deletions
diff --git a/luaotfload.dtx b/luaotfload.dtx index 648a0b3..36f403f 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -405,36 +405,36 @@ and the derived files % % \begin{multicols}{3} % \begin{itemize*} -% \item |luat-dum.lua| % \item |data-con.lua| -% \item |node-inj.lua| -% \item |node-dum.lua| -% \item |font-ini.lua| -% \item |font-tfm.lua| +% \item |font-age.lua| % \item |font-cid.lua| -% \item |font-ott.lua| -% \item |font-otf.lua| +% \item |font-def.lua| +% \item |font-dum.lua| +% \item |font-ini.lua| +% \item |font-lua.lua| +% \item |font-map.lua| +% \item |font-ota.lua| +% \item |font-otb.lua| +% \item |font-otc.lua| % \item |font-otd.lua| +% \item |font-otf.lua| % \item |font-oti.lua| -% \item |font-otb.lua| % \item |font-otn.lua| -% \item |font-ota.lua| -% \item |font-otc.lua| -% \item |font-def.lua| -% \item |font-xtx.lua| -% \item |font-map.lua| -% \item |font-dum.lua| +% \item |font-ott.lua| +% \item |font-tfm.lua| +% \item |luat-dum.lua| +% \item |node-dum.lua| +% \item |node-inj.lua| % \end{itemize*} % \end{multicols} % % The following files have been written for this package: -% \begin{multicols}{3} % \begin{itemize*} % \item |font-clr.lua| % \item |font-nms.lua| % \item |luat-ovr.lua| +% \item |font-ltx.lua|, heavily modified version of |font-xtx.lua|. % \end{itemize*} -% \end{multicols} % % \section{Troubleshooting} % @@ -496,6 +496,23 @@ if tex.luatexversion < luatex_version then end % \end{macrocode} % +% +% \begin{macrocode} +function table.reversed(t) + if t then + local tt, tn = { }, #t + if tn > 0 then + local ttn = 0 + for i=tn,1,-1 do + ttn = ttn + 1 + tt[ttn] = t[i] + end + end + return tt + end +end +% \end{macrocode} +% % \subsection{Module loading} % % We load the \context files with this function. It automatically adds the @@ -535,9 +552,8 @@ tex.attribute[0] = 0 % Node support modules. % % \begin{macrocode} -luaotfload.loadmodule("font-ini.lua") -luaotfload.loadmodule("node-dum.lua") -luaotfload.loadmodule("node-inj.lua") +luaotfload.loadmodule('node-dum.lua') +luaotfload.loadmodule('node-inj.lua') % \end{macrocode} % % By default \context takes some private attributes for internal use. To @@ -560,20 +576,23 @@ end % Font handling modules. % % \begin{macrocode} -luaotfload.loadmodule("font-tfm.lua") -luaotfload.loadmodule("font-cid.lua") -luaotfload.loadmodule("font-ott.lua") -luaotfload.loadmodule("font-map.lua") -luaotfload.loadmodule("font-otf.lua") -luaotfload.loadmodule("font-otd.lua") -luaotfload.loadmodule("font-oti.lua") -luaotfload.loadmodule("font-otb.lua") -luaotfload.loadmodule("font-otn.lua") -luaotfload.loadmodule("font-ota.lua") -luaotfload.loadmodule("font-otc.lua") -luaotfload.loadmodule("font-def.lua") -luaotfload.loadmodule("font-xtx.lua") -luaotfload.loadmodule("font-dum.lua") +luaotfload.loadmodule('font-ini.lua') +luaotfload.loadmodule('font-tfm.lua') +luaotfload.loadmodule('font-cid.lua') +luaotfload.loadmodule('font-ott.lua') +luaotfload.loadmodule('font-map.lua') +luaotfload.loadmodule('font-lua.lua') +luaotfload.loadmodule('font-otf.lua') +luaotfload.loadmodule('font-otd.lua') +luaotfload.loadmodule('font-oti.lua') +luaotfload.loadmodule('font-otb.lua') +luaotfload.loadmodule('font-otn.lua') +luaotfload.loadmodule('font-ota.lua') +luaotfload.loadmodule('font-otc.lua') +luaotfload.loadmodule('font-age.lua') +luaotfload.loadmodule('font-def.lua') +luaotfload.loadmodule('font-ltx.lua') +luaotfload.loadmodule('font-dum.lua') % \end{macrocode} % % This is a patch for |otfl-font-def.lua|, that defines a reader for ofm @@ -608,7 +627,7 @@ luatexbase.create_callback("luaotfload.patch_font", "simple", function() end) % % \begin{macrocode} local function def_font(...) - local fontdata = fonts.define.read(...) + local fontdata = fonts.definers.read(...) if type(fontdata) == "table" and fontdata.shared then % \end{macrocode} % @@ -652,23 +671,6 @@ end fonts.mode = "node" % \end{macrocode} % -% The following features are useful in math (e.g. in XITS Math font), -% but \textsf{luaotfload} does not recognize them in |base| mode. -% -% \begin{macrocode} -local register_base_sub = fonts.otf.features.register_base_substitution -local gsubs = { - "ss01", "ss02", "ss03", "ss04", "ss05", - "ss06", "ss07", "ss08", "ss09", "ss10", - "ss11", "ss12", "ss13", "ss14", "ss15", - "ss16", "ss17", "ss18", "ss19", "ss20", -} - -for _,v in next, gsubs do - register_base_sub(v) -end -% \end{macrocode} -% % Finally we register the callbacks % % \begin{macrocode} diff --git a/otfl-data-con.lua b/otfl-data-con.lua index e7bb8af..ed4f2de 100644 --- a/otfl-data-con.lua +++ b/otfl-data-con.lua @@ -25,13 +25,15 @@ table structures without bothering about the disk cache.</p> <p>Examples of usage can be found in the font related code.</p> --ldx]]-- -containers = containers or { } - +containers = containers or { } +local containers = containers containers.usecache = true +local report_containers = logs.reporter("resolvers","containers") + local function report(container,tag,name) if trace_cache or trace_containers then - logs.report(format("%s cache",container.subcategory),"%s: %s",tag,name or 'invalid') + report_containers("container: %s, tag: %s, name: %s",container.subcategory,tag,name or 'invalid') end end @@ -48,7 +50,8 @@ local mt = { t.readables = readables return readables end - end + end, + __storage__ = true } function containers.define(category, subcategory, version, enabled) @@ -78,7 +81,7 @@ function containers.define(category, subcategory, version, enabled) end function containers.is_usable(container, name) - return container.enabled and caches and caches.iswritable(container.writable, name) + return container.enabled and caches and caches.is_writable(container.writable, name) end function containers.is_valid(container, name) diff --git a/otfl-font-age.lua b/otfl-font-age.lua new file mode 100644 index 0000000..5c19d41 --- /dev/null +++ b/otfl-font-age.lua @@ -0,0 +1,3743 @@ +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 = "derived from http://www.adobe.com/devnet/opentype/archives/glyphlist.txt", + original = "Adobe Glyph List, version 2.0, September 20, 2002", +} + +fonts = fonts or { } +fonts.enc = fonts.enc or { } +fonts.enc.agl = fonts.enc.agl or { } + +fonts.enc.agl.unicodes = { -- generated + ["A"]=65, + ["AE"]=198, + ["AEacute"]=508, + ["AEmacron"]=482, + ["Aacute"]=193, + ["Abreve"]=258, + ["Abreveacute"]=7854, + ["Abrevecyrillic"]=1232, + ["Abrevedotbelow"]=7862, + ["Abrevegrave"]=7856, + ["Abrevehookabove"]=7858, + ["Abrevetilde"]=7860, + ["Acaron"]=461, + ["Acircle"]=9398, + ["Acircumflex"]=194, + ["Acircumflexacute"]=7844, + ["Acircumflexdotbelow"]=7852, + ["Acircumflexgrave"]=7846, + ["Acircumflexhookabove"]=7848, + ["Acircumflextilde"]=7850, + ["Adblgrave"]=512, + ["Adieresis"]=196, + ["Adieresiscyrillic"]=1234, + ["Adieresismacron"]=478, + ["Adotbelow"]=7840, + ["Adotmacron"]=480, + ["Agrave"]=192, + ["Ahookabove"]=7842, + ["Aiecyrillic"]=1236, + ["Ainvertedbreve"]=514, + ["Alpha"]=913, + ["Alphatonos"]=902, + ["Amacron"]=256, + ["Amonospace"]=65313, + ["Aogonek"]=260, + ["Aring"]=197, + ["Aringacute"]=506, + ["Aringbelow"]=7680, + ["Atilde"]=195, + ["Aybarmenian"]=1329, + ["B"]=66, + ["Bcircle"]=9399, + ["Bdotaccent"]=7682, + ["Bdotbelow"]=7684, + ["Benarmenian"]=1330, + ["Beta"]=914, + ["Bhook"]=385, + ["Blinebelow"]=7686, + ["Bmonospace"]=65314, + ["Btopbar"]=386, + ["C"]=67, + ["Caarmenian"]=1342, + ["Cacute"]=262, + ["Ccaron"]=268, + ["Ccedilla"]=199, + ["Ccedillaacute"]=7688, + ["Ccircle"]=9400, + ["Ccircumflex"]=264, + ["Cdotaccent"]=266, + ["Chaarmenian"]=1353, + ["Cheabkhasiancyrillic"]=1212, + ["Chedescenderabkhasiancyrillic"]=1214, + ["Chedescendercyrillic"]=1206, + ["Chedieresiscyrillic"]=1268, + ["Cheharmenian"]=1347, + ["Chekhakassiancyrillic"]=1227, + ["Cheverticalstrokecyrillic"]=1208, + ["Chi"]=935, + ["Chook"]=391, + ["Cmonospace"]=65315, + ["Coarmenian"]=1361, + ["D"]=68, + ["DZ"]=497, + ["DZcaron"]=452, + ["Daarmenian"]=1332, + ["Dafrican"]=393, + ["Dcaron"]=270, + ["Dcedilla"]=7696, + ["Dcircle"]=9401, + ["Dcircumflexbelow"]=7698, + ["Ddotaccent"]=7690, + ["Ddotbelow"]=7692, + ["Deicoptic"]=1006, + ["Deltagreek"]=916, + ["Dhook"]=394, + ["Digammagreek"]=988, + ["Dlinebelow"]=7694, + ["Dmonospace"]=65316, + ["Dslash"]=272, + ["Dtopbar"]=395, + ["Dz"]=498, + ["Dzcaron"]=453, + ["Dzeabkhasiancyrillic"]=1248, + ["E"]=69, + ["Eacute"]=201, + ["Ebreve"]=276, + ["Ecaron"]=282, + ["Ecedillabreve"]=7708, + ["Echarmenian"]=1333, + ["Ecircle"]=9402, + ["Ecircumflex"]=202, + ["Ecircumflexacute"]=7870, + ["Ecircumflexbelow"]=7704, + ["Ecircumflexdotbelow"]=7878, + ["Ecircumflexgrave"]=7872, + ["Ecircumflexhookabove"]=7874, + ["Ecircumflextilde"]=7876, + ["Edblgrave"]=516, + ["Edieresis"]=203, + ["Edotaccent"]=278, + ["Edotbelow"]=7864, + ["Egrave"]=200, + ["Eharmenian"]=1335, + ["Ehookabove"]=7866, + ["Eightroman"]=8551, + ["Einvertedbreve"]=518, + ["Eiotifiedcyrillic"]=1124, + ["Elevenroman"]=8554, + ["Emacron"]=274, + ["Emacronacute"]=7702, + ["Emacrongrave"]=7700, + ["Emonospace"]=65317, + ["Endescendercyrillic"]=1186, + ["Eng"]=330, + ["Enghecyrillic"]=1188, + ["Enhookcyrillic"]=1223, + ["Eogonek"]=280, + ["Eopen"]=400, + ["Epsilon"]=917, + ["Epsilontonos"]=904, + ["Ereversed"]=398, + ["Esdescendercyrillic"]=1194, + ["Esh"]=425, + ["Eta"]=919, + ["Etarmenian"]=1336, + ["Etatonos"]=905, + ["Eth"]=208, + ["Etilde"]=7868, + ["Etildebelow"]=7706, + ["Ezh"]=439, + ["Ezhcaron"]=494, + ["Ezhreversed"]=440, + ["F"]=70, + ["Fcircle"]=9403, + ["Fdotaccent"]=7710, + ["Feharmenian"]=1366, + ["Feicoptic"]=996, + ["Fhook"]=401, + ["Fiveroman"]=8548, + ["Fmonospace"]=65318, + ["Fourroman"]=8547, + ["G"]=71, + ["GBsquare"]=13191, + ["Gacute"]=500, + ["Gamma"]=915, + ["Gammaafrican"]=404, + ["Gangiacoptic"]=1002, + ["Gbreve"]=286, + ["Gcaron"]=486, + ["Gcircle"]=9404, + ["Gcircumflex"]=284, + ["Gcommaaccent"]=290, + ["Gdotaccent"]=288, + ["Ghadarmenian"]=1346, + ["Ghemiddlehookcyrillic"]=1172, + ["Ghestrokecyrillic"]=1170, + ["Ghook"]=403, + ["Gimarmenian"]=1331, + ["Gmacron"]=7712, + ["Gmonospace"]=65319, + ["Gsmallhook"]=667, + ["Gstroke"]=484, + ["H"]=72, + ["HPsquare"]=13259, + ["Haabkhasiancyrillic"]=1192, + ["Hadescendercyrillic"]=1202, + ["Hbar"]=294, + ["Hbrevebelow"]=7722, + ["Hcedilla"]=7720, + ["Hcircle"]=9405, + ["Hcircumflex"]=292, + ["Hdieresis"]=7718, + ["Hdotaccent"]=7714, + ["Hdotbelow"]=7716, + ["Hmonospace"]=65320, + ["Hoarmenian"]=1344, + ["Horicoptic"]=1000, + ["Hzsquare"]=13200, + ["I"]=73, + ["IJ"]=306, + ["Iacute"]=205, + ["Ibreve"]=300, + ["Icaron"]=463, + ["Icircle"]=9406, + ["Icircumflex"]=206, + ["Idblgrave"]=520, + ["Idieresis"]=207, + ["Idieresisacute"]=7726, + ["Idieresiscyrillic"]=1252, + ["Idotaccent"]=304, + ["Idotbelow"]=7882, + ["Iebrevecyrillic"]=1238, + ["Ifraktur"]=8465, + ["Igrave"]=204, + ["Ihookabove"]=7880, + ["Iinvertedbreve"]=522, + ["Imacron"]=298, + ["Imacroncyrillic"]=1250, + ["Imonospace"]=65321, + ["Iniarmenian"]=1339, + ["Iogonek"]=302, + ["Iota"]=921, + ["Iotaafrican"]=406, + ["Iotadieresis"]=938, + ["Iotatonos"]=906, + ["Istroke"]=407, + ["Itilde"]=296, + ["Itildebelow"]=7724, + ["Izhitsadblgravecyrillic"]=1142, + ["J"]=74, + ["Jaarmenian"]=1345, + ["Jcircle"]=9407, + ["Jcircumflex"]=308, + ["Jheharmenian"]=1355, + ["Jmonospace"]=65322, + ["K"]=75, + ["KBsquare"]=13189, + ["KKsquare"]=13261, + ["Kabashkircyrillic"]=1184, + ["Kacute"]=7728, + ["Kadescendercyrillic"]=1178, + ["Kahookcyrillic"]=1219, + ["Kappa"]=922, + ["Kastrokecyrillic"]=1182, + ["Kaverticalstrokecyrillic"]=1180, + ["Kcaron"]=488, + ["Kcircle"]=9408, + ["Kcommaaccent"]=310, + ["Kdotbelow"]=7730, + ["Keharmenian"]=1364, + ["Kenarmenian"]=1343, + ["Kheicoptic"]=998, + ["Khook"]=408, + ["Klinebelow"]=7732, + ["Kmonospace"]=65323, + ["Koppacyrillic"]=1152, + ["Koppagreek"]=990, + ["Ksicyrillic"]=1134, + ["L"]=76, + ["LJ"]=455, + ["Lacute"]=313, + ["Lambda"]=923, + ["Lcaron"]=317, + ["Lcircle"]=9409, + ["Lcircumflexbelow"]=7740, + ["Lcommaaccent"]=315, + ["Ldotaccent"]=319, + ["Ldotbelow"]=7734, + ["Ldotbelowmacron"]=7736, + ["Liwnarmenian"]=1340, + ["Lj"]=456, + ["Llinebelow"]=7738, + ["Lmonospace"]=65324, + ["Lslash"]=321, + ["M"]=77, + ["MBsquare"]=13190, + ["Macute"]=7742, + ["Mcircle"]=9410, + ["Mdotaccent"]=7744, + ["Mdotbelow"]=7746, + ["Menarmenian"]=1348, + ["Mmonospace"]=65325, + ["Mturned"]=412, + ["Mu"]=924, + ["N"]=78, + ["NJ"]=458, + ["Nacute"]=323, + ["Ncaron"]=327, + ["Ncircle"]=9411, + ["Ncircumflexbelow"]=7754, + ["Ncommaaccent"]=325, + ["Ndotaccent"]=7748, + ["Ndotbelow"]=7750, + ["Nhookleft"]=413, + ["Nineroman"]=8552, + ["Nj"]=459, + ["Nlinebelow"]=7752, + ["Nmonospace"]=65326, + ["Nowarmenian"]=1350, + ["Ntilde"]=209, + ["Nu"]=925, + ["O"]=79, + ["OE"]=338, + ["Oacute"]=211, + ["Obarredcyrillic"]=1256, + ["Obarreddieresiscyrillic"]=1258, + ["Obreve"]=334, + ["Ocaron"]=465, + ["Ocenteredtilde"]=415, + ["Ocircle"]=9412, + ["Ocircumflex"]=212, + ["Ocircumflexacute"]=7888, + ["Ocircumflexdotbelow"]=7896, + ["Ocircumflexgrave"]=7890, + ["Ocircumflexhookabove"]=7892, + ["Ocircumflextilde"]=7894, + ["Odblgrave"]=524, + ["Odieresis"]=214, + ["Odieresiscyrillic"]=1254, + ["Odotbelow"]=7884, + ["Ograve"]=210, + ["Oharmenian"]=1365, + ["Ohookabove"]=7886, + ["Ohorn"]=416, + ["Ohornacute"]=7898, + ["Ohorndotbelow"]=7906, + ["Ohorngrave"]=7900, + ["Ohornhookabove"]=7902, + ["Ohorntilde"]=7904, + ["Ohungarumlaut"]=336, + ["Oi"]=418, + ["Oinvertedbreve"]=526, + ["Omacron"]=332, + ["Omacronacute"]=7762, + ["Omacrongrave"]=7760, + ["Omega"]=8486, + ["Omegacyrillic"]=1120, + ["Omegagreek"]=937, + ["Omegaroundcyrillic"]=1146, + ["Omegatitlocyrillic"]=1148, + ["Omegatonos"]=911, + ["Omicron"]=927, + ["Omicrontonos"]=908, + ["Omonospace"]=65327, + ["Oneroman"]=8544, + ["Oogonek"]=490, + ["Oogonekmacron"]=492, + ["Oopen"]=390, + ["Oslash"]=216, + ["Ostrokeacute"]=510, + ["Otcyrillic"]=1150, + ["Otilde"]=213, + ["Otildeacute"]=7756, + ["Otildedieresis"]=7758, + ["P"]=80, + ["Pacute"]=7764, + ["Pcircle"]=9413, + ["Pdotaccent"]=7766, + ["Peharmenian"]=1354, + ["Pemiddlehookcyrillic"]=1190, + ["Phi"]=934, + ["Phook"]=420, + ["Pi"]=928, + ["Piwrarmenian"]=1363, + ["Pmonospace"]=65328, + ["Psi"]=936, + ["Psicyrillic"]=1136, + ["Q"]=81, + ["Qcircle"]=9414, + ["Qmonospace"]=65329, + ["R"]=82, + ["Raarmenian"]=1356, + ["Racute"]=340, + ["Rcaron"]=344, + ["Rcircle"]=9415, + ["Rcommaaccent"]=342, + ["Rdblgrave"]=528, + ["Rdotaccent"]=7768, + ["Rdotbelow"]=7770, + ["Rdotbelowmacron"]=7772, + ["Reharmenian"]=1360, + ["Rfraktur"]=8476, + ["Rho"]=929, + ["Rinvertedbreve"]=530, + ["Rlinebelow"]=7774, + ["Rmonospace"]=65330, + ["Rsmallinverted"]=641, + ["Rsmallinvertedsuperior"]=694, + ["S"]=83, + ["SF010000"]=9484, + ["SF020000"]=9492, + ["SF030000"]=9488, + ["SF040000"]=9496, + ["SF050000"]=9532, + ["SF060000"]=9516, + ["SF070000"]=9524, + ["SF080000"]=9500, + ["SF090000"]=9508, + ["SF100000"]=9472, + ["SF110000"]=9474, + ["SF190000"]=9569, + ["SF200000"]=9570, + ["SF210000"]=9558, + ["SF220000"]=9557, + ["SF230000"]=9571, + ["SF240000"]=9553, + ["SF250000"]=9559, + ["SF260000"]=9565, + ["SF270000"]=9564, + ["SF280000"]=9563, + ["SF360000"]=9566, + ["SF370000"]=9567, + ["SF380000"]=9562, + ["SF390000"]=9556, + ["SF400000"]=9577, + ["SF410000"]=9574, + ["SF420000"]=9568, + ["SF430000"]=9552, + ["SF440000"]=9580, + ["SF450000"]=9575, + ["SF460000"]=9576, + ["SF470000"]=9572, + ["SF480000"]=9573, + ["SF490000"]=9561, + ["SF500000"]=9560, + ["SF510000"]=9554, + ["SF520000"]=9555, + ["SF530000"]=9579, + ["SF540000"]=9578, + ["Sacute"]=346, + ["Sacutedotaccent"]=7780, + ["Sampigreek"]=992, + ["Scaron"]=352, + ["Scarondotaccent"]=7782, + ["Scedilla"]=350, + ["Schwa"]=399, + ["Schwacyrillic"]=1240, + ["Schwadieresiscyrillic"]=1242, + ["Scircle"]=9416, + ["Scircumflex"]=348, + ["Scommaaccent"]=536, + ["Sdotaccent"]=7776, + ["Sdotbelow"]=7778, + ["Sdotbelowdotaccent"]=7784, + ["Seharmenian"]=1357, + ["Sevenroman"]=8550, + ["Shaarmenian"]=1351, + ["Sheicoptic"]=994, + ["Shhacyrillic"]=1210, + ["Shimacoptic"]=1004, + ["Sigma"]=931, + ["Sixroman"]=8549, + ["Smonospace"]=65331, + ["Stigmagreek"]=986, + ["T"]=84, + ["Tau"]=932, + ["Tbar"]=358, + ["Tcaron"]=356, + ["Tcircle"]=9417, + ["Tcircumflexbelow"]=7792, + ["Tcommaaccent"]=354, + ["Tdotaccent"]=7786, + ["Tdotbelow"]=7788, + ["Tedescendercyrillic"]=1196, + ["Tenroman"]=8553, + ["Tetsecyrillic"]=1204, + ["Theta"]=920, + ["Thook"]=428, + ["Thorn"]=222, + ["Threeroman"]=8546, + ["Tiwnarmenian"]=1359, + ["Tlinebelow"]=7790, + ["Tmonospace"]=65332, + ["Toarmenian"]=1337, + ["Tonefive"]=444, + ["Tonesix"]=388, + ["Tonetwo"]=423, + ["Tretroflexhook"]=430, + ["Twelveroman"]=8555, + ["Tworoman"]=8545, + ["U"]=85, + ["Uacute"]=218, + ["Ubreve"]=364, + ["Ucaron"]=467, + ["Ucircle"]=9418, + ["Ucircumflex"]=219, + ["Ucircumflexbelow"]=7798, + ["Udblgrave"]=532, + ["Udieresis"]=220, + ["Udieresisacute"]=471, + ["Udieresisbelow"]=7794, + ["Udieresiscaron"]=473, + ["Udieresiscyrillic"]=1264, + ["Udieresisgrave"]=475, + ["Udieresismacron"]=469, + ["Udotbelow"]=7908, + ["Ugrave"]=217, + ["Uhookabove"]=7910, + ["Uhorn"]=431, + ["Uhornacute"]=7912, + ["Uhorndotbelow"]=7920, + ["Uhorngrave"]=7914, + ["Uhornhookabove"]=7916, + ["Uhorntilde"]=7918, + ["Uhungarumlaut"]=368, + ["Uhungarumlautcyrillic"]=1266, + ["Uinvertedbreve"]=534, + ["Ukcyrillic"]=1144, + ["Umacron"]=362, + ["Umacroncyrillic"]=1262, + ["Umacrondieresis"]=7802, + ["Umonospace"]=65333, + ["Uogonek"]=370, + ["Upsilon"]=933, + ["Upsilonacutehooksymbolgreek"]=979, + ["Upsilonafrican"]=433, + ["Upsilondieresis"]=939, + ["Upsilondieresishooksymbolgreek"]=980, + ["Upsilonhooksymbol"]=978, + ["Upsilontonos"]=910, + ["Uring"]=366, + ["Ustraightcyrillic"]=1198, + ["Ustraightstrokecyrillic"]=1200, + ["Utilde"]=360, + ["Utildeacute"]=7800, + ["Utildebelow"]=7796, + ["V"]=86, + ["Vcircle"]=9419, + ["Vdotbelow"]=7806, + ["Vewarmenian"]=1358, + ["Vhook"]=434, + ["Vmonospace"]=65334, + ["Voarmenian"]=1352, + ["Vtilde"]=7804, + ["W"]=87, + ["Wacute"]=7810, + ["Wcircle"]=9420, + ["Wcircumflex"]=372, + ["Wdieresis"]=7812, + ["Wdotaccent"]=7814, + ["Wdotbelow"]=7816, + ["Wgrave"]=7808, + ["Wmonospace"]=65335, + ["X"]=88, + ["Xcircle"]=9421, + ["Xdieresis"]=7820, + ["Xdotaccent"]=7818, + ["Xeharmenian"]=1341, + ["Xi"]=926, + ["Xmonospace"]=65336, + ["Y"]=89, + ["Yacute"]=221, + ["Ycircle"]=9422, + ["Ycircumflex"]=374, + ["Ydieresis"]=376, + ["Ydotaccent"]=7822, + ["Ydotbelow"]=7924, + ["Yerudieresiscyrillic"]=1272, + ["Ygrave"]=7922, + ["Yhook"]=435, + ["Yhookabove"]=7926, + ["Yiarmenian"]=1349, + ["Yiwnarmenian"]=1362, + ["Ymonospace"]=65337, + ["Ytilde"]=7928, + ["Yusbigcyrillic"]=1130, + ["Yusbigiotifiedcyrillic"]=1132, + ["Yuslittlecyrillic"]=1126, + ["Yuslittleiotifiedcyrillic"]=1128, + ["Z"]=90, + ["Zaarmenian"]=1334, + ["Zacute"]=377, + ["Zcaron"]=381, + ["Zcircle"]=9423, + ["Zcircumflex"]=7824, + ["Zdotaccent"]=379, + ["Zdotbelow"]=7826, + ["Zedescendercyrillic"]=1176, + ["Zedieresiscyrillic"]=1246, + ["Zeta"]=918, + ["Zhearmenian"]=1338, + ["Zhebrevecyrillic"]=1217, + ["Zhedescendercyrillic"]=1174, + ["Zhedieresiscyrillic"]=1244, + ["Zlinebelow"]=7828, + ["Zmonospace"]=65338, + ["Zstroke"]=437, + ["a"]=97, + ["aabengali"]=2438, + ["aacute"]=225, + ["aadeva"]=2310, + ["aagujarati"]=2694, + ["aagurmukhi"]=2566, + ["aamatragurmukhi"]=2622, + ["aarusquare"]=13059, + ["aavowelsignbengali"]=2494, + ["aavowelsigndeva"]=2366, + ["aavowelsigngujarati"]=2750, + ["abbreviationmarkarmenian"]=1375, + ["abbreviationsigndeva"]=2416, + ["abengali"]=2437, + ["abopomofo"]=12570, + ["abreve"]=259, + ["abreveacute"]=7855, + ["abrevecyrillic"]=1233, + ["abrevedotbelow"]=7863, + ["abrevegrave"]=7857, + ["abrevehookabove"]=7859, + ["abrevetilde"]=7861, + ["acaron"]=462, + ["acircle"]=9424, + ["acircumflex"]=226, + ["acircumflexacute"]=7845, + ["acircumflexdotbelow"]=7853, + ["acircumflexgrave"]=7847, + ["acircumflexhookabove"]=7849, + ["acircumflextilde"]=7851, + ["acute"]=180, + ["acutebelowcmb"]=791, + ["acutecomb"]=769, + ["acutedeva"]=2388, + ["acutelowmod"]=719, + ["acutetonecmb"]=833, + ["adblgrave"]=513, + ["addakgurmukhi"]=2673, + ["adeva"]=2309, + ["adieresis"]=228, + ["adieresiscyrillic"]=1235, + ["adieresismacron"]=479, + ["adotbelow"]=7841, + ["adotmacron"]=481, + ["ae"]=230, + ["aeacute"]=509, + ["aekorean"]=12624, + ["aemacron"]=483, + ["afii10017"]=1040, + ["afii10018"]=1041, + ["afii10019"]=1042, + ["afii10020"]=1043, + ["afii10021"]=1044, + ["afii10022"]=1045, + ["afii10023"]=1025, + ["afii10024"]=1046, + ["afii10025"]=1047, + ["afii10026"]=1048, + ["afii10027"]=1049, + ["afii10028"]=1050, + ["afii10029"]=1051, + ["afii10030"]=1052, + ["afii10031"]=1053, + ["afii10032"]=1054, + ["afii10033"]=1055, + ["afii10034"]=1056, + ["afii10035"]=1057, + ["afii10036"]=1058, + ["afii10037"]=1059, + ["afii10038"]=1060, + ["afii10039"]=1061, + ["afii10040"]=1062, + ["afii10041"]=1063, + ["afii10042"]=1064, + ["afii10043"]=1065, + ["afii10044"]=1066, + ["afii10045"]=1067, + ["afii10046"]=1068, + ["afii10047"]=1069, + ["afii10048"]=1070, + ["afii10049"]=1071, + ["afii10050"]=1168, + ["afii10051"]=1026, + ["afii10052"]=1027, + ["afii10053"]=1028, + ["afii10054"]=1029, + ["afii10055"]=1030, + ["afii10056"]=1031, + ["afii10057"]=1032, + ["afii10058"]=1033, + ["afii10059"]=1034, + ["afii10060"]=1035, + ["afii10061"]=1036, + ["afii10062"]=1038, + ["afii10065"]=1072, + ["afii10145"]=1039, + ["afii10146"]=1122, + ["afii10147"]=1138, + ["afii10148"]=1140, + ["afii299"]=8206, + ["afii300"]=8207, + ["afii301"]=8205, + ["afii57534"]=1749, + ["afii61573"]=8236, + ["afii61574"]=8237, + ["afii61575"]=8238, + ["agrave"]=224, + ["agujarati"]=2693, + ["agurmukhi"]=2565, + ["ahiragana"]=12354, + ["ahookabove"]=7843, + ["aibengali"]=2448, + ["aibopomofo"]=12574, + ["aideva"]=2320, + ["aiecyrillic"]=1237, + ["aigujarati"]=2704, + ["aigurmukhi"]=2576, + ["aimatragurmukhi"]=2632, + ["ainarabic"]=1593, + ["ainfinalarabic"]=65226, + ["aininitialarabic"]=65227, + ["ainmedialarabic"]=65228, + ["ainvertedbreve"]=515, + ["aivowelsignbengali"]=2504, + ["aivowelsigndeva"]=2376, + ["aivowelsigngujarati"]=2760, + ["akatakana"]=12450, + ["akatakanahalfwidth"]=65393, + ["akorean"]=12623, + ["alefarabic"]=1575, + ["alefdageshhebrew"]=64304, + ["aleffinalarabic"]=65166, + ["alefhamzaabovearabic"]=1571, + ["alefhamzaabovefinalarabic"]=65156, + ["alefhamzabelowarabic"]=1573, + ["alefhamzabelowfinalarabic"]=65160, + ["alefhebrew"]=1488, + ["aleflamedhebrew"]=64335, + ["alefmaddaabovearabic"]=1570, + ["alefmaddaabovefinalarabic"]=65154, + ["alefmaksuraarabic"]=1609, + ["alefmaksurafinalarabic"]=65264, + ["alefpatahhebrew"]=64302, + ["alefqamatshebrew"]=64303, + ["aleph"]=8501, + ["allequal"]=8780, + ["alpha"]=945, + ["alphatonos"]=940, + ["amacron"]=257, + ["amonospace"]=65345, + ["ampersand"]=38, + ["ampersandmonospace"]=65286, + ["amsquare"]=13250, + ["anbopomofo"]=12578, + ["angbopomofo"]=12580, + ["angkhankhuthai"]=3674, + ["angle"]=8736, + ["anglebracketleft"]=12296, + ["anglebracketleftvertical"]=65087, + ["anglebracketright"]=12297, + ["anglebracketrightvertical"]=65088, + ["angleleft"]=9001, + ["angleright"]=9002, + ["angstrom"]=8491, + ["anoteleia"]=903, + ["anudattadeva"]=2386, + ["anusvarabengali"]=2434, + ["anusvaradeva"]=2306, + ["anusvaragujarati"]=2690, + ["aogonek"]=261, + ["apaatosquare"]=13056, + ["aparen"]=9372, + ["apostrophearmenian"]=1370, + ["apostrophemod"]=700, + ["apple"]=63743, + ["approaches"]=8784, + ["approxequal"]=8776, + ["approxequalorimage"]=8786, + ["araeaekorean"]=12686, + ["araeakorean"]=12685, + ["arc"]=8978, + ["arighthalfring"]=7834, + ["aring"]=229, + ["aringacute"]=507, + ["aringbelow"]=7681, + ["arrowboth"]=8596, + ["arrowdashdown"]=8675, + ["arrowdashleft"]=8672, + ["arrowdashright"]=8674, + ["arrowdashup"]=8673, + ["arrowdbldown"]=8659, + ["arrowdblup"]=8657, + ["arrowdown"]=8595, + ["arrowdownleft"]=8601, + ["arrowdownright"]=8600, + ["arrowdownwhite"]=8681, + ["arrowheaddownmod"]=709, + ["arrowheadleftmod"]=706, + ["arrowheadrightmod"]=707, + ["arrowheadupmod"]=708, + ["arrowleft"]=8592, + ["arrowleftdbl"]=8656, + ["arrowleftdblstroke"]=8653, + ["arrowleftoverright"]=8646, + ["arrowleftwhite"]=8678, + ["arrowright"]=8594, + ["arrowrightdblstroke"]=8655, + ["arrowrightheavy"]=10142, + ["arrowrightoverleft"]=8644, + ["arrowrightwhite"]=8680, + ["arrowtableft"]=8676, + ["arrowtabright"]=8677, + ["arrowup"]=8593, + ["arrowupdn"]=8597, + ["arrowupdownbase"]=8616, + ["arrowupleft"]=8598, + ["arrowupleftofdown"]=8645, + ["arrowupright"]=8599, + ["arrowupwhite"]=8679, + ["asciicircum"]=94, + ["asciicircummonospace"]=65342, + ["asciitilde"]=126, + ["asciitildemonospace"]=65374, + ["ascript"]=593, + ["ascriptturned"]=594, + ["asmallhiragana"]=12353, + ["asmallkatakana"]=12449, + ["asmallkatakanahalfwidth"]=65383, + ["asterisk"]=42, + ["asteriskarabic"]=1645, + ["asteriskmath"]=8727, + ["asteriskmonospace"]=65290, + ["asterisksmall"]=65121, + ["asterism"]=8258, + ["asymptoticallyequal"]=8771, + ["at"]=64, + ["atilde"]=227, + ["atmonospace"]=65312, + ["atsmall"]=65131, + ["aturned"]=592, + ["aubengali"]=2452, + ["aubopomofo"]=12576, + ["audeva"]=2324, + ["augujarati"]=2708, + ["augurmukhi"]=2580, + ["aulengthmarkbengali"]=2519, + ["aumatragurmukhi"]=2636, + ["auvowelsignbengali"]=2508, + ["auvowelsigndeva"]=2380, + ["auvowelsigngujarati"]=2764, + ["avagrahadeva"]=2365, + ["aybarmenian"]=1377, + ["ayinaltonehebrew"]=64288, + ["ayinhebrew"]=1506, + ["b"]=98, + ["babengali"]=2476, + ["backslash"]=92, + ["backslashmonospace"]=65340, + ["badeva"]=2348, + ["bagujarati"]=2732, + ["bagurmukhi"]=2604, + ["bahiragana"]=12400, + ["bahtthai"]=3647, + ["bakatakana"]=12496, + ["barmonospace"]=65372, + ["bbopomofo"]=12549, + ["bcircle"]=9425, + ["bdotaccent"]=7683, + ["bdotbelow"]=7685, + ["beamedsixteenthnotes"]=9836, + ["because"]=8757, + ["becyrillic"]=1073, + ["beharabic"]=1576, + ["behfinalarabic"]=65168, + ["behinitialarabic"]=65169, + ["behiragana"]=12409, + ["behmedialarabic"]=65170, + ["behmeeminitialarabic"]=64671, + ["behmeemisolatedarabic"]=64520, + ["behnoonfinalarabic"]=64621, + ["bekatakana"]=12505, + ["benarmenian"]=1378, + ["beta"]=946, + ["betasymbolgreek"]=976, + ["betdageshhebrew"]=64305, + ["bethebrew"]=1489, + ["betrafehebrew"]=64332, + ["bhabengali"]=2477, + ["bhadeva"]=2349, + ["bhagujarati"]=2733, + ["bhagurmukhi"]=2605, + ["bhook"]=595, + ["bihiragana"]=12403, + ["bikatakana"]=12499, + ["bilabialclick"]=664, + ["bindigurmukhi"]=2562, + ["birusquare"]=13105, + ["blackcircle"]=9679, + ["blackdiamond"]=9670, + ["blackleftpointingtriangle"]=9664, + ["blacklenticularbracketleft"]=12304, + ["blacklenticularbracketleftvertical"]=65083, + ["blacklenticularbracketright"]=12305, + ["blacklenticularbracketrightvertical"]=65084, + ["blacklowerlefttriangle"]=9699, + ["blacklowerrighttriangle"]=9698, + ["blackrightpointingtriangle"]=9654, + ["blacksmallsquare"]=9642, + ["blackstar"]=9733, + ["blackupperlefttriangle"]=9700, + ["blackupperrighttriangle"]=9701, + ["blackuppointingsmalltriangle"]=9652, + ["blank"]=9251, + ["blinebelow"]=7687, + ["block"]=9608, + ["bmonospace"]=65346, + ["bobaimaithai"]=3610, + ["bohiragana"]=12412, + ["bokatakana"]=12508, + ["bparen"]=9373, + ["bqsquare"]=13251, + ["braceleft"]=123, + ["braceleftmonospace"]=65371, + ["braceleftsmall"]=65115, + ["braceleftvertical"]=65079, + ["braceright"]=125, + ["bracerightmonospace"]=65373, + ["bracerightsmall"]=65116, + ["bracerightvertical"]=65080, + ["bracketleft"]=91, + ["bracketleftmonospace"]=65339, + ["bracketright"]=93, + ["bracketrightmonospace"]=65341, + ["breve"]=728, + ["brevebelowcmb"]=814, + ["brevecmb"]=774, + ["breveinvertedbelowcmb"]=815, + ["breveinvertedcmb"]=785, + ["breveinverteddoublecmb"]=865, + ["bridgebelowcmb"]=810, + ["bridgeinvertedbelowcmb"]=826, + ["brokenbar"]=166, + ["bstroke"]=384, + ["btopbar"]=387, + ["buhiragana"]=12406, + ["bukatakana"]=12502, + ["bullet"]=8226, + ["bulletoperator"]=8729, + ["bullseye"]=9678, + ["c"]=99, + ["caarmenian"]=1390, + ["cabengali"]=2458, + ["cacute"]=263, + ["cadeva"]=2330, + ["cagujarati"]=2714, + ["cagurmukhi"]=2586, + ["calsquare"]=13192, + ["candrabindubengali"]=2433, + ["candrabinducmb"]=784, + ["candrabindudeva"]=2305, + ["candrabindugujarati"]=2689, + ["capslock"]=8682, + ["careof"]=8453, + ["caron"]=711, + ["caronbelowcmb"]=812, + ["caroncmb"]=780, + ["carriagereturn"]=8629, + ["cbopomofo"]=12568, + ["ccaron"]=269, + ["ccedilla"]=231, + ["ccedillaacute"]=7689, + ["ccircle"]=9426, + ["ccircumflex"]=265, + ["ccurl"]=597, + ["cdotaccent"]=267, + ["cdsquare"]=13253, + ["cedilla"]=184, + ["cedillacmb"]=807, + ["cent"]=162, + ["centigrade"]=8451, + ["centmonospace"]=65504, + ["chaarmenian"]=1401, + ["chabengali"]=2459, + ["chadeva"]=2331, + ["chagujarati"]=2715, + ["chagurmukhi"]=2587, + ["chbopomofo"]=12564, + ["cheabkhasiancyrillic"]=1213, + ["checkmark"]=10003, + ["checyrillic"]=1095, + ["chedescenderabkhasiancyrillic"]=1215, + ["chedescendercyrillic"]=1207, + ["chedieresiscyrillic"]=1269, + ["cheharmenian"]=1395, + ["chekhakassiancyrillic"]=1228, + ["cheverticalstrokecyrillic"]=1209, + ["chi"]=967, + ["chieuchacirclekorean"]=12919, + ["chieuchaparenkorean"]=12823, + ["chieuchcirclekorean"]=12905, + ["chieuchkorean"]=12618, + ["chieuchparenkorean"]=12809, + ["chochangthai"]=3594, + ["chochanthai"]=3592, + ["chochingthai"]=3593, + ["chochoethai"]=3596, + ["chook"]=392, + ["cieucacirclekorean"]=12918, + ["cieucaparenkorean"]=12822, + ["cieuccirclekorean"]=12904, + ["cieuckorean"]=12616, + ["cieucparenkorean"]=12808, + ["cieucuparenkorean"]=12828, + ["circleot"]=8857, + ["circlepostalmark"]=12342, + ["circlewithlefthalfblack"]=9680, + ["circlewithrighthalfblack"]=9681, + ["circumflex"]=710, + ["circumflexbelowcmb"]=813, + ["circumflexcmb"]=770, + ["clear"]=8999, + ["clickalveolar"]=450, + ["clickdental"]=448, + ["clicklateral"]=449, + ["clickretroflex"]=451, + ["clubsuitblack"]=9827, + ["clubsuitwhite"]=9831, + ["cmcubedsquare"]=13220, + ["cmonospace"]=65347, + ["cmsquaredsquare"]=13216, + ["coarmenian"]=1409, + ["colon"]=58, + ["colonmonospace"]=65306, + ["colonsign"]=8353, + ["colonsmall"]=65109, + ["colontriangularhalfmod"]=721, + ["colontriangularmod"]=720, + ["comma"]=44, + ["commaabovecmb"]=787, + ["commaaboverightcmb"]=789, + ["commaarabic"]=1548, + ["commaarmenian"]=1373, + ["commamonospace"]=65292, + ["commareversedabovecmb"]=788, + ["commareversedmod"]=701, + ["commasmall"]=65104, + ["commaturnedabovecmb"]=786, + ["commaturnedmod"]=699, + ["congruent"]=8773, + ["contourintegral"]=8750, + ["control"]=8963, + ["controlACK"]=6, + ["controlBEL"]=7, + ["controlBS"]=8, + ["controlCAN"]=24, + ["controlCR"]=13, + ["controlDC1"]=17, + ["controlDC2"]=18, + ["controlDC3"]=19, + ["controlDC4"]=20, + ["controlDEL"]=127, + ["controlDLE"]=16, + ["controlEM"]=25, + ["controlENQ"]=5, + ["controlEOT"]=4, + ["controlESC"]=27, + ["controlETB"]=23, + ["controlETX"]=3, + ["controlFF"]=12, + ["controlFS"]=28, + ["controlGS"]=29, + ["controlHT"]=9, + ["controlLF"]=10, + ["controlNAK"]=21, + ["controlRS"]=30, + ["controlSI"]=15, + ["controlSO"]=14, + ["controlSOT"]=2, + ["controlSTX"]=1, + ["controlSUB"]=26, + ["controlSYN"]=22, + ["controlUS"]=31, + ["controlVT"]=11, + ["copyright"]=169, + ["cornerbracketleft"]=12300, + ["cornerbracketlefthalfwidth"]=65378, + ["cornerbracketleftvertical"]=65089, + ["cornerbracketright"]=12301, + ["cornerbracketrighthalfwidth"]=65379, + ["cornerbracketrightvertical"]=65090, + ["corporationsquare"]=13183, + ["cosquare"]=13255, + ["coverkgsquare"]=13254, + ["cparen"]=9374, + ["cruzeiro"]=8354, + ["cstretched"]=663, + ["curlyand"]=8911, + ["curlyor"]=8910, + ["currency"]=164, + ["d"]=100, + ["daarmenian"]=1380, + ["dabengali"]=2470, + ["dadarabic"]=1590, + ["dadeva"]=2342, + ["dadfinalarabic"]=65214, + ["dadinitialarabic"]=65215, + ["dadmedialarabic"]=65216, + ["dageshhebrew"]=1468, + ["dagger"]=8224, + ["daggerdbl"]=8225, + ["dagujarati"]=2726, + ["dagurmukhi"]=2598, + ["dahiragana"]=12384, + ["dakatakana"]=12480, + ["dalarabic"]=1583, + ["daletdageshhebrew"]=64307, + ["dalettserehebrew"]=1491, + ["dalfinalarabic"]=65194, + ["dammalowarabic"]=1615, + ["dammatanarabic"]=1612, + ["danda"]=2404, + ["dargalefthebrew"]=1447, + ["dasiapneumatacyrilliccmb"]=1157, + ["dblanglebracketleft"]=12298, + ["dblanglebracketleftvertical"]=65085, + ["dblanglebracketright"]=12299, + ["dblanglebracketrightvertical"]=65086, + ["dblarchinvertedbelowcmb"]=811, + ["dblarrowleft"]=8660, + ["dblarrowright"]=8658, + ["dbldanda"]=2405, + ["dblgravecmb"]=783, + ["dblintegral"]=8748, + ["dbllowlinecmb"]=819, + ["dbloverlinecmb"]=831, + ["dblprimemod"]=698, + ["dblverticalbar"]=8214, + ["dblverticallineabovecmb"]=782, + ["dbopomofo"]=12553, + ["dbsquare"]=13256, + ["dcaron"]=271, + ["dcedilla"]=7697, + ["dcircle"]=9427, + ["dcircumflexbelow"]=7699, + ["ddabengali"]=2465, + ["ddadeva"]=2337, + ["ddagujarati"]=2721, + ["ddagurmukhi"]=2593, + ["ddalarabic"]=1672, + ["ddalfinalarabic"]=64393, + ["dddhadeva"]=2396, + ["ddhabengali"]=2466, + ["ddhadeva"]=2338, + ["ddhagujarati"]=2722, + ["ddhagurmukhi"]=2594, + ["ddotaccent"]=7691, + ["ddotbelow"]=7693, + ["decimalseparatorpersian"]=1643, + ["decyrillic"]=1076, + ["degree"]=176, + ["dehihebrew"]=1453, + ["dehiragana"]=12391, + ["deicoptic"]=1007, + ["dekatakana"]=12487, + ["deleteleft"]=9003, + ["deleteright"]=8998, + ["delta"]=948, + ["deltaturned"]=397, + ["denominatorminusonenumeratorbengali"]=2552, + ["dezh"]=676, + ["dhabengali"]=2471, + ["dhadeva"]=2343, + ["dhagujarati"]=2727, + ["dhagurmukhi"]=2599, + ["dhook"]=599, + ["dialytikatonoscmb"]=836, + ["diamond"]=9830, + ["diamondsuitwhite"]=9826, + ["dieresis"]=168, + ["dieresisbelowcmb"]=804, + ["dieresiscmb"]=776, + ["dieresistonos"]=901, + ["dihiragana"]=12386, + ["dikatakana"]=12482, + ["dittomark"]=12291, + ["divide"]=247, + ["divides"]=8739, + ["divisionslash"]=8725, + ["djecyrillic"]=1106, + ["dlinebelow"]=7695, + ["dlsquare"]=13207, + ["dmacron"]=273, + ["dmonospace"]=65348, + ["dnblock"]=9604, + ["dochadathai"]=3598, + ["dodekthai"]=3604, + ["dohiragana"]=12393, + ["dokatakana"]=12489, + ["dollar"]=36, + ["dollarmonospace"]=65284, + ["dollarsmall"]=65129, + ["dong"]=8363, + ["dorusquare"]=13094, + ["dotaccent"]=729, + ["dotaccentcmb"]=775, + ["dotbelowcomb"]=803, + ["dotkatakana"]=12539, + ["dotlessi"]=305, + ["dotlessjstrokehook"]=644, + ["dotmath"]=8901, + ["dottedcircle"]=9676, + ["downtackbelowcmb"]=798, + ["downtackmod"]=725, + ["dparen"]=9375, + ["dtail"]=598, + ["dtopbar"]=396, + ["duhiragana"]=12389, + ["dukatakana"]=12485, + ["dz"]=499, + ["dzaltone"]=675, + ["dzcaron"]=454, + ["dzcurl"]=677, + ["dzeabkhasiancyrillic"]=1249, + ["dzecyrillic"]=1109, + ["dzhecyrillic"]=1119, + ["e"]=101, + ["eacute"]=233, + ["earth"]=9793, + ["ebengali"]=2447, + ["ebopomofo"]=12572, + ["ebreve"]=277, + ["ecandradeva"]=2317, + ["ecandragujarati"]=2701, + ["ecandravowelsigndeva"]=2373, + ["ecandravowelsigngujarati"]=2757, + ["ecaron"]=283, + ["ecedillabreve"]=7709, + ["echarmenian"]=1381, + ["echyiwnarmenian"]=1415, + ["ecircle"]=9428, + ["ecircumflex"]=234, + ["ecircumflexacute"]=7871, + ["ecircumflexbelow"]=7705, + ["ecircumflexdotbelow"]=7879, + ["ecircumflexgrave"]=7873, + ["ecircumflexhookabove"]=7875, + ["ecircumflextilde"]=7877, + ["ecyrillic"]=1108, + ["edblgrave"]=517, + ["edeva"]=2319, + ["edieresis"]=235, + ["edotaccent"]=279, + ["edotbelow"]=7865, + ["eegurmukhi"]=2575, + ["eematragurmukhi"]=2631, + ["efcyrillic"]=1092, + ["egrave"]=232, + ["egujarati"]=2703, + ["eharmenian"]=1383, + ["ehbopomofo"]=12573, + ["ehiragana"]=12360, + ["ehookabove"]=7867, + ["eibopomofo"]=12575, + ["eight"]=56, + ["eightbengali"]=2542, + ["eightcircle"]=9319, + ["eightcircleinversesansserif"]=10129, + ["eightdeva"]=2414, + ["eighteencircle"]=9329, + ["eighteenparen"]=9349, + ["eighteenperiod"]=9369, + ["eightgujarati"]=2798, + ["eightgurmukhi"]=2670, + ["eighthackarabic"]=1640, + ["eighthangzhou"]=12328, + ["eightideographicparen"]=12839, + ["eightinferior"]=8328, + ["eightmonospace"]=65304, + ["eightparen"]=9339, + ["eightperiod"]=9359, + ["eightpersian"]=1784, + ["eightroman"]=8567, + ["eightsuperior"]=8312, + ["eightthai"]=3672, + ["einvertedbreve"]=519, + ["eiotifiedcyrillic"]=1125, + ["ekatakana"]=12456, + ["ekatakanahalfwidth"]=65396, + ["ekonkargurmukhi"]=2676, + ["ekorean"]=12628, + ["elcyrillic"]=1083, + ["element"]=8712, + ["elevencircle"]=9322, + ["elevenparen"]=9342, + ["elevenperiod"]=9362, + ["elevenroman"]=8570, + ["ellipsis"]=8230, + ["ellipsisvertical"]=8942, + ["emacron"]=275, + ["emacronacute"]=7703, + ["emacrongrave"]=7701, + ["emcyrillic"]=1084, + ["emdash"]=8212, + ["emdashvertical"]=65073, + ["emonospace"]=65349, + ["emphasismarkarmenian"]=1371, + ["emptyset"]=8709, + ["enbopomofo"]=12579, + ["encyrillic"]=1085, + ["endash"]=8211, + ["endashvertical"]=65074, + ["endescendercyrillic"]=1187, + ["eng"]=331, + ["engbopomofo"]=12581, + ["enghecyrillic"]=1189, + ["enhookcyrillic"]=1224, + ["enspace"]=8194, + ["eogonek"]=281, + ["eokorean"]=12627, + ["eopen"]=603, + ["eopenclosed"]=666, + ["eopenreversed"]=604, + ["eopenreversedclosed"]=606, + ["eopenreversedhook"]=605, + ["eparen"]=9376, + ["epsilon"]=949, + ["epsilontonos"]=941, + ["equal"]=61, + ["equalmonospace"]=65309, + ["equalsmall"]=65126, + ["equalsuperior"]=8316, + ["equivalence"]=8801, + ["erbopomofo"]=12582, + ["ercyrillic"]=1088, + ["ereversed"]=600, + ["ereversedcyrillic"]=1101, + ["escyrillic"]=1089, + ["esdescendercyrillic"]=1195, + ["esh"]=643, + ["eshcurl"]=646, + ["eshortdeva"]=2318, + ["eshortvowelsigndeva"]=2374, + ["eshreversedloop"]=426, + ["eshsquatreversed"]=645, + ["esmallhiragana"]=12359, + ["esmallkatakana"]=12455, + ["esmallkatakanahalfwidth"]=65386, + ["estimated"]=8494, + ["eta"]=951, + ["etarmenian"]=1384, + ["etatonos"]=942, + ["eth"]=240, + ["etilde"]=7869, + ["etildebelow"]=7707, + ["etnahtalefthebrew"]=1425, + ["eturned"]=477, + ["eukorean"]=12641, + ["euro"]=8364, + ["evowelsignbengali"]=2503, + ["evowelsigndeva"]=2375, + ["evowelsigngujarati"]=2759, + ["exclam"]=33, + ["exclamarmenian"]=1372, + ["exclamdbl"]=8252, + ["exclamdown"]=161, + ["exclammonospace"]=65281, + ["ezh"]=658, + ["ezhcaron"]=495, + ["ezhcurl"]=659, + ["ezhreversed"]=441, + ["ezhtail"]=442, + ["f"]=102, + ["fadeva"]=2398, + ["fagurmukhi"]=2654, + ["fahrenheit"]=8457, + ["fathalowarabic"]=1614, + ["fathatanarabic"]=1611, + ["fbopomofo"]=12552, + ["fcircle"]=9429, + ["fdotaccent"]=7711, + ["feharabic"]=1601, + ["feharmenian"]=1414, + ["fehfinalarabic"]=65234, + ["fehinitialarabic"]=65235, + ["fehmedialarabic"]=65236, + ["feicoptic"]=997, + ["ff"]=64256, + ["ffi"]=64259, + ["ffl"]=64260, + ["fi"]=64257, + ["fifteencircle"]=9326, + ["fifteenparen"]=9346, + ["fifteenperiod"]=9366, + ["figuredash"]=8210, + ["filledbox"]=9632, + ["filledrect"]=9644, + ["finalkafdageshhebrew"]=64314, + ["finalkafshevahebrew"]=1498, + ["finalmemhebrew"]=1501, + ["finalnunhebrew"]=1503, + ["finalpehebrew"]=1507, + ["finaltsadihebrew"]=1509, + ["firsttonechinese"]=713, + ["fisheye"]=9673, + ["fitacyrillic"]=1139, + ["five"]=53, + ["fivebengali"]=2539, + ["fivecircle"]=9316, + ["fivecircleinversesansserif"]=10126, + ["fivedeva"]=2411, + ["fiveeighths"]=8541, + ["fivegujarati"]=2795, + ["fivegurmukhi"]=2667, + ["fivehackarabic"]=1637, + ["fivehangzhou"]=12325, + ["fiveideographicparen"]=12836, + ["fiveinferior"]=8325, + ["fivemonospace"]=65301, + ["fiveparen"]=9336, + ["fiveperiod"]=9356, + ["fivepersian"]=1781, + ["fiveroman"]=8564, + ["fivesuperior"]=8309, + ["fivethai"]=3669, + ["fl"]=64258, + ["florin"]=402, + ["fmonospace"]=65350, + ["fmsquare"]=13209, + ["fofanthai"]=3615, + ["fofathai"]=3613, + ["fongmanthai"]=3663, + ["four"]=52, + ["fourbengali"]=2538, + ["fourcircle"]=9315, + ["fourcircleinversesansserif"]=10125, + ["fourdeva"]=2410, + ["fourgujarati"]=2794, + ["fourgurmukhi"]=2666, + ["fourhackarabic"]=1636, + ["fourhangzhou"]=12324, + ["fourideographicparen"]=12835, + ["fourinferior"]=8324, + ["fourmonospace"]=65300, + ["fournumeratorbengali"]=2551, + ["fourparen"]=9335, + ["fourperiod"]=9355, + ["fourpersian"]=1780, + ["fourroman"]=8563, + ["foursuperior"]=8308, + ["fourteencircle"]=9325, + ["fourteenparen"]=9345, + ["fourteenperiod"]=9365, + ["fourthai"]=3668, + ["fourthtonechinese"]=715, + ["fparen"]=9377, + ["fraction"]=8260, + ["franc"]=8355, + ["g"]=103, + ["gabengali"]=2455, + ["gacute"]=501, + ["gadeva"]=2327, + ["gafarabic"]=1711, + ["gaffinalarabic"]=64403, + ["gafinitialarabic"]=64404, + ["gafmedialarabic"]=64405, + ["gagujarati"]=2711, + ["gagurmukhi"]=2583, + ["gahiragana"]=12364, + ["gakatakana"]=12460, + ["gamma"]=947, + ["gammalatinsmall"]=611, + ["gammasuperior"]=736, + ["gangiacoptic"]=1003, + ["gbopomofo"]=12557, + ["gbreve"]=287, + ["gcaron"]=487, + ["gcircle"]=9430, + ["gcircumflex"]=285, + ["gcommaaccent"]=291, + ["gdotaccent"]=289, + ["gecyrillic"]=1075, + ["gehiragana"]=12370, + ["gekatakana"]=12466, + ["geometricallyequal"]=8785, + ["gereshaccenthebrew"]=1436, + ["gereshhebrew"]=1523, + ["gereshmuqdamhebrew"]=1437, + ["germandbls"]=223, + ["gershayimaccenthebrew"]=1438, + ["gershayimhebrew"]=1524, + ["getamark"]=12307, + ["ghabengali"]=2456, + ["ghadarmenian"]=1394, + ["ghadeva"]=2328, + ["ghagujarati"]=2712, + ["ghagurmukhi"]=2584, + ["ghainarabic"]=1594, + ["ghainfinalarabic"]=65230, + ["ghaininitialarabic"]=65231, + ["ghainmedialarabic"]=65232, + ["ghemiddlehookcyrillic"]=1173, + ["ghestrokecyrillic"]=1171, + ["gheupturncyrillic"]=1169, + ["ghhadeva"]=2394, + ["ghhagurmukhi"]=2650, + ["ghook"]=608, + ["ghzsquare"]=13203, + ["gihiragana"]=12366, + ["gikatakana"]=12462, + ["gimarmenian"]=1379, + ["gimeldageshhebrew"]=64306, + ["gimelhebrew"]=1490, + ["gjecyrillic"]=1107, + ["glottalinvertedstroke"]=446, + ["glottalstop"]=660, + ["glottalstopinverted"]=662, + ["glottalstopmod"]=704, + ["glottalstopreversed"]=661, + ["glottalstopreversedmod"]=705, + ["glottalstopreversedsuperior"]=740, + ["glottalstopstroke"]=673, + ["glottalstopstrokereversed"]=674, + ["gmacron"]=7713, + ["gmonospace"]=65351, + ["gohiragana"]=12372, + ["gokatakana"]=12468, + ["gparen"]=9378, + ["gpasquare"]=13228, + ["grave"]=96, + ["gravebelowcmb"]=790, + ["gravecomb"]=768, + ["gravedeva"]=2387, + ["gravelowmod"]=718, + ["gravemonospace"]=65344, + ["gravetonecmb"]=832, + ["greater"]=62, + ["greaterequal"]=8805, + ["greaterequalorless"]=8923, + ["greatermonospace"]=65310, + ["greaterorequivalent"]=8819, + ["greaterorless"]=8823, + ["greateroverequal"]=8807, + ["greatersmall"]=65125, + ["gscript"]=609, + ["gstroke"]=485, + ["guhiragana"]=12368, + ["guillemotleft"]=171, + ["guillemotright"]=187, + ["guilsinglleft"]=8249, + ["guilsinglright"]=8250, + ["gukatakana"]=12464, + ["guramusquare"]=13080, + ["gysquare"]=13257, + ["h"]=104, + ["haabkhasiancyrillic"]=1193, + ["habengali"]=2489, + ["hadescendercyrillic"]=1203, + ["hadeva"]=2361, + ["hagujarati"]=2745, + ["hagurmukhi"]=2617, + ["haharabic"]=1581, + ["hahfinalarabic"]=65186, + ["hahinitialarabic"]=65187, + ["hahiragana"]=12399, + ["hahmedialarabic"]=65188, + ["haitusquare"]=13098, + ["hakatakana"]=12495, + ["hakatakanahalfwidth"]=65418, + ["halantgurmukhi"]=2637, + ["hamzasukunarabic"]=1569, + ["hangulfiller"]=12644, + ["hardsigncyrillic"]=1098, + ["harpoonleftbarbup"]=8636, + ["harpoonrightbarbup"]=8640, + ["hasquare"]=13258, + ["hatafpatahwidehebrew"]=1458, + ["hatafqamatswidehebrew"]=1459, + ["hatafsegolwidehebrew"]=1457, + ["hbar"]=295, + ["hbopomofo"]=12559, + ["hbrevebelow"]=7723, + ["hcedilla"]=7721, + ["hcircle"]=9431, + ["hcircumflex"]=293, + ["hdieresis"]=7719, + ["hdotaccent"]=7715, + ["hdotbelow"]=7717, + ["heartsuitblack"]=9829, + ["heartsuitwhite"]=9825, + ["hedageshhebrew"]=64308, + ["hehaltonearabic"]=1729, + ["heharabic"]=1607, + ["hehebrew"]=1492, + ["hehfinalaltonearabic"]=64423, + ["hehfinalarabic"]=65258, + ["hehhamzaabovefinalarabic"]=64421, + ["hehhamzaaboveisolatedarabic"]=64420, + ["hehinitialaltonearabic"]=64424, + ["hehinitialarabic"]=65259, + ["hehiragana"]=12408, + ["hehmedialaltonearabic"]=64425, + ["hehmedialarabic"]=65260, + ["heiseierasquare"]=13179, + ["hekatakana"]=12504, + ["hekatakanahalfwidth"]=65421, + ["hekutaarusquare"]=13110, + ["henghook"]=615, + ["herutusquare"]=13113, + ["hethebrew"]=1495, + ["hhook"]=614, + ["hhooksuperior"]=689, + ["hieuhacirclekorean"]=12923, + ["hieuhaparenkorean"]=12827, + ["hieuhcirclekorean"]=12909, + ["hieuhkorean"]=12622, + ["hieuhparenkorean"]=12813, + ["hihiragana"]=12402, + ["hikatakana"]=12498, + ["hikatakanahalfwidth"]=65419, + ["hiriqwidehebrew"]=1460, + ["hlinebelow"]=7830, + ["hmonospace"]=65352, + ["hoarmenian"]=1392, + ["hohipthai"]=3627, + ["hohiragana"]=12411, + ["hokatakana"]=12507, + ["hokatakanahalfwidth"]=65422, + ["holamwidehebrew"]=1465, + ["honokhukthai"]=3630, + ["hookcmb"]=777, + ["hookpalatalizedbelowcmb"]=801, + ["hookretroflexbelowcmb"]=802, + ["hoonsquare"]=13122, + ["horicoptic"]=1001, + ["horizontalbar"]=8213, + ["horncmb"]=795, + ["hotsprings"]=9832, + ["house"]=8962, + ["hparen"]=9379, + ["hsuperior"]=688, + ["hturned"]=613, + ["huhiragana"]=12405, + ["huiitosquare"]=13107, + ["hukatakana"]=12501, + ["hukatakanahalfwidth"]=65420, + ["hungarumlaut"]=733, + ["hungarumlautcmb"]=779, + ["hv"]=405, + ["hyphen"]=45, + ["hyphenmonospace"]=65293, + ["hyphensmall"]=65123, + ["hyphentwo"]=8208, + ["i"]=105, + ["iacute"]=237, + ["iacyrillic"]=1103, + ["ibengali"]=2439, + ["ibopomofo"]=12583, + ["ibreve"]=301, + ["icaron"]=464, + ["icircle"]=9432, + ["icircumflex"]=238, + ["icyrillic"]=1110, + ["idblgrave"]=521, + ["ideographearthcircle"]=12943, + ["ideographfirecircle"]=12939, + ["ideographicallianceparen"]=12863, + ["ideographiccallparen"]=12858, + ["ideographiccentrecircle"]=12965, + ["ideographicclose"]=12294, + ["ideographiccomma"]=12289, + ["ideographiccommaleft"]=65380, + ["ideographiccongratulationparen"]=12855, + ["ideographiccorrectcircle"]=12963, + ["ideographicearthparen"]=12847, + ["ideographicenterpriseparen"]=12861, + ["ideographicexcellentcircle"]=12957, + ["ideographicfestivalparen"]=12864, + ["ideographicfinancialcircle"]=12950, + ["ideographicfinancialparen"]=12854, + ["ideographicfireparen"]=12843, + ["ideographichaveparen"]=12850, + ["ideographichighcircle"]=12964, + ["ideographiciterationmark"]=12293, + ["ideographiclaborcircle"]=12952, + ["ideographiclaborparen"]=12856, + ["ideographicleftcircle"]=12967, + ["ideographiclowcircle"]=12966, + ["ideographicmedicinecircle"]=12969, + ["ideographicmetalparen"]=12846, + ["ideographicmoonparen"]=12842, + ["ideographicnameparen"]=12852, + ["ideographicperiod"]=12290, + ["ideographicprintcircle"]=12958, + ["ideographicreachparen"]=12867, + ["ideographicrepresentparen"]=12857, + ["ideographicresourceparen"]=12862, + ["ideographicrightcircle"]=12968, + ["ideographicsecretcircle"]=12953, + ["ideographicselfparen"]=12866, + ["ideographicsocietyparen"]=12851, + ["ideographicspace"]=12288, + ["ideographicspecialparen"]=12853, + ["ideographicstockparen"]=12849, + ["ideographicstudyparen"]=12859, + ["ideographicsunparen"]=12848, + ["ideographicsuperviseparen"]=12860, + ["ideographicwaterparen"]=12844, + ["ideographicwoodparen"]=12845, + ["ideographiczero"]=12295, + ["ideographmetalcircle"]=12942, + ["ideographmooncircle"]=12938, + ["ideographnamecircle"]=12948, + ["ideographsuncircle"]=12944, + ["ideographwatercircle"]=12940, + ["ideographwoodcircle"]=12941, + ["ideva"]=2311, + ["idieresis"]=239, + ["idieresisacute"]=7727, + ["idieresiscyrillic"]=1253, + ["idotbelow"]=7883, + ["iebrevecyrillic"]=1239, + ["iecyrillic"]=1077, + ["ieungacirclekorean"]=12917, + ["ieungaparenkorean"]=12821, + ["ieungcirclekorean"]=12903, + ["ieungkorean"]=12615, + ["ieungparenkorean"]=12807, + ["igrave"]=236, + ["igujarati"]=2695, + ["igurmukhi"]=2567, + ["ihiragana"]=12356, + ["ihookabove"]=7881, + ["iibengali"]=2440, + ["iicyrillic"]=1080, + ["iideva"]=2312, + ["iigujarati"]=2696, + ["iigurmukhi"]=2568, + ["iimatragurmukhi"]=2624, + ["iinvertedbreve"]=523, + ["iishortcyrillic"]=1081, + ["iivowelsignbengali"]=2496, + ["iivowelsigndeva"]=2368, + ["iivowelsigngujarati"]=2752, + ["ij"]=307, + ["ikatakana"]=12452, + ["ikatakanahalfwidth"]=65394, + ["ikorean"]=12643, + ["iluyhebrew"]=1452, + ["imacron"]=299, + ["imacroncyrillic"]=1251, + ["imageorapproximatelyequal"]=8787, + ["imatragurmukhi"]=2623, + ["imonospace"]=65353, + ["increment"]=8710, + ["infinity"]=8734, + ["iniarmenian"]=1387, + ["integral"]=8747, + ["integralbt"]=8993, + ["integraltp"]=8992, + ["intersection"]=8745, + ["intisquare"]=13061, + ["invbullet"]=9688, + ["invsmileface"]=9787, + ["iocyrillic"]=1105, + ["iogonek"]=303, + ["iota"]=953, + ["iotadieresis"]=970, + ["iotadieresistonos"]=912, + ["iotalatin"]=617, + ["iotatonos"]=943, + ["iparen"]=9380, + ["irigurmukhi"]=2674, + ["ismallhiragana"]=12355, + ["ismallkatakana"]=12451, + ["ismallkatakanahalfwidth"]=65384, + ["issharbengali"]=2554, + ["istroke"]=616, + ["iterationhiragana"]=12445, + ["iterationkatakana"]=12541, + ["itilde"]=297, + ["itildebelow"]=7725, + ["iubopomofo"]=12585, + ["iucyrillic"]=1102, + ["ivowelsignbengali"]=2495, + ["ivowelsigndeva"]=2367, + ["ivowelsigngujarati"]=2751, + ["izhitsacyrillic"]=1141, + ["izhitsadblgravecyrillic"]=1143, + ["j"]=106, + ["jaarmenian"]=1393, + ["jabengali"]=2460, + ["jadeva"]=2332, + ["jagujarati"]=2716, + ["jagurmukhi"]=2588, + ["jbopomofo"]=12560, + ["jcaron"]=496, + ["jcircle"]=9433, + ["jcircumflex"]=309, + ["jcrossedtail"]=669, + ["jdotlessstroke"]=607, + ["jecyrillic"]=1112, + ["jeemarabic"]=1580, + ["jeemfinalarabic"]=65182, + ["jeeminitialarabic"]=65183, + ["jeemmedialarabic"]=65184, + ["jeharabic"]=1688, + ["jehfinalarabic"]=64395, + ["jhabengali"]=2461, + ["jhadeva"]=2333, + ["jhagujarati"]=2717, + ["jhagurmukhi"]=2589, + ["jheharmenian"]=1403, + ["jis"]=12292, + ["jmonospace"]=65354, + ["jparen"]=9381, + ["jsuperior"]=690, + ["k"]=107, + ["kabashkircyrillic"]=1185, + ["kabengali"]=2453, + ["kacute"]=7729, + ["kacyrillic"]=1082, + ["kadescendercyrillic"]=1179, + ["kadeva"]=2325, + ["kafarabic"]=1603, + ["kafdageshhebrew"]=64315, + ["kaffinalarabic"]=65242, + ["kafhebrew"]=1499, + ["kafinitialarabic"]=65243, + ["kafmedialarabic"]=65244, + ["kafrafehebrew"]=64333, + ["kagujarati"]=2709, + ["kagurmukhi"]=2581, + ["kahiragana"]=12363, + ["kahookcyrillic"]=1220, + ["kakatakana"]=12459, + ["kakatakanahalfwidth"]=65398, + ["kappa"]=954, + ["kappasymbolgreek"]=1008, + ["kapyeounmieumkorean"]=12657, + ["kapyeounphieuphkorean"]=12676, + ["kapyeounpieupkorean"]=12664, + ["kapyeounssangpieupkorean"]=12665, + ["karoriisquare"]=13069, + ["kasmallkatakana"]=12533, + ["kasquare"]=13188, + ["kasraarabic"]=1616, + ["kasratanarabic"]=1613, + ["kastrokecyrillic"]=1183, + ["katahiraprolongmarkhalfwidth"]=65392, + ["kaverticalstrokecyrillic"]=1181, + ["kbopomofo"]=12558, + ["kcalsquare"]=13193, + ["kcaron"]=489, + ["kcircle"]=9434, + ["kcommaaccent"]=311, + ["kdotbelow"]=7731, + ["keharmenian"]=1412, + ["kehiragana"]=12369, + ["kekatakana"]=12465, + ["kekatakanahalfwidth"]=65401, + ["kenarmenian"]=1391, + ["kesmallkatakana"]=12534, + ["kgreenlandic"]=312, + ["khabengali"]=2454, + ["khacyrillic"]=1093, + ["khadeva"]=2326, + ["khagujarati"]=2710, + ["khagurmukhi"]=2582, + ["khaharabic"]=1582, + ["khahfinalarabic"]=65190, + ["khahinitialarabic"]=65191, + ["khahmedialarabic"]=65192, + ["kheicoptic"]=999, + ["khhadeva"]=2393, + ["khhagurmukhi"]=2649, + ["khieukhacirclekorean"]=12920, + ["khieukhaparenkorean"]=12824, + ["khieukhcirclekorean"]=12906, + ["khieukhkorean"]=12619, + ["khieukhparenkorean"]=12810, + ["khokhaithai"]=3586, + ["khokhonthai"]=3589, + ["khokhuatthai"]=3587, + ["khokhwaithai"]=3588, + ["khomutthai"]=3675, + ["khook"]=409, + ["khorakhangthai"]=3590, + ["khzsquare"]=13201, + ["kihiragana"]=12365, + ["kikatakana"]=12461, + ["kikatakanahalfwidth"]=65399, + ["kiroguramusquare"]=13077, + ["kiromeetorusquare"]=13078, + ["kirosquare"]=13076, + ["kiyeokacirclekorean"]=12910, + ["kiyeokaparenkorean"]=12814, + ["kiyeokcirclekorean"]=12896, + ["kiyeokkorean"]=12593, + ["kiyeokparenkorean"]=12800, + ["kiyeoksioskorean"]=12595, + ["kjecyrillic"]=1116, + ["klinebelow"]=7733, + ["klsquare"]=13208, + ["kmcubedsquare"]=13222, + ["kmonospace"]=65355, + ["kmsquaredsquare"]=13218, + ["kohiragana"]=12371, + ["kohmsquare"]=13248, + ["kokaithai"]=3585, + ["kokatakana"]=12467, + ["kokatakanahalfwidth"]=65402, + ["kooposquare"]=13086, + ["koppacyrillic"]=1153, + ["koreanstandardsymbol"]=12927, + ["koroniscmb"]=835, + ["kparen"]=9382, + ["kpasquare"]=13226, + ["ksicyrillic"]=1135, + ["ktsquare"]=13263, + ["kturned"]=670, + ["kuhiragana"]=12367, + ["kukatakana"]=12463, + ["kukatakanahalfwidth"]=65400, + ["kvsquare"]=13240, + ["kwsquare"]=13246, + ["l"]=108, + ["labengali"]=2482, + ["lacute"]=314, + ["ladeva"]=2354, + ["lagujarati"]=2738, + ["lagurmukhi"]=2610, + ["lakkhangyaothai"]=3653, + ["lamaleffinalarabic"]=65276, + ["lamalefhamzaabovefinalarabic"]=65272, + ["lamalefhamzaaboveisolatedarabic"]=65271, + ["lamalefhamzabelowfinalarabic"]=65274, + ["lamalefhamzabelowisolatedarabic"]=65273, + ["lamalefisolatedarabic"]=65275, + ["lamalefmaddaabovefinalarabic"]=65270, + ["lamalefmaddaaboveisolatedarabic"]=65269, + ["lamarabic"]=1604, + ["lambda"]=955, + ["lambdastroke"]=411, + ["lameddageshhebrew"]=64316, + ["lamedholamhebrew"]=1500, + ["lamfinalarabic"]=65246, + ["lamhahinitialarabic"]=64714, + ["lamjeeminitialarabic"]=64713, + ["lamkhahinitialarabic"]=64715, + ["lamlamhehisolatedarabic"]=65010, + ["lammedialarabic"]=65248, + ["lammeemhahinitialarabic"]=64904, + ["lammeeminitialarabic"]=64716, + ["lammeemkhahinitialarabic"]=65247, + ["largecircle"]=9711, + ["lbar"]=410, + ["lbelt"]=620, + ["lbopomofo"]=12556, + ["lcaron"]=318, + ["lcircle"]=9435, + ["lcircumflexbelow"]=7741, + ["lcommaaccent"]=316, + ["ldotaccent"]=320, + ["ldotbelow"]=7735, + ["ldotbelowmacron"]=7737, + ["leftangleabovecmb"]=794, + ["lefttackbelowcmb"]=792, + ["less"]=60, + ["lessequal"]=8804, + ["lessequalorgreater"]=8922, + ["lessmonospace"]=65308, + ["lessorequivalent"]=8818, + ["lessorgreater"]=8822, + ["lessoverequal"]=8806, + ["lesssmall"]=65124, + ["lezh"]=622, + ["lfblock"]=9612, + ["lhookretroflex"]=621, + ["lira"]=8356, + ["liwnarmenian"]=1388, + ["lj"]=457, + ["ljecyrillic"]=1113, + ["lladeva"]=2355, + ["llagujarati"]=2739, + ["llinebelow"]=7739, + ["llladeva"]=2356, + ["llvocalicbengali"]=2529, + ["llvocalicdeva"]=2401, + ["llvocalicvowelsignbengali"]=2531, + ["llvocalicvowelsigndeva"]=2403, + ["lmiddletilde"]=619, + ["lmonospace"]=65356, + ["lmsquare"]=13264, + ["lochulathai"]=3628, + ["logicaland"]=8743, + ["logicalnot"]=172, + ["logicalor"]=8744, + ["lolingthai"]=3621, + ["lowlinecenterline"]=65102, + ["lowlinecmb"]=818, + ["lowlinedashed"]=65101, + ["lozenge"]=9674, + ["lparen"]=9383, + ["lslash"]=322, + ["lsquare"]=8467, + ["luthai"]=3622, + ["lvocalicbengali"]=2444, + ["lvocalicdeva"]=2316, + ["lvocalicvowelsignbengali"]=2530, + ["lvocalicvowelsigndeva"]=2402, + ["lxsquare"]=13267, + ["m"]=109, + ["mabengali"]=2478, + ["macron"]=175, + ["macronbelowcmb"]=817, + ["macroncmb"]=772, + ["macronlowmod"]=717, + ["macronmonospace"]=65507, + ["macute"]=7743, + ["madeva"]=2350, + ["magujarati"]=2734, + ["magurmukhi"]=2606, + ["mahapakhlefthebrew"]=1444, + ["mahiragana"]=12414, + ["maichattawathai"]=3659, + ["maiekthai"]=3656, + ["maihanakatthai"]=3633, + ["maitaikhuthai"]=3655, + ["maithothai"]=3657, + ["maitrithai"]=3658, + ["maiyamokthai"]=3654, + ["makatakana"]=12510, + ["makatakanahalfwidth"]=65423, + ["mansyonsquare"]=13127, + ["maqafhebrew"]=1470, + ["mars"]=9794, + ["masoracirclehebrew"]=1455, + ["masquare"]=13187, + ["mbopomofo"]=12551, + ["mbsquare"]=13268, + ["mcircle"]=9436, + ["mcubedsquare"]=13221, + ["mdotaccent"]=7745, + ["mdotbelow"]=7747, + ["meemarabic"]=1605, + ["meemfinalarabic"]=65250, + ["meeminitialarabic"]=65251, + ["meemmedialarabic"]=65252, + ["meemmeeminitialarabic"]=64721, + ["meemmeemisolatedarabic"]=64584, + ["meetorusquare"]=13133, + ["mehiragana"]=12417, + ["meizierasquare"]=13182, + ["mekatakana"]=12513, + ["mekatakanahalfwidth"]=65426, + ["memdageshhebrew"]=64318, + ["memhebrew"]=1502, + ["menarmenian"]=1396, + ["merkhakefulalefthebrew"]=1446, + ["merkhalefthebrew"]=1445, + ["mhook"]=625, + ["mhzsquare"]=13202, + ["middledotkatakanahalfwidth"]=65381, + ["mieumacirclekorean"]=12914, + ["mieumaparenkorean"]=12818, + ["mieumcirclekorean"]=12900, + ["mieumkorean"]=12609, + ["mieumpansioskorean"]=12656, + ["mieumparenkorean"]=12804, + ["mieumpieupkorean"]=12654, + ["mieumsioskorean"]=12655, + ["mihiragana"]=12415, + ["mikatakana"]=12511, + ["mikatakanahalfwidth"]=65424, + ["minus"]=8722, + ["minusbelowcmb"]=800, + ["minuscircle"]=8854, + ["minusmod"]=727, + ["minusplus"]=8723, + ["minute"]=8242, + ["miribaarusquare"]=13130, + ["mirisquare"]=13129, + ["mlonglegturned"]=624, + ["mlsquare"]=13206, + ["mmcubedsquare"]=13219, + ["mmonospace"]=65357, + ["mmsquaredsquare"]=13215, + ["mohiragana"]=12418, + ["mohmsquare"]=13249, + ["mokatakana"]=12514, + ["mokatakanahalfwidth"]=65427, + ["molsquare"]=13270, + ["momathai"]=3617, + ["moverssquare"]=13223, + ["moverssquaredsquare"]=13224, + ["mparen"]=9384, + ["mpasquare"]=13227, + ["mssquare"]=13235, + ["mturned"]=623, + ["mu1"]=181, + ["muasquare"]=13186, + ["muchgreater"]=8811, + ["muchless"]=8810, + ["mufsquare"]=13196, + ["mugreek"]=956, + ["mugsquare"]=13197, + ["muhiragana"]=12416, + ["mukatakana"]=12512, + ["mukatakanahalfwidth"]=65425, + ["mulsquare"]=13205, + ["multiply"]=215, + ["mumsquare"]=13211, + ["munahlefthebrew"]=1443, + ["musicalnote"]=9834, + ["musicalnotedbl"]=9835, + ["musicflatsign"]=9837, + ["musicsharpsign"]=9839, + ["mussquare"]=13234, + ["muvsquare"]=13238, + ["muwsquare"]=13244, + ["mvmegasquare"]=13241, + ["mvsquare"]=13239, + ["mwmegasquare"]=13247, + ["mwsquare"]=13245, + ["n"]=110, + ["nabengali"]=2472, + ["nabla"]=8711, + ["nacute"]=324, + ["nadeva"]=2344, + ["nagujarati"]=2728, + ["nagurmukhi"]=2600, + ["nahiragana"]=12394, + ["nakatakana"]=12490, + ["nakatakanahalfwidth"]=65413, + ["nasquare"]=13185, + ["nbopomofo"]=12555, + ["ncaron"]=328, + ["ncircle"]=9437, + ["ncircumflexbelow"]=7755, + ["ncommaaccent"]=326, + ["ndotaccent"]=7749, + ["ndotbelow"]=7751, + ["nehiragana"]=12397, + ["nekatakana"]=12493, + ["nekatakanahalfwidth"]=65416, + ["nfsquare"]=13195, + ["ngabengali"]=2457, + ["ngadeva"]=2329, + ["ngagujarati"]=2713, + ["ngagurmukhi"]=2585, + ["ngonguthai"]=3591, + ["nhiragana"]=12435, + ["nhookleft"]=626, + ["nhookretroflex"]=627, + ["nieunacirclekorean"]=12911, + ["nieunaparenkorean"]=12815, + ["nieuncieuckorean"]=12597, + ["nieuncirclekorean"]=12897, + ["nieunhieuhkorean"]=12598, + ["nieunkorean"]=12596, + ["nieunpansioskorean"]=12648, + ["nieunparenkorean"]=12801, + ["nieunsioskorean"]=12647, + ["nieuntikeutkorean"]=12646, + ["nihiragana"]=12395, + ["nikatakana"]=12491, + ["nikatakanahalfwidth"]=65414, + ["nikhahitthai"]=3661, + ["nine"]=57, + ["ninebengali"]=2543, + ["ninecircle"]=9320, + ["ninecircleinversesansserif"]=10130, + ["ninedeva"]=2415, + ["ninegujarati"]=2799, + ["ninegurmukhi"]=2671, + ["ninehackarabic"]=1641, + ["ninehangzhou"]=12329, + ["nineideographicparen"]=12840, + ["nineinferior"]=8329, + ["ninemonospace"]=65305, + ["nineparen"]=9340, + ["nineperiod"]=9360, + ["ninepersian"]=1785, + ["nineroman"]=8568, + ["ninesuperior"]=8313, + ["nineteencircle"]=9330, + ["nineteenparen"]=9350, + ["nineteenperiod"]=9370, + ["ninethai"]=3673, + ["nj"]=460, + ["njecyrillic"]=1114, + ["nkatakana"]=12531, + ["nkatakanahalfwidth"]=65437, + ["nlegrightlong"]=414, + ["nlinebelow"]=7753, + ["nmonospace"]=65358, + ["nmsquare"]=13210, + ["nnabengali"]=2467, + ["nnadeva"]=2339, + ["nnagujarati"]=2723, + ["nnagurmukhi"]=2595, + ["nnnadeva"]=2345, + ["nohiragana"]=12398, + ["nokatakana"]=12494, + ["nokatakanahalfwidth"]=65417, + ["nonbreakingspace"]=160, + ["nonenthai"]=3603, + ["nonuthai"]=3609, + ["noonarabic"]=1606, + ["noonfinalarabic"]=65254, + ["noonghunnaarabic"]=1722, + ["noonghunnafinalarabic"]=64415, + ["nooninitialarabic"]=65255, + ["noonjeeminitialarabic"]=64722, + ["noonjeemisolatedarabic"]=64587, + ["noonmedialarabic"]=65256, + ["noonmeeminitialarabic"]=64725, + ["noonmeemisolatedarabic"]=64590, + ["noonnoonfinalarabic"]=64653, + ["notcontains"]=8716, + ["notelementof"]=8713, + ["notequal"]=8800, + ["notgreater"]=8815, + ["notgreaternorequal"]=8817, + ["notgreaternorless"]=8825, + ["notidentical"]=8802, + ["notless"]=8814, + ["notlessnorequal"]=8816, + ["notparallel"]=8742, + ["notprecedes"]=8832, + ["notsubset"]=8836, + ["notsucceeds"]=8833, + ["notsuperset"]=8837, + ["nowarmenian"]=1398, + ["nparen"]=9385, + ["nssquare"]=13233, + ["nsuperior"]=8319, + ["ntilde"]=241, + ["nu"]=957, + ["nuhiragana"]=12396, + ["nukatakana"]=12492, + ["nukatakanahalfwidth"]=65415, + ["nuktabengali"]=2492, + ["nuktadeva"]=2364, + ["nuktagujarati"]=2748, + ["nuktagurmukhi"]=2620, + ["numbersign"]=35, + ["numbersignmonospace"]=65283, + ["numbersignsmall"]=65119, + ["numeralsigngreek"]=884, + ["numeralsignlowergreek"]=885, + ["numero"]=8470, + ["nundageshhebrew"]=64320, + ["nunhebrew"]=1504, + ["nvsquare"]=13237, + ["nwsquare"]=13243, + ["nyabengali"]=2462, + ["nyadeva"]=2334, + ["nyagujarati"]=2718, + ["nyagurmukhi"]=2590, + ["o"]=111, + ["oacute"]=243, + ["oangthai"]=3629, + ["obarred"]=629, + ["obarredcyrillic"]=1257, + ["obarreddieresiscyrillic"]=1259, + ["obengali"]=2451, + ["obopomofo"]=12571, + ["obreve"]=335, + ["ocandradeva"]=2321, + ["ocandragujarati"]=2705, + ["ocandravowelsigndeva"]=2377, + ["ocandravowelsigngujarati"]=2761, + ["ocaron"]=466, + ["ocircle"]=9438, + ["ocircumflex"]=244, + ["ocircumflexacute"]=7889, + ["ocircumflexdotbelow"]=7897, + ["ocircumflexgrave"]=7891, + ["ocircumflexhookabove"]=7893, + ["ocircumflextilde"]=7895, + ["ocyrillic"]=1086, + ["odblgrave"]=525, + ["odeva"]=2323, + ["odieresis"]=246, + ["odieresiscyrillic"]=1255, + ["odotbelow"]=7885, + ["oe"]=339, + ["oekorean"]=12634, + ["ogonek"]=731, + ["ogonekcmb"]=808, + ["ograve"]=242, + ["ogujarati"]=2707, + ["oharmenian"]=1413, + ["ohiragana"]=12362, + ["ohookabove"]=7887, + ["ohorn"]=417, + ["ohornacute"]=7899, + ["ohorndotbelow"]=7907, + ["ohorngrave"]=7901, + ["ohornhookabove"]=7903, + ["ohorntilde"]=7905, + ["ohungarumlaut"]=337, + ["oi"]=419, + ["oinvertedbreve"]=527, + ["okatakana"]=12458, + ["okatakanahalfwidth"]=65397, + ["okorean"]=12631, + ["olehebrew"]=1451, + ["omacron"]=333, + ["omacronacute"]=7763, + ["omacrongrave"]=7761, + ["omdeva"]=2384, + ["omega"]=969, + ["omegacyrillic"]=1121, + ["omegalatinclosed"]=631, + ["omegaroundcyrillic"]=1147, + ["omegatitlocyrillic"]=1149, + ["omegatonos"]=974, + ["omgujarati"]=2768, + ["omicron"]=959, + ["omicrontonos"]=972, + ["omonospace"]=65359, + ["one"]=49, + ["onebengali"]=2535, + ["onecircle"]=9312, + ["onecircleinversesansserif"]=10122, + ["onedeva"]=2407, + ["onedotenleader"]=8228, + ["oneeighth"]=8539, + ["onegujarati"]=2791, + ["onegurmukhi"]=2663, + ["onehackarabic"]=1633, + ["onehalf"]=189, + ["onehangzhou"]=12321, + ["oneideographicparen"]=12832, + ["oneinferior"]=8321, + ["onemonospace"]=65297, + ["onenumeratorbengali"]=2548, + ["oneparen"]=9332, + ["oneperiod"]=9352, + ["onepersian"]=1777, + ["onequarter"]=188, + ["oneroman"]=8560, + ["onesuperior"]=185, + ["onethai"]=3665, + ["onethird"]=8531, + ["oogonek"]=491, + ["oogonekmacron"]=493, + ["oogurmukhi"]=2579, + ["oomatragurmukhi"]=2635, + ["oopen"]=596, + ["oparen"]=9386, + ["option"]=8997, + ["ordfeminine"]=170, + ["ordmasculine"]=186, + ["oshortdeva"]=2322, + ["oshortvowelsigndeva"]=2378, + ["oslash"]=248, + ["osmallhiragana"]=12361, + ["osmallkatakana"]=12457, + ["osmallkatakanahalfwidth"]=65387, + ["ostrokeacute"]=511, + ["otcyrillic"]=1151, + ["otilde"]=245, + ["otildeacute"]=7757, + ["otildedieresis"]=7759, + ["oubopomofo"]=12577, + ["overline"]=8254, + ["overlinecenterline"]=65098, + ["overlinecmb"]=773, + ["overlinedashed"]=65097, + ["overlinedblwavy"]=65100, + ["overlinewavy"]=65099, + ["ovowelsignbengali"]=2507, + ["ovowelsigndeva"]=2379, + ["ovowelsigngujarati"]=2763, + ["p"]=112, + ["paampssquare"]=13184, + ["paasentosquare"]=13099, + ["pabengali"]=2474, + ["pacute"]=7765, + ["padeva"]=2346, + ["pagedown"]=8671, + ["pageup"]=8670, + ["pagujarati"]=2730, + ["pagurmukhi"]=2602, + ["pahiragana"]=12401, + ["paiyannoithai"]=3631, + ["pakatakana"]=12497, + ["palatalizationcyrilliccmb"]=1156, + ["palochkacyrillic"]=1216, + ["pansioskorean"]=12671, + ["paragraph"]=182, + ["parallel"]=8741, + ["parenleft"]=40, + ["parenleftaltonearabic"]=64830, + ["parenleftinferior"]=8333, + ["parenleftmonospace"]=65288, + ["parenleftsmall"]=65113, + ["parenleftsuperior"]=8317, + ["parenleftvertical"]=65077, + ["parenright"]=41, + ["parenrightaltonearabic"]=64831, + ["parenrightinferior"]=8334, + ["parenrightmonospace"]=65289, + ["parenrightsmall"]=65114, + ["parenrightsuperior"]=8318, + ["parenrightvertical"]=65078, + ["partialdiff"]=8706, + ["paseqhebrew"]=1472, + ["pashtahebrew"]=1433, + ["pasquare"]=13225, + ["patahwidehebrew"]=1463, + ["pazerhebrew"]=1441, + ["pbopomofo"]=12550, + ["pcircle"]=9439, + ["pdotaccent"]=7767, + ["pecyrillic"]=1087, + ["pedageshhebrew"]=64324, + ["peezisquare"]=13115, + ["pefinaldageshhebrew"]=64323, + ["peharabic"]=1662, + ["peharmenian"]=1402, + ["pehebrew"]=1508, + ["pehfinalarabic"]=64343, + ["pehinitialarabic"]=64344, + ["pehiragana"]=12410, + ["pehmedialarabic"]=64345, + ["pekatakana"]=12506, + ["pemiddlehookcyrillic"]=1191, + ["perafehebrew"]=64334, + ["percent"]=37, + ["percentarabic"]=1642, + ["percentmonospace"]=65285, + ["percentsmall"]=65130, + ["period"]=46, + ["periodarmenian"]=1417, + ["periodcentered"]=183, + ["periodhalfwidth"]=65377, + ["periodmonospace"]=65294, + ["periodsmall"]=65106, + ["perispomenigreekcmb"]=834, + ["perpendicular"]=8869, + ["perthousand"]=8240, + ["peseta"]=8359, + ["pfsquare"]=13194, + ["phabengali"]=2475, + ["phadeva"]=2347, + ["phagujarati"]=2731, + ["phagurmukhi"]=2603, + ["phi"]=966, + ["phieuphacirclekorean"]=12922, + ["phieuphaparenkorean"]=12826, + ["phieuphcirclekorean"]=12908, + ["phieuphkorean"]=12621, + ["phieuphparenkorean"]=12812, + ["philatin"]=632, + ["phinthuthai"]=3642, + ["phisymbolgreek"]=981, + ["phook"]=421, + ["phophanthai"]=3614, + ["phophungthai"]=3612, + ["phosamphaothai"]=3616, + ["pi"]=960, + ["pieupacirclekorean"]=12915, + ["pieupaparenkorean"]=12819, + ["pieupcieuckorean"]=12662, + ["pieupcirclekorean"]=12901, + ["pieupkiyeokkorean"]=12658, + ["pieupkorean"]=12610, + ["pieupparenkorean"]=12805, + ["pieupsioskiyeokkorean"]=12660, + ["pieupsioskorean"]=12612, + ["pieupsiostikeutkorean"]=12661, + ["pieupthieuthkorean"]=12663, + ["pieuptikeutkorean"]=12659, + ["pihiragana"]=12404, + ["pikatakana"]=12500, + ["pisymbolgreek"]=982, + ["piwrarmenian"]=1411, + ["plus"]=43, + ["plusbelowcmb"]=799, + ["pluscircle"]=8853, + ["plusminus"]=177, + ["plusmod"]=726, + ["plusmonospace"]=65291, + ["plussmall"]=65122, + ["plussuperior"]=8314, + ["pmonospace"]=65360, + ["pmsquare"]=13272, + ["pohiragana"]=12413, + ["pointingindexdownwhite"]=9759, + ["pointingindexleftwhite"]=9756, + ["pointingindexrightwhite"]=9758, + ["pointingindexupwhite"]=9757, + ["pokatakana"]=12509, + ["poplathai"]=3611, + ["postalmark"]=12306, + ["postalmarkface"]=12320, + ["pparen"]=9387, + ["precedes"]=8826, + ["prescription"]=8478, + ["primemod"]=697, + ["primereversed"]=8245, + ["product"]=8719, + ["projective"]=8965, + ["prolongedkana"]=12540, + ["propellor"]=8984, + ["proportion"]=8759, + ["proportional"]=8733, + ["psi"]=968, + ["psicyrillic"]=1137, + ["psilipneumatacyrilliccmb"]=1158, + ["pssquare"]=13232, + ["puhiragana"]=12407, + ["pukatakana"]=12503, + ["pvsquare"]=13236, + ["pwsquare"]=13242, + ["q"]=113, + ["qadeva"]=2392, + ["qadmahebrew"]=1448, + ["qafarabic"]=1602, + ["qaffinalarabic"]=65238, + ["qafinitialarabic"]=65239, + ["qafmedialarabic"]=65240, + ["qamatswidehebrew"]=1464, + ["qarneyparahebrew"]=1439, + ["qbopomofo"]=12561, + ["qcircle"]=9440, + ["qhook"]=672, + ["qmonospace"]=65361, + ["qofdageshhebrew"]=64327, + ["qoftserehebrew"]=1511, + ["qparen"]=9388, + ["quarternote"]=9833, + ["qubutswidehebrew"]=1467, + ["question"]=63, + ["questionarabic"]=1567, + ["questionarmenian"]=1374, + ["questiondown"]=191, + ["questiongreek"]=894, + ["questionmonospace"]=65311, + ["quotedbl"]=34, + ["quotedblbase"]=8222, + ["quotedblleft"]=8220, + ["quotedblmonospace"]=65282, + ["quotedblprime"]=12318, + ["quotedblprimereversed"]=12317, + ["quotedblright"]=8221, + ["quoteleft"]=8216, + ["quotereversed"]=8219, + ["quoteright"]=8217, + ["quoterightn"]=329, + ["quotesinglbase"]=8218, + ["quotesingle"]=39, + ["quotesinglemonospace"]=65287, + ["r"]=114, + ["raarmenian"]=1404, + ["rabengali"]=2480, + ["racute"]=341, + ["radeva"]=2352, + ["radical"]=8730, + ["radoverssquare"]=13230, + ["radoverssquaredsquare"]=13231, + ["radsquare"]=13229, + ["rafehebrew"]=1471, + ["ragujarati"]=2736, + ["ragurmukhi"]=2608, + ["rahiragana"]=12425, + ["rakatakana"]=12521, + ["rakatakanahalfwidth"]=65431, + ["ralowerdiagonalbengali"]=2545, + ["ramiddlediagonalbengali"]=2544, + ["ramshorn"]=612, + ["ratio"]=8758, + ["rbopomofo"]=12566, + ["rcaron"]=345, + ["rcircle"]=9441, + ["rcommaaccent"]=343, + ["rdblgrave"]=529, + ["rdotaccent"]=7769, + ["rdotbelow"]=7771, + ["rdotbelowmacron"]=7773, + ["referencemark"]=8251, + ["registered"]=174, + ["reharmenian"]=1408, + ["rehfinalarabic"]=65198, + ["rehiragana"]=12428, + ["rehyehaleflamarabic"]=1585, + ["rekatakana"]=12524, + ["rekatakanahalfwidth"]=65434, + ["reshdageshhebrew"]=64328, + ["reshtserehebrew"]=1512, + ["reversedtilde"]=8765, + ["reviamugrashhebrew"]=1431, + ["revlogicalnot"]=8976, + ["rfishhook"]=638, + ["rfishhookreversed"]=639, + ["rhabengali"]=2525, + ["rhadeva"]=2397, + ["rho"]=961, + ["rhook"]=637, + ["rhookturned"]=635, + ["rhookturnedsuperior"]=693, + ["rhosymbolgreek"]=1009, + ["rhotichookmod"]=734, + ["rieulacirclekorean"]=12913, + ["rieulaparenkorean"]=12817, + ["rieulcirclekorean"]=12899, + ["rieulhieuhkorean"]=12608, + ["rieulkiyeokkorean"]=12602, + ["rieulkiyeoksioskorean"]=12649, + ["rieulkorean"]=12601, + ["rieulmieumkorean"]=12603, + ["rieulpansioskorean"]=12652, + ["rieulparenkorean"]=12803, + ["rieulphieuphkorean"]=12607, + ["rieulpieupkorean"]=12604, + ["rieulpieupsioskorean"]=12651, + ["rieulsioskorean"]=12605, + ["rieulthieuthkorean"]=12606, + ["rieultikeutkorean"]=12650, + ["rieulyeorinhieuhkorean"]=12653, + ["rightangle"]=8735, + ["righttackbelowcmb"]=793, + ["righttriangle"]=8895, + ["rihiragana"]=12426, + ["rikatakana"]=12522, + ["rikatakanahalfwidth"]=65432, + ["ring"]=730, + ["ringbelowcmb"]=805, + ["ringcmb"]=778, + ["ringhalfleft"]=703, + ["ringhalfleftarmenian"]=1369, + ["ringhalfleftbelowcmb"]=796, + ["ringhalfleftcentered"]=723, + ["ringhalfright"]=702, + ["ringhalfrightbelowcmb"]=825, + ["ringhalfrightcentered"]=722, + ["rinvertedbreve"]=531, + ["rittorusquare"]=13137, + ["rlinebelow"]=7775, + ["rlongleg"]=636, + ["rlonglegturned"]=634, + ["rmonospace"]=65362, + ["rohiragana"]=12429, + ["rokatakana"]=12525, + ["rokatakanahalfwidth"]=65435, + ["roruathai"]=3619, + ["rparen"]=9389, + ["rrabengali"]=2524, + ["rradeva"]=2353, + ["rragurmukhi"]=2652, + ["rreharabic"]=1681, + ["rrehfinalarabic"]=64397, + ["rrvocalicbengali"]=2528, + ["rrvocalicdeva"]=2400, + ["rrvocalicgujarati"]=2784, + ["rrvocalicvowelsignbengali"]=2500, + ["rrvocalicvowelsigndeva"]=2372, + ["rrvocalicvowelsigngujarati"]=2756, + ["rtblock"]=9616, + ["rturned"]=633, + ["rturnedsuperior"]=692, + ["ruhiragana"]=12427, + ["rukatakana"]=12523, + ["rukatakanahalfwidth"]=65433, + ["rupeemarkbengali"]=2546, + ["rupeesignbengali"]=2547, + ["ruthai"]=3620, + ["rvocalicbengali"]=2443, + ["rvocalicdeva"]=2315, + ["rvocalicgujarati"]=2699, + ["rvocalicvowelsignbengali"]=2499, + ["rvocalicvowelsigndeva"]=2371, + ["rvocalicvowelsigngujarati"]=2755, + ["s"]=115, + ["sabengali"]=2488, + ["sacute"]=347, + ["sacutedotaccent"]=7781, + ["sadarabic"]=1589, + ["sadeva"]=2360, + ["sadfinalarabic"]=65210, + ["sadinitialarabic"]=65211, + ["sadmedialarabic"]=65212, + ["sagujarati"]=2744, + ["sagurmukhi"]=2616, + ["sahiragana"]=12373, + ["sakatakana"]=12469, + ["sakatakanahalfwidth"]=65403, + ["sallallahoualayhewasallamarabic"]=65018, + ["samekhdageshhebrew"]=64321, + ["samekhhebrew"]=1505, + ["saraaathai"]=3634, + ["saraaethai"]=3649, + ["saraaimaimalaithai"]=3652, + ["saraaimaimuanthai"]=3651, + ["saraamthai"]=3635, + ["saraathai"]=3632, + ["saraethai"]=3648, + ["saraiithai"]=3637, + ["saraithai"]=3636, + ["saraothai"]=3650, + ["saraueethai"]=3639, + ["sarauethai"]=3638, + ["sarauthai"]=3640, + ["sarauuthai"]=3641, + ["sbopomofo"]=12569, + ["scaron"]=353, + ["scarondotaccent"]=7783, + ["scedilla"]=351, + ["schwa"]=601, + ["schwacyrillic"]=1241, + ["schwadieresiscyrillic"]=1243, + ["schwahook"]=602, + ["scircle"]=9442, + ["scircumflex"]=349, + ["scommaaccent"]=537, + ["sdotaccent"]=7777, + ["sdotbelow"]=7779, + ["sdotbelowdotaccent"]=7785, + ["seagullbelowcmb"]=828, + ["second"]=8243, + ["secondtonechinese"]=714, + ["section"]=167, + ["seenarabic"]=1587, + ["seenfinalarabic"]=65202, + ["seeninitialarabic"]=65203, + ["seenmedialarabic"]=65204, + ["segoltahebrew"]=1426, + ["segolwidehebrew"]=1462, + ["seharmenian"]=1405, + ["sehiragana"]=12379, + ["sekatakana"]=12475, + ["sekatakanahalfwidth"]=65406, + ["semicolon"]=59, + ["semicolonarabic"]=1563, + ["semicolonmonospace"]=65307, + ["semicolonsmall"]=65108, + ["semivoicedmarkkana"]=12444, + ["semivoicedmarkkanahalfwidth"]=65439, + ["sentisquare"]=13090, + ["sentosquare"]=13091, + ["seven"]=55, + ["sevenbengali"]=2541, + ["sevencircle"]=9318, + ["sevencircleinversesansserif"]=10128, + ["sevendeva"]=2413, + ["seveneighths"]=8542, + ["sevengujarati"]=2797, + ["sevengurmukhi"]=2669, + ["sevenhackarabic"]=1639, + ["sevenhangzhou"]=12327, + ["sevenideographicparen"]=12838, + ["seveninferior"]=8327, + ["sevenmonospace"]=65303, + ["sevenparen"]=9338, + ["sevenperiod"]=9358, + ["sevenpersian"]=1783, + ["sevenroman"]=8566, + ["sevensuperior"]=8311, + ["seventeencircle"]=9328, + ["seventeenparen"]=9348, + ["seventeenperiod"]=9368, + ["seventhai"]=3671, + ["shaarmenian"]=1399, + ["shabengali"]=2486, + ["shacyrillic"]=1096, + ["shaddadammaarabic"]=64609, + ["shaddadammatanarabic"]=64606, + ["shaddafathaarabic"]=64608, + ["shaddafathatanarabic"]=1617, + ["shaddakasraarabic"]=64610, + ["shaddakasratanarabic"]=64607, + ["shadedark"]=9619, + ["shadelight"]=9617, + ["shademedium"]=9618, + ["shadeva"]=2358, + ["shagujarati"]=2742, + ["shagurmukhi"]=2614, + ["shalshelethebrew"]=1427, + ["shbopomofo"]=12565, + ["shchacyrillic"]=1097, + ["sheenarabic"]=1588, + ["sheenfinalarabic"]=65206, + ["sheeninitialarabic"]=65207, + ["sheenmedialarabic"]=65208, + ["sheicoptic"]=995, + ["sheqelhebrew"]=8362, + ["shevawidehebrew"]=1456, + ["shhacyrillic"]=1211, + ["shimacoptic"]=1005, + ["shindageshhebrew"]=64329, + ["shindageshshindothebrew"]=64300, + ["shindageshsindothebrew"]=64301, + ["shindothebrew"]=1473, + ["shinhebrew"]=1513, + ["shinshindothebrew"]=64298, + ["shinsindothebrew"]=64299, + ["shook"]=642, + ["sigma"]=963, + ["sigmafinal"]=962, + ["sigmalunatesymbolgreek"]=1010, + ["sihiragana"]=12375, + ["sikatakana"]=12471, + ["sikatakanahalfwidth"]=65404, + ["siluqlefthebrew"]=1469, + ["sindothebrew"]=1474, + ["siosacirclekorean"]=12916, + ["siosaparenkorean"]=12820, + ["sioscieuckorean"]=12670, + ["sioscirclekorean"]=12902, + ["sioskiyeokkorean"]=12666, + ["sioskorean"]=12613, + ["siosnieunkorean"]=12667, + ["siosparenkorean"]=12806, + ["siospieupkorean"]=12669, + ["siostikeutkorean"]=12668, + ["six"]=54, + ["sixbengali"]=2540, + ["sixcircle"]=9317, + ["sixcircleinversesansserif"]=10127, + ["sixdeva"]=2412, + ["sixgujarati"]=2796, + ["sixgurmukhi"]=2668, + ["sixhackarabic"]=1638, + ["sixhangzhou"]=12326, + ["sixideographicparen"]=12837, + ["sixinferior"]=8326, + ["sixmonospace"]=65302, + ["sixparen"]=9337, + ["sixperiod"]=9357, + ["sixpersian"]=1782, + ["sixroman"]=8565, + ["sixsuperior"]=8310, + ["sixteencircle"]=9327, + ["sixteencurrencydenominatorbengali"]=2553, + ["sixteenparen"]=9347, + ["sixteenperiod"]=9367, + ["sixthai"]=3670, + ["slash"]=47, + ["slashmonospace"]=65295, + ["slong"]=383, + ["slongdotaccent"]=7835, + ["smonospace"]=65363, + ["sofpasuqhebrew"]=1475, + ["softhyphen"]=173, + ["softsigncyrillic"]=1100, + ["sohiragana"]=12381, + ["sokatakana"]=12477, + ["sokatakanahalfwidth"]=65407, + ["soliduslongoverlaycmb"]=824, + ["solidusshortoverlaycmb"]=823, + ["sorusithai"]=3625, + ["sosalathai"]=3624, + ["sosothai"]=3595, + ["sosuathai"]=3626, + ["space"]=32, + ["spadesuitblack"]=9824, + ["spadesuitwhite"]=9828, + ["sparen"]=9390, + ["squarebelowcmb"]=827, + ["squarecc"]=13252, + ["squarecm"]=13213, + ["squarediagonalcrosshatchfill"]=9641, + ["squarehorizontalfill"]=9636, + ["squarekg"]=13199, + ["squarekm"]=13214, + ["squarekmcapital"]=13262, + ["squareln"]=13265, + ["squarelog"]=13266, + ["squaremg"]=13198, + ["squaremil"]=13269, + ["squaremm"]=13212, + ["squaremsquared"]=13217, + ["squareorthogonalcrosshatchfill"]=9638, + ["squareupperlefttolowerrightfill"]=9639, + ["squareupperrighttolowerleftfill"]=9640, + ["squareverticalfill"]=9637, + ["squarewhitewithsmallblack"]=9635, + ["srsquare"]=13275, + ["ssabengali"]=2487, + ["ssadeva"]=2359, + ["ssagujarati"]=2743, + ["ssangcieuckorean"]=12617, + ["ssanghieuhkorean"]=12677, + ["ssangieungkorean"]=12672, + ["ssangkiyeokkorean"]=12594, + ["ssangnieunkorean"]=12645, + ["ssangpieupkorean"]=12611, + ["ssangsioskorean"]=12614, + ["ssangtikeutkorean"]=12600, + ["sterling"]=163, + ["sterlingmonospace"]=65505, + ["strokelongoverlaycmb"]=822, + ["strokeshortoverlaycmb"]=821, + ["subset"]=8834, + ["subsetnotequal"]=8842, + ["subsetorequal"]=8838, + ["succeeds"]=8827, + ["suchthat"]=8715, + ["suhiragana"]=12377, + ["sukatakana"]=12473, + ["sukatakanahalfwidth"]=65405, + ["sukunarabic"]=1618, + ["summation"]=8721, + ["sun"]=9788, + ["superset"]=8835, + ["supersetnotequal"]=8843, + ["supersetorequal"]=8839, + ["svsquare"]=13276, + ["syouwaerasquare"]=13180, + ["t"]=116, + ["tabengali"]=2468, + ["tackdown"]=8868, + ["tackleft"]=8867, + ["tadeva"]=2340, + ["tagujarati"]=2724, + ["tagurmukhi"]=2596, + ["taharabic"]=1591, + ["tahfinalarabic"]=65218, + ["tahinitialarabic"]=65219, + ["tahiragana"]=12383, + ["tahmedialarabic"]=65220, + ["taisyouerasquare"]=13181, + ["takatakana"]=12479, + ["takatakanahalfwidth"]=65408, + ["tatweelarabic"]=1600, + ["tau"]=964, + ["tavdageshhebrew"]=64330, + ["tavhebrew"]=1514, + ["tbar"]=359, + ["tbopomofo"]=12554, + ["tcaron"]=357, + ["tccurl"]=680, + ["tcheharabic"]=1670, + ["tchehfinalarabic"]=64379, + ["tchehmedialarabic"]=64381, + ["tchehmeeminitialarabic"]=64380, + ["tcircle"]=9443, + ["tcircumflexbelow"]=7793, + ["tcommaaccent"]=355, + ["tdieresis"]=7831, + ["tdotaccent"]=7787, + ["tdotbelow"]=7789, + ["tecyrillic"]=1090, + ["tedescendercyrillic"]=1197, + ["teharabic"]=1578, + ["tehfinalarabic"]=65174, + ["tehhahinitialarabic"]=64674, + ["tehhahisolatedarabic"]=64524, + ["tehinitialarabic"]=65175, + ["tehiragana"]=12390, + ["tehjeeminitialarabic"]=64673, + ["tehjeemisolatedarabic"]=64523, + ["tehmarbutaarabic"]=1577, + ["tehmarbutafinalarabic"]=65172, + ["tehmedialarabic"]=65176, + ["tehmeeminitialarabic"]=64676, + ["tehmeemisolatedarabic"]=64526, + ["tehnoonfinalarabic"]=64627, + ["tekatakana"]=12486, + ["tekatakanahalfwidth"]=65411, + ["telephone"]=8481, + ["telephoneblack"]=9742, + ["telishagedolahebrew"]=1440, + ["telishaqetanahebrew"]=1449, + ["tencircle"]=9321, + ["tenideographicparen"]=12841, + ["tenparen"]=9341, + ["tenperiod"]=9361, + ["tenroman"]=8569, + ["tesh"]=679, + ["tetdageshhebrew"]=64312, + ["tethebrew"]=1496, + ["tetsecyrillic"]=1205, + ["tevirlefthebrew"]=1435, + ["thabengali"]=2469, + ["thadeva"]=2341, + ["thagujarati"]=2725, + ["thagurmukhi"]=2597, + ["thalarabic"]=1584, + ["thalfinalarabic"]=65196, + ["thanthakhatthai"]=3660, + ["theharabic"]=1579, + ["thehfinalarabic"]=65178, + ["thehinitialarabic"]=65179, + ["thehmedialarabic"]=65180, + ["thereexists"]=8707, + ["therefore"]=8756, + ["theta"]=952, + ["thetasymbolgreek"]=977, + ["thieuthacirclekorean"]=12921, + ["thieuthaparenkorean"]=12825, + ["thieuthcirclekorean"]=12907, + ["thieuthkorean"]=12620, + ["thieuthparenkorean"]=12811, + ["thirteencircle"]=9324, + ["thirteenparen"]=9344, + ["thirteenperiod"]=9364, + ["thonangmonthothai"]=3601, + ["thook"]=429, + ["thophuthaothai"]=3602, + ["thorn"]=254, + ["thothahanthai"]=3607, + ["thothanthai"]=3600, + ["thothongthai"]=3608, + ["thothungthai"]=3606, + ["thousandcyrillic"]=1154, + ["thousandsseparatorpersian"]=1644, + ["three"]=51, + ["threebengali"]=2537, + ["threecircle"]=9314, + ["threecircleinversesansserif"]=10124, + ["threedeva"]=2409, + ["threeeighths"]=8540, + ["threegujarati"]=2793, + ["threegurmukhi"]=2665, + ["threehackarabic"]=1635, + ["threehangzhou"]=12323, + ["threeideographicparen"]=12834, + ["threeinferior"]=8323, + ["threemonospace"]=65299, + ["threenumeratorbengali"]=2550, + ["threeparen"]=9334, + ["threeperiod"]=9354, + ["threepersian"]=1779, + ["threequarters"]=190, + ["threeroman"]=8562, + ["threesuperior"]=179, + ["threethai"]=3667, + ["thzsquare"]=13204, + ["tihiragana"]=12385, + ["tikatakana"]=12481, + ["tikatakanahalfwidth"]=65409, + ["tikeutacirclekorean"]=12912, + ["tikeutaparenkorean"]=12816, + ["tikeutcirclekorean"]=12898, + ["tikeutkorean"]=12599, + ["tikeutparenkorean"]=12802, + ["tilde"]=732, + ["tildebelowcmb"]=816, + ["tildecomb"]=771, + ["tildedoublecmb"]=864, + ["tildeoperator"]=8764, + ["tildeoverlaycmb"]=820, + ["tildeverticalcmb"]=830, + ["timescircle"]=8855, + ["tipehalefthebrew"]=1430, + ["tippigurmukhi"]=2672, + ["titlocyrilliccmb"]=1155, + ["tiwnarmenian"]=1407, + ["tlinebelow"]=7791, + ["tmonospace"]=65364, + ["toarmenian"]=1385, + ["tohiragana"]=12392, + ["tokatakana"]=12488, + ["tokatakanahalfwidth"]=65412, + ["tonebarextrahighmod"]=741, + ["tonebarextralowmod"]=745, + ["tonebarhighmod"]=742, + ["tonebarlowmod"]=744, + ["tonebarmidmod"]=743, + ["tonefive"]=445, + ["tonesix"]=389, + ["tonetwo"]=424, + ["tonos"]=900, + ["tonsquare"]=13095, + ["topatakthai"]=3599, + ["tortoiseshellbracketleft"]=12308, + ["tortoiseshellbracketleftsmall"]=65117, + ["tortoiseshellbracketleftvertical"]=65081, + ["tortoiseshellbracketright"]=12309, + ["tortoiseshellbracketrightsmall"]=65118, + ["tortoiseshellbracketrightvertical"]=65082, + ["totaothai"]=3605, + ["tpalatalhook"]=427, + ["tparen"]=9391, + ["trademark"]=8482, + ["tretroflexhook"]=648, + ["triagdn"]=9660, + ["triaglf"]=9668, + ["triagrt"]=9658, + ["triagup"]=9650, + ["ts"]=678, + ["tsadidageshhebrew"]=64326, + ["tsadihebrew"]=1510, + ["tsecyrillic"]=1094, + ["tserewidehebrew"]=1461, + ["tshecyrillic"]=1115, + ["ttabengali"]=2463, + ["ttadeva"]=2335, + ["ttagujarati"]=2719, + ["ttagurmukhi"]=2591, + ["tteharabic"]=1657, + ["ttehfinalarabic"]=64359, + ["ttehinitialarabic"]=64360, + ["ttehmedialarabic"]=64361, + ["tthabengali"]=2464, + ["tthadeva"]=2336, + ["tthagujarati"]=2720, + ["tthagurmukhi"]=2592, + ["tturned"]=647, + ["tuhiragana"]=12388, + ["tukatakana"]=12484, + ["tukatakanahalfwidth"]=65410, + ["tusmallhiragana"]=12387, + ["tusmallkatakana"]=12483, + ["tusmallkatakanahalfwidth"]=65391, + ["twelvecircle"]=9323, + ["twelveparen"]=9343, + ["twelveperiod"]=9363, + ["twelveroman"]=8571, + ["twentycircle"]=9331, + ["twentyparen"]=9351, + ["twentyperiod"]=9371, + ["two"]=50, + ["twobengali"]=2536, + ["twocircle"]=9313, + ["twocircleinversesansserif"]=10123, + ["twodeva"]=2408, + ["twodotleader"]=8229, + ["twodotleadervertical"]=65072, + ["twogujarati"]=2792, + ["twogurmukhi"]=2664, + ["twohackarabic"]=1634, + ["twohangzhou"]=12322, + ["twoideographicparen"]=12833, + ["twoinferior"]=8322, + ["twomonospace"]=65298, + ["twonumeratorbengali"]=2549, + ["twoparen"]=9333, + ["twoperiod"]=9353, + ["twopersian"]=1778, + ["tworoman"]=8561, + ["twostroke"]=443, + ["twosuperior"]=178, + ["twothai"]=3666, + ["twothirds"]=8532, + ["u"]=117, + ["uacute"]=250, + ["ubar"]=649, + ["ubengali"]=2441, + ["ubopomofo"]=12584, + ["ubreve"]=365, + ["ucaron"]=468, + ["ucircle"]=9444, + ["ucircumflex"]=251, + ["ucircumflexbelow"]=7799, + ["ucyrillic"]=1091, + ["udattadeva"]=2385, + ["udblgrave"]=533, + ["udeva"]=2313, + ["udieresis"]=252, + ["udieresisacute"]=472, + ["udieresisbelow"]=7795, + ["udieresiscaron"]=474, + ["udieresiscyrillic"]=1265, + ["udieresisgrave"]=476, + ["udieresismacron"]=470, + ["udotbelow"]=7909, + ["ugrave"]=249, + ["ugujarati"]=2697, + ["ugurmukhi"]=2569, + ["uhiragana"]=12358, + ["uhookabove"]=7911, + ["uhorn"]=432, + ["uhornacute"]=7913, + ["uhorndotbelow"]=7921, + ["uhorngrave"]=7915, + ["uhornhookabove"]=7917, + ["uhorntilde"]=7919, + ["uhungarumlaut"]=369, + ["uhungarumlautcyrillic"]=1267, + ["uinvertedbreve"]=535, + ["ukatakana"]=12454, + ["ukatakanahalfwidth"]=65395, + ["ukcyrillic"]=1145, + ["ukorean"]=12636, + ["umacron"]=363, + ["umacroncyrillic"]=1263, + ["umacrondieresis"]=7803, + ["umatragurmukhi"]=2625, + ["umonospace"]=65365, + ["underscore"]=95, + ["underscoredbl"]=8215, + ["underscoremonospace"]=65343, + ["underscorevertical"]=65075, + ["underscorewavy"]=65103, + ["union"]=8746, + ["universal"]=8704, + ["uogonek"]=371, + ["uparen"]=9392, + ["upblock"]=9600, + ["upperdothebrew"]=1476, + ["upsilon"]=965, + ["upsilondieresis"]=971, + ["upsilondieresistonos"]=944, + ["upsilonlatin"]=650, + ["upsilontonos"]=973, + ["uptackbelowcmb"]=797, + ["uptackmod"]=724, + ["uragurmukhi"]=2675, + ["uring"]=367, + ["ushortcyrillic"]=1118, + ["usmallhiragana"]=12357, + ["usmallkatakana"]=12453, + ["usmallkatakanahalfwidth"]=65385, + ["ustraightcyrillic"]=1199, + ["ustraightstrokecyrillic"]=1201, + ["utilde"]=361, + ["utildeacute"]=7801, + ["utildebelow"]=7797, + ["uubengali"]=2442, + ["uudeva"]=2314, + ["uugujarati"]=2698, + ["uugurmukhi"]=2570, + ["uumatragurmukhi"]=2626, + ["uuvowelsignbengali"]=2498, + ["uuvowelsigndeva"]=2370, + ["uuvowelsigngujarati"]=2754, + ["uvowelsignbengali"]=2497, + ["uvowelsigndeva"]=2369, + ["uvowelsigngujarati"]=2753, + ["v"]=118, + ["vadeva"]=2357, + ["vagujarati"]=2741, + ["vagurmukhi"]=2613, + ["vakatakana"]=12535, + ["vavdageshhebrew"]=64309, + ["vavhebrew"]=1493, + ["vavholamhebrew"]=64331, + ["vavvavhebrew"]=1520, + ["vavyodhebrew"]=1521, + ["vcircle"]=9445, + ["vdotbelow"]=7807, + ["vecyrillic"]=1074, + ["veharabic"]=1700, + ["vehfinalarabic"]=64363, + ["vehinitialarabic"]=64364, + ["vehmedialarabic"]=64365, + ["vekatakana"]=12537, + ["venus"]=9792, + ["verticalbar"]=124, + ["verticallineabovecmb"]=781, + ["verticallinebelowcmb"]=809, + ["verticallinelowmod"]=716, + ["verticallinemod"]=712, + ["vewarmenian"]=1406, + ["vhook"]=651, + ["vikatakana"]=12536, + ["viramabengali"]=2509, + ["viramadeva"]=2381, + ["viramagujarati"]=2765, + ["visargabengali"]=2435, + ["visargadeva"]=2307, + ["visargagujarati"]=2691, + ["vmonospace"]=65366, + ["voarmenian"]=1400, + ["voicediterationhiragana"]=12446, + ["voicediterationkatakana"]=12542, + ["voicedmarkkana"]=12443, + ["voicedmarkkanahalfwidth"]=65438, + ["vokatakana"]=12538, + ["vparen"]=9393, + ["vtilde"]=7805, + ["vturned"]=652, + ["vuhiragana"]=12436, + ["vukatakana"]=12532, + ["w"]=119, + ["wacute"]=7811, + ["waekorean"]=12633, + ["wahiragana"]=12431, + ["wakatakana"]=12527, + ["wakatakanahalfwidth"]=65436, + ["wakorean"]=12632, + ["wasmallhiragana"]=12430, + ["wasmallkatakana"]=12526, + ["wattosquare"]=13143, + ["wavedash"]=12316, + ["wavyunderscorevertical"]=65076, + ["wawarabic"]=1608, + ["wawfinalarabic"]=65262, + ["wawhamzaabovearabic"]=1572, + ["wawhamzaabovefinalarabic"]=65158, + ["wbsquare"]=13277, + ["wcircle"]=9446, + ["wcircumflex"]=373, + ["wdieresis"]=7813, + ["wdotaccent"]=7815, + ["wdotbelow"]=7817, + ["wehiragana"]=12433, + ["weierstrass"]=8472, + ["wekatakana"]=12529, + ["wekorean"]=12638, + ["weokorean"]=12637, + ["wgrave"]=7809, + ["whitebullet"]=9702, + ["whitecircle"]=9675, + ["whitecircleinverse"]=9689, + ["whitecornerbracketleft"]=12302, + ["whitecornerbracketleftvertical"]=65091, + ["whitecornerbracketright"]=12303, + ["whitecornerbracketrightvertical"]=65092, + ["whitediamond"]=9671, + ["whitediamondcontainingblacksmalldiamond"]=9672, + ["whitedownpointingsmalltriangle"]=9663, + ["whitedownpointingtriangle"]=9661, + ["whiteleftpointingsmalltriangle"]=9667, + ["whiteleftpointingtriangle"]=9665, + ["whitelenticularbracketleft"]=12310, + ["whitelenticularbracketright"]=12311, + ["whiterightpointingsmalltriangle"]=9657, + ["whiterightpointingtriangle"]=9655, + ["whitesmallsquare"]=9643, + ["whitesmilingface"]=9786, + ["whitesquare"]=9633, + ["whitestar"]=9734, + ["whitetelephone"]=9743, + ["whitetortoiseshellbracketleft"]=12312, + ["whitetortoiseshellbracketright"]=12313, + ["whiteuppointingsmalltriangle"]=9653, + ["whiteuppointingtriangle"]=9651, + ["wihiragana"]=12432, + ["wikatakana"]=12528, + ["wikorean"]=12639, + ["wmonospace"]=65367, + ["wohiragana"]=12434, + ["wokatakana"]=12530, + ["wokatakanahalfwidth"]=65382, + ["won"]=8361, + ["wonmonospace"]=65510, + ["wowaenthai"]=3623, + ["wparen"]=9394, + ["wring"]=7832, + ["wsuperior"]=695, + ["wturned"]=653, + ["wynn"]=447, + ["x"]=120, + ["xabovecmb"]=829, + ["xbopomofo"]=12562, + ["xcircle"]=9447, + ["xdieresis"]=7821, + ["xdotaccent"]=7819, + ["xeharmenian"]=1389, + ["xi"]=958, + ["xmonospace"]=65368, + ["xparen"]=9395, + ["xsuperior"]=739, + ["y"]=121, + ["yaadosquare"]=13134, + ["yabengali"]=2479, + ["yacute"]=253, + ["yadeva"]=2351, + ["yaekorean"]=12626, + ["yagujarati"]=2735, + ["yagurmukhi"]=2607, + ["yahiragana"]=12420, + ["yakatakana"]=12516, + ["yakatakanahalfwidth"]=65428, + ["yakorean"]=12625, + ["yamakkanthai"]=3662, + ["yasmallhiragana"]=12419, + ["yasmallkatakana"]=12515, + ["yasmallkatakanahalfwidth"]=65388, + ["yatcyrillic"]=1123, + ["ycircle"]=9448, + ["ycircumflex"]=375, + ["ydieresis"]=255, + ["ydotaccent"]=7823, + ["ydotbelow"]=7925, + ["yeharabic"]=1610, + ["yehbarreearabic"]=1746, + ["yehbarreefinalarabic"]=64431, + ["yehfinalarabic"]=65266, + ["yehhamzaabovearabic"]=1574, + ["yehhamzaabovefinalarabic"]=65162, + ["yehhamzaaboveinitialarabic"]=65163, + ["yehhamzaabovemedialarabic"]=65164, + ["yehinitialarabic"]=65267, + ["yehmedialarabic"]=65268, + ["yehmeeminitialarabic"]=64733, + ["yehmeemisolatedarabic"]=64600, + ["yehnoonfinalarabic"]=64660, + ["yehthreedotsbelowarabic"]=1745, + ["yekorean"]=12630, + ["yen"]=165, + ["yenmonospace"]=65509, + ["yeokorean"]=12629, + ["yeorinhieuhkorean"]=12678, + ["yerahbenyomolefthebrew"]=1450, + ["yericyrillic"]=1099, + ["yerudieresiscyrillic"]=1273, + ["yesieungkorean"]=12673, + ["yesieungpansioskorean"]=12675, + ["yesieungsioskorean"]=12674, + ["yetivhebrew"]=1434, + ["ygrave"]=7923, + ["yhook"]=436, + ["yhookabove"]=7927, + ["yiarmenian"]=1397, + ["yicyrillic"]=1111, + ["yikorean"]=12642, + ["yinyang"]=9775, + ["yiwnarmenian"]=1410, + ["ymonospace"]=65369, + ["yoddageshhebrew"]=64313, + ["yodhebrew"]=1497, + ["yodyodhebrew"]=1522, + ["yodyodpatahhebrew"]=64287, + ["yohiragana"]=12424, + ["yoikorean"]=12681, + ["yokatakana"]=12520, + ["yokatakanahalfwidth"]=65430, + ["yokorean"]=12635, + ["yosmallhiragana"]=12423, + ["yosmallkatakana"]=12519, + ["yosmallkatakanahalfwidth"]=65390, + ["yotgreek"]=1011, + ["yoyaekorean"]=12680, + ["yoyakorean"]=12679, + ["yoyakthai"]=3618, + ["yoyingthai"]=3597, + ["yparen"]=9396, + ["ypogegrammeni"]=890, + ["ypogegrammenigreekcmb"]=837, + ["yr"]=422, + ["yring"]=7833, + ["ysuperior"]=696, + ["ytilde"]=7929, + ["yturned"]=654, + ["yuhiragana"]=12422, + ["yuikorean"]=12684, + ["yukatakana"]=12518, + ["yukatakanahalfwidth"]=65429, + ["yukorean"]=12640, + ["yusbigcyrillic"]=1131, + ["yusbigiotifiedcyrillic"]=1133, + ["yuslittlecyrillic"]=1127, + ["yuslittleiotifiedcyrillic"]=1129, + ["yusmallhiragana"]=12421, + ["yusmallkatakana"]=12517, + ["yusmallkatakanahalfwidth"]=65389, + ["yuyekorean"]=12683, + ["yuyeokorean"]=12682, + ["yyabengali"]=2527, + ["yyadeva"]=2399, + ["z"]=122, + ["zaarmenian"]=1382, + ["zacute"]=378, + ["zadeva"]=2395, + ["zagurmukhi"]=2651, + ["zaharabic"]=1592, + ["zahfinalarabic"]=65222, + ["zahinitialarabic"]=65223, + ["zahiragana"]=12374, + ["zahmedialarabic"]=65224, + ["zainarabic"]=1586, + ["zainfinalarabic"]=65200, + ["zakatakana"]=12470, + ["zaqefgadolhebrew"]=1429, + ["zaqefqatanhebrew"]=1428, + ["zarqahebrew"]=1432, + ["zayindageshhebrew"]=64310, + ["zayinhebrew"]=1494, + ["zbopomofo"]=12567, + ["zcaron"]=382, + ["zcircle"]=9449, + ["zcircumflex"]=7825, + ["zcurl"]=657, + ["zdotaccent"]=380, + ["zdotbelow"]=7827, + ["zecyrillic"]=1079, + ["zedescendercyrillic"]=1177, + ["zedieresiscyrillic"]=1247, + ["zehiragana"]=12380, + ["zekatakana"]=12476, + ["zero"]=48, + ["zerobengali"]=2534, + ["zerodeva"]=2406, + ["zerogujarati"]=2790, + ["zerogurmukhi"]=2662, + ["zerohackarabic"]=1632, + ["zeroinferior"]=8320, + ["zeromonospace"]=65296, + ["zeropersian"]=1776, + ["zerosuperior"]=8304, + ["zerothai"]=3664, + ["zerowidthjoiner"]=65279, + ["zerowidthnonjoiner"]=8204, + ["zerowidthspace"]=8203, + ["zeta"]=950, + ["zhbopomofo"]=12563, + ["zhearmenian"]=1386, + ["zhebrevecyrillic"]=1218, + ["zhecyrillic"]=1078, + ["zhedescendercyrillic"]=1175, + ["zhedieresiscyrillic"]=1245, + ["zihiragana"]=12376, + ["zikatakana"]=12472, + ["zinorhebrew"]=1454, + ["zlinebelow"]=7829, + ["zmonospace"]=65370, + ["zohiragana"]=12382, + ["zokatakana"]=12478, + ["zparen"]=9397, + ["zretroflexhook"]=656, + ["zstroke"]=438, + ["zuhiragana"]=12378, + ["zukatakana"]=12474, + + -- extras + + ["Dcroat"]=272, + ["Delta"]=8710, + ["Euro"]=8364, + ["H18533"]=9679, + ["H18543"]=9642, + ["H18551"]=9643, + ["H22073"]=9633, + ["Ldot"]=319, + ["Oslashacute"]=510, + ["SF10000"]=9484, + ["SF20000"]=9492, + ["SF30000"]=9488, + ["SF40000"]=9496, + ["SF50000"]=9532, + ["SF60000"]=9516, + ["SF70000"]=9524, + ["SF80000"]=9500, + ["SF90000"]=9508, + ["Upsilon1"]=978, + ["afii10066"]=1073, + ["afii10067"]=1074, + ["afii10068"]=1075, + ["afii10069"]=1076, + ["afii10070"]=1077, + ["afii10071"]=1105, + ["afii10072"]=1078, + ["afii10073"]=1079, + ["afii10074"]=1080, + ["afii10075"]=1081, + ["afii10076"]=1082, + ["afii10077"]=1083, + ["afii10078"]=1084, + ["afii10079"]=1085, + ["afii10080"]=1086, + ["afii10081"]=1087, + ["afii10082"]=1088, + ["afii10083"]=1089, + ["afii10084"]=1090, + ["afii10085"]=1091, + ["afii10086"]=1092, + ["afii10087"]=1093, + ["afii10088"]=1094, + ["afii10089"]=1095, + ["afii10090"]=1096, + ["afii10091"]=1097, + ["afii10092"]=1098, + ["afii10093"]=1099, + ["afii10094"]=1100, + ["afii10095"]=1101, + ["afii10096"]=1102, + ["afii10097"]=1103, + ["afii10098"]=1169, + ["afii10099"]=1106, + ["afii10100"]=1107, + ["afii10101"]=1108, + ["afii10102"]=1109, + ["afii10103"]=1110, + ["afii10104"]=1111, + ["afii10105"]=1112, + ["afii10106"]=1113, + ["afii10107"]=1114, + ["afii10108"]=1115, + ["afii10109"]=1116, + ["afii10110"]=1118, + ["afii10193"]=1119, + ["afii10194"]=1123, + ["afii10195"]=1139, + ["afii10196"]=1141, + ["afii10846"]=1241, + ["afii208"]=8213, + ["afii57381"]=1642, + ["afii57388"]=1548, + ["afii57392"]=1632, + ["afii57393"]=1633, + ["afii57394"]=1634, + ["afii57395"]=1635, + ["afii57396"]=1636, + ["afii57397"]=1637, + ["afii57398"]=1638, + ["afii57399"]=1639, + ["afii57400"]=1640, + ["afii57401"]=1641, + ["afii57403"]=1563, + ["afii57407"]=1567, + ["afii57409"]=1569, + ["afii57410"]=1570, + ["afii57411"]=1571, + ["afii57412"]=1572, + ["afii57413"]=1573, + ["afii57414"]=1574, + ["afii57415"]=1575, + ["afii57416"]=1576, + ["afii57417"]=1577, + ["afii57418"]=1578, + ["afii57419"]=1579, + ["afii57420"]=1580, + ["afii57421"]=1581, + ["afii57422"]=1582, + ["afii57423"]=1583, + ["afii57424"]=1584, + ["afii57425"]=1585, + ["afii57426"]=1586, + ["afii57427"]=1587, + ["afii57428"]=1588, + ["afii57429"]=1589, + ["afii57430"]=1590, + ["afii57431"]=1591, + ["afii57432"]=1592, + ["afii57433"]=1593, + ["afii57434"]=1594, + ["afii57440"]=1600, + ["afii57441"]=1601, + ["afii57442"]=1602, + ["afii57443"]=1603, + ["afii57444"]=1604, + ["afii57445"]=1605, + ["afii57446"]=1606, + ["afii57448"]=1608, + ["afii57449"]=1609, + ["afii57450"]=1610, + ["afii57451"]=1611, + ["afii57452"]=1612, + ["afii57453"]=1613, + ["afii57454"]=1614, + ["afii57455"]=1615, + ["afii57456"]=1616, + ["afii57457"]=1617, + ["afii57458"]=1618, + ["afii57470"]=1607, + ["afii57505"]=1700, + ["afii57506"]=1662, + ["afii57507"]=1670, + ["afii57508"]=1688, + ["afii57509"]=1711, + ["afii57511"]=1657, + ["afii57512"]=1672, + ["afii57513"]=1681, + ["afii57514"]=1722, + ["afii57519"]=1746, + ["afii57636"]=8362, + ["afii57645"]=1470, + ["afii57658"]=1475, + ["afii57664"]=1488, + ["afii57665"]=1489, + ["afii57666"]=1490, + ["afii57667"]=1491, + ["afii57668"]=1492, + ["afii57669"]=1493, + ["afii57670"]=1494, + ["afii57671"]=1495, + ["afii57672"]=1496, + ["afii57673"]=1497, + ["afii57674"]=1498, + ["afii57675"]=1499, + ["afii57676"]=1500, + ["afii57677"]=1501, + ["afii57678"]=1502, + ["afii57679"]=1503, + ["afii57680"]=1504, + ["afii57681"]=1505, + ["afii57682"]=1506, + ["afii57683"]=1507, + ["afii57684"]=1508, + ["afii57685"]=1509, + ["afii57686"]=1510, + ["afii57687"]=1511, + ["afii57688"]=1512, + ["afii57689"]=1513, + ["afii57690"]=1514, + ["afii57716"]=1520, + ["afii57717"]=1521, + ["afii57718"]=1522, + ["afii57793"]=1460, + ["afii57794"]=1461, + ["afii57795"]=1462, + ["afii57796"]=1467, + ["afii57797"]=1464, + ["afii57798"]=1463, + ["afii57799"]=1456, + ["afii57800"]=1458, + ["afii57801"]=1457, + ["afii57802"]=1459, + ["afii57803"]=1474, + ["afii57804"]=1473, + ["afii57806"]=1465, + ["afii57807"]=1468, + ["afii57839"]=1469, + ["afii57841"]=1471, + ["afii57842"]=1472, + ["afii57929"]=700, + ["afii61248"]=8453, + ["afii61289"]=8467, + ["afii61352"]=8470, + ["afii61664"]=8204, + ["afii63167"]=1645, + ["afii64937"]=701, + ["arrowdblboth"]=8660, + ["arrowdblleft"]=8656, + ["arrowdblright"]=8658, + ["arrowupdnbse"]=8616, + ["bar"]=124, + ["circle"]=9675, + ["circlemultiply"]=8855, + ["circleplus"]=8853, + ["club"]=9827, + ["colonmonetary"]=8353, + ["dcroat"]=273, + ["dkshade"]=9619, + ["existential"]=8707, + ["female"]=9792, + ["gradient"]=8711, + ["heart"]=9829, + ["hookabovecomb"]=777, + ["invcircle"]=9689, + ["ldot"]=320, + ["longs"]=383, + ["ltshade"]=9617, + ["male"]=9794, + ["mu"]=181, + ["napostrophe"]=329, + ["notelement"]=8713, + ["omega1"]=982, + ["openbullet"]=9702, + ["orthogonal"]=8735, + ["oslashacute"]=511, + ["phi1"]=981, + ["propersubset"]=8834, + ["propersuperset"]=8835, + ["reflexsubset"]=8838, + ["reflexsuperset"]=8839, + ["shade"]=9618, + ["sigma1"]=962, + ["similar"]=8764, + ["smileface"]=9786, + ["spacehackarabic"]=32, + ["spade"]=9824, + ["theta1"]=977, + ["twodotenleader"]=8229, +} diff --git a/otfl-font-cid.lua b/otfl-font-cid.lua index d1c727a..a9bd3c5 100644 --- a/otfl-font-cid.lua +++ b/otfl-font-cid.lua @@ -12,11 +12,14 @@ local lpegmatch = lpeg.match local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) -fonts = fonts or { } -fonts.cid = fonts.cid or { } -fonts.cid.map = fonts.cid.map or { } -fonts.cid.max = fonts.cid.max or 10 +local report_otf = logs.reporter("fonts","otf loading") +local fonts = fonts + +fonts.cid = fonts.cid or { } +local cid = fonts.cid +cid.map = cid.map or { } +cid.max = cid.max or 10 -- original string parser: 0.109, lpeg parser: 0.036 seconds for Adobe-CNS1-4.cidmap -- @@ -25,12 +28,14 @@ fonts.cid.max = fonts.cid.max or 10 -- 1..95 0020 -- 99 3000 -local number = lpeg.C(lpeg.R("09","af","AF")^1) -local space = lpeg.S(" \n\r\t") +local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C + +local number = C(R("09","af","AF")^1) +local space = S(" \n\r\t") local spaces = space^0 -local period = lpeg.P(".") +local period = P(".") local periods = period * period -local name = lpeg.P("/") * lpeg.C((1-space)^1) +local name = P("/") * C((1-space)^1) local unicodes, names = { }, { } @@ -58,7 +63,7 @@ local grammar = lpeg.P { "start", named = (number * spaces * name) / do_name } -function fonts.cid.load(filename) +function cid.load(filename) local data = io.loaddata(filename) if data then unicodes, names = { }, { } @@ -79,23 +84,22 @@ end local template = "%s-%s-%s.cidmap" - local function locate(registry,ordering,supplement) local filename = format(template,registry,ordering,supplement) local hashname = lower(filename) - local cidmap = fonts.cid.map[hashname] + local cidmap = cid.map[hashname] if not cidmap then if trace_loading then - logs.report("load otf","checking cidmap, registry: %s, ordering: %s, supplement: %s, filename: %s",registry,ordering,supplement,filename) + report_otf("checking cidmap, registry: %s, ordering: %s, supplement: %s, filename: %s",registry,ordering,supplement,filename) end - local fullname = resolvers.find_file(filename,'cid') or "" + local fullname = resolvers.findfile(filename,'cid') or "" if fullname ~= "" then - cidmap = fonts.cid.load(fullname) + cidmap = cid.load(fullname) if cidmap then if trace_loading then - logs.report("load otf","using cidmap file %s",filename) + report_otf("using cidmap file %s",filename) end - fonts.cid.map[hashname] = cidmap + cid.map[hashname] = cidmap cidmap.usedname = file.basename(filename) return cidmap end @@ -104,18 +108,18 @@ local function locate(registry,ordering,supplement) return cidmap end -function fonts.cid.getmap(registry,ordering,supplement) +function cid.getmap(registry,ordering,supplement) -- cf Arthur R. we can safely scan upwards since cids are downward compatible local supplement = tonumber(supplement) if trace_loading then - logs.report("load otf","needed cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,supplement) + report_otf("needed cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,supplement) end local cidmap = locate(registry,ordering,supplement) if not cidmap then local cidnum = nil -- next highest (alternatively we could start high) - if supplement < fonts.cid.max then - for supplement=supplement+1,fonts.cid.max do + if supplement < cid.max then + for supplement=supplement+1,cid.max do local c = locate(registry,ordering,supplement) if c then cidmap, cidnum = c, supplement @@ -137,8 +141,8 @@ function fonts.cid.getmap(registry,ordering,supplement) if cidmap and cidnum > 0 then for s=0,cidnum-1 do filename = format(template,registry,ordering,s) - if not fonts.cid.map[filename] then - fonts.cid.map[filename] = cidmap -- copy of ref + if not cid.map[filename] then + cid.map[filename] = cidmap -- copy of ref end end end diff --git a/otfl-font-clr.lua b/otfl-font-clr.lua index e02d22a..a1fd8ff 100644 --- a/otfl-font-clr.lua +++ b/otfl-font-clr.lua @@ -94,8 +94,8 @@ local sbox = node.id('sub_box') local function lookup_next_color(head) for n in node.traverse(head) do if n.id == glyph then - if fonts.ids[n.font] and fonts.ids[n.font].color then - return fonts.ids[n.font].color + if fonts.identifiers[n.font] and fonts.identifiers[n.font].color then + return fonts.identifiers[n.font].color else return -1 end @@ -119,7 +119,7 @@ local function node_colorize(head, current_color, next_color) local next_color_in = lookup_next_color(n.next) or next_color n.list, current_color = node_colorize(n.list, current_color, next_color_in) elseif n.id == glyph then - local tfmdata = fonts.ids[n.font] + local tfmdata = fonts.identifiers[n.font] if tfmdata and tfmdata.color then if tfmdata.color ~= current_color then local pushcolor = hex_to_rgba(tfmdata.color) diff --git a/otfl-font-def.lua b/otfl-font-def.lua index 8e64872..d56520b 100644 --- a/otfl-font-def.lua +++ b/otfl-font-def.lua @@ -6,49 +6,56 @@ if not modules then modules = { } end modules ['font-def'] = { license = "see context related readme files" } -local format, concat, gmatch, match, find, lower = string.format, table.concat, string.gmatch, string.match, string.find, string.lower +local concat = table.concat +local format, gmatch, match, find, lower, gsub = string.format, string.gmatch, string.match, string.find, string.lower, string.gsub local tostring, next = tostring, next local lpegmatch = lpeg.match +local 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") + --[[ldx-- <p>Here we deal with defining fonts. We do so by intercepting the default loader that only handles <l n='tfm'/>.</p> --ldx]]-- -fonts = fonts or { } -fonts.define = fonts.define or { } -fonts.tfm = fonts.tfm or { } -fonts.ids = fonts.ids or { } -fonts.vf = fonts.vf or { } -fonts.used = fonts.used or { } +local fonts = fonts +local tfm = fonts.tfm +local vf = fonts.vf + +fonts.used = allocate() + +tfm.readers = tfm.readers or { } +tfm.fonts = allocate() -local tfm = fonts.tfm -local vf = fonts.vf -local define = fonts.define +local readers = tfm.readers +local sequence = allocate { 'otf', 'ttf', 'afm', 'tfm', 'lua' } +readers.sequence = sequence -tfm.version = 1.01 -tfm.cache = containers.define("fonts", "tfm", tfm.version, false) -- better in font-tfm +tfm.version = 1.01 +tfm.cache = containers.define("fonts", "tfm", tfm.version, false) -- better in font-tfm +tfm.autoprefixedafm = true -- this will become false some day (catches texnansi-blabla.*) -define.method = "afm or tfm" -- afm, tfm, afm or tfm, tfm or afm -define.specify = fonts.define.specify or { } -define.methods = fonts.define.methods or { } +fonts.definers = fonts.definers or { } +local definers = fonts.definers -tfm.fonts = tfm.fonts or { } -tfm.readers = tfm.readers or { } -tfm.internalized = tfm.internalized or { } -- internal tex numbers +definers.specifiers = definers.specifiers or { } +local specifiers = definers.specifiers -tfm.readers.sequence = { 'otf', 'ttf', 'afm', 'tfm' } +specifiers.variants = allocate() +local variants = specifiers.variants -tfm.auto_afm = true +definers.method = "afm or tfm" -- afm, tfm, afm or tfm, tfm or afm +definers.methods = definers.methods or { } -local readers = tfm.readers -local sequence = readers.sequence +local findbinfile = resolvers.findbinfile --[[ldx-- <p>We hardly gain anything when we cache the final (pre scaled) @@ -77,7 +84,7 @@ and prepares a table that will move along as we proceed.</p> -- name name(sub) name(sub)*spec name*spec -- name@spec*oeps -local splitter, specifiers = nil, "" +local splitter, splitspecifiers = nil, "" local P, C, S, Cc = lpeg.P, lpeg.C, lpeg.S, lpeg.Cc @@ -86,13 +93,13 @@ local right = P(")") local colon = P(":") local space = P(" ") -define.defaultlookup = "file" +definers.defaultlookup = "file" local prefixpattern = P(false) -function define.add_specifier(symbol) - specifiers = specifiers .. symbol - local method = S(specifiers) +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) @@ -100,36 +107,39 @@ function define.add_specifier(symbol) splitter = P((lookup + Cc("")) * name * (sub + Cc("")) * (specification + Cc(""))) end -function define.add_lookup(str,default) +local function addlookup(str,default) prefixpattern = prefixpattern + P(str) end -define.add_lookup("file") -define.add_lookup("name") -define.add_lookup("spec") +definers.addlookup = addlookup -function define.get_specification(str) +addlookup("file") +addlookup("name") +addlookup("spec") + +local function getspecification(str) return lpegmatch(splitter,str) end -function define.register_split(symbol,action) - define.add_specifier(symbol) - define.specify[symbol] = action +definers.getspecification = getspecification + +function definers.registersplit(symbol,action,verbosename) + addspecifier(symbol) + variants[symbol] = action + if verbosename then + variants[verbosename] = action + end end -function define.makespecification(specification, lookup, name, sub, method, detail, size) +function definers.makespecification(specification, lookup, name, sub, method, detail, size) size = size or 655360 if trace_defining then - logs.report("define font","%s -> lookup: %s, name: %s, sub: %s, method: %s, detail: %s", + report_defining("%s -> lookup: %s, name: %s, sub: %s, method: %s, detail: %s", specification, (lookup ~= "" and lookup) or "[file]", (name ~= "" and name) or "-", (sub ~= "" and sub) or "-", (method ~= "" and method) or "-", (detail ~= "" and detail) or "-") end ---~ if specification.lookup then ---~ lookup = specification.lookup -- can come from xetex [] syntax ---~ specification.lookup = nil ---~ end if not lookup or lookup == "" then - lookup = define.defaultlookup + lookup = definers.defaultlookup end local t = { lookup = lookup, -- forced type @@ -146,10 +156,10 @@ function define.makespecification(specification, lookup, name, sub, method, deta return t end -function define.analyze(specification, size) +function definers.analyze(specification, size) -- can be optimized with locals - local lookup, name, sub, method, detail = define.get_specification(specification or "") - return define.makespecification(specification, lookup, name, sub, method, detail, size) + local lookup, name, sub, method, detail = getspecification(specification or "") + return definers.makespecification(specification, lookup, name, sub, method, detail, size) end --[[ldx-- @@ -158,17 +168,18 @@ end local sortedhashkeys = table.sortedhashkeys -function tfm.hash_features(specification) +function tfm.hashfeatures(specification) local features = specification.features if features then - local t = { } + local t, tn = { }, 0 local normal = features.normal if normal and next(normal) then local f = sortedhashkeys(normal) for i=1,#f do local v = f[i] if v ~= "number" and v ~= "features" then -- i need to figure this out, features - t[#t+1] = v .. '=' .. tostring(normal[v]) + tn = tn + 1 + t[tn] = v .. '=' .. tostring(normal[v]) end end end @@ -177,20 +188,22 @@ function tfm.hash_features(specification) local f = sortedhashkeys(vtf) for i=1,#f do local v = f[i] - t[#t+1] = v .. '=' .. tostring(vtf[v]) + tn = tn + 1 + t[tn] = v .. '=' .. tostring(vtf[v]) end end ---~ if specification.mathsize then ---~ t[#t+1] = "mathsize=" .. specification.mathsize ---~ end - if #t > 0 then + -- if specification.mathsize then + -- tn = tn + 1 + -- t[tn] = "mathsize=" .. specification.mathsize + -- end + if tn > 0 then return concat(t,"+") end end return "unknown" end -fonts.designsizes = { } +fonts.designsizes = allocate() --[[ldx-- <p>In principle we can share tfm tables when we are in node for a font, but then @@ -200,42 +213,43 @@ when we get rid of base mode we can optimize even further by sharing, but then w loose our testcases for <l n='luatex'/>.</p> --ldx]]-- -function tfm.hash_instance(specification,force) +function tfm.hashinstance(specification,force) local hash, size, fallbacks = specification.hash, specification.size, specification.fallbacks if force or not hash then - hash = tfm.hash_features(specification) + hash = tfm.hashfeatures(specification) specification.hash = hash end if size < 1000 and fonts.designsizes[hash] then - size = math.round(tfm.scaled(size, fonts.designsizes[hash])) + size = math.round(tfm.scaled(size,fonts.designsizes[hash])) specification.size = size end ---~ local mathsize = specification.mathsize or 0 ---~ if mathsize > 0 then ---~ local textsize = specification.textsize ---~ if fallbacks then ---~ return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ] @ ' .. fallbacks ---~ else ---~ return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ]' ---~ end ---~ else + -- local mathsize = specification.mathsize or 0 + -- if mathsize > 0 then + -- local textsize = specification.textsize + -- if fallbacks then + -- return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ] @ ' .. fallbacks + -- else + -- return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ]' + -- end + -- else if fallbacks then return hash .. ' @ ' .. tostring(size) .. ' @ ' .. fallbacks else return hash .. ' @ ' .. tostring(size) end ---~ end + -- end end --[[ldx-- <p>We can resolve the filename using the next function:</p> --ldx]]-- -define.resolvers = resolvers +definers.resolvers = definers.resolvers or { } +local resolvers = definers.resolvers -- todo: reporter -function define.resolvers.file(specification) +function resolvers.file(specification) local suffix = file.suffix(specification.name) if fonts.formats[suffix] then specification.forced = suffix @@ -243,7 +257,7 @@ function define.resolvers.file(specification) end end -function define.resolvers.name(specification) +function resolvers.name(specification) local resolve = fonts.names.resolve if resolve then local resolved, sub = fonts.names.resolve(specification) @@ -258,11 +272,11 @@ function define.resolvers.name(specification) end end else - define.resolvers.file(specification) + resolvers.file(specification) end end -function define.resolvers.spec(specification) +function resolvers.spec(specification) local resolvespec = fonts.names.resolvespec if resolvespec then specification.resolved, specification.sub = fonts.names.resolvespec(specification) @@ -271,13 +285,13 @@ function define.resolvers.spec(specification) specification.name = file.removesuffix(specification.resolved) end else - define.resolvers.name(specification) + resolvers.name(specification) end end -function define.resolve(specification) +function definers.resolve(specification) if not specification.resolved or specification.resolved == "" then -- resolved itself not per se in mapping hash - local r = define.resolvers[specification.lookup] + local r = resolvers[specification.lookup] if r then r(specification) end @@ -287,7 +301,16 @@ function define.resolve(specification) else specification.forced = specification.forced end - specification.hash = lower(specification.name .. ' @ ' .. tfm.hash_features(specification)) + -- for the moment here (goodies set outside features) + local goodies = specification.goodies + if goodies and goodies ~= "" then + local normalgoodies = specification.features.normal.goodies + if not normalgoodies or normalgoodies == "" then + specification.features.normal.goodies = goodies + end + end + -- + specification.hash = lower(specification.name .. ' @ ' .. tfm.hashfeatures(specification)) if specification.sub and specification.sub ~= "" then specification.hash = specification.sub .. ' @ ' .. specification.hash end @@ -311,21 +334,22 @@ specification yet.</p> --ldx]]-- function tfm.read(specification) - local hash = tfm.hash_instance(specification) + local hash = tfm.hashinstance(specification) local tfmtable = tfm.fonts[hash] -- hashes by size ! if not tfmtable then local forced = specification.forced or "" if forced ~= "" then - tfmtable = readers[lower(forced)](specification) + local reader = readers[lower(forced)] + tfmtable = reader and reader(specification) if not tfmtable then - logs.report("define font","forced type %s of %s not found",forced,specification.name) + report_defining("forced type %s of %s not found",forced,specification.name) end else for s=1,#sequence do -- reader sequence local reader = sequence[s] if readers[reader] then -- not really needed if trace_defining then - logs.report("define font","trying (reader sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") + report_defining("trying (reader sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") end tfmtable = readers[reader](specification) if tfmtable then @@ -344,13 +368,26 @@ function tfm.read(specification) else tfmtable.embedding = "subset" end + -- fonts.goodies.postprocessors.apply(tfmdata) -- only here + local postprocessors = tfmtable.postprocessors + if postprocessors then + for i=1,#postprocessors do + local extrahash = postprocessors[i](tfmtable) -- after scaling etc + if type(extrahash) == "string" and extrahash ~= "" then + -- e.g. a reencoding needs this + extrahash = gsub(lower(extrahash),"[^a-z]","-") + tfmtable.fullname = format("%s-%s",tfmtable.fullname,extrahash) + end + end + end + -- tfm.fonts[hash] = tfmtable fonts.designsizes[specification.hash] = tfmtable.designsize -- we only know this for sure after loading once --~ tfmtable.mode = specification.features.normal.mode or "base" end end if not tfmtable then - logs.report("define font","font with name %s is not found",specification.name) + report_defining("font with asked name '%s' is not found using lookup '%s'",specification.name,specification.lookup) end return tfmtable end @@ -359,158 +396,35 @@ end <p>For virtual fonts we need a slightly different approach:</p> --ldx]]-- -function tfm.read_and_define(name,size) -- no id - local specification = define.analyze(name,size) +function tfm.readanddefine(name,size) -- no id + local specification = definers.analyze(name,size) local method = specification.method - if method and define.specify[method] then - specification = define.specify[method](specification) + if method and variants[method] then + specification = variants[method](specification) end - specification = define.resolve(specification) - local hash = tfm.hash_instance(specification) - local id = define.registered(hash) + specification = definers.resolve(specification) + local hash = tfm.hashinstance(specification) + local id = definers.registered(hash) if not id then - local fontdata = tfm.read(specification) - if fontdata then - fontdata.hash = hash - id = font.define(fontdata) - define.register(fontdata,id) - tfm.cleanup_table(fontdata) + local tfmdata = tfm.read(specification) + if tfmdata then + tfmdata.hash = hash + id = font.define(tfmdata) + definers.register(tfmdata,id) + tfm.cleanuptable(tfmdata) else id = 0 -- signal end end - return fonts.ids[id], id + return fonts.identifiers[id], id end --[[ldx-- -<p>Next follow the readers. This code was written while <l n='luatex'/> -evolved. Each one has its own way of dealing with its format.</p> ---ldx]]-- - -local function check_tfm(specification,fullname) - -- ofm directive blocks local path search unless set; btw, in context we - -- don't support ofm files anyway as this format is obsolete - local foundname = resolvers.findbinfile(fullname, 'tfm') or "" -- just to be sure - if foundname == "" then - foundname = resolvers.findbinfile(fullname, 'ofm') or "" -- bonus for usage outside context - end - if foundname ~= "" then - specification.filename, specification.format = foundname, "ofm" - return tfm.read_from_tfm(specification) - end -end - -local function check_afm(specification,fullname) - local foundname = resolvers.findbinfile(fullname, 'afm') or "" -- just to be sure - if foundname == "" and tfm.auto_afm then - local encoding, shortname = match(fullname,"^(.-)%-(.*)$") -- context: encoding-name.* - if encoding and shortname and fonts.enc.known[encoding] then - shortname = resolvers.findbinfile(shortname,'afm') or "" -- just to be sure - if shortname ~= "" then - foundname = shortname - -- tfm.set_normal_feature(specification,'encoding',encoding) -- will go away - if trace_loading then - logs.report("load afm","stripping encoding prefix from filename %s",afmname) - end - end - end - end - if foundname ~= "" then - specification.filename, specification.format = foundname, "afm" - return tfm.read_from_afm(specification) - end -end - -function readers.tfm(specification) - local fullname, tfmtable = specification.filename or "", nil - if fullname == "" then - local forced = specification.forced or "" - if forced ~= "" then - tfmtable = check_tfm(specification,specification.name .. "." .. forced) - end - if not tfmtable then - tfmtable = check_tfm(specification,specification.name) - end - else - tfmtable = check_tfm(specification,fullname) - end - return tfmtable -end - -function readers.afm(specification,method) - local fullname, tfmtable = specification.filename or "", nil - if fullname == "" then - local forced = specification.forced or "" - if forced ~= "" then - tfmtable = check_afm(specification,specification.name .. "." .. forced) - end - if not tfmtable then - method = method or define.method or "afm or tfm" - if method == "tfm" then - tfmtable = check_tfm(specification,specification.name) - elseif method == "afm" then - tfmtable = check_afm(specification,specification.name) - elseif method == "tfm or afm" then - tfmtable = check_tfm(specification,specification.name) or check_afm(specification,specification.name) - else -- method == "afm or tfm" or method == "" then - tfmtable = check_afm(specification,specification.name) or check_tfm(specification,specification.name) - end - end - else - tfmtable = check_afm(specification,fullname) - end - return tfmtable -end - --- maybe some day a set of names - -local function check_otf(forced,specification,suffix,what) - local name = specification.name - if forced then - name = file.addsuffix(name,suffix,true) - end - local fullname, tfmtable = resolvers.findbinfile(name,suffix) or "", nil -- one shot - if fullname == "" then - local fb = fonts.names.old_to_new[name] - if fb then - fullname = resolvers.findbinfile(fb,suffix) or "" - end - end - if fullname == "" then - local fb = fonts.names.new_to_old[name] - if fb then - fullname = resolvers.findbinfile(fb,suffix) or "" - end - end - if fullname ~= "" then - specification.filename, specification.format = fullname, what -- hm, so we do set the filename, then - tfmtable = tfm.read_from_open_type(specification) -- we need to do it for all matches / todo - end - return tfmtable -end - -function readers.opentype(specification,suffix,what) - local forced = specification.forced or "" - if forced == "otf" then - return check_otf(true,specification,forced,"opentype") - elseif forced == "ttf" or forced == "ttc" or forced == "dfont" then - return check_otf(true,specification,forced,"truetype") - else - return check_otf(false,specification,suffix,what) - end -end - -function readers.otf (specification) return readers.opentype(specification,"otf","opentype") end -function readers.ttf (specification) return readers.opentype(specification,"ttf","truetype") end -function readers.ttc (specification) return readers.opentype(specification,"ttf","truetype") end -- !! -function readers.dfont(specification) return readers.opentype(specification,"ttf","truetype") end -- !! - ---[[ldx-- <p>We need to check for default features. For this we provide a helper function.</p> --ldx]]-- -function define.check(features,defaults) -- nb adapts features ! +function definers.check(features,defaults) -- nb adapts features ! local done = false if features and next(features) then for k,v in next, defaults do @@ -525,7 +439,7 @@ function define.check(features,defaults) -- nb adapts features ! end --[[ldx-- -<p>So far the specifyers. Now comes the real definer. Here we cache +<p>So far the specifiers. Now comes the real definer. Here we cache based on id's. Here we also intercept the virtual font handler. Since it evolved stepwise I may rewrite this bit (combine code).</p> @@ -536,27 +450,29 @@ not gain much. By the way, passing id's back to in the callback was introduced later in the development.</p> --ldx]]-- -define.last = nil +local lastdefined = nil -- we don't want this one to end up in s-tra-02 +local internalized = { } -function define.register(fontdata,id) - if fontdata and id then - local hash = fontdata.hash - if not tfm.internalized[hash] then +function definers.current() -- or maybe current + return lastdefined +end + +function definers.register(tfmdata,id) -- will be overloaded + if tfmdata and id then + local hash = tfmdata.hash + if not internalized[hash] then if trace_defining then - logs.report("define font","loading at 2 id %s, hash: %s",id or "?",hash or "?") + report_defining("registering font, id: %s, hash: %s",id or "?",hash or "?") end - fonts.identifiers[id] = fontdata - fonts.characters [id] = fontdata.characters - fonts.quads [id] = fontdata.parameters.quad - -- todo: extra functions, e.g. setdigitwidth etc in list - tfm.internalized[hash] = id + fonts.identifiers[id] = tfmdata + internalized[hash] = id end end end -function define.registered(hash) - local id = tfm.internalized[hash] - return id, id and fonts.ids[id] +function definers.registered(hash) -- will be overloaded + local id = internalized[hash] + return id, id and fonts.identifiers[id] end local cache_them = false @@ -569,7 +485,7 @@ function tfm.make(specification) -- however, when virtual tricks are used as feature (makes more -- sense) we scale the commands in fonts.tfm.scale (and set the -- factor there) - local fvm = define.methods[specification.features.vtf.preset] + local fvm = definers.methods.variants[specification.features.vtf.preset] if fvm then return fvm(specification) else @@ -577,80 +493,79 @@ function tfm.make(specification) end end -function define.read(specification,size,id) -- id can be optional, name can already be table +function definers.read(specification,size,id) -- id can be optional, name can already be table statistics.starttiming(fonts) if type(specification) == "string" then - specification = define.analyze(specification,size) + specification = definers.analyze(specification,size) end local method = specification.method - if method and define.specify[method] then - specification = define.specify[method](specification) + if method and variants[method] then + specification = variants[method](specification) end - specification = define.resolve(specification) - local hash = tfm.hash_instance(specification) + specification = definers.resolve(specification) + local hash = tfm.hashinstance(specification) if cache_them then - local fontdata = containers.read(fonts.cache,hash) -- for tracing purposes + local tfmdata = containers.read(fonts.cache,hash) -- for tracing purposes end - local fontdata = define.registered(hash) -- id - if not fontdata then + local tfmdata = definers.registered(hash) -- id + if not tfmdata then if specification.features.vtf and specification.features.vtf.preset then - fontdata = tfm.make(specification) + tfmdata = tfm.make(specification) else - fontdata = tfm.read(specification) - if fontdata then - tfm.check_virtual_id(fontdata) + tfmdata = tfm.read(specification) + if tfmdata then + tfm.checkvirtualid(tfmdata) end end if cache_them then - fontdata = containers.write(fonts.cache,hash,fontdata) -- for tracing purposes + tfmdata = containers.write(fonts.cache,hash,tfmdata) -- for tracing purposes end - if fontdata then - fontdata.hash = hash - fontdata.cache = "no" + if tfmdata then + tfmdata.hash = hash + tfmdata.cache = "no" if id then - define.register(fontdata,id) + definers.register(tfmdata,id) end end end - define.last = fontdata or id -- todo ! ! ! ! ! - if not fontdata then - logs.report("define font", "unknown font %s, loading aborted",specification.name) - elseif trace_defining and type(fontdata) == "table" then - logs.report("define font","using %s font with id %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s", - fontdata.type or "unknown", - id or "?", - fontdata.name or "?", - fontdata.size or "default", - fontdata.encodingbytes or "?", - fontdata.encodingname or "unicode", - fontdata.fullname or "?", - file.basename(fontdata.filename or "?")) - + lastdefined = tfmdata or id -- todo ! ! ! ! ! + if not tfmdata then -- or id? + report_defining( "unknown font %s, loading aborted",specification.name) + elseif trace_defining and type(tfmdata) == "table" then + report_defining("using %s font with id %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s", + tfmdata.type or "unknown", + id or "?", + tfmdata.name or "?", + tfmdata.size or "default", + tfmdata.encodingbytes or "?", + tfmdata.encodingname or "unicode", + tfmdata.fullname or "?", + file.basename(tfmdata.filename or "?")) end statistics.stoptiming(fonts) - return fontdata + return tfmdata end function vf.find(name) name = file.removesuffix(file.basename(name)) - if tfm.resolve_vf then + if tfm.resolvevirtualtoo then local format = fonts.logger.format(name) if format == 'tfm' or format == 'ofm' then if trace_defining then - logs.report("define font","locating vf for %s",name) + report_defining("locating vf for %s",name) end - return resolvers.findbinfile(name,"ovf") + return findbinfile(name,"ovf") else if trace_defining then - logs.report("define font","vf for %s is already taken care of",name) + report_defining("vf for %s is already taken care of",name) end return nil -- "" end else if trace_defining then - logs.report("define font","locating vf for %s",name) + report_defining("locating vf for %s",name) end - return resolvers.findbinfile(name,"ovf") + return findbinfile(name,"ovf") end end @@ -658,5 +573,5 @@ end <p>We overload both the <l n='tfm'/> and <l n='vf'/> readers.</p> --ldx]]-- -callbacks.register('define_font' , define.read, "definition of fonts (tfmtable preparation)") -callbacks.register('find_vf_file', vf.find , "locating virtual fonts, insofar needed") -- not that relevant any more +callbacks.register('define_font' , definers.read, "definition of fonts (tfmtable preparation)") +callbacks.register('find_vf_file', vf.find, "locating virtual fonts, insofar needed") -- not that relevant any more diff --git a/otfl-font-dum.lua b/otfl-font-dum.lua index c9ffb63..54b631d 100644 --- a/otfl-font-dum.lua +++ b/otfl-font-dum.lua @@ -10,28 +10,29 @@ fonts = fonts or { } -- general -fonts.otf.pack = false -fonts.tfm.resolve_vf = false -- no sure about this -fonts.tfm.fontname_mode = "specification" -- somehow latex needs this +fonts.otf.pack = false -- only makes sense in context +fonts.tfm.resolvevirtualtoo = false -- context specific (due to resolver) +fonts.tfm.fontnamemode = "specification" -- somehow latex needs this (changed name!) -- readers fonts.tfm.readers = fonts.tfm.readers or { } -fonts.tfm.readers.sequence = { 'otf', 'ttf', 'tfm' } +fonts.tfm.readers.sequence = { 'otf', 'ttf', 'tfm', 'lua' } fonts.tfm.readers.afm = nil -- define -fonts.define = fonts.define or { } +fonts.definers = fonts.definers or { } +fonts.definers.specifiers = fonts.definers.specifiers or { } ---~ fonts.define.method = "tfm" +fonts.definers.specifiers.colonizedpreference = "name" -- is "file" in context -fonts.define.specify.colonized_default_lookup = "name" - -function fonts.define.get_specification(str) +function fonts.definers.getspecification(str) return "", str, "", ":", str end +fonts.definers.registersplit("",fonts.definers.specifiers.variants[":"]) -- we add another one for catching lone [names] + -- logger fonts.logger = fonts.logger or { } @@ -63,7 +64,7 @@ function fonts.names.resolve(name,sub) if basename and basename ~= "" then for i=1,#fileformats do local format = fileformats[i] - local foundname = resolvers.find_file(basename,format) or "" + local foundname = resolvers.findfile(basename,format) or "" if foundname ~= "" then data = dofile(foundname) break @@ -90,6 +91,10 @@ end fonts.names.resolvespec = fonts.names.resolve -- only supported in mkiv +function fonts.names.getfilename(askedname,suffix) -- only supported in mkiv + return "" +end + -- For the moment we put this (adapted) pseudo feature here. table.insert(fonts.triggers,"itlc") @@ -158,119 +163,20 @@ fonts.protrusions.setups = fonts.protrusions.setups or { } local setups = fonts.protrusions.setups --- As this is experimental code, users should not depend on it. The --- implications are still discussed on the ConTeXt Dev List and we're --- not sure yet what exactly the spec is (the next code is tested with --- a gyre font patched by / fea file made by Khaled Hosny). The double --- trick should not be needed it proper hanging punctuation is used in --- which case values < 1 can be used. --- --- preferred (in context, usine vectors): --- --- \definefontfeature[whatever][default][mode=node,protrusion=quality] --- --- using lfbd and rtbd, with possibibility to enable only one side : --- --- \definefontfeature[whocares][default][mode=node,protrusion=yes, opbd=yes,script=latn] --- \definefontfeature[whocares][default][mode=node,protrusion=right,opbd=yes,script=latn] --- --- idem, using multiplier --- --- \definefontfeature[whocares][default][mode=node,protrusion=2,opbd=yes,script=latn] --- \definefontfeature[whocares][default][mode=node,protrusion=double,opbd=yes,script=latn] --- --- idem, using named feature file (less frozen): --- --- \definefontfeature[whocares][default][mode=node,protrusion=2,opbd=yes,script=latn,featurefile=texgyrepagella-regularxx.fea] - -local function map_opbd_onto_protrusion(tfmdata,value,opbd) - local characters, descriptions = tfmdata.characters, tfmdata.descriptions - local otfdata = tfmdata.shared.otfdata - local singles = otfdata.shared.featuredata.gpos_single - local script, language = tfmdata.script, tfmdata.language - local done, factor, left, right = false, 1, 1, 1 - local setup = setups[value] - if setup then - factor = setup.factor or 1 - left = setup.left or 1 - right = setup.right or 1 - else - factor = tonumber(value) or 1 - end - if opbd ~= "right" then - local validlookups, lookuplist = fonts.otf.collect_lookups(otfdata,"lfbd",script,language) - if validlookups then - for i=1,#lookuplist do - local lookup = lookuplist[i] - local data = singles[lookup] - if data then - if trace_protrusion then - logs.report("fonts","set left protrusion using lfbd lookup '%s'",lookup) - end - for k, v in next, data do - -- local p = - v[3] / descriptions[k].width-- or 1 ~= 0 too but the same - local p = - (v[1] / 1000) * factor * left - characters[k].left_protruding = p - if trace_protrusion then - logs.report("opbd","lfbd -> %s -> 0x%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," ")) - end - end - done = true - end - end - end - end - if opbd ~= "left" then - local validlookups, lookuplist = fonts.otf.collect_lookups(otfdata,"rtbd",script,language) - if validlookups then - for i=1,#lookuplist do - local lookup = lookuplist[i] - local data = singles[lookup] - if data then - if trace_protrusion then - logs.report("fonts","set right protrusion using rtbd lookup '%s'",lookup) - end - for k, v in next, data do - -- local p = v[3] / descriptions[k].width -- or 3 - local p = (v[1] / 1000) * factor * right - characters[k].right_protruding = p - if trace_protrusion then - logs.report("opbd","rtbd -> %s -> 0x%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," ")) - end - end - end - done = true - end - end - end - tfmdata.auto_protrude = done -end - --- The opbd test is just there because it was discussed on the --- context development list. However, the mentioned fxlbi.otf font --- only has some kerns for digits. So, consider this feature not --- supported till we have a proper test font. - function fonts.initializers.common.protrusion(tfmdata,value) if value then - local opbd = tfmdata.shared.features.opbd - if opbd then - -- possible values: left right both yes no (experimental) - map_opbd_onto_protrusion(tfmdata,value,opbd) - elseif value then - local setup = setups[value] - if setup then - local factor, left, right = setup.factor or 1, setup.left or 1, setup.right or 1 - local emwidth = tfmdata.parameters.quad - tfmdata.auto_protrude = true - for i, chr in next, tfmdata.characters do - local v, pl, pr = setup[i], nil, nil - if v then - pl, pr = v[1], v[2] - end - if pl and pl ~= 0 then chr.left_protruding = left *pl*factor end - if pr and pr ~= 0 then chr.right_protruding = right*pr*factor end + local setup = setups[value] + if setup then + local factor, left, right = setup.factor or 1, setup.left or 1, setup.right or 1 + local emwidth = tfmdata.parameters.quad + tfmdata.auto_protrude = true + for i, chr in next, tfmdata.characters do + local v, pl, pr = setup[i], nil, nil + if v then + pl, pr = v[1], v[2] end + if pl and pl ~= 0 then chr.left_protruding = left *pl*factor end + if pr and pr ~= 0 then chr.right_protruding = right*pr*factor end end end end @@ -309,7 +215,7 @@ fonts.initializers.node.otf.expansion = fonts.initializers.common.expansion -- left over -function fonts.register_message() +function fonts.registermessage() end -- example vectors @@ -360,10 +266,16 @@ fonts.otf.meanings.normalize = fonts.otf.meanings.normalize or function(t) end end +-- needed (different in context) + +function fonts.otf.scriptandlanguage(tfmdata) + return tfmdata.script, tfmdata.language +end + -- bonus -function fonts.otf.name_to_slot(name) - local tfmdata = fonts.ids[font.current()] +function fonts.otf.nametoslot(name) + local tfmdata = fonts.identifiers[font.current()] if tfmdata and tfmdata.shared then local otfdata = tfmdata.shared.otfdata local unicode = otfdata.luatex.unicodes[name] @@ -373,7 +285,7 @@ end function fonts.otf.char(n) if type(n) == "string" then - n = fonts.otf.name_to_slot(n) + n = fonts.otf.nametoslot(n) end if type(n) == "number" then tex.sprint("\\char" .. n) @@ -398,3 +310,45 @@ fonts.strippables = table.tohash { 0xE0077, 0xE0078, 0xE0079, 0xE007A, 0xE007B, 0xE007C, 0xE007D, 0xE007E, 0xE007F, } +-- \font\test=file:somefont:reencode=mymessup +-- +-- fonts.enc.reencodings.mymessup = { +-- [109] = 110, -- m +-- [110] = 109, -- n +-- } + +fonts.enc = fonts.enc or {} +local reencodings = { } +fonts.enc.reencodings = reencodings + +local function specialreencode(tfmdata,value) + -- we forget about kerns as we assume symbols and we + -- could issue a message if ther are kerns but it's + -- a hack anyway so we odn't care too much here + 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 + -- if we use the font otherwise luatex gets confused so + -- we return an additional hash component for fullname + 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 + +table.insert(fonts.manipulators,"reencode") +fonts.initializers.base.otf.reencode = reencode diff --git a/otfl-font-ini.lua b/otfl-font-ini.lua index c695ec4..df534c6 100644 --- a/otfl-font-ini.lua +++ b/otfl-font-ini.lua @@ -6,6 +6,9 @@ if not modules then modules = { } end modules ['font-ini'] = { license = "see context related readme files" } +-- The font code will be upgraded and reorganized so that we have a +-- leaner generic code base and can do more tuning for context. + --[[ldx-- <p>Not much is happening here.</p> --ldx]]-- @@ -14,33 +17,38 @@ local utf = unicode.utf8 local format, serialize = string.format, table.serialize local write_nl = texio.write_nl local lower = string.lower +local allocate, mark = utilities.storage.allocate, utilities.storage.mark -if not fontloader then fontloader = fontforge end +local report_defining = logs.reporter("fonts","defining") fontloader.totable = fontloader.to_table -- vtf comes first -- fix comes last -fonts = fonts or { } +fonts = fonts or { } -fonts.ids = fonts.ids or { } fonts.identifiers = fonts.ids -- aka fontdata -fonts.chr = fonts.chr or { } fonts.characters = fonts.chr -- aka chardata -fonts.qua = fonts.qua or { } fonts.quads = fonts.qua -- aka quaddata +-- beware, some already defined -fonts.tfm = fonts.tfm or { } +fonts.identifiers = mark(fonts.identifiers or { }) -- fontdata +-----.characters = mark(fonts.characters or { }) -- chardata +-----.csnames = mark(fonts.csnames or { }) -- namedata +-----.quads = mark(fonts.quads or { }) -- quaddata -fonts.mode = 'base' -fonts.private = 0xF0000 -- 0x10FFFF -fonts.verbose = false -- more verbose cache tables +--~ fonts.identifiers[0] = { -- nullfont +--~ characters = { }, +--~ descriptions = { }, +--~ name = "nullfont", +--~ } -fonts.ids[0] = { -- nullfont - characters = { }, - descriptions = { }, - name = "nullfont", -} +fonts.tfm = fonts.tfm or { } +fonts.vf = fonts.vf or { } +fonts.afm = fonts.afm or { } +fonts.pfb = fonts.pfb or { } +fonts.otf = fonts.otf or { } -fonts.chr[0] = { } +fonts.privateoffset = 0xF0000 -- 0x10FFFF +fonts.verbose = false -- more verbose cache tables (will move to context namespace) fonts.methods = fonts.methods or { base = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, @@ -62,18 +70,28 @@ fonts.triggers = fonts.triggers or { fonts.processors = fonts.processors or { } +fonts.analyzers = fonts.analyzers or { + useunicodemarks = false, +} + fonts.manipulators = fonts.manipulators or { } -fonts.define = fonts.define or { } -fonts.define.specify = fonts.define.specify or { } -fonts.define.specify.synonyms = fonts.define.specify.synonyms or { } +fonts.tracers = fonts.tracers or { +} + +fonts.typefaces = fonts.typefaces or { +} + +fonts.definers = fonts.definers or { } +fonts.definers.specifiers = fonts.definers.specifiers or { } +fonts.definers.specifiers.synonyms = fonts.definers.specifiers.synonyms or { } -- tracing -if not fonts.color then +if not fonts.colors then - fonts.color = { + fonts.colors = allocate { set = function() end, reset = function() end, } @@ -82,7 +100,7 @@ end -- format identification -fonts.formats = { } +fonts.formats = allocate() function fonts.fontformat(filename,default) local extname = lower(file.extname(filename)) @@ -90,7 +108,11 @@ function fonts.fontformat(filename,default) if format then return format else - logs.report("fonts define","unable to determine font format for '%s'",filename) + report_defining("unable to determine font format for '%s'",filename) return default end end + +-- readers + +fonts.tfm.readers = fonts.tfm.readers or { } diff --git a/otfl-font-xtx.lua b/otfl-font-ltx.lua index 8237851..12fef44 100644 --- a/otfl-font-xtx.lua +++ b/otfl-font-ltx.lua @@ -1,4 +1,4 @@ -if not modules then modules = { } end modules ['font-xtx'] = { +if not modules then modules = { } end modules ['font-ltx'] = { version = 1.001, comment = "companion to font-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", @@ -31,36 +31,14 @@ well and that does not work too well with the general design of the specifier.</p> --ldx]]-- ---~ function fonts.define.specify.colonized(specification) -- xetex mode ---~ local list = { } ---~ if specification.detail and specification.detail ~= "" then ---~ for v in gmatch(specification.detail,"%s*([^;]+)%s*") do ---~ local a, b = match(v,"^(%S*)%s*=%s*(%S*)$") ---~ if a and b then ---~ list[a] = b:is_boolean() ---~ if type(list[a]) == "nil" then ---~ list[a] = b ---~ end ---~ else ---~ local a, b = match(v,"^([%+%-]?)%s*(%S+)$") ---~ if a and b then ---~ list[b] = a ~= "-" ---~ end ---~ end ---~ end ---~ end ---~ specification.features.normal = list ---~ return specification ---~ end - ---~ check("oeps/BI:+a;-b;c=d") ---~ check("[oeps]/BI:+a;-b;c=d") ---~ check("file:oeps/BI:+a;-b;c=d") ---~ check("name:oeps/BI:+a;-b;c=d") +local fonts = fonts +local definers = fonts.definers +local specifiers = definers.specifiers +local normalize_meanings = fonts.otf.meanings.normalize local list = { } -fonts.define.specify.colonized_default_lookup = "file" +specifiers.colonizedpreference = "file" local function isstyle(s) local style = string.lower(s):split("/") @@ -157,11 +135,15 @@ local function parse_script(script) end end -local function issome () list.lookup = fonts.define.specify.colonized_default_lookup end +specifiers.colonizedpreference = "file" + +local function issome () list.lookup = specifiers.colonizedpreference 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 istrue (s) list[s] = true end +local function isfalse(s) list[s] = false end local function iskey (k,v) if k == "script" then parse_script(v) @@ -169,28 +151,25 @@ 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 spaces = lpeg.P(" ")^0 -local namespec = (1-lpeg.S("/:("))^0 -- was: (1-lpeg.S("/: ("))^0 -local filespec = (lpeg.R("az", "AZ") * lpeg.P(":"))^-1 * (1-lpeg.S(":("))^1 -local crapspec = spaces * lpeg.P("/") * (((1-lpeg.P(":"))^0)/isstyle) * spaces -local filename = (lpeg.P("file:")/isfile * (filespec/thename)) + (lpeg.P("[") * lpeg.P(true)/isfile * (((1-lpeg.P("]"))^0)/thename) * lpeg.P("]")) -local fontname = (lpeg.P("name:")/isname * (namespec/thename)) + lpeg.P(true)/issome * (namespec/thename) -local sometext = (lpeg.R("az","AZ","09") + lpeg.S("+-."))^1 -local truevalue = lpeg.P("+") * spaces * (sometext/istrue) -local falsevalue = lpeg.P("-") * spaces * (sometext/isfalse) -local keyvalue = lpeg.P("+") + (lpeg.C(sometext) * spaces * lpeg.P("=") * spaces * lpeg.C(sometext))/iskey +local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C + +local spaces = P(" ")^0 +local namespec = (1-S("/:("))^0 -- was: (1-S("/: ("))^0 +local filespec = (R("az", "AZ") * P(":"))^-1 * (1-S(":("))^1 +local stylespec = spaces * P("/") * (((1-P(":"))^0)/isstyle) * spaces +local filename = (P("file:")/isfile * (filespec/thename)) + (P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]")) +local fontname = (P("name:")/isname * (namespec/thename)) + 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 = P("+") + (C(sometext) * spaces * P("=") * spaces * C(sometext))/iskey local somevalue = sometext/istrue -local subvalue = lpeg.P("(") * (lpeg.C(lpeg.P(1-lpeg.S("()"))^1)/issub) * lpeg.P(")") -- for Kim +local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces -local options = lpeg.P(":") * spaces * (lpeg.P(";")^0 * option)^0 -local pattern = (filename + fontname) * subvalue^0 * crapspec^0 * options^0 - -local normalize_meanings = fonts.otf.meanings.normalize +local options = P(":") * spaces * (P(";")^0 * option)^0 +local pattern = (filename + fontname) * subvalue^0 * stylespec^0 * options^0 -function fonts.define.specify.colonized(specification) -- xetex mode +local function colonized(specification) -- xetex mode list = { } lpegmatch(pattern,specification.specification) if list.style then @@ -202,10 +181,10 @@ function fonts.define.specify.colonized(specification) -- xetex mode list.optsize = nil end if list.name then - if resolvers.find_file(list.name, "tfm") then + if resolvers.findfile(list.name, "tfm") then list.lookup = "file" list.name = file.addsuffix(list.name, "tfm") - elseif resolvers.find_file(list.name, "ofm") then + elseif resolvers.findfile(list.name, "ofm") then list.lookup = "file" list.name = file.addsuffix(list.name, "ofm") end @@ -221,9 +200,9 @@ function fonts.define.specify.colonized(specification) -- xetex mode specification.sub = list.sub list.sub = nil end --- specification.features.normal = list + -- specification.features.normal = list specification.features.normal = normalize_meanings(list) return specification end -fonts.define.register_split(":", fonts.define.specify.colonized) +definers.registersplit(":",colonized,"cryptic") diff --git a/otfl-font-lua.lua b/otfl-font-lua.lua new file mode 100644 index 0000000..a6fcc16 --- /dev/null +++ b/otfl-font-lua.lua @@ -0,0 +1,45 @@ +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") + +fonts.formats.lua = "lua" + +local readers = fonts.tfm.readers + +local function check_lua(specification,fullname) + -- standard tex file lookup + local fullname = resolvers.findfile(fullname) or "" + if fullname ~= "" then + local loader = loadfile(fullname) + loader = loader and loader() + return loader and loader(specification) + end +end + +function readers.lua(specification) + local original = specification.specification + if trace_defining then + report_lua("using lua reader for '%s'",original) + end + local fullname, tfmtable = specification.filename or "", nil + if fullname == "" then + local forced = specification.forced or "" + if forced ~= "" then + tfmtable = check_lua(specification,specification.name .. "." .. forced) + end + if not tfmtable then + tfmtable = check_lua(specification,specification.name) + end + else + tfmtable = check_lua(specification,fullname) + end + return tfmtable +end diff --git a/otfl-font-map.lua b/otfl-font-map.lua index 2995087..26b22b6 100644 --- a/otfl-font-map.lua +++ b/otfl-font-map.lua @@ -14,7 +14,7 @@ local utfbyte = utf.byte local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) local trace_unimapping = false trackers.register("otf.unimapping", function(v) trace_unimapping = v end) -local ctxcatcodes = tex and tex.ctxcatcodes +local report_otf = logs.reporter("fonts","otf loading") --[[ldx-- <p>Eventually this code will disappear because map files are kind @@ -22,50 +22,51 @@ of obsolete. Some code may move to runtime or auxiliary modules.</p> <p>The name to unciode related code will stay of course.</p> --ldx]]-- -fonts = fonts or { } -fonts.map = fonts.map or { } +local fonts = fonts +fonts.map = fonts.map or { } -local function load_lum_table(filename) -- will move to font goodies +local function loadlumtable(filename) -- will move to font goodies local lumname = file.replacesuffix(file.basename(filename),"lum") - local lumfile = resolvers.find_file(lumname,"map") or "" + local lumfile = resolvers.findfile(lumname,"map") or "" if lumfile ~= "" and lfs.isfile(lumfile) then if trace_loading or trace_unimapping then - logs.report("load otf","enhance: loading %s ",lumfile) + report_otf("enhance: loading %s ",lumfile) end lumunic = dofile(lumfile) return lumunic, lumfile end end -local hex = lpeg.R("AF","09") +local P, R, S, C, Ct, Cc = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc + +local hex = R("AF","09") local hexfour = (hex*hex*hex*hex) / function(s) return tonumber(s,16) end local hexsix = (hex^1) / function(s) return tonumber(s,16) end -local dec = (lpeg.R("09")^1) / tonumber -local period = lpeg.P(".") - -local unicode = lpeg.P("uni") * (hexfour * (period + lpeg.P(-1)) * lpeg.Cc(false) + lpeg.Ct(hexfour^1) * lpeg.Cc(true)) -local ucode = lpeg.P("u") * (hexsix * (period + lpeg.P(-1)) * lpeg.Cc(false) + lpeg.Ct(hexsix ^1) * lpeg.Cc(true)) -local index = lpeg.P("index") * dec * lpeg.Cc(false) +local dec = (R("09")^1) / tonumber +local period = P(".") +local unicode = P("uni") * (hexfour * (period + P(-1)) * Cc(false) + Ct(hexfour^1) * Cc(true)) +local ucode = P("u") * (hexsix * (period + P(-1)) * Cc(false) + Ct(hexsix ^1) * Cc(true)) +local index = P("index") * dec * Cc(false) local parser = unicode + ucode + index local parsers = { } -local function make_name_parser(str) +local function makenameparser(str) if not str or str == "" then return parser else local p = parsers[str] if not p then - p = lpeg.P(str) * period * dec * lpeg.Cc(false) + p = P(str) * period * dec * Cc(false) parsers[str] = p end return p end end ---~ local parser = fonts.map.make_name_parser("Japan1") ---~ local parser = fonts.map.make_name_parser() +--~ local parser = fonts.map.makenameparser("Japan1") +--~ local parser = fonts.map.makenameparser() --~ local function test(str) --~ local b, a = lpegmatch(parser,str) --~ print((a and table.serialize(b)) or b) @@ -119,14 +120,14 @@ end --~ return s --~ end -fonts.map.load_lum_table = load_lum_table -fonts.map.make_name_parser = make_name_parser +fonts.map.loadlumtable = loadlumtable +fonts.map.makenameparser = makenameparser fonts.map.tounicode16 = tounicode16 fonts.map.tounicode16sequence = tounicode16sequence -local separator = lpeg.S("_.") -local other = lpeg.C((1 - separator)^1) -local ligsplitter = lpeg.Ct(other * (separator * other)^0) +local separator = S("_.") +local other = C((1 - separator)^1) +local ligsplitter = Ct(other * (separator * other)^0) --~ print(table.serialize(lpegmatch(ligsplitter,"this"))) --~ print(table.serialize(lpegmatch(ligsplitter,"this.that"))) @@ -134,7 +135,7 @@ local ligsplitter = lpeg.Ct(other * (separator * other)^0) --~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more"))) --~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more.that"))) -fonts.map.add_to_unicode = function(data,filename) +fonts.map.addtounicode = function(data,filename) local unicodes = data.luatex and data.luatex.unicodes if not unicodes then return @@ -145,11 +146,11 @@ fonts.map.add_to_unicode = function(data,filename) unicodes['zwj'] = unicodes['zwj'] or 0x200D unicodes['zwnj'] = unicodes['zwnj'] or 0x200C -- the tounicode mapping is sparse and only needed for alternatives - local tounicode, originals, ns, nl, private, unknown = { }, { }, 0, 0, fonts.private, format("%04X",utfbyte("?")) + local tounicode, originals, ns, nl, private, unknown = { }, { }, 0, 0, fonts.privateoffset, format("%04X",utfbyte("?")) data.luatex.tounicode, data.luatex.originals = tounicode, originals local lumunic, uparser, oparser if false then -- will become an option - lumunic = load_lum_table(filename) + lumunic = loadlumtable(filename) lumunic = lumunic and lumunic.tounicode end local cidinfo, cidnames, cidcodes = data.cidinfo @@ -157,16 +158,16 @@ fonts.map.add_to_unicode = function(data,filename) usedmap = usedmap and lower(usedmap) usedmap = usedmap and fonts.cid.map[usedmap] if usedmap then - oparser = usedmap and make_name_parser(cidinfo.ordering) + oparser = usedmap and makenameparser(cidinfo.ordering) cidnames = usedmap.names cidcodes = usedmap.unicodes end - uparser = make_name_parser() - local aglmap = fonts.map and fonts.map.agl_to_unicode + uparser = makenameparser() + local unicodevector = fonts.enc.agl.unicodes -- loaded runtime in context for index, glyph in next, data.glyphs do local name, unic = glyph.name, glyph.unicode or -1 -- play safe if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then - local unicode = (lumunic and lumunic[name]) or (aglmap and aglmap[name]) + local unicode = (lumunic and lumunic[name]) or unicodevector[name] if unicode then originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 end @@ -210,7 +211,7 @@ fonts.map.add_to_unicode = function(data,filename) -- skip elseif nplit == 1 then local base = split[1] - unicode = unicodes[base] or (aglmap and aglmap[base]) + unicode = unicodes[base] or unicodevector[base] if unicode then if type(unicode) == "table" then unicode = unicode[1] @@ -218,19 +219,25 @@ fonts.map.add_to_unicode = function(data,filename) originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 end else - local t = { } + local t, n = { }, 0 for l=1,nplit do local base = split[l] - local u = unicodes[base] or (aglmap and aglmap[base]) + local u = unicodes[base] or unicodevector[base] if not u then break elseif type(u) == "table" then - t[#t+1] = u[1] + n = n + 1 + t[n] = u[1] else - t[#t+1] = u + n = n + 1 + t[n] = u end end - if #t > 0 then -- done then + if n == 0 then -- done then + -- nothing + elseif n == 1 then + originals[index], tounicode[index], nl, unicode = t[1], tounicode16(t[1]), nl + 1, true + else originals[index], tounicode[index], nl, unicode = t, tounicode16sequence(t), nl + 1, true end end @@ -255,116 +262,13 @@ fonts.map.add_to_unicode = function(data,filename) for index, glyph in table.sortedhash(data.glyphs) do local toun, name, unic = tounicode[index], glyph.name, glyph.unicode or -1 -- play safe if toun then - logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) + report_otf("internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) else - logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) + report_otf("internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) end end end if trace_loading and (ns > 0 or nl > 0) then - logs.report("load otf","enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) + report_otf("enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) end end - --- the following is sort of obsolete --- --- fonts.map.data = fonts.map.data or { } --- fonts.map.encodings = fonts.map.encodings or { } --- fonts.map.loaded = fonts.map.loaded or { } --- fonts.map.line = fonts.map.line or { } --- --- function fonts.map.line.pdftex(e) --- if e.name and e.fontfile then --- local fullname = e.fullname or "" --- if e.slant and e.slant ~= 0 then --- if e.encoding then --- pdf.mapline(format('= %s %s "%g SlantFont" <%s <%s',e.name,fullname,e.slant,e.encoding,e.fontfile))) --- else --- pdf.mapline(format('= %s %s "%g SlantFont" <%s',e.name,fullname,e.slant,e.fontfile))) --- end --- elseif e.extend and e.extend ~= 1 and e.extend ~= 0 then --- if e.encoding then --- pdf.mapline(format('= %s %s "%g ExtendFont" <%s <%s',e.name,fullname,e.extend,e.encoding,e.fontfile))) --- else --- pdf.mapline(format('= %s %s "%g ExtendFont" <%s',e.name,fullname,e.extend,e.fontfile))) --- end --- else --- if e.encoding then --- pdf.mapline(format('= %s %s <%s <%s',e.name,fullname,e.encoding,e.fontfile))) --- else --- pdf.mapline(format('= %s %s <%s',e.name,fullname,e.fontfile))) --- end --- end --- else --- return nil --- end --- end --- --- function fonts.map.flush(backend) -- will also erase the accumulated data --- local flushline = fonts.map.line[backend or "pdftex"] or fonts.map.line.pdftex --- for _, e in next, fonts.map.data do --- flushline(e) --- end --- fonts.map.data = { } --- end --- --- fonts.map.line.dvips = fonts.map.line.pdftex --- fonts.map.line.dvipdfmx = function() end --- --- function fonts.map.convert_entries(filename) --- if not fonts.map.loaded[filename] then --- fonts.map.data, fonts.map.encodings = fonts.map.load_file(filename,fonts.map.data, fonts.map.encodings) --- fonts.map.loaded[filename] = true --- end --- end --- --- function fonts.map.load_file(filename, entries, encodings) --- entries = entries or { } --- encodings = encodings or { } --- local f = io.open(filename) --- if f then --- local data = f:read("*a") --- if data then --- for line in gmatch(data,"(.-)[\n\t]") do --- if find(line,"^[%#%%%s]") then --- -- print(line) --- else --- local extend, slant, name, fullname, fontfile, encoding --- line = gsub(line,'"(.+)"', function(s) --- extend = find(s,'"([^"]+) ExtendFont"') --- slant = find(s,'"([^"]+) SlantFont"') --- return "" --- end) --- if not name then --- -- name fullname encoding fontfile --- name, fullname, encoding, fontfile = match(line,"^(%S+)%s+(%S*)[%s<]+(%S*)[%s<]+(%S*)%s*$") --- end --- if not name then --- -- name fullname (flag) fontfile encoding --- name, fullname, fontfile, encoding = match(line,"^(%S+)%s+(%S*)[%d%s<]+(%S*)[%s<]+(%S*)%s*$") --- end --- if not name then --- -- name fontfile --- name, fontfile = match(line,"^(%S+)%s+[%d%s<]+(%S*)%s*$") --- end --- if name then --- if encoding == "" then encoding = nil end --- entries[name] = { --- name = name, -- handy --- fullname = fullname, --- encoding = encoding, --- fontfile = fontfile, --- slant = tonumber(slant), --- extend = tonumber(extend) --- } --- encodings[name] = encoding --- elseif line ~= "" then --- -- print(line) --- end --- end --- end --- end --- f:close() --- end --- return entries, encodings --- end diff --git a/otfl-font-ota.lua b/otfl-font-ota.lua index 0e5b555..f972d28 100644 --- a/otfl-font-ota.lua +++ b/otfl-font-ota.lua @@ -17,46 +17,46 @@ local trace_cjk = false trackers.register("cjk.injections", function(v) t trackers.register("cjk.analyzing","otf.analyzing") -fonts = fonts or { } -fonts.analyzers = fonts.analyzers or { } -fonts.analyzers.initializers = fonts.analyzers.initializers or { node = { otf = { } } } -fonts.analyzers.methods = fonts.analyzers.methods or { node = { otf = { } } } +local fonts, nodes = fonts, nodes +local node = node local otf = fonts.otf local tfm = fonts.tfm -local initializers = fonts.analyzers.initializers -local methods = fonts.analyzers.methods +fonts.analyzers = fonts.analyzers or { } +local analyzers = fonts.analyzers -local glyph = node.id('glyph') -local glue = node.id('glue') -local penalty = node.id('penalty') +analyzers.initializers = analyzers.initializers or { node = { otf = { } } } +analyzers.methods = analyzers.methods or { node = { otf = { } } } + +local initializers = analyzers.initializers +local methods = analyzers.methods + +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph local set_attribute = node.set_attribute local has_attribute = node.has_attribute local traverse_id = node.traverse_id local traverse_node_list = node.traverse -local fontdata = fonts.ids -local state = attributes.private('state') +local fontdata = fonts.identifiers +local state = attributes.private('state') +local categories = characters and characters.categories or { } -- sorry, only in context -local fcs = (fonts.color and fonts.color.set) or function() end -local fcr = (fonts.color and fonts.color.reset) or function() end +local fontscolors = fonts.colors +local fcs = (fontscolors and fontscolors.set) or function() end +local fcr = (fontscolors and fontscolors.reset) or function() end -local a_to_script = otf.a_to_script -local a_to_language = otf.a_to_language -- in the future we will use language/script attributes instead of the -- font related value, but then we also need dynamic features which is -- somewhat slower; and .. we need a chain of them +local scriptandlanguage = otf.scriptandlanguage + function fonts.initializers.node.otf.analyze(tfmdata,value,attr) - local script, language - if attr and attr > 0 then - script, language = a_to_script[attr], a_to_language[attr] - else - script, language = tfmdata.script, tfmdata.language - end + local script, language = otf.scriptandlanguage(tfmdata,attr) local action = initializers[script] if action then if type(action) == "function" then @@ -73,12 +73,7 @@ end function fonts.methods.node.otf.analyze(head,font,attr) local tfmdata = fontdata[font] - local script, language - if attr and attr > 0 then - script, language = a_to_script[attr], a_to_language[attr] - else - script, language = tfmdata.script, tfmdata.language - end + local script, language = otf.scriptandlanguage(tfmdata,attr) local action = methods[script] if action then if type(action) == "function" then @@ -98,7 +93,7 @@ table.insert(fonts.triggers,"analyze") -- we need a proper function for doing t -- latin -fonts.analyzers.methods.latn = fonts.analyzers.aux.setstate +analyzers.methods.latn = analyzers.aux.setstate -- this info eventually will go into char-def @@ -180,8 +175,8 @@ local function warning(current,what) end end -function fonts.analyzers.methods.nocolor(head,font,attr) - for n in traverse_node_list(head,glyph) do +function analyzers.methods.nocolor(head,font,attr) + for n in traverse_id(glyph_code,head) do if not font or n.font == font then fcr(n) end @@ -230,15 +225,16 @@ local function finish(first,last) return first, last end -function fonts.analyzers.methods.arab(head,font,attr) -- maybe make a special version with no trace +function analyzers.methods.arab(head,font,attr) -- maybe make a special version with no trace + local useunicodemarks = analyzers.useunicodemarks local tfmdata = fontdata[font] local marks = tfmdata.marks local first, last, current, done = nil, nil, head, false while current do - if current.id == glyph and current.subtype<256 and current.font == font and not has_attribute(current,state) then + if current.id == glyph_code and current.subtype<256 and current.font == font and not has_attribute(current,state) then done = true local char = current.char - if marks[char] then + if marks[char] or (useunicodemarks and categories[char] == "mn") then set_attribute(current,state,5) -- mark if trace_analyzing then fcs(current,"font:mark") end elseif isol[char] then -- can be zwj or zwnj too diff --git a/otfl-font-otb.lua b/otfl-font-otb.lua index e0528a4..8ee39b8 100644 --- a/otfl-font-otb.lua +++ b/otfl-font-otb.lua @@ -11,8 +11,9 @@ local format, gmatch, gsub, find, match, lower, strip = string.format, string.gm local type, next, tonumber, tostring = type, next, tonumber, tostring local lpegmatch = lpeg.match -local otf = fonts.otf -local tfm = fonts.tfm +local fonts = fonts +local otf = fonts.otf +local tfm = fonts.tfm local trace_baseinit = false trackers.register("otf.baseinit", function(v) trace_baseinit = v end) local trace_singles = false trackers.register("otf.singles", function(v) trace_singles = v end) @@ -22,6 +23,8 @@ local trace_ligatures = false trackers.register("otf.ligatures", function 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 wildcard = "*" local default = "dflt" @@ -41,8 +44,20 @@ local function gref(descriptions,n) local num, nam = { }, { } for i=1,#n do local ni = n[i] - num[i] = format("U+%04X",ni) - nam[i] = descriptions[ni].name or "?" + -- ! ! ! could be a helper ! ! ! + if type(ni) == "table" then + local nnum, nnam = { }, { } + for j=1,#ni do + local nj = ni[j] + nnum[j] = format("U+%04X",nj) + nnam[j] = descriptions[nj].name or "?" + end + num[i] = concat(nnum,"|") + nam[i] = concat(nnam,"|") + else + num[i] = format("U+%04X",ni) + nam[i] = descriptions[ni].name or "?" + end end return format("%s (%s)",concat(num," "), concat(nam," ")) else @@ -76,7 +91,7 @@ local function resolve_ligatures(tfmdata,ligatures,kind) local c, f, s = characters[uc], ligs[1], ligs[2] local uft, ust = unicodes[f] or 0, unicodes[s] or 0 if not uft or not ust then - logs.report("define otf","%s: unicode problem with base ligature %s = %s + %s",cref(kind),gref(descriptions,uc),gref(descriptions,uft),gref(descriptions,ust)) + report_prepare("%s: unicode problem with base ligature %s = %s + %s",cref(kind),gref(descriptions,uc),gref(descriptions,uft),gref(descriptions,ust)) -- some kind of error else if type(uft) == "number" then uft = { uft } end @@ -87,7 +102,7 @@ local function resolve_ligatures(tfmdata,ligatures,kind) local us = ust[usi] if changed[uf] or changed[us] then if trace_baseinit and trace_ligatures then - logs.report("define otf","%s: base ligature %s + %s ignored",cref(kind),gref(descriptions,uf),gref(descriptions,us)) + report_prepare("%s: base ligature %s + %s ignored",cref(kind),gref(descriptions,uf),gref(descriptions,us)) end else local first, second = characters[uf], us @@ -103,7 +118,7 @@ local function resolve_ligatures(tfmdata,ligatures,kind) t[second] = { type = 0, char = uc[1] } -- can this still happen? end if trace_baseinit and trace_ligatures then - logs.report("define otf","%s: base ligature %s + %s => %s",cref(kind),gref(descriptions,uf),gref(descriptions,us),gref(descriptions,uc)) + report_prepare("%s: base ligature %s + %s => %s",cref(kind),gref(descriptions,uf),gref(descriptions,us),gref(descriptions,uc)) end end end @@ -139,7 +154,7 @@ local splitter = lpeg.splitat(" ") local function prepare_base_substitutions(tfmdata,kind,value) -- we can share some code with the node features if value then local otfdata = tfmdata.shared.otfdata - local validlookups, lookuplist = otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) + local validlookups, lookuplist = otf.collectlookups(otfdata,kind,tfmdata.script,tfmdata.language) if validlookups then local ligatures = { } local unicodes = tfmdata.unicodes -- names to unicodes @@ -154,12 +169,12 @@ local function prepare_base_substitutions(tfmdata,kind,value) -- we can share so if pv then local upv = unicodes[pv] if upv then - if type(upv) == "table" then + if type(upv) == "table" then -- zero change that table upv = upv[1] end if characters[upv] then if trace_baseinit and trace_singles then - logs.report("define otf","%s: base substitution %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upv)) + report_prepare("%s: base substitution %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upv)) end changed[k] = upv end @@ -182,12 +197,12 @@ local function prepare_base_substitutions(tfmdata,kind,value) -- we can share so if pc then local upc = unicodes[pc] if upc then - if type(upc) == "table" then + if type(upc) == "table" then -- zero change that table upc = upc[1] end if characters[upc] then if trace_baseinit and trace_alternatives then - logs.report("define otf","%s: base alternate %s %s => %s",cref(kind,lookup),tostring(value),gref(descriptions,k),gref(descriptions,upc)) + report_prepare("%s: base alternate %s %s => %s",cref(kind,lookup),tostring(value),gref(descriptions,k),gref(descriptions,upc)) end changed[k] = upc end @@ -202,7 +217,7 @@ local function prepare_base_substitutions(tfmdata,kind,value) -- we can share so local upc = { lpegmatch(splitter,pc) } for i=1,#upc do upc[i] = unicodes[upc[i]] end -- we assume that it's no table - logs.report("define otf","%s: base ligature %s => %s",cref(kind,lookup),gref(descriptions,upc),gref(descriptions,k)) + report_prepare("%s: base ligature %s => %s",cref(kind,lookup),gref(descriptions,upc),gref(descriptions,k)) end ligatures[#ligatures+1] = { pc, k } end @@ -248,10 +263,10 @@ local function prepare_base_substitutions(tfmdata,kind,value) -- we can share so end end -local function prepare_base_kerns(tfmdata,kind,value) -- todo what kind of kerns, currently all +local function preparebasekerns(tfmdata,kind,value) -- todo what kind of kerns, currently all if value then local otfdata = tfmdata.shared.otfdata - local validlookups, lookuplist = otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language) + local validlookups, lookuplist = otf.collectlookups(otfdata,kind,tfmdata.script,tfmdata.language) if validlookups then local unicodes = tfmdata.unicodes -- names to unicodes local indices = tfmdata.indices @@ -261,7 +276,7 @@ local function prepare_base_kerns(tfmdata,kind,value) -- todo what kind of kerns for u, chr in next, characters do local d = descriptions[u] if d then - local dk = d.mykerns -- shared + local dk = d.kerns -- shared if dk then local s = sharedkerns[dk] if s == false then @@ -278,7 +293,7 @@ local function prepare_base_kerns(tfmdata,kind,value) -- todo what kind of kerns if v ~= 0 and not t[k] then -- maybe no 0 test here t[k], done = v, true if trace_baseinit and trace_kerns then - logs.report("define otf","%s: base kern %s + %s => %s",cref(kind,lookup),gref(descriptions,u),gref(descriptions,k),v) + report_prepare("%s: base kern %s + %s => %s",cref(kind,lookup),gref(descriptions,u),gref(descriptions,k),v) end end end @@ -318,10 +333,10 @@ local supported_gpos = { 'kern' } -function otf.features.register_base_substitution(tag) +function otf.features.registerbasesubstitution(tag) supported_gsub[#supported_gsub+1] = tag end -function otf.features.register_base_kern(tag) +function otf.features.registerbasekern(tag) supported_gsub[#supported_gpos+1] = tag end @@ -345,7 +360,7 @@ function fonts.initializers.base.otf.features(tfmdata,value) for f=1,#supported_gpos do local feature = supported_gpos[f] local value = features[feature] - prepare_base_kerns(tfmdata,feature,features[feature]) + preparebasekerns(tfmdata,feature,features[feature]) if value then h[#h+1] = feature .. "=" .. tostring(value) end @@ -364,10 +379,10 @@ function fonts.initializers.base.otf.features(tfmdata,value) -- verbose name as long as we don't use <()<>[]{}/%> and the length -- is < 128. tfmdata.fullname = tfmdata.fullname .. "-" .. base -- tfmdata.psname is the original - --~ logs.report("otf define","fullname base hash: '%s', featureset '%s'",tfmdata.fullname,hash) + --~ report_prepare("fullname base hash: '%s', featureset '%s'",tfmdata.fullname,hash) end if trace_preparing then - logs.report("otf define","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") + report_prepare("preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") end end end diff --git a/otfl-font-otc.lua b/otfl-font-otc.lua index 35555ed..5eb51f9 100644 --- a/otfl-font-otc.lua +++ b/otfl-font-otc.lua @@ -13,8 +13,10 @@ local type, next = type, next local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) -local otf = fonts.otf -local tfm = fonts.tfm +local fonts = fonts +local otf = fonts.otf + +local report_otf = logs.reporter("fonts","otf loading") -- instead of "script = "DFLT", langs = { 'dflt' }" we now use wildcards (we used to -- have always); some day we can write a "force always when true" trick for other @@ -22,6 +24,12 @@ local tfm = fonts.tfm -- -- we could have a tnum variant as well +-- In the userdata interface we can not longer tweak the loaded font as +-- conveniently as before. For instance, instead of pushing extra data in +-- in the table using the original structure, we now have to operate on +-- the mkiv representation. And as the fontloader interface is modelled +-- after fontforge we cannot change that one too much either. + local extra_lists = { tlig = { { @@ -76,142 +84,157 @@ local extra_lists = { local extra_features = { -- maybe just 1..n so that we prescribe order tlig = { { - features = { { scripts = { { script = "*", langs = { "*" }, } }, tag = "tlig", comment = "added bij mkiv" }, }, + features = { ["*"] = { ["*"] = true } }, name = "ctx_tlig_1", - subtables = { { name = "ctx_tlig_1_s" } }, + subtables = { "ctx_tlig_1_s" }, type = "gsub_ligature", flags = { }, }, }, trep = { { - features = { { scripts = { { script = "*", langs = { "*" }, } }, tag = "trep", comment = "added bij mkiv" }, }, + features = { ["*"] = { ["*"] = true } }, name = "ctx_trep_1", - subtables = { { name = "ctx_trep_1_s" } }, + subtables = { "ctx_trep_1_s" }, type = "gsub_single", flags = { }, }, }, anum = { { - features = { { scripts = { { script = "arab", langs = { "dflt", "ARA" }, } }, tag = "anum", comment = "added bij mkiv" }, }, + features = { arab = { FAR = true, dflt = true } }, name = "ctx_anum_1", - subtables = { { name = "ctx_anum_1_s" } }, + subtables = { "ctx_anum_1_s" }, type = "gsub_single", flags = { }, }, { - features = { { scripts = { { script = "arab", langs = { "FAR" }, } }, tag = "anum", comment = "added bij mkiv" }, }, + features = { arab = { FAR = true } }, name = "ctx_anum_2", - subtables = { { name = "ctx_anum_2_s" } }, + subtables = { "ctx_anum_2_s" }, type = "gsub_single", flags = { }, }, }, } -fonts.otf.enhancers["add some missing characters"] = function(data,filename) - -- todo -end - -fonts.otf.enhancers["enrich with features"] = function(data,filename) - -- could be done elsewhere (true can be #) - local used = { } - for i=1,#otf.glists do - local g = data[otf.glists[i]] - if g then - for i=1,#g do - local f = g[i].features - if f then - for i=1,#f do - local t = f[i].tag - if t then used[t] = true end - end - end - end - end - end - -- +local function enhancedata(data,filename,raw) + local luatex = data.luatex + local lookups = luatex.lookups + local sequences = luatex.sequences local glyphs = data.glyphs - local indices = data.map.map - data.gsub = data.gsub or { } + local indices = luatex.indices + local gsubfeatures = luatex.features.gsub for kind, specifications in next, extra_features do - if not used[kind] then + if gsub and gsub[kind] then + -- already present + else local done = 0 for s=1,#specifications do local added = false local specification = specifications[s] + local features, subtables = specification.features, specification.subtables + local name, type, flags = specification.name, specification.type, specification.flags + local full = subtables[1] local list = extra_lists[kind][s] - local name = specification.name .. "_s" - if specification.type == "gsub_ligature" then + if type == "gsub_ligature" then + -- inefficient loop for unicode, index in next, indices do local glyph = glyphs[index] local ligature = list[glyph.name] if ligature then - local o = glyph.lookups or { } - -- o[name] = { "ligature", ligature, glyph.name } - o[name] = { - { - ["type"] = "ligature", - ["specification"] = { - char = glyph.name, - components = ligature, - } - } - } - glyph.lookups, done, added = o, done+1, true + if glyph.slookups then + glyph.slookups [full] = { "ligature", ligature, glyph.name } + else + glyph.slookups = { [full] = { "ligature", ligature, glyph.name } } + end + done, added = done+1, true end end - elseif specification.type == "gsub_single" then + elseif type == "gsub_single" then + -- inefficient loop for unicode, index in next, indices do local glyph = glyphs[index] local r = list[unicode] if r then local replacement = indices[r] if replacement and glyphs[replacement] then - local o = glyph.lookups or { } - -- o[name] = { { "substitution", glyphs[replacement].name } } - o[name] = { - { - ["type"] = "substitution", - ["specification"] = { - variant = glyphs[replacement].name, - } - } - } - glyph.lookups, done, added = o, done+1, true + if glyph.slookups then + glyph.slookups [full] = { "substitution", glyphs[replacement].name } + else + glyph.slookups = { [full] = { "substitution", glyphs[replacement].name } } + end + done, added = done+1, true end end end end if added then - insert(data.gsub,s,table.fastcopy(specification)) -- right order + sequences[#sequences+1] = { + chain = 0, + features = { [kind] = features }, + flags = flags, + name = name, + subtables = subtables, + type = type, + } + -- register in metadata (merge as there can be a few) + if not gsubfeatures then + gsubfeatures = { } + luatex.features.gsub = gsubfeatures + end + local k = gsubfeatures[kind] + if not k then + k = { } + gsubfeatures[kind] = k + end + for script, languages in next, features 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 if done > 0 then if trace_loading then - logs.report("load otf","enhance: registering %s feature (%s glyphs affected)",kind,done) + report_otf("enhance: registering %s feature (%s glyphs affected)",kind,done) end end end end end -otf.tables.features['tlig'] = 'TeX Ligatures' -otf.tables.features['trep'] = 'TeX Replacements' -otf.tables.features['anum'] = 'Arabic Digits' +otf.enhancers.register("check extra features",enhancedata) -otf.features.register_base_substitution('tlig') -otf.features.register_base_substitution('trep') -otf.features.register_base_substitution('anum') +local features = otf.tables.features + +features['tlig'] = 'TeX Ligatures' +features['trep'] = 'TeX Replacements' +features['anum'] = 'Arabic Digits' + +local registerbasesubstitution = otf.features.registerbasesubstitution + +registerbasesubstitution('tlig') +registerbasesubstitution('trep') +registerbasesubstitution('anum') -- the functionality is defined elsewhere -fonts.initializers.base.otf.equaldigits = fonts.initializers.common.equaldigits -fonts.initializers.node.otf.equaldigits = fonts.initializers.common.equaldigits +local initializers = fonts.initializers +local common_initializers = initializers.common +local base_initializers = initializers.base.otf +local node_initializers = initializers.node.otf + +base_initializers.equaldigits = common_initializers.equaldigits +node_initializers.equaldigits = common_initializers.equaldigits -fonts.initializers.base.otf.lineheight = fonts.initializers.common.lineheight -fonts.initializers.node.otf.lineheight = fonts.initializers.common.lineheight +base_initializers.lineheight = common_initializers.lineheight +node_initializers.lineheight = common_initializers.lineheight -fonts.initializers.base.otf.compose = fonts.initializers.common.compose -fonts.initializers.node.otf.compose = fonts.initializers.common.compose +base_initializers.compose = common_initializers.compose +node_initializers.compose = common_initializers.compose diff --git a/otfl-font-otd.lua b/otfl-font-otd.lua index 46899fd..4754816 100644 --- a/otfl-font-otd.lua +++ b/otfl-font-otd.lua @@ -6,25 +6,28 @@ if not modules then modules = { } end modules ['font-otd'] = { license = "see context related readme files" } -local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) +local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) -fonts = fonts or { } -fonts.otf = fonts.otf or { } +local report_otf = logs.reporter("fonts","otf loading") -local otf = fonts.otf -local fontdata = fonts.ids +local fonts = fonts +local otf = fonts.otf +local fontdata = fonts.identifiers otf.features = otf.features or { } otf.features.default = otf.features.default or { } -local context_setups = fonts.define.specify.context_setups -local context_numbers = fonts.define.specify.context_numbers +local definers = fonts.definers +local contextsetups = definers.specifiers.contextsetups +local contextnumbers = definers.specifiers.contextnumbers -local a_to_script = { } otf.a_to_script = a_to_script -local a_to_language = { } otf.a_to_language = a_to_language +-- todo: dynamics namespace -function otf.set_dynamics(font,dynamics,attribute) - local features = context_setups[context_numbers[attribute]] -- can be moved to caller +local a_to_script = { } +local a_to_language = { } + +function otf.setdynamics(font,dynamics,attribute) + local features = contextsetups[contextnumbers[attribute]] -- can be moved to caller if features then local script = features.script or 'dflt' local language = features.language or 'dflt' @@ -41,7 +44,7 @@ function otf.set_dynamics(font,dynamics,attribute) local dsla = dsl[attribute] if dsla then -- if trace_dynamics then - -- logs.report("otf define","using dynamics %s: attribute %s, script %s, language %s",context_numbers[attribute],attribute,script,language) + -- report_otf("using dynamics %s: attribute %s, script %s, language %s",contextnumbers[attribute],attribute,script,language) -- end return dsla else @@ -56,14 +59,15 @@ function otf.set_dynamics(font,dynamics,attribute) features = tfmdata.shared.features } tfmdata.mode = "node" + tfmdata.dynamics = true -- handy for tracing tfmdata.language = language tfmdata.script = script tfmdata.shared.features = { } -- end of save - local set = fonts.define.check(features,otf.features.default) - dsla = otf.set_features(tfmdata,set) + local set = definers.check(features,otf.features.default) + dsla = otf.setfeatures(tfmdata,set) if trace_dynamics then - logs.report("otf define","setting dynamics %s: attribute %s, script %s, language %s, set: %s",context_numbers[attribute],attribute,script,language,table.sequenced(set)) + report_otf("setting dynamics %s: attribute %s, script %s, language %s, set: %s",contextnumbers[attribute],attribute,script,language,table.sequenced(set)) end -- we need to restore some values tfmdata.script = saved.script @@ -77,3 +81,11 @@ function otf.set_dynamics(font,dynamics,attribute) end return nil -- { } end + +function otf.scriptandlanguage(tfmdata,attr) + if attr and attr > 0 then + return a_to_script[attr] or tfmdata.script, a_to_language[attr] or tfmdata.language + else + return tfmdata.script, tfmdata.language + end +end diff --git a/otfl-font-otf.lua b/otfl-font-otf.lua index fe9cd51..c974d89 100644 --- a/otfl-font-otf.lua +++ b/otfl-font-otf.lua @@ -6,97 +6,162 @@ if not modules then modules = { } end modules ['font-otf'] = { license = "see context related readme files" } +-- langs -> languages enz +-- anchor_classes vs kernclasses +-- modification/creationtime in subfont is runtime dus zinloos +-- to_table -> totable + local utf = unicode.utf8 -local concat, utfbyte = table.concat, utf.byte +local utfbyte = utf.byte local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip local type, next, tonumber, tostring = type, next, tonumber, tostring local abs = math.abs local getn = table.getn local lpegmatch = lpeg.match +local reversed, concat = table.reversed, table.concat +local ioflush = io.flush -local trace_private = false trackers.register("otf.private", function(v) trace_private = v end) -local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) -local trace_features = false trackers.register("otf.features", function(v) trace_features = v end) -local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) -local trace_sequences = false trackers.register("otf.sequences", function(v) trace_sequences = v end) -local trace_math = false trackers.register("otf.math", function(v) trace_math = v end) -local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) +local allocate = utilities.storage.allocate ---~ trackers.enable("otf.loading") +local trace_private = false trackers.register("otf.private", function(v) trace_private = v end) +local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) +local trace_features = false trackers.register("otf.features", function(v) trace_features = v end) +local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) +local trace_sequences = false trackers.register("otf.sequences", function(v) trace_sequences = v end) +local trace_math = false trackers.register("otf.math", function(v) trace_math = v end) +local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) ---[[ldx-- -<p>The fontforge table has organized lookups in a certain way. A first implementation -of this code was organized featurewise: information related to features was -collected and processing boiled down to a run over the features. The current -implementation honors the order in the main feature table. Since we can reorder this -table as we want, we can eventually support several models of processing. We kept -the static as well as dynamic feature processing, because it had proved to be -rather useful. The formerly three loop variants have beem discarded but will -reapear at some time.</p> - -<itemize> -<item>we loop over all lookups</item> -<item>for each lookup we do a run over the list of glyphs</item> -<item>but we only process them for features that are enabled</item> -<item>if we're dealing with a contextual lookup, we loop over all contexts</item> -<item>in that loop we quit at a match and then process the list of sublookups</item> -<item>we always continue after the match</item> -</itemize> - -<p>In <l n='context'/> we do this for each font that is used in a list, so in -practice we have quite some nested loops.</p> - -<p>We process the whole list and then consult the glyph nodes. An alternative approach -is to collect strings of characters using the same font including spaces (because some -lookups involve spaces). However, we then need to reconstruct the list which is no fun. -Also, we need to carry quite some information, like attributes, so eventually we don't -gain much (if we gain something at all).</p> - -<p>Another consideration has been to operate on sublists (subhead, subtail) but again -this would complicate matters as we then neext to keep track of a changing subhead -and subtail. On the other hand, this might save some runtime. The number of changes -involved is not that large. This only makes sense when we have many fonts in a list -and don't change to frequently.</p> ---ldx]]-- +local report_otf = logs.reporter("fonts","otf loading") + +local starttiming, stoptiming, elapsedtime = statistics.starttiming, statistics.stoptiming, statistics.elapsedtime + +local findbinfile = resolvers.findbinfile + +local fonts = fonts + +fonts.otf = fonts.otf or { } +local otf = fonts.otf +local tfm = fonts.tfm + +local fontdata = fonts.identifiers +local chardata = characters and characters.data -- not used + +-- todo: probably first time so local first -fonts = fonts or { } -fonts.otf = fonts.otf or { } -fonts.tfm = fonts.tfm or { } +otf.features = otf.features or { } +local features = otf.features +features.list = features.list or { } +local featurelist = features.list +features.default = features.default or { } +local defaultfeatures = features.default -local otf = fonts.otf -local tfm = fonts.tfm +otf.enhancers = allocate() +local enhancers = otf.enhancers +enhancers.patches = { } +local patches = enhancers.patches -local fontdata = fonts.ids +local definers = fonts.definers +local readers = fonts.tfm.readers -otf.tables = otf.tables or { } -- defined in font-ott.lua -otf.meanings = otf.meanings or { } -- defined in font-ott.lua -otf.tables.features = otf.tables.features or { } -- defined in font-ott.lua -otf.tables.languages = otf.tables.languages or { } -- defined in font-ott.lua -otf.tables.scripts = otf.tables.scripts or { } -- defined in font-ott.lua +otf.glists = { "gsub", "gpos" } -otf.features = otf.features or { } -otf.features.list = otf.features.list or { } -otf.features.default = otf.features.default or { } +otf.version = 2.710 -- beware: also sync font-mis.lua +otf.cache = containers.define("fonts", "otf", otf.version, true) -otf.enhancers = otf.enhancers or { } -otf.glists = { "gsub", "gpos" } +local loadmethod = "table" -- table, mixed, sparse +local forceload = false +local cleanup = 0 +local usemetatables = false -- .4 slower on mk but 30 M less mem so we might change the default -- will be directive +local packdata = true +local syncspace = true +local forcenotdef = false -otf.version = 2.653 -- beware: also sync font-mis.lua -otf.pack = true -- beware: also sync font-mis.lua -otf.syncspace = true -otf.notdef = false -otf.cache = containers.define("fonts", "otf", otf.version, true) -otf.cleanup_aat = false -- only context +local wildcard = "*" +local default = "dflt" -local wildcard = "*" -local default = "dflt" +local fontloaderfields = fontloader.fields +local mainfields = nil +local glyphfields = nil -- not used yet + +directives.register("fonts.otf.loader.method", function(v) + if v == "sparse" and fontloaderfields then + loadmethod = "sparse" + elseif v == "mixed" then + loadmethod = "mixed" + elseif v == "table" then + loadmethod = "table" + else + loadmethod = "table" + report_otf("no loader method '%s', using '%s' instead",v,loadmethod) + end +end) + +directives.register("fonts.otf.loader.cleanup",function(v) + cleanup = tonumber(v) or (v and 1) or 0 +end) + +directives.register("fonts.otf.loader.force", function(v) forceload = v end) +directives.register("fonts.otf.loader.usemetatables", function(v) usemetatables = v end) +directives.register("fonts.otf.loader.pack", function(v) packdata = v end) +directives.register("fonts.otf.loader.syncspace", function(v) syncspace = v end) +directives.register("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("featurefile: %s", featurefile) + end + fontloader.apply_featurefile(raw, featurefile) + end +end + +local function showfeatureorder(otfdata,filename) + local sequences = otfdata.luatex.sequences + if sequences and #sequences > 0 then + if trace_loading then + report_otf("font %s has %s sequences",filename,#sequences) + report_otf(" ") + end + for nos=1,#sequences do + local sequence = sequences[nos] + local typ = sequence.type or "no-type" + local name = sequence.name or "no-name" + local subtables = sequence.subtables or { "no-subtables" } + local features = sequence.features + if trace_loading then + report_otf("%3i %-15s %-20s [%s]",nos,name,typ,concat(subtables,",")) + end + if features then + for feature, scripts in next, features do + local tt = { } + for script, languages in next, scripts do + local ttt = { } + for language, _ in next, languages do + ttt[#ttt+1] = language + end + tt[#tt+1] = format("[%s: %s]",script,concat(ttt," ")) + end + if trace_loading then + report_otf(" %s: %s",feature,concat(tt," ")) + end + end + end + end + if trace_loading then + report_otf("\n") + end + elseif trace_loading then + report_otf("font %s has no sequences",filename) + end +end --[[ldx-- <p>We start with a lot of tables and related functions.</p> --ldx]]-- -otf.tables.global_fields = table.tohash { +local global_fields = table.tohash { + "metadata", "lookups", "glyphs", "subfonts", @@ -107,20 +172,20 @@ otf.tables.global_fields = table.tohash { "names", "unicodes", "names", ---~ "math", + -- "math", "anchor_classes", "kern_classes", "gpos", "gsub" } -otf.tables.valid_fields = { - "anchor_classes", +local valid_fields = table.tohash { + -- "anchor_classes", "ascent", - "cache_version", + -- "cache_version", "cidinfo", "copyright", - "creationtime", + -- "creationtime", "descent", "design_range_bottom", "design_range_top", @@ -132,23 +197,23 @@ otf.tables.valid_fields = { "fontstyle_id", "fontstyle_name", "fullname", - "glyphs", + -- "glyphs", "hasvmetrics", "head_optimized_for_cleartype", "horiz_base", "issans", "isserif", "italicangle", - "kerns", - "lookups", + -- "kerns", + -- "lookups", -- "luatex", "macstyle", - "modificationtime", + -- "modificationtime", "onlybitmaps", "origname", "os2_version", - "pfminfo", - "private", + -- "pfminfo", + -- "private", "serifcheck", "sfd_version", -- "size", @@ -165,69 +230,124 @@ otf.tables.valid_fields = { "upos", "use_typo_metrics", "uwidth", - "validation_state", + -- "validation_state", "verbose", "version", "vert_base", "weight", "weight_width_slope_only", - "xuid", + -- "xuid", +} + +local ordered_enhancers = { + "prepare tables", + "prepare glyphs", + "prepare unicodes", + "prepare lookups", + + "analyze glyphs", + "analyze math", + + "prepare tounicode", -- maybe merge with prepare + + "reorganize lookups", + "reorganize mark classes", + "reorganize anchor classes", + + "reorganize glyph kerns", + "reorganize glyph lookups", + "reorganize glyph anchors", + + "reorganize features", + "reorganize subtables", + + "check glyphs", + "check metadata", + "check math parameters", + "check extra features", -- after metadata } --[[ldx-- <p>Here we go.</p> --ldx]]-- -local function load_featurefile(ff,featurefile) - if featurefile then - featurefile = resolvers.find_file(file.addsuffix(featurefile,'fea'),'fea') - if featurefile and featurefile ~= "" then - if trace_loading then - logs.report("load otf", "featurefile: %s", featurefile) - end - fontloader.apply_featurefile(ff, featurefile) +local actions = { } + +patches.before = allocate() +patches.after = allocate() + +local before = patches.before +local after = patches.after + +local function enhance(name,data,filename,raw,verbose) + local enhancer = actions[name] + if enhancer then + if verbose then + report_otf("enhance: %s (%s)",name,filename) + ioflush() end + enhancer(data,filename,raw) + else + report_otf("enhance: %s is undefined",name) end end -function otf.enhance(name,data,filename,verbose) - local enhancer = otf.enhancers[name] - if enhancer then - if (verbose ~= nil and verbose) or trace_loading then - logs.report("load otf","enhance: %s (%s)",name,filename) +function enhancers.apply(data,filename,raw,verbose) + local basename = file.basename(lower(filename)) + report_otf("start enhancing: %s",filename) + ioflush() -- we want instant messages + for e=1,#ordered_enhancers do + local enhancer = ordered_enhancers[e] + local b = before[enhancer] + if b then + for pattern, action in next, b do + if find(basename,pattern) then + action(data,filename,raw) + end + end + end + enhance(enhancer,data,filename,raw,verbose) + 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 - enhancer(data,filename) + ioflush() -- we want instant messages end + report_otf("stop enhancing") + ioflush() -- we want instant messages end -local enhancers = { - -- pack and unpack are handled separately; they might even be moved - -- away from the enhancers namespace - "patch bugs", - "merge cid fonts", "prepare unicode", "cleanup ttf tables", "compact glyphs", "reverse coverage", - "cleanup aat", "enrich with features", "add some missing characters", - "reorganize mark classes", - "reorganize kerns", -- moved here - "flatten glyph lookups", "flatten anchor tables", "flatten feature tables", - "simplify glyph lookups", -- some saving - "prepare luatex tables", - "analyse features", "rehash features", - "analyse anchors", "analyse marks", "analyse unicodes", "analyse subtables", - "check italic correction","check math", - "share widths", - "strip not needed data", - "migrate metadata", - "check math parameters", -} +-- patches.register("before","migrate metadata","cambria",function() end) + +function patches.register(what,where,pattern,action) + local ww = what[where] + if ww then + ww[pattern] = action + else + ww = { [pattern] = action} + end +end + +function patches.report(fmt,...) + if trace_loading then + report_otf("patching: " ..fmt,...) + end +end + +function enhancers.register(what,action) -- only already registered can be overloaded + actions[what] = action +end function otf.load(filename,format,sub,featurefile) local name = file.basename(file.removesuffix(filename)) local attr = lfs.attributes(filename) - local size, time = attr.size or 0, attr.modification or 0 + local size, time = attr and attr.size or 0, attr and attr.modification or 0 if featurefile then - local fattr = lfs.attributes(featurefile) - local fsize, ftime = fattr and fattr.size or 0, fattr and fattr.modification or 0 - name = name .. "@" .. file.removesuffix(file.basename(featurefile)) .. ftime .. fsize + name = name .. "@" .. file.removesuffix(file.basename(featurefile)) end if sub == "" then sub = false end local hash = name @@ -235,151 +355,468 @@ function otf.load(filename,format,sub,featurefile) 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: no featurefile '%s'",s) + else + local attr = lfs.attributes(name) + featurefiles[#featurefiles+1] = { + name = name, + size = attr.size or 0, + time = attr.modification or 0, + } + end + end + if #featurefiles == 0 then + featurefiles = nil + end + end local data = containers.read(otf.cache,hash) - if not data or data.verbose ~= fonts.verbose or data.size ~= size or data.time ~= time then - logs.report("load otf","loading: %s (hash: %s)",filename,hash) - local ff, messages + local reload = not data or data.verbose ~= fonts.verbose or data.size ~= size or data.time ~= time + if forceload then + report_otf("loading: forced reload due to hard coded flag") + 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: %s",featurefile or "--") + end + end + if reload then + report_otf("loading: %s (hash: %s)",filename,hash) + local fontdata, messages, rawdata if sub then - ff, messages = fontloader.open(filename,sub) + fontdata, messages = fontloader.open(filename,sub) else - ff, messages = fontloader.open(filename) + fontdata, messages = fontloader.open(filename) + end + if fontdata then + mainfields = mainfields or (fontloaderfields and fontloaderfields(fontdata)) end if trace_loading and messages and #messages > 0 then if type(messages) == "string" then - logs.report("load otf","warning: %s",messages) + report_otf("warning: %s",messages) else for m=1,#messages do - logs.report("load otf","warning: %s",tostring(messages[m])) + report_otf("warning: %s",tostring(messages[m])) end end else - logs.report("load otf","font loaded okay") - end - if ff then - load_featurefile(ff,featurefile) - data = fontloader.to_table(ff) - fontloader.close(ff) - if data then - logs.report("load otf","file size: %s", size) - logs.report("load otf","enhancing ...") - for e=1,#enhancers do - otf.enhance(enhancers[e],data,filename) - io.flush() -- we want instant messages + report_otf("font loaded okay") + end + if fontdata then + if featurefiles then + for i=1,#featurefiles do + load_featurefile(fontdata,featurefiles[i].name) end - if otf.pack and not fonts.verbose then - otf.enhance("pack",data,filename) + end + report_otf("loading method: %s",loadmethod) + if loadmethod == "sparse" then + rawdata = fontdata + else + rawdata = fontloader.to_table(fontdata) + fontloader.close(fontdata) + end + if rawdata then + data = { } + starttiming(data) + local verboseindeed = verbose ~= nil and verbose or trace_loading + report_otf("file size: %s", size) + enhancers.apply(data,filename,rawdata,verboseindeed) + if packdata and not fonts.verbose then + enhance("pack",data,filename,nil,verboseindeed) end data.size = size data.time = time + data.format = format + if featurefiles then + data.featuredata = featurefiles + end data.verbose = fonts.verbose - logs.report("load otf","saving in cache: %s",filename) + report_otf("saving in cache: %s",filename) data = containers.write(otf.cache, hash, data) - collectgarbage("collect") + if cleanup > 0 then + collectgarbage("collect") + end + stoptiming(data) + if elapsedtime then -- not in generic + report_otf("preprocessing and caching took %s seconds",elapsedtime(data)) + end data = containers.read(otf.cache, hash) -- this frees the old table and load the sparse one - collectgarbage("collect") + if cleanup > 1 then + collectgarbage("collect") + end else - logs.report("load otf","loading failed (table conversion error)") + data = nil + report_otf("loading failed (table conversion error)") + end + if loadmethod == "sparse" then + fontloader.close(fontdata) + if cleanup > 2 then + -- collectgarbage("collect") + end end else - logs.report("load otf","loading failed (file read error)") + data = nil + report_otf("loading failed (file read error)") end end if data then if trace_defining then - logs.report("define font","loading from cache: %s",hash) + report_otf("loading from cache: %s",hash) end - otf.enhance("unpack",data,filename,false) -- no message here - otf.add_dimensions(data) + enhance("unpack",data,filename,nil,false) + enhance("add dimensions",data,filename,nil,false) if trace_sequences then - otf.show_feature_order(data,filename) + showfeatureorder(data,filename) end end return data end -function otf.add_dimensions(data) +local mt = { + __index = function(t,k) -- maybe set it + 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 -- or maybe uni* + return forcenotdef and ".notdef" + end + end +} + +actions["add dimensions"] = function(data,filename) -- todo: forget about the width if it's the defaultwidth (saves mem) -- we could also build the marks hash here (instead of storing it) if data then - local force = otf.notdef local luatex = data.luatex local defaultwidth = luatex.defaultwidth or 0 local defaultheight = luatex.defaultheight or 0 local defaultdepth = luatex.defaultdepth or 0 - for _, d in next, data.glyphs do - local bb, wd = d.boundingbox, d.width - if not wd then - d.width = defaultwidth - elseif wd ~= 0 and d.class == "mark" then - d.width = -wd + if usemetatables then + for _, d in next, data.glyphs do + local wd = d.width + if not wd then + d.width = defaultwidth + elseif wd ~= 0 and d.class == "mark" then + d.width = -wd + end + setmetatable(d,mt) end - if force and not d.name then - d.name = ".notdef" + else + for _, d in next, data.glyphs do + local bb, wd = d.boundingbox, d.width + if not wd then + d.width = defaultwidth + elseif wd ~= 0 and d.class == "mark" then + d.width = -wd + end + if forcenotdef and not d.name then + d.name = ".notdef" + end + if bb then + local ht, dp = bb[4], -bb[2] + if ht == 0 or ht < 0 then + -- not set + else + d.height = ht + end + if dp == 0 or dp < 0 then + -- not set + else + d.depth = dp + end + end end - if bb then - local ht, dp = bb[4], -bb[2] - if ht == 0 or ht < 0 then - -- no need to set it and no negative heights, nil == 0 + end + end +end + +actions["prepare tables"] = function(data,filename,raw) + local luatex = { + filename = resolvers.unresolve(filename), -- no shortcut + version = otf.version, + creator = "context mkiv", + } + data.luatex = luatex + data.metadata = { } +end + +local function somecopy(old) -- fast one + if old then + local new = { } + if type(old) == "table" then + for k, v in next, old do + if k == "glyphs" then + -- skip + elseif type(v) == "table" then + new[k] = somecopy(v) else - d.height = ht + new[k] = v end - if dp == 0 or dp < 0 then - -- no negative depths and no negative depths, nil == 0 + end + else + for i=1,#mainfields do + local k = mainfields[i] + local v = old[k] + if k == "glyphs" then + -- skip + elseif type(v) == "table" then + new[k] = somecopy(v) else - d.depth = dp + new[k] = v end end end + return new + else + return { } end end -function otf.show_feature_order(otfdata,filename) - local sequences = otfdata.luatex.sequences - if sequences and #sequences > 0 then - if trace_loading then - logs.report("otf check","font %s has %s sequences",filename,#sequences) - logs.report("otf check"," ") +-- not setting italic_correction and class (when nil) during +-- table cronstruction can save some mem + +actions["prepare glyphs"] = function(data,filename,raw) + -- we can also move the names to data.luatex.names which might + -- save us some more memory (at the cost of harder tracing) + local rawglyphs = raw.glyphs + local glyphs, udglyphs + if loadmethod == "sparse" then + glyphs, udglyphs = { }, { } + elseif loadmethod == "mixed" then + glyphs, udglyphs = { }, rawglyphs + else + glyphs, udglyphs = rawglyphs, rawglyphs + end + data.glyphs, data.udglyphs = glyphs, udglyphs + local subfonts = raw.subfonts + if subfonts then + if data.glyphs and next(data.glyphs) then + report_otf("replacing existing glyph table due to subfonts") end - for nos=1,#sequences do - local sequence = sequences[nos] - local typ = sequence.type or "no-type" - local name = sequence.name or "no-name" - local subtables = sequence.subtables or { "no-subtables" } - local features = sequence.features - if trace_loading then - logs.report("otf check","%3i %-15s %-20s [%s]",nos,name,typ,concat(subtables,",")) - end - if features then - for feature, scripts in next, features do - local tt = { } - for script, languages in next, scripts do - local ttt = { } - for language, _ in next, languages do - ttt[#ttt+1] = language + local cidinfo = raw.cidinfo + if cidinfo.registry then + local cidmap, cidname = fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) + if cidmap then + cidinfo.usedname = cidmap.usedname + local uni_to_int, int_to_uni, nofnames, nofunicodes = { }, { }, 0, 0 + local unicodes, names = cidmap.unicodes, cidmap.names + for cidindex=1,#subfonts do + local subfont = subfonts[cidindex] + if loadmethod == "sparse" then + local rawglyphs = subfont.glyphs + for index=0,subfont.glyphmax - 1 do + local g = rawglyphs[index] + if g then + local unicode, name = unicodes[index], names[index] + if unicode then + uni_to_int[unicode] = index + int_to_uni[index] = unicode + nofunicodes = nofunicodes + 1 + elseif name then + nofnames = nofnames + 1 + end + udglyphs[index] = g + glyphs[index] = { + width = g.width, + italic = g.italic_correction, + boundingbox = g.boundingbox, + class = g.class, + name = g.name or name or "unknown", -- uniXXXX + cidindex = cidindex, + unicode = unicode, + } + end end - tt[#tt+1] = format("[%s: %s]",script,concat(ttt," ")) - end - if trace_loading then - logs.report("otf check"," %s: %s",feature,concat(tt," ")) + -- If we had more userdata, we would need more of this + -- and it would start working against us in terms of + -- convenience and speed. + subfont = somecopy(subfont) + subfont.glyphs = nil + subfont[cidindex] = subfont + elseif loadmethod == "mixed" then + for index, g in next, subfont.glyphs do + local unicode, name = unicodes[index], names[index] + if unicode then + uni_to_int[unicode] = index + int_to_uni[index] = unicode + nofunicodes = nofunicodes + 1 + elseif name then + nofnames = nofnames + 1 + end + udglyphs[index] = g + glyphs[index] = { + width = g.width, + italic = g.italic_correction, + boundingbox = g.boundingbox, + class = g.class, + name = g.name or name or "unknown", -- uniXXXX + cidindex = cidindex, + unicode = unicode, + } + end + subfont.glyphs = nil + else + for index, g in next, subfont.glyphs do + local unicode, name = unicodes[index], names[index] + if unicode then + uni_to_int[unicode] = index + int_to_uni[index] = unicode + nofunicodes = nofunicodes + 1 + g.unicode = unicode + elseif name then + nofnames = nofnames + 1 + end + g.cidindex = cidindex + glyphs[index] = g + end + subfont.glyphs = nil end end + if trace_loading then + report_otf("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames) + end + data.map = data.map or { } + data.map.map = uni_to_int + data.map.backmap = int_to_uni + elseif trace_loading then + report_otf("unable to remap cid font, missing cid file for %s",filename) end + data.subfonts = subfonts + elseif trace_loading then + report_otf("font %s has no glyphs",filename) end - if trace_loading then - logs.report("otf check","\n") + else + if loadmethod == "sparse" then + -- we get fields from the userdata glyph table and create + -- a minimal entry first + for index=0,raw.glyphmax - 1 do + local g = rawglyphs[index] + if g then + udglyphs[index] = g + glyphs[index] = { + width = g.width, + italic = g.italic_correction, + boundingbox = g.boundingbox, + class = g.class, + name = g.name, + unicode = g.unicode, + } + end + end + elseif loadmethod == "mixed" then + -- we get fields from the totable glyph table and copy to the + -- final glyph table so first we create a minimal entry + for index, g in next, rawglyphs do + udglyphs[index] = g + glyphs[index] = { + width = g.width, + italic = g.italic_correction, + boundingbox = g.boundingbox, + class = g.class, + name = g.name, + unicode = g.unicode, + } + end + else + -- we use the totable glyph table directly and manipulate the + -- entries in this (also final) table end - elseif trace_loading then - logs.report("otf check","font %s has no sequences",filename) + data.map = raw.map end + data.cidinfo = raw.cidinfo -- hack end --- todo: normalize, design_size => designsize +-- watch copy of cidinfo: we can best make some more copies to data -otf.enhancers["reorganize mark classes"] = function(data,filename) - if data.mark_classes then - local unicodes = data.luatex.unicodes +actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this in the previous + local glyphs = data.glyphs + -- collect info + local has_italic, widths, marks = false, { }, { } + for index, glyph in next, glyphs do + local italic = glyph.italic_correction + if not italic then + -- skip + elseif italic == 0 then + glyph.italic_correction = nil + glyph.italic = nil + else + glyph.italic_correction = nil + glyph.italic = italic + has_italic = true + end + local width = glyph.width + widths[width] = (widths[width] or 0) + 1 + local class = glyph.class + local unicode = glyph.unicode + if class == "mark" then + marks[unicode] = true + -- elseif chardata[unicode].category == "mn" then + -- marks[unicode] = true + -- glyph.class = "mark" + end + local a = glyph.altuni if a then glyph.altuni = nil end + local d = glyph.dependents if d then glyph.dependents = nil end + local v = glyph.vwidth if v then glyph.vwidth = nil end + end + -- flag italic + data.metadata.has_italic = has_italic + -- flag marks + data.luatex.marks = marks + -- share most common width for cjk fonts + local wd, most = 0, 1 + for k,v in next, widths do + if v > most then + wd, most = k, v + end + end + if most > 1000 then -- maybe 500 + if trace_loading then + report_otf("most common width: %s (%s times), sharing (cjk font)",wd,most) + end + for index, glyph in next, glyphs do + if glyph.width == wd then + glyph.width = nil + end + end + data.luatex.defaultwidth = wd + end +end + +actions["reorganize mark classes"] = function(data,filename,raw) + local mark_classes = raw.mark_classes + if mark_classes then + local luatex = data.luatex + local unicodes = luatex.unicodes local reverse = { } - for name, class in next, data.mark_classes do + luatex.markclasses = reverse + for name, class in next, mark_classes do local t = { } for s in gmatch(class,"[^ ]+") do local us = unicodes[s] @@ -393,58 +830,15 @@ otf.enhancers["reorganize mark classes"] = function(data,filename) end reverse[name] = t end - data.luatex.markclasses = reverse - data.mark_classes = nil - end -end - -otf.enhancers["prepare luatex tables"] = function(data,filename) - data.luatex = data.luatex or { } - local luatex = data.luatex - luatex.filename = filename - luatex.version = otf.version - luatex.creator = "context mkiv" -end - -otf.enhancers["cleanup aat"] = function(data,filename) - if otf.cleanup_aat then - end -end - -local function analyze_features(g, features) - if g then - local t, done = { }, { } - for k=1,#g do - local f = features or g[k].features - if f then - for k=1,#f do - -- scripts and tag - local tag = f[k].tag - if not done[tag] then - t[#t+1] = tag - done[tag] = true - end - end - end - end - if #t > 0 then - return t - end + data.mark_classes = nil -- when using table end - return nil end -otf.enhancers["analyse features"] = function(data,filename) - -- local luatex = data.luatex - -- luatex.gposfeatures = analyze_features(data.gpos) - -- luatex.gsubfeatures = analyze_features(data.gsub) -end - -otf.enhancers["rehash features"] = function(data,filename) +actions["reorganize features"] = function(data,filename,raw) -- combine with other local features = { } data.luatex.features = features for k, what in next, otf.glists do - local dw = data[what] + local dw = raw[what] if dw then local f = { } features[what] = f @@ -457,8 +851,10 @@ otf.enhancers["rehash features"] = function(data,filename) local tag = strip(lower(df.tag)) local ft = f[tag] if not ft then ft = {} f[tag] = ft end local dscripts = df.scripts - for script, languages in next, dscripts do - script = strip(lower(script)) + for i=1,#dscripts do + local d = dscripts[i] + local languages = d.langs + local script = strip(lower(d.script)) local fts = ft[script] if not fts then fts = {} ft[script] = fts end for i=1,#languages do fts[strip(lower(languages[i]))] = true @@ -471,8 +867,8 @@ otf.enhancers["rehash features"] = function(data,filename) end end -otf.enhancers["analyse anchors"] = function(data,filename) - local classes = data.anchor_classes +actions["reorganize anchor classes"] = function(data,filename,raw) + local classes = raw.anchor_classes -- anchor classes not in final table local luatex = data.luatex local anchor_to_lookup, lookup_to_anchor = { }, { } luatex.anchor_to_lookup, luatex.lookup_to_anchor = anchor_to_lookup, lookup_to_anchor @@ -497,218 +893,202 @@ otf.enhancers["analyse anchors"] = function(data,filename) end end -otf.enhancers["analyse marks"] = function(data,filename) - local glyphs = data.glyphs - local marks = { } - data.luatex.marks = marks - for unicode, index in next, data.luatex.indices do - local glyph = glyphs[index] - if glyph.class == "mark" then - marks[unicode] = true - end - end +actions["prepare tounicode"] = function(data,filename,raw) + fonts.map.addtounicode(data,filename) end -otf.enhancers["analyse unicodes"] = fonts.map.add_to_unicode - -otf.enhancers["analyse subtables"] = function(data,filename) - data.luatex = data.luatex or { } +actions["reorganize subtables"] = function(data,filename,raw) local luatex = data.luatex - local sequences = { } - local lookups = { } - luatex.sequences = sequences - luatex.lookups = lookups - for _, g in next, { data.gsub, data.gpos } do - for k=1,#g do - local gk = g[k] - local typ = gk.type - if typ == "gsub_contextchain" or typ == "gpos_contextchain" then - gk.chain = 1 - elseif typ == "gsub_reversecontextchain" or typ == "gpos_reversecontextchain" then - gk.chain = -1 - else - gk.chain = 0 - end - local features = gk.features - if features then - sequences[#sequences+1] = gk - -- scripts, tag, ismac - local t = { } - for f=1,#features do - local feature = features[f] - local hash = { } - -- only script and langs matter - for s, languages in next, feature.scripts do - s = lower(s) - local h = hash[s] - if not h then h = { } hash[s] = h end - for l=1,#languages do - h[strip(lower(languages[l]))] = true - end + local sequences, lookups = { }, { } + luatex.sequences, luatex.lookups = sequences, lookups + for _, what in next, otf.glists do + local dw = raw[what] + if dw then + for k=1,#dw do + local gk = dw[k] + local typ = gk.type + local chain = + (typ == "gsub_contextchain" or typ == "gpos_contextchain") and 1 or + (typ == "gsub_reversecontextchain" or typ == "gpos_reversecontextchain") and -1 or 0 + -- + local subtables = gk.subtables + if subtables then + local t = { } + for s=1,#subtables do + local subtable = subtables[s] + local name = subtable.name + t[#t+1] = name end - t[feature.tag] = hash + subtables = t end - gk.features = t - else - lookups[gk.name] = gk - gk.name = nil - end - local subtables = gk.subtables - if subtables then - local t = { } - for s=1,#subtables do - local subtable = subtables[s] - local name = subtable.name - t[#t+1] = name - end - gk.subtables = t - end - local flags = gk.flags - if flags then - gk.flags = { -- forcing false packs nicer - (flags.ignorecombiningmarks and "mark") or false, - (flags.ignoreligatures and "ligature") or false, - (flags.ignorebaseglyphs and "base") or false, - flags.r2l or false, - } - if flags.mark_class then - gk.markclass = luatex.markclasses[flags.mark_class] + local flags, markclass = gk.flags, nil + if flags then + local t = { -- forcing false packs nicer + (flags.ignorecombiningmarks and "mark") or false, + (flags.ignoreligatures and "ligature") or false, + (flags.ignorebaseglyphs and "base") or false, + flags.r2l or false, + } + markclass = flags.mark_class + if markclass then + markclass = luatex.markclasses[markclass] + end + flags = t end - end - end - end -end - -otf.enhancers["merge cid fonts"] = function(data,filename) - -- we can also move the names to data.luatex.names which might - -- save us some more memory (at the cost of harder tracing) - if data.subfonts then - if data.glyphs and next(data.glyphs) then - logs.report("load otf","replacing existing glyph table due to subfonts") - end - local cidinfo = data.cidinfo - local verbose = fonts.verbose - if cidinfo.registry then - local cidmap, cidname = fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) - if cidmap then - cidinfo.usedname = cidmap.usedname - local glyphs, uni_to_int, int_to_uni, nofnames, nofunicodes = { }, { }, { }, 0, 0 - local unicodes, names = cidmap.unicodes, cidmap.names - for n, subfont in next, data.subfonts do - for index, g in next, subfont.glyphs do - if not next(g) then - -- dummy entry - else - local unicode, name = unicodes[index], names[index] - g.cidindex = n - g.boundingbox = g.boundingbox -- or zerobox - g.name = g.name or name or "unknown" - if unicode then - uni_to_int[unicode] = index - int_to_uni[index] = unicode - nofunicodes = nofunicodes + 1 - g.unicode = unicode - elseif name then - nofnames = nofnames + 1 - g.unicode = -1 + -- + local name = gk.name + -- + local features = gk.features + if features then + -- scripts, tag, ismac + local f = { } + for i=1,#features do + local df = features[i] + local tag = strip(lower(df.tag)) + local ft = f[tag] if not ft then ft = {} f[tag] = ft end + local dscripts = df.scripts + for i=1,#dscripts do + local d = dscripts[i] + local languages = d.langs + local script = strip(lower(d.script)) + local fts = ft[script] if not fts then fts = {} ft[script] = fts end + for i=1,#languages do + fts[strip(lower(languages[i]))] = true end - glyphs[index] = g end end - subfont.glyphs = nil - end - if trace_loading then - logs.report("load otf","cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames) + sequences[#sequences+1] = { + type = typ, + chain = chain, + flags = flags, + name = name, + subtables = subtables, + markclass = markclass, + features = f, + } + else + lookups[name] = { + type = typ, + chain = chain, + flags = flags, + subtables = subtables, + markclass = markclass, + } end - data.glyphs = glyphs - data.map = data.map or { } - data.map.map = uni_to_int - data.map.backmap = int_to_uni - elseif trace_loading then - logs.report("load otf","unable to remap cid font, missing cid file for %s",filename) end - elseif trace_loading then - logs.report("load otf","font %s has no glyphs",filename) end end end -otf.enhancers["prepare unicode"] = function(data,filename) +-- the next one is still messy but will get better when we have +-- flattened map/enc tables in the font loader + +actions["prepare unicodes"] = function(data,filename,raw) local luatex = data.luatex - if not luatex then luatex = { } data.luatex = luatex end - local indices, unicodes, multiples, internals = { }, { }, { }, { } - local glyphs = data.glyphs - local mapmap = data.map - if not mapmap then - logs.report("load otf","no map in %s",filename) + local indices, unicodes, multiples, internals= { }, { }, { }, { } + local mapdata = data.map or raw.map -- map already moved + local mapmap + if not mapdata then + report_otf("no mapdata in '%s'",filename) mapmap = { } - data.map = { map = mapmap } - elseif not mapmap.map then - logs.report("load otf","no unicode map in %s",filename) + mapdata = { map = mapmap } + data.map = mapdata + elseif not mapdata.map then + report_otf("no map in mapdata of '%s'",filename) mapmap = { } - data.map.map = mapmap + mapdata.map = mapmap else - mapmap = mapmap.map + mapmap = mapdata.map end - local criterium = fonts.private - local private = fonts.private + local encname = lower(data.enc_name or raw.enc_name or mapdata.enc_name or "") + local criterium = fonts.privateoffset + local private = criterium + local glyphs = data.glyphs + -- todo: nofmultiples for index, glyph in next, glyphs do if index > 0 then - local name = glyph.name + local name = glyph.name -- really needed ? if name then local unicode = glyph.unicode - if unicode == -1 or unicode >= criterium then + if not unicode or unicode == -1 or unicode >= criterium then glyph.unicode = private indices[private] = index unicodes[name] = private internals[index] = true if trace_private then - logs.report("load otf","enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private) + report_otf("enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private) end private = private + 1 else indices[unicode] = index unicodes[name] = unicode end + -- maybe deal with altuni here in the future but first we need + -- to encounter a proper font that sets them; we have to wait till + -- a next luatex binary as currently the unicode numbers can be out + -- of bounds + if false then + local altuni = glyph.altuni + if altuni then + local un = { unicodes[name] } + for i=1,#altuni do + local unicode = altuni[i].unicode + multiples[#multiples+1] = name + un[i+1] = unicode + indices[unicode] = index -- maybe check for duplicates + end + unicodes[name] = un + end + end + else + -- message that something is wrong end end end -- beware: the indices table is used to initialize the tfm table - for unicode, index in next, mapmap do - if not internals[index] then - local name = glyphs[index].name - if name then - local un = unicodes[name] - if not un then - unicodes[name] = unicode -- or 0 - elseif type(un) == "number" then - if un ~= unicode then - multiples[#multiples+1] = name - unicodes[name] = { un, unicode } - indices[unicode] = index - end - else - local ok = false - for u=1,#un do - if un[u] == unicode then - ok = true - break + if find(encname,"unicode") then -- unicodebmp, unicodefull, ... + if trace_loading then + report_otf("using embedded unicode map '%s'",encname) + end + -- ok -- we can also consider using the altuni + for unicode, index in next, mapmap do + if not internals[index] then + local name = glyphs[index].name + if name then + local un = unicodes[name] + if not un then + unicodes[name] = unicode -- or 0 + elseif type(un) == "number" then -- tonumber(un) + if un ~= unicode then + multiples[#multiples+1] = name + unicodes[name] = { un, unicode } + indices[unicode] = index + end + else + local ok = false + for u=1,#un do + if un[u] == unicode then + ok = true + break + end + end + if not ok then + multiples[#multiples+1] = name + un[#un+1] = unicode + indices[unicode] = index end - end - if not ok then - multiples[#multiples+1] = name - un[#un+1] = unicode - indices[unicode] = index end end end end + else + report_otf("warning: non unicode map '%s', only using glyph unicode data",encname or "whatever") end if trace_loading then if #multiples > 0 then - logs.report("load otf","%s glyph are reused: %s",#multiples, concat(multiples," ")) + report_otf("%s glyphs are reused: %s",#multiples, concat(multiples," ")) else - logs.report("load otf","no glyph are reused") + report_otf("no glyphs are reused") end end luatex.indices = indices @@ -716,26 +1096,14 @@ otf.enhancers["prepare unicode"] = function(data,filename) luatex.private = private end -otf.enhancers["cleanup ttf tables"] = function(data,filename) - local ttf_tables = data.ttf_tables - if ttf_tables then - for k=1,#ttf_tables do - if ttf_tables[k].data then ttf_tables[k].data = "deleted" end - end +actions["prepare lookups"] = function(data,filename,raw) + local lookups = raw.lookups + if lookups then + data.lookups = lookups end - data.ttf_tab_saved = nil end -otf.enhancers["compact glyphs"] = function(data,filename) - table.compact(data.glyphs) -- needed? - if data.subfonts then - for _, subfont in next, data.subfonts do - table.compact(subfont.glyphs) -- needed? - end - end -end - -otf.enhancers["reverse coverage"] = function(data,filename) +actions["reorganize lookups"] = function(data,filename,raw) -- we prefer the before lookups in a normal order if data.lookups then for _, v in next, data.lookups do @@ -743,7 +1111,7 @@ otf.enhancers["reverse coverage"] = function(data,filename) for _, vv in next, v.rules do local c = vv.coverage if c and c.before then - c.before = table.reverse(c.before) + c.before = reversed(c.before) end end end @@ -751,35 +1119,19 @@ otf.enhancers["reverse coverage"] = function(data,filename) end end -otf.enhancers["check italic correction"] = function(data,filename) - local glyphs = data.glyphs - local ok = false - for index, glyph in next, glyphs do - local ic = glyph.italic_correction - if ic then - if ic ~= 0 then - glyph.italic = ic - end - glyph.italic_correction = nil - ok = true - end - end - -- we can use this to avoid calculations - otf.tables.valid_fields[#otf.tables.valid_fields+1] = "has_italic" - data.has_italic = true -end - -otf.enhancers["check math"] = function(data,filename) - if data.math then +actions["analyze math"] = function(data,filename,raw) + if raw.math then +data.metadata.math = raw.math -- we move the math stuff into a math subtable because we then can -- test faster in the tfm copy - local glyphs = data.glyphs + local glyphs, udglyphs = data.glyphs, data.udglyphs local unicodes = data.luatex.unicodes - for index, glyph in next, glyphs do - local mk = glyph.mathkern - local hv = glyph.horiz_variants - local vv = glyph.vert_variants + for index, udglyph in next, udglyphs do + local mk = udglyph.mathkern + local hv = udglyph.horiz_variants + local vv = udglyph.vert_variants if mk or hv or vv then + local glyph = glyphs[index] local math = { } glyph.math = math if mk then @@ -789,7 +1141,6 @@ otf.enhancers["check math"] = function(data,filename) end end math.kerns = mk - glyph.mathkern = nil end if hv then math.horiz_variants = hv.variants @@ -805,7 +1156,6 @@ otf.enhancers["check math"] = function(data,filename) if ic and ic ~= 0 then math.horiz_italic_correction = ic end - glyph.horiz_variants = nil end if vv then local uc = unicodes[index] @@ -822,227 +1172,54 @@ otf.enhancers["check math"] = function(data,filename) if ic and ic ~= 0 then math.vert_italic_correction = ic end - glyph.vert_variants = nil end local ic = glyph.italic_correction if ic then if ic ~= 0 then math.italic_correction = ic end - glyph.italic_correction = nil end end end end end -otf.enhancers["share widths"] = function(data,filename) - local glyphs = data.glyphs - local widths = { } - for index, glyph in next, glyphs do - local width = glyph.width - widths[width] = (widths[width] or 0) + 1 - end - -- share width for cjk fonts - local wd, most = 0, 1 - for k,v in next, widths do - if v > most then - wd, most = k, v - end - end - if most > 1000 then - if trace_loading then - logs.report("load otf", "most common width: %s (%s times), sharing (cjk font)",wd,most) - end - for k, v in next, glyphs do - if v.width == wd then - v.width = nil - end - end - data.luatex.defaultwidth = wd - end -end - --- kern: ttf has a table with kerns - --- Weird, as maxfirst and maxseconds can have holes, first seems to be indexed, but --- seconds can start at 2 .. this need to be fixed as getn as well as # are sort of --- unpredictable alternatively we could force an [1] if not set (maybe I will do that --- anyway). - ---~ otf.enhancers["reorganize kerns"] = function(data,filename) ---~ local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes ---~ local mkdone = false ---~ for index, glyph in next, glyphs do ---~ if glyph.kerns then ---~ local mykerns = { } ---~ for k,v in next, glyph.kerns do ---~ local vc, vo, vl = v.char, v.off, v.lookup ---~ if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones ---~ local uvc = unicodes[vc] ---~ if not uvc then ---~ if trace_loading then ---~ logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index) ---~ end ---~ else ---~ if type(vl) ~= "table" then ---~ vl = { vl } ---~ end ---~ for l=1,#vl do ---~ local vll = vl[l] ---~ local mkl = mykerns[vll] ---~ if not mkl then ---~ mkl = { } ---~ mykerns[vll] = mkl ---~ end ---~ if type(uvc) == "table" then ---~ for u=1,#uvc do ---~ mkl[uvc[u]] = vo ---~ end ---~ else ---~ mkl[uvc] = vo ---~ end ---~ end ---~ end ---~ end ---~ end ---~ glyph.mykerns = mykerns ---~ glyph.kerns = nil -- saves space and time ---~ mkdone = true ---~ end ---~ end ---~ if trace_loading and mkdone then ---~ logs.report("load otf", "replacing 'kerns' tables by 'mykerns' tables") ---~ end ---~ if data.kerns then ---~ if trace_loading then ---~ logs.report("load otf", "removing global 'kern' table") ---~ end ---~ data.kerns = nil ---~ end ---~ local dgpos = data.gpos ---~ if dgpos then ---~ local separator = lpeg.P(" ") ---~ local other = ((1 - separator)^0) / unicodes ---~ local splitter = lpeg.Ct(other * (separator * other)^0) ---~ for gp=1,#dgpos do ---~ local gpos = dgpos[gp] ---~ local subtables = gpos.subtables ---~ if subtables then ---~ for s=1,#subtables do ---~ local subtable = subtables[s] ---~ local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes ---~ if kernclass then -- the next one is quite slow ---~ local split = { } -- saves time ---~ for k=1,#kernclass do ---~ local kcl = kernclass[k] ---~ local firsts, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular ---~ if type(lookups) ~= "table" then ---~ lookups = { lookups } ---~ end ---~ local maxfirsts, maxseconds = getn(firsts), getn(seconds) ---~ for _, s in next, firsts do ---~ split[s] = split[s] or lpegmatch(splitter,s) ---~ end ---~ for _, s in next, seconds do ---~ split[s] = split[s] or lpegmatch(splitter,s) ---~ end ---~ for l=1,#lookups do ---~ local lookup = lookups[l] ---~ local function do_it(fk,first_unicode) ---~ local glyph = glyphs[mapmap[first_unicode]] ---~ if glyph then ---~ local mykerns = glyph.mykerns ---~ if not mykerns then ---~ mykerns = { } -- unicode indexed ! ---~ glyph.mykerns = mykerns ---~ end ---~ local lookupkerns = mykerns[lookup] ---~ if not lookupkerns then ---~ lookupkerns = { } ---~ mykerns[lookup] = lookupkerns ---~ end ---~ local baseoffset = (fk-1) * maxseconds ---~ for sk=2,maxseconds do -- we can avoid this loop with a table ---~ local sv = seconds[sk] ---~ local splt = split[sv] ---~ if splt then ---~ local offset = offsets[baseoffset + sk] ---~ --~ local offset = offsets[sk] -- (fk-1) * maxseconds + sk] ---~ if offset then ---~ for i=1,#splt do ---~ local second_unicode = splt[i] ---~ if tonumber(second_unicode) then ---~ lookupkerns[second_unicode] = offset ---~ else for s=1,#second_unicode do ---~ lookupkerns[second_unicode[s]] = offset ---~ end end ---~ end ---~ end ---~ end ---~ end ---~ elseif trace_loading then ---~ logs.report("load otf", "no glyph data for U+%04X", first_unicode) ---~ end ---~ end ---~ for fk=1,#firsts do ---~ local fv = firsts[fk] ---~ local splt = split[fv] ---~ if splt then ---~ for i=1,#splt do ---~ local first_unicode = splt[i] ---~ if tonumber(first_unicode) then ---~ do_it(fk,first_unicode) ---~ else for f=1,#first_unicode do ---~ do_it(fk,first_unicode[f]) ---~ end end ---~ end ---~ end ---~ end ---~ end ---~ end ---~ subtable.comment = "The kernclass table is merged into mykerns in the indexed glyph tables." ---~ subtable.kernclass = { } ---~ end ---~ end ---~ end ---~ end ---~ end ---~ end - -otf.enhancers["reorganize kerns"] = function(data,filename) - local glyphs, mapmap, unicodes = data.glyphs, data.luatex.indices, data.luatex.unicodes +actions["reorganize glyph kerns"] = function(data,filename,raw) + local luatex = data.luatex + local udglyphs, glyphs, mapmap, unicodes = data.udglyphs, data.glyphs, luatex.indices, luatex.unicodes local mkdone = false - local function do_it(lookup,first_unicode,kerns) + local function do_it(lookup,first_unicode,extrakerns) -- can be moved inline but seldom used local glyph = glyphs[mapmap[first_unicode]] if glyph then - local mykerns = glyph.mykerns - if not mykerns then - mykerns = { } -- unicode indexed ! - glyph.mykerns = mykerns + local kerns = glyph.kerns + if not kerns then + kerns = { } -- unicode indexed ! + glyph.kerns = kerns end - local lookupkerns = mykerns[lookup] + local lookupkerns = kerns[lookup] if not lookupkerns then lookupkerns = { } - mykerns[lookup] = lookupkerns + kerns[lookup] = lookupkerns end - for second_unicode, kern in next, kerns do + for second_unicode, kern in next, extrakerns do lookupkerns[second_unicode] = kern end elseif trace_loading then - logs.report("load otf", "no glyph data for U+%04X", first_unicode) + report_otf("no glyph data for U+%04X", first_unicode) end end - for index, glyph in next, glyphs do - if glyph.kerns then - local mykerns = { } - for k,v in next, glyph.kerns do + for index, udglyph in next, data.udglyphs do + local kerns = udglyph.kerns + if kerns then + local glyph = glyphs[index] + local newkerns = { } + for k,v in next, kerns do local vc, vo, vl = v.char, v.off, v.lookup if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones local uvc = unicodes[vc] if not uvc then if trace_loading then - logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index) + report_otf("problems with unicode %s of kern %s at glyph %s",vc,k,index) end else if type(vl) ~= "table" then @@ -1050,10 +1227,10 @@ otf.enhancers["reorganize kerns"] = function(data,filename) end for l=1,#vl do local vll = vl[l] - local mkl = mykerns[vll] + local mkl = newkerns[vll] if not mkl then mkl = { } - mykerns[vll] = mkl + newkerns[vll] = mkl end if type(uvc) == "table" then for u=1,#uvc do @@ -1066,21 +1243,14 @@ otf.enhancers["reorganize kerns"] = function(data,filename) end end end - glyph.mykerns = mykerns - glyph.kerns = nil -- saves space and time + glyph.kerns = newkerns -- udglyph.kerns = nil when in mixed mode mkdone = true end end if trace_loading and mkdone then - logs.report("load otf", "replacing 'kerns' tables by 'mykerns' tables") - end - if data.kerns then - if trace_loading then - logs.report("load otf", "removing global 'kern' table") - end - data.kerns = nil + report_otf("replacing 'kerns' tables by a new 'kerns' tables") end - local dgpos = data.gpos + local dgpos = raw.gpos if dgpos then local separator = lpeg.P(" ") local other = ((1 - separator)^0) / unicodes @@ -1145,7 +1315,7 @@ otf.enhancers["reorganize kerns"] = function(data,filename) end end end - subtable.comment = "The kernclass table is merged into mykerns in the indexed glyph tables." + subtable.comment = "The kernclass table is merged into kerns in the indexed glyph tables." subtable.kernclass = { } end end @@ -1154,22 +1324,10 @@ otf.enhancers["reorganize kerns"] = function(data,filename) end end - - - - - - - - -otf.enhancers["strip not needed data"] = function(data,filename) +actions["check glyphs"] = function(data,filename,raw) local verbose = fonts.verbose local int_to_uni = data.luatex.unicodes for k, v in next, data.glyphs do - local d = v.dependents - if d then v.dependents = nil end - local a = v.altuni - if a then v.altuni = nil end if verbose then local code = int_to_uni[k] -- looks like this is done twice ... bug? @@ -1191,48 +1349,80 @@ otf.enhancers["strip not needed data"] = function(data,filename) v.unicode = nil v.index = nil end + -- only needed on non sparse/mixed mode + if v.math then + if v.mathkern then v.mathkern = nil end + if v.horiz_variant then v.horiz_variant = nil end + if v.vert_variants then v.vert_variants = nil end + end + -- end - data.luatex.comment = "Glyph tables have their original index. When present, mykern tables are indexed by unicode." - data.map = nil - data.names = nil -- funny names for editors - data.glyphcnt = nil - data.glyphmax = nil - if true then - data.gpos = nil - data.gsub = nil - data.anchor_classes = nil - end + data.luatex.comment = "Glyph tables have their original index. When present, kern tables are indexed by unicode." end -otf.enhancers["migrate metadata"] = function(data,filename) - local global_fields = otf.tables.global_fields - local metadata = { } - for k,v in next, data do - if not global_fields[k] then - metadata[k] = v - data[k] = nil +actions["check metadata"] = function(data,filename,raw) + local metadata = data.metadata + metadata.method = loadmethod + if loadmethod == "sparse" then + for _, k in next, mainfields do + if valid_fields[k] then + local v = raw[k] + if global_fields[k] then + if not data[k] then + data[k] = v + end + else + if not metadata[k] then + metadata[k] = v + end + end + end + end + else + for k, v in next, raw do + if valid_fields[k] then + if global_fields[k] then + if not data[k] then + data[v] = v + end + else + if not metadata[k] then + metadata[k] = v + end + end + end + end + end + local pfminfo = raw.pfminfo + if pfminfo then + data.pfminfo = pfminfo + metadata.isfixedpitch = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced") + metadata.charwidth = pfminfo and pfminfo.avgwidth + end + local ttftables = metadata.ttf_tables + if ttftables then + for i=1,#ttftables do + ttftables[i].data = "deleted" end end - data.metadata = metadata - -- goodies - local pfminfo = data.pfminfo - metadata.isfixedpitch = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose["proportion"] == "Monospaced") - metadata.charwidth = pfminfo and pfminfo.avgwidth + metadata.xuid = nil + data.udglyphs = nil + data.map = nil end -local private_math_parameters = { +local private_mathparameters = { "FractionDelimiterSize", "FractionDelimiterDisplayStyleSize", } -otf.enhancers["check math parameters"] = function(data,filename) +actions["check math parameters"] = function(data,filename,raw) local mathdata = data.metadata.math if mathdata then - for m=1,#private_math_parameters do - local pmp = private_math_parameters[m] + for m=1,#private_mathparameters do + local pmp = private_mathparameters[m] if not mathdata[pmp] then if trace_loading then - logs.report("load otf", "setting math parameter '%s' to 0", pmp) + report_otf("setting math parameter '%s' to 0", pmp) end mathdata[pmp] = 0 end @@ -1240,96 +1430,100 @@ otf.enhancers["check math parameters"] = function(data,filename) end end -otf.enhancers["flatten glyph lookups"] = function(data,filename) - for k, v in next, data.glyphs do - local lookups = v.lookups + +-- kern: ttf has a table with kerns +-- +-- Weird, as maxfirst and maxseconds can have holes, first seems to be indexed, but +-- seconds can start at 2 .. this need to be fixed as getn as well as # are sort of +-- unpredictable alternatively we could force an [1] if not set (maybe I will do that +-- anyway). + +actions["reorganize glyph lookups"] = function(data,filename,raw) + local glyphs = data.glyphs + for index, udglyph in next, data.udglyphs do + local lookups = udglyph.lookups if lookups then + local glyph = glyphs[index] + local l = { } for kk, vv in next, lookups do + local aa = { } + l[kk] = aa for kkk=1,#vv do local vvv = vv[kkk] local s = vvv.specification - if s then - local t = vvv.type - if t == "ligature" then - vv[kkk] = { "ligature", s.components, s.char } - elseif t == "alternate" then - vv[kkk] = { "alternate", s.components } - elseif t == "substitution" then - vv[kkk] = { "substitution", s.variant } - elseif t == "multiple" then - vv[kkk] = { "multiple", s.components } - elseif t == "position" then - vv[kkk] = { "position", { s.x or 0, s.y or 0, s.h or 0, s.v or 0 } } - elseif t == "pair" then - local one, two, paired = s.offsets[1], s.offsets[2], s.paired or "" - if one then - if two then - vv[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0 } } - else - vv[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } } - end + local t = vvv.type + -- #aa+1 + if t == "ligature" then + aa[kkk] = { "ligature", s.components, s.char } + elseif t == "alternate" then + aa[kkk] = { "alternate", s.components } + elseif t == "substitution" then + aa[kkk] = { "substitution", s.variant } + elseif t == "multiple" then + aa[kkk] = { "multiple", s.components } + elseif t == "position" then + aa[kkk] = { "position", { s.x or 0, s.y or 0, s.h or 0, s.v or 0 } } + elseif t == "pair" then + -- maybe flatten this one + local one, two, paired = s.offsets[1], s.offsets[2], s.paired or "" + if one then + if two then + aa[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0 } } else - if two then - vv[kkk] = { "pair", paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { } - else - vv[kkk] = { "pair", paired } - end + aa[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } } end else - if trace_loading then - logs.report("load otf", "flattening needed, report to context list") - end - for a, b in next, s do - if trace_loading and vvv[a] then - logs.report("load otf", "flattening conflict, report to context list") - end - vvv[a] = b + if two then + aa[kkk] = { "pair", paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { } + else + aa[kkk] = { "pair", paired } end - vvv.specification = nil end end end end - end - end -end - -otf.enhancers["simplify glyph lookups"] = function(data,filename) - for k, v in next, data.glyphs do - local lookups = v.lookups - if lookups then + -- we could combine this local slookups, mlookups - for kk, vv in next, lookups do + for kk, vv in next, l do if #vv == 1 then if not slookups then slookups = { } - v.slookups = slookups + glyph.slookups = slookups end slookups[kk] = vv[1] else if not mlookups then mlookups = { } - v.mlookups = mlookups + glyph.mlookups = mlookups end mlookups[kk] = vv end end - v.lookups = nil + glyph.lookups = nil -- when using table end end end -otf.enhancers["flatten anchor tables"] = function(data,filename) - for k, v in next, data.glyphs do - if v.anchors then - for kk, vv in next, v.anchors do +actions["reorganize glyph anchors"] = function(data,filename,raw) + local glyphs = data.glyphs + for index, udglyph in next, data.udglyphs do + local anchors = udglyph.anchors + if anchors then + local glyph = glyphs[index] + local a = { } + glyph.anchors = a + for kk, vv in next, anchors do + local aa = { } + a[kk] = aa for kkk, vvv in next, vv do if vvv.x or vvv.y then - vv[kkk] = { vvv.x or 0, vvv.y or 0 } + aa[kkk] = { vvv.x , vvv.y } else + local aaa = { } + aa[kkk] = aaa for kkkk=1,#vvv do local vvvv = vvv[kkkk] - vvv[kkkk] = { vvvv.x or 0, vvvv.y or 0 } + aaa[kkkk] = { vvvv.x, vvvv.y } end end end @@ -1338,72 +1532,40 @@ otf.enhancers["flatten anchor tables"] = function(data,filename) end end -otf.enhancers["flatten feature tables"] = function(data,filename) - -- is this needed? do we still use them at all? - for _, tag in next, otf.glists do - if data[tag] then - if trace_loading then - logs.report("load otf", "flattening %s table", tag) - end - for k, v in next, data[tag] do - local features = v.features - if features then - for kk=1,#features do - local vv = features[kk] - local t = { } - local scripts = vv.scripts - for kkk=1,#scripts do - local vvv = scripts[kkk] - t[vvv.script] = vvv.langs - end - vv.scripts = t - end - end - end - end - end -end +--~ actions["check extra features"] = function(data,filename,raw) +--~ -- later, ctx only +--~ end -otf.enhancers.patches = otf.enhancers.patches or { } +-- -- -- -- -- -- +-- -- -- -- -- -- -otf.enhancers["patch bugs"] = function(data,filename) - local basename = file.basename(lower(filename)) - for pattern, action in next, otf.enhancers.patches do - if find(basename,pattern) then - action(data,filename) - end +function features.register(name,default,description) + featurelist[#featurelist+1] = name + defaultfeatures[name] = default + if description and description ~= "" then + fonts.otf.tables.features[name] = description end end --- tex features - -fonts.otf.enhancers["enrich with features"] = function(data,filename) - -- later, ctx only -end - -function otf.features.register(name,default) - otf.features.list[#otf.features.list+1] = name - otf.features.default[name] = default -end - -- for context this will become a task handler -function otf.set_features(tfmdata,features) +local lists = { -- why local + fonts.triggers, + fonts.processors, + fonts.manipulators, +} + +function otf.setfeatures(tfmdata,features) local processes = { } if features and next(features) then - local lists = { -- why local - fonts.triggers, - fonts.processors, - fonts.manipulators, - } - local mode = tfmdata.mode or fonts.mode -- or features.mode + local mode = tfmdata.mode or features.mode or "base" local initializers = fonts.initializers local fi = initializers[mode] if fi then local fiotf = fi.otf if fiotf then local done = { } - for l=1,4 do + for l=1,#lists do local list = lists[l] if list then for i=1,#list do @@ -1412,10 +1574,10 @@ function otf.set_features(tfmdata,features) if value and fiotf[f] then -- brr if not done[f] then -- so, we can move some to triggers if trace_features then - logs.report("define otf","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown', tfmdata.fullname or 'unknown') + report_otf("initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown', tfmdata.fullname or 'unknown') end fiotf[f](tfmdata,value) -- can set mode (no need to pass otf) - mode = tfmdata.mode or fonts.mode -- keep this, mode can be set local ! + mode = tfmdata.mode or features.mode or "base" local im = initializers[mode] if im then fiotf = initializers[mode].otf @@ -1428,18 +1590,19 @@ function otf.set_features(tfmdata,features) end end end +tfmdata.mode = mode local fm = fonts.methods[mode] -- todo: zonder node/mode otf/... if fm then local fmotf = fm.otf if fmotf then - for l=1,4 do + for l=1,#lists do local list = lists[l] if list then for i=1,#list do local f = list[i] if fmotf[f] then -- brr if trace_features then - logs.report("define otf","installing feature handler %s for mode %s for font %s",f,mode or 'unknown', tfmdata.fullname or 'unknown') + report_otf("installing feature handler %s for mode %s for font %s",f,mode or 'unknown', tfmdata.fullname or 'unknown') end processes[#processes+1] = fmotf[f] end @@ -1454,95 +1617,28 @@ function otf.set_features(tfmdata,features) return processes, features end -function otf.otf_to_tfm(specification) - local name = specification.name - local sub = specification.sub - local filename = specification.filename - local format = specification.format - local features = specification.features.normal - local cache_id = specification.hash - local tfmdata = containers.read(tfm.cache,cache_id) ---~ print(cache_id) - if not tfmdata then - local otfdata = otf.load(filename,format,sub,features and features.featurefile) - if otfdata and next(otfdata) then - otfdata.shared = otfdata.shared or { - featuredata = { }, - anchorhash = { }, - initialized = false, - } - tfmdata = otf.copy_to_tfm(otfdata,cache_id) - if tfmdata and next(tfmdata) then - tfmdata.unique = tfmdata.unique or { } - tfmdata.shared = tfmdata.shared or { } -- combine - local shared = tfmdata.shared - shared.otfdata = otfdata - shared.features = features -- default - shared.dynamics = { } - shared.processes = { } - shared.set_dynamics = otf.set_dynamics -- fast access and makes other modules independent - -- this will be done later anyway, but it's convenient to have - -- them already for fast access - tfmdata.luatex = otfdata.luatex - tfmdata.indices = otfdata.luatex.indices - tfmdata.unicodes = otfdata.luatex.unicodes - tfmdata.marks = otfdata.luatex.marks - tfmdata.originals = otfdata.luatex.originals - tfmdata.changed = { } - tfmdata.has_italic = otfdata.metadata.has_italic - if not tfmdata.language then tfmdata.language = 'dflt' end - if not tfmdata.script then tfmdata.script = 'dflt' end - shared.processes, shared.features = otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) - end - end - containers.write(tfm.cache,cache_id,tfmdata) - end - return tfmdata -end - ---~ { ---~ ['boundingbox']={ 95, -458, 733, 1449 }, ---~ ['class']="base", ---~ ['name']="braceleft", ---~ ['unicode']=123, ---~ ['vert_variants']={ ---~ ['italic_correction']=0, ---~ ['parts']={ ---~ { ['component']="uni23A9", ['endConnectorLength']=1000, ['fullAdvance']=2546, ['is_extender']=0, ['startConnectorLength']=0, }, -- bot ---~ { ['component']="uni23AA", ['endConnectorLength']=2500, ['fullAdvance']=2501, ['is_extender']=1, ['startConnectorLength']=2500, }, -- rep ---~ { ['component']="uni23A8", ['endConnectorLength']=1000, ['fullAdvance']=4688, ['is_extender']=0, ['startConnectorLength']=1000, }, -- mid ---~ { ['component']="uni23AA", ['endConnectorLength']=2500, ['fullAdvance']=2501, ['is_extender']=1, ['startConnectorLength']=2500, }, -- rep ---~ { ['component']="uni23A7", ['endConnectorLength']=0, ['fullAdvance']=2546, ['is_extender']=0, ['startConnectorLength']=1000, }, -- top ---~ }, ---~ ['variants']="braceleft braceleft.vsize1 braceleft.vsize2 braceleft.vsize3 braceleft.vsize4 braceleft.vsize5 braceleft.vsize6 braceleft.vsize7", ---~ }, ---~ ['width']=793, ---~ }, - -- the first version made a top/mid/not extensible table, now we just pass on the variants data -- and deal with it in the tfm scaler (there is no longer an extensible table anyway) -- we cannot share descriptions as virtual fonts might extend them (ok, we could -- use a cache with a hash -fonts.formats.dfont = "truetype" -fonts.formats.ttc = "truetype" -fonts.formats.ttf = "truetype" -fonts.formats.otf = "opentype" - -function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder the tma to unicode (nasty due to one->many) +local function copytotfm(data,cache_id) -- we can save a copy when we reorder the tma to unicode (nasty due to one->many) if data then local glyphs, pfminfo, metadata = data.glyphs or { }, data.pfminfo or { }, data.metadata or { } local luatex = data.luatex local unicodes = luatex.unicodes -- names to unicodes local indices = luatex.indices - local characters, parameters, math_parameters, descriptions = { }, { }, { }, { } + local mode = data.mode or "base" + local characters, parameters, mathparameters, descriptions = { }, { }, { }, { } local designsize = metadata.designsize or metadata.design_size or 100 if designsize == 0 then designsize = 100 end local spaceunits, spacer = 500, "space" -- indices maps from unicodes to indices + -- this wil stay as we can manipulate indices + -- beforehand for u, i in next, indices do characters[u] = { } -- we need this because for instance we add protruding info and loop over characters descriptions[u] = glyphs[i] @@ -1551,7 +1647,7 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th if metadata.math then -- parameters for name, value in next, metadata.math do - math_parameters[name] = value + mathparameters[name] = value end -- we could use a subset for u, char in next, characters do @@ -1598,10 +1694,10 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th end end -- end math - local endash, emdash, space = 0x20, 0x2014, "space" -- unicodes['space'], unicodes['emdash'] + local space, emdash = 0x20, 0x2014 -- unicodes['space'], unicodes['emdash'] if metadata.isfixedpitch then - if descriptions[endash] then - spaceunits, spacer = descriptions[endash].width, "space" + if descriptions[space] then + spaceunits, spacer = descriptions[space].width, "space" end if not spaceunits and descriptions[emdash] then spaceunits, spacer = descriptions[emdash].width, "emdash" @@ -1610,8 +1706,8 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th spaceunits, spacer = metadata.charwidth, "charwidth" end else - if descriptions[endash] then - spaceunits, spacer = descriptions[endash].width, "space" + 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" @@ -1620,12 +1716,12 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th spaceunits, spacer = metadata.charwidth, "charwidth" end end - spaceunits = tonumber(spaceunits) or tfm.units/2 -- 500 -- brrr + spaceunits = tonumber(spaceunits) or 500 -- brrr -- we need a runtime lookup because of running from cdrom or zip, brrr (shouldn't we use the basename then?) - local filename = fonts.tfm.checked_filename(luatex) + local filename = fonts.tfm.checkedfilename(luatex) local fontname = metadata.fontname local fullname = metadata.fullname or fontname - local cidinfo = data.cidinfo + local cidinfo = data.cidinfo -- or { } local units = metadata.units_per_em or 1000 -- cidinfo.registry = cidinfo and cidinfo.registry or "" -- weird here, fix upstream @@ -1646,7 +1742,7 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th if metadata.isfixedpitch then parameters.space_stretch = 0 parameters.space_shrink = 0 - elseif otf.syncspace then -- + elseif syncspace then -- parameters.space_stretch = spaceunits/2 parameters.space_shrink = spaceunits/3 end @@ -1663,10 +1759,14 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th end end -- + local fileformat = data.format or fonts.fontformat(filename,"opentype") + if units > 1000 then + fileformat = "truetype" + end return { characters = characters, parameters = parameters, - math_parameters = math_parameters, + mathparameters = mathparameters, descriptions = descriptions, indices = indices, unicodes = unicodes, @@ -1675,15 +1775,15 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th boundarychar_label = 0, boundarychar = 65536, designsize = (designsize/10)*65536, - spacer = "500 units", encodingbytes = 2, + mode = mode, filename = filename, fontname = fontname, fullname = fullname, psname = fontname or fullname, name = filename or fullname, units = units, - format = fonts.fontformat(filename,"opentype"), + format = fileformat, cidinfo = cidinfo, ascender = abs(metadata.ascent or 0), descender = abs(metadata.descent or 0), @@ -1695,10 +1795,57 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th end end -otf.features.register('mathsize') +local function otftotfm(specification) + local name = specification.name + local sub = specification.sub + local filename = specification.filename + local format = specification.format + local features = specification.features.normal + local cache_id = specification.hash + local tfmdata = containers.read(tfm.cache,cache_id) +--~ print(cache_id) + if not tfmdata then + local otfdata = otf.load(filename,format,sub,features and features.featurefile) + if otfdata and next(otfdata) then + otfdata.shared = otfdata.shared or { + featuredata = { }, + anchorhash = { }, + initialized = false, + } + tfmdata = copytotfm(otfdata,cache_id) + if tfmdata and next(tfmdata) then + tfmdata.unique = tfmdata.unique or { } + tfmdata.shared = tfmdata.shared or { } -- combine + local shared = tfmdata.shared + shared.otfdata = otfdata + shared.features = features -- default + shared.dynamics = { } + shared.processes = { } + shared.setdynamics = otf.setdynamics -- fast access and makes other modules independent + -- this will be done later anyway, but it's convenient to have + -- them already for fast access + tfmdata.luatex = otfdata.luatex + tfmdata.indices = otfdata.luatex.indices + tfmdata.unicodes = otfdata.luatex.unicodes + tfmdata.marks = otfdata.luatex.marks + tfmdata.originals = otfdata.luatex.originals + tfmdata.changed = { } + tfmdata.has_italic = otfdata.metadata.has_italic + if not tfmdata.language then tfmdata.language = 'dflt' end + if not tfmdata.script then tfmdata.script = 'dflt' end + -- at this moment no characters are assinged yet, only empty slots + shared.processes, shared.features = otf.setfeatures(tfmdata,definers.check(features,defaultfeatures)) + end + end + containers.write(tfm.cache,cache_id,tfmdata) + end + return tfmdata +end + +features.register('mathsize') -function tfm.read_from_open_type(specification) - local tfmtable = otf.otf_to_tfm(specification) +local function read_from_otf(specification) -- wrong namespace + local tfmtable = otftotfm(specification) if tfmtable then local otfdata = tfmtable.shared.otfdata tfmtable.name = specification.name @@ -1717,7 +1864,7 @@ function tfm.read_from_open_type(specification) if p then local ps = p * specification.textsize / 100 if trace_math then - logs.report("define font","asked script size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) + report_otf("asked script size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) end s = ps end @@ -1726,7 +1873,7 @@ function tfm.read_from_open_type(specification) if p then local ps = p * specification.textsize / 100 if trace_math then - logs.report("define font","asked scriptscript size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) + report_otf("asked scriptscript size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) end s = ps end @@ -1735,13 +1882,13 @@ function tfm.read_from_open_type(specification) end end tfmtable = tfm.scale(tfmtable,s,specification.relativeid) - if tfm.fontname_mode == "specification" then + if tfm.fontnamemode == "specification" then -- not to be used in context ! local specname = specification.specification if specname then tfmtable.name = specname if trace_defining then - logs.report("define font","overloaded fontname: '%s'",specname) + report_otf("overloaded fontname: '%s'",specname) end end end @@ -1753,7 +1900,7 @@ end -- helpers -function otf.collect_lookups(otfdata,kind,script,language) +function otf.collectlookups(otfdata,kind,script,language) -- maybe store this in the font local sequences = otfdata.luatex.sequences if sequences then @@ -1783,3 +1930,56 @@ function otf.collect_lookups(otfdata,kind,script,language) end return nil, nil end + +-- readers + +fonts.formats.dfont = "truetype" +fonts.formats.ttc = "truetype" +fonts.formats.ttf = "truetype" +fonts.formats.otf = "opentype" + +local function check_otf(forced,specification,suffix,what) + local name = specification.name + if forced then + name = file.addsuffix(name,suffix,true) + end + local fullname, tfmtable = findbinfile(name,suffix) or "", nil -- one shot + -- if false then -- can be enabled again when needed + -- if fullname == "" then + -- local fb = fonts.names.old_to_new[name] + -- if fb then + -- fullname = findbinfile(fb,suffix) or "" + -- end + -- end + -- if fullname == "" then + -- local fb = fonts.names.new_to_old[name] + -- if fb then + -- fullname = findbinfile(fb,suffix) or "" + -- end + -- end + -- end + if fullname == "" then + fullname = fonts.names.getfilename(name,suffix) + end + if fullname ~= "" then + specification.filename, specification.format = fullname, what -- hm, so we do set the filename, then + tfmtable = read_from_otf(specification) -- we need to do it for all matches / todo + end + return tfmtable +end + +function readers.opentype(specification,suffix,what) + local forced = specification.forced or "" + if forced == "otf" then + return check_otf(true,specification,forced,"opentype") + elseif forced == "ttf" or forced == "ttc" or forced == "dfont" then + return check_otf(true,specification,forced,"truetype") + else + return check_otf(false,specification,suffix,what) + end +end + +function readers.otf (specification) return readers.opentype(specification,"otf","opentype") end +function readers.ttf (specification) return readers.opentype(specification,"ttf","truetype") end +function readers.ttc (specification) return readers.opentype(specification,"ttf","truetype") end -- !! +function readers.dfont(specification) return readers.opentype(specification,"ttf","truetype") end -- !! diff --git a/otfl-font-oti.lua b/otfl-font-oti.lua index 4cb2706..e531ba8 100644 --- a/otfl-font-oti.lua +++ b/otfl-font-oti.lua @@ -6,19 +6,17 @@ if not modules then modules = { } end modules ['font-oti'] = { license = "see context related readme files" } --- i need to check features=yes|no also in relation to hashing - local lower = string.lower -local otf = fonts.otf +local fonts = fonts -otf.default_language = 'latn' -otf.default_script = 'dflt' +local otf = fonts.otf +local initializers = fonts.initializers -local languages = otf.tables.languages -local scripts = otf.tables.scripts +local languages = otf.tables.languages +local scripts = otf.tables.scripts -function otf.features.language(tfmdata,value) +local function set_language(tfmdata,value) if value then value = lower(value) if languages[value] then @@ -27,7 +25,7 @@ function otf.features.language(tfmdata,value) end end -function otf.features.script(tfmdata,value) +local function set_script(tfmdata,value) if value then value = lower(value) if scripts[value] then @@ -36,21 +34,24 @@ function otf.features.script(tfmdata,value) end end -function otf.features.mode(tfmdata,value) +local function set_mode(tfmdata,value) if value then tfmdata.mode = lower(value) end end -fonts.initializers.base.otf.language = otf.features.language -fonts.initializers.base.otf.script = otf.features.script -fonts.initializers.base.otf.mode = otf.features.mode -fonts.initializers.base.otf.method = otf.features.mode +local base_initializers = initializers.base.otf +local node_initializers = initializers.node.otf + +base_initializers.language = set_language +base_initializers.script = set_script +base_initializers.mode = set_mode +base_initializers.method = set_mode -fonts.initializers.node.otf.language = otf.features.language -fonts.initializers.node.otf.script = otf.features.script -fonts.initializers.node.otf.mode = otf.features.mode -fonts.initializers.node.otf.method = otf.features.mode +node_initializers.language = set_language +node_initializers.script = set_script +node_initializers.mode = set_mode +node_initializers.method = set_mode otf.features.register("features",true) -- we always do features table.insert(fonts.processors,"features") -- we need a proper function for doing this diff --git a/otfl-font-otn.lua b/otfl-font-otn.lua index 6a6a046..6c5ba12 100644 --- a/otfl-font-otn.lua +++ b/otfl-font-otn.lua @@ -124,6 +124,9 @@ local concat, insert, remove = table.concat, table.insert, table.remove local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip local type, next, tonumber, tostring = type, next, tonumber, tostring local lpegmatch = lpeg.match +local random = math.random + +local logs, trackers, fonts, nodes, attributes = logs, trackers, fonts, nodes, attributes local otf = fonts.otf local tfm = fonts.tfm @@ -145,6 +148,12 @@ local trace_steps = false trackers.register("otf.steps", function local trace_skips = false trackers.register("otf.skips", function(v) trace_skips = v end) local trace_directions = false trackers.register("otf.directions", function(v) trace_directions = v end) +local report_direct = logs.reporter("fonts","otf direct") +local report_subchain = logs.reporter("fonts","otf subchain") +local report_chain = logs.reporter("fonts","otf chain") +local report_process = logs.reporter("fonts","otf process") +local report_prepare = logs.reporter("fonts","otf prepare") + trackers.register("otf.verbose_chain", function(v) otf.setcontextchain(v and "verbose") end) trackers.register("otf.normal_chain", function(v) otf.setcontextchain(v and "normal") end) @@ -167,13 +176,21 @@ local zwj = 0x200D local wildcard = "*" local default = "dflt" -local split_at_space = lpeg.splitters[" "] or lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway +local split_at_space = lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway + +local nodecodes = nodes.nodecodes +local whatcodes = nodes.whatcodes +local glyphcodes = nodes.glyphcodes + +local glyph_code = nodecodes.glyph +local glue_code = nodecodes.glue +local disc_code = nodecodes.disc +local whatsit_code = nodecodes.whatsit -local glyph = node.id('glyph') -local glue = node.id('glue') -local kern = node.id('kern') -local disc = node.id('disc') -local whatsit = node.id('whatsit') +local dir_code = whatcodes.dir +local localpar_code = whatcodes.localpar + +local ligature_code = glyphcodes.ligature local state = attributes.private('state') local markbase = attributes.private('markbase') @@ -184,16 +201,17 @@ local curscurs = attributes.private('curscurs') local cursdone = attributes.private('cursdone') local kernpair = attributes.private('kernpair') -local set_mark = nodes.set_mark -local set_cursive = nodes.set_cursive -local set_kern = nodes.set_kern -local set_pair = nodes.set_pair +local injections = nodes.injections +local setmark = injections.setmark +local setcursive = injections.setcursive +local setkern = injections.setkern +local setpair = injections.setpair local markonce = true local cursonce = true local kernonce = true -local fontdata = fonts.ids +local fontdata = fonts.identifiers otf.features.process = { } @@ -216,11 +234,12 @@ local featurevalue = false -- we cheat a bit and assume that a font,attr combination are kind of ranged -local context_setups = fonts.define.specify.context_setups -local context_numbers = fonts.define.specify.context_numbers -local context_merged = fonts.define.specify.context_merged +local specifiers = fonts.definers.specifiers +local contextsetups = specifiers.contextsetups +local contextnumbers = specifiers.contextnumbers +local contextmerged = specifiers.contextmerged --- we cannot optimize with "start = first_character(head)" because then we don't +-- we cannot optimize with "start = first_glyph(head)" because then we don't -- know which rlmode we're in which messes up cursive handling later on -- -- head is always a whatsit so we can safely assume that head is not changed @@ -242,10 +261,10 @@ local function logprocess(...) if trace_steps then registermessage(...) end - logs.report("otf direct",...) + report_direct(...) end local function logwarning(...) - logs.report("otf direct",...) + report_direct(...) end local function gref(n) @@ -263,9 +282,9 @@ local function gref(n) local num, nam = { }, { } for i=1,#n do local ni = n[i] - num[#num+1] = format("U+%04X",ni) - local dni = descriptions[ni] - nam[#num] = (dni and dni.name) or "?" + local di = descriptions[ni] + num[i] = format("U+%04X",ni) + nam[i] = di and di.name or "?" end return format("%s (%s)",concat(num," "), concat(nam," ")) end @@ -303,7 +322,7 @@ local function markstoligature(kind,lookupname,start,stop,char) snext.prev = current end start.prev, stop.next = nil, nil - current.char, current.subtype, current.components = char, 2, start + current.char, current.subtype, current.components = char, ligature_code, start return keep end @@ -313,16 +332,16 @@ local function toligature(kind,lookupname,start,stop,char,markflag,discfound) -- --~ local lignode = copy_node(start) --~ lignode.font = start.font --~ lignode.char = char ---~ lignode.subtype = 2 +--~ lignode.subtype = ligature_code --~ start = node.do_ligature_n(start, stop, lignode) ---~ if start.id == disc then +--~ if start.id == disc_code then --~ local prev = start.prev --~ start = start.next --~ end if discfound then -- print("start->stop",nodes.tosequence(start,stop)) local lignode = copy_node(start) - lignode.font, lignode.char, lignode.subtype = start.font, char, 2 + lignode.font, lignode.char, lignode.subtype = start.font, char, ligature_code local next, prev = stop.next, start.prev stop.next = nil lignode = node.do_ligature_n(start, stop, lignode) @@ -344,7 +363,7 @@ local function toligature(kind,lookupname,start,stop,char,markflag,discfound) -- snext.prev = current end start.prev, stop.next = nil, nil - current.char, current.subtype, current.components = char, 2, start + current.char, current.subtype, current.components = char, ligature_code, start local head = current if deletemarks then if trace_marks then @@ -370,7 +389,7 @@ local function toligature(kind,lookupname,start,stop,char,markflag,discfound) -- start = start.next end start = current.next - while start and start.id == glyph do + while start and start.id == glyph_code do if marks[start.char] then set_attribute(start,markdone,i) if trace_marks then @@ -401,7 +420,7 @@ end local function alternative_glyph(start,alternatives,kind,chainname,chainlookupname,lookupname) -- chainname and chainlookupname optional local value, choice, n = featurevalue or tfmdata.shared.features[kind], nil, #alternatives -- global value, brrr if value == "random" then - local r = math.random(1,n) + local r = random(1,n) value, choice = format("random, choice %s",r), alternatives[r] elseif value == "first" then value, choice = format("first, choice %s",1), alternatives[1] @@ -465,7 +484,7 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma if marks[startchar] then while s do local id = s.id - if id == glyph and s.subtype<256 then + if id == glyph_code and s.subtype<256 then if s.font == currentfont then local char = s.char local lg = ligature[1][char] @@ -497,7 +516,7 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma local skipmark = sequence.flags[1] while s do local id = s.id - if id == glyph and s.subtype<256 then + if id == glyph_code and s.subtype<256 then if s.font == currentfont then local char = s.char if skipmark and marks[char] then @@ -515,7 +534,7 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma else break end - elseif id == disc then + elseif id == disc_code then discfound = true s = s.next else @@ -545,12 +564,12 @@ function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) local markchar = start.char if marks[markchar] then local base = start.prev -- [glyph] [start=mark] - if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then local basechar = base.char if marks[basechar] then while true do base = base.prev - if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then basechar = base.char if not marks[basechar] then break @@ -575,7 +594,7 @@ function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma) + local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -590,7 +609,7 @@ function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - fonts.register_message(currentfont,basechar,"no base anchors") + fonts.registermessage(currentfont,basechar,"no base anchors") end elseif trace_bugs then logwarning("%s: prev node is no char",pref(kind,lookupname)) @@ -607,13 +626,13 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) if marks[markchar] then local base = start.prev -- [glyph] [optional marks] [start=mark] local index = 1 - if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then local basechar = base.char if marks[basechar] then index = index + 1 while true do base = base.prev - if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then basechar = base.char if marks[basechar] then index = index + 1 @@ -643,7 +662,7 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) if ma then ba = ba[index] if ba then - local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index) + local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma,index) if trace_marks then logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) @@ -660,7 +679,7 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - fonts.register_message(currentfont,basechar,"no base anchors") + fonts.registermessage(currentfont,basechar,"no base anchors") end elseif trace_bugs then logwarning("%s: prev node is no char",pref(kind,lookupname)) @@ -677,7 +696,7 @@ function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) --~ local alreadydone = markonce and has_attribute(start,markmark) --~ if not alreadydone then local base = start.prev -- [glyph] [basemark] [start=mark] - if base and base.id == glyph and base.subtype<256 and base.font == currentfont then -- subtype test can go + if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -- subtype test can go local basechar = base.char local baseanchors = descriptions[basechar] if baseanchors then @@ -690,7 +709,7 @@ function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma) + local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -706,7 +725,7 @@ function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - fonts.register_message(currentfont,basechar,"no base anchors") + fonts.registermessage(currentfont,basechar,"no base anchors") end elseif trace_bugs then logwarning("%s: prev node is no mark",pref(kind,lookupname)) @@ -731,7 +750,7 @@ function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to end else local nxt = start.next - while not done and nxt and nxt.id == glyph and nxt.subtype<256 and nxt.font == currentfont do + while not done and nxt and nxt.id == glyph_code and nxt.subtype<256 and nxt.font == currentfont do local nextchar = nxt.char if marks[nextchar] then -- should not happen (maybe warning) @@ -748,7 +767,7 @@ function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to if al[anchor] then local exit = exitanchors[anchor] if exit then - local dx, dy, bound = set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + local dx, dy, bound = setcursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) if trace_cursive then logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) end @@ -761,7 +780,7 @@ function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) - fonts.register_message(currentfont,startchar,"no entry anchors") + fonts.registermessage(currentfont,startchar,"no entry anchors") end break end @@ -778,7 +797,7 @@ end function handlers.gpos_single(start,kind,lookupname,kerns,sequence) local startchar = start.char - local dx, dy, w, h = set_pair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + local dx, dy, w, h = setpair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) if trace_kerns then logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) end @@ -794,9 +813,9 @@ function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) else local prev, done = start, false local factor = tfmdata.factor - while snext and snext.id == glyph and snext.subtype<256 and snext.font == currentfont do + while snext and snext.id == glyph_code and snext.subtype<256 and snext.font == currentfont do local nextchar = snext.char -local krn = kerns[nextchar] + local krn = kerns[nextchar] if not krn and marks[nextchar] then prev = snext snext = snext.next @@ -809,23 +828,23 @@ local krn = kerns[nextchar] local a, b = krn[3], krn[4] if a and #a > 0 then local startchar = start.char - local x, y, w, h = set_pair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) + local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) if trace_kerns then logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) end end if b and #b > 0 then local startchar = start.char - local x, y, w, h = set_pair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) + local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) if trace_kerns then logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) end end else - logs.report("%s: check this out (old kern stuff)",pref(kind,lookupname)) + report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) local a, b = krn[3], krn[7] if a and a ~= 0 then - local k = set_kern(snext,factor,rlmode,a) + local k = setkern(snext,factor,rlmode,a) if trace_kerns then logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) end @@ -836,7 +855,7 @@ local krn = kerns[nextchar] end done = true elseif krn ~= 0 then - local k = set_kern(snext,factor,rlmode,krn) + local k = setkern(snext,factor,rlmode,krn) if trace_kerns then logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) end @@ -861,12 +880,11 @@ local function logprocess(...) if trace_steps then registermessage(...) end - logs.report("otf subchain",...) -end -local function logwarning(...) - logs.report("otf subchain",...) + report_subchain(...) end +local logwarning = report_subchain + -- ['coverage']={ -- ['after']={ "r" }, -- ['before']={ "q" }, @@ -904,12 +922,11 @@ local function logprocess(...) if trace_steps then registermessage(...) end - logs.report("otf chain",...) -end -local function logwarning(...) - logs.report("otf chain",...) + report_chain(...) end +local logwarning = report_chain + -- We could share functions but that would lead to extra function calls with many -- arguments, redundant tests and confusing messages. @@ -976,7 +993,7 @@ function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,c local current = start local subtables = currentlookup.subtables while current do - if current.id == glyph then + if current.id == glyph_code then local currentchar = current.char local lookupname = subtables[1] local replacement = cache.gsub_single[lookupname] @@ -1064,7 +1081,7 @@ function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,cach local current = start local subtables = currentlookup.subtables while current do - if current.id == glyph then + if current.id == glyph_code then local currentchar = current.char local lookupname = subtables[1] local alternatives = cache.gsub_alternate[lookupname] @@ -1121,7 +1138,7 @@ function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache local s, discfound, last, nofreplacements = start.next, false, stop, 0 while s do local id = s.id - if id == disc then + if id == disc_code then s = s.next discfound = true else @@ -1182,12 +1199,12 @@ function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cach end if markanchors then local base = start.prev -- [glyph] [start=mark] - if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then local basechar = base.char if marks[basechar] then while true do base = base.prev - if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then basechar = base.char if not marks[basechar] then break @@ -1209,7 +1226,7 @@ function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cach if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma) + local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -1247,13 +1264,13 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, if markanchors then local base = start.prev -- [glyph] [optional marks] [start=mark] local index = 1 - if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then local basechar = base.char if marks[basechar] then index = index + 1 while true do base = base.prev - if base and base.id == glyph and base.subtype<256 and base.font == currentfont then + if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then basechar = base.char if marks[basechar] then index = index + 1 @@ -1282,7 +1299,7 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, if ma then ba = ba[index] if ba then - local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index) + local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma,index) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) @@ -1323,7 +1340,7 @@ function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cach end if markanchors then local base = start.prev -- [glyph] [basemark] [start=mark] - if base and base.id == glyph and base.subtype<256 and base.font == currentfont then -- subtype test can go + if base and base.id == glyph_code and base.subtype<256 and base.font == currentfont then -- subtype test can go local basechar = base.char local baseanchors = descriptions[basechar].anchors if baseanchors then @@ -1334,7 +1351,7 @@ function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cach if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = set_mark(start,base,tfmdata.factor,rlmode,ba,ma) + local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -1383,7 +1400,7 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache, end else local nxt = start.next - while not done and nxt and nxt.id == glyph and nxt.subtype<256 and nxt.font == currentfont do + while not done and nxt and nxt.id == glyph_code and nxt.subtype<256 and nxt.font == currentfont do local nextchar = nxt.char if marks[nextchar] then -- should not happen (maybe warning) @@ -1400,7 +1417,7 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache, if al[anchor] then local exit = exitanchors[anchor] if exit then - local dx, dy, bound = set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + local dx, dy, bound = setcursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) if trace_cursive then logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) end @@ -1413,7 +1430,7 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache, end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) - fonts.register_message(currentfont,startchar,"no entry anchors") + fonts.registermessage(currentfont,startchar,"no entry anchors") end break end @@ -1439,7 +1456,7 @@ function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,cache,c if kerns then kerns = kerns[startchar] if kerns then - local dx, dy, w, h = set_pair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + local dx, dy, w, h = setpair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) if trace_kerns then logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) end @@ -1463,7 +1480,7 @@ function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,cur if kerns then local prev, done = start, false local factor = tfmdata.factor - while snext and snext.id == glyph and snext.subtype<256 and snext.font == currentfont do + while snext and snext.id == glyph_code and snext.subtype<256 and snext.font == currentfont do local nextchar = snext.char local krn = kerns[nextchar] if not krn and marks[nextchar] then @@ -1477,23 +1494,23 @@ function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,cur local a, b = krn[3], krn[4] if a and #a > 0 then local startchar = start.char - local x, y, w, h = set_pair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) + local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) if trace_kerns then logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) end end if b and #b > 0 then local startchar = start.char - local x, y, w, h = set_pair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) + local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) if trace_kerns then logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) end end else - logs.report("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) + report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) local a, b = krn[3], krn[7] if a and a ~= 0 then - local k = set_kern(snext,factor,rlmode,a) + local k = setkern(snext,factor,rlmode,a) if trace_kerns then logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) end @@ -1504,7 +1521,7 @@ function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,cur end done = true elseif krn ~= 0 then - local k = set_kern(snext,factor,rlmode,krn) + local k = setkern(snext,factor,rlmode,krn) if trace_kerns then logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) end @@ -1551,7 +1568,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence -- f..l = mid string if s == 1 then -- never happens - match = current.id == glyph and current.subtype<256 and current.font == currentfont and seq[1][current.char] + match = current.id == glyph_code and current.subtype<256 and current.font == currentfont and seq[1][current.char] else -- todo: better space check (maybe check for glue) local f, l = ck[4], ck[5] @@ -1565,12 +1582,12 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence -- we cannot optimize for n=2 because there can be disc nodes -- if not someskip and n == l then -- -- n=2 and no skips then faster loop - -- match = last and last.id == glyph and last.subtype<256 and last.font == currentfont and seq[n][last.char] + -- match = last and last.id == glyph_code and last.subtype<256 and last.font == currentfont and seq[n][last.char] -- else while n <= l do if last then local id = last.id - if id == glyph then + if id == glyph_code then if last.subtype<256 and last.font == currentfont then local char = last.char local ccd = descriptions[char] @@ -1596,7 +1613,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence else match = false break end - elseif id == disc then -- what to do with kerns? + elseif id == disc_code then -- what to do with kerns? last = last.next else match = false break @@ -1615,7 +1632,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence while n >= 1 do if prev then local id = prev.id - if id == glyph then + if id == glyph_code then if prev.subtype<256 and prev.font == currentfont then -- normal char local char = prev.char local ccd = descriptions[char] @@ -1637,7 +1654,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence else match = false break end - elseif id == disc then + elseif id == disc_code then -- skip 'm elseif seq[n][32] then n = n -1 @@ -1670,7 +1687,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence while n <= s do if current then local id = current.id - if id == glyph then + if id == glyph_code then if current.subtype<256 and current.font == currentfont then -- normal char local char = current.char local ccd = descriptions[char] @@ -1692,7 +1709,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence else match = false break end - elseif id == disc then + elseif id == disc_code then -- skip 'm elseif seq[n][32] then -- brrr n = n + 1 @@ -1768,22 +1785,22 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local i = 1 repeat -if skipped then - while true do - local char = start.char - local ccd = descriptions[char] - if ccd then - local class = ccd.class - if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then - start = start.next - else - break - end - else - break - end - end -end + if skipped then + while true do + local char = start.char + local ccd = descriptions[char] + if ccd then + local class = ccd.class + if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then + start = start.next + else + break + end + else + break + end + end + end local chainlookupname = chainlookups[i] local chainlookup = lookuptable[chainlookupname] local cp = chainmores[chainlookup.type] @@ -1864,12 +1881,11 @@ local function logprocess(...) if trace_steps then registermessage(...) end - logs.report("otf process",...) -end -local function logwarning(...) - logs.report("otf process",...) + report_process(...) end +local logwarning = report_process + local function report_missing_cache(typ,lookup) local f = missing[currentfont] if not f then f = { } missing[currentfont] = f end local t = f[typ] if not t then t = { } f[typ] = t end @@ -1909,8 +1925,8 @@ function fonts.methods.node.otf.features(head,font,attr) local script, language, s_enabled, a_enabled, dyn local attribute_driven = attr and attr ~= 0 if attribute_driven then - local features = context_setups[context_numbers[attr]] -- could be a direct list - dyn = context_merged[attr] or 0 + local features = contextsetups[contextnumbers[attr]] -- could be a direct list + dyn = contextmerged[attr] or 0 language, script = features.language or "dflt", features.script or "dflt" a_enabled = features -- shared.features -- can be made local to the resolver if dyn == 2 or dyn == -2 then @@ -1967,7 +1983,7 @@ function fonts.methods.node.otf.features(head,font,attr) end if trace_applied then local typ, action = match(sequence.type,"(.*)_(.*)") - logs.report("otf node mode", + report_process( "%s font: %03i, dynamic: %03i, kind: %s, lookup: %3i, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s", (valid and "+") or "-",font,attr or 0,kind,s,script,language,what,typ,action,sequence.name) end @@ -1995,7 +2011,7 @@ function fonts.methods.node.otf.features(head,font,attr) local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo while start do local id = start.id - if id == glyph then + if id == glyph_code then if start.subtype<256 and start.font == font then local a = has_attribute(start,0) if a then @@ -2044,7 +2060,7 @@ function fonts.methods.node.otf.features(head,font,attr) else while start do local id = start.id - if id == glyph then + if id == glyph_code then if start.subtype<256 and start.font == font then local a = has_attribute(start,0) if a then @@ -2069,7 +2085,7 @@ function fonts.methods.node.otf.features(head,font,attr) else start = start.next end - -- elseif id == glue then + -- elseif id == glue_code then -- if p[5] then -- chain -- local pc = pp[32] -- if pc then @@ -2084,9 +2100,9 @@ function fonts.methods.node.otf.features(head,font,attr) -- else -- start = start.next -- end - elseif id == whatsit then + elseif id == whatsit_code then local subtype = start.subtype - if subtype == 7 then + if subtype == dir_code then local dir = start.dir if dir == "+TRT" or dir == "+TLT" then insert(txtdir,dir) @@ -2102,9 +2118,9 @@ function fonts.methods.node.otf.features(head,font,attr) rlmode = pardir end if trace_directions then - logs.report("fonts","directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) + report_process("directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) end - elseif subtype == 6 then + elseif subtype == localpar_code then local dir = start.dir if dir == "TRT" then pardir = -1 @@ -2116,7 +2132,7 @@ function fonts.methods.node.otf.features(head,font,attr) rlmode = pardir --~ txtdir = { } if trace_directions then - logs.report("fonts","directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) + report_process("directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) end end start = start.next @@ -2128,7 +2144,7 @@ function fonts.methods.node.otf.features(head,font,attr) else while start do local id = start.id - if id == glyph then + if id == glyph_code then if start.subtype<256 and start.font == font then local a = has_attribute(start,0) if a then @@ -2162,7 +2178,7 @@ function fonts.methods.node.otf.features(head,font,attr) else start = start.next end - -- elseif id == glue then + -- elseif id == glue_code then -- if p[5] then -- chain -- local pc = pp[32] -- if pc then @@ -2177,9 +2193,9 @@ function fonts.methods.node.otf.features(head,font,attr) -- else -- start = start.next -- end - elseif id == whatsit then + elseif id == whatsit_code then local subtype = start.subtype - if subtype == 7 then + if subtype == dir_code then local dir = start.dir if dir == "+TRT" or dir == "+TLT" then insert(txtdir,dir) @@ -2195,9 +2211,9 @@ function fonts.methods.node.otf.features(head,font,attr) rlmode = pardir end if trace_directions then - logs.report("fonts","directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) + report_process("directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) end - elseif subtype == 6 then + elseif subtype == localpar_code then local dir = start.dir if dir == "TRT" then pardir = -1 @@ -2209,7 +2225,7 @@ function fonts.methods.node.otf.features(head,font,attr) rlmode = pardir --~ txtdir = { } if trace_directions then - logs.report("fonts","directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) + report_process("directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode) end end start = start.next @@ -2238,13 +2254,14 @@ otf.features.prepare = { } local function split(replacement,original,cache,unicodes) -- we can cache this too, but not the same (although unicode is a unique enough hash) - local o, t, n = { }, { }, 0 + local o, t, n, no = { }, { }, 0, 0 for s in gmatch(original,"[^ ]+") do local us = unicodes[s] + no = no + 1 if type(us) == "number" then -- tonumber(us) - o[#o+1] = us + o[no] = us else - o[#o+1] = us[1] + o[no] = us[1] end end for s in gmatch(replacement,"[^ ]+") do @@ -2261,9 +2278,11 @@ end local function uncover(covers,result,cache,unicodes) -- lpeg hardly faster (.005 sec on mk) + local nofresults = #result for n=1,#covers do local c = covers[n] local cc = cache[c] + nofresults = nofresults + 1 if not cc then local t = { } for s in gmatch(c,"[^ ]+") do @@ -2277,9 +2296,9 @@ local function uncover(covers,result,cache,unicodes) end end cache[c] = t - result[#result+1] = t + result[nofresults] = t else - result[#result+1] = cc + result[nofresults] = cc end end end @@ -2317,46 +2336,48 @@ local function prepare_lookups(tfmdata) if not s then s = { } single[lookup] = s end s[old] = new --~ if trace_lookups then - --~ logs.report("define otf","lookup %s: substitution %s => %s",lookup,old,new) + --~ report_prepare("lookup %s: substitution %s => %s",lookup,old,new) --~ end end, multiple = function (p,lookup,glyph,unicode) - local old, new = unicode, { } + local old, new, nnew = unicode, { }, 0 local m = multiple[lookup] if not m then m = { } multiple[lookup] = m end m[old] = new for pc in gmatch(p[2],"[^ ]+") do local upc = unicodes[pc] + nnew = nnew + 1 if type(upc) == "number" then - new[#new+1] = upc + new[nnew] = upc else - new[#new+1] = upc[1] + new[nnew] = upc[1] end end --~ if trace_lookups then - --~ logs.report("define otf","lookup %s: multiple %s => %s",lookup,old,concat(new," ")) + --~ report_prepare("lookup %s: multiple %s => %s",lookup,old,concat(new," ")) --~ end end, alternate = function(p,lookup,glyph,unicode) - local old, new = unicode, { } + local old, new, nnew = unicode, { }, 0 local a = alternate[lookup] if not a then a = { } alternate[lookup] = a end a[old] = new for pc in gmatch(p[2],"[^ ]+") do local upc = unicodes[pc] + nnew = nnew + 1 if type(upc) == "number" then - new[#new+1] = upc + new[nnew] = upc else - new[#new+1] = upc[1] + new[nnew] = upc[1] end end --~ if trace_lookups then - --~ logs.report("define otf","lookup %s: alternate %s => %s",lookup,old,concat(new,"|")) + --~ report_prepare("lookup %s: alternate %s => %s",lookup,old,concat(new,"|")) --~ end end, ligature = function (p,lookup,glyph,unicode) --~ if trace_lookups then - --~ logs.report("define otf","lookup %s: ligature %s => %s",lookup,p[2],glyph.name) + --~ report_prepare("lookup %s: ligature %s => %s",lookup,p[2],glyph.name) --~ end local first = true local t = ligature[lookup] @@ -2365,7 +2386,7 @@ local function prepare_lookups(tfmdata) if first then local u = unicodes[s] if not u then - logs.report("define otf","lookup %s: ligature %s => %s ignored due to invalid unicode",lookup,p[2],glyph.name) + report_prepare("lookup %s: ligature %s => %s ignored due to invalid unicode",lookup,p[2],glyph.name) break elseif type(u) == "number" then if not t[u] then @@ -2435,7 +2456,7 @@ local function prepare_lookups(tfmdata) end end --~ if trace_lookups then - --~ logs.report("define otf","lookup %s: pair for U+%04X",lookup,unicode) + --~ report_prepare("lookup %s: pair for U+%04X",lookup,unicode) --~ end end, } @@ -2456,14 +2477,14 @@ local function prepare_lookups(tfmdata) end end end - local list = glyph.mykerns + local list = glyph.kerns if list then for lookup, krn in next, list do local k = kerns[lookup] if not k then k = { } kerns[lookup] = k end k[unicode] = krn -- ref to glyph, saves lookup --~ if trace_lookups then - --~ logs.report("define otf","lookup %s: kern for U+%04X",lookup,unicode) + --~ report_prepare("lookup %s: kern for U+%04X",lookup,unicode) --~ end end end @@ -2479,7 +2500,7 @@ local function prepare_lookups(tfmdata) if not f then f = { } mark[lookup] = f end f[unicode] = anchors -- ref to glyph, saves lookup --~ if trace_lookups then - --~ logs.report("define otf","lookup %s: mark anchor %s for U+%04X",lookup,name,unicode) + --~ report_prepare("lookup %s: mark anchor %s for U+%04X",lookup,name,unicode) --~ end end end @@ -2493,7 +2514,7 @@ local function prepare_lookups(tfmdata) if not f then f = { } cursive[lookup] = f end f[unicode] = anchors -- ref to glyph, saves lookup --~ if trace_lookups then - --~ logs.report("define otf","lookup %s: exit anchor %s for U+%04X",lookup,name,unicode) + --~ report_prepare("lookup %s: exit anchor %s for U+%04X",lookup,name,unicode) --~ end end end @@ -2526,7 +2547,7 @@ local function prepare_contextchains(tfmdata) for lookupname, lookupdata in next, otfdata.lookups do local lookuptype = lookupdata.type if not lookuptype then - logs.report("otf process","missing lookuptype for %s",lookupname) + report_prepare("missing lookuptype for %s",lookupname) else local rules = lookupdata.rules if rules then @@ -2534,14 +2555,14 @@ local function prepare_contextchains(tfmdata) -- contextchain[lookupname][unicode] if fmt == "coverage" then if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then - logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname) + report_prepare("unsupported coverage %s for %s",lookuptype,lookupname) else local contexts = contextchain[lookupname] if not contexts then contexts = { } contextchain[lookupname] = contexts end - local t = { } + local t, nt = { }, 0 for nofrules=1,#rules do -- does #rules>1 happen often? local rule = rules[nofrules] local coverage = rule.coverage @@ -2557,7 +2578,8 @@ local function prepare_contextchains(tfmdata) uncover(after,sequence,cache,unicodes) end if sequence[1] then - t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } for unic, _ in next, sequence[start] do local cu = contexts[unic] if not cu then @@ -2570,14 +2592,14 @@ local function prepare_contextchains(tfmdata) end elseif fmt == "reversecoverage" then if lookuptype ~= "reversesub" then - logs.report("otf process","unsupported reverse coverage %s for %s",lookuptype,lookupname) + report_prepare("unsupported reverse coverage %s for %s",lookuptype,lookupname) else local contexts = reversecontextchain[lookupname] if not contexts then contexts = { } reversecontextchain[lookupname] = contexts end - local t = { } + local t, nt = { }, 0 for nofrules=1,#rules do local rule = rules[nofrules] local reversecoverage = rule.reversecoverage @@ -2597,7 +2619,8 @@ local function prepare_contextchains(tfmdata) end if sequence[1] then -- this is different from normal coverage, we assume only replacements - t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } for unic, _ in next, sequence[start] do local cu = contexts[unic] if not cu then @@ -2610,14 +2633,14 @@ local function prepare_contextchains(tfmdata) end elseif fmt == "glyphs" then if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then - logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname) + report_prepare("unsupported coverage %s for %s",lookuptype,lookupname) else local contexts = contextchain[lookupname] if not contexts then contexts = { } contextchain[lookupname] = contexts end - local t = { } + local t, nt = { }, 0 for nofrules=1,#rules do -- nearly the same as coverage so we could as well rename it local rule = rules[nofrules] @@ -2637,7 +2660,8 @@ local function prepare_contextchains(tfmdata) uncover(back,sequence,cache,unicodes) end if sequence[1] then - t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } for unic, _ in next, sequence[start] do local cu = contexts[unic] if not cu then @@ -2681,7 +2705,7 @@ function fonts.initializers.node.otf.features(tfmdata,value) prepare_lookups(tfmdata) otfdata.shared.initialized = true if trace_preparing then - logs.report("otf process","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") + report_prepare("preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") end end end diff --git a/otfl-font-ott.lua b/otfl-font-ott.lua index c56e984..ec915b8 100644 --- a/otfl-font-ott.lua +++ b/otfl-font-ott.lua @@ -7,17 +7,23 @@ if not modules then modules = { } end modules ['font-otf'] = { } local type, next, tonumber, tostring = type, next, tonumber, tostring -local gsub, lower = string.gsub, string.lower +local gsub, lower, format = string.gsub, string.lower, string.format +local is_boolean = string.is_boolean -fonts = fonts or { } -fonts.otf = fonts.otf or { } +local allocate = utilities.storage.allocate -local otf = fonts.otf +fonts = fonts or { } -- needed for font server +local fonts = fonts +fonts.otf = fonts.otf or { } +local otf = fonts.otf -otf.tables = otf.tables or { } -otf.meanings = otf.meanings or { } +otf.tables = otf.tables or { } +local tables = otf.tables -otf.tables.scripts = { +otf.meanings = otf.meanings or { } +local meanings = otf.meanings + +local scripts = allocate { ['dflt'] = 'Default', ['arab'] = 'Arabic', @@ -90,7 +96,7 @@ otf.tables.scripts = { ['yi' ] = 'Yi', } -otf.tables.languages = { +local languages = allocate { ['dflt'] = 'Default', ['aba'] = 'Abaza', @@ -484,7 +490,7 @@ otf.tables.languages = { ['zul'] = 'Zulu' } -otf.tables.features = { +local features = allocate { ['aalt'] = 'Access All Alternates', ['abvf'] = 'Above-Base Forms', ['abvm'] = 'Above-Base Mark Positioning', @@ -622,7 +628,7 @@ otf.tables.features = { ['tlig'] = 'Traditional TeX Ligatures', } -otf.tables.baselines = { +local baselines = allocate { ['hang'] = 'Hanging baseline', ['icfb'] = 'Ideographic character face bottom edge baseline', ['icft'] = 'Ideographic character face tope edige baseline', @@ -632,10 +638,36 @@ otf.tables.baselines = { ['romn'] = 'Roman baseline' } --- can be sped up by local tables -function otf.tables.to_tag(id) - return stringformat("%4s",lower(id)) +local function swap(h) -- can be a tables.swap when we get a better name + local r = { } + for k, v in next, h do + r[v] = lower(gsub(k," ","")) + end + return r +end + +local verbosescripts = allocate(swap(scripts )) +local verboselanguages = allocate(swap(languages)) +local verbosefeatures = allocate(swap(features )) + +tables.scripts = scripts +tables.languages = languages +tables.features = features +tables.baselines = baselines + +tables.verbosescripts = verbosescripts +tables.verboselanguages = verboselanguages +tables.verbosefeatures = verbosefeatures + +for k, v in next, verbosefeatures do + local stripped = gsub(k,"%-"," ") + verbosefeatures[stripped] = v + local stripped = gsub(k,"[^a-zA-Z0-9]","") + verbosefeatures[stripped] = v +end +for k, v in next, verbosefeatures do + verbosefeatures[lower(k)] = v end local function resolve(tab,id) @@ -647,87 +679,59 @@ local function resolve(tab,id) end end -function otf.meanings.script(id) - return resolve(otf.tables.scripts,id) -end -function otf.meanings.language(id) - return resolve(otf.tables.languages,id) -end -function otf.meanings.feature(id) - return resolve(otf.tables.features,id) -end -function otf.meanings.baseline(id) - return resolve(otf.tables.baselines,id) -end - -otf.tables.to_scripts = table.reverse_hash(otf.tables.scripts ) -otf.tables.to_languages = table.reverse_hash(otf.tables.languages) -otf.tables.to_features = table.reverse_hash(otf.tables.features ) +function meanings.script (id) return resolve(scripts, id) end +function meanings.language(id) return resolve(languages,id) end +function meanings.feature (id) return resolve(features, id) end +function meanings.baseline(id) return resolve(baselines,id) end -local scripts = otf.tables.scripts -local languages = otf.tables.languages -local features = otf.tables.features - -local to_scripts = otf.tables.to_scripts -local to_languages = otf.tables.to_languages -local to_features = otf.tables.to_features - -for k, v in next, to_features do - local stripped = gsub(k,"%-"," ") - to_features[stripped] = v - local stripped = gsub(k,"[^a-zA-Z0-9]","") - to_features[stripped] = v -end -for k, v in next, to_features do - to_features[lower(k)] = v -end - -otf.meanings.checkers = { +local checkers = { rand = function(v) return v and "random" end } -local checkers = otf.meanings.checkers +meanings.checkers = checkers -function otf.meanings.normalize(features) - local h = { } - for k,v in next, features do - k = lower(k) - if k == "language" or k == "lang" then - v = gsub(lower(v),"[^a-z0-9%-]","") - if not languages[v] then - h.language = to_languages[v] or "dflt" - else - h.language = v - end - elseif k == "script" then - v = gsub(lower(v),"[^a-z0-9%-]","") - if not scripts[v] then - h.script = to_scripts[v] or "dflt" - else - h.script = v - end - else - if type(v) == "string" then - local b = v:is_boolean() - if type(b) == "nil" then - v = tonumber(v) or lower(v) +function meanings.normalize(features) + if features then + local h = { } + for k,v in next, features do + k = lower(k) + if k == "language" or k == "lang" then + v = gsub(lower(v),"[^a-z0-9%-]","") + if not languages[v] then + h.language = verboselanguages[v] or "dflt" else - v = b + h.language = v + end + elseif k == "script" then + v = gsub(lower(v),"[^a-z0-9%-]","") + if not scripts[v] then + h.script = verbosescripts[v] or "dflt" + else + h.script = v + end + 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 + k = verbosefeatures[k] or k + local c = checkers[k] + h[k] = c and c(v) or v end - k = to_features[k] or k - local c = checkers[k] - h[k] = c and c(v) or v end + return h end - return h end -- When I feel the need ... ---~ otf.tables.aat = { +--~ tables.aat = { --~ [ 0] = { --~ name = "allTypographicFeaturesType", --~ [ 0] = "allTypeFeaturesOnSelector", diff --git a/otfl-font-tfm.lua b/otfl-font-tfm.lua index 560ba1c..5e841b2 100644 --- a/otfl-font-tfm.lua +++ b/otfl-font-tfm.lua @@ -11,9 +11,13 @@ local utf = unicode.utf8 local next, format, match, lower, gsub = next, string.format, string.match, string.lower, string.gsub local concat, sortedkeys, utfbyte, serialize = table.concat, table.sortedkeys, utf.byte, table.serialize +local allocate = utilities.storage.allocate + 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") + -- tfmdata has also fast access to indices and unicodes -- to be checked: otf -> tfm -> tfmscaled -- @@ -23,49 +27,49 @@ local trace_scaling = false trackers.register("fonts.scaling" , function(v) tr <p>Here we only implement a few helper functions.</p> --ldx]]-- -fonts = fonts or { } -fonts.tfm = fonts.tfm or { } -fonts.ids = fonts.ids or { } +local fonts = fonts +local tfm = fonts.tfm -local tfm = fonts.tfm - -fonts.loaded = fonts.loaded or { } -fonts.dontembed = fonts.dontembed or { } -fonts.triggers = fonts.triggers or { } -- brrr -fonts.initializers = fonts.initializers or { } +fonts.loaded = allocate() +fonts.dontembed = allocate() +fonts.triggers = fonts.triggers or { } -- brrr +fonts.initializers = fonts.initializers or { } fonts.initializers.common = fonts.initializers.common or { } -local fontdata = fonts.ids -local disc = node.id('disc') -local glyph = node.id('glyph') local set_attribute = node.set_attribute +local findbinfile = resolvers.findbinfile + +local readers = fonts.tfm.readers +local fontdata = fonts.identifiers +local nodecodes = nodes.nodecodes + +local disc_code = nodecodes.disc +local glyph_code = nodecodes.glyph --[[ldx-- <p>The next function encapsulates the standard <l n='tfm'/> loader as supplied by <l n='luatex'/>.</p> --ldx]]-- -tfm.resolve_vf = true -- false -tfm.share_base_kerns = false -- true (.5 sec slower on mk but brings down mem from 410M to 310M, beware: then script/lang share too) -tfm.mathactions = { } -tfm.fontname_mode = "fullpath" +tfm.resolvevirtualtoo = true -- false +tfm.sharebasekerns = false -- true (.5 sec slower on mk but brings down mem from 410M to 310M, beware: then script/lang share too) +tfm.mathactions = { } +tfm.fontnamemode = "fullpath" tfm.enhance = tfm.enhance or function() end -fonts.formats.tfm = "type1" -- we need to have at least a value here - -function tfm.read_from_tfm(specification) +local function read_from_tfm(specification) local fname, tfmdata = specification.filename or "", nil if fname ~= "" then if trace_defining then - logs.report("define font","loading tfm file %s at size %s",fname,specification.size) + report_defining("loading tfm file %s at size %s",fname,specification.size) end tfmdata = font.read_tfm(fname,specification.size) -- not cached, fast enough if tfmdata then tfmdata.descriptions = tfmdata.descriptions or { } - if tfm.resolve_vf then + if tfm.resolvevirtualtoo then fonts.logger.save(tfmdata,file.extname(fname),specification) -- strange, why here - fname = resolvers.findbinfile(specification.name, 'ovf') + fname = findbinfile(specification.name, 'ovf') if fname and fname ~= "" then local vfdata = font.read_vf(fname,specification.size) -- not cached, fast enough if vfdata then @@ -81,7 +85,7 @@ function tfm.read_from_tfm(specification) tfm.enhance(tfmdata,specification) end elseif trace_defining then - logs.report("define font","loading tfm with name %s fails",specification.name) + report_defining("loading tfm with name %s fails",specification.name) end return tfmdata end @@ -124,19 +128,34 @@ end to scale virtual characters.</p> --ldx]]-- -function tfm.get_virtual_id(tfmdata) +--~ function tfm.getvirtualid(tfmdata) +--~ -- since we don't know the id yet, we use 0 as signal +--~ local tf = tfmdata.fonts +--~ if not tf then +--~ tfmdata.type = "virtual" +--~ tfmdata.fonts = { { id = 0 } } +--~ return 1 +--~ else +--~ local ntf = #tf + 1 +--~ tf[ntf] = { id = 0 } +--~ return ntf +--~ end +--~ end + +function tfm.getvirtualid(tfmdata) -- since we don't know the id yet, we use 0 as signal - if not tfmdata.fonts then + local tf = tfmdata.fonts + if not tf then + tf = { } tfmdata.type = "virtual" - tfmdata.fonts = { { id = 0 } } - return 1 - else - tfmdata.fonts[#tfmdata.fonts+1] = { id = 0 } - return #tfmdata.fonts + tfmdata.fonts = tf end + local ntf = #tf + 1 + tf[ntf] = { id = 0 } + return ntf end -function tfm.check_virtual_id(tfmdata, id) +function tfm.checkvirtualid(tfmdata, id) if tfmdata and tfmdata.type == "virtual" then if not tfmdata.fonts or #tfmdata.fonts == 0 then tfmdata.type, tfmdata.fonts = "real", nil @@ -166,7 +185,7 @@ fonts.trace_scaling = false -- sharedkerns are unscaled and are be hashed by concatenated indexes --~ function tfm.check_base_kerns(tfmdata) ---~ if tfm.share_base_kerns then +--~ if tfm.sharebasekerns then --~ local sharedkerns = tfmdata.sharedkerns --~ if sharedkerns then --~ local basekerns = { } @@ -178,7 +197,7 @@ fonts.trace_scaling = false --~ end --~ function tfm.prepare_base_kerns(tfmdata) ---~ if tfm.share_base_kerns and not tfmdata.sharedkerns then +--~ if tfm.sharebasekerns and not tfmdata.sharedkerns then --~ local sharedkerns = { } --~ tfmdata.sharedkerns = sharedkerns --~ for u, chr in next, tfmdata.characters do @@ -207,7 +226,47 @@ local charactercache = { } -- a virtual font has italic correction make sure to set the -- has_italic flag. Some more flags will be added in the future. -function tfm.calculate_scale(tfmtable, scaledpoints, relativeid) +--[[ldx-- +<p>The reason why the scaler was originally split, is that for a while we experimented +with a helper function. However, in practice the <l n='api'/> calls are too slow to +make this profitable and the <l n='lua'/> based variant was just faster. A days +wasted day but an experience richer.</p> +--ldx]]-- + +tfm.autocleanup = true + +local lastfont = nil + +-- we can get rid of the tfm instance when we have fast access to the +-- scaled character dimensions at the tex end, e.g. a fontobject.width +-- +-- flushing the kern and ligature tables from memory saves a lot (only +-- base mode) but it complicates vf building where the new characters +-- demand this data .. solution: functions that access them + +-- we don't need the glyph data as we can use the description .. but we will +-- have to wait till we can access the internal tfm table efficiently in which +-- case characters will become a metatable afterwards + +function tfm.cleanuptable(tfmdata) -- we need a cleanup callback, now we miss the last one + if tfm.autocleanup then -- ok, we can hook this into everyshipout or so ... todo + if tfmdata.type == 'virtual' or tfmdata.virtualized then + for k, v in next, tfmdata.characters do + if v.commands then v.commands = nil end + -- if v.kerns then v.kerns = nil end + end + else + -- for k, v in next, tfmdata.characters do + -- if v.kerns then v.kerns = nil end + -- end + end + end +end + +function tfm.cleanup(tfmdata) -- we need a cleanup callback, now we miss the last one +end + +function tfm.calculatescale(tfmtable, scaledpoints) if scaledpoints < 0 then scaledpoints = (- scaledpoints/1000) * tfmtable.designsize -- already in sp end @@ -216,11 +275,13 @@ function tfm.calculate_scale(tfmtable, scaledpoints, relativeid) return scaledpoints, delta, units end -function tfm.do_scale(tfmtable, scaledpoints, relativeid) +function tfm.scale(tfmtable, scaledpoints, relativeid) -- tfm.prepare_base_kerns(tfmtable) -- optimalization local t = { } -- the new table - local scaledpoints, delta, units = tfm.calculate_scale(tfmtable, scaledpoints, relativeid) + local scaledpoints, delta, units = tfm.calculatescale(tfmtable, scaledpoints, relativeid) + -- is just a trigger for the backend t.units_per_em = units or 1000 + -- local hdelta, vdelta = delta, delta -- unicoded unique descriptions shared cidinfo characters changed parameters indices for k,v in next, tfmtable do @@ -245,28 +306,36 @@ function tfm.do_scale(tfmtable, scaledpoints, relativeid) end -- status local isvirtual = tfmtable.type == "virtual" or tfmtable.virtualized - local hasmath = (tfmtable.math_parameters ~= nil and next(tfmtable.math_parameters) ~= nil) or (tfmtable.MathConstants ~= nil and next(tfmtable.MathConstants) ~= nil) + local hasmath = (tfmtable.mathparameters ~= nil and next(tfmtable.mathparameters) ~= nil) or (tfmtable.MathConstants ~= nil and next(tfmtable.MathConstants) ~= nil) local nodemode = tfmtable.mode == "node" local hasquality = tfmtable.auto_expand or tfmtable.auto_protrude local hasitalic = tfmtable.has_italic + local descriptions = tfmtable.descriptions or { } + -- + if hasmath then + t.has_math = true -- this will move to elsewhere + end -- t.parameters = { } t.characters = { } t.MathConstants = { } -- fast access - local descriptions = tfmtable.descriptions or { } + t.unscaled = tfmtable -- the original unscaled one (temp) t.unicodes = tfmtable.unicodes t.indices = tfmtable.indices t.marks = tfmtable.marks -t.goodies = tfmtable.goodies -t.colorscheme = tfmtable.colorscheme ---~ t.embedding = tfmtable.embedding + -- this will move to some subtable so that it is copied at once + t.goodies = tfmtable.goodies + t.colorscheme = tfmtable.colorscheme + t.postprocessors = tfmtable.postprocessors + -- + -- t.embedding = tfmtable.embedding t.descriptions = descriptions if tfmtable.fonts then t.fonts = table.fastcopy(tfmtable.fonts) -- hm also at the end end local tp = t.parameters - local mp = t.math_parameters + local mp = t.mathparameters local tfmp = tfmtable.parameters -- let's check for indexes -- tp.slant = (tfmp.slant or tfmp[1] or 0) @@ -296,7 +365,7 @@ t.colorscheme = tfmtable.colorscheme local scaledheight = defaultheight * vdelta local scaleddepth = defaultdepth * vdelta local stackmath = tfmtable.ignore_stack_math ~= true - local private = fonts.private + local private = fonts.privateoffset local sharedkerns = { } for k,v in next, characters do local chr, description, index @@ -357,7 +426,7 @@ t.colorscheme = tfmtable.colorscheme end end -- if trace_scaling then - -- logs.report("define font","t=%s, u=%s, i=%s, n=%s c=%s",k,chr.tounicode or k,description.index,description.name or '-',description.class or '-') + -- report_defining("t=%s, u=%s, i=%s, n=%s c=%s",k,chr.tounicode or "",index or 0,description.name or '-',description.class or '-') -- end if tounicode then local tu = tounicode[index] -- nb: index! @@ -394,7 +463,7 @@ t.colorscheme = tfmtable.colorscheme if vn then chr.next = vn --~ if v.vert_variants or v.horiz_variants then - --~ logs.report("glyph 0x%05X has combination of next, vert_variants and horiz_variants",index) + --~ report_defining("glyph 0x%05X has combination of next, vert_variants and horiz_variants",index) --~ end else local vv = v.vert_variants @@ -515,19 +584,20 @@ t.colorscheme = tfmtable.colorscheme local ivc = vc[i] local key = ivc[1] if key == "right" then - tt[#tt+1] = { key, ivc[2]*hdelta } + tt[i] = { key, ivc[2]*hdelta } elseif key == "down" then - tt[#tt+1] = { key, ivc[2]*vdelta } + tt[i] = { key, ivc[2]*vdelta } elseif key == "rule" then - tt[#tt+1] = { key, ivc[2]*vdelta, ivc[3]*hdelta } + tt[i] = { key, ivc[2]*vdelta, ivc[3]*hdelta } else -- not comment - tt[#tt+1] = ivc -- shared since in cache and untouched + tt[i] = ivc -- shared since in cache and untouched end end chr.commands = tt else chr.commands = vc end + chr.index = nil end end tc[k] = chr @@ -565,11 +635,11 @@ t.colorscheme = tfmtable.colorscheme -- can have multiple subfonts if hasmath then if trace_defining then - logs.report("define font","math enabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") + report_defining("math enabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") end else if trace_defining then - logs.report("define font","math disabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") + report_defining("math disabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") end t.nomath, t.MathConstants = true, nil end @@ -578,58 +648,17 @@ t.colorscheme = tfmtable.colorscheme t.psname = t.fontname or (t.fullname and fonts.names.cleanname(t.fullname)) end if trace_defining then - logs.report("define font","used for accesing subfont: '%s'",t.psname or "nopsname") - logs.report("define font","used for subsetting: '%s'",t.fontname or "nofontname") + report_defining("used for accessing (sub)font: '%s'",t.psname or "nopsname") + report_defining("used for subsetting: '%s'",t.fontname or "nofontname") end ---~ print(t.fontname,table.serialize(t.MathConstants)) - return t, delta -end - ---[[ldx-- -<p>The reason why the scaler is split, is that for a while we experimented -with a helper function. However, in practice the <l n='api'/> calls are too slow to -make this profitable and the <l n='lua'/> based variant was just faster. A days -wasted day but an experience richer.</p> ---ldx]]-- - -tfm.auto_cleanup = true - -local lastfont = nil - --- we can get rid of the tfm instance when we have fast access to the --- scaled character dimensions at the tex end, e.g. a fontobject.width --- --- flushing the kern and ligature tables from memory saves a lot (only --- base mode) but it complicates vf building where the new characters --- demand this data .. solution: functions that access them - -function tfm.cleanup_table(tfmdata) -- we need a cleanup callback, now we miss the last one - if tfm.auto_cleanup then -- ok, we can hook this into everyshipout or so ... todo - if tfmdata.type == 'virtual' or tfmdata.virtualized then - for k, v in next, tfmdata.characters do - if v.commands then v.commands = nil end - -- if v.kerns then v.kerns = nil end - end - else - -- for k, v in next, tfmdata.characters do - -- if v.kerns then v.kerns = nil end - -- end - end - end -end - -function tfm.cleanup(tfmdata) -- we need a cleanup callback, now we miss the last one -end - -function tfm.scale(tfmtable, scaledpoints, relativeid) - local t, factor = tfm.do_scale(tfmtable, scaledpoints, relativeid) - t.factor = factor - t.ascender = factor*(tfmtable.ascender or 0) - t.descender = factor*(tfmtable.descender or 0) + -- this will move up (side effect of merging split call) + t.factor = delta + t.ascender = delta*(tfmtable.ascender or 0) + t.descender = delta*(tfmtable.descender or 0) t.shared = tfmtable.shared or { } t.unique = table.fastcopy(tfmtable.unique or {}) ---~ print("scaling", t.name, t.factor) -- , tfm.hash_features(tfmtable.specification)) tfm.cleanup(t) + -- print(t.fontname,table.serialize(t.MathConstants)) return t end @@ -638,10 +667,12 @@ end process features right.</p> --ldx]]-- -fonts.analyzers = fonts.analyzers or { } -fonts.analyzers.aux = fonts.analyzers.aux or { } -fonts.analyzers.methods = fonts.analyzers.methods or { } -fonts.analyzers.initializers = fonts.analyzers.initializers or { } +fonts.analyzers = fonts.analyzers or { } +local analyzers = fonts.analyzers + +analyzers.aux = analyzers.aux or { } +analyzers.methods = analyzers.methods or { } +analyzers.initializers = analyzers.initializers or { } -- todo: analyzers per script/lang, cross font, so we need an font id hash -> script -- e.g. latin -> hyphenate, arab -> 1/2/3 analyze @@ -650,17 +681,19 @@ fonts.analyzers.initializers = fonts.analyzers.initializers or { } local state = attributes.private('state') -function fonts.analyzers.aux.setstate(head,font) +function analyzers.aux.setstate(head,font) + local useunicodemarks = analyzers.useunicodemarks local tfmdata = fontdata[font] local characters = tfmdata.characters local descriptions = tfmdata.descriptions local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean while current do local id = current.id - if id == glyph and current.font == font then - local d = descriptions[current.char] + if id == glyph_code and current.font == font then + local char = current.char + local d = descriptions[char] if d then - if d.class == "mark" then + if d.class == "mark" or (useunicodemarks and categories[char] == "mn") then done = true set_attribute(current,state,5) -- mark elseif n == 0 then @@ -678,7 +711,7 @@ function fonts.analyzers.aux.setstate(head,font) end first, last, n = nil, nil, 0 end - elseif id == disc then + elseif id == disc_code then -- always in the middle set_attribute(current,state,2) -- midi last = current @@ -711,25 +744,26 @@ end -- checking -function tfm.checked_filename(metadata,whatever) +function tfm.checkedfilename(metadata,whatever) local foundfilename = metadata.foundfilename if not foundfilename then local askedfilename = metadata.filename or "" if askedfilename ~= "" then - foundfilename = resolvers.findbinfile(askedfilename,"") or "" + askedfilename = resolvers.resolve(askedfilename) -- no shortcut + foundfilename = findbinfile(askedfilename,"") or "" if foundfilename == "" then - logs.report("fonts","source file '%s' is not found",askedfilename) - foundfilename = resolvers.findbinfile(file.basename(askedfilename),"") or "" + report_defining("source file '%s' is not found",askedfilename) + foundfilename = findbinfile(file.basename(askedfilename),"") or "" if foundfilename ~= "" then - logs.report("fonts","using source file '%s' (cache mismatch)",foundfilename) + report_defining("using source file '%s' (cache mismatch)",foundfilename) end end elseif whatever then - logs.report("fonts","no source file for '%s'",whatever) + report_defining("no source file for '%s'",whatever) foundfilename = "" end metadata.foundfilename = foundfilename - -- logs.report("fonts","using source file '%s'",foundfilename) + -- report_defining("using source file '%s'",foundfilename) end return foundfilename end @@ -739,3 +773,41 @@ end statistics.register("fonts load time", function() return statistics.elapsedseconds(fonts) end) + +-- readers + +fonts.formats.tfm = "type1" -- we need to have at least a value here + +local function check_tfm(specification,fullname) + -- ofm directive blocks local path search unless set; btw, in context we + -- don't support ofm files anyway as this format is obsolete + local foundname = findbinfile(fullname, 'tfm') or "" -- just to be sure + if foundname == "" then + foundname = findbinfile(fullname, 'ofm') or "" -- bonus for usage outside context + end + if foundname == "" then + foundname = fonts.names.getfilename(fullname,"tfm") + end + if foundname ~= "" then + specification.filename, specification.format = foundname, "ofm" + return read_from_tfm(specification) + end +end + +readers.check_tfm = check_tfm + +function readers.tfm(specification) + local fullname, tfmtable = specification.filename or "", nil + if fullname == "" then + local forced = specification.forced or "" + if forced ~= "" then + tfmtable = check_tfm(specification,specification.name .. "." .. forced) + end + if not tfmtable then + tfmtable = check_tfm(specification,specification.name) + end + else + tfmtable = check_tfm(specification,fullname) + end + return tfmtable +end diff --git a/otfl-luat-dum.lua b/otfl-luat-dum.lua index 2f6627f..e0b6292 100644 --- a/otfl-luat-dum.lua +++ b/otfl-luat-dum.lua @@ -7,11 +7,13 @@ if not modules then modules = { } end modules ['luat-dum'] = { } local dummyfunction = function() end +local dummyreporter = function(c) return function(...) texio.write(c .. " : " .. string.format(...)) end end statistics = { register = dummyfunction, starttiming = dummyfunction, stoptiming = dummyfunction, + elapsedtime = nil, } directives = { register = dummyfunction, @@ -28,23 +30,29 @@ experiments = { enable = dummyfunction, disable = dummyfunction, } -storage = { +storage = { -- probably no longer needed register = dummyfunction, shared = { }, } logs = { + new = dummyreporter, + reporter = dummyreporter, + messenger = dummyreporter, report = dummyfunction, - simple = dummyfunction, -} -tasks = { - new = dummyfunction, - actions = dummyfunction, - appendaction = dummyfunction, - prependaction = dummyfunction, } callbacks = { register = function(n,f) return callback.register(n,f) end, } +utilities = { + storage = { + allocate = function(t) return t or { } end, + mark = function(t) return t or { } end, + }, +} + +characters = characters or { + data = { } +} -- we need to cheat a bit here @@ -56,22 +64,34 @@ local remapper = { otf = "opentype fonts", ttf = "truetype fonts", ttc = "truetype fonts", - dfont = "truetype fonts", + dfont = "truetype fonts", -- "truetype dictionary", cid = "cid maps", fea = "font feature files", } -function resolvers.find_file(name,kind) +function resolvers.findfile(name,kind) name = string.gsub(name,"\\","\/") - kind = string.lower(kind) - return kpse.find_file(name,(kind and kind ~= "" and (remapper[kind] or kind)) or file.extname(name,"tex")) + kind = kind and string.lower(kind) + local found = kpse.find_file(name,(kind and kind ~= "" and (remapper[kind] or kind)) or file.extname(name,"tex")) + if not found or found == "" then + found = kpse.find_file(name,"other text file") + end + return found end function resolvers.findbinfile(name,kind) if not kind or kind == "" then kind = file.extname(name) -- string.match(name,"%.([^%.]-)$") end - return resolvers.find_file(name,(kind and remapper[kind]) or kind) + return resolvers.findfile(name,(kind and remapper[kind]) or kind) +end + +function resolvers.resolve(s) + return s +end + +function resolvers.unresolve(s) + return s end -- Caches ... I will make a real stupid version some day when I'm in the @@ -92,41 +112,43 @@ end do - local cachepaths + local cachepaths = kpse.expand_path('$TEXMFCACHE') or "" + + if cachepaths == "" then + cachepaths = kpse.expand_path('$TEXMFVAR') + end - if kpse.expand_var('$TEXMFCACHE') ~= '$TEXMFCACHE' then - cachepaths = kpse.expand_var('$TEXMFCACHE') - elseif kpse.expand_var('$TEXMFVAR') ~= '$TEXMFVAR' then - cachepaths = kpse.expand_var('$TEXMFVAR') + if cachepaths == "" then + cachepaths = kpse.expand_path('$VARTEXMF') end - if not cachepaths then + if cachepaths == "" then cachepaths = "." end cachepaths = string.split(cachepaths,os.type == "windows" and ";" or ":") for i=1,#cachepaths do - local done - writable = file.join(cachepaths[i], "luatex-cache") - writable = file.join(writable,caches.namespace) - writable, done = dir.mkdirs(writable) - if done then + if file.is_writable(cachepaths[i]) then + writable = file.join(cachepaths[i],"luatex-cache") + lfs.mkdir(writable) + writable = file.join(writable,caches.namespace) + lfs.mkdir(writable) break end end for i=1,#cachepaths do - if file.isreadable(cachepaths[i]) then + if file.is_readable(cachepaths[i]) then readables[#readables+1] = file.join(cachepaths[i],"luatex-cache",caches.namespace) end end if not writable then - texio.write_nl("quiting: fix your writable cache path\n") + texio.write_nl("quiting: fix your writable cache path") os.exit() elseif #readables == 0 then - texio.write_nl("quiting: fix your readable cache path\n") + texio.write_nl("quiting: fix your readable cache path") os.exit() elseif #readables == 1 and readables[1] == writable then texio.write(string.format("(using cache: %s)",writable)) @@ -160,9 +182,9 @@ local function makefullname(path,name) end end -function caches.iswritable(path,name) +function caches.is_writable(path,name) local fullname = makefullname(path,name) - return fullname and file.iswritable(fullname) + return fullname and file.is_writable(fullname) end function caches.loaddata(paths,name) diff --git a/otfl-node-dum.lua b/otfl-node-dum.lua index 9483e51..5127481 100644 --- a/otfl-node-dum.lua +++ b/otfl-node-dum.lua @@ -10,22 +10,30 @@ nodes = nodes or { } fonts = fonts or { } attributes = attributes or { } +nodes.pool = nodes.pool or { } +nodes.handlers = nodes.handlers or { } + +local nodecodes = { } for k,v in next, node.types () do nodecodes[string.gsub(v,"_","")] = k end +local whatcodes = { } for k,v in next, node.whatsits() do whatcodes[string.gsub(v,"_","")] = k end +local glyphcodes = { [0] = "character", "glyph", "ligature", "ghost", "left", "right" } + +nodes.nodecodes = nodecodes +nodes.whatcodes = whatcodes +nodes.whatsitcodes = whatcodes +nodes.glyphcodes = glyphcodes + local traverse_id = node.traverse_id local free_node = node.free local remove_node = node.remove local new_node = node.new -local glyph = node.id('glyph') - --- fonts - -local fontdata = fonts.ids or { } +local glyph_code = nodecodes.glyph function nodes.simple_font_handler(head) -- lang.hyphenate(head) - head = nodes.process_characters(head) - nodes.inject_kerns(head) - nodes.protect_glyphs(head) + head = nodes.handlers.characters(head) + nodes.injections.handler(head) + nodes.handlers.protectglyphs(head) head = node.ligaturing(head) head = node.kerning(head) return head @@ -36,52 +44,57 @@ if tex.attribute[0] ~= 0 then texio.write_nl("log","!") texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be") texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special") - texio.write_nl("log","! purposed so setting them at the TeX end might break the font handler.") + texio.write_nl("log","! purposes so setting them at the TeX end might break the font handler.") texio.write_nl("log","!") tex.attribute[0] = 0 -- else no features end -nodes.protect_glyphs = node.protect_glyphs -nodes.unprotect_glyphs = node.unprotect_glyphs - -function nodes.process_characters(head) - local usedfonts, done, prevfont = { }, false, nil - for n in traverse_id(glyph,head) do - local font = n.font - if font ~= prevfont then - prevfont = font - local used = usedfonts[font] - if not used then - local tfmdata = fontdata[font] - if tfmdata then - local shared = tfmdata.shared -- 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 - done = true +nodes.handlers.protectglyphs = node.protect_glyphs +nodes.handlers.unprotectglyphs = node.unprotect_glyphs + +function nodes.handlers.characters(head) + local fontdata = fonts.identifiers + if fontdata then + local usedfonts, done, prevfont = { }, false, nil + for n in traverse_id(glyph_code,head) do + local font = n.font + if font ~= prevfont then + prevfont = font + local used = usedfonts[font] + if not used then + local tfmdata = fontdata[font] -- + if tfmdata then + local shared = tfmdata.shared -- 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 + done = true + end end end end end end - end - if done then - for font, processors in next, usedfonts do - for i=1,#processors do - local h, d = processors[i](head,font,0) - head, done = h or head, done or d + if done then + for font, processors in next, usedfonts do + for i=1,#processors do + local h, d = processors[i](head,font,0) + head, done = h or head, done or d + end end end + return head, true + else + return head, false end - return head, true end -- helper -function nodes.kern(k) +function nodes.pool.kern(k) local n = new_node("kern",1) n.kern = k return n diff --git a/otfl-node-inj.lua b/otfl-node-inj.lua index fdea7f1..bf6a609 100644 --- a/otfl-node-inj.lua +++ b/otfl-node-inj.lua @@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['node-inj'] = { license = "see context related readme files" } --- tricky ... fonts.ids is not yet defined .. to be solved (maybe general tex ini) +-- tricky ... fonts.identifiers is not yet defined .. to be solved (maybe general tex ini) -- This is very experimental (this will change when we have luatex > .50 and -- a few pending thingies are available. Also, Idris needs to make a few more @@ -17,14 +17,22 @@ local next = next local trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end) -fonts = fonts or { } -fonts.tfm = fonts.tfm or { } -fonts.ids = fonts.ids or { } +local report_injections = logs.reporter("nodes","injections") -local fontdata = fonts.ids +local attributes, nodes, node = attributes, nodes, node -local glyph = node.id('glyph') -local kern = node.id('kern') +fonts = fonts or { } +fonts.tfm = fonts.tfm or { } +fonts.identifiers = fonts.identifiers or { } + +nodes.injections = nodes.injections or { } +local injections = nodes.injections + +local fontdata = fonts.identifiers +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph +local nodepool = nodes.pool +local newkern = nodepool.kern local traverse_id = node.traverse_id local unset_attribute = node.unset_attribute @@ -33,8 +41,6 @@ local set_attribute = node.set_attribute local insert_node_before = node.insert_before local insert_node_after = node.insert_after -local newkern = nodes.kern - local markbase = attributes.private('markbase') local markmark = attributes.private('markmark') local markdone = attributes.private('markdone') @@ -54,7 +60,7 @@ local kerns = { } -- for the moment we pass the r2l key ... volt/arabtype tests -function nodes.set_cursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) +function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2]) local ws, wn = tfmstart.width, tfmnext.width local bound = #cursives + 1 @@ -64,7 +70,7 @@ function nodes.set_cursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) return dx, dy, bound end -function nodes.set_pair(current,factor,rlmode,r2lflag,spec,tfmchr) +function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr) local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4] -- dy = y - h if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then @@ -83,7 +89,7 @@ function nodes.set_pair(current,factor,rlmode,r2lflag,spec,tfmchr) return x, y, w, h -- no bound end -function nodes.set_kern(current,factor,rlmode,x,tfmchr) +function injections.setkern(current,factor,rlmode,x,tfmchr) local dx = factor*x if dx ~= 0 then local bound = #kerns + 1 @@ -95,7 +101,7 @@ function nodes.set_kern(current,factor,rlmode,x,tfmchr) end end -function nodes.set_mark(start,base,factor,rlmode,ba,ma,index) --ba=baseanchor, ma=markanchor +function injections.setmark(start,base,factor,rlmode,ba,ma,index) --ba=baseanchor, ma=markanchor local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) local bound = has_attribute(base,markbase) if bound then @@ -107,7 +113,7 @@ function nodes.set_mark(start,base,factor,rlmode,ba,ma,index) --ba=baseanchor, m set_attribute(start,markdone,index) return dx, dy, bound else - logs.report("nodes mark", "possible problem, U+%04X is base without data (id: %s)",base.char,bound) + report_injections("possible problem, U+%04X is base mark without data (id: %s)",base.char,bound) end end index = index or 1 @@ -119,15 +125,13 @@ function nodes.set_mark(start,base,factor,rlmode,ba,ma,index) --ba=baseanchor, m return dx, dy, bound end -function nodes.trace_injection(head) - local function dir(n) - return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or ("unset") - end - local function report(...) - logs.report("nodes finisher",...) - end - report("begin run") - for n in traverse_id(glyph,head) do +local function dir(n) + return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset" +end + +local function trace(head) + report_injections("begin run") + for n in traverse_id(glyph_code,head) do if n.subtype < 256 then local kp = has_attribute(n,kernpair) local mb = has_attribute(n,markbase) @@ -135,61 +139,62 @@ function nodes.trace_injection(head) local md = has_attribute(n,markdone) local cb = has_attribute(n,cursbase) local cc = has_attribute(n,curscurs) - report("char U+%05X, font=%s",n.char,n.font) + report_injections("char U+%05X, font=%s",n.char,n.font) if kp then local k = kerns[kp] if k[3] then - report(" pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2] or "?",k[3] or "?",k[4] or "?",k[5] or "?") + report_injections(" pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2] or "?",k[3] or "?",k[4] or "?",k[5] or "?") else - report(" kern: dir=%s, dx=%s",dir(k[1]),k[2] or "?") + report_injections(" kern: dir=%s, dx=%s",dir(k[1]),k[2] or "?") end end if mb then - report(" markbase: bound=%s",mb) + report_injections(" markbase: bound=%s",mb) end if mm then local m = marks[mm] if mb then local m = m[mb] if m then - report(" markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,md or "?",m[1] or "?",m[2] or "?") + report_injections(" markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,md or "?",m[1] or "?",m[2] or "?") else - report(" markmark: bound=%s, missing index",mm) + report_injections(" markmark: bound=%s, missing index",mm) end else m = m[1] - report(" markmark: bound=%s, dx=%s, dy=%s",mm,m[1] or "?",m[2] or "?") + report_injections(" markmark: bound=%s, dx=%s, dy=%s",mm,m[1] or "?",m[2] or "?") end end if cb then - report(" cursbase: bound=%s",cb) + report_injections(" cursbase: bound=%s",cb) end if cc then local c = cursives[cc] - report(" curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2] or "?",c[3] or "?") + report_injections(" curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2] or "?",c[3] or "?") end end end - report("end run") + report_injections("end run") end -- todo: reuse tables (i.e. no collection), but will be extra fields anyway -- todo: check for attribute -function nodes.inject_kerns(head,where,keep) +function injections.handler(head,where,keep) local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns) if has_marks or has_cursives then --~ if has_marks or has_cursives or has_kerns then if trace_injections then - nodes.trace_injection(head) + trace(head) end -- in the future variant we will not copy items but refs to tables - local done, ky, rl, valid, cx, wx, mk = false, { }, { }, { }, { }, { }, { } + local done, ky, rl, valid, cx, wx, mk, nofvalid = false, { }, { }, { }, { }, { }, { }, 0 if has_kerns then -- move outside loop local nf, tm = nil, nil - for n in traverse_id(glyph,head) do + for n in traverse_id(glyph_code,head) do if n.subtype < 256 then - valid[#valid+1] = n + nofvalid = nofvalid + 1 + valid[nofvalid] = n if n.font ~= nf then nf = n.font tm = fontdata[nf].marks @@ -215,9 +220,10 @@ function nodes.inject_kerns(head,where,keep) end else local nf, tm = nil, nil - for n in traverse_id(glyph,head) do + for n in traverse_id(glyph_code,head) do if n.subtype < 256 then - valid[#valid+1] = n + nofvalid = nofvalid + 1 + valid[nofvalid] = n if n.font ~= nf then nf = n.font tm = fontdata[nf].marks @@ -226,7 +232,7 @@ function nodes.inject_kerns(head,where,keep) end end end - if #valid > 0 then + if nofvalid > 0 then -- we can assume done == true because we have cursives and marks local cx = { } if has_kerns and next(ky) then @@ -239,7 +245,7 @@ function nodes.inject_kerns(head,where,keep) local p_cursbase, p = nil, nil -- since we need valid[n+1] we can also use a "while true do" local t, d, maxt = { }, { }, 0 - for i=1,#valid do -- valid == glyphs + for i=1,nofvalid do -- valid == glyphs local n = valid[i] if not mk[n] then local n_cursbase = has_attribute(n,cursbase) @@ -303,12 +309,12 @@ function nodes.inject_kerns(head,where,keep) end end if has_marks then - for i=1,#valid do + for i=1,nofvalid do local p = valid[i] local p_markbase = has_attribute(p,markbase) if p_markbase then local mrks = marks[p_markbase] - for n in traverse_id(glyph,p.next) do + for n in traverse_id(glyph_code,p.next) do local n_markmark = has_attribute(n,markmark) if p_markbase == n_markmark then local index = has_attribute(n,markdone) or 1 @@ -391,9 +397,9 @@ function nodes.inject_kerns(head,where,keep) end elseif has_kerns then if trace_injections then - nodes.trace_injection(head) + trace(head) end - for n in traverse_id(glyph,head) do + for n in traverse_id(glyph_code,head) do if n.subtype < 256 then local k = has_attribute(n,kernpair) if k then @@ -0,0 +1,9 @@ +#!/bin/bash +CTX_TEXFM=$1 +CTX_FILES=`grep -o "loadmodule('....-....lua')" $CTX_TEXFM/tex/generic/context/luatex-fonts.lua |\ + sed -e "s/loadmodule('\(....-....lua\)')/\1/" | sort | uniq` + +echo $CTX_FILES +for i in $CTX_FILES; do + cp -v $CTX_TEXFM/tex/context/base/$i otfl-$i +done |