luatex-fonts-merged.lua /size: 968 Kb    last modification: 2025-02-21 11:03
1-- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua
2-- parent file : c:/data/develop/context/sources/luatex-fonts.lua
3-- merge date  : 2025-02-19 14:35
4
5do -- begin closure to overcome local limits and interference
6
7if not modules then modules={} end modules ['l-lua']={
8 version=1.001,
9 comment="companion to luat-lib.mkiv",
10 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
11 copyright="PRAGMA ADE / ConTeXt Development Team",
12 license="see context related readme files"
13}
14local next,type,tonumber=next,type,tonumber
15LUAMAJORVERSION,LUAMINORVERSION=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$")
16LUAMAJORVERSION=tonumber(LUAMAJORVERSION) or 5
17LUAMINORVERSION=tonumber(LUAMINORVERSION) or 1
18LUAVERSION=LUAMAJORVERSION+LUAMINORVERSION/10
19LUAFORMAT=status and status.lua_format or 0
20if LUAVERSION<5.2 and jit then
21 MINORVERSION=2
22 LUAVERSION=5.2
23end
24if not lpeg then
25 lpeg=require("lpeg")
26end
27if loadstring then
28 local loadnormal=load
29 function load(first,...)
30  if type(first)=="string" then
31   return loadstring(first,...)
32  else
33   return loadnormal(first,...)
34  end
35 end
36else
37 loadstring=load
38end
39if not ipairs then
40 local function iterate(a,i)
41  i=i+1
42  local v=a[i]
43  if v~=nil then
44   return i,v 
45  end
46 end
47 function ipairs(a)
48  return iterate,a,0
49 end
50end
51if not pairs then
52 function pairs(t)
53  return next,t 
54 end
55end
56if not table.unpack then
57 table.unpack=_G.unpack
58elseif not unpack then
59 _G.unpack=table.unpack
60end
61if not package.loaders then 
62 package.loaders=package.searchers
63end
64local print,select,tostring=print,select,tostring
65local inspectors={}
66function setinspector(kind,inspector) 
67 inspectors[kind]=inspector
68end
69function inspect(...) 
70 for s=1,select("#",...) do
71  local value=select(s,...)
72  if value==nil then
73   print("nil")
74  else
75   local done=false
76   local kind=type(value)
77   local inspector=inspectors[kind]
78   if inspector then
79    done=inspector(value)
80    if done then
81     break
82    end
83   end
84   for kind,inspector in next,inspectors do
85    done=inspector(value)
86    if done then
87     break
88    end
89   end
90   if not done then
91    print(tostring(value))
92   end
93  end
94 end
95end
96local dummy=function() end
97function optionalrequire(...)
98 local ok,result=xpcall(require,dummy,...)
99 if ok then
100  return result
101 end
102end
103local flush=io.flush
104if flush then
105 local execute=os.execute if execute then function os.execute(...) flush() return execute(...) end end
106 local exec=os.exec if exec then function os.exec   (...) flush() return exec   (...) end end
107 local spawn=os.spawn   if spawn   then function os.spawn  (...) flush() return spawn  (...) end end
108 local popen=io.popen   if popen   then function io.popen  (...) flush() return popen  (...) end end
109end
110FFISUPPORTED=type(ffi)=="table" and ffi.os~="" and ffi.arch~="" and ffi.load
111if not FFISUPPORTED then
112 local okay;okay,ffi=pcall(require,"ffi")
113 FFISUPPORTED=type(ffi)=="table" and ffi.os~="" and ffi.arch~="" and ffi.load
114end
115if not FFISUPPORTED then
116 ffi=nil
117elseif not ffi.number then
118 ffi.number=tonumber
119end
120if LUAVERSION>5.3 then
121end
122if status and os.setenv then
123 os.setenv("engine",string.lower(status.luatex_engine or "unknown"))
124end
125
126end -- closure
127
128do -- begin closure to overcome local limits and interference
129
130if not modules then modules={} end modules ['l-lpeg']={
131 version=1.001,
132 comment="companion to luat-lib.mkiv",
133 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
134 copyright="PRAGMA ADE / ConTeXt Development Team",
135 license="see context related readme files"
136}
137lpeg=require("lpeg") 
138local lpeg=lpeg
139if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end
140local type,next,tostring=type,next,tostring
141local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.format
142local floor=math.floor
143local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt
144local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print
145if setinspector then
146 setinspector("lpeg",function(v) if lpegtype(v) then lpegprint(v) return true end end)
147end
148lpeg.patterns=lpeg.patterns or {} 
149local patterns=lpeg.patterns
150local anything=P(1)
151local endofstring=P(-1)
152local alwaysmatched=P(true)
153patterns.anything=anything
154patterns.endofstring=endofstring
155patterns.beginofstring=alwaysmatched
156patterns.alwaysmatched=alwaysmatched
157local sign=S('+-')
158local zero=P('0')
159local digit=R('09')
160local digits=digit^1
161local octdigit=R("07")
162local octdigits=octdigit^1
163local lowercase=R("az")
164local uppercase=R("AZ")
165local underscore=P("_")
166local hexdigit=digit+lowercase+uppercase
167local hexdigits=hexdigit^1
168local cr,lf,crlf=P("\r"),P("\n"),P("\r\n")
169local newline=P("\r")*(P("\n")+P(true))+P("\n")  
170local escaped=P("\\")*anything
171local squote=P("'")
172local dquote=P('"')
173local space=P(" ")
174local period=P(".")
175local comma=P(",")
176local utfbom_32_be=P('\000\000\254\255') 
177local utfbom_32_le=P('\255\254\000\000') 
178local utfbom_16_be=P('\254\255')   
179local utfbom_16_le=P('\255\254')   
180local utfbom_8=P('\239\187\191')  
181local utfbom=utfbom_32_be+utfbom_32_le+utfbom_16_be+utfbom_16_le+utfbom_8
182local utftype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")+alwaysmatched*Cc("utf-8") 
183local utfstricttype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")
184local utfoffset=utfbom_32_be*Cc(4)+utfbom_32_le*Cc(4)+utfbom_16_be*Cc(2)+utfbom_16_le*Cc(2)+utfbom_8*Cc(3)+Cc(0)
185local utf8next=R("\128\191")
186patterns.utfbom_32_be=utfbom_32_be
187patterns.utfbom_32_le=utfbom_32_le
188patterns.utfbom_16_be=utfbom_16_be
189patterns.utfbom_16_le=utfbom_16_le
190patterns.utfbom_8=utfbom_8
191patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") 
192patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") 
193patterns.utf_32_be_nl=P("\000\000\000\r\000\000\000\n")+P("\000\000\000\r")+P("\000\000\000\n")
194patterns.utf_32_le_nl=P("\r\000\000\000\n\000\000\000")+P("\r\000\000\000")+P("\n\000\000\000")
195patterns.utf8one=R("\000\127")
196patterns.utf8two=R("\194\223")*utf8next
197patterns.utf8three=R("\224\239")*utf8next*utf8next
198patterns.utf8four=R("\240\244")*utf8next*utf8next*utf8next
199patterns.utfbom=utfbom
200patterns.utftype=utftype
201patterns.utfstricttype=utfstricttype
202patterns.utfoffset=utfoffset
203local utf8char=patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four
204local validutf8char=utf8char^0*endofstring*Cc(true)+Cc(false)
205local utf8character=P(1)*R("\128\191")^0 
206patterns.utf8=utf8char
207patterns.utf8char=utf8char
208patterns.utf8character=utf8character 
209patterns.validutf8=validutf8char
210patterns.validutf8char=validutf8char
211local eol=S("\n\r")
212local spacer=S(" \t\f\v")  
213local whitespace=eol+spacer
214local nonspacer=1-spacer
215local nonwhitespace=1-whitespace
216patterns.eol=eol
217patterns.spacer=spacer
218patterns.whitespace=whitespace
219patterns.nonspacer=nonspacer
220patterns.nonwhitespace=nonwhitespace
221local stripper=spacer^0*C((spacer^0*nonspacer^1)^0)  
222local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0)
223local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0))
224local nospacer=Cs((whitespace^1/""+nonwhitespace^1)^0)
225local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0)
226local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0)
227local e_collapser=Cs((whitespace^1*endofstring/""+nonwhitespace^1+whitespace^1/" ")^0)
228local x_collapser=Cs((nonwhitespace^1+whitespace^1/"" )^0)
229local b_stripper=Cs(spacer^0/""*(nonspacer^1+spacer^1/" ")^0)
230local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0)
231local e_stripper=Cs((spacer^1*endofstring/""+nonspacer^1+spacer^1/" ")^0)
232local x_stripper=Cs((nonspacer^1+spacer^1/"" )^0)
233patterns.stripper=stripper
234patterns.fullstripper=fullstripper
235patterns.collapser=collapser
236patterns.nospacer=nospacer
237patterns.b_collapser=b_collapser
238patterns.m_collapser=m_collapser
239patterns.e_collapser=e_collapser
240patterns.x_collapser=x_collapser
241patterns.b_stripper=b_stripper
242patterns.m_stripper=m_stripper
243patterns.e_stripper=e_stripper
244patterns.x_stripper=x_stripper
245patterns.lowercase=lowercase
246patterns.uppercase=uppercase
247patterns.letter=patterns.lowercase+patterns.uppercase
248patterns.space=space
249patterns.tab=P("\t")
250patterns.spaceortab=patterns.space+patterns.tab
251patterns.newline=newline
252patterns.emptyline=newline^1
253patterns.equal=P("=")
254patterns.comma=comma
255patterns.commaspacer=comma*spacer^0
256patterns.period=period
257patterns.colon=P(":")
258patterns.semicolon=P(";")
259patterns.underscore=underscore
260patterns.escaped=escaped
261patterns.squote=squote
262patterns.dquote=dquote
263patterns.nosquote=(escaped+(1-squote))^0
264patterns.nodquote=(escaped+(1-dquote))^0
265patterns.unsingle=(squote/"")*patterns.nosquote*(squote/"") 
266patterns.undouble=(dquote/"")*patterns.nodquote*(dquote/"") 
267patterns.unquoted=patterns.undouble+patterns.unsingle 
268patterns.unspacer=((patterns.spacer^1)/"")^0
269patterns.singlequoted=squote*patterns.nosquote*squote
270patterns.doublequoted=dquote*patterns.nodquote*dquote
271patterns.quoted=patterns.doublequoted+patterns.singlequoted
272patterns.digit=digit
273patterns.digits=digits
274patterns.octdigit=octdigit
275patterns.octdigits=octdigits
276patterns.hexdigit=hexdigit
277patterns.hexdigits=hexdigits
278patterns.sign=sign
279patterns.cardinal=digits
280patterns.integer=sign^-1*digits
281patterns.unsigned=digit^0*period*digits
282patterns.float=sign^-1*patterns.unsigned
283patterns.cunsigned=digit^0*comma*digits
284patterns.cpunsigned=digit^0*(period+comma)*digits
285patterns.cfloat=sign^-1*patterns.cunsigned
286patterns.cpfloat=sign^-1*patterns.cpunsigned
287patterns.number=patterns.float+patterns.integer
288patterns.cnumber=patterns.cfloat+patterns.integer
289patterns.cpnumber=patterns.cpfloat+patterns.integer
290patterns.oct=zero*octdigits 
291patterns.octal=patterns.oct
292patterns.HEX=zero*P("X")*(digit+uppercase)^1
293patterns.hex=zero*P("x")*(digit+lowercase)^1
294patterns.hexadecimal=zero*S("xX")*hexdigits
295patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigits+hexdigits*period*hexdigit^0+hexdigits)*(S("pP")*sign^-1*hexdigits)^-1
296patterns.decafloat=sign^-1*(digit^0*period*digits+digits*period*digit^0+digits)*S("eE")*sign^-1*digits
297patterns.propername=(uppercase+lowercase+underscore)*(uppercase+lowercase+underscore+digit)^0*endofstring
298patterns.somecontent=(anything-newline-space)^1 
299patterns.beginline=#(1-newline)
300patterns.longtostring=Cs(whitespace^0/""*((patterns.quoted+nonwhitespace^1+whitespace^1/""*(endofstring+Cc(" ")))^0))
301local function anywhere(pattern) 
302 return (1-P(pattern))^0*P(pattern)
303end
304lpeg.anywhere=anywhere
305function lpeg.instringchecker(p)
306 p=anywhere(p)
307 return function(str)
308  return lpegmatch(p,str) and true or false
309 end
310end
311function lpeg.splitter(pattern,action)
312 if action then
313  return (((1-P(pattern))^1)/action+1)^0
314 else
315  return (Cs((1-P(pattern))^1)+1)^0
316 end
317end
318function lpeg.tsplitter(pattern,action)
319 if action then
320  return Ct((((1-P(pattern))^1)/action+1)^0)
321 else
322  return Ct((Cs((1-P(pattern))^1)+1)^0)
323 end
324end
325local splitters_s,splitters_m,splitters_t={},{},{}
326local function splitat(separator,single)
327 local splitter=(single and splitters_s[separator]) or splitters_m[separator]
328 if not splitter then
329  separator=P(separator)
330  local other=C((1-separator)^0)
331  if single then
332   local any=anything
333   splitter=other*(separator*C(any^0)+"") 
334   splitters_s[separator]=splitter
335  else
336   splitter=other*(separator*other)^0
337   splitters_m[separator]=splitter
338  end
339 end
340 return splitter
341end
342local function tsplitat(separator)
343 local splitter=splitters_t[separator]
344 if not splitter then
345  splitter=Ct(splitat(separator))
346  splitters_t[separator]=splitter
347 end
348 return splitter
349end
350lpeg.splitat=splitat
351lpeg.tsplitat=tsplitat
352function string.splitup(str,separator)
353 if not separator then
354  separator=","
355 end
356 return lpegmatch(splitters_m[separator] or splitat(separator),str)
357end
358local cache={}
359function lpeg.split(separator,str)
360 local c=cache[separator]
361 if not c then
362  c=tsplitat(separator)
363  cache[separator]=c
364 end
365 return lpegmatch(c,str)
366end
367function string.split(str,separator)
368 if separator then
369  local c=cache[separator]
370  if not c then
371   c=tsplitat(separator)
372   cache[separator]=c
373  end
374  return lpegmatch(c,str)
375 else
376  return { str }
377 end
378end
379local spacing=patterns.spacer^0*newline 
380local empty=spacing*Cc("")
381local nonempty=Cs((1-spacing)^1)*spacing^-1
382local content=(empty+nonempty)^1
383patterns.textline=content
384local linesplitter=tsplitat(newline)
385patterns.linesplitter=linesplitter
386function string.splitlines(str)
387 return lpegmatch(linesplitter,str)
388end
389local cache={}
390function lpeg.checkedsplit(separator,str)
391 local c=cache[separator]
392 if not c then
393  separator=P(separator)
394  local other=C((1-separator)^1)
395  c=Ct(separator^0*other*(separator^1*other)^0)
396  cache[separator]=c
397 end
398 return lpegmatch(c,str)
399end
400function string.checkedsplit(str,separator)
401 local c=cache[separator]
402 if not c then
403  separator=P(separator)
404  local other=C((1-separator)^1)
405  c=Ct(separator^0*other*(separator^1*other)^0)
406  cache[separator]=c
407 end
408 return lpegmatch(c,str)
409end
410local function f2(s) local c1,c2=byte(s,1,2) return   c1*64+c2-12416 end
411local function f3(s) local c1,c2,c3=byte(s,1,3) return  (c1*64+c2)*64+c3-925824 end
412local function f4(s) local c1,c2,c3,c4=byte(s,1,4) return ((c1*64+c2)*64+c3)*64+c4-63447168 end
413local utf8byte=patterns.utf8one/byte+patterns.utf8two/f2+patterns.utf8three/f3+patterns.utf8four/f4
414patterns.utf8byte=utf8byte
415local cache={}
416function lpeg.stripper(str)
417 if type(str)=="string" then
418  local s=cache[str]
419  if not s then
420   s=Cs(((S(str)^1)/""+1)^0)
421   cache[str]=s
422  end
423  return s
424 else
425  return Cs(((str^1)/""+1)^0)
426 end
427end
428local cache={}
429function lpeg.keeper(str)
430 if type(str)=="string" then
431  local s=cache[str]
432  if not s then
433   s=Cs((((1-S(str))^1)/""+1)^0)
434   cache[str]=s
435  end
436  return s
437 else
438  return Cs((((1-str)^1)/""+1)^0)
439 end
440end
441function lpeg.frontstripper(str) 
442 return (P(str)+P(true))*Cs(anything^0)
443end
444function lpeg.endstripper(str) 
445 return Cs((1-P(str)*endofstring)^0)
446end
447function lpeg.replacer(one,two,makefunction,isutf) 
448 local pattern
449 local u=isutf and utf8char or 1
450 if type(one)=="table" then
451  local no=#one
452  local p=P(false)
453  if no==0 then
454   for k,v in next,one do
455    p=p+P(k)/v
456   end
457   pattern=Cs((p+u)^0)
458  elseif no==1 then
459   local o=one[1]
460   one,two=P(o[1]),o[2]
461   pattern=Cs((one/two+u)^0)
462  else
463   for i=1,no do
464    local o=one[i]
465    p=p+P(o[1])/o[2]
466   end
467   pattern=Cs((p+u)^0)
468  end
469 else
470  pattern=Cs((P(one)/(two or "")+u)^0)
471 end
472 if makefunction then
473  return function(str)
474   return lpegmatch(pattern,str)
475  end
476 else
477  return pattern
478 end
479end
480function lpeg.finder(lst,makefunction,isutf) 
481 local pattern
482 if type(lst)=="table" then
483  pattern=P(false)
484  if #lst==0 then
485   for k,v in next,lst do
486    pattern=pattern+P(k) 
487   end
488  else
489   for i=1,#lst do
490    pattern=pattern+P(lst[i])
491   end
492  end
493 else
494  pattern=P(lst)
495 end
496 if isutf then
497  pattern=((utf8char or 1)-pattern)^0*pattern
498 else
499  pattern=(1-pattern)^0*pattern
500 end
501 if makefunction then
502  return function(str)
503   return lpegmatch(pattern,str)
504  end
505 else
506  return pattern
507 end
508end
509local splitters_f,splitters_s={},{}
510function lpeg.firstofsplit(separator) 
511 local splitter=splitters_f[separator]
512 if not splitter then
513  local pattern=P(separator)
514  splitter=C((1-pattern)^0)
515  splitters_f[separator]=splitter
516 end
517 return splitter
518end
519function lpeg.secondofsplit(separator) 
520 local splitter=splitters_s[separator]
521 if not splitter then
522  local pattern=P(separator)
523  splitter=(1-pattern)^0*pattern*C(anything^0)
524  splitters_s[separator]=splitter
525 end
526 return splitter
527end
528local splitters_s,splitters_p={},{}
529function lpeg.beforesuffix(separator) 
530 local splitter=splitters_s[separator]
531 if not splitter then
532  local pattern=P(separator)
533  splitter=C((1-pattern)^0)*pattern*endofstring
534  splitters_s[separator]=splitter
535 end
536 return splitter
537end
538function lpeg.afterprefix(separator) 
539 local splitter=splitters_p[separator]
540 if not splitter then
541  local pattern=P(separator)
542  splitter=pattern*C(anything^0)
543  splitters_p[separator]=splitter
544 end
545 return splitter
546end
547function lpeg.balancer(left,right)
548 left,right=P(left),P(right)
549 return P { left*((1-left-right)+V(1))^0*right }
550end
551function lpeg.counter(pattern,action)
552 local n=0
553 local pattern=(P(pattern)/function() n=n+1 end+anything)^0
554 if action then
555  return function(str) n=0;lpegmatch(pattern,str);action(n) end
556 else
557  return function(str) n=0;lpegmatch(pattern,str);return n end
558 end
559end
560function lpeg.is_lpeg(p)
561 return p and lpegtype(p)=="pattern"
562end
563function lpeg.oneof(list,...) 
564 if type(list)~="table" then
565  list={ list,... }
566 end
567 local p=P(list[1])
568 for l=2,#list do
569  p=p+P(list[l])
570 end
571 return p
572end
573local sort=table.sort
574local function copyindexed(old)
575 local new={}
576 for i=1,#old do
577  new[i]=old
578 end
579 return new
580end
581local function sortedkeys(tab)
582 local keys,s={},0
583 for key,_ in next,tab do
584  s=s+1
585  keys[s]=key
586 end
587 sort(keys)
588 return keys
589end
590function lpeg.append(list,pp,delayed,checked)
591 local p=pp
592 if #list>0 then
593  local keys=copyindexed(list)
594  sort(keys)
595  for i=#keys,1,-1 do
596   local k=keys[i]
597   if p then
598    p=P(k)+p
599   else
600    p=P(k)
601   end
602  end
603 elseif delayed then 
604  local keys=sortedkeys(list)
605  if p then
606   for i=1,#keys,1 do
607    local k=keys[i]
608    local v=list[k]
609    p=P(k)/list+p
610   end
611  else
612   for i=1,#keys do
613    local k=keys[i]
614    local v=list[k]
615    if p then
616     p=P(k)+p
617    else
618     p=P(k)
619    end
620   end
621   if p then
622    p=p/list
623   end
624  end
625 elseif checked then
626  local keys=sortedkeys(list)
627  for i=1,#keys do
628   local k=keys[i]
629   local v=list[k]
630   if p then
631    if k==v then
632     p=P(k)+p
633    else
634     p=P(k)/v+p
635    end
636   else
637    if k==v then
638     p=P(k)
639    else
640     p=P(k)/v
641    end
642   end
643  end
644 else
645  local keys=sortedkeys(list)
646  for i=1,#keys do
647   local k=keys[i]
648   local v=list[k]
649   if p then
650    p=P(k)/v+p
651   else
652    p=P(k)/v
653   end
654  end
655 end
656 return p
657end
658local p_false=P(false)
659local p_true=P(true)
660local lower=utf and utf.lower or string.lower
661local upper=utf and utf.upper or string.upper
662function lpeg.setutfcasers(l,u)
663 lower=l or lower
664 upper=u or upper
665end
666local function make1(t,rest)
667 local p=p_false
668 local keys=sortedkeys(t)
669 for i=1,#keys do
670  local k=keys[i]
671  if k~="" then
672   local v=t[k]
673   if v==true then
674    p=p+P(k)*p_true
675   elseif v==false then
676   else
677    p=p+P(k)*make1(v,v[""])
678   end
679  end
680 end
681 if rest then
682  p=p+p_true
683 end
684 return p
685end
686local function make2(t,rest) 
687 local p=p_false
688 local keys=sortedkeys(t)
689 for i=1,#keys do
690  local k=keys[i]
691  if k~="" then
692   local v=t[k]
693   if v==true then
694    p=p+(P(lower(k))+P(upper(k)))*p_true
695   elseif v==false then
696   else
697    p=p+(P(lower(k))+P(upper(k)))*make2(v,v[""])
698   end
699  end
700 end
701 if rest then
702  p=p+p_true
703 end
704 return p
705end
706local function utfchartabletopattern(list,insensitive) 
707 local tree={}
708 local n=#list
709 if n==0 then
710  for s in next,list do
711   local t=tree
712   local p,pk
713   for c in gmatch(s,".") do
714    if t==true then
715     t={ [c]=true,[""]=true }
716     p[pk]=t
717     p=t
718     t=false
719    elseif t==false then
720     t={ [c]=false }
721     p[pk]=t
722     p=t
723     t=false
724    else
725     local tc=t[c]
726     if not tc then
727      tc=false
728      t[c]=false
729     end
730     p=t
731     t=tc
732    end
733    pk=c
734   end
735   if t==false then
736    p[pk]=true
737   elseif t==true then
738   else
739    t[""]=true
740   end
741  end
742 else
743  for i=1,n do
744   local s=list[i]
745   local t=tree
746   local p,pk
747   for c in gmatch(s,".") do
748    if t==true then
749     t={ [c]=true,[""]=true }
750     p[pk]=t
751     p=t
752     t=false
753    elseif t==false then
754     t={ [c]=false }
755     p[pk]=t
756     p=t
757     t=false
758    else
759     local tc=t[c]
760     if not tc then
761      tc=false
762      t[c]=false
763     end
764     p=t
765     t=tc
766    end
767    pk=c
768   end
769   if t==false then
770    p[pk]=true
771   elseif t==true then
772   else
773    t[""]=true
774   end
775  end
776 end
777 return (insensitive and make2 or make1)(tree)
778end
779lpeg.utfchartabletopattern=utfchartabletopattern
780function lpeg.utfreplacer(list,insensitive)
781 local pattern=Cs((utfchartabletopattern(list,insensitive)/list+utf8character)^0)
782 return function(str)
783  return lpegmatch(pattern,str) or str
784 end
785end
786patterns.containseol=lpeg.finder(eol)
787local function nextstep(n,step,result)
788 local m=n%step   
789 local d=floor(n/step) 
790 if d>0 then
791  local v=V(tostring(step))
792  local s=result.start
793  for i=1,d do
794   if s then
795    s=v*s
796   else
797    s=v
798   end
799  end
800  result.start=s
801 end
802 if step>1 and result.start then
803  local v=V(tostring(step/2))
804  result[tostring(step)]=v*v
805 end
806 if step>0 then
807  return nextstep(m,step/2,result)
808 else
809  return result
810 end
811end
812function lpeg.times(pattern,n)
813 return P(nextstep(n,2^16,{ "start",["1"]=pattern }))
814end
815do
816 local trailingzeros=zero^0*-digit 
817 local stripper=Cs((
818  digits*(
819   period*trailingzeros/""+period*(digit-trailingzeros)^1*(trailingzeros/"")
820  )+1
821 )^0)
822 lpeg.patterns.stripzeros=stripper 
823 local nonzero=digit-zero
824 local trailingzeros=zero^1*endofstring
825 local stripper=Cs((1-period)^0*(
826  period*trailingzeros/""+period*(nonzero^1+(trailingzeros/"")+zero^1)^0+endofstring
827 ))
828 lpeg.patterns.stripzero=stripper
829end
830local byte_to_HEX={}
831local byte_to_hex={}
832local byte_to_dec={} 
833local hex_to_byte={}
834for i=0,255 do
835 local H=format("%02X",i)
836 local h=format("%02x",i)
837 local d=format("%03i",i)
838 local c=char(i)
839 byte_to_HEX[c]=H
840 byte_to_hex[c]=h
841 byte_to_dec[c]=d
842 hex_to_byte[h]=c
843 hex_to_byte[H]=c
844end
845local hextobyte=P(2)/hex_to_byte
846local bytetoHEX=P(1)/byte_to_HEX
847local bytetohex=P(1)/byte_to_hex
848local bytetodec=P(1)/byte_to_dec
849local hextobytes=Cs(hextobyte^0)
850local bytestoHEX=Cs(bytetoHEX^0)
851local bytestohex=Cs(bytetohex^0)
852local bytestodec=Cs(bytetodec^0)
853patterns.hextobyte=hextobyte
854patterns.bytetoHEX=bytetoHEX
855patterns.bytetohex=bytetohex
856patterns.bytetodec=bytetodec
857patterns.hextobytes=hextobytes
858patterns.bytestoHEX=bytestoHEX
859patterns.bytestohex=bytestohex
860patterns.bytestodec=bytestodec
861function string.toHEX(s)
862 if not s or s=="" then
863  return s
864 else
865  return lpegmatch(bytestoHEX,s)
866 end
867end
868function string.tohex(s)
869 if not s or s=="" then
870  return s
871 else
872  return lpegmatch(bytestohex,s)
873 end
874end
875function string.todec(s)
876 if not s or s=="" then
877  return s
878 else
879  return lpegmatch(bytestodec,s)
880 end
881end
882function string.tobytes(s)
883 if not s or s=="" then
884  return s
885 else
886  return lpegmatch(hextobytes,s)
887 end
888end
889local patterns={} 
890local function containsws(what)
891 local p=patterns[what]
892 if not p then
893  local p1=P(what)*(whitespace+endofstring)*Cc(true)
894  local p2=whitespace*P(p1)
895  p=P(p1)+P(1-p2)^0*p2+Cc(false)
896  patterns[what]=p
897 end
898 return p
899end
900lpeg.containsws=containsws
901function string.containsws(str,what)
902 return lpegmatch(patterns[what] or containsws(what),str)
903end
904
905end -- closure
906
907do -- begin closure to overcome local limits and interference
908
909if not modules then modules={} end modules ['l-functions']={
910 version=1.001,
911 comment="companion to luat-lib.mkiv",
912 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
913 copyright="PRAGMA ADE / ConTeXt Development Team",
914 license="see context related readme files"
915}
916functions=functions or {}
917function functions.dummy() end
918
919end -- closure
920
921do -- begin closure to overcome local limits and interference
922
923if not modules then modules={} end modules ['l-string']={
924 version=1.001,
925 comment="companion to luat-lib.mkiv",
926 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
927 copyright="PRAGMA ADE / ConTeXt Development Team",
928 license="see context related readme files"
929}
930local string=string
931local sub,gmatch,format,char,byte,rep,lower,find=string.sub,string.gmatch,string.format,string.char,string.byte,string.rep,string.lower,string.find
932local lpegmatch,patterns=lpeg.match,lpeg.patterns
933local P,S,C,Ct,Cc,Cs=lpeg.P,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.Cs
934local unquoted=patterns.squote*C(patterns.nosquote)*patterns.squote+patterns.dquote*C(patterns.nodquote)*patterns.dquote
935function string.unquoted(str)
936 return lpegmatch(unquoted,str) or str
937end
938function string.quoted(str)
939 return format("%q",str) 
940end
941function string.count(str,pattern)
942 local n=0
943 local i=1
944 local l=#pattern
945 while true do
946  i=find(str,pattern,i)
947  if i then
948   n=n+1
949   i=i+l
950  else
951   break
952  end
953 end
954 return n
955end
956function string.limit(str,n,sentinel) 
957 if #str>n then
958  sentinel=sentinel or "..."
959  return sub(str,1,(n-#sentinel))..sentinel
960 else
961  return str
962 end
963end
964local stripper=patterns.stripper
965local fullstripper=patterns.fullstripper
966local collapser=patterns.collapser
967local nospacer=patterns.nospacer
968local longtostring=patterns.longtostring
969function string.strip(str)
970 return str and lpegmatch(stripper,str) or ""
971end
972function string.fullstrip(str)
973 return str and lpegmatch(fullstripper,str) or ""
974end
975function string.collapsespaces(str)
976 return str and lpegmatch(collapser,str) or ""
977end
978function string.nospaces(str)
979 return str and lpegmatch(nospacer,str) or ""
980end
981function string.longtostring(str)
982 return str and lpegmatch(longtostring,str) or ""
983end
984local pattern=P(" ")^0*P(-1)
985function string.is_empty(str)
986 if not str or str=="" then
987  return true
988 else
989  return lpegmatch(pattern,str) and true or false
990 end
991end
992local anything=patterns.anything
993local moreescapes=Cc("%")*S(".-+%?()[]*$^{}")
994local allescapes=Cc("%")*S(".-+%?()[]*")   
995local someescapes=Cc("%")*S(".-+%()[]")  
996local matchescapes=Cc(".")*S("*?")     
997local pattern_m=Cs ((moreescapes+anything )^0 )
998local pattern_a=Cs ((allescapes+anything )^0 )
999local pattern_b=Cs ((someescapes+matchescapes+anything )^0 )
1000local pattern_c=Cs (Cc("^")*(someescapes+matchescapes+anything )^0*Cc("$") )
1001function string.escapedpattern(str,simple)
1002 return lpegmatch(simple and pattern_b or pattern_a,str)
1003end
1004function string.topattern(str,lowercase,strict)
1005 if str=="" or type(str)~="string" then
1006  return ".*"
1007 elseif strict=="all" then
1008  str=lpegmatch(pattern_m,str)
1009 elseif strict then
1010  str=lpegmatch(pattern_c,str)
1011 else
1012  str=lpegmatch(pattern_b,str)
1013 end
1014 if lowercase then
1015  return lower(str)
1016 else
1017  return str
1018 end
1019end
1020function string.valid(str,default)
1021 return (type(str)=="string" and str~="" and str) or default or nil
1022end
1023string.itself=function(s) return s end
1024local pattern_c=Ct(C(1)^0) 
1025local pattern_b=Ct((C(1)/byte)^0)
1026function string.totable(str,bytes)
1027 return lpegmatch(bytes and pattern_b or pattern_c,str)
1028end
1029local replacer=lpeg.replacer("@","%%") 
1030function string.tformat(fmt,...)
1031 return format(lpegmatch(replacer,fmt),...)
1032end
1033string.quote=string.quoted
1034string.unquote=string.unquoted
1035if not string.bytetable then 
1036 local limit=5000 
1037 function string.bytetable(str) 
1038  local n=#str
1039  if n>limit then
1040   local t={ byte(str,1,limit) }
1041   for i=limit+1,n do
1042    t[i]=byte(str,i)
1043   end
1044   return t
1045  else
1046   return { byte(str,1,n) }
1047  end
1048 end
1049end
1050
1051end -- closure
1052
1053do -- begin closure to overcome local limits and interference
1054
1055if not modules then modules={} end modules ['l-table']={
1056 version=1.001,
1057 comment="companion to luat-lib.mkiv",
1058 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
1059 copyright="PRAGMA ADE / ConTeXt Development Team",
1060 license="see context related readme files"
1061}
1062local type,next,tostring,tonumber,select,rawget=type,next,tostring,tonumber,select,rawget
1063local table,string=table,string
1064local concat,sort=table.concat,table.sort
1065local format,lower,dump=string.format,string.lower,string.dump
1066local getmetatable,setmetatable=getmetatable,setmetatable
1067local lpegmatch,patterns=lpeg.match,lpeg.patterns
1068local floor=math.floor
1069local stripper=patterns.stripper
1070function table.getn(t)
1071 return t and #t 
1072end
1073function table.strip(tab)
1074 local lst={}
1075 local l=0
1076 for i=1,#tab do
1077  local s=lpegmatch(stripper,tab[i]) or ""
1078  if s=="" then
1079  else
1080   l=l+1
1081   lst[l]=s
1082  end
1083 end
1084 return lst
1085end
1086function table.keys(t)
1087 if t then
1088  local keys={}
1089  local k=0
1090  for key in next,t do
1091   k=k+1
1092   keys[k]=key
1093  end
1094  return keys
1095 else
1096  return {}
1097 end
1098end
1099local function compare(a,b)
1100 local ta=type(a) 
1101 if ta=="number" then
1102  local tb=type(b) 
1103  if ta==tb then
1104   return a<b
1105  elseif tb=="string" then
1106   return tostring(a)<b
1107  end
1108 elseif ta=="string" then
1109  local tb=type(b) 
1110  if ta==tb then
1111   return a<b
1112  else
1113   return a<tostring(b)
1114  end
1115 end
1116 return tostring(a)<tostring(b) 
1117end
1118local function sortedkeys(tab)
1119 if tab then
1120  local srt={}
1121  local category=0 
1122  local s=0
1123  for key in next,tab do
1124   s=s+1
1125   srt[s]=key
1126   if category~=3 then
1127    local tkey=type(key)
1128    if category==1 then
1129     if tkey~="string" then
1130      category=3
1131     end
1132    elseif category==2 then
1133     if tkey~="number" then
1134      category=3
1135     end
1136    else
1137     if tkey=="string" then
1138      category=1
1139     elseif tkey=="number" then
1140      category=2
1141     else
1142      category=3
1143     end
1144    end
1145   end
1146  end
1147  if s<2 then
1148  elseif category==3 then
1149   sort(srt,compare)
1150  else
1151   sort(srt)
1152  end
1153  return srt
1154 else
1155  return {}
1156 end
1157end
1158local function sortedhashonly(tab)
1159 if tab then
1160  local srt={}
1161  local s=0
1162  for key in next,tab do
1163   if type(key)=="string" then
1164    s=s+1
1165    srt[s]=key
1166   end
1167  end
1168  if s>1 then
1169   sort(srt)
1170  end
1171  return srt
1172 else
1173  return {}
1174 end
1175end
1176local function sortedindexonly(tab)
1177 if tab then
1178  local srt={}
1179  local s=0
1180  for key in next,tab do
1181   if type(key)=="number" then
1182    s=s+1
1183    srt[s]=key
1184   end
1185  end
1186  if s>1 then
1187   sort(srt)
1188  end
1189  return srt
1190 else
1191  return {}
1192 end
1193end
1194local function sortedhashkeys(tab,cmp) 
1195 if tab then
1196  local srt={}
1197  local s=0
1198  for key in next,tab do
1199   if key then
1200    s=s+1
1201    srt[s]=key
1202   end
1203  end
1204  if s>1 then
1205   sort(srt,cmp)
1206  end
1207  return srt
1208 else
1209  return {}
1210 end
1211end
1212function table.allkeys(t)
1213 local keys={}
1214 for k,v in next,t do
1215  for k in next,v do
1216   keys[k]=true
1217  end
1218 end
1219 return sortedkeys(keys)
1220end
1221table.sortedkeys=sortedkeys
1222table.sortedhashonly=sortedhashonly
1223table.sortedindexonly=sortedindexonly
1224table.sortedhashkeys=sortedhashkeys
1225local function nothing() end
1226local function sortedhash(t,cmp)
1227 if t then
1228  local s
1229  if cmp then
1230   s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
1231  else
1232   s=sortedkeys(t) 
1233  end
1234  local m=#s
1235  if m==1 then
1236   return next,t
1237  elseif m>0 then
1238   local n=0
1239   return function()
1240    if n<m then
1241     n=n+1
1242     local k=s[n]
1243     return k,t[k]
1244    end
1245   end
1246  end
1247 end
1248 return nothing
1249end
1250table.sortedhash=sortedhash
1251table.sortedpairs=sortedhash 
1252function table.append(t,list)
1253 local n=#t
1254 for i=1,#list do
1255  n=n+1
1256  t[n]=list[i]
1257 end
1258 return t
1259end
1260function table.prepend(t,list)
1261 local nl=#list
1262 local nt=nl+#t
1263 for i=#t,1,-1 do
1264  t[nt]=t[i]
1265  nt=nt-1
1266 end
1267 for i=1,#list do
1268  t[i]=list[i]
1269 end
1270 return t
1271end
1272function table.merge(t,...) 
1273 if not t then
1274  t={}
1275 end
1276 for i=1,select("#",...) do
1277  for k,v in next,(select(i,...)) do
1278   t[k]=v
1279  end
1280 end
1281 return t
1282end
1283function table.merged(...)
1284 local t={}
1285 for i=1,select("#",...) do
1286  for k,v in next,(select(i,...)) do
1287   t[k]=v
1288  end
1289 end
1290 return t
1291end
1292function table.imerge(t,...)
1293 local nt=#t
1294 for i=1,select("#",...) do
1295  local nst=select(i,...)
1296  for j=1,#nst do
1297   nt=nt+1
1298   t[nt]=nst[j]
1299  end
1300 end
1301 return t
1302end
1303function table.imerged(...)
1304 local tmp={}
1305 local ntmp=0
1306 for i=1,select("#",...) do
1307  local nst=select(i,...)
1308  for j=1,#nst do
1309   ntmp=ntmp+1
1310   tmp[ntmp]=nst[j]
1311  end
1312 end
1313 return tmp
1314end
1315local function fastcopy(old,metatabletoo) 
1316 if old then
1317  local new={}
1318  for k,v in next,old do
1319   if type(v)=="table" then
1320    new[k]=fastcopy(v,metatabletoo) 
1321   else
1322    new[k]=v
1323   end
1324  end
1325  if metatabletoo then
1326   local mt=getmetatable(old)
1327   if mt then
1328    setmetatable(new,mt)
1329   end
1330  end
1331  return new
1332 else
1333  return {}
1334 end
1335end
1336local function copy(t,tables) 
1337 if not tables then
1338  tables={}
1339 end
1340 local tcopy={}
1341 if not tables[t] then
1342  tables[t]=tcopy
1343 end
1344 for i,v in next,t do 
1345  local k
1346  if type(i)=="table" then
1347   if tables[i] then
1348    k=tables[i]
1349   else
1350    k=copy(i,tables)
1351   end
1352  else
1353   k=i
1354  end
1355  if type(v)~="table" then
1356   tcopy[k]=v
1357  elseif tables[v] then
1358   tcopy[k]=tables[v]
1359  else
1360   tcopy[k]=copy(v,tables)
1361  end
1362 end
1363 local mt=getmetatable(t)
1364 if mt then
1365  setmetatable(tcopy,mt)
1366 end
1367 return tcopy
1368end
1369table.fastcopy=fastcopy
1370table.copy=copy
1371function table.derive(parent) 
1372 local child={}
1373 if parent then
1374  setmetatable(child,{ __index=parent })
1375 end
1376 return child
1377end
1378function table.tohash(t,value)
1379 local h={}
1380 if t then
1381  if value==nil then value=true end
1382  for _,v in next,t do
1383   h[v]=value
1384  end
1385 end
1386 return h
1387end
1388function table.fromhash(t)
1389 local hsh={}
1390 local h=0
1391 for k,v in next,t do
1392  if v then
1393   h=h+1
1394   hsh[h]=k
1395  end
1396 end
1397 return hsh
1398end
1399local noquotes,hexify,handle,compact,inline,functions,metacheck,accurate
1400local reserved=table.tohash { 
1401 'and','break','do','else','elseif','end','false','for','function','if',
1402 'in','local','nil','not','or','repeat','return','then','true','until','while',
1403 'NaN','goto','const',
1404}
1405local function is_simple_table(t,hexify,accurate) 
1406 local nt=#t
1407 if nt>0 then
1408  local n=0
1409  for _,v in next,t do
1410   n=n+1
1411   if type(v)=="table" then
1412    return nil
1413   end
1414  end
1415  local haszero=rawget(t,0) 
1416  if n==nt then
1417   local tt={}
1418   for i=1,nt do
1419    local v=t[i]
1420    local tv=type(v)
1421    if tv=="number" then
1422     if hexify then
1423      tt[i]=format("0x%X",v)
1424     elseif accurate then
1425      tt[i]=format("%q",v)
1426     else
1427      tt[i]=v 
1428     end
1429    elseif tv=="string" then
1430     tt[i]=format("%q",v) 
1431    elseif tv=="boolean" then
1432     tt[i]=v and "true" or "false"
1433    else
1434     return nil
1435    end
1436   end
1437   return tt
1438  elseif haszero and (n==nt+1) then
1439   local tt={}
1440   for i=0,nt do
1441    local v=t[i]
1442    local tv=type(v)
1443    if tv=="number" then
1444     if hexify then
1445      tt[i+1]=format("0x%X",v)
1446     elseif accurate then
1447      tt[i+1]=format("%q",v)
1448     else
1449      tt[i+1]=v 
1450     end
1451    elseif tv=="string" then
1452     tt[i+1]=format("%q",v) 
1453    elseif tv=="boolean" then
1454     tt[i+1]=v and "true" or "false"
1455    else
1456     return nil
1457    end
1458   end
1459   tt[1]="[0] = "..tt[1]
1460   return tt
1461  end
1462 end
1463 return nil
1464end
1465table.is_simple_table=is_simple_table
1466local propername=patterns.propername 
1467local function dummy() end
1468local function do_serialize(root,name,depth,level,indexed)
1469 if level>0 then
1470  depth=depth.." "
1471  if indexed then
1472   handle(format("%s{",depth))
1473  else
1474   local tn=type(name)
1475   if tn=="number" then
1476    if hexify then
1477     handle(format("%s[0x%X]={",depth,name))
1478    else
1479     handle(format("%s[%s]={",depth,name))
1480    end
1481   elseif tn=="string" then
1482    if noquotes and not reserved[name] and lpegmatch(propername,name) then
1483     handle(format("%s%s={",depth,name))
1484    else
1485     handle(format("%s[%q]={",depth,name))
1486    end
1487   elseif tn=="boolean" then
1488    handle(format("%s[%s]={",depth,name and "true" or "false"))
1489   else
1490    handle(format("%s{",depth))
1491   end
1492  end
1493 end
1494 if root and next(root)~=nil then
1495  local first=nil
1496  local last=0
1497  if compact then
1498   last=#root
1499   for k=1,last do
1500    if rawget(root,k)==nil then
1501     last=k-1
1502     break
1503    end
1504   end
1505   if last>0 then
1506    first=1
1507   end
1508  end
1509  local sk=sortedkeys(root)
1510  for i=1,#sk do
1511   local k=sk[i]
1512   local v=root[k]
1513   local tv=type(v)
1514   local tk=type(k)
1515   if compact and first and tk=="number" and k>=first and k<=last then
1516    if tv=="number" then
1517     if hexify then
1518      handle(format("%s 0x%X,",depth,v))
1519     elseif accurate then
1520      handle(format("%s %q,",depth,v))
1521     else
1522      handle(format("%s %s,",depth,v)) 
1523     end
1524    elseif tv=="string" then
1525     handle(format("%s %q,",depth,v))
1526    elseif tv=="table" then
1527     if next(v)==nil then
1528      handle(format("%s {},",depth))
1529     elseif inline then 
1530      local st=is_simple_table(v,hexify,accurate)
1531      if st then
1532       handle(format("%s { %s },",depth,concat(st,", ")))
1533      else
1534       do_serialize(v,k,depth,level+1,true)
1535      end
1536     else
1537      do_serialize(v,k,depth,level+1,true)
1538     end
1539    elseif tv=="boolean" then
1540     handle(format("%s %s,",depth,v and "true" or "false"))
1541    elseif tv=="function" then
1542     if functions then
1543      handle(format('%s load(%q),',depth,dump(v))) 
1544     else
1545      handle(format('%s "function",',depth))
1546     end
1547    else
1548     handle(format("%s %q,",depth,tostring(v)))
1549    end
1550   elseif k=="__p__" then 
1551    if false then
1552     handle(format("%s __p__=nil,",depth))
1553    end
1554   elseif tv=="number" then
1555    if tk=="number" then
1556     if hexify then
1557      if accurate then
1558       handle(format("%s [0x%X]=%q,",depth,k,v))
1559      else
1560       handle(format("%s [0x%X]=%s,",depth,k,v))
1561      end
1562     elseif accurate then
1563      handle(format("%s [%s]=%q,",depth,k,v))
1564     else
1565      handle(format("%s [%s]=%s,",depth,k,v)) 
1566     end
1567    elseif tk=="boolean" then
1568     if hexify then
1569      if accurate then
1570       handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
1571      else
1572       handle(format("%s [%s]=%s,",depth,k and "true" or "false",v))
1573      end
1574     elseif accurate then
1575      handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
1576     else
1577      handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) 
1578     end
1579    elseif tk~="string" then
1580    elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
1581     if hexify then
1582      if accurate then
1583       handle(format("%s %s=%q,",depth,k,v))
1584      else
1585       handle(format("%s %s=0x%X,",depth,k,v))
1586      end
1587     elseif accurate then
1588      handle(format("%s %s=%q,",depth,k,v))
1589     else
1590      handle(format("%s %s=%s,",depth,k,v)) 
1591     end
1592    else
1593     if hexify then
1594      if accurate then
1595       handle(format("%s [%q]=%q,",depth,k,v))
1596      else
1597       handle(format("%s [%q]=0x%X,",depth,k,v))
1598      end
1599     elseif accurate then
1600      handle(format("%s [%q]=%q,",depth,k,v))
1601     else
1602      handle(format("%s [%q]=%s,",depth,k,v)) 
1603     end
1604    end
1605   elseif tv=="string" then
1606    if tk=="number" then
1607     if hexify then
1608      handle(format("%s [0x%X]=%q,",depth,k,v))
1609     elseif accurate then
1610      handle(format("%s [%q]=%q,",depth,k,v))
1611     else
1612      handle(format("%s [%s]=%q,",depth,k,v))
1613     end
1614    elseif tk=="boolean" then
1615     handle(format("%s [%s]=%q,",depth,k and "true" or "false",v))
1616    elseif tk~="string" then
1617    elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
1618     handle(format("%s %s=%q,",depth,k,v))
1619    else
1620     handle(format("%s [%q]=%q,",depth,k,v))
1621    end
1622   elseif tv=="table" then
1623    if next(v)==nil then
1624     if tk=="number" then
1625      if hexify then
1626       handle(format("%s [0x%X]={},",depth,k))
1627      elseif accurate then
1628       handle(format("%s [%q]={},",depth,k))
1629      else
1630       handle(format("%s [%s]={},",depth,k))
1631      end
1632     elseif tk=="boolean" then
1633      handle(format("%s [%s]={},",depth,k and "true" or "false"))
1634     elseif tk~="string" then
1635     elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
1636      handle(format("%s %s={},",depth,k))
1637     else
1638      handle(format("%s [%q]={},",depth,k))
1639     end
1640    elseif inline then
1641     local st=is_simple_table(v,hexify,accurate)
1642     if st then
1643      if tk=="number" then
1644       if hexify then
1645        handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", ")))
1646       elseif accurate then
1647        handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
1648       else
1649        handle(format("%s [%s]={ %s },",depth,k,concat(st,", ")))
1650       end
1651      elseif tk=="boolean" then
1652       handle(format("%s [%s]={ %s },",depth,k and "true" or "false",concat(st,", ")))
1653      elseif tk~="string" then
1654      elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
1655       handle(format("%s %s={ %s },",depth,k,concat(st,", ")))
1656      else
1657       handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
1658      end
1659     else
1660      do_serialize(v,k,depth,level+1)
1661     end
1662    else
1663     do_serialize(v,k,depth,level+1)
1664    end
1665   elseif tv=="boolean" then
1666    if tk=="number" then
1667     if hexify then
1668      handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false"))
1669     elseif accurate then
1670      handle(format("%s [%q]=%s,",depth,k,v and "true" or "false"))
1671     else
1672      handle(format("%s [%s]=%s,",depth,k,v and "true" or "false"))
1673     end
1674    elseif tk=="boolean" then
1675     handle(format("%s [%s]=%s,",depth,tostring(k),v and "true" or "false"))
1676    elseif tk~="string" then
1677    elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
1678     handle(format("%s %s=%s,",depth,k,v and "true" or "false"))
1679    else
1680     handle(format("%s [%q]=%s,",depth,k,v and "true" or "false"))
1681    end
1682   elseif tv=="function" then
1683    if functions then
1684     local getinfo=debug and debug.getinfo
1685     if getinfo then
1686      local f=getinfo(v).what=="C" and dump(dummy) or dump(v)
1687      if tk=="number" then
1688       if hexify then
1689        handle(format("%s [0x%X]=load(%q),",depth,k,f))
1690       elseif accurate then
1691        handle(format("%s [%q]=load(%q),",depth,k,f))
1692       else
1693        handle(format("%s [%s]=load(%q),",depth,k,f))
1694       end
1695      elseif tk=="boolean" then
1696       handle(format("%s [%s]=load(%q),",depth,k and "true" or "false",f))
1697      elseif tk~="string" then
1698      elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
1699       handle(format("%s %s=load(%q),",depth,k,f))
1700      else
1701       handle(format("%s [%q]=load(%q),",depth,k,f))
1702      end
1703     end
1704    end
1705   else
1706    if tk=="number" then
1707     if hexify then
1708      handle(format("%s [0x%X]=%q,",depth,k,tostring(v)))
1709     elseif accurate then
1710      handle(format("%s [%q]=%q,",depth,k,tostring(v)))
1711     else
1712      handle(format("%s [%s]=%q,",depth,k,tostring(v)))
1713     end
1714    elseif tk=="boolean" then
1715     handle(format("%s [%s]=%q,",depth,k and "true" or "false",tostring(v)))
1716    elseif tk~="string" then
1717    elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
1718     handle(format("%s %s=%q,",depth,k,tostring(v)))
1719    else
1720     handle(format("%s [%q]=%q,",depth,k,tostring(v)))
1721    end
1722   end
1723  end
1724 end
1725 if level>0 then
1726  handle(format("%s},",depth))
1727 end
1728end
1729local function serialize(_handle,root,name,specification) 
1730 local tname=type(name)
1731 if type(specification)=="table" then
1732  noquotes=specification.noquotes
1733  hexify=specification.hexify
1734  accurate=specification.accurate
1735  handle=_handle or specification.handle or print
1736  functions=specification.functions
1737  compact=specification.compact
1738  inline=specification.inline and compact
1739  metacheck=specification.metacheck
1740  if functions==nil then
1741   functions=true
1742  end
1743  if compact==nil then
1744   compact=true
1745  end
1746  if inline==nil then
1747   inline=compact
1748  end
1749  if metacheck==nil then
1750   metacheck=true
1751  end
1752 else
1753  noquotes=false
1754  hexify=false
1755  handle=_handle or print
1756  compact=true
1757  inline=true
1758  functions=true
1759  metacheck=true
1760 end
1761 if tname=="string" then
1762  if name=="return" then
1763   handle("return {")
1764  else
1765   handle(name.."={")
1766  end
1767 elseif tname=="number" then
1768  if hexify then
1769   handle(format("[0x%X]={",name))
1770  else
1771   handle("["..name.."]={")
1772  end
1773 elseif tname=="boolean" then
1774  if name then
1775   handle("return {")
1776  else
1777   handle("{")
1778  end
1779 else
1780  handle("t={")
1781 end
1782 if root then
1783  if metacheck and getmetatable(root) then
1784   local dummy=root._w_h_a_t_e_v_e_r_
1785   root._w_h_a_t_e_v_e_r_=nil
1786  end
1787  if next(root)~=nil then
1788   do_serialize(root,name,"",0)
1789  end
1790 end
1791 handle("}")
1792end
1793function table.serialize(root,name,specification)
1794 local t={}
1795 local n=0
1796 local function flush(s)
1797  n=n+1
1798  t[n]=s
1799 end
1800 serialize(flush,root,name,specification)
1801 return concat(t,"\n")
1802end
1803table.tohandle=serialize
1804local maxtab=2*1024
1805function table.tofile(filename,root,name,specification)
1806 local f=io.open(filename,'w')
1807 if f then
1808  if maxtab>1 then
1809   local t={}
1810   local n=0
1811   local function flush(s)
1812    n=n+1
1813    t[n]=s
1814    if n>maxtab then
1815     f:write(concat(t,"\n"),"\n") 
1816     t={} 
1817     n=0
1818    end
1819   end
1820   serialize(flush,root,name,specification)
1821   f:write(concat(t,"\n"),"\n")
1822  else
1823   local function flush(s)
1824    f:write(s,"\n")
1825   end
1826   serialize(flush,root,name,specification)
1827  end
1828  f:close()
1829  io.flush()
1830 end
1831end
1832local function flattened(t,f,depth) 
1833 if f==nil then
1834  f={}
1835  depth=0xFFFF
1836 elseif tonumber(f) then
1837  depth=f
1838  f={}
1839 elseif not depth then
1840  depth=0xFFFF
1841 end
1842 for k,v in next,t do
1843  if type(k)~="number" then
1844   if depth>0 and type(v)=="table" then
1845    flattened(v,f,depth-1)
1846   else
1847    f[#f+1]=v
1848   end
1849  end
1850 end
1851 for k=1,#t do
1852  local v=t[k]
1853  if depth>0 and type(v)=="table" then
1854   flattened(v,f,depth-1)
1855  else
1856   f[#f+1]=v
1857  end
1858 end
1859 return f
1860end
1861table.flattened=flattened
1862local function collapsed(t,f,h)
1863 if f==nil then
1864  f={}
1865  h={}
1866 end
1867 for k=1,#t do
1868  local v=t[k]
1869  if type(v)=="table" then
1870   collapsed(v,f,h)
1871  elseif not h[v] then
1872   f[#f+1]=v
1873   h[v]=true
1874  end
1875 end
1876 return f
1877end
1878local function collapsedhash(t,h)
1879 if h==nil then
1880  h={}
1881 end
1882 for k=1,#t do
1883  local v=t[k]
1884  if type(v)=="table" then
1885   collapsedhash(v,h)
1886  else
1887   h[v]=true
1888  end
1889 end
1890 return h
1891end
1892table.collapsed=collapsed  
1893table.collapsedhash=collapsedhash
1894local function unnest(t,f) 
1895 if not f then    
1896  f={}   
1897 end
1898 for i=1,#t do
1899  local v=t[i]
1900  if type(v)=="table" then
1901   if type(v[1])=="table" then
1902    unnest(v,f)
1903   else
1904    f[#f+1]=v
1905   end
1906  else
1907   f[#f+1]=v
1908  end
1909 end
1910 return f
1911end
1912function table.unnest(t) 
1913 return unnest(t)
1914end
1915local function are_equal(a,b,n,m) 
1916 if a==b then
1917  return true
1918 elseif a and b and #a==#b then
1919  if not n then
1920   n=1
1921  end
1922  if not m then
1923   m=#a
1924  end
1925  for i=n,m do
1926   local ai,bi=a[i],b[i]
1927   if ai==bi then
1928   elseif type(ai)=="table" and type(bi)=="table" then
1929    if not are_equal(ai,bi) then
1930     return false
1931    end
1932   else
1933    return false
1934   end
1935  end
1936  return true
1937 else
1938  return false
1939 end
1940end
1941local function identical(a,b) 
1942 if a~=b then
1943  for ka,va in next,a do
1944   local vb=b[ka]
1945   if va==vb then
1946   elseif type(va)=="table" and  type(vb)=="table" then
1947    if not identical(va,vb) then
1948     return false
1949    end
1950   else
1951    return false
1952   end
1953  end
1954 end
1955 return true
1956end
1957table.identical=identical
1958table.are_equal=are_equal
1959local function sparse(old,nest,keeptables)
1960 local new={}
1961 for k,v in next,old do
1962  if not (v=="" or v==false) then
1963   if nest and type(v)=="table" then
1964    v=sparse(v,nest)
1965    if keeptables or next(v)~=nil then
1966     new[k]=v
1967    end
1968   else
1969    new[k]=v
1970   end
1971  end
1972 end
1973 return new
1974end
1975table.sparse=sparse
1976function table.compact(t)
1977 return sparse(t,true,true)
1978end
1979function table.contains(t,v)
1980 if t then
1981  for i=1,#t do
1982   if t[i]==v then
1983    return i
1984   end
1985  end
1986 end
1987 return false
1988end
1989function table.count(t)
1990 local n=0
1991 for k,v in next,t do
1992  n=n+1
1993 end
1994 return n
1995end
1996function table.swapped(t,s) 
1997 local n={}
1998 if s then
1999  for k,v in next,s do
2000   n[k]=v
2001  end
2002 end
2003 for k,v in next,t do
2004  n[v]=k
2005 end
2006 return n
2007end
2008function table.hashed(t) 
2009 for i=1,#t do
2010  t[t[i]]=i
2011 end
2012 return t
2013end
2014function table.mirrored(t) 
2015 local n={}
2016 for k,v in next,t do
2017  n[v]=k
2018  n[k]=v
2019 end
2020 return n
2021end
2022function table.reversed(t)
2023 if t then
2024  local tt={}
2025  local tn=#t
2026  if tn>0 then
2027   local ttn=0
2028   for i=tn,1,-1 do
2029    ttn=ttn+1
2030    tt[ttn]=t[i]
2031   end
2032  end
2033  return tt
2034 end
2035end
2036function table.reverse(t) 
2037 if t then
2038  local n=#t
2039  local m=n+1
2040  for i=1,floor(n/2) do 
2041   local j=m-i
2042   t[i],t[j]=t[j],t[i]
2043  end
2044  return t
2045 end
2046end
2047local function sequenced(t,sep,simple)
2048 if not t then
2049  return ""
2050 elseif type(t)~="table" then
2051  return t 
2052 end
2053 local n=#t
2054 local s={}
2055 if n>0 then
2056  for i=1,n do
2057   local v=t[i]
2058   if type(v)=="table" then
2059    s[i]="{"..sequenced(v,sep,simple).."}"
2060   else
2061    s[i]=tostring(t[i])
2062   end
2063  end
2064 else
2065  n=0
2066  for k,v in sortedhash(t) do
2067   if simple then
2068    if v==true then
2069     n=n+1
2070     s[n]=k
2071    elseif v and v~="" then
2072     n=n+1
2073     if type(v)=="table" then
2074      s[n]=k.."={"..sequenced(v,sep,simple).."}"
2075     else
2076      s[n]=k.."="..tostring(v)
2077     end
2078    end
2079   else
2080    n=n+1
2081    if type(v)=="table" then
2082     s[n]=k.."={"..sequenced(v,sep,simple).."}"
2083    else
2084     s[n]=k.."="..tostring(v)
2085    end
2086   end
2087  end
2088 end
2089 if sep==true then
2090  return "{ "..concat(s,", ").." }"
2091 else
2092  return concat(s,sep or " | ")
2093 end
2094end
2095table.sequenced=sequenced
2096function table.print(t,...)
2097 if type(t)~="table" then
2098  print(tostring(t))
2099 else
2100  serialize(print,t,...)
2101 end
2102end
2103if setinspector then
2104 setinspector("table",function(v) if type(v)=="table" then serialize(print,v,"table") return true end end)
2105end
2106function table.sub(t,i,j)
2107 return { unpack(t,i,j) }
2108end
2109function table.is_empty(t)
2110 return not t or next(t)==nil
2111end
2112function table.has_one_entry(t)
2113 return t and next(t,next(t))==nil
2114end
2115function table.loweredkeys(t) 
2116 local l={}
2117 for k,v in next,t do
2118  l[lower(k)]=v
2119 end
2120 return l
2121end
2122function table.unique(old)
2123 local hash={}
2124 local new={}
2125 local n=0
2126 for i=1,#old do
2127  local oi=old[i]
2128  if not hash[oi] then
2129   n=n+1
2130   new[n]=oi
2131   hash[oi]=true
2132  end
2133 end
2134 return new
2135end
2136function table.sorted(t,...)
2137 sort(t,...)
2138 return t 
2139end
2140function table.values(t,s) 
2141 if t then
2142  local values={}
2143  local keys={}
2144  local v=0
2145  for key,value in next,t do
2146   if not keys[value] then
2147    v=v+1
2148    values[v]=value
2149    keys[k]=key
2150   end
2151  end
2152  if s then
2153   sort(values)
2154  end
2155  return values
2156 else
2157  return {}
2158 end
2159end
2160function table.filtered(t,pattern,sort,cmp)
2161 if t and type(pattern)=="string" then
2162  if sort then
2163   local s
2164   if cmp then
2165    s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
2166   else
2167    s=sortedkeys(t) 
2168   end
2169   local n=0
2170   local m=#s
2171   local function kv(s)
2172    while n<m do
2173     n=n+1
2174     local k=s[n]
2175     if find(k,pattern) then
2176      return k,t[k]
2177     end
2178    end
2179   end
2180   return kv,s
2181  else
2182   local n=next(t)
2183   local function iterator()
2184    while n~=nil do
2185     local k=n
2186     n=next(t,k)
2187     if find(k,pattern) then
2188      return k,t[k]
2189     end
2190    end
2191   end
2192   return iterator,t
2193  end
2194 else
2195  return nothing
2196 end
2197end
2198if not table.move then
2199 function table.move(a1,f,e,t,a2)
2200  if a2 and a1~=a2 then
2201   for i=f,e do
2202    a2[t]=a1[i]
2203    t=t+1
2204   end
2205   return a2
2206  else
2207   t=t+e-f
2208   for i=e,f,-1 do
2209    a1[t]=a1[i]
2210    t=t-1
2211   end
2212   return a1
2213  end
2214 end
2215end
2216
2217end -- closure
2218
2219do -- begin closure to overcome local limits and interference
2220
2221if not modules then modules={} end modules ['l-io']={
2222 version=1.001,
2223 comment="companion to luat-lib.mkiv",
2224 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
2225 copyright="PRAGMA ADE / ConTeXt Development Team",
2226 license="see context related readme files"
2227}
2228local io=io
2229local open,flush,write,read=io.open,io.flush,io.write,io.read
2230local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format
2231local concat=table.concat
2232local type=type
2233if string.find(os.getenv("PATH") or "",";",1,true) then
2234 io.fileseparator,io.pathseparator="\\",";"
2235else
2236 io.fileseparator,io.pathseparator="/",":"
2237end
2238local large=0x01000000 
2239local medium=0x00100000 
2240local small=0x00020000
2241local function readall(f)
2242 local size=f:seek("end")
2243 if size>0 then
2244  f:seek("set",0)
2245  return f:read(size)
2246 else
2247  return ""
2248 end
2249end
2250io.readall=readall
2251function io.loaddata(filename,textmode) 
2252 local f=open(filename,(textmode and 'r') or 'rb')
2253 if f then
2254  local size=f:seek("end")
2255  local data=nil
2256  if size>0 then
2257   f:seek("set",0)
2258   data=f:read(size)
2259  end
2260  f:close()
2261  return data
2262 end
2263end
2264function io.copydata(source,target,action)
2265 local f=open(source,"rb")
2266 if f then
2267  local g=open(target,"wb")
2268  if g then
2269   local size=f:seek("end")
2270   if size>0 then
2271    f:seek("set",0)
2272    local data=f:read(size)
2273    if action then
2274     data=action(data)
2275    end
2276    if data then
2277     g:write(data)
2278    end
2279   end
2280   g:close()
2281  end
2282  f:close()
2283  flush()
2284 end
2285end
2286function io.savedata(filename,data,joiner,append)
2287 local f=open(filename,append and "ab" or "wb")
2288 if f then
2289  if append and joiner and f:seek("end")>0 then
2290   f:write(joiner)
2291  end
2292  if type(data)=="table" then
2293   f:write(concat(data,joiner or ""))
2294  elseif type(data)=="function" then
2295   data(f)
2296  else
2297   f:write(data or "")
2298  end
2299  f:close()
2300  flush()
2301  return true
2302 else
2303  return false
2304 end
2305end
2306if fio and fio.readline then
2307 local readline=fio.readline
2308 function io.loadlines(filename,n) 
2309  local f=open(filename,'r')
2310  if not f then
2311  elseif n then
2312   local lines={}
2313   for i=1,n do
2314    local line=readline(f)
2315    if line then
2316     lines[i]=line
2317    else
2318     break
2319    end
2320   end
2321   f:close()
2322   lines=concat(lines,"\n")
2323   if #lines>0 then
2324    return lines
2325   end
2326  else
2327   local line=readline(f)
2328   f:close()
2329   if line and #line>0 then
2330    return line
2331   end
2332  end
2333 end
2334else
2335 function io.loadlines(filename,n) 
2336  local f=open(filename,'r')
2337  if not f then
2338  elseif n then
2339   local lines={}
2340   for i=1,n do
2341    local line=f:read("*lines")
2342    if line then
2343     lines[i]=line
2344    else
2345     break
2346    end
2347   end
2348   f:close()
2349   lines=concat(lines,"\n")
2350   if #lines>0 then
2351    return lines
2352   end
2353  else
2354   local line=f:read("*line") or ""
2355   f:close()
2356   if #line>0 then
2357    return line
2358   end
2359  end
2360 end
2361end
2362function io.loadchunk(filename,n)
2363 local f=open(filename,'rb')
2364 if f then
2365  local data=f:read(n or 1024)
2366  f:close()
2367  if #data>0 then
2368   return data
2369  end
2370 end
2371end
2372function io.exists(filename)
2373 local f=open(filename)
2374 if f==nil then
2375  return false
2376 else
2377  f:close()
2378  return true
2379 end
2380end
2381function io.size(filename)
2382 local f=open(filename)
2383 if f==nil then
2384  return 0
2385 else
2386  local s=f:seek("end")
2387  f:close()
2388  return s
2389 end
2390end
2391local function noflines(f)
2392 if type(f)=="string" then
2393  local f=open(filename)
2394  if f then
2395   local n=f and noflines(f) or 0
2396   f:close()
2397   return n
2398  else
2399   return 0
2400  end
2401 else
2402  local n=0
2403  for _ in f:lines() do
2404   n=n+1
2405  end
2406  f:seek('set',0)
2407  return n
2408 end
2409end
2410io.noflines=noflines
2411local nextchar={
2412 [ 4]=function(f)
2413  return f:read(1,1,1,1)
2414 end,
2415 [ 2]=function(f)
2416  return f:read(1,1)
2417 end,
2418 [ 1]=function(f)
2419  return f:read(1)
2420 end,
2421 [-2]=function(f)
2422  local a,b=f:read(1,1)
2423  return b,a
2424 end,
2425 [-4]=function(f)
2426  local a,b,c,d=f:read(1,1,1,1)
2427  return d,c,b,a
2428 end
2429}
2430function io.characters(f,n)
2431 if f then
2432  return nextchar[n or 1],f
2433 end
2434end
2435local nextbyte={
2436 [4]=function(f)
2437  local a,b,c,d=f:read(1,1,1,1)
2438  if d then
2439   return byte(a),byte(b),byte(c),byte(d)
2440  end
2441 end,
2442 [3]=function(f)
2443  local a,b,c=f:read(1,1,1)
2444  if b then
2445   return byte(a),byte(b),byte(c)
2446  end
2447 end,
2448 [2]=function(f)
2449  local a,b=f:read(1,1)
2450  if b then
2451   return byte(a),byte(b)
2452  end
2453 end,
2454 [1]=function (f)
2455  local a=f:read(1)
2456  if a then
2457   return byte(a)
2458  end
2459 end,
2460 [-2]=function (f)
2461  local a,b=f:read(1,1)
2462  if b then
2463   return byte(b),byte(a)
2464  end
2465 end,
2466 [-3]=function(f)
2467  local a,b,c=f:read(1,1,1)
2468  if b then
2469   return byte(c),byte(b),byte(a)
2470  end
2471 end,
2472 [-4]=function(f)
2473  local a,b,c,d=f:read(1,1,1,1)
2474  if d then
2475   return byte(d),byte(c),byte(b),byte(a)
2476  end
2477 end
2478}
2479function io.bytes(f,n)
2480 if f then
2481  return nextbyte[n or 1],f
2482 else
2483  return nil,nil
2484 end
2485end
2486function io.ask(question,default,options)
2487 while true do
2488  write(question)
2489  if options then
2490   write(format(" [%s]",concat(options,"|")))
2491  end
2492  if default then
2493   write(format(" [%s]",default))
2494  end
2495  write(format(" "))
2496  flush()
2497  local answer=read()
2498  answer=gsub(answer,"^%s*(.*)%s*$","%1")
2499  if answer=="" and default then
2500   return default
2501  elseif not options then
2502   return answer
2503  else
2504   for k=1,#options do
2505    if options[k]==answer then
2506     return answer
2507    end
2508   end
2509   local pattern="^"..answer
2510   for k=1,#options do
2511    local v=options[k]
2512    if find(v,pattern) then
2513     return v
2514    end
2515   end
2516  end
2517 end
2518end
2519local function readnumber(f,n,m) 
2520 if m then
2521  f:seek("set",n)
2522  n=m
2523 end
2524 if n==1 then
2525  return byte(f:read(1))
2526 elseif n==2 then
2527  local a,b=byte(f:read(2),1,2)
2528  return 0x100*a+b
2529 elseif n==3 then
2530  local a,b,c=byte(f:read(3),1,3)
2531  return 0x10000*a+0x100*b+c
2532 elseif n==4 then
2533  local a,b,c,d=byte(f:read(4),1,4)
2534  return 0x1000000*a+0x10000*b+0x100*c+d
2535 elseif n==8 then
2536  local a,b=readnumber(f,4),readnumber(f,4)
2537  return 0x100*a+b
2538 elseif n==12 then
2539  local a,b,c=readnumber(f,4),readnumber(f,4),readnumber(f,4)
2540  return 0x10000*a+0x100*b+c
2541 elseif n==-2 then
2542  local b,a=byte(f:read(2),1,2)
2543  return 0x100*a+b
2544 elseif n==-3 then
2545  local c,b,a=byte(f:read(3),1,3)
2546  return 0x10000*a+0x100*b+c
2547 elseif n==-4 then
2548  local d,c,b,a=byte(f:read(4),1,4)
2549  return 0x1000000*a+0x10000*b+0x100*c+d
2550 elseif n==-8 then
2551  local h,g,f,e,d,c,b,a=byte(f:read(8),1,8)
2552  return 0x100000000000000*a+0x1000000000000*b+0x10000000000*c+0x100000000*d+0x1000000*e+0x10000*f+0x100*g+h
2553 else
2554  return 0
2555 end
2556end
2557io.readnumber=readnumber
2558function io.readstring(f,n,m)
2559 if m then
2560  f:seek("set",n)
2561  n=m
2562 end
2563 local str=gsub(f:read(n),"\000","")
2564 return str
2565end
2566
2567end -- closure
2568
2569do -- begin closure to overcome local limits and interference
2570
2571if not modules then modules={} end modules ['l-file']={
2572 version=1.001,
2573 comment="companion to luat-lib.mkiv",
2574 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
2575 copyright="PRAGMA ADE / ConTeXt Development Team",
2576 license="see context related readme files"
2577}
2578file=file or {}
2579local file=file
2580if not lfs then
2581 lfs=optionalrequire("lfs")
2582end
2583local insert,concat=table.insert,table.concat
2584local match,find,gmatch=string.match,string.find,string.gmatch
2585local lpegmatch=lpeg.match
2586local getcurrentdir,attributes=lfs.currentdir,lfs.attributes
2587local checkedsplit=string.checkedsplit
2588local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct
2589local attributes=lfs.attributes
2590function lfs.isdir(name)
2591 if name then
2592  return attributes(name,"mode")=="directory"
2593 end
2594end
2595function lfs.isfile(name)
2596 if name then
2597  local a=attributes(name,"mode")
2598  return a=="file" or a=="link" or nil
2599 end
2600end
2601function lfs.isfound(name)
2602 if name then
2603  local a=attributes(name,"mode")
2604  return (a=="file" or a=="link") and name or nil
2605 end
2606end
2607function lfs.modification(name)
2608 return name and attributes(name,"modification") or nil
2609end
2610if sandbox then
2611 sandbox.redefine(lfs.isfile,"lfs.isfile")
2612 sandbox.redefine(lfs.isdir,"lfs.isdir")
2613 sandbox.redefine(lfs.isfound,"lfs.isfound")
2614end
2615local colon=P(":")
2616local period=P(".")
2617local periods=P("..")
2618local fwslash=P("/")
2619local bwslash=P("\\")
2620local slashes=S("\\/")
2621local noperiod=1-period
2622local noslashes=1-slashes
2623local name=noperiod^1
2624local suffix=period/""*(1-period-slashes)^1*-1
2625local pattern=C((1-(slashes^1*noslashes^1*-1))^1)*P(1) 
2626local function pathpart(name,default)
2627 return name and lpegmatch(pattern,name) or default or ""
2628end
2629local pattern=(noslashes^0*slashes)^1*C(noslashes^1)*-1
2630local function basename(name)
2631 return name and lpegmatch(pattern,name) or name
2632end
2633local pattern=(noslashes^0*slashes^1)^0*Cs((1-suffix)^1)*suffix^0
2634local function nameonly(name)
2635 return name and lpegmatch(pattern,name) or name
2636end
2637local pattern=(noslashes^0*slashes)^0*(noperiod^1*period)^1*C(noperiod^1)*-1
2638local function suffixonly(name)
2639 return name and lpegmatch(pattern,name) or ""
2640end
2641local pattern=(noslashes^0*slashes)^0*noperiod^1*((period*C(noperiod^1))^1)*-1+Cc("")
2642local function suffixesonly(name)
2643 if name then
2644  return lpegmatch(pattern,name)
2645 else
2646  return ""
2647 end
2648end
2649file.pathpart=pathpart
2650file.basename=basename
2651file.nameonly=nameonly
2652file.suffixonly=suffixonly
2653file.suffix=suffixonly
2654file.suffixesonly=suffixesonly
2655file.suffixes=suffixesonly
2656file.dirname=pathpart   
2657file.extname=suffixonly
2658local drive=C(R("az","AZ"))*colon
2659local path=C((noslashes^0*slashes)^0)
2660local suffix=period*C(P(1-period)^0*P(-1))
2661local base=C((1-suffix)^0)
2662local rest=C(P(1)^0)
2663drive=drive+Cc("")
2664path=path+Cc("")
2665base=base+Cc("")
2666suffix=suffix+Cc("")
2667local pattern_a=drive*path*base*suffix
2668local pattern_b=path*base*suffix
2669local pattern_c=C(drive*path)*C(base*suffix) 
2670local pattern_d=path*rest
2671function file.splitname(str,splitdrive)
2672 if not str then
2673 elseif splitdrive then
2674  return lpegmatch(pattern_a,str) 
2675 else
2676  return lpegmatch(pattern_b,str) 
2677 end
2678end
2679function file.splitbase(str)
2680 if str then
2681  return lpegmatch(pattern_d,str) 
2682 else
2683  return "",str 
2684 end
2685end
2686function file.nametotable(str,splitdrive)
2687 if str then
2688  local path,drive,subpath,name,base,suffix=lpegmatch(pattern_c,str)
2689  if splitdrive then
2690   return {
2691    path=path,
2692    drive=drive,
2693    subpath=subpath,
2694    name=name,
2695    base=base,
2696    suffix=suffix,
2697   }
2698  else
2699   return {
2700    path=path,
2701    name=name,
2702    base=base,
2703    suffix=suffix,
2704   }
2705  end
2706 end
2707end
2708local pattern=Cs(((period*(1-period-slashes)^1*-1)/""+1)^1)
2709function file.removesuffix(name)
2710 return name and lpegmatch(pattern,name)
2711end
2712local suffix=period/""*(1-period-slashes)^1*-1
2713local pattern=Cs((noslashes^0*slashes^1)^0*((1-suffix)^1))*Cs(suffix)
2714function file.addsuffix(filename,suffix,criterium)
2715 if not filename or not suffix or suffix=="" then
2716  return filename
2717 elseif criterium==true then
2718  return filename.."."..suffix
2719 elseif not criterium then
2720  local n,s=lpegmatch(pattern,filename)
2721  if not s or s=="" then
2722   return filename.."."..suffix
2723  else
2724   return filename
2725  end
2726 else
2727  local n,s=lpegmatch(pattern,filename)
2728  if s and s~="" then
2729   local t=type(criterium)
2730   if t=="table" then
2731    for i=1,#criterium do
2732     if s==criterium[i] then
2733      return filename
2734     end
2735    end
2736   elseif t=="string" then
2737    if s==criterium then
2738     return filename
2739    end
2740   end
2741  end
2742  return (n or filename).."."..suffix
2743 end
2744end
2745local suffix=period*(1-period-slashes)^1*-1
2746local pattern=Cs((1-suffix)^0)
2747function file.replacesuffix(name,suffix)
2748 if name and suffix and suffix~="" then
2749  return lpegmatch(pattern,name).."."..suffix
2750 else
2751  return name
2752 end
2753end
2754local reslasher=lpeg.replacer(P("\\"),"/")
2755function file.reslash(str)
2756 return str and lpegmatch(reslasher,str)
2757end
2758if lfs.isreadablefile and lfs.iswritablefile then
2759 file.is_readable=lfs.isreadablefile
2760 file.is_writable=lfs.iswritablefile
2761else
2762 function file.is_writable(name)
2763  if not name then
2764  elseif lfs.isdir(name) then
2765   name=name.."/m_t_x_t_e_s_t.tmp"
2766   local f=io.open(name,"wb")
2767   if f then
2768    f:close()
2769    os.remove(name)
2770    return true
2771   end
2772  elseif lfs.isfile(name) then
2773   local f=io.open(name,"ab")
2774   if f then
2775    f:close()
2776    return true
2777   end
2778  else
2779   local f=io.open(name,"ab")
2780   if f then
2781    f:close()
2782    os.remove(name)
2783    return true
2784   end
2785  end
2786  return false
2787 end
2788 local readable=P("r")*Cc(true)
2789 function file.is_readable(name)
2790  if name then
2791   local a=attributes(name)
2792   return a and lpegmatch(readable,a.permissions) or false
2793  else
2794   return false
2795  end
2796 end
2797end
2798file.isreadable=file.is_readable 
2799file.iswritable=file.is_writable 
2800function file.size(name)
2801 if name then
2802  local a=attributes(name)
2803  return a and a.size or 0
2804 else
2805  return 0
2806 end
2807end
2808function file.splitpath(str,separator) 
2809 return str and checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator)
2810end
2811function file.joinpath(tab,separator) 
2812 return tab and concat(tab,separator or io.pathseparator) 
2813end
2814local someslash=S("\\/")
2815local stripper=Cs(P(fwslash)^0/""*reslasher)
2816local isnetwork=someslash*someslash*(1-someslash)+(1-fwslash-colon)^1*colon
2817local isroot=fwslash^1*-1
2818local hasroot=fwslash^1
2819local reslasher=lpeg.replacer(S("\\/"),"/")
2820local deslasher=lpeg.replacer(S("\\/")^1,"/")
2821function file.join(one,two,three,...)
2822 if not two then
2823  return one=="" and one or lpegmatch(reslasher,one)
2824 end
2825 if not one or one=="" then
2826  return lpegmatch(stripper,three and concat({ two,three,... },"/") or two)
2827 end
2828 if lpegmatch(isnetwork,one) then
2829  local one=lpegmatch(reslasher,one)
2830  local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two)
2831  if lpegmatch(hasroot,two) then
2832   return one..two
2833  else
2834   return one.."/"..two
2835  end
2836 elseif lpegmatch(isroot,one) then
2837  local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two)
2838  if lpegmatch(hasroot,two) then
2839   return two
2840  else
2841   return "/"..two
2842  end
2843 else
2844  return lpegmatch(deslasher,concat({  one,two,three,... },"/"))
2845 end
2846end
2847local drivespec=R("az","AZ")^1*colon
2848local anchors=fwslash+drivespec
2849local untouched=periods+(1-period)^1*P(-1)
2850local mswindrive=Cs(drivespec*(bwslash/"/"+fwslash)^0)
2851local mswinuncpath=(bwslash+fwslash)*(bwslash+fwslash)*Cc("//")
2852local splitstarter=(mswindrive+mswinuncpath+Cc(false))*Ct(lpeg.splitat(S("/\\")^1))
2853local absolute=fwslash
2854function file.collapsepath(str,anchor) 
2855 if not str then
2856  return
2857 end
2858 if anchor==true and not lpegmatch(anchors,str) then
2859  str=getcurrentdir().."/"..str
2860 end
2861 if str=="" or str=="." then
2862  return "."
2863 elseif lpegmatch(untouched,str) then
2864  return lpegmatch(reslasher,str)
2865 end
2866 local starter,oldelements=lpegmatch(splitstarter,str)
2867 local newelements={}
2868 local i=#oldelements
2869 while i>0 do
2870  local element=oldelements[i]
2871  if element=='.' then
2872  elseif element=='..' then
2873   local n=i-1
2874   while n>0 do
2875    local element=oldelements[n]
2876    if element~='..' and element~='.' then
2877     oldelements[n]='.'
2878     break
2879    else
2880     n=n-1
2881    end
2882    end
2883   if n<1 then
2884      insert(newelements,1,'..')
2885   end
2886  elseif element~="" then
2887   insert(newelements,1,element)
2888  end
2889  i=i-1
2890 end
2891 if #newelements==0 then
2892  return starter or "."
2893 elseif starter then
2894  return starter..concat(newelements,'/')
2895 elseif lpegmatch(absolute,str) then
2896  return "/"..concat(newelements,'/')
2897 else
2898  newelements=concat(newelements,'/')
2899  if anchor=="." and find(str,"^%./") then
2900   return "./"..newelements
2901  else
2902   return newelements
2903  end
2904 end
2905end
2906local validchars=R("az","09","AZ","--","..")
2907local pattern_a=lpeg.replacer(1-validchars)
2908local pattern_a=Cs((validchars+P(1)/"-")^1)
2909local whatever=P("-")^0/""
2910local pattern_b=Cs(whatever*(1-whatever*-1)^1)
2911function file.robustname(str,strict)
2912 if str then
2913  str=lpegmatch(pattern_a,str) or str
2914  if strict then
2915   return lpegmatch(pattern_b,str) or str 
2916  else
2917   return str
2918  end
2919 end
2920end
2921local loaddata=io.loaddata
2922local savedata=io.savedata
2923file.readdata=loaddata
2924file.savedata=savedata
2925function file.copy(oldname,newname)
2926 if oldname and newname then
2927  local data=loaddata(oldname)
2928  if data and data~="" then
2929   savedata(newname,data)
2930  end
2931 end
2932end
2933local letter=R("az","AZ")+S("_-+")
2934local separator=P("://")
2935local qualified=period^0*fwslash+letter*colon+letter^1*separator+letter^1*fwslash
2936local rootbased=fwslash+letter*colon
2937lpeg.patterns.qualified=qualified
2938lpeg.patterns.rootbased=rootbased
2939function file.is_qualified_path(filename)
2940 return filename and lpegmatch(qualified,filename)~=nil
2941end
2942function file.is_rootbased_path(filename)
2943 return filename and lpegmatch(rootbased,filename)~=nil
2944end
2945function file.strip(name,dir)
2946 if name then
2947  local b,a=match(name,"^(.-)"..dir.."(.*)$")
2948  return a~="" and a or name
2949 end
2950end
2951function lfs.mkdirs(path)
2952 local full=""
2953 for sub in gmatch(path,"(/*[^\\/]+)") do 
2954  full=full..sub
2955  lfs.mkdir(full)
2956 end
2957end
2958function file.withinbase(path) 
2959 local l=0
2960 if not find(path,"^/") then
2961  path="/"..path
2962 end
2963 for dir in gmatch(path,"/([^/]+)") do
2964  if dir==".." then
2965   l=l-1
2966  elseif dir~="." then
2967   l=l+1
2968  end
2969  if l<0 then
2970   return false
2971  end
2972 end
2973 return true
2974end
2975do
2976 local symlinktarget=lfs.symlinktarget  
2977 local symlinkattributes=lfs.symlinkattributes 
2978 if symlinktarget then
2979  function lfs.readlink(name)
2980   local target=symlinktarget(name)
2981   return name~=target and name or nil
2982  end
2983 elseif symlinkattributes then
2984  function lfs.readlink(name)
2985   return symlinkattributes(name,"target") or nil
2986  end
2987 else
2988  function lfs.readlink(name)
2989   return nil
2990  end
2991 end
2992end
2993
2994end -- closure
2995
2996do -- begin closure to overcome local limits and interference
2997
2998if not modules then modules={} end modules ['l-boolean']={
2999 version=1.001,
3000 comment="companion to luat-lib.mkiv",
3001 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
3002 copyright="PRAGMA ADE / ConTeXt Development Team",
3003 license="see context related readme files"
3004}
3005local type,tonumber=type,tonumber
3006boolean=boolean or {}
3007local boolean=boolean
3008function boolean.tonumber(b)
3009 if b then return 1 else return 0 end 
3010end
3011function toboolean(str,tolerant) 
3012 if  str==nil then
3013  return false
3014 elseif str==false then
3015  return false
3016 elseif str==true then
3017  return true
3018 elseif str=="true" then
3019  return true
3020 elseif str=="false" then
3021  return false
3022 elseif not tolerant then
3023  return false
3024 elseif str==0 then
3025  return false
3026 elseif (tonumber(str) or 0)>0 then
3027  return true
3028 else
3029  return str=="yes" or str=="on" or str=="t"
3030 end
3031end
3032string.toboolean=toboolean
3033function string.booleanstring(str)
3034 if str=="0" then
3035  return false
3036 elseif str=="1" then
3037  return true
3038 elseif str=="" then
3039  return false
3040 elseif str=="false" then
3041  return false
3042 elseif str=="true" then
3043  return true
3044 elseif (tonumber(str) or 0)>0 then
3045  return true
3046 else
3047  return str=="yes" or str=="on" or str=="t"
3048 end
3049end
3050function string.is_boolean(str,default,strict)
3051 if type(str)=="string" then
3052  if str=="true" or str=="yes" or str=="on" or str=="t" or (not strict and str=="1") then
3053   return true
3054  elseif str=="false" or str=="no" or str=="off" or str=="f" or (not strict and str=="0") then
3055   return false
3056  end
3057 end
3058 return default
3059end
3060
3061end -- closure
3062
3063do -- begin closure to overcome local limits and interference
3064
3065if not modules then modules={} end modules ['l-math']={
3066 version=1.001,
3067 comment="companion to luat-lib.mkiv",
3068 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
3069 copyright="PRAGMA ADE / ConTeXt Development Team",
3070 license="see context related readme files"
3071}
3072if not math.ceiling then
3073 math.ceiling=math.ceil
3074end
3075if not math.round then
3076 if xmath then
3077  math.round=xmath.round
3078 else
3079  local floor=math.floor
3080  function math.round(x)
3081   return x<0 and -floor(-x+0.5) or floor(x+0.5)
3082  end
3083 end
3084end
3085if not math.div then
3086 local floor=math.floor
3087 function math.div(n,m) return floor(n/m) end
3088end
3089if not math.mod then
3090 function math.mod(n,m) return n%m end
3091end
3092if not math.sind then
3093 local sin,cos,tan=math.sin,math.cos,math.tan
3094 local pipi=2*math.pi/360
3095 function math.sind(d) return sin(d*pipi) end
3096 function math.cosd(d) return cos(d*pipi) end
3097 function math.tand(d) return tan(d*pipi) end
3098end
3099if not math.odd then
3100 function math.odd (n) return n%2~=0 end
3101 function math.even(n) return n%2==0 end
3102end
3103if not math.cosh then
3104 local exp=math.exp
3105 function math.cosh(x)
3106  local xx=exp(x)
3107  return (xx+1/xx)/2
3108 end
3109 function math.sinh(x)
3110  local xx=exp(x)
3111  return (xx-1/xx)/2
3112 end
3113 function math.tanh(x)
3114  local xx=exp(x)
3115  return (xx-1/xx)/(xx+1/xx)
3116 end
3117end
3118if not math.pow then
3119 function math.pow(x,y)
3120  return x^y
3121 end
3122end
3123if not math.atan2 then
3124 math.atan2=math.atan
3125end
3126if not math.ldexp then
3127 function math.ldexp(x,e)
3128  return x*2.0^e
3129 end
3130end
3131if not math.log10 then
3132 local log=math.log
3133 function math.log10(x)
3134  return log(x,10)
3135 end
3136end
3137if not math.type then
3138 function math.type()
3139  return "float"
3140 end
3141end
3142if not math.tointeger then
3143 math.mininteger=-0x4FFFFFFFFFFF
3144 math.maxinteger=0x4FFFFFFFFFFF
3145 local floor=math.floor
3146 function math.tointeger(n)
3147  local f=floor(n)
3148  return f==n and f or nil
3149 end
3150end
3151if not math.ult then
3152 local floor=math.floor
3153 function math.ult(m,n)
3154  return floor(m)<floor(n) 
3155 end
3156end
3157
3158end -- closure
3159
3160do -- begin closure to overcome local limits and interference
3161
3162if not modules then modules={} end modules ['util-str']={
3163 version=1.001,
3164 comment="companion to luat-lib.mkiv",
3165 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
3166 copyright="PRAGMA ADE / ConTeXt Development Team",
3167 license="see context related readme files"
3168}
3169utilities=utilities or {}
3170utilities.strings=utilities.strings or {}
3171local strings=utilities.strings
3172local format,gsub,rep,sub,find,char=string.format,string.gsub,string.rep,string.sub,string.find,string.char
3173local load,dump=load,string.dump
3174local tonumber,type,tostring,next,setmetatable=tonumber,type,tostring,next,setmetatable
3175local unpack,concat=table.unpack,table.concat
3176local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc
3177local patterns,lpegmatch=lpeg.patterns,lpeg.match
3178local tsplitat=lpeg.tsplitat
3179local utfchar,utfbyte,utflen=utf.char,utf.byte,utf.len
3180local loadstripped=function(str,shortcuts)
3181 if shortcuts then
3182  return load(dump(load(str),true),nil,nil,shortcuts)
3183 else
3184  return load(dump(load(str),true))
3185 end
3186end
3187if not number then number={} end 
3188local stripzero=patterns.stripzero
3189local stripzeros=patterns.stripzeros
3190local newline=patterns.newline
3191local endofstring=patterns.endofstring
3192local anything=patterns.anything
3193local whitespace=patterns.whitespace
3194local space=patterns.space
3195local spacer=patterns.spacer
3196local spaceortab=patterns.spaceortab
3197local digit=patterns.digit
3198local sign=patterns.sign
3199local period=patterns.period
3200local ptf=1/65536
3201local bpf=(7200/7227)/65536
3202local function points(n)
3203 if n==0 then
3204  return "0pt"
3205 end
3206 n=tonumber(n)
3207 if not n or n==0 then
3208  return "0pt"
3209 end
3210 n=n*ptf
3211 if n%1==0 then
3212  return format("%ipt",n)
3213 else
3214  return lpegmatch(stripzeros,format("%.5fpt",n)) 
3215 end
3216end
3217local function nupoints(n)
3218 if n==0 then
3219  return "0"
3220 end
3221 n=tonumber(n)
3222 if not n or n==0 then
3223  return "0"
3224 end
3225 n=n*ptf
3226 if n%1==0 then
3227  return format("%i",n)
3228 else
3229  return format("%.5f",n) 
3230 end
3231end
3232local function basepoints(n)
3233 if n==0 then
3234  return "0bp"
3235 end
3236 n=tonumber(n)
3237 if not n or n==0 then
3238  return "0bp"
3239 end
3240 n=n*bpf
3241 if n%1==0 then
3242  return format("%ibp",n)
3243 else
3244  return lpegmatch(stripzeros,format("%.5fbp",n)) 
3245 end
3246end
3247local function nubasepoints(n)
3248 if n==0 then
3249  return "0"
3250 end
3251 n=tonumber(n)
3252 if not n or n==0 then
3253  return "0"
3254 end
3255 n=n*bpf
3256 if n%1==0 then
3257  return format("%i",n)
3258 else
3259  return format("%.5f",n) 
3260 end
3261end
3262number.points=points
3263number.nupoints=nupoints
3264number.basepoints=basepoints
3265number.nubasepoints=nubasepoints
3266local rubish=spaceortab^0*newline
3267local anyrubish=spaceortab+newline
3268local stripped=(spaceortab^1/"")*newline
3269local leading=rubish^0/""
3270local trailing=(anyrubish^1*endofstring)/""
3271local redundant=rubish^3/"\n"
3272local pattern=Cs(leading*(trailing+redundant+stripped+anything)^0)
3273function strings.collapsecrlf(str)
3274 return lpegmatch(pattern,str)
3275end
3276local repeaters={} 
3277function strings.newrepeater(str,offset)
3278 offset=offset or 0
3279 local s=repeaters[str]
3280 if not s then
3281  s={}
3282  repeaters[str]=s
3283 end
3284 local t=s[offset]
3285 if t then
3286  return t
3287 end
3288 t={}
3289 setmetatable(t,{ __index=function(t,k)
3290  if not k then
3291   return ""
3292  end
3293  local n=k+offset
3294  local s=n>0 and rep(str,n) or ""
3295  t[k]=s
3296  return s
3297 end })
3298 s[offset]=t
3299 return t
3300end
3301local extra,tab,start=0,0,4,0
3302local nspaces=strings.newrepeater(" ")
3303string.nspaces=nspaces
3304local pattern=Carg(1)/function(t)
3305  extra,tab,start=0,t or 7,1
3306 end*Cs((
3307   Cp()*patterns.tab/function(position)
3308    local current=(position-start+1)+extra
3309    local spaces=tab-(current-1)%tab
3310    if spaces>0 then
3311     extra=extra+spaces-1
3312     return nspaces[spaces] 
3313    else
3314     return ""
3315    end
3316   end+newline*Cp()/function(position)
3317    extra,start=0,position
3318   end+anything
3319  )^1)
3320function strings.tabtospace(str,tab)
3321 return lpegmatch(pattern,str,1,tab or 7)
3322end
3323function string.utfpadding(s,n)
3324 if not n or n==0 then
3325  return ""
3326 end
3327 local l=utflen(s)
3328 if n>0 then
3329  return nspaces[n-l]
3330 else
3331  return nspaces[-n-l]
3332 end
3333end
3334local optionalspace=spacer^0
3335local nospace=optionalspace/""
3336local endofline=nospace*newline
3337local stripend=(whitespace^1*endofstring)/""
3338local normalline=(nospace*((1-optionalspace*(newline+endofstring))^1)*nospace)
3339local stripempty=endofline^1/""
3340local normalempty=endofline^1
3341local singleempty=endofline*(endofline^0/"")
3342local doubleempty=endofline*endofline^-1*(endofline^0/"")
3343local stripstart=stripempty^0
3344local intospace=whitespace^1/" "
3345local noleading=whitespace^1/""
3346local notrailing=noleading*endofstring
3347local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 )
3348local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 )
3349local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 )
3350local p_prune_intospace=Cs (noleading*(notrailing+intospace+1     )^0 )
3351local p_retain_normal=Cs ((normalline+normalempty )^0 )
3352local p_retain_collapse=Cs ((normalline+doubleempty )^0 )
3353local p_retain_noempty=Cs ((normalline+singleempty )^0 )
3354local p_collapse_all=Cs (stripstart*(stripend+((whitespace+newline)^1/" ")+1)^0 )
3355local striplinepatterns={
3356 ["prune"]=p_prune_normal,
3357 ["prune and collapse"]=p_prune_collapse,
3358 ["prune and no empty"]=p_prune_noempty,
3359 ["prune and to space"]=p_prune_intospace,
3360 ["retain"]=p_retain_normal,
3361 ["retain and collapse"]=p_retain_collapse,
3362 ["retain and no empty"]=p_retain_noempty,
3363 ["collapse all"]=p_collapse_all,
3364 ["collapse"]=patterns.collapser,
3365}
3366setmetatable(striplinepatterns,{ __index=function(t,k) return p_prune_collapse end })
3367strings.striplinepatterns=striplinepatterns
3368function strings.striplines(str,how)
3369 return str and lpegmatch(striplinepatterns[how],str) or str
3370end
3371function strings.collapse(str) 
3372 return str and lpegmatch(p_prune_intospace,str) or str
3373end
3374strings.striplong=strings.striplines
3375function strings.nice(str)
3376 str=gsub(str,"[:%-+_]+"," ") 
3377 return str
3378end
3379local n=0
3380local sequenced=table.sequenced
3381function string.autodouble(s,sep)
3382 if s==nil then
3383  return '""'
3384 end
3385 local t=type(s)
3386 if t=="number" then
3387  return tostring(s) 
3388 end
3389 if t=="table" then
3390  return ('"'..sequenced(s,sep or ",")..'"')
3391 end
3392 return ('"'..tostring(s)..'"')
3393end
3394function string.autosingle(s,sep)
3395 if s==nil then
3396  return "''"
3397 end
3398 local t=type(s)
3399 if t=="number" then
3400  return tostring(s) 
3401 end
3402 if t=="table" then
3403  return ("'"..sequenced(s,sep or ",").."'")
3404 end
3405 return ("'"..tostring(s).."'")
3406end
3407local tracedchars={ [0]=
3408 "[null]","[soh]","[stx]","[etx]","[eot]","[enq]","[ack]","[bel]",
3409 "[bs]","[ht]","[lf]","[vt]","[ff]","[cr]","[so]","[si]",
3410 "[dle]","[dc1]","[dc2]","[dc3]","[dc4]","[nak]","[syn]","[etb]",
3411 "[can]","[em]","[sub]","[esc]","[fs]","[gs]","[rs]","[us]",
3412 "[space]",
3413}
3414string.tracedchars=tracedchars
3415strings.tracers=tracedchars
3416function string.tracedchar(b)
3417 if type(b)=="number" then
3418  return tracedchars[b] or (utfchar(b).." (U+"..format("%05X",b)..")")
3419 else
3420  local c=utfbyte(b)
3421  return tracedchars[c] or (b.." (U+"..(c and format("%05X",c) or "?????")..")")
3422 end
3423end
3424function number.signed(i)
3425 if i>0 then
3426  return "+",i
3427 else
3428  return "-",-i
3429 end
3430end
3431local two=digit*digit
3432local three=two*digit
3433local prefix=(Carg(1)*three)^1
3434local splitter=Cs (
3435 (((1-(three^1*period))^1+C(three))*prefix+C((1-period)^1))*(anything/""*Carg(2))*C(2)
3436)
3437local splitter3=Cs (
3438 three*prefix*endofstring+two*prefix*endofstring+digit*prefix*endofstring+three+two+digit
3439)
3440patterns.formattednumber=splitter
3441function number.formatted(n,sep1,sep2)
3442 if sep1==false then
3443  if type(n)=="number" then
3444   n=tostring(n)
3445  end
3446  return lpegmatch(splitter3,n,1,sep2 or ".")
3447 else
3448  if type(n)=="number" then
3449   n=format("%0.2f",n)
3450  end
3451  if sep1==true then
3452   return lpegmatch(splitter,n,1,".",",")
3453  elseif sep1=="." then
3454   return lpegmatch(splitter,n,1,sep1,sep2 or ",")
3455  elseif sep1=="," then
3456   return lpegmatch(splitter,n,1,sep1,sep2 or ".")
3457  else
3458   return lpegmatch(splitter,n,1,sep1 or ",",sep2 or ".")
3459  end
3460 end
3461end
3462local p=Cs(
3463  P("-")^0*(P("0")^1/"")^0*(1-period)^0*(period*P("0")^1*endofstring/""+period^0)*P(1-P("0")^1*endofstring)^0
3464 )
3465function number.compactfloat(n,fmt)
3466 if n==0 then
3467  return "0"
3468 elseif n==1 then
3469  return "1"
3470 end
3471 n=lpegmatch(p,format(fmt or "%0.3f",n))
3472 if n=="." or n=="" or n=="-" then
3473  return "0"
3474 end
3475 return n
3476end
3477local zero=P("0")^1/""
3478local plus=P("+")/""
3479local minus=P("-")
3480local separator=period
3481local trailing=zero^1*#S("eE")
3482local exponent=(S("eE")*(plus+Cs((minus*zero^0*endofstring)/"")+minus)*zero^0*(endofstring*Cc("0")+anything^1))
3483local pattern_a=Cs(minus^0*digit^1*(separator/""*trailing+separator*(trailing+digit)^0)*exponent)
3484local pattern_b=Cs((exponent+anything)^0)
3485function number.sparseexponent(f,n)
3486 if not n then
3487  n=f
3488  f="%e"
3489 end
3490 local tn=type(n)
3491 if tn=="string" then 
3492  local m=tonumber(n)
3493  if m then
3494   return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,m))
3495  end
3496 elseif tn=="number" then
3497  return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,n))
3498 end
3499 return tostring(n)
3500end
3501local hf={}
3502local hs={}
3503setmetatable(hf,{ __index=function(t,k)
3504 local v="%."..k.."f"
3505 t[k]=v
3506 return v
3507end } )
3508setmetatable(hs,{ __index=function(t,k)
3509 local v="%"..k.."s"
3510 t[k]=v
3511 return v
3512end } )
3513function number.formattedfloat(n,b,a)
3514 local s=format(hf[a],n)
3515 local l=(b or 0)+(a or 0)+1
3516 if #s<l then
3517  return format(hs[l],s)
3518 else
3519  return s
3520 end
3521end
3522local template=[[
3523%s
3524%s
3525return function(%s) return %s end
3526]]
3527local pattern=Cs(Cc('"')*(
3528 (1-S('"\\\n\r'))^1+P('"')/'\\"'+P('\\')/'\\\\'+P('\n')/'\\n'+P('\r')/'\\r'
3529)^0*Cc('"'))
3530patterns.escapedquotes=pattern
3531function string.escapedquotes(s)
3532 return lpegmatch(pattern,s)
3533end
3534local pattern=(1-P("\\"))^1;pattern=Cs (
3535 pattern*((P("\\")/""*(digit^-3/function(s) return char(tonumber(s)) end))+pattern )^1
3536)
3537patterns.unescapedquotes=pattern
3538function string.unescapedquotes(s)
3539 return lpegmatch(pattern,s) or s
3540end
3541string.texnewlines=lpeg.replacer(patterns.newline,"\r",true)
3542local preamble=""
3543local environment={
3544 global=global or _G,
3545 lpeg=lpeg,
3546 type=type,
3547 tostring=tostring,
3548 tonumber=tonumber,
3549 format=string.format,
3550 concat=table.concat,
3551 signed=number.signed,
3552 points=number.points,
3553 nupoints=number.nupoints,
3554 basepoints=number.basepoints,
3555 nubasepoints=number.nubasepoints,
3556 utfchar=utf.char,
3557 utfbyte=utf.byte,
3558 lpegmatch=lpeg.match,
3559 nspaces=string.nspaces,
3560 utfpadding=string.utfpadding,
3561 tracedchar=string.tracedchar,
3562 autosingle=string.autosingle,
3563 autodouble=string.autodouble,
3564 sequenced=table.sequenced,
3565 formattednumber=number.formatted,
3566 sparseexponent=number.sparseexponent,
3567 formattedfloat=number.formattedfloat,
3568 stripzero=patterns.stripzero,
3569 stripzeros=patterns.stripzeros,
3570 escapedquotes=string.escapedquotes,
3571 FORMAT=string.f6,
3572}
3573local arguments={ "a1" } 
3574setmetatable(arguments,{ __index=function(t,k)
3575  local v=t[k-1]..",a"..k
3576  t[k]=v
3577  return v
3578 end
3579})
3580local prefix_any=C((sign+space+period+digit)^0)
3581local prefix_sub=(C((sign+digit)^0)+Cc(0))*period*(C((sign+digit)^0)+Cc(0))
3582local prefix_tab=P("{")*C((1-P("}"))^0)*P("}")+C((1-R("az","AZ","09","%%"))^0)
3583local format_s=function(f)
3584 n=n+1
3585 if f and f~="" then
3586  return format("format('%%%ss',a%s)",f,n)
3587 else 
3588  return format("(a%s or '')",n) 
3589 end
3590end
3591local format_S=function(f) 
3592 n=n+1
3593 if f and f~="" then
3594  return format("format('%%%ss',tostring(a%s))",f,n)
3595 else
3596  return format("tostring(a%s)",n)
3597 end
3598end
3599local format_right=function(f)
3600 n=n+1
3601 f=tonumber(f)
3602 if not f or f==0 then
3603  return format("(a%s or '')",n)
3604 elseif f>0 then
3605  return format("utfpadding(a%s,%i)..a%s",n,f,n)
3606 else
3607  return format("a%s..utfpadding(a%s,%i)",n,n,f)
3608 end
3609end
3610local format_left=function(f)
3611 n=n+1
3612 f=tonumber(f)
3613 if not f or f==0 then
3614  return format("(a%s or '')",n)
3615 end
3616 if f<0 then
3617  return format("utfpadding(a%s,%i)..a%s",n,-f,n)
3618 else
3619  return format("a%s..utfpadding(a%s,%i)",n,n,-f)
3620 end
3621end
3622local format_q=JITSUPPORTED and function()
3623 n=n+1
3624 return format("(a%s ~= nil and format('%%q',tostring(a%s)) or '')",n,n)
3625end or function()
3626 n=n+1
3627 return format("(a%s ~= nil and format('%%q',a%s) or '')",n,n)
3628end
3629local format_Q=function() 
3630 n=n+1
3631 return format("escapedquotes(tostring(a%s))",n)
3632end
3633local format_i=function(f)
3634 n=n+1
3635 if f and f~="" then
3636  return format("format('%%%si',a%s)",f,n)
3637 else
3638  return format("format('%%i',a%s)",n) 
3639 end
3640end
3641local format_d=format_i
3642local format_I=function(f)
3643 n=n+1
3644 return format("format('%%s%%%si',signed(a%s))",f,n)
3645end
3646local format_f=function(f)
3647 n=n+1
3648 return format("format('%%%sf',a%s)",f,n)
3649end
3650local format_F=function(f) 
3651 n=n+1
3652 if not f or f=="" then
3653  return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s))",n,n,n,n)
3654 else
3655  return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n)
3656 end
3657end
3658local format_k=function(b,a) 
3659 n=n+1
3660 return format("formattedfloat(a%s,%s,%s)",n,b or 0,a or 0)
3661end
3662local format_g=function(f)
3663 n=n+1
3664 return format("format('%%%sg',a%s)",f,n)
3665end
3666local format_G=function(f)
3667 n=n+1
3668 return format("format('%%%sG',a%s)",f,n)
3669end
3670local format_e=function(f)
3671 n=n+1
3672 return format("format('%%%se',a%s)",f,n)
3673end
3674local format_E=function(f)
3675 n=n+1
3676 return format("format('%%%sE',a%s)",f,n)
3677end
3678local format_j=function(f)
3679 n=n+1
3680 return format("sparseexponent('%%%se',a%s)",f,n)
3681end
3682local format_J=function(f)
3683 n=n+1
3684 return format("sparseexponent('%%%sE',a%s)",f,n)
3685end
3686local format_x=function(f)
3687 n=n+1
3688 return format("format('%%%sx',a%s)",f,n)
3689end
3690local format_X=function(f)
3691 n=n+1
3692 return format("format('%%%sX',a%s)",f,n)
3693end
3694local format_o=function(f)
3695 n=n+1
3696 return format("format('%%%so',a%s)",f,n)
3697end
3698local format_c=function()
3699 n=n+1
3700 return format("utfchar(a%s)",n)
3701end
3702local format_C=function()
3703 n=n+1
3704 return format("tracedchar(a%s)",n)
3705end
3706local format_r=function(f)
3707 n=n+1
3708 return format("format('%%%s.0f',a%s)",f,n)
3709end
3710local format_h=function(f)
3711 n=n+1
3712 if f=="-" then
3713  f=sub(f,2)
3714  return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
3715 else
3716  return format("format('0x%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
3717 end
3718end
3719local format_H=function(f)
3720 n=n+1
3721 if f=="-" then
3722  f=sub(f,2)
3723  return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
3724 else
3725  return format("format('0x%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
3726 end
3727end
3728local format_u=function(f)
3729 n=n+1
3730 if f=="-" then
3731  f=sub(f,2)
3732  return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
3733 else
3734  return format("format('u+%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
3735 end
3736end
3737local format_U=function(f)
3738 n=n+1
3739 if f=="-" then
3740  f=sub(f,2)
3741  return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
3742 else
3743  return format("format('U+%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
3744 end
3745end
3746local format_p=function()
3747 n=n+1
3748 return format("points(a%s)",n)
3749end
3750local format_P=function()
3751 n=n+1
3752 return format("nupoints(a%s)",n)
3753end
3754local format_b=function()
3755 n=n+1
3756 return format("basepoints(a%s)",n)
3757end
3758local format_B=function()
3759 n=n+1
3760 return format("nubasepoints(a%s)",n)
3761end
3762local format_t=function(f)
3763 n=n+1
3764 if f and f~="" then
3765  return format("concat(a%s,%q)",n,f)
3766 else
3767  return format("concat(a%s)",n)
3768 end
3769end
3770local format_T=function(f)
3771 n=n+1
3772 if f and f~="" then
3773  return format("sequenced(a%s,%q)",n,f)
3774 else
3775  return format("sequenced(a%s)",n)
3776 end
3777end
3778local format_l=function()
3779 n=n+1
3780 return format("(a%s and 'true' or 'false')",n)
3781end
3782local format_L=function()
3783 n=n+1
3784 return format("(a%s and 'TRUE' or 'FALSE')",n)
3785end
3786local format_n=function() 
3787 n=n+1
3788 return format("((a%s %% 1 == 0) and format('%%i',a%s) or tostring(a%s))",n,n,n)
3789end
3790local format_N  if environment.FORMAT then
3791 format_N=function(f)
3792  n=n+1
3793  if not f or f=="" then
3794   return format("FORMAT(a%s,'%%.9f')",n)
3795  elseif f==".6" or f=="0.6" then
3796   return format("FORMAT(a%s)",n)
3797  else
3798   return format("FORMAT(a%s,'%%%sf')",n,f)
3799  end
3800 end
3801else
3802 format_N=function(f) 
3803  n=n+1
3804  if not f or f=="" then
3805   f=".9"
3806  end 
3807  return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%%sf',a%s)))",n,n,f,n)
3808 end
3809end
3810local format_a=function(f)
3811 n=n+1
3812 if f and f~="" then
3813  return format("autosingle(a%s,%q)",n,f)
3814 else
3815  return format("autosingle(a%s)",n)
3816 end
3817end
3818local format_A=function(f)
3819 n=n+1
3820 if f and f~="" then
3821  return format("autodouble(a%s,%q)",n,f)
3822 else
3823  return format("autodouble(a%s)",n)
3824 end
3825end
3826local format_w=function(f) 
3827 n=n+1
3828 f=tonumber(f)
3829 if f then 
3830  return format("nspaces[%s+a%s]",f,n) 
3831 else
3832  return format("nspaces[a%s]",n) 
3833 end
3834end
3835local format_W=function(f) 
3836 return format("nspaces[%s]",tonumber(f) or 0)
3837end
3838local format_m=function(f)
3839 n=n+1
3840 if not f or f=="" then
3841  f=","
3842 end
3843 if f=="0" then
3844  return format([[formattednumber(a%s,false)]],n)
3845 else
3846  return format([[formattednumber(a%s,%q,".")]],n,f)
3847 end
3848end
3849local format_M=function(f)
3850 n=n+1
3851 if not f or f=="" then
3852  f="."
3853 end
3854 if f=="0" then
3855  return format([[formattednumber(a%s,false)]],n)
3856 else
3857  return format([[formattednumber(a%s,%q,",")]],n,f)
3858 end
3859end
3860local format_z=function(f)
3861 n=n+(tonumber(f) or 1)
3862 return "''" 
3863end
3864local format_rest=function(s)
3865 return format("%q",s) 
3866end
3867local format_extension=function(extensions,f,name)
3868 local extension=extensions[name] or "tostring(%s)"
3869 local f=tonumber(f) or 1
3870 local w=find(extension,"%.%.%.")
3871 if f==0 then
3872  if w then
3873   extension=gsub(extension,"%.%.%.","")
3874  end
3875  return extension
3876 elseif f==1 then
3877  if w then
3878   extension=gsub(extension,"%.%.%.","%%s")
3879  end
3880  n=n+1
3881  local a="a"..n
3882  return format(extension,a,a) 
3883 elseif f<0 then
3884  if w then
3885   extension=gsub(extension,"%.%.%.","")
3886   return extension
3887  else
3888   local a="a"..(n+f+1)
3889   return format(extension,a,a)
3890  end
3891 else
3892  if w then
3893   extension=gsub(extension,"%.%.%.",rep("%%s,",f-1).."%%s")
3894  end
3895  local t={}
3896  for i=1,f do
3897   n=n+1
3898   t[i]="a"..n
3899  end
3900  return format(extension,unpack(t))
3901 end
3902end
3903local builder=Cs { "start",
3904 start=(
3905  (
3906   P("%")/""*(
3907    V("!") 
3908+V("s")+V("q")+V("i")+V("d")+V("f")+V("F")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o")
3909+V("c")+V("C")+V("S") 
3910+V("Q") 
3911+V("n") 
3912+V("N") 
3913+V("k")
3914+V("r")+V("h")+V("H")+V("u")+V("U")+V("p")+V("P")+V("b")+V("B")+V("t")+V("T")+V("l")+V("L")+V("I")+V("w") 
3915+V("W") 
3916+V("a") 
3917+V("A") 
3918+V("j")+V("J") 
3919+V("m")+V("M") 
3920+V("z")
3921+V(">") 
3922+V("<")
3923   )+V("*")
3924  )*(endofstring+Carg(1))
3925 )^0,
3926 ["s"]=(prefix_any*P("s"))/format_s,
3927 ["q"]=(prefix_any*P("q"))/format_q,
3928 ["i"]=(prefix_any*P("i"))/format_i,
3929 ["d"]=(prefix_any*P("d"))/format_d,
3930 ["f"]=(prefix_any*P("f"))/format_f,
3931 ["F"]=(prefix_any*P("F"))/format_F,
3932 ["g"]=(prefix_any*P("g"))/format_g,
3933 ["G"]=(prefix_any*P("G"))/format_G,
3934 ["e"]=(prefix_any*P("e"))/format_e,
3935 ["E"]=(prefix_any*P("E"))/format_E,
3936 ["x"]=(prefix_any*P("x"))/format_x,
3937 ["X"]=(prefix_any*P("X"))/format_X,
3938 ["o"]=(prefix_any*P("o"))/format_o,
3939 ["S"]=(prefix_any*P("S"))/format_S,
3940 ["Q"]=(prefix_any*P("Q"))/format_Q,
3941 ["n"]=(prefix_any*P("n"))/format_n,
3942 ["N"]=(prefix_any*P("N"))/format_N,
3943 ["k"]=(prefix_sub*P("k"))/format_k,
3944 ["c"]=(prefix_any*P("c"))/format_c,
3945 ["C"]=(prefix_any*P("C"))/format_C,
3946 ["r"]=(prefix_any*P("r"))/format_r,
3947 ["h"]=(prefix_any*P("h"))/format_h,
3948 ["H"]=(prefix_any*P("H"))/format_H,
3949 ["u"]=(prefix_any*P("u"))/format_u,
3950 ["U"]=(prefix_any*P("U"))/format_U,
3951 ["p"]=(prefix_any*P("p"))/format_p,
3952 ["P"]=(prefix_any*P("P"))/format_P,
3953 ["b"]=(prefix_any*P("b"))/format_b,
3954 ["B"]=(prefix_any*P("B"))/format_B,
3955 ["t"]=(prefix_tab*P("t"))/format_t,
3956 ["T"]=(prefix_tab*P("T"))/format_T,
3957 ["l"]=(prefix_any*P("l"))/format_l,
3958 ["L"]=(prefix_any*P("L"))/format_L,
3959 ["I"]=(prefix_any*P("I"))/format_I,
3960 ["w"]=(prefix_any*P("w"))/format_w,
3961 ["W"]=(prefix_any*P("W"))/format_W,
3962 ["j"]=(prefix_any*P("j"))/format_j,
3963 ["J"]=(prefix_any*P("J"))/format_J,
3964 ["m"]=(prefix_any*P("m"))/format_m,
3965 ["M"]=(prefix_any*P("M"))/format_M,
3966 ["z"]=(prefix_any*P("z"))/format_z,
3967 ["a"]=(prefix_any*P("a"))/format_a,
3968 ["A"]=(prefix_any*P("A"))/format_A,
3969 ["<"]=(prefix_any*P("<"))/format_left,
3970 [">"]=(prefix_any*P(">"))/format_right,
3971 ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest,
3972 ["?"]=Cs(((1-P("%"))^1      )^1)/format_rest,
3973 ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension,
3974}
3975local xx=setmetatable({},{ __index=function(t,k) local v=format("%02x",k) t[k]=v return v end })
3976local XX=setmetatable({},{ __index=function(t,k) local v=format("%02X",k) t[k]=v return v end })
3977local preset={
3978 ["%02x"]=function(n) return xx[n] end,
3979 ["%02X"]=function(n) return XX[n] end,
3980}
3981local direct=P("%")*(sign+space+period+digit)^0*S("sqidfgGeExXo")*endofstring/[[local format = string.format return function(str) return format("%0",str) end]]
3982local function make(t,str)
3983 local f=preset[str]
3984 if f then
3985  return f
3986 end
3987 local p=lpegmatch(direct,str)
3988 if p then
3989  f=loadstripped(p)()
3990 else
3991  n=0
3992  p=lpegmatch(builder,str,1,t._connector_,t._extensions_) 
3993  if n>0 then
3994   p=format(template,preamble,t._preamble_,arguments[n],p)
3995   f=loadstripped(p,t._environment_)() 
3996  else
3997   f=function() return str end
3998  end
3999 end
4000 t[str]=f
4001 return f
4002end
4003local function use(t,fmt,...)
4004 return t[fmt](...)
4005end
4006strings.formatters={}
4007function strings.formatters.new(noconcat)
4008 local e={} 
4009 for k,v in next,environment do
4010  e[k]=v
4011 end
4012 local t={
4013  _type_="formatter",
4014  _connector_=noconcat and "," or "..",
4015  _extensions_={},
4016  _preamble_="",
4017  _environment_=e,
4018 }
4019 setmetatable(t,{ __index=make,__call=use })
4020 return t
4021end
4022local formatters=strings.formatters.new() 
4023string.formatters=formatters 
4024string.formatter=function(str,...) return formatters[str](...) end 
4025local function add(t,name,template,preamble)
4026 if type(t)=="table" and t._type_=="formatter" then
4027  t._extensions_[name]=template or "%s"
4028  if type(preamble)=="string" then
4029   t._preamble_=preamble.."\n"..t._preamble_ 
4030  elseif type(preamble)=="table" then
4031   for k,v in next,preamble do
4032    t._environment_[k]=v
4033   end
4034  end
4035 end
4036end
4037strings.formatters.add=add
4038patterns.xmlescape=Cs((P("<")/"&lt;"+P(">")/"&gt;"+P("&")/"&amp;"+P('"')/"&quot;"+anything)^0)
4039patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+anything)^0)
4040patterns.ctxescape=Cs((C(S("#$%\\{}|"))/"\\%1"+anything)^0)
4041patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) 
4042patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"'))
4043add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=patterns.xmlescape })
4044add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=patterns.texescape })
4045add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=patterns.luaescape })
4046local dquote=patterns.dquote 
4047local equote=patterns.escaped+dquote/'\\"'+1
4048local cquote=Cc('"')
4049local pattern=Cs(dquote*(equote-P(-2))^0*dquote)     
4050+Cs(cquote*(equote-space)^0*space*equote^0*cquote) 
4051function string.optionalquoted(str)
4052 return lpegmatch(pattern,str) or str
4053end
4054local pattern=Cs((newline/(os.newline or "\r")+1)^0)
4055function string.replacenewlines(str)
4056 return lpegmatch(pattern,str)
4057end
4058function strings.newcollector()
4059 local result,r={},0
4060 return
4061  function(fmt,str,...) 
4062   r=r+1
4063   result[r]=str==nil and fmt or formatters[fmt](str,...)
4064  end,
4065  function(connector) 
4066   if result then
4067    local str=concat(result,connector)
4068    result,r={},0
4069    return str
4070   end
4071  end
4072end
4073local f_16_16=formatters["%0.5N"]
4074function number.to16dot16(n)
4075 return f_16_16(n/65536.0)
4076end
4077if not string.explode then
4078 local p_utf=patterns.utf8character
4079 local p_check=C(p_utf)*(P("+")*Cc(true))^0
4080 local p_split=Ct(C(p_utf)^0)
4081 local p_space=Ct((C(1-P(" ")^1)+P(" ")^1)^0)
4082 function string.explode(str,symbol)
4083  if symbol=="" then
4084   return lpegmatch(p_split,str)
4085  elseif symbol then
4086   local a,b=lpegmatch(p_check,symbol)
4087   if b then
4088    return lpegmatch(tsplitat(P(a)^1),str)
4089   else
4090    return lpegmatch(tsplitat(a),str)
4091   end
4092  else
4093   return lpegmatch(p_space,str)
4094  end
4095 end
4096end
4097do
4098 local p_whitespace=patterns.whitespace^1
4099 local cache=setmetatable({},{ __index=function(t,k)
4100  local p=tsplitat(p_whitespace*P(k)*p_whitespace)
4101  local v=function(s)
4102   return lpegmatch(p,s)
4103  end
4104  t[k]=v
4105  return v
4106 end })
4107 function string.wordsplitter(s)
4108  return cache[s]
4109 end
4110end
4111if CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 then
4112 local t={
4113  ["#"]="#H",
4114  ["\n"]="#L",
4115  ['"']="#Q",
4116  ["\r"]="#R",
4117  [" "]="#S",
4118  ["\t"]="#T",
4119  ["\\"]="#X",
4120 }
4121 function string.texhashed(s)
4122  return (gsub(s,".",t))
4123 end
4124end
4125
4126end -- closure
4127
4128do -- begin closure to overcome local limits and interference
4129
4130if not modules then modules={} end modules ['util-fil']={
4131 version=1.001,
4132 optimize=true,
4133 comment="companion to luat-lib.mkiv",
4134 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
4135 copyright="PRAGMA ADE / ConTeXt Development Team",
4136 license="see context related readme files"
4137}
4138local tonumber=tonumber
4139local byte=string.byte
4140local char=string.char
4141utilities=utilities or {}
4142local files={}
4143utilities.files=files
4144local zerobased={}
4145function files.open(filename,zb)
4146 local f=io.open(filename,"rb")
4147 if f then
4148  zerobased[f]=zb or false
4149 end
4150 return f
4151end
4152function files.close(f)
4153 zerobased[f]=nil
4154 f:close()
4155end
4156function files.size(f)
4157 local current=f:seek()
4158 local size=f:seek("end")
4159 f:seek("set",current)
4160 return size
4161end
4162files.getsize=files.size
4163function files.setposition(f,n)
4164 if zerobased[f] then
4165  f:seek("set",n)
4166 else
4167  f:seek("set",n-1)
4168 end
4169end
4170function files.getposition(f)
4171 if zerobased[f] then
4172  return f:seek()
4173 else
4174  return f:seek()+1
4175 end
4176end
4177function files.look(f,n,chars)
4178 local p=f:seek()
4179 local s=f:read(n)
4180 f:seek("set",p)
4181 if chars then
4182  return s
4183 else
4184  return byte(s,1,#s)
4185 end
4186end
4187function files.skip(f,n)
4188 if n==1 then
4189  f:read(n)
4190 else
4191  f:seek("set",f:seek()+n)
4192 end
4193end
4194function files.readbyte(f)
4195 return byte(f:read(1))
4196end
4197function files.readbytes(f,n)
4198 return byte(f:read(n),1,n)
4199end
4200function files.readbytetable(f,n)
4201 local s=f:read(n or 1)
4202 return { byte(s,1,#s) } 
4203end
4204function files.readchar(f)
4205 return f:read(1)
4206end
4207function files.readstring(f,n)
4208 return f:read(n or 1)
4209end
4210function files.readinteger1(f)  
4211 local n=byte(f:read(1))
4212 if n>=0x80 then
4213  return n-0x100
4214 else
4215  return n
4216 end
4217end
4218files.readcardinal1=files.readbyte  
4219files.readcardinal=files.readcardinal1
4220files.readinteger=files.readinteger1
4221files.readsignedbyte=files.readinteger1
4222function files.readcardinal2(f)
4223 local a,b=byte(f:read(2),1,2)
4224 return 0x100*a+b
4225end
4226function files.readcardinal2le(f)
4227 local b,a=byte(f:read(2),1,2)
4228 return 0x100*a+b
4229end
4230function files.readinteger2(f)
4231 local a,b=byte(f:read(2),1,2)
4232 if a>=0x80 then
4233  return 0x100*a+b-0x10000
4234 else
4235  return 0x100*a+b
4236 end
4237end
4238function files.readinteger2le(f)
4239 local b,a=byte(f:read(2),1,2)
4240 if a>=0x80 then
4241  return 0x100*a+b-0x10000
4242 else
4243  return 0x100*a+b
4244 end
4245end
4246function files.readcardinal3(f)
4247 local a,b,c=byte(f:read(3),1,3)
4248 return 0x10000*a+0x100*b+c
4249end
4250function files.readcardinal3le(f)
4251 local c,b,a=byte(f:read(3),1,3)
4252 return 0x10000*a+0x100*b+c
4253end
4254function files.readinteger3(f)
4255 local a,b,c=byte(f:read(3),1,3)
4256 if a>=0x80 then
4257  return 0x10000*a+0x100*b+c-0x1000000
4258 else
4259  return 0x10000*a+0x100*b+c
4260 end
4261end
4262function files.readinteger3le(f)
4263 local c,b,a=byte(f:read(3),1,3)
4264 if a>=0x80 then
4265  return 0x10000*a+0x100*b+c-0x1000000
4266 else
4267  return 0x10000*a+0x100*b+c
4268 end
4269end
4270function files.readcardinal4(f)
4271 local a,b,c,d=byte(f:read(4),1,4)
4272 return 0x1000000*a+0x10000*b+0x100*c+d
4273end
4274function files.readcardinal4le(f)
4275 local d,c,b,a=byte(f:read(4),1,4)
4276 return 0x1000000*a+0x10000*b+0x100*c+d
4277end
4278function files.readinteger4(f)
4279 local a,b,c,d=byte(f:read(4),1,4)
4280 if a>=0x80 then
4281  return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000
4282 else
4283  return 0x1000000*a+0x10000*b+0x100*c+d
4284 end
4285end
4286function files.readinteger4le(f)
4287 local d,c,b,a=byte(f:read(4),1,4)
4288 if a>=0x80 then
4289  return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000
4290 else
4291  return 0x1000000*a+0x10000*b+0x100*c+d
4292 end
4293end
4294function files.readfixed2(f)
4295 local n1,n2=byte(f:read(2),1,2)
4296 if n1>=0x80 then
4297  n1=n1-0x100
4298 end
4299 return n1+n2/0xFF
4300end
4301function files.readfixed4(f)
4302 local a,b,c,d=byte(f:read(4),1,4)
4303 local n1=0x100*a+b
4304 local n2=0x100*c+d
4305 if n1>=0x8000 then
4306  n1=n1-0x10000
4307 end
4308 return n1+n2/0xFFFF
4309end
4310if bit32 then
4311 local extract=bit32.extract
4312 local band=bit32.band
4313 function files.read2dot14(f)
4314  local a,b=byte(f:read(2),1,2)
4315  if a>=0x80 then
4316   local n=-(0x100*a+b)
4317   return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0))
4318  else
4319   local n=0x100*a+b
4320   return   (extract(n,14,2)+(band(n,0x3FFF)/16384.0))
4321  end
4322 end
4323end
4324function files.skipshort(f,n)
4325 f:read(2*(n or 1))
4326end
4327function files.skiplong(f,n)
4328 f:read(4*(n or 1))
4329end
4330if bit32 then
4331 local rshift=bit32.rshift
4332 function files.writecardinal2(f,n)
4333  local a=char(n%256)
4334  n=rshift(n,8)
4335  local b=char(n%256)
4336  f:write(b,a)
4337 end
4338 function files.writecardinal4(f,n)
4339  local a=char(n%256)
4340  n=rshift(n,8)
4341  local b=char(n%256)
4342  n=rshift(n,8)
4343  local c=char(n%256)
4344  n=rshift(n,8)
4345  local d=char(n%256)
4346  f:write(d,c,b,a)
4347 end
4348 function files.writecardinal2le(f,n)
4349  local a=char(n%256)
4350  n=rshift(n,8)
4351  local b=char(n%256)
4352  f:write(a,b)
4353 end
4354 function files.writecardinal4le(f,n)
4355  local a=char(n%256)
4356  n=rshift(n,8)
4357  local b=char(n%256)
4358  n=rshift(n,8)
4359  local c=char(n%256)
4360  n=rshift(n,8)
4361  local d=char(n%256)
4362  f:write(a,b,c,d)
4363 end
4364else
4365 local floor=math.floor
4366 function files.writecardinal2(f,n)
4367  local a=char(n%256)
4368  n=floor(n/256)
4369  local b=char(n%256)
4370  f:write(b,a)
4371 end
4372 function files.writecardinal4(f,n)
4373  local a=char(n%256)
4374  n=floor(n/256)
4375  local b=char(n%256)
4376  n=floor(n/256)
4377  local c=char(n%256)
4378  n=floor(n/256)
4379  local d=char(n%256)
4380  f:write(d,c,b,a)
4381 end
4382 function files.writecardinal2le(f,n)
4383  local a=char(n%256)
4384  n=floor(n/256)
4385  local b=char(n%256)
4386  f:write(a,b)
4387 end
4388 function files.writecardinal4le(f,n)
4389  local a=char(n%256)
4390  n=floor(n/256)
4391  local b=char(n%256)
4392  n=floor(n/256)
4393  local c=char(n%256)
4394  n=floor(n/256)
4395  local d=char(n%256)
4396  f:write(a,b,c,d)
4397 end
4398end
4399function files.writestring(f,s)
4400 f:write(char(byte(s,1,#s)))
4401end
4402function files.writebyte(f,b)
4403 f:write(char(b))
4404end
4405if fio and fio.readcardinal1 then
4406 files.readcardinal1=fio.readcardinal1
4407 files.readcardinal2=fio.readcardinal2
4408 files.readcardinal3=fio.readcardinal3
4409 files.readcardinal4=fio.readcardinal4
4410 files.readcardinal1le=fio.readcardinal1le or files.readcardinal1le
4411 files.readcardinal2le=fio.readcardinal2le or files.readcardinal2le
4412 files.readcardinal3le=fio.readcardinal3le or files.readcardinal3le
4413 files.readcardinal4le=fio.readcardinal4le or files.readcardinal4le
4414 files.readinteger1=fio.readinteger1
4415 files.readinteger2=fio.readinteger2
4416 files.readinteger3=fio.readinteger3
4417 files.readinteger4=fio.readinteger4
4418 files.readinteger1le=fio.readinteger1le or files.readinteger1le
4419 files.readinteger2le=fio.readinteger2le or files.readinteger2le
4420 files.readinteger3le=fio.readinteger3le or files.readinteger3le
4421 files.readinteger4le=fio.readinteger4le or files.readinteger4le
4422 files.readfixed2=fio.readfixed2
4423 files.readfixed4=fio.readfixed4
4424 files.read2dot14=fio.read2dot14
4425 files.setposition=fio.setposition
4426 files.getposition=fio.getposition
4427 files.readbyte=files.readcardinal1
4428 files.readsignedbyte=files.readinteger1
4429 files.readcardinal=files.readcardinal1
4430 files.readinteger=files.readinteger1
4431 local skipposition=fio.skipposition
4432 files.skipposition=skipposition
4433 files.readbytes=fio.readbytes
4434 files.readbytetable=fio.readbytetable
4435 function files.skipshort(f,n)
4436  skipposition(f,2*(n or 1))
4437 end
4438 function files.skiplong(f,n)
4439  skipposition(f,4*(n or 1))
4440 end
4441end
4442if fio and fio.writecardinal1 then
4443 files.writecardinal1=fio.writecardinal1
4444 files.writecardinal2=fio.writecardinal2
4445 files.writecardinal3=fio.writecardinal3
4446 files.writecardinal4=fio.writecardinal4
4447 files.writecardinal1le=fio.writecardinal1le
4448 files.writecardinal2le=fio.writecardinal2le
4449 files.writecardinal3le=fio.writecardinal3le
4450 files.writecardinal4le=fio.writecardinal4le
4451 files.writeinteger1=fio.writeinteger1 or fio.writecardinal1
4452 files.writeinteger2=fio.writeinteger2 or fio.writecardinal2
4453 files.writeinteger3=fio.writeinteger3 or fio.writecardinal3
4454 files.writeinteger4=fio.writeinteger4 or fio.writecardinal4
4455 files.writeinteger1le=files.writeinteger1le or fio.writecardinal1le
4456 files.writeinteger2le=files.writeinteger2le or fio.writecardinal2le
4457 files.writeinteger3le=files.writeinteger3le or fio.writecardinal3le
4458 files.writeinteger4le=files.writeinteger4le or fio.writecardinal4le
4459end
4460if fio and fio.readcardinaltable then
4461 files.readcardinaltable=fio.readcardinaltable
4462 files.readintegertable=fio.readintegertable
4463else
4464 local readcardinal1=files.readcardinal1
4465 local readcardinal2=files.readcardinal2
4466 local readcardinal3=files.readcardinal3
4467 local readcardinal4=files.readcardinal4
4468 function files.readcardinaltable(f,n,b)
4469  local t={}
4470   if b==1 then for i=1,n do t[i]=readcardinal1(f) end
4471  elseif b==2 then for i=1,n do t[i]=readcardinal2(f) end
4472  elseif b==3 then for i=1,n do t[i]=readcardinal3(f) end
4473  elseif b==4 then for i=1,n do t[i]=readcardinal4(f) end end
4474  return t
4475 end
4476 local readinteger1=files.readinteger1
4477 local readinteger2=files.readinteger2
4478 local readinteger3=files.readinteger3
4479 local readinteger4=files.readinteger4
4480 function files.readintegertable(f,n,b)
4481  local t={}
4482   if b==1 then for i=1,n do t[i]=readinteger1(f) end
4483  elseif b==2 then for i=1,n do t[i]=readinteger2(f) end
4484  elseif b==3 then for i=1,n do t[i]=readinteger3(f) end
4485  elseif b==4 then for i=1,n do t[i]=readinteger4(f) end end
4486  return t
4487 end
4488end
4489
4490end -- closure
4491
4492do -- begin closure to overcome local limits and interference
4493
4494if not modules then modules={} end modules ['luat-basics-gen']={
4495 version=1.100,
4496 comment="companion to luatex-*.tex",
4497 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
4498 copyright="PRAGMA ADE / ConTeXt Development Team",
4499 license="see context related readme files"
4500}
4501if context then
4502--removed
4503
4504end
4505local match,gmatch,gsub,lower=string.match,string.gmatch,string.gsub,string.lower
4506local formatters,split,format,dump=string.formatters,string.split,string.format,string.dump
4507local loadfile,type=loadfile,type
4508local setmetatable,getmetatable,collectgarbage=setmetatable,getmetatable,collectgarbage
4509local floor=math.floor
4510local dummyfunction=function()
4511end
4512local dummyreporter=function(c)
4513 return function(f,...)
4514  local r=texio.reporter or texio.write_nl
4515  if f then
4516   r(c.." : "..(formatters or format)(f,...))
4517  else
4518   r("")
4519  end
4520 end
4521end
4522local dummyreport=function(c,f,...)
4523 local r=texio.reporter or texio.write_nl
4524 if f then
4525  r(c.." : "..(formatters or format)(f,...))
4526 else
4527  r("")
4528 end
4529end
4530statistics={
4531 register=dummyfunction,
4532 starttiming=dummyfunction,
4533 stoptiming=dummyfunction,
4534 elapsedtime=nil,
4535}
4536directives={
4537 register=dummyfunction,
4538 enable=dummyfunction,
4539 disable=dummyfunction,
4540}
4541trackers={
4542 register=dummyfunction,
4543 enable=dummyfunction,
4544 disable=dummyfunction,
4545}
4546experiments={
4547 register=dummyfunction,
4548 enable=dummyfunction,
4549 disable=dummyfunction,
4550}
4551storage={ 
4552 register=dummyfunction,
4553 shared={},
4554}
4555logs={
4556 new=dummyreporter,
4557 reporter=dummyreporter,
4558 messenger=dummyreporter,
4559 report=dummyreport,
4560}
4561callbacks={
4562 register=function(n,f)
4563  return callback.register(n,f)
4564 end,
4565}
4566utilities=utilities or {}
4567utilities.storage=utilities.storage or {
4568 allocate=function(t)
4569  return t or {}
4570 end,
4571 mark=function(t)
4572  return t or {}
4573 end,
4574}
4575utilities.parsers=utilities.parsers or {
4576 settings_to_array=function(s)
4577  return split(s,",")
4578 end,
4579 settings_to_hash=function(s)
4580  local t={}
4581  for k,v in gmatch((gsub(s,"^{(.*)}$","%1")),"([^%s,=]+)=([^%s,]+)") do
4582   t[k]=v
4583  end
4584  return t
4585 end,
4586 settings_to_hash_colon_too=function(s)
4587  local t={}
4588  for k,v in gmatch((gsub(s,"^{(.*)}$","%1")),"([^%s,=:]+)[=:]([^%s,]+)") do
4589   t[k]=v
4590  end
4591  return t
4592 end,
4593}
4594characters=characters or {
4595 data={}
4596}
4597texconfig.kpse_init=true
4598resolvers=resolvers or {} 
4599local remapper={
4600 otf="opentype fonts",
4601 ttf="truetype fonts",
4602 ttc="truetype fonts",
4603 cid="cid maps",
4604 cidmap="cid maps",
4605 pfb="type1 fonts",
4606 afm="afm",
4607 enc="enc files",
4608 lua="tex",
4609}
4610function resolvers.findfile(name,fileformat)
4611 name=gsub(name,"\\","/")
4612 if not fileformat or fileformat=="" then
4613  fileformat=file.suffix(name)
4614  if fileformat=="" then
4615   fileformat="tex"
4616  end
4617 end
4618 fileformat=lower(fileformat)
4619 fileformat=remapper[fileformat] or fileformat
4620 local found=kpse.find_file(name,fileformat)
4621 if not found or found=="" then
4622  found=kpse.find_file(name,"other text files")
4623 end
4624 return found
4625end
4626resolvers.findbinfile=resolvers.findfile
4627function resolvers.loadbinfile(filename,filetype)
4628 local data=io.loaddata(filename)
4629 return true,data,#data
4630end
4631function resolvers.resolve(s)
4632 return s
4633end
4634function resolvers.unresolve(s)
4635 return s
4636end
4637caches={}
4638local writable=nil
4639local readables={}
4640local usingjit=jit
4641if not caches.namespace or caches.namespace=="" or caches.namespace=="context" then
4642 caches.namespace='generic'
4643end
4644do
4645 local cachepaths=kpse.expand_var('$TEXMFCACHE') or ""
4646 if cachepaths=="" or cachepaths=="$TEXMFCACHE" then
4647  cachepaths=kpse.expand_var('$TEXMFVAR') or ""
4648 end
4649 if cachepaths=="" or cachepaths=="$TEXMFVAR" then
4650  cachepaths=kpse.expand_var('$VARTEXMF') or ""
4651 end
4652 if cachepaths=="" then
4653  local fallbacks={ "TMPDIR","TEMPDIR","TMP","TEMP","HOME","HOMEPATH" }
4654  for i=1,#fallbacks do
4655   cachepaths=os.getenv(fallbacks[i]) or ""
4656   if cachepath~="" and lfs.isdir(cachepath) then
4657    break
4658   end
4659  end
4660 end
4661 if cachepaths=="" then
4662  cachepaths="."
4663 end
4664 cachepaths=split(cachepaths,os.type=="windows" and ";" or ":")
4665 for i=1,#cachepaths do
4666  local cachepath=cachepaths[i]
4667  if not lfs.isdir(cachepath) then
4668   lfs.mkdirs(cachepath) 
4669   if lfs.isdir(cachepath) then
4670    logs.report("system","creating cache path '%s'",cachepath)
4671   end
4672  end
4673  if file.is_writable(cachepath) then
4674   writable=file.join(cachepath,"luatex-cache")
4675   lfs.mkdir(writable)
4676   writable=file.join(writable,caches.namespace)
4677   lfs.mkdir(writable)
4678   break
4679  end
4680 end
4681 for i=1,#cachepaths do
4682  if file.is_readable(cachepaths[i]) then
4683   readables[#readables+1]=file.join(cachepaths[i],"luatex-cache",caches.namespace)
4684  end
4685 end
4686 if not writable then
4687  logs.report("system","no writeable cache path, quiting")
4688  os.exit()
4689 elseif #readables==0 then
4690  logs.report("system","no readable cache path, quiting")
4691  os.exit()
4692 elseif #readables==1 and readables[1]==writable then
4693  logs.report("system","using cache '%s'",writable)
4694 else
4695  logs.report("system","using write cache '%s'",writable)
4696  logs.report("system","using read cache '%s'",table.concat(readables," "))
4697 end
4698end
4699function caches.getwritablepath(category,subcategory)
4700 local path=file.join(writable,category)
4701 lfs.mkdir(path)
4702 path=file.join(path,subcategory)
4703 lfs.mkdir(path)
4704 return path
4705end
4706function caches.getreadablepaths(category,subcategory)
4707 local t={}
4708 for i=1,#readables do
4709  t[i]=file.join(readables[i],category,subcategory)
4710 end
4711 return t
4712end
4713local function makefullname(path,name)
4714 if path and path~="" then
4715  return file.addsuffix(file.join(path,name),"lua"),file.addsuffix(file.join(path,name),usingjit and "lub" or "luc")
4716 end
4717end
4718function caches.is_writable(path,name)
4719 local fullname=makefullname(path,name)
4720 return fullname and file.is_writable(fullname)
4721end
4722function caches.loaddata(readables,name,writable)
4723 for i=1,#readables do
4724  local path=readables[i]
4725  local loader=false
4726  local luaname,lucname=makefullname(path,name)
4727  if lfs.isfile(lucname) then
4728   logs.report("system","loading luc file '%s'",lucname)
4729   loader=loadfile(lucname)
4730  end
4731  if not loader and lfs.isfile(luaname) then
4732   local luacrap,lucname=makefullname(writable,name)
4733   logs.report("system","compiling luc file '%s'",lucname)
4734   if lfs.isfile(lucname) then
4735    loader=loadfile(lucname)
4736   end
4737   caches.compile(data,luaname,lucname)
4738   if lfs.isfile(lucname) then
4739    logs.report("system","loading luc file '%s'",lucname)
4740    loader=loadfile(lucname)
4741   else
4742    logs.report("system","error in loading luc file '%s'",lucname)
4743   end
4744   if not loader then
4745    logs.report("system","loading lua file '%s'",luaname)
4746    loader=loadfile(luaname)
4747   else
4748    logs.report("system","error in loading lua file '%s'",luaname)
4749   end
4750  end
4751  if loader then
4752   loader=loader()
4753   collectgarbage("step")
4754   return loader
4755  end
4756 end
4757 return false
4758end
4759function caches.savedata(path,name,data)
4760 local luaname,lucname=makefullname(path,name)
4761 if luaname then
4762  logs.report("system","saving lua file '%s'",luaname)
4763  table.tofile(luaname,data,true)
4764  if lucname and type(caches.compile)=="function" then
4765   os.remove(lucname) 
4766   logs.report("system","saving luc file '%s'",lucname)
4767   caches.compile(data,luaname,lucname)
4768  end
4769 end
4770end
4771function caches.compile(data,luaname,lucname)
4772 local d=io.loaddata(luaname)
4773 if not d or d=="" then
4774  d=table.serialize(data,true) 
4775 end
4776 if d and d~="" then
4777  local f=io.open(lucname,'wb')
4778  if f then
4779   local s=loadstring(d)
4780   if s then
4781    f:write(dump(s,true))
4782   end
4783   f:close()
4784  end
4785 end
4786end
4787function table.setmetatableindex(t,f)
4788 if type(t)~="table" then
4789  f,t=t,{}
4790 end
4791 local m=getmetatable(t)
4792 if f=="table" then
4793  f=function(t,k) local v={} t[k]=v return v end
4794 end
4795 if m then
4796  m.__index=f
4797 else
4798  setmetatable(t,{ __index=f })
4799 end
4800 return t
4801end
4802function table.makeweak(t)
4803 local m=getmetatable(t)
4804 if m then
4805  m.__mode="v"
4806 else
4807  setmetatable(t,{ __mode="v" })
4808 end
4809 return t
4810end
4811arguments={}
4812if arg then
4813 for i=1,#arg do
4814  local k,v=match(arg[i],"^%-%-([^=]+)=?(.-)$")
4815  if k and v then
4816   arguments[k]=v
4817  end
4818 end
4819end
4820if not number.idiv then
4821 function number.idiv(i,d)
4822  return floor(i/d) 
4823 end
4824end
4825local u=unicode and unicode.utf8
4826if u then
4827 utf.lower=u.lower
4828 utf.upper=u.upper
4829 utf.char=u.char
4830 utf.byte=u.byte
4831 utf.len=u.len
4832 if lpeg.setutfcasers then
4833  lpeg.setutfcasers(u.lower,u.upper)
4834 end
4835 local bytepairs=string.bytepairs
4836 local utfchar=utf.char
4837 local concat=table.concat
4838 function utf.utf16_to_utf8_be(s)
4839  if not s then
4840   return nil
4841  elseif s=="" then
4842   return ""
4843  end
4844  local result,r,more={},0,0
4845  for left,right in bytepairs(s) do
4846   if right then
4847    local now=256*left+right
4848    if more>0 then
4849     now=(more-0xD800)*0x400+(now-0xDC00)+0x10000
4850     more=0
4851     r=r+1
4852     result[r]=utfchar(now)
4853    elseif now>=0xD800 and now<=0xDBFF then
4854     more=now
4855    else
4856     r=r+1
4857     result[r]=utfchar(now)
4858    end
4859   end
4860  end
4861  return concat(result)
4862 end
4863 local characters=string.utfcharacters
4864 function utf.split(str)
4865  local t,n={},0
4866  for s in characters(str) do
4867   n=n+1
4868   t[n]=s
4869  end
4870  return t
4871 end
4872end
4873
4874end -- closure
4875
4876do -- begin closure to overcome local limits and interference
4877
4878if not modules then modules={} end modules ['data-con']={
4879 version=1.100,
4880 comment="companion to luat-lib.mkiv",
4881 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
4882 copyright="PRAGMA ADE / ConTeXt Development Team",
4883 license="see context related readme files"
4884}
4885local setmetatable=setmetatable
4886local format,lower,gsub=string.format,string.lower,string.gsub
4887local trace_cache=false  trackers.register("resolvers.cache",function(v) trace_cache=v end)
4888local trace_containers=false  trackers.register("resolvers.containers",function(v) trace_containers=v end)
4889local trace_storage=false  trackers.register("resolvers.storage",function(v) trace_storage=v end)
4890containers=containers or {}
4891local containers=containers
4892containers.usecache=true
4893local getwritablepath=caches.getwritablepath
4894local getreadablepaths=caches.getreadablepaths
4895local cacheiswritable=caches.is_writable
4896local loaddatafromcache=caches.loaddata
4897local savedataincache=caches.savedata
4898local report_containers=logs.reporter("resolvers","containers")
4899local allocated={}
4900local cache_format=1.001 
4901local mt={
4902 __index=function(t,k)
4903  if k=="writable" then
4904   local writable=getwritablepath(t.category,t.subcategory) or { "." }
4905   t.writable=writable
4906   return writable
4907  elseif k=="readables" then
4908   local readables=getreadablepaths(t.category,t.subcategory) or { "." }
4909   t.readables=readables
4910   return readables
4911  end
4912 end,
4913 __storage__=true
4914}
4915function containers.define(category,subcategory,version,enabled,reload)
4916 if category and subcategory then
4917  local c=allocated[category]
4918  if not c then
4919   c={}
4920   allocated[category]=c
4921  end
4922  local s=c[subcategory]
4923  if not s then
4924   s={
4925    category=category,
4926    subcategory=subcategory,
4927    storage={},
4928    enabled=enabled,
4929    reload=reload,
4930    version=version or math.pi,
4931    trace=false,
4932   }
4933   setmetatable(s,mt)
4934   c[subcategory]=s
4935  end
4936  return s
4937 end
4938end
4939function containers.is_usable(container,name)
4940 return container.enabled and caches and cacheiswritable(container.writable,name)
4941end
4942function containers.is_valid(container,name)
4943 if name and name~="" then
4944  local storage=container.storage[name]
4945  return storage
4946   and storage.cache_format==cache_format 
4947   and storage.cache_version==container.version
4948 else
4949  return false
4950 end
4951end
4952function containers.read(container,name)
4953 local storage=container.storage
4954 local reload=container.reload
4955 local stored=not reload and storage[name]
4956 if not stored and container.enabled and caches and containers.usecache then
4957  stored=loaddatafromcache(container.readables,name,container.writable)
4958  if stored and stored.cache_format==cache_format and stored.cache_version==container.version then
4959   if trace_cache or trace_containers then
4960    report_containers("action %a, category %a, name %a","load",container.subcategory,name)
4961   end
4962  else
4963   stored=nil
4964  end
4965  storage[name]=stored
4966 elseif stored then
4967  if trace_cache or trace_containers then
4968   report_containers("action %a, category %a, name %a","reuse",container.subcategory,name)
4969  end
4970 end
4971 return stored
4972end
4973function containers.write(container,name,data,fast)
4974 if data then
4975  data.cache_format=cache_format
4976  data.cache_version=container.version
4977  if container.enabled and caches then
4978   local unique=data.unique
4979   local shared=data.shared
4980   data.unique=nil
4981   data.shared=nil
4982   savedataincache(container.writable,name,data,fast)
4983   if trace_cache or trace_containers then
4984    report_containers("action %a, category %a, name %a","save",container.subcategory,name)
4985   end
4986   data.unique=unique
4987   data.shared=shared
4988  end
4989  if trace_cache or trace_containers then
4990   report_containers("action %a, category %a, name %a","store",container.subcategory,name)
4991  end
4992  container.storage[name]=data
4993 end
4994 return data
4995end
4996function containers.content(container,name)
4997 return container.storage[name]
4998end
4999function containers.cleanname(name)
5000 return (gsub(lower(name),"[^%w\128-\255]+","-")) 
5001end
5002
5003end -- closure
5004
5005do -- begin closure to overcome local limits and interference
5006
5007if not modules then modules={} end modules ['luatex-fonts-nod']={
5008 version=1.001,
5009 comment="companion to luatex-fonts.lua",
5010 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
5011 copyright="PRAGMA ADE / ConTeXt Development Team",
5012 license="see context related readme files"
5013}
5014if context then
5015--removed
5016
5017end
5018if tex.attribute[0]~=0 then
5019 texio.write_nl("log","!")
5020 texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be")
5021 texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special")
5022 texio.write_nl("log","! purposes so setting them at the TeX end might break the font handler.")
5023 texio.write_nl("log","!")
5024 tex.attribute[0]=0 
5025end
5026attributes=attributes or {}
5027attributes.unsetvalue=-0x7FFFFFFF
5028local numbers,last={},127
5029attributes.private=attributes.private or function(name)
5030 local number=numbers[name]
5031 if not number then
5032  if last<255 then
5033   last=last+1
5034  end
5035  number=last
5036  numbers[name]=number
5037 end
5038 return number
5039end
5040nodes={}
5041nodes.handlers={}
5042local nodecodes={}
5043local glyphcodes=node.subtypes("glyph")
5044local disccodes=node.subtypes("disc")
5045for k,v in next,node.types() do
5046 v=string.gsub(v,"_","")
5047 nodecodes[k]=v
5048 nodecodes[v]=k
5049end
5050for k,v in next,glyphcodes do
5051 glyphcodes[v]=k
5052end
5053for k,v in next,disccodes do
5054 disccodes[v]=k
5055end
5056nodes.nodecodes=nodecodes
5057nodes.glyphcodes=glyphcodes
5058nodes.disccodes=disccodes
5059nodes.dirvalues={ lefttoright=0,righttoleft=1 }
5060nodes.handlers.protectglyphs=node.protectglyphs   or node.protect_glyphs   
5061nodes.handlers.unprotectglyphs=node.unprotectglyphs or node.unprotect_glyphs
5062local direct=node.direct
5063local nuts={}
5064nodes.nuts=nuts
5065local tonode=direct.tonode
5066local tonut=direct.todirect
5067nodes.tonode=tonode
5068nodes.tonut=tonut
5069nuts.tonode=tonode
5070nuts.tonut=tonut
5071nuts.getattr=direct.get_attribute
5072nuts.getboth=direct.getboth
5073nuts.getchar=direct.getchar
5074nuts.getdirection=direct.getdirection
5075nuts.getdisc=direct.getdisc
5076nuts.getreplace=direct.getreplace
5077nuts.getfield=direct.getfield
5078nuts.getfont=direct.getfont
5079nuts.getid=direct.getid
5080nuts.getkern=direct.getkern
5081nuts.getlist=direct.getlist
5082nuts.getnext=direct.getnext
5083nuts.getoffsets=direct.getoffsets
5084nuts.getoptions=direct.getoptions or function() return 0 end
5085nuts.getprev=direct.getprev
5086nuts.getsubtype=direct.getsubtype
5087nuts.getwidth=direct.getwidth
5088nuts.setattr=direct.setfield
5089nuts.setboth=direct.setboth
5090nuts.setchar=direct.setchar
5091nuts.setcomponents=direct.setcomponents
5092nuts.setdirection=direct.setdirection
5093nuts.setdisc=direct.setdisc
5094nuts.setreplace=direct.setreplace
5095nuts.setfield=direct.setfield
5096nuts.setkern=direct.setkern
5097nuts.setlink=direct.setlink
5098nuts.setlist=direct.setlist
5099nuts.setnext=direct.setnext
5100nuts.setoffsets=direct.setoffsets
5101nuts.setprev=direct.setprev
5102nuts.setsplit=direct.setsplit
5103nuts.setsubtype=direct.setsubtype
5104nuts.setwidth=direct.setwidth
5105nuts.getglyphdata=nuts.getattribute or nuts.getattr
5106nuts.setglyphdata=nuts.setattribute or nuts.setattr
5107nuts.ischar=direct.ischar  or direct.is_char
5108nuts.isglyph=direct.isglyph    or direct.is_glyph
5109nuts.copy=direct.copy
5110nuts.copynode=direct.copy
5111nuts.copylist=direct.copylist   or direct.copy_list
5112nuts.endofmath=direct.endofmath  or direct.end_of_math
5113nuts.flush=direct.flush
5114nuts.flushlist=direct.flushlist  or direct.flush_list
5115nuts.flushnode=direct.flushnode  or direct.flush_node
5116nuts.free=direct.free
5117nuts.insertafter=direct.insertafter   or direct.insert_after
5118nuts.insertbefore=direct.insertbefore  or direct.insert_before
5119nuts.isnode=direct.isnode  or direct.is_node
5120nuts.isdirect=direct.isdirect   or direct.is_direct
5121nuts.isnut=direct.isdirect   or direct.is_direct
5122nuts.kerning=direct.kerning
5123nuts.ligaturing=direct.ligaturing
5124nuts.new=direct.new
5125nuts.remove=direct.remove
5126nuts.tail=direct.tail
5127nuts.traverse=direct.traverse
5128nuts.traversechar=direct.traversechar  or direct.traverse_char
5129nuts.traverseglyph=direct.traverseglyph or direct.traverse_glyph
5130nuts.traverseid=direct.traverseid or direct.traverse_id
5131local propertydata=(direct.getpropertiestable or direct.get_properties_table)()
5132nodes.properties={ data=propertydata }
5133if direct.set_properties_mode then
5134 direct.set_properties_mode(true,true)
5135 function direct.set_properties_mode() end
5136end
5137nuts.getprop=function(n,k)
5138 local p=propertydata[n]
5139 if p then
5140  return p[k]
5141 end
5142end
5143nuts.setprop=function(n,k,v)
5144 if v then
5145  local p=propertydata[n]
5146  if p then
5147   p[k]=v
5148  else
5149   propertydata[n]={ [k]=v }
5150  end
5151 end
5152end
5153nodes.setprop=nodes.setproperty
5154nodes.getprop=nodes.getproperty
5155local setprev=nuts.setprev
5156local setnext=nuts.setnext
5157local getnext=nuts.getnext
5158local setlink=nuts.setlink
5159local getfield=nuts.getfield
5160local setfield=nuts.setfield
5161local getsubtype=nuts.getsubtype
5162local isglyph=nuts.isglyph
5163local find_tail=nuts.tail
5164local flushlist=nuts.flushlist
5165local flushnode=nuts.flushnode
5166local traverseid=nuts.traverseid
5167local copynode=nuts.copynode
5168local glyph_code=nodes.nodecodes.glyph
5169local ligature_code=nodes.glyphcodes.ligature
5170do 
5171 local p=nodecodes.localpar or nodecodes.local_par
5172 if p then
5173  nodecodes.par=p
5174  nodecodes[p]="par"
5175  nodecodes.localpar=p 
5176  nodecodes.local_par=p 
5177 end
5178end
5179do
5180 local getcomponents=node.direct.getcomponents
5181 local setcomponents=node.direct.setcomponents
5182 local function copynocomponents(g,copyinjection)
5183  local components=getcomponents(g)
5184  if components then
5185   setcomponents(g)
5186   local n=copynode(g)
5187   if copyinjection then
5188    copyinjection(n,g)
5189   end
5190   setcomponents(g,components)
5191   return n
5192  else
5193   local n=copynode(g)
5194   if copyinjection then
5195    copyinjection(n,g)
5196   end
5197   return n
5198  end
5199 end
5200 local function copyonlyglyphs(current)
5201  local head=nil
5202  local previous=nil
5203  for n in traverseid(glyph_code,current) do
5204   n=copynode(n)
5205   if head then
5206    setlink(previous,n)
5207   else
5208    head=n
5209   end
5210   previous=n
5211  end
5212  return head
5213 end
5214 local function countcomponents(start,marks)
5215  local char=isglyph(start)
5216  if char then
5217   if getsubtype(start)==ligature_code then
5218    local n=0
5219    local components=getcomponents(start)
5220    while components do
5221     n=n+countcomponents(components,marks)
5222     components=getnext(components)
5223    end
5224    return n
5225   elseif not marks[char] then
5226    return 1
5227   end
5228  end
5229  return 0
5230 end
5231 local function flushcomponents()
5232 end
5233 nuts.components={
5234  set=setcomponents,
5235  get=getcomponents,
5236  copyonlyglyphs=copyonlyglyphs,
5237  copynocomponents=copynocomponents,
5238  count=countcomponents,
5239  flush=flushcomponents,
5240 }
5241end
5242nuts.usesfont=direct.usesfont or direct.uses_font
5243do
5244 local dummy=tonut(node.new("glyph"))
5245 nuts.traversers={
5246  glyph=nuts.traverseid(nodecodes.glyph,dummy),
5247  glue=nuts.traverseid(nodecodes.glue,dummy),
5248  disc=nuts.traverseid(nodecodes.disc,dummy),
5249  boundary=nuts.traverseid(nodecodes.boundary,dummy),
5250  char=nuts.traversechar(dummy),
5251  node=nuts.traverse(dummy),
5252 }
5253end
5254if not nuts.setreplace then
5255 local getdisc=nuts.getdisc
5256 local setfield=nuts.setfield
5257 function nuts.getreplace(n)
5258  local _,_,h,_,_,t=getdisc(n,true)
5259  return h,t
5260 end
5261 function nuts.setreplace(n,h)
5262  setfield(n,"replace",h)
5263 end
5264end
5265do
5266 local getsubtype=nuts.getsubtype
5267 function nuts.startofpar(n)
5268  local s=getsubtype(n)
5269  return s==0 or s==2 
5270 end
5271end
5272
5273end -- closure
5274
5275do -- begin closure to overcome local limits and interference
5276
5277
5278characters=characters or {}
5279characters.blockrange={}
5280characters.classifiers={
5281 [768]=5,
5282 [769]=5,
5283 [770]=5,
5284 [771]=5,
5285 [772]=5,
5286 [773]=5,
5287 [774]=5,
5288 [775]=5,
5289 [776]=5,
5290 [777]=5,
5291 [778]=5,
5292 [779]=5,
5293 [780]=5,
5294 [781]=5,
5295 [782]=5,
5296 [783]=5,
5297 [784]=5,
5298 [785]=5,
5299 [786]=5,
5300 [787]=5,
5301 [788]=5,
5302 [789]=5,
5303 [790]=5,
5304 [791]=5,
5305 [792]=5,
5306 [793]=5,
5307 [794]=5,
5308 [795]=5,
5309 [796]=5,
5310 [797]=5,
5311 [798]=5,
5312 [799]=5,
5313 [800]=5,
5314 [801]=5,
5315 [802]=5,
5316 [803]=5,
5317 [804]=5,
5318 [805]=5,
5319 [806]=5,
5320 [807]=5,
5321 [808]=5,
5322 [809]=5,
5323 [810]=5,
5324 [811]=5,
5325 [812]=5,
5326 [813]=5,
5327 [814]=5,
5328 [815]=5,
5329 [816]=5,
5330 [817]=5,
5331 [818]=5,
5332 [819]=5,
5333 [820]=5,
5334 [821]=5,
5335 [822]=5,
5336 [823]=5,
5337 [824]=5,
5338 [825]=5,
5339 [826]=5,
5340 [827]=5,
5341 [828]=5,
5342 [829]=5,
5343 [830]=5,
5344 [831]=5,
5345 [832]=5,
5346 [833]=5,
5347 [834]=5,
5348 [835]=5,
5349 [836]=5,
5350 [837]=5,
5351 [838]=5,
5352 [839]=5,
5353 [840]=5,
5354 [841]=5,
5355 [842]=5,
5356 [843]=5,
5357 [844]=5,
5358 [845]=5,
5359 [846]=5,
5360 [847]=5,
5361 [848]=5,
5362 [849]=5,
5363 [850]=5,
5364 [851]=5,
5365 [852]=5,
5366 [853]=5,
5367 [854]=5,
5368 [855]=5,
5369 [856]=5,
5370 [857]=5,
5371 [858]=5,
5372 [859]=5,
5373 [860]=5,
5374 [861]=5,
5375 [862]=5,
5376 [863]=5,
5377 [864]=5,
5378 [865]=5,
5379 [866]=5,
5380 [867]=5,
5381 [868]=5,
5382 [869]=5,
5383 [870]=5,
5384 [871]=5,
5385 [872]=5,
5386 [873]=5,
5387 [874]=5,
5388 [875]=5,
5389 [876]=5,
5390 [877]=5,
5391 [878]=5,
5392 [879]=5,
5393 [1155]=5,
5394 [1156]=5,
5395 [1157]=5,
5396 [1158]=5,
5397 [1159]=5,
5398 [1425]=5,
5399 [1426]=5,
5400 [1427]=5,
5401 [1428]=5,
5402 [1429]=5,
5403 [1430]=5,
5404 [1431]=5,
5405 [1432]=5,
5406 [1433]=5,
5407 [1434]=5,
5408 [1435]=5,
5409 [1436]=5,
5410 [1437]=5,
5411 [1438]=5,
5412 [1439]=5,
5413 [1440]=5,
5414 [1441]=5,
5415 [1442]=5,
5416 [1443]=5,
5417 [1444]=5,
5418 [1445]=5,
5419 [1446]=5,
5420 [1447]=5,
5421 [1448]=5,
5422 [1449]=5,
5423 [1450]=5,
5424 [1451]=5,
5425 [1452]=5,
5426 [1453]=5,
5427 [1454]=5,
5428 [1455]=5,
5429 [1456]=5,
5430 [1457]=5,
5431 [1458]=5,
5432 [1459]=5,
5433 [1460]=5,
5434 [1461]=5,
5435 [1462]=5,
5436 [1463]=5,
5437 [1464]=5,
5438 [1465]=5,
5439 [1466]=5,
5440 [1467]=5,
5441 [1468]=5,
5442 [1469]=5,
5443 [1471]=5,
5444 [1473]=5,
5445 [1474]=5,
5446 [1476]=5,
5447 [1477]=5,
5448 [1479]=5,
5449 [1536]=4,
5450 [1537]=4,
5451 [1538]=4,
5452 [1539]=4,
5453 [1540]=4,
5454 [1541]=4,
5455 [1542]=6,
5456 [1543]=6,
5457 [1544]=4,
5458 [1545]=6,
5459 [1546]=6,
5460 [1547]=4,
5461 [1548]=6,
5462 [1549]=6,
5463 [1550]=6,
5464 [1551]=6,
5465 [1552]=5,
5466 [1553]=5,
5467 [1554]=5,
5468 [1555]=5,
5469 [1556]=5,
5470 [1557]=5,
5471 [1558]=5,
5472 [1559]=5,
5473 [1560]=5,
5474 [1561]=5,
5475 [1562]=5,
5476 [1563]=6,
5477 [1564]=6,
5478 [1565]=6,
5479 [1566]=6,
5480 [1567]=6,
5481 [1568]=2,
5482 [1569]=4,
5483 [1570]=3,
5484 [1571]=3,
5485 [1572]=3,
5486 [1573]=3,
5487 [1574]=2,
5488 [1575]=3,
5489 [1576]=2,
5490 [1577]=3,
5491 [1578]=2,
5492 [1579]=2,
5493 [1580]=2,
5494 [1581]=2,
5495 [1582]=2,
5496 [1583]=3,
5497 [1584]=3,
5498 [1585]=3,
5499 [1586]=3,
5500 [1587]=2,
5501 [1588]=2,
5502 [1589]=2,
5503 [1590]=2,
5504 [1591]=2,
5505 [1592]=2,
5506 [1593]=2,
5507 [1594]=2,
5508 [1595]=2,
5509 [1596]=2,
5510 [1597]=2,
5511 [1598]=2,
5512 [1599]=2,
5513 [1600]=2,
5514 [1601]=2,
5515 [1602]=2,
5516 [1603]=2,
5517 [1604]=2,
5518 [1605]=2,
5519 [1606]=2,
5520 [1607]=2,
5521 [1608]=3,
5522 [1609]=2,
5523 [1610]=2,
5524 [1611]=5,
5525 [1612]=5,
5526 [1613]=5,
5527 [1614]=5,
5528 [1615]=5,
5529 [1616]=5,
5530 [1617]=5,
5531 [1618]=5,
5532 [1619]=5,
5533 [1620]=5,
5534 [1621]=5,
5535 [1622]=5,
5536 [1623]=5,
5537 [1624]=5,
5538 [1625]=5,
5539 [1626]=5,
5540 [1627]=5,
5541 [1628]=5,
5542 [1629]=5,
5543 [1630]=5,
5544 [1631]=5,
5545 [1632]=6,
5546 [1633]=6,
5547 [1634]=6,
5548 [1635]=6,
5549 [1636]=6,
5550 [1637]=6,
5551 [1638]=6,
5552 [1639]=6,
5553 [1640]=6,
5554 [1641]=6,
5555 [1642]=6,
5556 [1643]=6,
5557 [1644]=6,
5558 [1645]=6,
5559 [1646]=2,
5560 [1647]=2,
5561 [1648]=5,
5562 [1649]=3,
5563 [1650]=3,
5564 [1651]=3,
5565 [1652]=4,
5566 [1653]=3,
5567 [1654]=3,
5568 [1655]=3,
5569 [1656]=2,
5570 [1657]=2,
5571 [1658]=2,
5572 [1659]=2,
5573 [1660]=2,
5574 [1661]=2,
5575 [1662]=2,
5576 [1663]=2,
5577 [1664]=2,
5578 [1665]=2,
5579 [1666]=2,
5580 [1667]=2,
5581 [1668]=2,
5582 [1669]=2,
5583 [1670]=2,
5584 [1671]=2,
5585 [1672]=3,
5586 [1673]=3,
5587 [1674]=3,
5588 [1675]=3,
5589 [1676]=3,
5590 [1677]=3,
5591 [1678]=3,
5592 [1679]=3,
5593 [1680]=3,
5594 [1681]=3,
5595 [1682]=3,
5596 [1683]=3,
5597 [1684]=3,
5598 [1685]=3,
5599 [1686]=3,
5600 [1687]=3,
5601 [1688]=3,
5602 [1689]=3,
5603 [1690]=2,
5604 [1691]=2,
5605 [1692]=2,
5606 [1693]=2,
5607 [1694]=2,
5608 [1695]=2,
5609 [1696]=2,
5610 [1697]=2,
5611 [1698]=2,
5612 [1699]=2,
5613 [1700]=2,
5614 [1701]=2,
5615 [1702]=2,
5616 [1703]=2,
5617 [1704]=2,
5618 [1705]=2,
5619 [1706]=2,
5620 [1707]=2,
5621 [1708]=2,
5622 [1709]=2,
5623 [1710]=2,
5624 [1711]=2,
5625 [1712]=2,
5626 [1713]=2,
5627 [1714]=2,
5628 [1715]=2,
5629 [1716]=2,
5630 [1717]=2,
5631 [1718]=2,
5632 [1719]=2,
5633 [1720]=2,
5634 [1721]=2,
5635 [1722]=2,
5636 [1723]=2,
5637 [1724]=2,
5638 [1725]=2,
5639 [1726]=2,
5640 [1727]=2,
5641 [1728]=3,
5642 [1729]=2,
5643 [1730]=2,
5644 [1731]=3,
5645 [1732]=3,
5646 [1733]=3,
5647 [1734]=3,
5648 [1735]=3,
5649 [1736]=3,
5650 [1737]=3,
5651 [1738]=3,
5652 [1739]=3,
5653 [1740]=2,
5654 [1741]=3,
5655 [1742]=2,
5656 [1743]=3,
5657 [1744]=2,
5658 [1745]=2,
5659 [1746]=3,
5660 [1747]=3,
5661 [1748]=6,
5662 [1749]=3,
5663 [1750]=5,
5664 [1751]=5,
5665 [1752]=5,
5666 [1753]=5,
5667 [1754]=5,
5668 [1755]=5,
5669 [1756]=5,
5670 [1757]=4,
5671 [1758]=6,
5672 [1759]=5,
5673 [1760]=5,
5674 [1761]=5,
5675 [1762]=5,
5676 [1763]=5,
5677 [1764]=5,
5678 [1765]=6,
5679 [1766]=6,
5680 [1767]=5,
5681 [1768]=5,
5682 [1769]=6,
5683 [1770]=5,
5684 [1771]=5,
5685 [1772]=5,
5686 [1773]=5,
5687 [1774]=3,
5688 [1775]=3,
5689 [1776]=6,
5690 [1777]=6,
5691 [1778]=6,
5692 [1779]=6,
5693 [1780]=6,
5694 [1781]=6,
5695 [1782]=6,
5696 [1783]=6,
5697 [1784]=6,
5698 [1785]=6,
5699 [1786]=2,
5700 [1787]=2,
5701 [1788]=2,
5702 [1789]=6,
5703 [1790]=6,
5704 [1791]=2,
5705 [1792]=6,
5706 [1793]=6,
5707 [1794]=6,
5708 [1795]=6,
5709 [1796]=6,
5710 [1797]=6,
5711 [1798]=6,
5712 [1799]=6,
5713 [1800]=6,
5714 [1801]=6,
5715 [1802]=6,
5716 [1803]=6,
5717 [1804]=6,
5718 [1805]=6,
5719 [1808]=3,
5720 [1809]=5,
5721 [1810]=2,
5722 [1811]=2,
5723 [1812]=2,
5724 [1813]=3,
5725 [1814]=3,
5726 [1815]=3,
5727 [1816]=3,
5728 [1817]=3,
5729 [1818]=2,
5730 [1819]=2,
5731 [1820]=2,
5732 [1821]=2,
5733 [1822]=3,
5734 [1823]=2,
5735 [1824]=2,
5736 [1825]=2,
5737 [1826]=2,
5738 [1827]=2,
5739 [1828]=2,
5740 [1829]=2,
5741 [1830]=2,
5742 [1831]=2,
5743 [1832]=3,
5744 [1833]=2,
5745 [1834]=3,
5746 [1835]=2,
5747 [1836]=3,
5748 [1837]=2,
5749 [1838]=2,
5750 [1839]=3,
5751 [1840]=5,
5752 [1841]=5,
5753 [1842]=5,
5754 [1843]=5,
5755 [1844]=5,
5756 [1845]=5,
5757 [1846]=5,
5758 [1847]=5,
5759 [1848]=5,
5760 [1849]=5,
5761 [1850]=5,
5762 [1851]=5,
5763 [1852]=5,
5764 [1853]=5,
5765 [1854]=5,
5766 [1855]=5,
5767 [1856]=5,
5768 [1857]=5,
5769 [1858]=5,
5770 [1859]=5,
5771 [1860]=5,
5772 [1861]=5,
5773 [1862]=5,
5774 [1863]=5,
5775 [1864]=5,
5776 [1865]=5,
5777 [1866]=5,
5778 [1869]=3,
5779 [1870]=2,
5780 [1871]=2,
5781 [1872]=2,
5782 [1873]=2,
5783 [1874]=2,
5784 [1875]=2,
5785 [1876]=2,
5786 [1877]=2,
5787 [1878]=2,
5788 [1879]=2,
5789 [1880]=2,
5790 [1881]=3,
5791 [1882]=3,
5792 [1883]=3,
5793 [1884]=2,
5794 [1885]=2,
5795 [1886]=2,
5796 [1887]=2,
5797 [1888]=2,
5798 [1889]=2,
5799 [1890]=2,
5800 [1891]=2,
5801 [1892]=2,
5802 [1893]=2,
5803 [1894]=2,
5804 [1895]=2,
5805 [1896]=2,
5806 [1897]=2,
5807 [1898]=2,
5808 [1899]=3,
5809 [1900]=3,
5810 [1901]=2,
5811 [1902]=2,
5812 [1903]=2,
5813 [1904]=2,
5814 [1905]=3,
5815 [1906]=2,
5816 [1907]=3,
5817 [1908]=3,
5818 [1909]=2,
5819 [1910]=2,
5820 [1911]=2,
5821 [1912]=3,
5822 [1913]=3,
5823 [1914]=2,
5824 [1915]=2,
5825 [1916]=2,
5826 [1917]=2,
5827 [1918]=2,
5828 [1919]=2,
5829 [1958]=5,
5830 [1959]=5,
5831 [1960]=5,
5832 [1961]=5,
5833 [1962]=5,
5834 [1963]=5,
5835 [1964]=5,
5836 [1965]=5,
5837 [1966]=5,
5838 [1967]=5,
5839 [1968]=5,
5840 [1984]=6,
5841 [1985]=6,
5842 [1986]=6,
5843 [1987]=6,
5844 [1988]=6,
5845 [1989]=6,
5846 [1990]=6,
5847 [1991]=6,
5848 [1992]=6,
5849 [1993]=6,
5850 [1994]=2,
5851 [1995]=2,
5852 [1996]=2,
5853 [1997]=2,
5854 [1998]=2,
5855 [1999]=2,
5856 [2000]=2,
5857 [2001]=2,
5858 [2002]=2,
5859 [2003]=2,
5860 [2004]=2,
5861 [2005]=2,
5862 [2006]=2,
5863 [2007]=2,
5864 [2008]=2,
5865 [2009]=2,
5866 [2010]=2,
5867 [2011]=2,
5868 [2012]=2,
5869 [2013]=2,
5870 [2014]=2,
5871 [2015]=2,
5872 [2016]=2,
5873 [2017]=2,
5874 [2018]=2,
5875 [2019]=2,
5876 [2020]=2,
5877 [2021]=2,
5878 [2022]=2,
5879 [2023]=2,
5880 [2024]=2,
5881 [2025]=2,
5882 [2026]=2,
5883 [2027]=5,
5884 [2028]=5,
5885 [2029]=5,
5886 [2030]=5,
5887 [2031]=5,
5888 [2032]=5,
5889 [2033]=5,
5890 [2034]=5,
5891 [2035]=5,
5892 [2036]=6,
5893 [2037]=6,
5894 [2038]=6,
5895 [2039]=6,
5896 [2040]=6,
5897 [2041]=6,
5898 [2042]=2,
5899 [2045]=5,
5900 [2046]=6,
5901 [2047]=6,
5902 [2070]=5,
5903 [2071]=5,
5904 [2072]=5,
5905 [2073]=5,
5906 [2075]=5,
5907 [2076]=5,
5908 [2077]=5,
5909 [2078]=5,
5910 [2079]=5,
5911 [2080]=5,
5912 [2081]=5,
5913 [2082]=5,
5914 [2083]=5,
5915 [2085]=5,
5916 [2086]=5,
5917 [2087]=5,
5918 [2089]=5,
5919 [2090]=5,
5920 [2091]=5,
5921 [2092]=5,
5922 [2093]=5,
5923 [2112]=3,
5924 [2113]=2,
5925 [2114]=2,
5926 [2115]=2,
5927 [2116]=2,
5928 [2117]=2,
5929 [2118]=3,
5930 [2119]=3,
5931 [2120]=2,
5932 [2121]=3,
5933 [2122]=2,
5934 [2123]=2,
5935 [2124]=2,
5936 [2125]=2,
5937 [2126]=2,
5938 [2127]=2,
5939 [2128]=2,
5940 [2129]=2,
5941 [2130]=2,
5942 [2131]=2,
5943 [2132]=3,
5944 [2133]=2,
5945 [2134]=3,
5946 [2135]=3,
5947 [2136]=3,
5948 [2137]=5,
5949 [2138]=5,
5950 [2139]=5,
5951 [2144]=2,
5952 [2145]=4,
5953 [2146]=2,
5954 [2147]=2,
5955 [2148]=2,
5956 [2149]=2,
5957 [2150]=4,
5958 [2151]=3,
5959 [2152]=2,
5960 [2153]=3,
5961 [2154]=3,
5962 [2160]=3,
5963 [2161]=3,
5964 [2162]=3,
5965 [2163]=3,
5966 [2164]=3,
5967 [2165]=3,
5968 [2166]=3,
5969 [2167]=3,
5970 [2168]=3,
5971 [2169]=3,
5972 [2170]=3,
5973 [2171]=3,
5974 [2172]=3,
5975 [2173]=3,
5976 [2174]=3,
5977 [2175]=3,
5978 [2176]=3,
5979 [2177]=3,
5980 [2178]=3,
5981 [2179]=2,
5982 [2180]=2,
5983 [2181]=2,
5984 [2182]=2,
5985 [2183]=4,
5986 [2184]=4,
5987 [2185]=2,
5988 [2186]=2,
5989 [2187]=2,
5990 [2188]=2,
5991 [2189]=2,
5992 [2190]=3,
5993 [2192]=4,
5994 [2193]=4,
5995 [2200]=5,
5996 [2201]=5,
5997 [2202]=5,
5998 [2203]=5,
5999 [2204]=5,
6000 [2205]=5,
6001 [2206]=5,
6002 [2207]=5,
6003 [2208]=2,
6004 [2209]=2,
6005 [2210]=2,
6006 [2211]=2,
6007 [2212]=2,
6008 [2213]=2,
6009 [2214]=2,
6010 [2215]=2,
6011 [2216]=2,
6012 [2217]=2,
6013 [2218]=3,
6014 [2219]=3,
6015 [2220]=3,
6016 [2221]=4,
6017 [2222]=3,
6018 [2223]=2,
6019 [2224]=2,
6020 [2225]=3,
6021 [2226]=3,
6022 [2227]=2,
6023 [2228]=2,
6024 [2229]=2,
6025 [2230]=2,
6026 [2231]=2,
6027 [2232]=2,
6028 [2233]=3,
6029 [2234]=2,
6030 [2235]=2,
6031 [2236]=2,
6032 [2237]=2,
6033 [2238]=2,
6034 [2239]=2,
6035 [2240]=2,
6036 [2241]=2,
6037 [2242]=2,
6038 [2243]=2,
6039 [2244]=2,
6040 [2245]=2,
6041 [2246]=2,
6042 [2247]=2,
6043 [2248]=2,
6044 [2250]=5,
6045 [2251]=5,
6046 [2252]=5,
6047 [2253]=5,
6048 [2254]=5,
6049 [2255]=5,
6050 [2256]=5,
6051 [2257]=5,
6052 [2258]=5,
6053 [2259]=5,
6054 [2260]=5,
6055 [2261]=5,
6056 [2262]=5,
6057 [2263]=5,
6058 [2264]=5,
6059 [2265]=5,
6060 [2266]=5,
6061 [2267]=5,
6062 [2268]=5,
6063 [2269]=5,
6064 [2270]=5,
6065 [2271]=5,
6066 [2272]=5,
6067 [2273]=5,
6068 [2274]=4,
6069 [2275]=5,
6070 [2276]=5,
6071 [2277]=5,
6072 [2278]=5,
6073 [2279]=5,
6074 [2280]=5,
6075 [2281]=5,
6076 [2282]=5,
6077 [2283]=5,
6078 [2284]=5,
6079 [2285]=5,
6080 [2286]=5,
6081 [2287]=5,
6082 [2288]=5,
6083 [2289]=5,
6084 [2290]=5,
6085 [2291]=5,
6086 [2292]=5,
6087 [2293]=5,
6088 [2294]=5,
6089 [2295]=5,
6090 [2296]=5,
6091 [2297]=5,
6092 [2298]=5,
6093 [2299]=5,
6094 [2300]=5,
6095 [2301]=5,
6096 [2302]=5,
6097 [2303]=5,
6098 [2304]=5,
6099 [2305]=5,
6100 [2306]=5,
6101 [2362]=5,
6102 [2364]=5,
6103 [2369]=5,
6104 [2370]=5,
6105 [2371]=5,
6106 [2372]=5,
6107 [2373]=5,
6108 [2374]=5,
6109 [2375]=5,
6110 [2376]=5,
6111 [2381]=5,
6112 [2385]=5,
6113 [2386]=5,
6114 [2387]=5,
6115 [2388]=5,
6116 [2389]=5,
6117 [2390]=5,
6118 [2391]=5,
6119 [2402]=5,
6120 [2403]=5,
6121 [2433]=5,
6122 [2492]=5,
6123 [2497]=5,
6124 [2498]=5,
6125 [2499]=5,
6126 [2500]=5,
6127 [2509]=5,
6128 [2530]=5,
6129 [2531]=5,
6130 [2558]=5,
6131 [2561]=5,
6132 [2562]=5,
6133 [2620]=5,
6134 [2625]=5,
6135 [2626]=5,
6136 [2631]=5,
6137 [2632]=5,
6138 [2635]=5,
6139 [2636]=5,
6140 [2637]=5,
6141 [2641]=5,
6142 [2672]=5,
6143 [2673]=5,
6144 [2677]=5,
6145 [2689]=5,
6146 [2690]=5,
6147 [2748]=5,
6148 [2753]=5,
6149 [2754]=5,
6150 [2755]=5,
6151 [2756]=5,
6152 [2757]=5,
6153 [2759]=5,
6154 [2760]=5,
6155 [2765]=5,
6156 [2786]=5,
6157 [2787]=5,
6158 [2810]=5,
6159 [2811]=5,
6160 [2812]=5,
6161 [2813]=5,
6162 [2814]=5,
6163 [2815]=5,
6164 [2817]=5,
6165 [2876]=5,
6166 [2879]=5,
6167 [2881]=5,
6168 [2882]=5,
6169 [2883]=5,
6170 [2884]=5,
6171 [2893]=5,
6172 [2901]=5,
6173 [2902]=5,
6174 [2914]=5,
6175 [2915]=5,
6176 [2946]=5,
6177 [3008]=5,
6178 [3021]=5,
6179 [3072]=5,
6180 [3076]=5,
6181 [3132]=5,
6182 [3134]=5,
6183 [3135]=5,
6184 [3136]=5,
6185 [3142]=5,
6186 [3143]=5,
6187 [3144]=5,
6188 [3146]=5,
6189 [3147]=5,
6190 [3148]=5,
6191 [3149]=5,
6192 [3157]=5,
6193 [3158]=5,
6194 [3170]=5,
6195 [3171]=5,
6196 [3201]=5,
6197 [3260]=5,
6198 [3263]=5,
6199 [3270]=5,
6200 [3276]=5,
6201 [3277]=5,
6202 [3298]=5,
6203 [3299]=5,
6204 [3328]=5,
6205 [3329]=5,
6206 [3387]=5,
6207 [3388]=5,
6208 [3393]=5,
6209 [3394]=5,
6210 [3395]=5,
6211 [3396]=5,
6212 [3405]=5,
6213 [3426]=5,
6214 [3427]=5,
6215 [3457]=5,
6216 [3530]=5,
6217 [3538]=5,
6218 [3539]=5,
6219 [3540]=5,
6220 [3542]=5,
6221 [3633]=5,
6222 [3636]=5,
6223 [3637]=5,
6224 [3638]=5,
6225 [3639]=5,
6226 [3640]=5,
6227 [3641]=5,
6228 [3642]=5,
6229 [3655]=5,
6230 [3656]=5,
6231 [3657]=5,
6232 [3658]=5,
6233 [3659]=5,
6234 [3660]=5,
6235 [3661]=5,
6236 [3662]=5,
6237 [3761]=5,
6238 [3764]=5,
6239 [3765]=5,
6240 [3766]=5,
6241 [3767]=5,
6242 [3768]=5,
6243 [3769]=5,
6244 [3770]=5,
6245 [3771]=5,
6246 [3772]=5,
6247 [3784]=5,
6248 [3785]=5,
6249 [3786]=5,
6250 [3787]=5,
6251 [3788]=5,
6252 [3789]=5,
6253 [3864]=5,
6254 [3865]=5,
6255 [3893]=5,
6256 [3895]=5,
6257 [3897]=5,
6258 [3953]=5,
6259 [3954]=5,
6260 [3955]=5,
6261 [3956]=5,
6262 [3957]=5,
6263 [3958]=5,
6264 [3959]=5,
6265 [3960]=5,
6266 [3961]=5,
6267 [3962]=5,
6268 [3963]=5,
6269 [3964]=5,
6270 [3965]=5,
6271 [3966]=5,
6272 [3968]=5,
6273 [3969]=5,
6274 [3970]=5,
6275 [3971]=5,
6276 [3972]=5,
6277 [3974]=5,
6278 [3975]=5,
6279 [3981]=5,
6280 [3982]=5,
6281 [3983]=5,
6282 [3984]=5,
6283 [3985]=5,
6284 [3986]=5,
6285 [3987]=5,
6286 [3988]=5,
6287 [3989]=5,
6288 [3990]=5,
6289 [3991]=5,
6290 [3993]=5,
6291 [3994]=5,
6292 [3995]=5,
6293 [3996]=5,
6294 [3997]=5,
6295 [3998]=5,
6296 [3999]=5,
6297 [4000]=5,
6298 [4001]=5,
6299 [4002]=5,
6300 [4003]=5,
6301 [4004]=5,
6302 [4005]=5,
6303 [4006]=5,
6304 [4007]=5,
6305 [4008]=5,
6306 [4009]=5,
6307 [4010]=5,
6308 [4011]=5,
6309 [4012]=5,
6310 [4013]=5,
6311 [4014]=5,
6312 [4015]=5,
6313 [4016]=5,
6314 [4017]=5,
6315 [4018]=5,
6316 [4019]=5,
6317 [4020]=5,
6318 [4021]=5,
6319 [4022]=5,
6320 [4023]=5,
6321 [4024]=5,
6322 [4025]=5,
6323 [4026]=5,
6324 [4027]=5,
6325 [4028]=5,
6326 [4038]=5,
6327 [4141]=5,
6328 [4142]=5,
6329 [4143]=5,
6330 [4144]=5,
6331 [4146]=5,
6332 [4147]=5,
6333 [4148]=5,
6334 [4149]=5,
6335 [4150]=5,
6336 [4151]=5,
6337 [4153]=5,
6338 [4154]=5,
6339 [4157]=5,
6340 [4158]=5,
6341 [4184]=5,
6342 [4185]=5,
6343 [4190]=5,
6344 [4191]=5,
6345 [4192]=5,
6346 [4209]=5,
6347 [4210]=5,
6348 [4211]=5,
6349 [4212]=5,
6350 [4226]=5,
6351 [4229]=5,
6352 [4230]=5,
6353 [4237]=5,
6354 [4253]=5,
6355 [4957]=5,
6356 [4958]=5,
6357 [4959]=5,
6358 [5906]=5,
6359 [5907]=5,
6360 [5908]=5,
6361 [5938]=5,
6362 [5939]=5,
6363 [5940]=5,
6364 [5970]=5,
6365 [5971]=5,
6366 [6002]=5,
6367 [6003]=5,
6368 [6071]=5,
6369 [6072]=5,
6370 [6073]=5,
6371 [6074]=5,
6372 [6075]=5,
6373 [6076]=5,
6374 [6077]=5,
6375 [6086]=5,
6376 [6089]=5,
6377 [6090]=5,
6378 [6091]=5,
6379 [6092]=5,
6380 [6093]=5,
6381 [6094]=5,
6382 [6095]=5,
6383 [6096]=5,
6384 [6097]=5,
6385 [6098]=5,
6386 [6099]=5,
6387 [6109]=5,
6388 [6150]=4,
6389 [6151]=2,
6390 [6154]=2,
6391 [6155]=5,
6392 [6156]=5,
6393 [6157]=5,
6394 [6158]=4,
6395 [6159]=5,
6396 [6176]=2,
6397 [6177]=2,
6398 [6178]=2,
6399 [6179]=2,
6400 [6180]=2,
6401 [6181]=2,
6402 [6182]=2,
6403 [6183]=2,
6404 [6184]=2,
6405 [6185]=2,
6406 [6186]=2,
6407 [6187]=2,
6408 [6188]=2,
6409 [6189]=2,
6410 [6190]=2,
6411 [6191]=2,
6412 [6192]=2,
6413 [6193]=2,
6414 [6194]=2,
6415 [6195]=2,
6416 [6196]=2,
6417 [6197]=2,
6418 [6198]=2,
6419 [6199]=2,
6420 [6200]=2,
6421 [6201]=2,
6422 [6202]=2,
6423 [6203]=2,
6424 [6204]=2,
6425 [6205]=2,
6426 [6206]=2,
6427 [6207]=2,
6428 [6208]=2,
6429 [6209]=2,
6430 [6210]=2,
6431 [6211]=2,
6432 [6212]=2,
6433 [6213]=2,
6434 [6214]=2,
6435 [6215]=2,
6436 [6216]=2,
6437 [6217]=2,
6438 [6218]=2,
6439 [6219]=2,
6440 [6220]=2,
6441 [6221]=2,
6442 [6222]=2,
6443 [6223]=2,
6444 [6224]=2,
6445 [6225]=2,
6446 [6226]=2,
6447 [6227]=2,
6448 [6228]=2,
6449 [6229]=2,
6450 [6230]=2,
6451 [6231]=2,
6452 [6232]=2,
6453 [6233]=2,
6454 [6234]=2,
6455 [6235]=2,
6456 [6236]=2,
6457 [6237]=2,
6458 [6238]=2,
6459 [6239]=2,
6460 [6240]=2,
6461 [6241]=2,
6462 [6242]=2,
6463 [6243]=2,
6464 [6244]=2,
6465 [6245]=2,
6466 [6246]=2,
6467 [6247]=2,
6468 [6248]=2,
6469 [6249]=2,
6470 [6250]=2,
6471 [6251]=2,
6472 [6252]=2,
6473 [6253]=2,
6474 [6254]=2,
6475 [6255]=2,
6476 [6256]=2,
6477 [6257]=2,
6478 [6258]=2,
6479 [6259]=2,
6480 [6260]=2,
6481 [6261]=2,
6482 [6262]=2,
6483 [6263]=2,
6484 [6264]=2,
6485 [6272]=4,
6486 [6273]=4,
6487 [6274]=4,
6488 [6275]=4,
6489 [6276]=4,
6490 [6279]=2,
6491 [6280]=2,
6492 [6281]=2,
6493 [6282]=2,
6494 [6283]=2,
6495 [6284]=2,
6496 [6285]=2,
6497 [6286]=2,
6498 [6287]=2,
6499 [6288]=2,
6500 [6289]=2,
6501 [6290]=2,
6502 [6291]=2,
6503 [6292]=2,
6504 [6293]=2,
6505 [6294]=2,
6506 [6295]=2,
6507 [6296]=2,
6508 [6297]=2,
6509 [6298]=2,
6510 [6299]=2,
6511 [6300]=2,
6512 [6301]=2,
6513 [6302]=2,
6514 [6303]=2,
6515 [6304]=2,
6516 [6305]=2,
6517 [6306]=2,
6518 [6307]=2,
6519 [6308]=2,
6520 [6309]=2,
6521 [6310]=2,
6522 [6311]=2,
6523 [6312]=2,
6524 [6313]=5,
6525 [6314]=2,
6526 [6432]=5,
6527 [6433]=5,
6528 [6434]=5,
6529 [6439]=5,
6530 [6440]=5,
6531 [6450]=5,
6532 [6457]=5,
6533 [6458]=5,
6534 [6459]=5,
6535 [6679]=5,
6536 [6680]=5,
6537 [6742]=5,
6538 [6744]=5,
6539 [6745]=5,
6540 [6746]=5,
6541 [6747]=5,
6542 [6748]=5,
6543 [6749]=5,
6544 [6750]=5,
6545 [6752]=5,
6546 [6754]=5,
6547 [6757]=5,
6548 [6758]=5,
6549 [6759]=5,
6550 [6760]=5,
6551 [6761]=5,
6552 [6762]=5,
6553 [6763]=5,
6554 [6764]=5,
6555 [6771]=5,
6556 [6772]=5,
6557 [6773]=5,
6558 [6774]=5,
6559 [6775]=5,
6560 [6776]=5,
6561 [6777]=5,
6562 [6778]=5,
6563 [6779]=5,
6564 [6780]=5,
6565 [6783]=5,
6566 [6832]=5,
6567 [6833]=5,
6568 [6834]=5,
6569 [6835]=5,
6570 [6836]=5,
6571 [6837]=5,
6572 [6838]=5,
6573 [6839]=5,
6574 [6840]=5,
6575 [6841]=5,
6576 [6842]=5,
6577 [6843]=5,
6578 [6844]=5,
6579 [6845]=5,
6580 [6847]=5,
6581 [6848]=5,
6582 [6849]=5,
6583 [6850]=5,
6584 [6851]=5,
6585 [6852]=5,
6586 [6853]=5,
6587 [6854]=5,
6588 [6855]=5,
6589 [6856]=5,
6590 [6857]=5,
6591 [6858]=5,
6592 [6859]=5,
6593 [6860]=5,
6594 [6861]=5,
6595 [6862]=5,
6596 [6912]=5,
6597 [6913]=5,
6598 [6914]=5,
6599 [6915]=5,
6600 [6964]=5,
6601 [6966]=5,
6602 [6967]=5,
6603 [6968]=5,
6604 [6969]=5,
6605 [6970]=5,
6606 [6972]=5,
6607 [6978]=5,
6608 [7019]=5,
6609 [7020]=5,
6610 [7021]=5,
6611 [7022]=5,
6612 [7023]=5,
6613 [7024]=5,
6614 [7025]=5,
6615 [7026]=5,
6616 [7027]=5,
6617 [7040]=5,
6618 [7041]=5,
6619 [7074]=5,
6620 [7075]=5,
6621 [7076]=5,
6622 [7077]=5,
6623 [7080]=5,
6624 [7081]=5,
6625 [7083]=5,
6626 [7142]=5,
6627 [7144]=5,
6628 [7145]=5,
6629 [7149]=5,
6630 [7151]=5,
6631 [7152]=5,
6632 [7153]=5,
6633 [7212]=5,
6634 [7213]=5,
6635 [7214]=5,
6636 [7215]=5,
6637 [7216]=5,
6638 [7217]=5,
6639 [7218]=5,
6640 [7219]=5,
6641 [7222]=5,
6642 [7223]=5,
6643 [7376]=5,
6644 [7377]=5,
6645 [7378]=5,
6646 [7380]=5,
6647 [7381]=5,
6648 [7382]=5,
6649 [7383]=5,
6650 [7384]=5,
6651 [7385]=5,
6652 [7386]=5,
6653 [7387]=5,
6654 [7388]=5,
6655 [7389]=5,
6656 [7390]=5,
6657 [7391]=5,
6658 [7392]=5,
6659 [7394]=5,
6660 [7395]=5,
6661 [7396]=5,
6662 [7397]=5,
6663 [7398]=5,
6664 [7399]=5,
6665 [7400]=5,
6666 [7405]=5,
6667 [7412]=5,
6668 [7416]=5,
6669 [7417]=5,
6670 [7616]=5,
6671 [7617]=5,
6672 [7618]=5,
6673 [7619]=5,
6674 [7620]=5,
6675 [7621]=5,
6676 [7622]=5,
6677 [7623]=5,
6678 [7624]=5,
6679 [7625]=5,
6680 [7626]=5,
6681 [7627]=5,
6682 [7628]=5,
6683 [7629]=5,
6684 [7630]=5,
6685 [7631]=5,
6686 [7632]=5,
6687 [7633]=5,
6688 [7634]=5,
6689 [7635]=5,
6690 [7636]=5,
6691 [7637]=5,
6692 [7638]=5,
6693 [7639]=5,
6694 [7640]=5,
6695 [7641]=5,
6696 [7642]=5,
6697 [7643]=5,
6698 [7644]=5,
6699 [7645]=5,
6700 [7646]=5,
6701 [7647]=5,
6702 [7648]=5,
6703 [7649]=5,
6704 [7650]=5,
6705 [7651]=5,
6706 [7652]=5,
6707 [7653]=5,
6708 [7654]=5,
6709 [7655]=5,
6710 [7656]=5,
6711 [7657]=5,
6712 [7658]=5,
6713 [7659]=5,
6714 [7660]=5,
6715 [7661]=5,
6716 [7662]=5,
6717 [7663]=5,
6718 [7664]=5,
6719 [7665]=5,
6720 [7666]=5,
6721 [7667]=5,
6722 [7668]=5,
6723 [7669]=5,
6724 [7670]=5,
6725 [7671]=5,
6726 [7672]=5,
6727 [7673]=5,
6728 [7674]=5,
6729 [7675]=5,
6730 [7676]=5,
6731 [7677]=5,
6732 [7678]=5,
6733 [7679]=5,
6734 [8204]=4,
6735 [8205]=2,
6736 [8239]=4,
6737 [8294]=4,
6738 [8295]=4,
6739 [8296]=4,
6740 [8297]=4,
6741 [8400]=5,
6742 [8401]=5,
6743 [8402]=5,
6744 [8403]=5,
6745 [8404]=5,
6746 [8405]=5,
6747 [8406]=5,
6748 [8407]=5,
6749 [8408]=5,
6750 [8409]=5,
6751 [8410]=5,
6752 [8411]=5,
6753 [8412]=5,
6754 [8417]=5,
6755 [8421]=5,
6756 [8422]=5,
6757 [8423]=5,
6758 [8424]=5,
6759 [8425]=5,
6760 [8426]=5,
6761 [8427]=5,
6762 [8428]=5,
6763 [8429]=5,
6764 [8430]=5,
6765 [8431]=5,
6766 [8432]=5,
6767 [11503]=5,
6768 [11504]=5,
6769 [11505]=5,
6770 [11647]=5,
6771 [11744]=5,
6772 [11745]=5,
6773 [11746]=5,
6774 [11747]=5,
6775 [11748]=5,
6776 [11749]=5,
6777 [11750]=5,
6778 [11751]=5,
6779 [11752]=5,
6780 [11753]=5,
6781 [11754]=5,
6782 [11755]=5,
6783 [11756]=5,
6784 [11757]=5,
6785 [11758]=5,
6786 [11759]=5,
6787 [11760]=5,
6788 [11761]=5,
6789 [11762]=5,
6790 [11763]=5,
6791 [11764]=5,
6792 [11765]=5,
6793 [11766]=5,
6794 [11767]=5,
6795 [11768]=5,
6796 [11769]=5,
6797 [11770]=5,
6798 [11771]=5,
6799 [11772]=5,
6800 [11773]=5,
6801 [11774]=5,
6802 [11775]=5,
6803 [12330]=5,
6804 [12331]=5,
6805 [12332]=5,
6806 [12333]=5,
6807 [12334]=5,
6808 [12335]=5,
6809 [12441]=5,
6810 [12442]=5,
6811 [42607]=5,
6812 [42612]=5,
6813 [42613]=5,
6814 [42614]=5,
6815 [42615]=5,
6816 [42616]=5,
6817 [42617]=5,
6818 [42618]=5,
6819 [42619]=5,
6820 [42620]=5,
6821 [42621]=5,
6822 [42654]=5,
6823 [42655]=5,
6824 [42736]=5,
6825 [42737]=5,
6826 [43014]=5,
6827 [43019]=5,
6828 [43045]=5,
6829 [43046]=5,
6830 [43052]=5,
6831 [43072]=2,
6832 [43073]=2,
6833 [43074]=2,
6834 [43075]=2,
6835 [43076]=2,
6836 [43077]=2,
6837 [43078]=2,
6838 [43079]=2,
6839 [43080]=2,
6840 [43081]=2,
6841 [43082]=2,
6842 [43083]=2,
6843 [43084]=2,
6844 [43085]=2,
6845 [43086]=2,
6846 [43087]=2,
6847 [43088]=2,
6848 [43089]=2,
6849 [43090]=2,
6850 [43091]=2,
6851 [43092]=2,
6852 [43093]=2,
6853 [43094]=2,
6854 [43095]=2,
6855 [43096]=2,
6856 [43097]=2,
6857 [43098]=2,
6858 [43099]=2,
6859 [43100]=2,
6860 [43101]=2,
6861 [43102]=2,
6862 [43103]=2,
6863 [43104]=2,
6864 [43105]=2,
6865 [43106]=2,
6866 [43107]=2,
6867 [43108]=2,
6868 [43109]=2,
6869 [43110]=2,
6870 [43111]=2,
6871 [43112]=2,
6872 [43113]=2,
6873 [43114]=2,
6874 [43115]=2,
6875 [43116]=2,
6876 [43117]=2,
6877 [43118]=2,
6878 [43119]=2,
6879 [43120]=2,
6880 [43121]=2,
6881 [43122]=1,
6882 [43123]=4,
6883 [43204]=5,
6884 [43205]=5,
6885 [43232]=5,
6886 [43233]=5,
6887 [43234]=5,
6888 [43235]=5,
6889 [43236]=5,
6890 [43237]=5,
6891 [43238]=5,
6892 [43239]=5,
6893 [43240]=5,
6894 [43241]=5,
6895 [43242]=5,
6896 [43243]=5,
6897 [43244]=5,
6898 [43245]=5,
6899 [43246]=5,
6900 [43247]=5,
6901 [43248]=5,
6902 [43249]=5,
6903 [43263]=5,
6904 [43302]=5,
6905 [43303]=5,
6906 [43304]=5,
6907 [43305]=5,
6908 [43306]=5,
6909 [43307]=5,
6910 [43308]=5,
6911 [43309]=5,
6912 [43335]=5,
6913 [43336]=5,
6914 [43337]=5,
6915 [43338]=5,
6916 [43339]=5,
6917 [43340]=5,
6918 [43341]=5,
6919 [43342]=5,
6920 [43343]=5,
6921 [43344]=5,
6922 [43345]=5,
6923 [43392]=5,
6924 [43393]=5,
6925 [43394]=5,
6926 [43443]=5,
6927 [43446]=5,
6928 [43447]=5,
6929 [43448]=5,
6930 [43449]=5,
6931 [43452]=5,
6932 [43493]=5,
6933 [43561]=5,
6934 [43562]=5,
6935 [43563]=5,
6936 [43564]=5,
6937 [43565]=5,
6938 [43566]=5,
6939 [43569]=5,
6940 [43570]=5,
6941 [43573]=5,
6942 [43574]=5,
6943 [43587]=5,
6944 [43596]=5,
6945 [43644]=5,
6946 [43696]=5,
6947 [43698]=5,
6948 [43699]=5,
6949 [43700]=5,
6950 [43703]=5,
6951 [43704]=5,
6952 [43710]=5,
6953 [43711]=5,
6954 [43713]=5,
6955 [43756]=5,
6956 [43757]=5,
6957 [43766]=5,
6958 [44005]=5,
6959 [44008]=5,
6960 [44013]=5,
6961 [64286]=5,
6962 [65056]=5,
6963 [65057]=5,
6964 [65058]=5,
6965 [65059]=5,
6966 [65060]=5,
6967 [65061]=5,
6968 [65062]=5,
6969 [65063]=5,
6970 [65064]=5,
6971 [65065]=5,
6972 [65066]=5,
6973 [65067]=5,
6974 [65068]=5,
6975 [65069]=5,
6976 [65070]=5,
6977 [65071]=5,
6978 [66045]=5,
6979 [66272]=5,
6980 [66422]=5,
6981 [66423]=5,
6982 [66424]=5,
6983 [66425]=5,
6984 [66426]=5,
6985 [68097]=5,
6986 [68098]=5,
6987 [68099]=5,
6988 [68101]=5,
6989 [68102]=5,
6990 [68108]=5,
6991 [68109]=5,
6992 [68110]=5,
6993 [68111]=5,
6994 [68152]=5,
6995 [68153]=5,
6996 [68154]=5,
6997 [68159]=5,
6998 [68288]=2,
6999 [68289]=2,
7000 [68290]=2,
7001 [68291]=2,
7002 [68292]=2,
7003 [68293]=3,
7004 [68294]=4,
7005 [68295]=3,
7006 [68296]=4,
7007 [68297]=3,
7008 [68298]=3,
7009 [68299]=4,
7010 [68300]=4,
7011 [68301]=1,
7012 [68302]=3,
7013 [68303]=3,
7014 [68304]=3,
7015 [68305]=3,
7016 [68306]=3,
7017 [68307]=2,
7018 [68308]=2,
7019 [68309]=2,
7020 [68310]=2,
7021 [68311]=1,
7022 [68312]=2,
7023 [68313]=2,
7024 [68314]=2,
7025 [68315]=2,
7026 [68316]=2,
7027 [68317]=3,
7028 [68318]=2,
7029 [68319]=2,
7030 [68320]=2,
7031 [68321]=3,
7032 [68322]=4,
7033 [68323]=4,
7034 [68324]=3,
7035 [68325]=5,
7036 [68326]=5,
7037 [68331]=2,
7038 [68332]=2,
7039 [68333]=2,
7040 [68334]=2,
7041 [68335]=3,
7042 [68480]=2,
7043 [68481]=3,
7044 [68482]=2,
7045 [68483]=3,
7046 [68484]=3,
7047 [68485]=3,
7048 [68486]=2,
7049 [68487]=2,
7050 [68488]=2,
7051 [68489]=3,
7052 [68490]=2,
7053 [68491]=2,
7054 [68492]=3,
7055 [68493]=2,
7056 [68494]=3,
7057 [68495]=3,
7058 [68496]=2,
7059 [68497]=3,
7060 [68521]=3,
7061 [68522]=3,
7062 [68523]=3,
7063 [68524]=3,
7064 [68525]=2,
7065 [68526]=2,
7066 [68527]=4,
7067 [68864]=1,
7068 [68865]=2,
7069 [68866]=2,
7070 [68867]=2,
7071 [68868]=2,
7072 [68869]=2,
7073 [68870]=2,
7074 [68871]=2,
7075 [68872]=2,
7076 [68873]=2,
7077 [68874]=2,
7078 [68875]=2,
7079 [68876]=2,
7080 [68877]=2,
7081 [68878]=2,
7082 [68879]=2,
7083 [68880]=2,
7084 [68881]=2,
7085 [68882]=2,
7086 [68883]=2,
7087 [68884]=2,
7088 [68885]=2,
7089 [68886]=2,
7090 [68887]=2,
7091 [68888]=2,
7092 [68889]=2,
7093 [68890]=2,
7094 [68891]=2,
7095 [68892]=2,
7096 [68893]=2,
7097 [68894]=2,
7098 [68895]=2,
7099 [68896]=2,
7100 [68897]=2,
7101 [68898]=3,
7102 [68899]=2,
7103 [68900]=5,
7104 [68901]=5,
7105 [68902]=5,
7106 [68903]=5,
7107 [69291]=5,
7108 [69292]=5,
7109 [69424]=2,
7110 [69425]=2,
7111 [69426]=2,
7112 [69427]=3,
7113 [69428]=2,
7114 [69429]=2,
7115 [69430]=2,
7116 [69431]=2,
7117 [69432]=2,
7118 [69433]=2,
7119 [69434]=2,
7120 [69435]=2,
7121 [69436]=2,
7122 [69437]=2,
7123 [69438]=2,
7124 [69439]=2,
7125 [69440]=2,
7126 [69441]=2,
7127 [69442]=2,
7128 [69443]=2,
7129 [69444]=2,
7130 [69445]=4,
7131 [69446]=5,
7132 [69447]=5,
7133 [69448]=5,
7134 [69449]=5,
7135 [69450]=5,
7136 [69451]=5,
7137 [69452]=5,
7138 [69453]=5,
7139 [69454]=5,
7140 [69455]=5,
7141 [69456]=5,
7142 [69457]=2,
7143 [69458]=2,
7144 [69459]=2,
7145 [69460]=3,
7146 [69488]=2,
7147 [69489]=2,
7148 [69490]=2,
7149 [69491]=2,
7150 [69492]=3,
7151 [69493]=3,
7152 [69494]=2,
7153 [69495]=2,
7154 [69496]=2,
7155 [69497]=2,
7156 [69498]=2,
7157 [69499]=2,
7158 [69500]=2,
7159 [69501]=2,
7160 [69502]=2,
7161 [69503]=2,
7162 [69504]=2,
7163 [69505]=2,
7164 [69506]=5,
7165 [69507]=5,
7166 [69508]=5,
7167 [69509]=5,
7168 [69552]=2,
7169 [69553]=4,
7170 [69554]=2,
7171 [69555]=2,
7172 [69556]=3,
7173 [69557]=3,
7174 [69558]=3,
7175 [69559]=4,
7176 [69560]=2,
7177 [69561]=3,
7178 [69562]=3,
7179 [69563]=2,
7180 [69564]=2,
7181 [69565]=3,
7182 [69566]=2,
7183 [69567]=2,
7184 [69568]=4,
7185 [69569]=2,
7186 [69570]=3,
7187 [69571]=3,
7188 [69572]=2,
7189 [69573]=4,
7190 [69574]=4,
7191 [69575]=4,
7192 [69576]=4,
7193 [69577]=3,
7194 [69578]=2,
7195 [69579]=1,
7196 [69633]=5,
7197 [69688]=5,
7198 [69689]=5,
7199 [69690]=5,
7200 [69691]=5,
7201 [69692]=5,
7202 [69693]=5,
7203 [69694]=5,
7204 [69695]=5,
7205 [69696]=5,
7206 [69697]=5,
7207 [69698]=5,
7208 [69699]=5,
7209 [69700]=5,
7210 [69701]=5,
7211 [69702]=5,
7212 [69744]=5,
7213 [69747]=5,
7214 [69748]=5,
7215 [69759]=5,
7216 [69760]=5,
7217 [69761]=5,
7218 [69811]=5,
7219 [69812]=5,
7220 [69813]=5,
7221 [69814]=5,
7222 [69817]=5,
7223 [69818]=5,
7224 [69821]=4,
7225 [69826]=5,
7226 [69837]=4,
7227 [69888]=5,
7228 [69889]=5,
7229 [69890]=5,
7230 [69927]=5,
7231 [69928]=5,
7232 [69929]=5,
7233 [69930]=5,
7234 [69931]=5,
7235 [69933]=5,
7236 [69934]=5,
7237 [69935]=5,
7238 [69936]=5,
7239 [69937]=5,
7240 [69938]=5,
7241 [69939]=5,
7242 [69940]=5,
7243 [70003]=5,
7244 [70016]=5,
7245 [70017]=5,
7246 [70070]=5,
7247 [70071]=5,
7248 [70072]=5,
7249 [70073]=5,
7250 [70074]=5,
7251 [70075]=5,
7252 [70076]=5,
7253 [70077]=5,
7254 [70078]=5,
7255 [70090]=5,
7256 [70091]=5,
7257 [70092]=5,
7258 [70095]=5,
7259 [70191]=5,
7260 [70192]=5,
7261 [70193]=5,
7262 [70196]=5,
7263 [70198]=5,
7264 [70199]=5,
7265 [70206]=5,
7266 [70367]=5,
7267 [70371]=5,
7268 [70372]=5,
7269 [70373]=5,
7270 [70374]=5,
7271 [70375]=5,
7272 [70376]=5,
7273 [70377]=5,
7274 [70378]=5,
7275 [70400]=5,
7276 [70401]=5,
7277 [70459]=5,
7278 [70460]=5,
7279 [70464]=5,
7280 [70502]=5,
7281 [70503]=5,
7282 [70504]=5,
7283 [70505]=5,
7284 [70506]=5,
7285 [70507]=5,
7286 [70508]=5,
7287 [70512]=5,
7288 [70513]=5,
7289 [70514]=5,
7290 [70515]=5,
7291 [70516]=5,
7292 [70712]=5,
7293 [70713]=5,
7294 [70714]=5,
7295 [70715]=5,
7296 [70716]=5,
7297 [70717]=5,
7298 [70718]=5,
7299 [70719]=5,
7300 [70722]=5,
7301 [70723]=5,
7302 [70724]=5,
7303 [70726]=5,
7304 [70750]=5,
7305 [70835]=5,
7306 [70836]=5,
7307 [70837]=5,
7308 [70838]=5,
7309 [70839]=5,
7310 [70840]=5,
7311 [70842]=5,
7312 [70847]=5,
7313 [70848]=5,
7314 [70850]=5,
7315 [70851]=5,
7316 [71090]=5,
7317 [71091]=5,
7318 [71092]=5,
7319 [71093]=5,
7320 [71100]=5,
7321 [71101]=5,
7322 [71103]=5,
7323 [71104]=5,
7324 [71132]=5,
7325 [71133]=5,
7326 [71219]=5,
7327 [71220]=5,
7328 [71221]=5,
7329 [71222]=5,
7330 [71223]=5,
7331 [71224]=5,
7332 [71225]=5,
7333 [71226]=5,
7334 [71229]=5,
7335 [71231]=5,
7336 [71232]=5,
7337 [71339]=5,
7338 [71341]=5,
7339 [71344]=5,
7340 [71345]=5,
7341 [71346]=5,
7342 [71347]=5,
7343 [71348]=5,
7344 [71349]=5,
7345 [71351]=5,
7346 [71453]=5,
7347 [71454]=5,
7348 [71455]=5,
7349 [71458]=5,
7350 [71459]=5,
7351 [71460]=5,
7352 [71461]=5,
7353 [71463]=5,
7354 [71464]=5,
7355 [71465]=5,
7356 [71466]=5,
7357 [71467]=5,
7358 [71727]=5,
7359 [71728]=5,
7360 [71729]=5,
7361 [71730]=5,
7362 [71731]=5,
7363 [71732]=5,
7364 [71733]=5,
7365 [71734]=5,
7366 [71735]=5,
7367 [71737]=5,
7368 [71738]=5,
7369 [71995]=5,
7370 [71996]=5,
7371 [71998]=5,
7372 [72003]=5,
7373 [72148]=5,
7374 [72149]=5,
7375 [72150]=5,
7376 [72151]=5,
7377 [72154]=5,
7378 [72155]=5,
7379 [72160]=5,
7380 [72193]=5,
7381 [72194]=5,
7382 [72195]=5,
7383 [72196]=5,
7384 [72197]=5,
7385 [72198]=5,
7386 [72201]=5,
7387 [72202]=5,
7388 [72243]=5,
7389 [72244]=5,
7390 [72245]=5,
7391 [72246]=5,
7392 [72247]=5,
7393 [72248]=5,
7394 [72251]=5,
7395 [72252]=5,
7396 [72253]=5,
7397 [72254]=5,
7398 [72263]=5,
7399 [72273]=5,
7400 [72274]=5,
7401 [72275]=5,
7402 [72276]=5,
7403 [72277]=5,
7404 [72278]=5,
7405 [72281]=5,
7406 [72282]=5,
7407 [72283]=5,
7408 [72330]=5,
7409 [72331]=5,
7410 [72332]=5,
7411 [72333]=5,
7412 [72334]=5,
7413 [72335]=5,
7414 [72336]=5,
7415 [72337]=5,
7416 [72338]=5,
7417 [72339]=5,
7418 [72340]=5,
7419 [72341]=5,
7420 [72342]=5,
7421 [72344]=5,
7422 [72345]=5,
7423 [72752]=5,
7424 [72753]=5,
7425 [72754]=5,
7426 [72755]=5,
7427 [72756]=5,
7428 [72757]=5,
7429 [72758]=5,
7430 [72760]=5,
7431 [72761]=5,
7432 [72762]=5,
7433 [72763]=5,
7434 [72764]=5,
7435 [72765]=5,
7436 [72767]=5,
7437 [72850]=5,
7438 [72851]=5,
7439 [72852]=5,
7440 [72853]=5,
7441 [72854]=5,
7442 [72855]=5,
7443 [72856]=5,
7444 [72857]=5,
7445 [72858]=5,
7446 [72859]=5,
7447 [72860]=5,
7448 [72861]=5,
7449 [72862]=5,
7450 [72863]=5,
7451 [72864]=5,
7452 [72865]=5,
7453 [72866]=5,
7454 [72867]=5,
7455 [72868]=5,
7456 [72869]=5,
7457 [72870]=5,
7458 [72871]=5,
7459 [72874]=5,
7460 [72875]=5,
7461 [72876]=5,
7462 [72877]=5,
7463 [72878]=5,
7464 [72879]=5,
7465 [72880]=5,
7466 [72882]=5,
7467 [72883]=5,
7468 [72885]=5,
7469 [72886]=5,
7470 [73009]=5,
7471 [73010]=5,
7472 [73011]=5,
7473 [73012]=5,
7474 [73013]=5,
7475 [73014]=5,
7476 [73018]=5,
7477 [73020]=5,
7478 [73021]=5,
7479 [73023]=5,
7480 [73024]=5,
7481 [73025]=5,
7482 [73026]=5,
7483 [73027]=5,
7484 [73028]=5,
7485 [73029]=5,
7486 [73031]=5,
7487 [73104]=5,
7488 [73105]=5,
7489 [73109]=5,
7490 [73111]=5,
7491 [73459]=5,
7492 [73460]=5,
7493 [92912]=5,
7494 [92913]=5,
7495 [92914]=5,
7496 [92915]=5,
7497 [92916]=5,
7498 [92976]=5,
7499 [92977]=5,
7500 [92978]=5,
7501 [92979]=5,
7502 [92980]=5,
7503 [92981]=5,
7504 [92982]=5,
7505 [94031]=5,
7506 [94095]=5,
7507 [94096]=5,
7508 [94097]=5,
7509 [94098]=5,
7510 [94180]=5,
7511 [113821]=5,
7512 [113822]=5,
7513 [118528]=5,
7514 [118529]=5,
7515 [118530]=5,
7516 [118531]=5,
7517 [118532]=5,
7518 [118533]=5,
7519 [118534]=5,
7520 [118535]=5,
7521 [118536]=5,
7522 [118537]=5,
7523 [118538]=5,
7524 [118539]=5,
7525 [118540]=5,
7526 [118541]=5,
7527 [118542]=5,
7528 [118543]=5,
7529 [118544]=5,
7530 [118545]=5,
7531 [118546]=5,
7532 [118547]=5,
7533 [118548]=5,
7534 [118549]=5,
7535 [118550]=5,
7536 [118551]=5,
7537 [118552]=5,
7538 [118553]=5,
7539 [118554]=5,
7540 [118555]=5,
7541 [118556]=5,
7542 [118557]=5,
7543 [118558]=5,
7544 [118559]=5,
7545 [118560]=5,
7546 [118561]=5,
7547 [118562]=5,
7548 [118563]=5,
7549 [118564]=5,
7550 [118565]=5,
7551 [118566]=5,
7552 [118567]=5,
7553 [118568]=5,
7554 [118569]=5,
7555 [118570]=5,
7556 [118571]=5,
7557 [118572]=5,
7558 [118573]=5,
7559 [118576]=5,
7560 [118577]=5,
7561 [118578]=5,
7562 [118579]=5,
7563 [118580]=5,
7564 [118581]=5,
7565 [118582]=5,
7566 [118583]=5,
7567 [118584]=5,
7568 [118585]=5,
7569 [118586]=5,
7570 [118587]=5,
7571 [118588]=5,
7572 [118589]=5,
7573 [118590]=5,
7574 [118591]=5,
7575 [118592]=5,
7576 [118593]=5,
7577 [118594]=5,
7578 [118595]=5,
7579 [118596]=5,
7580 [118597]=5,
7581 [118598]=5,
7582 [119143]=5,
7583 [119144]=5,
7584 [119145]=5,
7585 [119163]=5,
7586 [119164]=5,
7587 [119165]=5,
7588 [119166]=5,
7589 [119167]=5,
7590 [119168]=5,
7591 [119169]=5,
7592 [119170]=5,
7593 [119173]=5,
7594 [119174]=5,
7595 [119175]=5,
7596 [119176]=5,
7597 [119177]=5,
7598 [119178]=5,
7599 [119179]=5,
7600 [119210]=5,
7601 [119211]=5,
7602 [119212]=5,
7603 [119213]=5,
7604 [119362]=5,
7605 [119363]=5,
7606 [119364]=5,
7607 [121344]=5,
7608 [121345]=5,
7609 [121346]=5,
7610 [121347]=5,
7611 [121348]=5,
7612 [121349]=5,
7613 [121350]=5,
7614 [121351]=5,
7615 [121352]=5,
7616 [121353]=5,
7617 [121354]=5,
7618 [121355]=5,
7619 [121356]=5,
7620 [121357]=5,
7621 [121358]=5,
7622 [121359]=5,
7623 [121360]=5,
7624 [121361]=5,
7625 [121362]=5,
7626 [121363]=5,
7627 [121364]=5,
7628 [121365]=5,
7629 [121366]=5,
7630 [121367]=5,
7631 [121368]=5,
7632 [121369]=5,
7633 [121370]=5,
7634 [121371]=5,
7635 [121372]=5,
7636 [121373]=5,
7637 [121374]=5,
7638 [121375]=5,
7639 [121376]=5,
7640 [121377]=5,
7641 [121378]=5,
7642 [121379]=5,
7643 [121380]=5,
7644 [121381]=5,
7645 [121382]=5,
7646 [121383]=5,
7647 [121384]=5,
7648 [121385]=5,
7649 [121386]=5,
7650 [121387]=5,
7651 [121388]=5,
7652 [121389]=5,
7653 [121390]=5,
7654 [121391]=5,
7655 [121392]=5,
7656 [121393]=5,
7657 [121394]=5,
7658 [121395]=5,
7659 [121396]=5,
7660 [121397]=5,
7661 [121398]=5,
7662 [121403]=5,
7663 [121404]=5,
7664 [121405]=5,
7665 [121406]=5,
7666 [121407]=5,
7667 [121408]=5,
7668 [121409]=5,
7669 [121410]=5,
7670 [121411]=5,
7671 [121412]=5,
7672 [121413]=5,
7673 [121414]=5,
7674 [121415]=5,
7675 [121416]=5,
7676 [121417]=5,
7677 [121418]=5,
7678 [121419]=5,
7679 [121420]=5,
7680 [121421]=5,
7681 [121422]=5,
7682 [121423]=5,
7683 [121424]=5,
7684 [121425]=5,
7685 [121426]=5,
7686 [121427]=5,
7687 [121428]=5,
7688 [121429]=5,
7689 [121430]=5,
7690 [121431]=5,
7691 [121432]=5,
7692 [121433]=5,
7693 [121434]=5,
7694 [121435]=5,
7695 [121436]=5,
7696 [121437]=5,
7697 [121438]=5,
7698 [121439]=5,
7699 [121440]=5,
7700 [121441]=5,
7701 [121442]=5,
7702 [121443]=5,
7703 [121444]=5,
7704 [121445]=5,
7705 [121446]=5,
7706 [121447]=5,
7707 [121448]=5,
7708 [121449]=5,
7709 [121450]=5,
7710 [121451]=5,
7711 [121452]=5,
7712 [121461]=5,
7713 [121476]=5,
7714 [121499]=5,
7715 [121500]=5,
7716 [121501]=5,
7717 [121502]=5,
7718 [121503]=5,
7719 [121505]=5,
7720 [121506]=5,
7721 [121507]=5,
7722 [121508]=5,
7723 [121509]=5,
7724 [121510]=5,
7725 [121511]=5,
7726 [121512]=5,
7727 [121513]=5,
7728 [121514]=5,
7729 [121515]=5,
7730 [121516]=5,
7731 [121517]=5,
7732 [121518]=5,
7733 [121519]=5,
7734 [122880]=5,
7735 [122881]=5,
7736 [122882]=5,
7737 [122883]=5,
7738 [122884]=5,
7739 [122885]=5,
7740 [122886]=5,
7741 [122888]=5,
7742 [122889]=5,
7743 [122890]=5,
7744 [122891]=5,
7745 [122892]=5,
7746 [122893]=5,
7747 [122894]=5,
7748 [122895]=5,
7749 [122896]=5,
7750 [122897]=5,
7751 [122898]=5,
7752 [122899]=5,
7753 [122900]=5,
7754 [122901]=5,
7755 [122902]=5,
7756 [122903]=5,
7757 [122904]=5,
7758 [122907]=5,
7759 [122908]=5,
7760 [122909]=5,
7761 [122910]=5,
7762 [122911]=5,
7763 [122912]=5,
7764 [122913]=5,
7765 [122915]=5,
7766 [122916]=5,
7767 [122918]=5,
7768 [122919]=5,
7769 [122920]=5,
7770 [122921]=5,
7771 [122922]=5,
7772 [123184]=5,
7773 [123185]=5,
7774 [123186]=5,
7775 [123187]=5,
7776 [123188]=5,
7777 [123189]=5,
7778 [123190]=5,
7779 [123566]=5,
7780 [123628]=5,
7781 [123629]=5,
7782 [123630]=5,
7783 [123631]=5,
7784 [125136]=5,
7785 [125137]=5,
7786 [125138]=5,
7787 [125139]=5,
7788 [125140]=5,
7789 [125141]=5,
7790 [125142]=5,
7791 [125184]=2,
7792 [125185]=2,
7793 [125186]=2,
7794 [125187]=2,
7795 [125188]=2,
7796 [125189]=2,
7797 [125190]=2,
7798 [125191]=2,
7799 [125192]=2,
7800 [125193]=2,
7801 [125194]=2,
7802 [125195]=2,
7803 [125196]=2,
7804 [125197]=2,
7805 [125198]=2,
7806 [125199]=2,
7807 [125200]=2,
7808 [125201]=2,
7809 [125202]=2,
7810 [125203]=2,
7811 [125204]=2,
7812 [125205]=2,
7813 [125206]=2,
7814 [125207]=2,
7815 [125208]=2,
7816 [125209]=2,
7817 [125210]=2,
7818 [125211]=2,
7819 [125212]=2,
7820 [125213]=2,
7821 [125214]=2,
7822 [125215]=2,
7823 [125216]=2,
7824 [125217]=2,
7825 [125218]=2,
7826 [125219]=2,
7827 [125220]=2,
7828 [125221]=2,
7829 [125222]=2,
7830 [125223]=2,
7831 [125224]=2,
7832 [125225]=2,
7833 [125226]=2,
7834 [125227]=2,
7835 [125228]=2,
7836 [125229]=2,
7837 [125230]=2,
7838 [125231]=2,
7839 [125232]=2,
7840 [125233]=2,
7841 [125234]=2,
7842 [125235]=2,
7843 [125236]=2,
7844 [125237]=2,
7845 [125238]=2,
7846 [125239]=2,
7847 [125240]=2,
7848 [125241]=2,
7849 [125242]=2,
7850 [125243]=2,
7851 [125244]=2,
7852 [125245]=2,
7853 [125246]=2,
7854 [125247]=2,
7855 [125248]=2,
7856 [125249]=2,
7857 [125250]=2,
7858 [125251]=2,
7859 [125252]=5,
7860 [125253]=5,
7861 [125254]=5,
7862 [125255]=5,
7863 [125256]=5,
7864 [125257]=5,
7865 [125258]=5,
7866 [1042752]=5,
7867}
7868characters.indicgroups={
7869 ["above_mark"]={
7870  [2304]=true,
7871  [2305]=true,
7872  [2306]=true,
7873  [2362]=true,
7874  [2373]=true,
7875  [2374]=true,
7876  [2375]=true,
7877  [2376]=true,
7878  [2385]=true,
7879  [2387]=true,
7880  [2388]=true,
7881  [2389]=true,
7882  [2631]=true,
7883  [2632]=true,
7884  [2635]=true,
7885  [2636]=true,
7886  [2690]=true,
7887  [2757]=true,
7888  [2759]=true,
7889  [2760]=true,
7890  [2879]=true,
7891  [3008]=true,
7892  [3021]=true,
7893  [3134]=true,
7894  [3135]=true,
7895  [3136]=true,
7896  [3142]=true,
7897  [3143]=true,
7898  [3146]=true,
7899  [3147]=true,
7900  [3148]=true,
7901  [3149]=true,
7902  [3263]=true,
7903  [3270]=true,
7904  [3406]=true,
7905  [4141]=true,
7906  [4142]=true,
7907  [4146]=true,
7908  [4147]=true,
7909  [4148]=true,
7910  [4149]=true,
7911  [4150]=true,
7912  [4154]=true,
7913  [4209]=true,
7914  [4210]=true,
7915  [4211]=true,
7916  [4212]=true,
7917  [4229]=true,
7918  [4230]=true,
7919  [4253]=true,
7920  [43232]=true,
7921  [43233]=true,
7922  [43234]=true,
7923  [43235]=true,
7924  [43236]=true,
7925  [43237]=true,
7926  [43238]=true,
7927  [43239]=true,
7928  [43240]=true,
7929  [43241]=true,
7930  [43242]=true,
7931  [43243]=true,
7932  [43244]=true,
7933  [43245]=true,
7934  [43246]=true,
7935  [43247]=true,
7936  [43248]=true,
7937  [43249]=true,
7938  [43493]=true,
7939  [43644]=true,
7940 },
7941 ["after_half"]={},
7942 ["after_main"]={
7943  [2864]=true,
7944  [2879]=true,
7945  [2902]=true,
7946  [3376]=true,
7947  [5901]=true,
7948 },
7949 ["after_postscript"]={
7950  [2433]=true,
7951  [2494]=true,
7952  [2496]=true,
7953  [2519]=true,
7954  [2561]=true,
7955  [2562]=true,
7956  [2622]=true,
7957  [2624]=true,
7958  [2625]=true,
7959  [2626]=true,
7960  [2672]=true,
7961  [2673]=true,
7962  [2735]=true,
7963  [2750]=true,
7964  [2752]=true,
7965  [2753]=true,
7966  [2754]=true,
7967  [2755]=true,
7968  [2756]=true,
7969  [2761]=true,
7970  [2763]=true,
7971  [2764]=true,
7972  [2786]=true,
7973  [2787]=true,
7974  [2878]=true,
7975  [2880]=true,
7976  [2903]=true,
7977  [2992]=true,
7978  [3006]=true,
7979  [3007]=true,
7980  [3009]=true,
7981  [3010]=true,
7982  [3031]=true,
7983  [3120]=true,
7984  [3248]=true,
7985  [3390]=true,
7986  [3391]=true,
7987  [3392]=true,
7988  [3393]=true,
7989  [3394]=true,
7990  [3395]=true,
7991  [3415]=true,
7992 },
7993 ["after_subscript"]={
7994  [2366]=true,
7995  [2368]=true,
7996  [2369]=true,
7997  [2370]=true,
7998  [2371]=true,
7999  [2372]=true,
8000  [2373]=true,
8001  [2374]=true,
8002  [2375]=true,
8003  [2376]=true,
8004  [2377]=true,
8005  [2378]=true,
8006  [2379]=true,
8007  [2380]=true,
8008  [2402]=true,
8009  [2403]=true,
8010  [2480]=true,
8011  [2497]=true,
8012  [2498]=true,
8013  [2499]=true,
8014  [2500]=true,
8015  [2530]=true,
8016  [2531]=true,
8017  [2544]=true,
8018  [2631]=true,
8019  [2632]=true,
8020  [2635]=true,
8021  [2636]=true,
8022  [2757]=true,
8023  [2759]=true,
8024  [2760]=true,
8025  [2881]=true,
8026  [2882]=true,
8027  [2883]=true,
8028  [3008]=true,
8029  [3139]=true,
8030  [3140]=true,
8031  [3267]=true,
8032  [3268]=true,
8033  [3285]=true,
8034  [3286]=true,
8035 },
8036 ["anudatta"]={
8037  [2386]=true,
8038 },
8039 ["before_half"]={
8040  [2367]=true,
8041  [2382]=true,
8042  [2495]=true,
8043  [2503]=true,
8044  [2504]=true,
8045  [2623]=true,
8046  [2751]=true,
8047  [2887]=true,
8048 },
8049 ["before_main"]={
8050  [3014]=true,
8051  [3015]=true,
8052  [3016]=true,
8053  [3398]=true,
8054  [3399]=true,
8055  [3400]=true,
8056 },
8057 ["before_postscript"]={
8058  [2352]=true,
8059  [2736]=true,
8060 },
8061 ["before_subscript"]={
8062  [2608]=true,
8063  [2817]=true,
8064  [3134]=true,
8065  [3135]=true,
8066  [3136]=true,
8067  [3137]=true,
8068  [3138]=true,
8069  [3142]=true,
8070  [3143]=true,
8071  [3146]=true,
8072  [3147]=true,
8073  [3148]=true,
8074  [3157]=true,
8075  [3158]=true,
8076  [3262]=true,
8077  [3263]=true,
8078  [3265]=true,
8079  [3266]=true,
8080  [3270]=true,
8081  [3276]=true,
8082  [3298]=true,
8083  [3299]=true,
8084 },
8085 ["below_mark"]={
8086  [2364]=true,
8087  [2369]=true,
8088  [2370]=true,
8089  [2371]=true,
8090  [2372]=true,
8091  [2381]=true,
8092  [2386]=true,
8093  [2390]=true,
8094  [2391]=true,
8095  [2402]=true,
8096  [2403]=true,
8097  [2492]=true,
8098  [2497]=true,
8099  [2498]=true,
8100  [2499]=true,
8101  [2500]=true,
8102  [2509]=true,
8103  [2620]=true,
8104  [2625]=true,
8105  [2626]=true,
8106  [2637]=true,
8107  [2748]=true,
8108  [2753]=true,
8109  [2754]=true,
8110  [2755]=true,
8111  [2756]=true,
8112  [2765]=true,
8113  [2876]=true,
8114  [2881]=true,
8115  [2882]=true,
8116  [2883]=true,
8117  [2884]=true,
8118  [2893]=true,
8119  [2914]=true,
8120  [2915]=true,
8121  [3009]=true,
8122  [3010]=true,
8123  [3132]=true,
8124  [3170]=true,
8125  [3171]=true,
8126  [3260]=true,
8127  [3286]=true,
8128  [3298]=true,
8129  [3299]=true,
8130  [3426]=true,
8131  [3427]=true,
8132  [4143]=true,
8133  [4144]=true,
8134  [4151]=true,
8135  [4153]=true,
8136  [4157]=true,
8137  [4158]=true,
8138  [4184]=true,
8139  [4185]=true,
8140  [4190]=true,
8141  [4191]=true,
8142  [4192]=true,
8143  [4226]=true,
8144  [4237]=true,
8145 },
8146 ["consonant"]={
8147  [2325]=true,
8148  [2326]=true,
8149  [2327]=true,
8150  [2328]=true,
8151  [2329]=true,
8152  [2330]=true,
8153  [2331]=true,
8154  [2332]=true,
8155  [2333]=true,
8156  [2334]=true,
8157  [2335]=true,
8158  [2336]=true,
8159  [2337]=true,
8160  [2338]=true,
8161  [2339]=true,
8162  [2340]=true,
8163  [2341]=true,
8164  [2342]=true,
8165  [2343]=true,
8166  [2344]=true,
8167  [2345]=true,
8168  [2346]=true,
8169  [2347]=true,
8170  [2348]=true,
8171  [2349]=true,
8172  [2350]=true,
8173  [2351]=true,
8174  [2352]=true,
8175  [2353]=true,
8176  [2354]=true,
8177  [2355]=true,
8178  [2356]=true,
8179  [2357]=true,
8180  [2358]=true,
8181  [2359]=true,
8182  [2360]=true,
8183  [2361]=true,
8184  [2392]=true,
8185  [2393]=true,
8186  [2394]=true,
8187  [2395]=true,
8188  [2396]=true,
8189  [2397]=true,
8190  [2398]=true,
8191  [2399]=true,
8192  [2424]=true,
8193  [2425]=true,
8194  [2426]=true,
8195  [2453]=true,
8196  [2454]=true,
8197  [2455]=true,
8198  [2456]=true,
8199  [2457]=true,
8200  [2458]=true,
8201  [2459]=true,
8202  [2460]=true,
8203  [2461]=true,
8204  [2462]=true,
8205  [2463]=true,
8206  [2464]=true,
8207  [2465]=true,
8208  [2466]=true,
8209  [2467]=true,
8210  [2468]=true,
8211  [2469]=true,
8212  [2470]=true,
8213  [2471]=true,
8214  [2472]=true,
8215  [2474]=true,
8216  [2475]=true,
8217  [2476]=true,
8218  [2477]=true,
8219  [2478]=true,
8220  [2479]=true,
8221  [2480]=true,
8222  [2482]=true,
8223  [2486]=true,
8224  [2487]=true,
8225  [2488]=true,
8226  [2489]=true,
8227  [2510]=true,
8228  [2524]=true,
8229  [2525]=true,
8230  [2527]=true,
8231  [2581]=true,
8232  [2582]=true,
8233  [2583]=true,
8234  [2584]=true,
8235  [2585]=true,
8236  [2586]=true,
8237  [2587]=true,
8238  [2588]=true,
8239  [2589]=true,
8240  [2590]=true,
8241  [2591]=true,
8242  [2592]=true,
8243  [2593]=true,
8244  [2594]=true,
8245  [2595]=true,
8246  [2596]=true,
8247  [2597]=true,
8248  [2598]=true,
8249  [2599]=true,
8250  [2600]=true,
8251  [2602]=true,
8252  [2603]=true,
8253  [2604]=true,
8254  [2605]=true,
8255  [2606]=true,
8256  [2607]=true,
8257  [2608]=true,
8258  [2610]=true,
8259  [2611]=true,
8260  [2613]=true,
8261  [2614]=true,
8262  [2616]=true,
8263  [2617]=true,
8264  [2649]=true,
8265  [2650]=true,
8266  [2651]=true,
8267  [2652]=true,
8268  [2654]=true,
8269  [2709]=true,
8270  [2710]=true,
8271  [2711]=true,
8272  [2712]=true,
8273  [2713]=true,
8274  [2714]=true,
8275  [2715]=true,
8276  [2716]=true,
8277  [2717]=true,
8278  [2718]=true,
8279  [2719]=true,
8280  [2720]=true,
8281  [2721]=true,
8282  [2722]=true,
8283  [2723]=true,
8284  [2724]=true,
8285  [2725]=true,
8286  [2726]=true,
8287  [2727]=true,
8288  [2728]=true,
8289  [2730]=true,
8290  [2731]=true,
8291  [2732]=true,
8292  [2733]=true,
8293  [2734]=true,
8294  [2735]=true,
8295  [2736]=true,
8296  [2738]=true,
8297  [2739]=true,
8298  [2741]=true,
8299  [2742]=true,
8300  [2743]=true,
8301  [2744]=true,
8302  [2745]=true,
8303  [2837]=true,
8304  [2838]=true,
8305  [2839]=true,
8306  [2840]=true,
8307  [2841]=true,
8308  [2842]=true,
8309  [2843]=true,
8310  [2844]=true,
8311  [2845]=true,
8312  [2846]=true,
8313  [2847]=true,
8314  [2848]=true,
8315  [2849]=true,
8316  [2850]=true,
8317  [2851]=true,
8318  [2852]=true,
8319  [2853]=true,
8320  [2854]=true,
8321  [2855]=true,
8322  [2856]=true,
8323  [2858]=true,
8324  [2859]=true,
8325  [2860]=true,
8326  [2861]=true,
8327  [2862]=true,
8328  [2863]=true,
8329  [2864]=true,
8330  [2866]=true,
8331  [2867]=true,
8332  [2869]=true,
8333  [2870]=true,
8334  [2871]=true,
8335  [2872]=true,
8336  [2873]=true,
8337  [2908]=true,
8338  [2909]=true,
8339  [2929]=true,
8340  [2965]=true,
8341  [2969]=true,
8342  [2970]=true,
8343  [2972]=true,
8344  [2974]=true,
8345  [2975]=true,
8346  [2979]=true,
8347  [2980]=true,
8348  [2984]=true,
8349  [2985]=true,
8350  [2986]=true,
8351  [2990]=true,
8352  [2991]=true,
8353  [2992]=true,
8354  [2993]=true,
8355  [2994]=true,
8356  [2995]=true,
8357  [2996]=true,
8358  [2997]=true,
8359  [2998]=true,
8360  [2999]=true,
8361  [3000]=true,
8362  [3001]=true,
8363  [3093]=true,
8364  [3094]=true,
8365  [3095]=true,
8366  [3096]=true,
8367  [3097]=true,
8368  [3098]=true,
8369  [3099]=true,
8370  [3100]=true,
8371  [3101]=true,
8372  [3102]=true,
8373  [3103]=true,
8374  [3104]=true,
8375  [3105]=true,
8376  [3106]=true,
8377  [3107]=true,
8378  [3108]=true,
8379  [3109]=true,
8380  [3110]=true,
8381  [3111]=true,
8382  [3112]=true,
8383  [3114]=true,
8384  [3115]=true,
8385  [3116]=true,
8386  [3117]=true,
8387  [3118]=true,
8388  [3119]=true,
8389  [3120]=true,
8390  [3121]=true,
8391  [3122]=true,
8392  [3123]=true,
8393  [3124]=true,
8394  [3125]=true,
8395  [3126]=true,
8396  [3127]=true,
8397  [3128]=true,
8398  [3129]=true,
8399  [3133]=true,
8400  [3221]=true,
8401  [3222]=true,
8402  [3223]=true,
8403  [3224]=true,
8404  [3225]=true,
8405  [3226]=true,
8406  [3227]=true,
8407  [3228]=true,
8408  [3229]=true,
8409  [3230]=true,
8410  [3231]=true,
8411  [3232]=true,
8412  [3233]=true,
8413  [3234]=true,
8414  [3235]=true,
8415  [3236]=true,
8416  [3237]=true,
8417  [3238]=true,
8418  [3239]=true,
8419  [3240]=true,
8420  [3242]=true,
8421  [3243]=true,
8422  [3244]=true,
8423  [3245]=true,
8424  [3246]=true,
8425  [3247]=true,
8426  [3248]=true,
8427  [3249]=true,
8428  [3250]=true,
8429  [3251]=true,
8430  [3253]=true,
8431  [3254]=true,
8432  [3255]=true,
8433  [3256]=true,
8434  [3257]=true,
8435  [3294]=true,
8436  [3349]=true,
8437  [3350]=true,
8438  [3351]=true,
8439  [3352]=true,
8440  [3353]=true,
8441  [3354]=true,
8442  [3355]=true,
8443  [3356]=true,
8444  [3357]=true,
8445  [3358]=true,
8446  [3359]=true,
8447  [3360]=true,
8448  [3361]=true,
8449  [3362]=true,
8450  [3363]=true,
8451  [3364]=true,
8452  [3365]=true,
8453  [3366]=true,
8454  [3367]=true,
8455  [3368]=true,
8456  [3369]=true,
8457  [3370]=true,
8458  [3371]=true,
8459  [3372]=true,
8460  [3373]=true,
8461  [3374]=true,
8462  [3375]=true,
8463  [3376]=true,
8464  [3377]=true,
8465  [3378]=true,
8466  [3379]=true,
8467  [3380]=true,
8468  [3381]=true,
8469  [3382]=true,
8470  [3383]=true,
8471  [3384]=true,
8472  [3385]=true,
8473  [3386]=true,
8474  [4096]=true,
8475  [4097]=true,
8476  [4098]=true,
8477  [4099]=true,
8478  [4100]=true,
8479  [4101]=true,
8480  [4102]=true,
8481  [4103]=true,
8482  [4104]=true,
8483  [4105]=true,
8484  [4106]=true,
8485  [4107]=true,
8486  [4108]=true,
8487  [4109]=true,
8488  [4110]=true,
8489  [4111]=true,
8490  [4112]=true,
8491  [4113]=true,
8492  [4114]=true,
8493  [4115]=true,
8494  [4116]=true,
8495  [4117]=true,
8496  [4118]=true,
8497  [4119]=true,
8498  [4120]=true,
8499  [4121]=true,
8500  [4122]=true,
8501  [4123]=true,
8502  [4124]=true,
8503  [4125]=true,
8504  [4126]=true,
8505  [4127]=true,
8506  [4128]=true,
8507  [4155]=true,
8508  [4156]=true,
8509  [4157]=true,
8510  [4158]=true,
8511  [4159]=true,
8512  [4176]=true,
8513  [4177]=true,
8514  [4186]=true,
8515  [4187]=true,
8516  [4188]=true,
8517  [4189]=true,
8518  [4190]=true,
8519  [4191]=true,
8520  [4192]=true,
8521  [4193]=true,
8522  [4197]=true,
8523  [4198]=true,
8524  [4206]=true,
8525  [4207]=true,
8526  [4208]=true,
8527  [4213]=true,
8528  [4214]=true,
8529  [4215]=true,
8530  [4216]=true,
8531  [4217]=true,
8532  [4218]=true,
8533  [4219]=true,
8534  [4220]=true,
8535  [4221]=true,
8536  [4222]=true,
8537  [4223]=true,
8538  [4224]=true,
8539  [4225]=true,
8540  [4226]=true,
8541  [4238]=true,
8542  [5901]=true,
8543  [43488]=true,
8544  [43489]=true,
8545  [43490]=true,
8546  [43491]=true,
8547  [43492]=true,
8548  [43495]=true,
8549  [43496]=true,
8550  [43497]=true,
8551  [43498]=true,
8552  [43499]=true,
8553  [43500]=true,
8554  [43501]=true,
8555  [43502]=true,
8556  [43503]=true,
8557  [43514]=true,
8558  [43515]=true,
8559  [43516]=true,
8560  [43517]=true,
8561  [43518]=true,
8562  [43616]=true,
8563  [43617]=true,
8564  [43618]=true,
8565  [43619]=true,
8566  [43620]=true,
8567  [43621]=true,
8568  [43622]=true,
8569  [43623]=true,
8570  [43624]=true,
8571  [43625]=true,
8572  [43626]=true,
8573  [43628]=true,
8574  [43629]=true,
8575  [43630]=true,
8576  [43631]=true,
8577  [43633]=true,
8578  [43634]=true,
8579  [43635]=true,
8580  [43636]=true,
8581  [43637]=true,
8582  [43638]=true,
8583  [43642]=true,
8584  [43646]=true,
8585  [43647]=true,
8586 },
8587 ["dependent_vowel"]={
8588  [2362]=true,
8589  [2363]=true,
8590  [2366]=true,
8591  [2367]=true,
8592  [2368]=true,
8593  [2369]=true,
8594  [2370]=true,
8595  [2371]=true,
8596  [2372]=true,
8597  [2373]=true,
8598  [2374]=true,
8599  [2375]=true,
8600  [2376]=true,
8601  [2377]=true,
8602  [2378]=true,
8603  [2379]=true,
8604  [2380]=true,
8605  [2382]=true,
8606  [2383]=true,
8607  [2389]=true,
8608  [2390]=true,
8609  [2391]=true,
8610  [2402]=true,
8611  [2403]=true,
8612  [2494]=true,
8613  [2495]=true,
8614  [2497]=true,
8615  [2498]=true,
8616  [2499]=true,
8617  [2500]=true,
8618  [2503]=true,
8619  [2504]=true,
8620  [2507]=true,
8621  [2508]=true,
8622  [2622]=true,
8623  [2623]=true,
8624  [2624]=true,
8625  [2625]=true,
8626  [2626]=true,
8627  [2631]=true,
8628  [2632]=true,
8629  [2635]=true,
8630  [2636]=true,
8631  [2750]=true,
8632  [2751]=true,
8633  [2752]=true,
8634  [2753]=true,
8635  [2754]=true,
8636  [2755]=true,
8637  [2756]=true,
8638  [2757]=true,
8639  [2759]=true,
8640  [2760]=true,
8641  [2761]=true,
8642  [2763]=true,
8643  [2764]=true,
8644  [2878]=true,
8645  [2879]=true,
8646  [2880]=true,
8647  [2881]=true,
8648  [2882]=true,
8649  [2883]=true,
8650  [2884]=true,
8651  [2887]=true,
8652  [2888]=true,
8653  [2891]=true,
8654  [2892]=true,
8655  [2914]=true,
8656  [2915]=true,
8657  [3006]=true,
8658  [3007]=true,
8659  [3008]=true,
8660  [3009]=true,
8661  [3010]=true,
8662  [3014]=true,
8663  [3015]=true,
8664  [3016]=true,
8665  [3018]=true,
8666  [3019]=true,
8667  [3020]=true,
8668  [3134]=true,
8669  [3135]=true,
8670  [3136]=true,
8671  [3137]=true,
8672  [3138]=true,
8673  [3139]=true,
8674  [3140]=true,
8675  [3142]=true,
8676  [3143]=true,
8677  [3144]=true,
8678  [3146]=true,
8679  [3147]=true,
8680  [3148]=true,
8681  [3170]=true,
8682  [3171]=true,
8683  [3262]=true,
8684  [3263]=true,
8685  [3264]=true,
8686  [3265]=true,
8687  [3266]=true,
8688  [3267]=true,
8689  [3268]=true,
8690  [3270]=true,
8691  [3271]=true,
8692  [3272]=true,
8693  [3274]=true,
8694  [3275]=true,
8695  [3276]=true,
8696  [3285]=true,
8697  [3286]=true,
8698  [3298]=true,
8699  [3299]=true,
8700  [3390]=true,
8701  [3391]=true,
8702  [3392]=true,
8703  [3393]=true,
8704  [3394]=true,
8705  [3395]=true,
8706  [3396]=true,
8707  [3398]=true,
8708  [3399]=true,
8709  [3400]=true,
8710  [3402]=true,
8711  [3403]=true,
8712  [3404]=true,
8713  [3415]=true,
8714  [3426]=true,
8715  [3427]=true,
8716  [4139]=true,
8717  [4140]=true,
8718  [4141]=true,
8719  [4142]=true,
8720  [4143]=true,
8721  [4144]=true,
8722  [4145]=true,
8723  [4146]=true,
8724  [4147]=true,
8725  [4148]=true,
8726  [4149]=true,
8727  [4182]=true,
8728  [4183]=true,
8729  [4184]=true,
8730  [4185]=true,
8731  [4194]=true,
8732  [4199]=true,
8733  [4200]=true,
8734  [4209]=true,
8735  [4210]=true,
8736  [4211]=true,
8737  [4212]=true,
8738  [4227]=true,
8739  [4228]=true,
8740  [4229]=true,
8741  [4230]=true,
8742  [4252]=true,
8743  [4253]=true,
8744  [43493]=true,
8745 },
8746 ["halant"]={
8747  [2381]=true,
8748  [2509]=true,
8749  [2637]=true,
8750  [2765]=true,
8751  [2893]=true,
8752  [3021]=true,
8753  [3149]=true,
8754  [3277]=true,
8755  [3405]=true,
8756 },
8757 ["independent_vowel"]={
8758  [2308]=true,
8759  [2309]=true,
8760  [2310]=true,
8761  [2311]=true,
8762  [2312]=true,
8763  [2313]=true,
8764  [2314]=true,
8765  [2315]=true,
8766  [2316]=true,
8767  [2317]=true,
8768  [2318]=true,
8769  [2319]=true,
8770  [2320]=true,
8771  [2321]=true,
8772  [2322]=true,
8773  [2323]=true,
8774  [2324]=true,
8775  [2400]=true,
8776  [2401]=true,
8777  [2418]=true,
8778  [2419]=true,
8779  [2420]=true,
8780  [2421]=true,
8781  [2422]=true,
8782  [2423]=true,
8783  [2437]=true,
8784  [2438]=true,
8785  [2439]=true,
8786  [2440]=true,
8787  [2441]=true,
8788  [2442]=true,
8789  [2443]=true,
8790  [2444]=true,
8791  [2447]=true,
8792  [2448]=true,
8793  [2451]=true,
8794  [2452]=true,
8795  [2528]=true,
8796  [2529]=true,
8797  [2530]=true,
8798  [2531]=true,
8799  [2565]=true,
8800  [2566]=true,
8801  [2567]=true,
8802  [2568]=true,
8803  [2569]=true,
8804  [2570]=true,
8805  [2575]=true,
8806  [2576]=true,
8807  [2579]=true,
8808  [2580]=true,
8809  [2693]=true,
8810  [2694]=true,
8811  [2695]=true,
8812  [2696]=true,
8813  [2697]=true,
8814  [2698]=true,
8815  [2699]=true,
8816  [2700]=true,
8817  [2701]=true,
8818  [2703]=true,
8819  [2704]=true,
8820  [2705]=true,
8821  [2707]=true,
8822  [2708]=true,
8823  [2784]=true,
8824  [2785]=true,
8825  [2786]=true,
8826  [2787]=true,
8827  [2821]=true,
8828  [2822]=true,
8829  [2823]=true,
8830  [2824]=true,
8831  [2825]=true,
8832  [2826]=true,
8833  [2827]=true,
8834  [2828]=true,
8835  [2831]=true,
8836  [2832]=true,
8837  [2835]=true,
8838  [2836]=true,
8839  [2912]=true,
8840  [2913]=true,
8841  [2949]=true,
8842  [2950]=true,
8843  [2951]=true,
8844  [2952]=true,
8845  [2953]=true,
8846  [2954]=true,
8847  [2958]=true,
8848  [2959]=true,
8849  [2960]=true,
8850  [2962]=true,
8851  [2963]=true,
8852  [2964]=true,
8853  [3077]=true,
8854  [3078]=true,
8855  [3079]=true,
8856  [3080]=true,
8857  [3081]=true,
8858  [3082]=true,
8859  [3083]=true,
8860  [3084]=true,
8861  [3086]=true,
8862  [3087]=true,
8863  [3088]=true,
8864  [3090]=true,
8865  [3091]=true,
8866  [3092]=true,
8867  [3165]=true,
8868  [3168]=true,
8869  [3169]=true,
8870  [3205]=true,
8871  [3206]=true,
8872  [3207]=true,
8873  [3208]=true,
8874  [3209]=true,
8875  [3210]=true,
8876  [3211]=true,
8877  [3212]=true,
8878  [3214]=true,
8879  [3215]=true,
8880  [3216]=true,
8881  [3218]=true,
8882  [3219]=true,
8883  [3220]=true,
8884  [3293]=true,
8885  [3296]=true,
8886  [3297]=true,
8887  [3333]=true,
8888  [3334]=true,
8889  [3335]=true,
8890  [3336]=true,
8891  [3337]=true,
8892  [3338]=true,
8893  [3339]=true,
8894  [3340]=true,
8895  [3342]=true,
8896  [3343]=true,
8897  [3344]=true,
8898  [3346]=true,
8899  [3347]=true,
8900  [3348]=true,
8901  [3423]=true,
8902  [3424]=true,
8903  [3425]=true,
8904  [4129]=true,
8905  [4130]=true,
8906  [4131]=true,
8907  [4132]=true,
8908  [4133]=true,
8909  [4134]=true,
8910  [4135]=true,
8911  [4136]=true,
8912  [4137]=true,
8913  [4138]=true,
8914  [4178]=true,
8915  [4179]=true,
8916  [4180]=true,
8917  [4181]=true,
8918 },
8919 ["nukta"]={
8920  [2364]=true,
8921  [2492]=true,
8922  [2620]=true,
8923  [2748]=true,
8924  [2876]=true,
8925  [3132]=true,
8926  [3260]=true,
8927 },
8928 ["post_mark"]={
8929  [2307]=true,
8930  [2363]=true,
8931  [2366]=true,
8932  [2368]=true,
8933  [2377]=true,
8934  [2378]=true,
8935  [2379]=true,
8936  [2380]=true,
8937  [2383]=true,
8938  [2494]=true,
8939  [2496]=true,
8940  [2622]=true,
8941  [2624]=true,
8942  [2750]=true,
8943  [2752]=true,
8944  [2761]=true,
8945  [2763]=true,
8946  [2764]=true,
8947  [2878]=true,
8948  [2880]=true,
8949  [3006]=true,
8950  [3007]=true,
8951  [3137]=true,
8952  [3138]=true,
8953  [3139]=true,
8954  [3140]=true,
8955  [3262]=true,
8956  [3265]=true,
8957  [3266]=true,
8958  [3267]=true,
8959  [3268]=true,
8960  [3276]=true,
8961  [3285]=true,
8962  [3390]=true,
8963  [3391]=true,
8964  [3392]=true,
8965  [3393]=true,
8966  [3394]=true,
8967  [3395]=true,
8968  [3396]=true,
8969  [3415]=true,
8970  [4139]=true,
8971  [4140]=true,
8972  [4152]=true,
8973  [4155]=true,
8974  [4182]=true,
8975  [4183]=true,
8976  [4194]=true,
8977  [4195]=true,
8978  [4196]=true,
8979  [4199]=true,
8980  [4200]=true,
8981  [4201]=true,
8982  [4202]=true,
8983  [4203]=true,
8984  [4204]=true,
8985  [4205]=true,
8986  [4227]=true,
8987  [4231]=true,
8988  [4232]=true,
8989  [4233]=true,
8990  [4234]=true,
8991  [4235]=true,
8992  [4236]=true,
8993  [4239]=true,
8994  [4250]=true,
8995  [4251]=true,
8996  [4252]=true,
8997  [43643]=true,
8998  [43645]=true,
8999 },
9000 ["pre_mark"]={
9001  [2367]=true,
9002  [2382]=true,
9003  [2495]=true,
9004  [2503]=true,
9005  [2504]=true,
9006  [2623]=true,
9007  [2751]=true,
9008  [2887]=true,
9009  [3014]=true,
9010  [3015]=true,
9011  [3016]=true,
9012  [3398]=true,
9013  [3399]=true,
9014  [3400]=true,
9015  [4145]=true,
9016  [4228]=true,
9017 },
9018 ["ra"]={
9019  [2352]=true,
9020  [2480]=true,
9021  [2544]=true,
9022  [2608]=true,
9023  [2736]=true,
9024  [2864]=true,
9025  [2992]=true,
9026  [3120]=true,
9027  [3248]=true,
9028  [3376]=true,
9029  [5901]=true,
9030 },
9031 ["stress_tone_mark"]={
9032  [2385]=true,
9033  [2386]=true,
9034  [2387]=true,
9035  [2388]=true,
9036  [4151]=true,
9037  [4195]=true,
9038  [4196]=true,
9039  [4201]=true,
9040  [4202]=true,
9041  [4203]=true,
9042  [4204]=true,
9043  [4205]=true,
9044  [4231]=true,
9045  [4232]=true,
9046  [4233]=true,
9047  [4234]=true,
9048  [4235]=true,
9049  [4236]=true,
9050  [4237]=true,
9051  [4239]=true,
9052  [4250]=true,
9053  [4251]=true,
9054  [43643]=true,
9055  [43644]=true,
9056  [43645]=true,
9057 },
9058 ["twopart_mark"]={
9059  [2507]={ 2503,2494 },
9060  [2508]={ 2503,2519 },
9061  [2888]={ 2887,2902 },
9062  [2891]={ 2887,2878 },
9063  [2892]={ 2887,2903 },
9064  [3018]={ 3014,3006 },
9065  [3019]={ 3015,3006 },
9066  [3020]={ 3014,3031 },
9067  [3144]={ 3142,3158 },
9068  [3264]={ 3263,3285 },
9069  [3271]={ 3270,3285 },
9070  [3272]={ 3270,3286 },
9071  [3274]={ 3270,3266 },
9072  [3275]={ 3274,3285 },
9073  [3402]={ 3398,3390 },
9074  [3403]={ 3399,3390 },
9075  [3404]={ 3398,3415 },
9076 },
9077 ["vowel_modifier"]={
9078  [2304]=true,
9079  [2305]=true,
9080  [2306]=true,
9081  [2307]=true,
9082  [2433]=true,
9083  [3330]=true,
9084  [3331]=true,
9085  [4150]=true,
9086  [4152]=true,
9087  [4153]=true,
9088  [4154]=true,
9089  [43232]=true,
9090  [43233]=true,
9091  [43234]=true,
9092  [43235]=true,
9093  [43236]=true,
9094  [43237]=true,
9095  [43238]=true,
9096  [43239]=true,
9097  [43240]=true,
9098  [43241]=true,
9099  [43242]=true,
9100  [43243]=true,
9101  [43244]=true,
9102  [43245]=true,
9103  [43246]=true,
9104  [43247]=true,
9105  [43249]=true,
9106 },
9107}
9108
9109end -- closure
9110
9111do -- begin closure to overcome local limits and interference
9112
9113if not modules then modules={} end modules ['font-ini']={
9114 version=1.001,
9115 comment="companion to font-ini.mkiv",
9116 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
9117 copyright="PRAGMA ADE / ConTeXt Development Team",
9118 license="see context related readme files"
9119}
9120local allocate=utilities.storage.allocate
9121local sortedhash=table.sortedhash
9122fonts=fonts or {}
9123local fonts=fonts
9124local identifiers=allocate()
9125fonts.hashes=fonts.hashes  or { identifiers=identifiers }
9126fonts.tables=fonts.tables  or {}
9127fonts.helpers=fonts.helpers or {}
9128fonts.tracers=fonts.tracers or {} 
9129fonts.specifiers=fonts.specifiers or {} 
9130fonts.analyzers={} 
9131fonts.readers={}
9132fonts.definers={ methods={} }
9133fonts.loggers={ register=function() end }
9134if context then
9135
9136--removed
9137
9138end
9139fonts.privateoffsets={
9140 textbase=0xF0000,
9141 textextrabase=0xFD000,
9142 mathextrabase=0xFE000,
9143 mathbase=0xFF000,
9144 keepnames=false,
9145}
9146if node and not tex.getfontoffamily then
9147 tex.getfontoffamily=node.family_font 
9148end
9149
9150end -- closure
9151
9152do -- begin closure to overcome local limits and interference
9153
9154if not modules then modules={} end modules ['luatex-font-mis']={
9155 version=1.001,
9156 comment="companion to luatex-*.tex",
9157 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
9158 copyright="PRAGMA ADE / ConTeXt Development Team",
9159 license="see context related readme files"
9160}
9161if context then
9162--removed
9163
9164end
9165local currentfont=font.current
9166local hashes=fonts.hashes
9167local identifiers=hashes.identifiers or {}
9168local marks=hashes.marks    or {}
9169hashes.identifiers=identifiers
9170hashes.marks=marks
9171table.setmetatableindex(marks,function(t,k)
9172 if k==true then
9173  return marks[currentfont()]
9174 else
9175  local resources=identifiers[k].resources or {}
9176  local marks=resources.marks or {}
9177  t[k]=marks
9178  return marks
9179 end
9180end)
9181function font.each()
9182 return table.sortedhash(fonts.hashes.identifiers)
9183end
9184
9185end -- closure
9186
9187do -- begin closure to overcome local limits and interference
9188
9189if not modules then modules={} end modules ['font-con']={
9190 version=1.001,
9191 comment="companion to font-ini.mkiv",
9192 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
9193 copyright="PRAGMA ADE / ConTeXt Development Team",
9194 license="see context related readme files"
9195}
9196local next,tostring,tonumber,rawget=next,tostring,tonumber,rawget
9197local format,match,lower,gsub,find=string.format,string.match,string.lower,string.gsub,string.find
9198local sort,insert,concat=table.sort,table.insert,table.concat
9199local sortedkeys,sortedhash,serialize,fastcopy=table.sortedkeys,table.sortedhash,table.serialize,table.fastcopy
9200local derivetable=table.derive
9201local ioflush=io.flush
9202local round=math.round
9203local setmetatable,getmetatable,rawget,rawset=setmetatable,getmetatable,rawget,rawset
9204local trace_defining=false  trackers.register("fonts.defining",function(v) trace_defining=v end)
9205local trace_scaling=false  trackers.register("fonts.scaling",function(v) trace_scaling=v end)
9206local report_defining=logs.reporter("fonts","defining")
9207local fonts=fonts
9208local constructors=fonts.constructors or {}
9209fonts.constructors=constructors
9210local handlers=fonts.handlers or {} 
9211fonts.handlers=handlers
9212local allocate=utilities.storage.allocate
9213local setmetatableindex=table.setmetatableindex
9214constructors.dontembed=allocate()
9215constructors.namemode="fullpath" 
9216constructors.version=1.01
9217constructors.cache=containers.define("fonts","constructors",constructors.version,false)
9218constructors.privateoffset=fonts.privateoffsets.textbase or 0xF0000
9219constructors.cacheintex=true 
9220constructors.addtounicode=true
9221constructors.fixprotrusion=true
9222local designsizes=allocate()
9223constructors.designsizes=designsizes
9224local loadedfonts=allocate()
9225constructors.loadedfonts=loadedfonts
9226local factors={
9227 pt=65536.0,
9228 bp=65781.8,
9229}
9230function constructors.setfactor(f)
9231 constructors.factor=factors[f or 'pt'] or factors.pt
9232end
9233constructors.setfactor()
9234function constructors.scaled(scaledpoints,designsize) 
9235 if scaledpoints<0 then
9236  local factor=constructors.factor
9237  if designsize then
9238   if designsize>factor then 
9239    return (- scaledpoints/1000)*designsize 
9240   else
9241    return (- scaledpoints/1000)*designsize*factor
9242   end
9243  else
9244   return (- scaledpoints/1000)*10*factor
9245  end
9246 else
9247  return scaledpoints
9248 end
9249end
9250function constructors.getprivate(tfmdata)
9251 local properties=tfmdata.properties
9252 local private=properties.private
9253 properties.private=private+1
9254 return private
9255end
9256function constructors.setmathparameter(tfmdata,name,value)
9257 local m=tfmdata.mathparameters
9258 local c=tfmdata.MathConstants
9259 if m then
9260  m[name]=value
9261 end
9262 if c and c~=m then
9263  c[name]=value
9264 end
9265end
9266function constructors.getmathparameter(tfmdata,name)
9267 local p=tfmdata.mathparameters or tfmdata.MathConstants
9268 if p then
9269  return p[name]
9270 end
9271end
9272function constructors.cleanuptable(tfmdata)
9273end
9274function constructors.calculatescale(tfmdata,scaledpoints)
9275 local parameters=tfmdata.parameters
9276 if scaledpoints<0 then
9277  scaledpoints=(- scaledpoints/1000)*(tfmdata.designsize or parameters.designsize) 
9278 end
9279 return scaledpoints,scaledpoints/(parameters.units or 1000) 
9280end
9281local unscaled={
9282 ScriptPercentScaleDown=true,
9283 ScriptScriptPercentScaleDown=true,
9284 RadicalDegreeBottomRaisePercent=true,
9285 NoLimitSupFactor=true,
9286 NoLimitSubFactor=true,
9287}
9288function constructors.assignmathparameters(target,original)
9289 local mathparameters=original.mathparameters
9290 if mathparameters and next(mathparameters) then
9291  local targetparameters=target.parameters
9292  local targetproperties=target.properties
9293  local targetmathparameters={}
9294  local factor=targetproperties.math_is_scaled and 1 or targetparameters.factor
9295  for name,value in next,mathparameters do
9296   if unscaled[name] then
9297    targetmathparameters[name]=value
9298   else
9299    targetmathparameters[name]=value*factor
9300   end
9301  end
9302  if not targetmathparameters.FractionDelimiterSize then
9303   targetmathparameters.FractionDelimiterSize=1.01*targetparameters.size
9304  end
9305  if not mathparameters.FractionDelimiterDisplayStyleSize then
9306   targetmathparameters.FractionDelimiterDisplayStyleSize=2.40*targetparameters.size
9307  end
9308  if not targetmathparameters.SpaceBeforeScript then
9309   targetmathparameters.SpaceBeforeScript=targetmathparameters.SpaceAfterScript
9310  end
9311  target.mathparameters=targetmathparameters
9312 end
9313end
9314function constructors.beforecopyingcharacters(target,original)
9315end
9316function constructors.aftercopyingcharacters(target,original)
9317end
9318local nofinstances=0
9319local instances=setmetatableindex(function(t,k)
9320 nofinstances=nofinstances+1
9321 t[k]=nofinstances
9322 return nofinstances
9323end)
9324function constructors.trytosharefont(target,tfmdata)
9325 local properties=target.properties
9326 local instance=properties.instance
9327 if instance then
9328  local fullname=target.fullname
9329  local fontname=target.fontname
9330  local psname=target.psname
9331  local format=tfmdata.properties.format
9332  if format=="opentype" then
9333   target.streamprovider=1
9334  elseif format=="truetype" then
9335   target.streamprovider=2
9336  else
9337   target.streamprovider=0
9338  end
9339  if target.streamprovider>0 then
9340   if fullname then
9341    fullname=fullname..":"..instances[instance]
9342    target.fullname=fullname
9343   end
9344   if fontname then
9345    fontname=fontname..":"..instances[instance]
9346    target.fontname=fontname
9347   end
9348   if psname then
9349    psname=psname..":"..instances[instance]
9350    target.psname=psname
9351   end
9352  end
9353 end
9354end
9355local synonyms={
9356 exheight="x_height",
9357 xheight="x_height",
9358 ex="x_height",
9359 emwidth="quad",
9360 em="quad",
9361 spacestretch="space_stretch",
9362 stretch="space_stretch",
9363 spaceshrink="space_shrink",
9364 shrink="space_shrink",
9365 extraspace="extra_space",
9366 xspace="extra_space",
9367 slantperpoint="slant",
9368}
9369function constructors.enhanceparameters(parameters)
9370 local mt=getmetatable(parameters)
9371 local getter=function(t,k)
9372  if not k then
9373   return nil
9374  end
9375  local s=synonyms[k]
9376  if s then
9377   return rawget(t,s) or (mt and mt[s]) or nil
9378  end
9379  if k=="spacing" then
9380   return {
9381    width=t.space,
9382    stretch=t.space_stretch,
9383    shrink=t.space_shrink,
9384    extra=t.extra_space,
9385   }
9386  end
9387  return mt and mt[k] or nil
9388 end
9389 local setter=function(t,k,v)
9390  if not k then
9391   return 0
9392  end
9393  local s=synonyms[k]
9394  if s then
9395   rawset(t,s,v)
9396  elseif k=="spacing" then
9397   if type(v)=="table" then
9398    rawset(t,"space",v.width or 0)
9399    rawset(t,"space_stretch",v.stretch or 0)
9400    rawset(t,"space_shrink",v.shrink or 0)
9401    rawset(t,"extra_space",v.extra or 0)
9402   end
9403  else
9404   rawset(t,k,v)
9405  end
9406 end
9407 setmetatable(parameters,{
9408  __index=getter,
9409  __newindex=setter,
9410 })
9411end
9412local function mathkerns(v,vdelta)
9413 local k={}
9414 for i=1,#v do
9415  local entry=v[i]
9416  local height=entry.height
9417  local kern=entry.kern
9418  k[i]={
9419   height=height and vdelta*height or 0,
9420   kern=kern   and vdelta*kern   or 0,
9421  }
9422 end
9423 return k
9424end
9425local psfake=0
9426local function fixedpsname(psname,fallback)
9427 local usedname=psname
9428 if psname and psname~="" then
9429  if find(psname," ",1,true) then
9430   usedname=gsub(psname,"[%s]+","-")
9431  else
9432  end
9433 elseif not fallback or fallback=="" then
9434  psfake=psfake+1
9435  psname="fakename-"..psfake
9436 else
9437  psname=fallback
9438  usedname=gsub(psname,"[^a-zA-Z0-9]+","-")
9439 end
9440 return usedname,psname~=usedname
9441end
9442function constructors.scale(tfmdata,specification)
9443 local target={}
9444 if tonumber(specification) then
9445  specification={ size=specification }
9446 end
9447 target.specification=specification
9448 local scaledpoints=specification.size
9449 local relativeid=specification.relativeid
9450 local properties=tfmdata.properties  or {}
9451 local goodies=tfmdata.goodies  or {}
9452 local resources=tfmdata.resources   or {}
9453 local descriptions=tfmdata.descriptions   or {} 
9454 local characters=tfmdata.characters  or {} 
9455 local changed=tfmdata.changed  or {} 
9456 local shared=tfmdata.shared   or {}
9457 local parameters=tfmdata.parameters  or {}
9458 local mathparameters=tfmdata.mathparameters or {}
9459 local targetcharacters={}
9460 local targetdescriptions=derivetable(descriptions)
9461 local targetparameters=derivetable(parameters)
9462 local targetproperties=derivetable(properties)
9463 local targetgoodies=goodies      
9464 target.characters=targetcharacters
9465 target.descriptions=targetdescriptions
9466 target.parameters=targetparameters
9467 target.properties=targetproperties
9468 target.goodies=targetgoodies
9469 target.shared=shared
9470 target.resources=resources
9471 target.unscaled=tfmdata
9472 local mathsize=tonumber(specification.mathsize) or 0
9473 local textsize=tonumber(specification.textsize) or scaledpoints
9474 local extrafactor=tonumber(specification.factor  ) or 1
9475 targetparameters.mathsize=mathsize 
9476 targetparameters.textsize=textsize
9477 targetparameters.extrafactor=extrafactor
9478 local addtounicode=constructors.addtounicode
9479 local tounicode=fonts.mappings.tounicode
9480 local unknowncode=tounicode(0xFFFD)
9481 local defaultwidth=resources.defaultwidth  or 0
9482 local defaultheight=resources.defaultheight or 0
9483 local defaultdepth=resources.defaultdepth or 0
9484 local units=parameters.units or 1000
9485 targetproperties.language=properties.language or "dflt" 
9486 targetproperties.script=properties.script   or "dflt" 
9487 targetproperties.mode=properties.mode  or "base" 
9488 targetproperties.method=properties.method
9489 local askedscaledpoints=scaledpoints
9490 local scaledpoints,delta=constructors.calculatescale(tfmdata,scaledpoints,nil,specification)
9491 local hdelta=delta
9492 local vdelta=delta
9493 target.designsize=parameters.designsize 
9494 target.units=units
9495 target.units_per_em=units
9496 local direction=properties.direction or tfmdata.direction or 0 
9497 target.direction=direction
9498 properties.direction=direction
9499 target.size=scaledpoints
9500 target.encodingbytes=properties.encodingbytes or 1
9501 target.subfont=properties.subfont
9502 target.embedding=properties.embedding or "subset"
9503 target.tounicode=1
9504 target.cidinfo=properties.cidinfo
9505 target.format=properties.format
9506 target.cache=constructors.cacheintex and "yes" or "renew"
9507 local original=properties.original or tfmdata.original
9508 local fontname=properties.fontname or tfmdata.fontname
9509 local fullname=properties.fullname or tfmdata.fullname
9510 local filename=properties.filename or tfmdata.filename
9511 local psname=properties.psname   or tfmdata.psname
9512 local name=properties.name  or tfmdata.name
9513 local psname,psfixed=fixedpsname(psname,fontname or fullname or file.nameonly(filename))
9514 target.original=original
9515 target.fontname=fontname
9516 target.fullname=fullname
9517 target.filename=filename
9518 target.psname=psname
9519 target.name=name
9520 properties.fontname=fontname
9521 properties.fullname=fullname
9522 properties.filename=filename
9523 properties.psname=psname
9524 properties.name=name
9525 local expansion=parameters.expansion
9526 if expansion then
9527  target.stretch=expansion.stretch
9528  target.shrink=expansion.shrink
9529  target.step=expansion.step
9530 end
9531 local slantfactor=parameters.slantfactor or 0
9532 if slantfactor~=0 then
9533  target.slant=slantfactor*1000
9534 else
9535  target.slant=0
9536 end
9537 local extendfactor=parameters.extendfactor or 0
9538 if extendfactor~=0 and extendfactor~=1 then
9539  hdelta=hdelta*extendfactor
9540  target.extend=extendfactor*1000
9541 else
9542  target.extend=1000 
9543 end
9544 local squeezefactor=parameters.squeezefactor or 0
9545 if squeezefactor~=0 and squeezefactor~=1 then
9546  vdelta=vdelta*squeezefactor
9547  target.squeeze=squeezefactor*1000
9548 else
9549  target.squeeze=1000 
9550 end
9551 local mode=parameters.mode or 0
9552 if mode~=0 then
9553  target.mode=mode
9554 end
9555 local width=parameters.width or 0
9556 if width~=0 then
9557  target.width=width*delta*1000/655360
9558 end
9559 targetparameters.factor=delta
9560 targetparameters.hfactor=hdelta
9561 targetparameters.vfactor=vdelta
9562 targetparameters.size=scaledpoints
9563 targetparameters.units=units
9564 targetparameters.scaledpoints=askedscaledpoints
9565 targetparameters.mode=mode
9566 targetparameters.width=width
9567 local isvirtual=properties.virtualized or tfmdata.type=="virtual"
9568 local hasquality=parameters.expansion or parameters.protrusion
9569 local hasitalics=properties.hasitalics
9570 local autoitalicamount=properties.autoitalicamount
9571 local stackmath=not properties.nostackmath
9572 local haskerns=properties.haskerns  or properties.mode=="base" 
9573 local hasligatures=properties.hasligatures or properties.mode=="base" 
9574 local writingmode=properties.writingmode or "horizontal"
9575 local identity=properties.identity or "horizontal"
9576 local vfonts=target.fonts
9577 if vfonts and #vfonts>0 then
9578  target.fonts=fastcopy(vfonts) 
9579 elseif isvirtual then
9580  target.fonts={ { id=0 } } 
9581 end
9582 if changed and not next(changed) then
9583  changed=false
9584 end
9585 target.type=isvirtual and "virtual" or "real"
9586 target.writingmode=writingmode=="vertical" and "vertical" or "horizontal"
9587 target.identity=identity=="vertical" and "vertical" or "horizontal"
9588 target.postprocessors=tfmdata.postprocessors
9589 local targetslant=(parameters.slant   or parameters[1] or 0)*factors.pt 
9590 local targetspace=(parameters.space   or parameters[2] or 0)*hdelta
9591 local targetspace_stretch=(parameters.space_stretch or parameters[3] or 0)*hdelta
9592 local targetspace_shrink=(parameters.space_shrink  or parameters[4] or 0)*hdelta
9593 local targetx_height=(parameters.x_height   or parameters[5] or 0)*vdelta
9594 local targetquad=(parameters.quad    or parameters[6] or 0)*hdelta
9595 local targetextra_space=(parameters.extra_space   or parameters[7] or 0)*hdelta
9596 targetparameters.slant=targetslant 
9597 targetparameters.space=targetspace
9598 targetparameters.space_stretch=targetspace_stretch
9599 targetparameters.space_shrink=targetspace_shrink
9600 targetparameters.x_height=targetx_height
9601 targetparameters.quad=targetquad
9602 targetparameters.extra_space=targetextra_space
9603 local hshift=parameters.hshift
9604 if hshift then
9605  targetparameters.hshift=delta*hshift
9606 end
9607 local vshift=parameters.vshift
9608 if vshift then
9609  targetparameters.vshift=delta*vshift
9610 end
9611 local ascender=parameters.ascender
9612 if ascender then
9613  targetparameters.ascender=delta*ascender
9614 end
9615 local descender=parameters.descender
9616 if descender then
9617  targetparameters.descender=delta*descender
9618 end
9619 constructors.enhanceparameters(targetparameters)
9620 local protrusionfactor=constructors.fixprotrusion and ((targetquad~=0 and 1000/targetquad) or 1) or 1
9621 local scaledwidth=defaultwidth*hdelta
9622 local scaledheight=defaultheight*vdelta
9623 local scaleddepth=defaultdepth*vdelta
9624 local hasmath=(properties.hasmath or next(mathparameters)) and true
9625 if hasmath then
9626  constructors.assignmathparameters(target,tfmdata) 
9627  properties.hasmath=true
9628  target.nomath=false
9629  target.MathConstants=target.mathparameters
9630  local oldmath=properties.oldmath
9631  targetproperties.oldmath=oldmath
9632  target.oldmath=oldmath
9633 else
9634  properties.hasmath=false
9635  target.nomath=true
9636  target.mathparameters=nil 
9637 end
9638 if hasmath then
9639  local mathitalics=properties.mathitalics
9640  if mathitalics==false then
9641   if trace_defining then
9642    report_defining("%s italics %s for font %a, fullname %a, filename %a","math",hasitalics and "ignored" or "disabled",name,fullname,filename)
9643   end
9644   hasitalics=false
9645   autoitalicamount=false
9646  end
9647 else
9648  local textitalics=properties.textitalics
9649  if textitalics==false then
9650   if trace_defining then
9651    report_defining("%s italics %s for font %a, fullname %a, filename %a","text",hasitalics and "ignored" or "disabled",name,fullname,filename)
9652   end
9653   hasitalics=false
9654   autoitalicamount=false
9655  end
9656 end
9657 if trace_defining then
9658  report_defining("defining tfm, name %a, fullname %a, filename %a, %spsname %a, hscale %a, vscale %a, math %a, italics %a",
9659   name,fullname,filename,psfixed and "(fixed) " or "",psname,hdelta,vdelta,
9660   hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled")
9661 end
9662 constructors.beforecopyingcharacters(target,tfmdata)
9663 local sharedkerns={}
9664 for unicode,character in next,characters do
9665  local chr,description,index
9666  if changed then
9667   local c=changed[unicode]
9668   if c and c~=unicode then
9669    local cc=changed[c]
9670    if cc then
9671     while cc do
9672      c=cc
9673      cc=changed[c]
9674     end
9675    end
9676    if c then
9677     description=descriptions[c] or descriptions[unicode] or character
9678     character=characters[c] or character
9679     index=description.index or c
9680    else
9681     description=descriptions[unicode] or character
9682     index=description.index or unicode
9683    end
9684   else
9685    description=descriptions[unicode] or character
9686    index=description.index or unicode
9687   end
9688  else
9689   description=descriptions[unicode] or character
9690   index=description.index or unicode
9691  end
9692  local width=description.width
9693  local height=description.height
9694  local depth=description.depth
9695  local isunicode=description.unicode
9696  if width  then width=hdelta*width  else width=scaledwidth  end
9697  if height then height=vdelta*height else height=scaledheight end
9698  if depth and depth~=0 then
9699   depth=delta*depth
9700   if isunicode then
9701    chr={
9702     index=index,
9703     height=height,
9704     depth=depth,
9705     width=width,
9706     unicode=isunicode,
9707    }
9708   else
9709    chr={
9710     index=index,
9711     height=height,
9712     depth=depth,
9713     width=width,
9714    }
9715   end
9716  else
9717   if isunicode then
9718    chr={
9719     index=index,
9720     height=height,
9721     width=width,
9722     unicode=isunicode,
9723    }
9724   else
9725    chr={
9726     index=index,
9727     height=height,
9728     width=width,
9729    }
9730   end
9731  end
9732  if addtounicode then
9733   chr.tounicode=isunicode and tounicode(isunicode) or unknowncode
9734  end
9735  if hasquality then
9736   local ve=character.expansion_factor
9737   if ve then
9738    chr.expansion_factor=ve*1000 
9739   end
9740   local vl=character.left_protruding
9741   if vl then
9742    chr.left_protruding=protrusionfactor*width*vl
9743   end
9744   local vr=character.right_protruding
9745   if vr then
9746    chr.right_protruding=protrusionfactor*width*vr
9747   end
9748  end
9749  if hasmath then
9750   local vn=character.next
9751   if vn then
9752    chr.next=vn
9753   else
9754    local vv=character.vert_variants
9755    if vv then
9756     local t={}
9757     for i=1,#vv do
9758      local vvi=vv[i]
9759      local s=vvi["start"]   or 0
9760      local e=vvi["end"]  or 0
9761      local a=vvi["advance"] or 0
9762      t[i]={ 
9763       ["start"]=s==0 and 0 or s*vdelta,
9764       ["end"]=e==0 and 0 or e*vdelta,
9765       ["advance"]=a==0 and 0 or a*vdelta,
9766       ["extender"]=vvi["extender"],
9767       ["glyph"]=vvi["glyph"],
9768      }
9769     end
9770     chr.vert_variants=t
9771    else
9772     local hv=character.horiz_variants
9773     if hv then
9774      local t={}
9775      for i=1,#hv do
9776       local hvi=hv[i]
9777       local s=hvi["start"]   or 0
9778       local e=hvi["end"]  or 0
9779       local a=hvi["advance"] or 0
9780       t[i]={ 
9781        ["start"]=s==0 and 0 or s*hdelta,
9782        ["end"]=e==0 and 0 or e*hdelta,
9783        ["advance"]=a==0 and 0 or a*hdelta,
9784        ["extender"]=hvi["extender"],
9785        ["glyph"]=hvi["glyph"],
9786       }
9787      end
9788      chr.horiz_variants=t
9789     end
9790    end
9791   end
9792   local vi=character.vert_italic
9793   if vi and vi~=0 then
9794    chr.vert_italic=vi*hdelta
9795   end
9796   local va=character.accent
9797   if va then
9798    chr.top_accent=vdelta*va
9799   end
9800   if stackmath then
9801    local mk=character.mathkerns
9802    if mk then
9803     local tr=mk.topright
9804     local tl=mk.topleft
9805     local br=mk.bottomright
9806     local bl=mk.bottomleft
9807     chr.mathkern={ 
9808      top_right=tr and mathkerns(tr,vdelta) or nil,
9809      top_left=tl and mathkerns(tl,vdelta) or nil,
9810      bottom_right=br and mathkerns(br,vdelta) or nil,
9811      bottom_left=bl and mathkerns(bl,vdelta) or nil,
9812     }
9813    end
9814   end
9815   if hasitalics then
9816    local vi=character.italic
9817    if vi and vi~=0 then
9818     chr.italic=vi*hdelta
9819    end
9820   end
9821  elseif autoitalicamount then 
9822   local vi=description.italic
9823   if not vi then
9824    local bb=description.boundingbox
9825    if bb then
9826     local vi=bb[3]-description.width+autoitalicamount
9827     if vi>0 then 
9828      chr.italic=vi*hdelta
9829     end
9830    else
9831    end
9832   elseif vi~=0 then
9833    chr.italic=vi*hdelta
9834   end
9835  elseif hasitalics then 
9836   local vi=character.italic
9837   if vi and vi~=0 then
9838    chr.italic=vi*hdelta
9839   end
9840  end
9841  if haskerns then
9842   local vk=character.kerns
9843   if vk then
9844    local s=sharedkerns[vk]
9845    if not s then
9846     s={}
9847     for k,v in next,vk do s[k]=v*hdelta end
9848     sharedkerns[vk]=s
9849    end
9850    chr.kerns=s
9851   end
9852  end
9853  if hasligatures then
9854   local vl=character.ligatures
9855   if vl then
9856    if true then
9857     chr.ligatures=vl 
9858    else
9859     local tt={}
9860     for i,l in next,vl do
9861      tt[i]=l
9862     end
9863     chr.ligatures=tt
9864    end
9865   end
9866  end
9867  if isvirtual then
9868   local vc=character.commands
9869   if vc then
9870    local ok=false
9871    for i=1,#vc do
9872     local key=vc[i][1]
9873     if key=="right" or key=="down" or key=="rule" then
9874      ok=true
9875      break
9876     end
9877    end
9878    if ok then
9879     local tt={}
9880     for i=1,#vc do
9881      local ivc=vc[i]
9882      local key=ivc[1]
9883      if key=="right" then
9884       tt[i]={ key,ivc[2]*hdelta }
9885      elseif key=="down" then
9886       tt[i]={ key,ivc[2]*vdelta }
9887      elseif key=="rule" then
9888       tt[i]={ key,ivc[2]*vdelta,ivc[3]*hdelta }
9889      else 
9890       tt[i]=ivc 
9891      end
9892     end
9893     chr.commands=tt
9894    else
9895     chr.commands=vc
9896    end
9897   end
9898  end
9899  targetcharacters[unicode]=chr
9900 end
9901 properties.setitalics=hasitalics
9902 constructors.aftercopyingcharacters(target,tfmdata)
9903 constructors.trytosharefont(target,tfmdata)
9904 local vfonts=target.fonts
9905 if isvirtual or target.type=="virtual" or properties.virtualized then
9906  properties.virtualized=true
9907  target.type="virtual"
9908  if not vfonts or #vfonts==0 then
9909   target.fonts={ { id=0 } }
9910  end
9911 elseif vfonts then
9912  properties.virtualized=true
9913  target.type="virtual"
9914  if #vfonts==0 then
9915   target.fonts={ { id=0 } }
9916  end
9917 end
9918 return target
9919end
9920function constructors.finalize(tfmdata)
9921 if tfmdata.properties and tfmdata.properties.finalized then
9922  return
9923 end
9924 if not tfmdata.characters then
9925  return nil
9926 end
9927 if not tfmdata.goodies then
9928  tfmdata.goodies={} 
9929 end
9930 local parameters=tfmdata.parameters
9931 if not parameters then
9932  return nil
9933 end
9934 if not parameters.expansion then
9935  parameters.expansion={
9936   stretch=tfmdata.stretch or 0,
9937   shrink=tfmdata.shrink  or 0,
9938   step=tfmdata.step or 0,
9939  }
9940 end
9941 if not parameters.size then
9942  parameters.size=tfmdata.size
9943 end
9944 if not parameters.mode then
9945  parameters.mode=0
9946 end
9947 if not parameters.width then
9948  parameters.width=0
9949 end
9950 if not parameters.slantfactor then
9951  parameters.slantfactor=(tfmdata.slant or 0)/1000
9952 end
9953 if not parameters.extendfactor then
9954  parameters.extendfactor=(tfmdata.extend or 1000)/1000
9955 end
9956 if not parameters.squeezefactor then
9957  parameters.squeezefactor=(tfmdata.squeeze or 1000)/1000
9958 end
9959 local designsize=parameters.designsize
9960 if designsize then
9961  parameters.minsize=tfmdata.minsize or designsize
9962  parameters.maxsize=tfmdata.maxsize or designsize
9963 else
9964  designsize=factors.pt*10
9965  parameters.designsize=designsize
9966  parameters.minsize=designsize
9967  parameters.maxsize=designsize
9968 end
9969 parameters.minsize=tfmdata.minsize or parameters.designsize
9970 parameters.maxsize=tfmdata.maxsize or parameters.designsize
9971 if not parameters.units then
9972  parameters.units=tfmdata.units or tfmdata.units_per_em or 1000
9973 end
9974 if not tfmdata.descriptions then
9975  local descriptions={} 
9976  setmetatableindex(descriptions,function(t,k) local v={} t[k]=v return v end)
9977  tfmdata.descriptions=descriptions
9978 end
9979 local properties=tfmdata.properties
9980 if not properties then
9981  properties={}
9982  tfmdata.properties=properties
9983 end
9984 if not properties.virtualized then
9985  properties.virtualized=tfmdata.type=="virtual"
9986 end
9987 properties.fontname=properties.fontname or tfmdata.fontname
9988 properties.filename=properties.filename or tfmdata.filename
9989 properties.fullname=properties.fullname or tfmdata.fullname
9990 properties.name=properties.name  or tfmdata.name
9991 properties.psname=properties.psname   or tfmdata.psname
9992 properties.encodingbytes=tfmdata.encodingbytes or 1
9993 properties.subfont=tfmdata.subfont    or nil
9994 properties.embedding=tfmdata.embedding  or "subset"
9995 properties.tounicode=tfmdata.tounicode  or 1
9996 properties.cidinfo=tfmdata.cidinfo    or nil
9997 properties.format=tfmdata.format  or "type1"
9998 properties.direction=tfmdata.direction  or 0
9999 properties.writingmode=tfmdata.writingmode   or "horizontal"
10000 properties.identity=tfmdata.identity   or "horizontal"
10001 properties.usedbitmap=tfmdata.usedbitmap
10002 if not tfmdata.resources then
10003  tfmdata.resources={}
10004 end
10005 if not tfmdata.shared then
10006  tfmdata.shared={}
10007 end
10008 if not properties.hasmath then
10009  properties.hasmath=not tfmdata.nomath
10010 end
10011 tfmdata.MathConstants=nil
10012 tfmdata.postprocessors=nil
10013 tfmdata.fontname=nil
10014 tfmdata.filename=nil
10015 tfmdata.fullname=nil
10016 tfmdata.name=nil 
10017 tfmdata.psname=nil
10018 tfmdata.encodingbytes=nil
10019 tfmdata.subfont=nil
10020 tfmdata.embedding=nil
10021 tfmdata.tounicode=nil
10022 tfmdata.cidinfo=nil
10023 tfmdata.format=nil
10024 tfmdata.direction=nil
10025 tfmdata.type=nil
10026 tfmdata.nomath=nil
10027 tfmdata.designsize=nil
10028 tfmdata.size=nil
10029 tfmdata.stretch=nil
10030 tfmdata.shrink=nil
10031 tfmdata.step=nil
10032 tfmdata.slant=nil
10033 tfmdata.extend=nil
10034 tfmdata.squeeze=nil
10035 tfmdata.mode=nil
10036 tfmdata.width=nil
10037 tfmdata.units=nil
10038 tfmdata.units_per_em=nil
10039 tfmdata.cache=nil
10040 properties.finalized=true
10041 return tfmdata
10042end
10043local hashmethods={}
10044constructors.hashmethods=hashmethods
10045function constructors.hashfeatures(specification) 
10046 local features=specification.features
10047 if features then
10048  local t,n={},0
10049  for category,list in sortedhash(features) do
10050   if next(list) then
10051    local hasher=hashmethods[category]
10052    if hasher then
10053     local hash=hasher(list)
10054     if hash then
10055      n=n+1
10056      t[n]=category..":"..hash
10057     end
10058    end
10059   end
10060  end
10061  if n>0 then
10062   return concat(t," & ")
10063  end
10064 end
10065 return "unknown"
10066end
10067hashmethods.normal=function(list)
10068 local s={}
10069 local n=0
10070 for k,v in next,list do
10071  if not k then
10072  elseif k=="number" or k=="features" then
10073  else
10074   n=n+1
10075   if type(v)=="table" then
10076    local t={}
10077    local m=0
10078    for k,v in next,v do
10079     m=m+1
10080     t[m]=k..'='..tostring(v)
10081    end
10082    sort(t)
10083    s[n]=k..'={'..concat(t,",").."}"
10084   else
10085    s[n]=k..'='..tostring(v)
10086   end
10087  end
10088 end
10089 if n>0 then
10090  sort(s)
10091  return concat(s,"+")
10092 end
10093end
10094function constructors.hashinstance(specification,force)
10095 local hash=specification.hash
10096 local size=specification.size
10097 local fallbacks=specification.fallbacks
10098 if force or not hash then
10099  hash=constructors.hashfeatures(specification)
10100  specification.hash=hash
10101 end
10102 if size<1000 and designsizes[hash] then
10103  size=round(constructors.scaled(size,designsizes[hash]))
10104 else
10105  size=round(size)
10106 end
10107 specification.size=size
10108 if fallbacks then
10109  return hash..' @ '..size..' @ '..fallbacks
10110 else
10111  return hash..' @ '..size
10112 end
10113end
10114function constructors.setname(tfmdata,specification) 
10115 if constructors.namemode=="specification" then
10116  local specname=specification.specification
10117  if specname then
10118   tfmdata.properties.name=specname
10119   if trace_defining then
10120    report_otf("overloaded fontname %a",specname)
10121   end
10122  end
10123 end
10124end
10125function constructors.checkedfilename(data)
10126 local foundfilename=data.foundfilename
10127 if not foundfilename then
10128  local askedfilename=data.filename or ""
10129  if askedfilename~="" then
10130   askedfilename=resolvers.resolve(askedfilename) 
10131   foundfilename=resolvers.findbinfile(askedfilename,"") or ""
10132   if foundfilename=="" then
10133    report_defining("source file %a is not found",askedfilename)
10134    foundfilename=resolvers.findbinfile(file.basename(askedfilename),"") or ""
10135    if foundfilename~="" then
10136     report_defining("using source file %a due to cache mismatch",foundfilename)
10137    end
10138   end
10139  end
10140  data.foundfilename=foundfilename
10141 end
10142 return foundfilename
10143end
10144local formats=allocate()
10145fonts.formats=formats
10146setmetatableindex(formats,function(t,k)
10147 local l=lower(k)
10148 if rawget(t,k) then
10149  t[k]=l
10150  return l
10151 end
10152 return rawget(t,file.suffix(l))
10153end)
10154do
10155 local function setindeed(mode,source,target,group,name,position)
10156  local action=source[mode]
10157  if not action then
10158   return
10159  end
10160  local t=target[mode]
10161  if not t then
10162   report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode)
10163   os.exit()
10164  elseif position then
10165   insert(t,position,{ name=name,action=action })
10166  else
10167   for i=1,#t do
10168    local ti=t[i]
10169    if ti.name==name then
10170     ti.action=action
10171     return
10172    end
10173   end
10174   insert(t,{ name=name,action=action })
10175  end
10176 end
10177 local function set(group,name,target,source)
10178  target=target[group]
10179  if not target then
10180   report_defining("fatal target error in setting feature %a, group %a",name,group)
10181   os.exit()
10182  end
10183  local source=source[group]
10184  if not source then
10185   report_defining("fatal source error in setting feature %a, group %a",name,group)
10186   os.exit()
10187  end
10188  local position=source.position
10189  setindeed("node",source,target,group,name,position)
10190  setindeed("base",source,target,group,name,position)
10191  setindeed("plug",source,target,group,name,position)
10192 end
10193 local function register(where,specification)
10194  local name=specification.name
10195  if name and name~="" then
10196   local default=specification.default
10197   local description=specification.description
10198   local initializers=specification.initializers
10199   local processors=specification.processors
10200   local manipulators=specification.manipulators
10201   local modechecker=specification.modechecker
10202   if default then
10203    where.defaults[name]=default
10204   end
10205   if description and description~="" then
10206    where.descriptions[name]=description
10207   end
10208   if initializers then
10209    set('initializers',name,where,specification)
10210   end
10211   if processors then
10212    set('processors',name,where,specification)
10213   end
10214   if manipulators then
10215    set('manipulators',name,where,specification)
10216   end
10217   if modechecker then
10218      where.modechecker=modechecker
10219   end
10220  end
10221 end
10222 constructors.registerfeature=register
10223 function constructors.getfeatureaction(what,where,mode,name)
10224  what=handlers[what].features
10225  if what then
10226   where=what[where]
10227   if where then
10228    mode=where[mode]
10229    if mode then
10230     for i=1,#mode do
10231      local m=mode[i]
10232      if m.name==name then
10233       return m.action
10234      end
10235     end
10236    end
10237   end
10238  end
10239 end
10240 local newfeatures={}
10241 constructors.newfeatures=newfeatures 
10242 constructors.features=newfeatures
10243 local function setnewfeatures(what)
10244  local handler=handlers[what]
10245  local features=handler.features
10246  if not features then
10247   local tables=handler.tables  
10248   local statistics=handler.statistics 
10249   features=allocate {
10250    defaults={},
10251    descriptions=tables and tables.features or {},
10252    used=statistics and statistics.usedfeatures or {},
10253    initializers={ base={},node={},plug={} },
10254    processors={ base={},node={},plug={} },
10255    manipulators={ base={},node={},plug={} },
10256   }
10257   features.register=function(specification) return register(features,specification) end
10258   handler.features=features 
10259  end
10260  return features
10261 end
10262 setmetatable(newfeatures,{
10263  __call=function(t,k) local v=t[k] return v end,
10264  __index=function(t,k) local v=setnewfeatures(k) t[k]=v return v end,
10265 })
10266end
10267do
10268 local newhandler={}
10269 constructors.handlers=newhandler 
10270 constructors.newhandler=newhandler
10271 local function setnewhandler(what) 
10272  local handler=handlers[what]
10273  if not handler then
10274   handler={}
10275   handlers[what]=handler
10276  end
10277  return handler
10278 end
10279 setmetatable(newhandler,{
10280  __call=function(t,k) local v=t[k] return v end,
10281  __index=function(t,k) local v=setnewhandler(k) t[k]=v return v end,
10282 })
10283end
10284do
10285 local newenhancer={}
10286 constructors.enhancers=newenhancer
10287 constructors.newenhancer=newenhancer
10288 local function setnewenhancer(format)
10289  local handler=handlers[format]
10290  local enhancers=handler.enhancers
10291  if not enhancers then
10292   local actions=allocate() 
10293   local before=allocate()
10294   local after=allocate()
10295   local order=allocate()
10296   local known={}
10297   local nofsteps=0
10298   local patches={ before=before,after=after }
10299   local trace=false
10300   local report=logs.reporter("fonts",format.." enhancing")
10301   trackers.register(format..".loading",function(v) trace=v end)
10302   local function enhance(name,data,filename,raw)
10303    local enhancer=actions[name]
10304    if enhancer then
10305     if trace then
10306      report("apply enhancement %a to file %a",name,filename)
10307      ioflush()
10308     end
10309     enhancer(data,filename,raw)
10310    else
10311    end
10312   end
10313   local function apply(data,filename,raw)
10314    local basename=file.basename(lower(filename))
10315    if trace then
10316     report("%s enhancing file %a","start",filename)
10317    end
10318    ioflush() 
10319    for e=1,nofsteps do
10320     local enhancer=order[e]
10321     local b=before[enhancer]
10322     if b then
10323      for pattern,action in next,b do
10324       if find(basename,pattern) then
10325        action(data,filename,raw)
10326       end
10327      end
10328     end
10329     enhance(enhancer,data,filename,raw) 
10330     local a=after[enhancer]
10331     if a then
10332      for pattern,action in next,a do
10333       if find(basename,pattern) then
10334        action(data,filename,raw)
10335       end
10336      end
10337     end
10338     ioflush() 
10339    end
10340    if trace then
10341     report("%s enhancing file %a","stop",filename)
10342    end
10343    ioflush() 
10344   end
10345   local function register(what,action)
10346    if action then
10347     if actions[what] then
10348     else
10349      nofsteps=nofsteps+1
10350      order[nofsteps]=what
10351      known[what]=nofsteps
10352     end
10353     actions[what]=action
10354    else
10355     report("bad enhancer %a",what)
10356    end
10357   end
10358   local function patch(what,where,pattern,action)
10359    local pw=patches[what]
10360    if pw then
10361     local ww=pw[where]
10362     if ww then
10363      ww[pattern]=action
10364     else
10365      pw[where]={ [pattern]=action }
10366      if not known[where] then
10367       nofsteps=nofsteps+1
10368       order[nofsteps]=where
10369       known[where]=nofsteps
10370      end
10371     end
10372    end
10373   end
10374   enhancers={
10375    register=register,
10376    apply=apply,
10377    patch=patch,
10378    report=report,
10379    patches={
10380     register=patch,
10381     report=report,
10382    },
10383   }
10384   handler.enhancers=enhancers
10385  end
10386  return enhancers
10387 end
10388 setmetatable(newenhancer,{
10389  __call=function(t,k) local v=t[k] return v end,
10390  __index=function(t,k) local v=setnewenhancer(k) t[k]=v return v end,
10391 })
10392end
10393function constructors.checkedfeatures(what,features)
10394 local defaults=handlers[what].features.defaults
10395 if features and next(features) then
10396  features=fastcopy(features) 
10397  for key,value in next,defaults do
10398   if features[key]==nil then
10399    features[key]=value
10400   end
10401  end
10402  return features
10403 else
10404  return fastcopy(defaults) 
10405 end
10406end
10407function constructors.initializefeatures(what,tfmdata,features,trace,report)
10408 if features and next(features) then
10409  local properties=tfmdata.properties or {} 
10410  local whathandler=handlers[what]
10411  local whatfeatures=whathandler.features
10412  local whatmodechecker=whatfeatures.modechecker
10413  local mode=properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features,features.mode)) or features.mode or "base"
10414  properties.mode=mode 
10415  features.mode=mode
10416  local done={}
10417  while true do
10418   local redo=false
10419   local initializers=whatfeatures.initializers[mode]
10420   if initializers then
10421    for i=1,#initializers do
10422     local step=initializers[i]
10423     local feature=step.name
10424     local value=features[feature]
10425     if not value then
10426     elseif done[feature] then
10427     else
10428      local action=step.action
10429      if trace then
10430       report("initializing feature %a to %a for mode %a for font %a",feature,
10431        value,mode,tfmdata.properties.fullname)
10432      end
10433      action(tfmdata,value,features) 
10434      if mode~=properties.mode or mode~=features.mode then
10435       if whatmodechecker then
10436        properties.mode=whatmodechecker(tfmdata,features,properties.mode) 
10437        features.mode=properties.mode
10438       end
10439       if mode~=properties.mode then
10440        mode=properties.mode
10441        redo=true
10442       end
10443      end
10444      done[feature]=true
10445     end
10446     if redo then
10447      break
10448     end
10449    end
10450    if not redo then
10451     break
10452    end
10453   else
10454    break
10455   end
10456  end
10457  properties.mode=mode 
10458  return true
10459 else
10460  return false
10461 end
10462end
10463function constructors.collectprocessors(what,tfmdata,features,trace,report)
10464 local processes={}
10465 local nofprocesses=0
10466 if features and next(features) then
10467  local properties=tfmdata.properties
10468  local whathandler=handlers[what]
10469  local whatfeatures=whathandler.features
10470  local whatprocessors=whatfeatures.processors
10471  local mode=properties.mode
10472  local processors=whatprocessors[mode]
10473  if processors then
10474   for i=1,#processors do
10475    local step=processors[i]
10476    local feature=step.name
10477    if features[feature] then
10478     local action=step.action
10479     if trace then
10480      report("installing feature processor %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname)
10481     end
10482     if action then
10483      nofprocesses=nofprocesses+1
10484      processes[nofprocesses]=action
10485     end
10486    end
10487   end
10488  elseif trace then
10489   report("no feature processors for mode %a for font %a",mode,properties.fullname)
10490  end
10491 end
10492 return processes
10493end
10494function constructors.applymanipulators(what,tfmdata,features,trace,report)
10495 if features and next(features) then
10496  local properties=tfmdata.properties
10497  local whathandler=handlers[what]
10498  local whatfeatures=whathandler.features
10499  local whatmanipulators=whatfeatures.manipulators
10500  local mode=properties.mode
10501  local manipulators=whatmanipulators[mode]
10502  if manipulators then
10503   for i=1,#manipulators do
10504    local step=manipulators[i]
10505    local feature=step.name
10506    local value=features[feature]
10507    if value then
10508     local action=step.action
10509     if trace then
10510      report("applying feature manipulator %a for mode %a for font %a",feature,mode,properties.fullname)
10511     end
10512     if action then
10513      action(tfmdata,feature,value)
10514     end
10515    end
10516   end
10517  end
10518 end
10519end
10520function constructors.addcoreunicodes(unicodes) 
10521 if not unicodes then
10522  unicodes={}
10523 end
10524 unicodes.space=0x0020
10525 unicodes.hyphen=0x002D
10526 unicodes.zwj=0x200D
10527 unicodes.zwnj=0x200C
10528 return unicodes
10529end
10530
10531end -- closure
10532
10533do -- begin closure to overcome local limits and interference
10534
10535if not modules then modules={} end modules ['luatex-font-enc']={
10536 version=1.001,
10537 comment="companion to luatex-*.tex",
10538 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
10539 copyright="PRAGMA ADE / ConTeXt Development Team",
10540 license="see context related readme files"
10541}
10542if context then
10543--removed
10544
10545end
10546local fonts=fonts
10547local encodings={}
10548fonts.encodings=encodings
10549encodings.agl={}
10550encodings.known={}
10551encodings.glyphlistfilename="font-age.lua"
10552setmetatable(encodings.agl,{ __index=function(t,k)
10553 if k=="unicodes" then
10554  logs.report("fonts","loading (extended) adobe glyph list")
10555  local foundname=resolvers.findfile(encodings.glyphlistfilename) or ""
10556  local unicodes=foundname~="" and dofile(foundname)
10557  if type(unicodes)~="table" then
10558   logs.report("fonts","missing or invalid (extended) adobe glyph list")
10559   unicodes={}
10560  end
10561  encodings.agl={ unicodes=unicodes }
10562  return unicodes
10563 else
10564  return nil
10565 end
10566end })
10567encodings.cache=containers.define("fonts","enc",encodings.version,true)
10568function encodings.load(filename)
10569 local name=file.removesuffix(filename)
10570 local data=containers.read(encodings.cache,name)
10571 if data then
10572  return data
10573 end
10574 local vector,tag,hash,unicodes={},"",{},{}
10575 local foundname=resolvers.findfile(filename,'enc')
10576 if foundname and foundname~="" then
10577  local ok,encoding,size=resolvers.loadbinfile(foundname)
10578  if ok and encoding then
10579   encoding=string.gsub(encoding,"%%(.-)\n","")
10580   local unicoding=encodings.agl.unicodes
10581   local tag,vec=string.match(encoding,"/(%w+)%s*%[(.*)%]%s*def")
10582   local i=0
10583   for ch in string.gmatch(vec,"/([%a%d%.]+)") do
10584    if ch~=".notdef" then
10585     vector[i]=ch
10586     if not hash[ch] then
10587      hash[ch]=i
10588     else
10589     end
10590     local u=unicoding[ch]
10591     if u then
10592      unicodes[u]=i
10593     end
10594    end
10595    i=i+1
10596   end
10597  end
10598 end
10599 local data={
10600  name=name,
10601  tag=tag,
10602  vector=vector,
10603  hash=hash,
10604  unicodes=unicodes
10605 }
10606 return containers.write(encodings.cache,name,data)
10607end
10608
10609end -- closure
10610
10611do -- begin closure to overcome local limits and interference
10612
10613if not modules then modules={} end modules ['font-cid']={
10614 version=1.001,
10615 comment="companion to font-ini.mkiv",
10616 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
10617 copyright="PRAGMA ADE / ConTeXt Development Team",
10618 license="see context related readme files"
10619}
10620local format,match,lower=string.format,string.match,string.lower
10621local tonumber=tonumber
10622local P,S,R,C,V,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.V,lpeg.match
10623local fonts,logs,trackers=fonts,logs,trackers
10624local trace_loading=false  trackers.register("otf.loading",function(v) trace_loading=v end)
10625local report_otf=logs.reporter("fonts","otf loading")
10626local cid={}
10627fonts.cid=cid
10628local cidmap={}
10629local cidmax=10
10630local number=C(R("09","af","AF")^1)
10631local space=S(" \n\r\t")
10632local spaces=space^0
10633local period=P(".")
10634local periods=period*period
10635local name=P("/")*C((1-space)^1)
10636local unicodes,names={},{} 
10637local function do_one(a,b)
10638 unicodes[tonumber(a)]=tonumber(b,16)
10639end
10640local function do_range(a,b,c)
10641 c=tonumber(c,16)
10642 for i=tonumber(a),tonumber(b) do
10643  unicodes[i]=c
10644  c=c+1
10645 end
10646end
10647local function do_name(a,b)
10648 names[tonumber(a)]=b
10649end
10650local grammar=P { "start",
10651 start=number*spaces*number*V("series"),
10652 series=(spaces*(V("one")+V("range")+V("named")))^1,
10653 one=(number*spaces*number)/do_one,
10654 range=(number*periods*number*spaces*number)/do_range,
10655 named=(number*spaces*name)/do_name
10656}
10657local function loadcidfile(filename)
10658 local data=io.loaddata(filename)
10659 if data then
10660  unicodes,names={},{}
10661  lpegmatch(grammar,data)
10662  local supplement,registry,ordering=match(filename,"^(.-)%-(.-)%-()%.(.-)$")
10663  return {
10664   supplement=supplement,
10665   registry=registry,
10666   ordering=ordering,
10667   filename=filename,
10668   unicodes=unicodes,
10669   names=names,
10670  }
10671 end
10672end
10673cid.loadfile=loadcidfile 
10674local template="%s-%s-%s.cidmap"
10675local function locate(registry,ordering,supplement)
10676 local filename=format(template,registry,ordering,supplement)
10677 local hashname=lower(filename)
10678 local found=cidmap[hashname]
10679 if not found then
10680  if trace_loading then
10681   report_otf("checking cidmap, registry %a, ordering %a, supplement %a, filename %a",registry,ordering,supplement,filename)
10682  end
10683  local fullname=resolvers.findfile(filename,'cid') or ""
10684  if fullname~="" then
10685   found=loadcidfile(fullname)
10686   if found then
10687    if trace_loading then
10688     report_otf("using cidmap file %a",filename)
10689    end
10690    cidmap[hashname]=found
10691    found.usedname=file.basename(filename)
10692   end
10693  end
10694 end
10695 return found
10696end
10697function cid.getmap(specification)
10698 if not specification then
10699  report_otf("invalid cidinfo specification, table expected")
10700  return
10701 end
10702 local registry=specification.registry
10703 local ordering=specification.ordering
10704 local supplement=specification.supplement
10705 local filename=format(registry,ordering,supplement)
10706 local lowername=lower(filename)
10707 local found=cidmap[lowername]
10708 if found then
10709  return found
10710 end
10711 if ordering=="Identity" then
10712  local found={
10713   supplement=supplement,
10714   registry=registry,
10715   ordering=ordering,
10716   filename=filename,
10717   unicodes={},
10718   names={},
10719  }
10720  cidmap[lowername]=found
10721  return found
10722 end
10723 if trace_loading then
10724  report_otf("cidmap needed, registry %a, ordering %a, supplement %a",registry,ordering,supplement)
10725 end
10726 found=locate(registry,ordering,supplement)
10727 if not found then
10728  local supnum=tonumber(supplement)
10729  local cidnum=nil
10730  if supnum<cidmax then
10731   for s=supnum+1,cidmax do
10732    local c=locate(registry,ordering,s)
10733    if c then
10734     found,cidnum=c,s
10735     break
10736    end
10737   end
10738  end
10739  if not found and supnum>0 then
10740   for s=supnum-1,0,-1 do
10741    local c=locate(registry,ordering,s)
10742    if c then
10743     found,cidnum=c,s
10744     break
10745    end
10746   end
10747  end
10748  registry=lower(registry)
10749  ordering=lower(ordering)
10750  if found and cidnum>0 then
10751   for s=0,cidnum-1 do
10752    local filename=format(template,registry,ordering,s)
10753    if not cidmap[filename] then
10754     cidmap[filename]=found
10755    end
10756   end
10757  end
10758 end
10759 return found
10760end
10761
10762end -- closure
10763
10764do -- begin closure to overcome local limits and interference
10765
10766if not modules then modules={} end modules ['font-map']={
10767 version=1.001,
10768 optimize=true,
10769 comment="companion to font-ini.mkiv",
10770 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
10771 copyright="PRAGMA ADE / ConTeXt Development Team",
10772 license="see context related readme files"
10773}
10774local tonumber,next,type=tonumber,next,type
10775local match,format,find,concat,gsub,lower=string.match,string.format,string.find,table.concat,string.gsub,string.lower
10776local P,R,S,C,Ct,Cc,lpegmatch=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.match
10777local formatters=string.formatters
10778local sortedhash,sortedkeys=table.sortedhash,table.sortedkeys
10779local idiv=number.idiv
10780local trace_loading=false  trackers.register("fonts.loading",function(v) trace_loading=v end)
10781local trace_mapping=false  trackers.register("fonts.mapping",function(v) trace_mapping=v end)
10782local report_fonts=logs.reporter("fonts","loading")
10783local force_ligatures=false  directives.register("fonts.mapping.forceligatures",function(v) force_ligatures=v end)
10784local fonts=fonts or {}
10785local mappings=fonts.mappings or {}
10786fonts.mappings=mappings
10787local allocate=utilities.storage.allocate
10788local hex=R("AF","af","09")
10789local hexfour=(hex*hex*hex^-2)/function(s) return tonumber(s,16) end
10790local hexsix=(hex*hex*hex^-4)/function(s) return tonumber(s,16) end
10791local dec=(R("09")^1)/tonumber
10792local period=P(".")
10793local unicode=(P("uni")+P("UNI"))*(hexfour*(period+P(-1))*Cc(false)+Ct(hexfour^1)*Cc(true)) 
10794local ucode=(P("u")+P("U")  )*(hexsix*(period+P(-1))*Cc(false)+Ct(hexsix^1)*Cc(true)) 
10795local index=P("index")*dec*Cc(false)
10796local parser=unicode+ucode+index
10797local parsers={}
10798local function makenameparser(str)
10799 if not str or str=="" then
10800  return parser
10801 else
10802  local p=parsers[str]
10803  if not p then
10804   p=P(str)*period*dec*Cc(false)
10805   parsers[str]=p
10806  end
10807  return p
10808 end
10809end
10810local f_single=formatters["%04X"]
10811local f_double=formatters["%04X%04X"]
10812local s_unknown="FFFD"
10813local function tounicode16(unicode)
10814 if unicode<0xD7FF or (unicode>0xDFFF and unicode<=0xFFFF) then
10815  return f_single(unicode)
10816 elseif unicode>=0x00E000 and unicode<=0x00F8FF then
10817  return s_unknown
10818 elseif unicode>=0x0F0000 and unicode<=0x0FFFFF then
10819  return s_unknown
10820 elseif unicode>=0x100000 and unicode<=0x10FFFF then
10821  return s_unknown
10822 elseif unicode>=0x00D800 and unicode<=0x00DFFF then
10823  return s_unknown
10824 else
10825  unicode=unicode-0x10000
10826  return f_double(idiv(unicode,0x400)+0xD800,unicode%0x400+0xDC00)
10827 end
10828end
10829local function tounicode16sequence(unicodes)
10830 local t={}
10831 for l=1,#unicodes do
10832  local u=unicodes[l]
10833  if u<0xD7FF or (u>0xDFFF and u<=0xFFFF) then
10834   t[l]=f_single(u)
10835  elseif unicode>=0x00E000 and unicode<=0x00F8FF then
10836   t[l]=s_unknown
10837  elseif unicode>=0x0F0000 and unicode<=0x0FFFFF then
10838   t[l]=s_unknown
10839  elseif unicode>=0x100000 and unicode<=0x10FFFF then
10840   t[l]=s_unknown
10841  elseif unicode>=0x00D7FF and unicode<=0x00DFFF then
10842   t[l]=s_unknown
10843  else
10844   u=u-0x10000
10845   t[l]=f_double(idiv(u,0x400)+0xD800,u%0x400+0xDC00)
10846  end
10847 end
10848 return concat(t)
10849end
10850local hash={}
10851local conc={}
10852table.setmetatableindex(hash,function(t,k)
10853 local v
10854 if k<0xD7FF or (k>0xDFFF and k<=0xFFFF) then
10855  v=f_single(k)
10856 else
10857  local k=k-0x10000
10858  v=f_double(idiv(k,0x400)+0xD800,k%0x400+0xDC00)
10859 end
10860 t[k]=v
10861 return v
10862end)
10863local function tounicode(k)
10864 if type(k)=="table" then
10865  local n=#k
10866  for l=1,n do
10867   conc[l]=hash[k[l]]
10868  end
10869  return concat(conc,"",1,n)
10870 elseif k>=0x00E000 and k<=0x00F8FF then
10871  return s_unknown
10872 elseif k>=0x0F0000 and k<=0x0FFFFF then
10873  return s_unknown
10874 elseif k>=0x100000 and k<=0x10FFFF then
10875  return s_unknown
10876 elseif k>=0x00D7FF and k<=0x00DFFF then
10877  return s_unknown
10878 else
10879  return hash[k]
10880 end
10881end
10882local function fromunicode16(str)
10883 if #str==4 then
10884  return tonumber(str,16)
10885 else
10886  local l,r=match(str,"(....)(....)")
10887  return 0x10000+(tonumber(l,16)-0xD800)*0x400+tonumber(r,16)-0xDC00
10888 end
10889end
10890mappings.makenameparser=makenameparser
10891mappings.tounicode=tounicode
10892mappings.tounicode16=tounicode16
10893mappings.tounicode16sequence=tounicode16sequence
10894mappings.fromunicode16=fromunicode16
10895local ligseparator=P("_")
10896local varseparator=P(".")
10897local namesplitter=Ct(C((1-ligseparator-varseparator)^1)*(ligseparator*C((1-ligseparator-varseparator)^1))^0)
10898do
10899 local overloads={
10900  IJ={ name="I_J",unicode={ 0x49,0x4A },mess=0x0132 },
10901  ij={ name="i_j",unicode={ 0x69,0x6A },mess=0x0133 },
10902  ff={ name="f_f",unicode={ 0x66,0x66 },mess=0xFB00 },
10903  fi={ name="f_i",unicode={ 0x66,0x69 },mess=0xFB01 },
10904  fl={ name="f_l",unicode={ 0x66,0x6C },mess=0xFB02 },
10905  ffi={ name="f_f_i",unicode={ 0x66,0x66,0x69 },mess=0xFB03 },
10906  ffl={ name="f_f_l",unicode={ 0x66,0x66,0x6C },mess=0xFB04 },
10907  fj={ name="f_j",unicode={ 0x66,0x6A } },
10908  fk={ name="f_k",unicode={ 0x66,0x6B } },
10909 }
10910 local o=allocate {}
10911 for k,v in next,overloads do
10912  local name=v.name
10913  local mess=v.mess
10914  if name then
10915   o[name]=v
10916  end
10917  if mess then
10918   o[mess]=v
10919  end
10920  o[k]=v
10921 end
10922 mappings.overloads=o
10923end
10924function mappings.addtounicode(data,filename,checklookups,forceligatures)
10925 local resources=data.resources
10926 local unicodes=resources.unicodes
10927 if not unicodes then
10928  if trace_mapping then
10929   report_fonts("no unicode list, quitting tounicode for %a",filename)
10930  end
10931  return
10932 end
10933 local properties=data.properties
10934 local descriptions=data.descriptions
10935 local overloads=mappings.overloads
10936 unicodes['space']=unicodes['space']  or 32
10937 unicodes['hyphen']=unicodes['hyphen'] or 45
10938 unicodes['zwj']=unicodes['zwj'] or 0x200D
10939 unicodes['zwnj']=unicodes['zwnj']   or 0x200C
10940 local private=fonts.constructors and fonts.constructors.privateoffset or 0xF0000 
10941 local unicodevector=fonts.encodings.agl.unicodes or {} 
10942 local contextvector=fonts.encodings.agl.ctxcodes or {} 
10943 local missing={}
10944 local nofmissing=0
10945 local oparser=nil
10946 local cidnames=nil
10947 local cidcodes=nil
10948 local cidinfo=properties.cidinfo
10949 local usedmap=cidinfo and fonts.cid.getmap(cidinfo)
10950 local uparser=makenameparser() 
10951 if usedmap then
10952  oparser=usedmap and makenameparser(cidinfo.ordering)
10953  cidnames=usedmap.names
10954  cidcodes=usedmap.unicodes
10955 end
10956 local ns=0
10957 local nl=0
10958 local dlist=sortedkeys(descriptions)
10959 for i=1,#dlist do
10960  local du=dlist[i]
10961  local glyph=descriptions[du]
10962  local name=glyph.name
10963  if name then
10964   local overload=overloads[name] or overloads[du]
10965   if overload then
10966    glyph.unicode=overload.unicode
10967   else
10968    local gu=glyph.unicode 
10969    if not gu or gu==-1 or du>=private or (du>=0xE000 and du<=0xF8FF) or du==0xFFFE or du==0xFFFF then
10970     local unicode=unicodevector[name] or contextvector[name]
10971     if unicode then
10972      glyph.unicode=unicode
10973      ns=ns+1
10974     end
10975     if (not unicode) and usedmap then
10976      local foundindex=lpegmatch(oparser,name)
10977      if foundindex then
10978       unicode=cidcodes[foundindex] 
10979       if unicode then
10980        glyph.unicode=unicode
10981        ns=ns+1
10982       else
10983        local reference=cidnames[foundindex] 
10984        if reference then
10985         local foundindex=lpegmatch(oparser,reference)
10986         if foundindex then
10987          unicode=cidcodes[foundindex]
10988          if unicode then
10989           glyph.unicode=unicode
10990           ns=ns+1
10991          end
10992         end
10993         if not unicode or unicode=="" then
10994          local foundcodes,multiple=lpegmatch(uparser,reference)
10995          if foundcodes then
10996           glyph.unicode=foundcodes
10997           if multiple then
10998            nl=nl+1
10999            unicode=true
11000           else
11001            ns=ns+1
11002            unicode=foundcodes
11003           end
11004          end
11005         end
11006        end
11007       end
11008      end
11009     end
11010     if not unicode or unicode=="" then
11011      local split=lpegmatch(namesplitter,name)
11012      local nsplit=split and #split or 0 
11013      if nsplit==0 then
11014      elseif nsplit==1 then
11015       local base=split[1]
11016       local u=unicodes[base] or unicodevector[base] or contextvector[name]
11017       if not u then
11018       elseif type(u)=="table" then
11019        if u[1]<private then
11020         unicode=u
11021         glyph.unicode=unicode
11022        end
11023       elseif u<private then
11024        unicode=u
11025        glyph.unicode=unicode
11026       end
11027      else
11028       local t={}
11029       local n=0
11030       for l=1,nsplit do
11031        local base=split[l]
11032        local u=unicodes[base] or unicodevector[base] or contextvector[name]
11033        if not u then
11034         break
11035        elseif type(u)=="table" then
11036         if u[1]>=private then
11037          break
11038         end
11039         n=n+1
11040         t[n]=u[1]
11041        else
11042         if u>=private then
11043          break
11044         end
11045         n=n+1
11046         t[n]=u
11047        end
11048       end
11049       if n>0 then
11050        if n==1 then
11051         unicode=t[1]
11052        else
11053         unicode=t
11054        end
11055        glyph.unicode=unicode
11056       end
11057      end
11058      nl=nl+1
11059     end
11060     if not unicode or unicode=="" then
11061      local foundcodes,multiple=lpegmatch(uparser,name)
11062      if foundcodes then
11063       glyph.unicode=foundcodes
11064       if multiple then
11065        nl=nl+1
11066        unicode=true
11067       else
11068        ns=ns+1
11069        unicode=foundcodes
11070       end
11071      end
11072     end
11073     local r=overloads[unicode]
11074     if r then
11075      unicode=r.unicode
11076      glyph.unicode=unicode
11077     end
11078     if not unicode then
11079      missing[du]=true
11080      nofmissing=nofmissing+1
11081     end
11082    else
11083    end
11084   end
11085  else
11086   local overload=overloads[du]
11087   if overload then
11088    glyph.unicode=overload.unicode
11089   elseif not glyph.unicode then
11090    missing[du]=true
11091    nofmissing=nofmissing+1
11092   end
11093  end
11094 end
11095 if type(checklookups)=="function" then
11096  checklookups(data,missing,nofmissing)
11097 end
11098 local unicoded=0
11099 local collected=fonts.handlers.otf.readers.getcomponents(data) 
11100 local function resolve(glyph,u)
11101  local n=#u
11102  for i=1,n do
11103   if u[i]>private then
11104    n=0
11105    break
11106   end
11107  end
11108  if n>0 then
11109   if n>1 then
11110    glyph.unicode=u
11111   else
11112    glyph.unicode=u[1]
11113   end
11114   unicoded=unicoded+1
11115  end
11116 end
11117 if not collected then
11118 elseif forceligatures or force_ligatures then
11119  for i=1,#dlist do
11120   local du=dlist[i]
11121   if du>=private or (du>=0xE000 and du<=0xF8FF) then
11122    local u=collected[du] 
11123    if u then
11124     resolve(descriptions[du],u)
11125    end
11126   end
11127  end
11128 else
11129  for i=1,#dlist do
11130   local du=dlist[i]
11131   if du>=private or (du>=0xE000 and du<=0xF8FF) then
11132    local glyph=descriptions[du]
11133    if glyph.class=="ligature" and not glyph.unicode then
11134     local u=collected[du] 
11135     if u then
11136       resolve(glyph,u)
11137     end
11138    end
11139   end
11140  end
11141 end
11142 if trace_mapping and unicoded>0 then
11143  report_fonts("%n ligature tounicode mappings deduced from gsub ligature features",unicoded)
11144 end
11145 if trace_mapping then
11146  for i=1,#dlist do
11147   local du=dlist[i]
11148   local glyph=descriptions[du]
11149   local name=glyph.name or "-"
11150   local index=glyph.index or 0
11151   local unicode=glyph.unicode
11152   if unicode then
11153    if type(unicode)=="table" then
11154     local unicodes={}
11155     for i=1,#unicode do
11156      unicodes[i]=formatters("%U",unicode[i])
11157     end
11158     report_fonts("internal slot %U, name %a, unicode %U, tounicode % t",index,name,du,unicodes)
11159    else
11160     report_fonts("internal slot %U, name %a, unicode %U, tounicode %U",index,name,du,unicode)
11161    end
11162   else
11163    report_fonts("internal slot %U, name %a, unicode %U",index,name,du)
11164   end
11165  end
11166 end
11167 if trace_loading and (ns>0 or nl>0) then
11168  report_fonts("%s tounicode entries added, ligatures %s",nl+ns,ns)
11169 end
11170end
11171
11172end -- closure
11173
11174do -- begin closure to overcome local limits and interference
11175
11176if not modules then modules={} end modules ['luatex-fonts-syn']={
11177 version=1.001,
11178 comment="companion to luatex-*.tex",
11179 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
11180 copyright="PRAGMA ADE / ConTeXt Development Team",
11181 license="see context related readme files"
11182}
11183if context then
11184--removed
11185
11186end
11187local fonts=fonts
11188fonts.names=fonts.names or {}
11189fonts.names.version=1.001 
11190fonts.names.basename="luatex-fonts-names"
11191fonts.names.cache=containers.define("fonts","data",fonts.names.version,true)
11192local data=nil
11193local loaded=false
11194local fileformats={ "lua","tex","other text files" }
11195function fonts.names.reportmissingbase()
11196 logs.report("fonts","missing font database, run: mtxrun --script fonts --reload --simple")
11197 fonts.names.reportmissingbase=nil
11198end
11199function fonts.names.reportmissingname()
11200 logs.report("fonts","unknown font in font database, run: mtxrun --script fonts --reload --simple")
11201 fonts.names.reportmissingname=nil
11202end
11203function fonts.names.resolve(name,sub)
11204 if not loaded then
11205  local basename=fonts.names.basename
11206  if basename and basename~="" then
11207   data=containers.read(fonts.names.cache,basename)
11208   if not data then
11209    basename=file.addsuffix(basename,"lua")
11210    for i=1,#fileformats do
11211     local format=fileformats[i]
11212     local foundname=resolvers.findfile(basename,format) or ""
11213     if foundname~="" then
11214      data=dofile(foundname)
11215      logs.report("fonts","font database '%s' loaded",foundname)
11216      break
11217     end
11218    end
11219   end
11220  end
11221  loaded=true
11222 end
11223 if type(data)=="table" and data.version==fonts.names.version then
11224  local condensed=string.gsub(string.lower(name),"[^%a%d]","")
11225  local found=data.mappings and data.mappings[condensed]
11226  if found then
11227   local fontname,filename,subfont=found[1],found[2],found[3]
11228   if subfont then
11229    return filename,fontname
11230   else
11231    return filename,false
11232   end
11233  elseif fonts.names.reportmissingname then
11234   fonts.names.reportmissingname()
11235   return name,false 
11236  end
11237 elseif fonts.names.reportmissingbase then
11238  fonts.names.reportmissingbase()
11239 end
11240end
11241fonts.names.resolvespec=fonts.names.resolve 
11242function fonts.names.getfilename(askedname,suffix)  
11243 return ""
11244end
11245function fonts.names.ignoredfile(filename) 
11246 return false 
11247end
11248
11249end -- closure
11250
11251do -- begin closure to overcome local limits and interference
11252
11253if not modules then modules={} end modules ['font-vfc']={
11254 version=1.001,
11255 comment="companion to font-ini.mkiv and hand-ini.mkiv",
11256 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
11257 copyright="PRAGMA ADE / ConTeXt Development Team",
11258 license="see context related readme files"
11259}
11260local select,type=select,type
11261local insert=table.insert
11262local fonts=fonts
11263local helpers=fonts.helpers
11264local setmetatableindex=table.setmetatableindex
11265local push={ "push" }
11266local pop={ "pop" }
11267local dummy={ "comment" }
11268function helpers.prependcommands(commands,...)
11269 insert(commands,1,push)
11270 for i=select("#",...),1,-1 do
11271  local s=(select(i,...))
11272  if s then
11273   insert(commands,1,s)
11274  end
11275 end
11276 insert(commands,pop)
11277 return commands
11278end
11279function helpers.appendcommands(commands,...)
11280 insert(commands,1,push)
11281 insert(commands,pop)
11282 for i=1,select("#",...) do
11283  local s=(select(i,...))
11284  if s then
11285   insert(commands,s)
11286  end
11287 end
11288 return commands
11289end
11290function helpers.prependcommandtable(commands,t)
11291 insert(commands,1,push)
11292 for i=#t,1,-1 do
11293  local s=t[i]
11294  if s then
11295   insert(commands,1,s)
11296  end
11297 end
11298 insert(commands,pop)
11299 return commands
11300end
11301function helpers.appendcommandtable(commands,t)
11302 insert(commands,1,push)
11303 insert(commands,pop)
11304 for i=1,#t do
11305  local s=t[i]
11306  if s then
11307   insert(commands,s)
11308  end
11309 end
11310 return commands
11311end
11312local char=setmetatableindex(function(t,k)
11313 local v={ "slot",0,k }
11314 t[k]=v
11315 return v
11316end)
11317local right=setmetatableindex(function(t,k)
11318 local v={ "right",k }
11319 t[k]=v
11320 return v
11321end)
11322local left=setmetatableindex(function(t,k)
11323 local v={ "right",-k }
11324 t[k]=v
11325 return v
11326end)
11327local down=setmetatableindex(function(t,k)
11328 local v={ "down",k }
11329 t[k]=v
11330 return v
11331end)
11332local up=setmetatableindex(function(t,k)
11333 local v={ "down",-k }
11334 t[k]=v
11335 return v
11336end)
11337helpers.commands=utilities.storage.allocate {
11338 char=char,
11339 right=right,
11340 left=left,
11341 down=down,
11342 up=up,
11343 push=push,
11344 pop=pop,
11345 dummy=dummy,
11346}
11347
11348end -- closure
11349
11350do -- begin closure to overcome local limits and interference
11351
11352if not modules then modules={} end modules ['font-otr']={
11353 version=1.001,
11354 optimize=true,
11355 comment="companion to font-ini.mkiv",
11356 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
11357 copyright="PRAGMA ADE / ConTeXt Development Team",
11358 license="see context related readme files"
11359}
11360local number=number
11361local next,type,tonumber,rawget=next,type,tonumber,rawget
11362local byte,lower,char,gsub=string.byte,string.lower,string.char,string.gsub
11363local fullstrip=string.fullstrip
11364local floor,round=math.floor,math.round
11365local P,R,S,C,Cs,Cc,Ct,Carg,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Ct,lpeg.Carg,lpeg.Cmt
11366local lpegmatch=lpeg.match
11367local rshift=bit32.rshift
11368local setmetatableindex=table.setmetatableindex
11369local sortedkeys=table.sortedkeys
11370local sortedhash=table.sortedhash
11371local stripstring=string.nospaces
11372local utf16_to_utf8_be=utf.utf16_to_utf8_be
11373local report=logs.reporter("otf reader")
11374local report_cmap=logs.reporter("otf reader","cmap")
11375local trace_cmap=false  trackers.register("otf.cmap",function(v) trace_cmap=v end)
11376local trace_cmap_details=false  trackers.register("otf.cmap.details",function(v) trace_cmap_details=v end)
11377fonts=fonts or {}
11378local handlers=fonts.handlers or {}
11379fonts.handlers=handlers
11380local otf=handlers.otf or {}
11381handlers.otf=otf
11382local readers=otf.readers or {}
11383otf.readers=readers
11384local streamreader=utilities.files   
11385local streamwriter=utilities.files
11386readers.streamreader=streamreader
11387readers.streamwriter=streamwriter
11388local openfile=streamreader.open
11389local closefile=streamreader.close
11390local setposition=streamreader.setposition
11391local skipshort=streamreader.skipshort
11392local readbytes=streamreader.readbytes
11393local readstring=streamreader.readstring
11394local readbyte=streamreader.readcardinal1  
11395local readushort=streamreader.readcardinal2  
11396local readuint=streamreader.readcardinal3  
11397local readulong=streamreader.readcardinal4
11398local readshort=streamreader.readinteger2   
11399local readlong=streamreader.readinteger4   
11400local readfixed=streamreader.readfixed4
11401local read2dot14=streamreader.read2dot14  
11402local readfword=readshort       
11403local readufword=readushort      
11404local readoffset=readushort
11405local readcardinaltable=streamreader.readcardinaltable
11406local readintegertable=streamreader.readintegertable
11407function streamreader.readtag(f)
11408 return lower(stripstring(readstring(f,4)))
11409end
11410local short=2
11411local ushort=2
11412local ulong=4
11413directives.register("fonts.streamreader",function()
11414 streamreader=utilities.streams
11415 openfile=streamreader.open
11416 closefile=streamreader.close
11417 setposition=streamreader.setposition
11418 skipshort=streamreader.skipshort
11419 readbytes=streamreader.readbytes
11420 readstring=streamreader.readstring
11421 readbyte=streamreader.readcardinal1
11422 readushort=streamreader.readcardinal2
11423 readuint=streamreader.readcardinal3
11424 readulong=streamreader.readcardinal4
11425 readshort=streamreader.readinteger2
11426 readlong=streamreader.readinteger4
11427 readfixed=streamreader.readfixed4
11428 read2dot14=streamreader.read2dot14
11429 readfword=readshort
11430 readufword=readushort
11431 readoffset=readushort
11432 readcardinaltable=streamreader.readcardinaltable
11433 readintegertable=streamreader.readintegertable
11434 function streamreader.readtag(f)
11435  return lower(stripstring(readstring(f,4)))
11436 end
11437end)
11438local function readlongdatetime(f)
11439 local a,b,c,d,e,f,g,h=readbytes(f,8)
11440 return 0x100000000*d+0x1000000*e+0x10000*f+0x100*g+h
11441end
11442local tableversion=0.004
11443readers.tableversion=tableversion
11444local privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000
11445local reservednames={ [0]="copyright",
11446 "family",
11447 "subfamily",
11448 "uniqueid",
11449 "fullname",
11450 "version",
11451 "postscriptname",
11452 "trademark",
11453 "manufacturer",
11454 "designer",
11455 "description",
11456 "vendorurl",
11457 "designerurl",
11458 "license",
11459 "licenseurl",
11460 "reserved",
11461 "typographicfamily",
11462 "typographicsubfamily",
11463 "compatiblefullname",
11464 "sampletext",
11465 "cidfindfontname",
11466 "wwsfamily",
11467 "wwssubfamily",
11468 "lightbackgroundpalette",
11469 "darkbackgroundpalette",
11470 "variationspostscriptnameprefix",
11471}
11472local platforms={ [0]="unicode",
11473 "macintosh",
11474 "iso",
11475 "windows",
11476 "custom",
11477}
11478local encodings={
11479 unicode={ [0]="unicode 1.0 semantics",
11480  "unicode 1.1 semantics",
11481  "iso/iec 10646",
11482  "unicode 2.0 bmp",
11483  "unicode 2.0 full",
11484  "unicode variation sequences",
11485  "unicode full repertoire",
11486 },
11487 macintosh={ [0]="roman","japanese","chinese (traditional)","korean","arabic","hebrew","greek","russian",
11488  "rsymbol","devanagari","gurmukhi","gujarati","oriya","bengali","tamil","telugu","kannada",
11489  "malayalam","sinhalese","burmese","khmer","thai","laotian","georgian","armenian",
11490  "chinese (simplified)","tibetan","mongolian","geez","slavic","vietnamese","sindhi",
11491  "uninterpreted",
11492 },
11493 iso={ [0]="7-bit ascii",
11494  "iso 10646",
11495  "iso 8859-1",
11496 },
11497 windows={ [0]="symbol",
11498  "unicode bmp",
11499  "shiftjis",
11500  "prc",
11501  "big5",
11502  "wansung",
11503  "johab",
11504  "reserved 7",
11505  "reserved 8",
11506  "reserved 9",
11507  "unicode ucs-4",
11508 },
11509 custom={
11510 }
11511}
11512local decoders={
11513 unicode={},
11514 macintosh={},
11515 iso={},
11516 windows={
11517  ["unicode semantics"]=utf16_to_utf8_be,
11518  ["unicode bmp"]=utf16_to_utf8_be,
11519  ["unicode full"]=utf16_to_utf8_be,
11520  ["unicode 1.0 semantics"]=utf16_to_utf8_be,
11521  ["unicode 1.1 semantics"]=utf16_to_utf8_be,
11522  ["unicode 2.0 bmp"]=utf16_to_utf8_be,
11523  ["unicode 2.0 full"]=utf16_to_utf8_be,
11524  ["unicode variation sequences"]=utf16_to_utf8_be,
11525  ["unicode full repertoire"]=utf16_to_utf8_be,
11526 },
11527 custom={},
11528}
11529local languages={
11530 unicode={
11531  [  0]="english",
11532 },
11533 macintosh={
11534  [  0]="english",
11535 },
11536 iso={},
11537 windows={
11538  [0x0409]="english - united states",
11539 },
11540 custom={},
11541}
11542local standardromanencoding={ [0]=
11543 "notdef",".null","nonmarkingreturn","space","exclam","quotedbl",
11544 "numbersign","dollar","percent","ampersand","quotesingle","parenleft",
11545 "parenright","asterisk","plus","comma","hyphen","period","slash",
11546 "zero","one","two","three","four","five","six","seven","eight",
11547 "nine","colon","semicolon","less","equal","greater","question","at",
11548 "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O",
11549 "P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft",
11550 "backslash","bracketright","asciicircum","underscore","grave","a","b",
11551 "c","d","e","f","g","h","i","j","k","l","m","n","o","p","q",
11552 "r","s","t","u","v","w","x","y","z","braceleft","bar",
11553 "braceright","asciitilde","Adieresis","Aring","Ccedilla","Eacute",
11554 "Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex",
11555 "adieresis","atilde","aring","ccedilla","eacute","egrave",
11556 "ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis",
11557 "ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute",
11558 "ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling",
11559 "section","bullet","paragraph","germandbls","registered","copyright",
11560 "trademark","acute","dieresis","notequal","AE","Oslash","infinity",
11561 "plusminus","lessequal","greaterequal","yen","mu","partialdiff",
11562 "summation","product","pi","integral","ordfeminine","ordmasculine",
11563 "Omega","ae","oslash","questiondown","exclamdown","logicalnot",
11564 "radical","florin","approxequal","Delta","guillemotleft",
11565 "guillemotright","ellipsis","nonbreakingspace","Agrave","Atilde",
11566 "Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright",
11567 "quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis",
11568 "fraction","currency","guilsinglleft","guilsinglright","fi","fl",
11569 "daggerdbl","periodcentered","quotesinglbase","quotedblbase",
11570 "perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave",
11571 "Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex",
11572 "apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi",
11573 "circumflex","tilde","macron","breve","dotaccent","ring","cedilla",
11574 "hungarumlaut","ogonek","caron","Lslash","lslash","Scaron","scaron",
11575 "Zcaron","zcaron","brokenbar","Eth","eth","Yacute","yacute","Thorn",
11576 "thorn","minus","multiply","onesuperior","twosuperior","threesuperior",
11577 "onehalf","onequarter","threequarters","franc","Gbreve","gbreve",
11578 "Idotaccent","Scedilla","scedilla","Cacute","cacute","Ccaron","ccaron",
11579 "dcroat",
11580}
11581local weights={
11582 [100]="thin",
11583 [200]="extralight",
11584 [300]="light",
11585 [400]="normal",
11586 [500]="medium",
11587 [600]="semibold",
11588 [700]="bold",
11589 [800]="extrabold",
11590 [900]="black",
11591}
11592local widths={
11593 "ultracondensed",
11594 "extracondensed",
11595 "condensed",
11596 "semicondensed",
11597 "normal",
11598 "semiexpanded",
11599 "expanded",
11600 "extraexpanded",
11601 "ultraexpanded",
11602}
11603setmetatableindex(weights,function(t,k)
11604 local r=floor((k+50)/100)*100
11605 local v=(r>900 and "black") or rawget(t,r) or "normal"
11606 return v
11607end)
11608setmetatableindex(widths,function(t,k)
11609 return "normal"
11610end)
11611local panoseweights={ [0]="normal",
11612 "normal",
11613 "verylight",
11614 "light",
11615 "thin",
11616 "book",
11617 "medium",
11618 "demi",
11619 "bold",
11620 "heavy",
11621 "black",
11622}
11623local panosewidths={ [0]="normal",
11624 "normal",
11625 "normal",
11626 "normal",
11627 "normal",
11628 "expanded",
11629 "condensed",
11630 "veryexpanded",
11631 "verycondensed",
11632 "monospaced",
11633}
11634local helpers={}
11635readers.helpers=helpers
11636local function gotodatatable(f,fontdata,tag,criterium)
11637 if criterium and f then
11638  local tables=fontdata.tables
11639  if tables then
11640   local datatable=tables[tag]
11641   if datatable then
11642    local tableoffset=datatable.offset
11643    setposition(f,tableoffset)
11644    return tableoffset
11645   end
11646  else
11647   report("no tables")
11648  end
11649 end
11650end
11651local function reportskippedtable(f,fontdata,tag,criterium)
11652 if criterium and f then
11653  local tables=fontdata.tables
11654  if tables then
11655   local datatable=tables[tag]
11656   if datatable then
11657    report("loading of table %a skipped",tag)
11658   end
11659  else
11660   report("no tables")
11661  end
11662 end
11663end
11664local function setvariabledata(fontdata,tag,data)
11665 local variabledata=fontdata.variabledata
11666 if variabledata then
11667  variabledata[tag]=data
11668 else
11669  fontdata.variabledata={ [tag]=data }
11670 end
11671end
11672helpers.gotodatatable=gotodatatable
11673helpers.setvariabledata=setvariabledata
11674helpers.reportskippedtable=reportskippedtable
11675local platformnames={
11676 postscriptname=true,
11677 fullname=true,
11678 family=true,
11679 subfamily=true,
11680 typographicfamily=true,
11681 typographicsubfamily=true,
11682 compatiblefullname=true,
11683}
11684local platformextras={
11685 uniqueid=true,
11686 version=true,
11687 copyright=true,
11688 license=true,
11689 licenseurl=true,
11690 manufacturer=true,
11691 vendorurl=true,
11692}
11693function readers.name(f,fontdata,specification)
11694 local tableoffset=gotodatatable(f,fontdata,"name",true)
11695 if tableoffset then
11696  local format=readushort(f)
11697  local nofnames=readushort(f)
11698  local offset=readushort(f)
11699  local start=tableoffset+offset
11700  local namelists={
11701   unicode={},
11702   windows={},
11703   macintosh={},
11704  }
11705  for i=1,nofnames do
11706   local platform=platforms[readushort(f)]
11707   if platform then
11708    local namelist=namelists[platform]
11709    if namelist then
11710     local encoding=readushort(f)
11711     local language=readushort(f)
11712     local encodings=encodings[platform]
11713     local languages=languages[platform]
11714     if encodings and languages then
11715      local encoding=encodings[encoding]
11716      local language=languages[language]
11717      if encoding and language then
11718       local index=readushort(f)
11719       local name=reservednames[index]
11720       namelist[#namelist+1]={
11721        platform=platform,
11722        encoding=encoding,
11723        language=language,
11724        name=name,
11725        index=index,
11726        length=readushort(f),
11727        offset=start+readushort(f),
11728       }
11729      else
11730       skipshort(f,3)
11731      end
11732     else
11733      skipshort(f,3)
11734     end
11735    else
11736     skipshort(f,5)
11737    end
11738   else
11739    skipshort(f,5)
11740   end
11741  end
11742  local names={}
11743  local done={}
11744  local extras={}
11745  local function decoded(platform,encoding,content)
11746   local decoder=decoders[platform]
11747   if decoder then
11748    decoder=decoder[encoding]
11749   end
11750   if decoder then
11751    return decoder(content)
11752   else
11753    return content
11754   end
11755  end
11756  local function filter(platform,e,l)
11757   local namelist=namelists[platform]
11758   for i=1,#namelist do
11759    local name=namelist[i]
11760    local nametag=name.name
11761    local index=name.index
11762    if not done[nametag or i] then
11763     local encoding=name.encoding
11764     local language=name.language
11765     if (not e or encoding==e) and (not l or language==l) then
11766      setposition(f,name.offset)
11767      local content=decoded(platform,encoding,readstring(f,name.length))
11768      if nametag then
11769       names[nametag]={
11770        content=content,
11771        platform=platform,
11772        encoding=encoding,
11773        language=language,
11774       }
11775      end
11776      extras[index]=content
11777      done[nametag or i]=true
11778     end
11779    end
11780   end
11781  end
11782  filter("windows","unicode bmp","english - united states")
11783  filter("macintosh","roman","english")
11784  filter("windows")
11785  filter("macintosh")
11786  filter("unicode")
11787  fontdata.names=names
11788  fontdata.extras=extras
11789  if specification.platformnames then
11790   local collected={}
11791   local platformextras=specification.platformextras and platformextras
11792   for platform,namelist in next,namelists do
11793    local filtered=false
11794    for i=1,#namelist do
11795     local entry=namelist[i]
11796     local name=entry.name
11797     if platformnames[name] or (platformextras and platformextras[name]) then
11798      setposition(f,entry.offset)
11799      local content=decoded(platform,entry.encoding,readstring(f,entry.length))
11800      if filtered then
11801       filtered[name]=content
11802      else
11803       filtered={ [name]=content }
11804      end
11805     end
11806    end
11807    if filtered then
11808     collected[platform]=filtered
11809    end
11810   end
11811   fontdata.platformnames=collected
11812  end
11813 else
11814  fontdata.names={}
11815 end
11816end
11817local validutf=lpeg.patterns.validutf8
11818local function getname(fontdata,key)
11819 local names=fontdata.names
11820 if names then
11821  local value=names[key]
11822  if value then
11823   local content=value.content
11824   return lpegmatch(validutf,content) and content or nil
11825  end
11826 end
11827end
11828readers["os/2"]=function(f,fontdata)
11829 local tableoffset=gotodatatable(f,fontdata,"os/2",true)
11830 if tableoffset then
11831  local version=readushort(f)
11832  local windowsmetrics={
11833   version=version,
11834   averagewidth=readshort(f),
11835   weightclass=readushort(f),
11836   widthclass=readushort(f),
11837   fstype=readushort(f),
11838   subscriptxsize=readshort(f),
11839   subscriptysize=readshort(f),
11840   subscriptxoffset=readshort(f),
11841   subscriptyoffset=readshort(f),
11842   superscriptxsize=readshort(f),
11843   superscriptysize=readshort(f),
11844   superscriptxoffset=readshort(f),
11845   superscriptyoffset=readshort(f),
11846   strikeoutsize=readshort(f),
11847   strikeoutpos=readshort(f),
11848   familyclass=readshort(f),
11849   panose={ readbytes(f,10) },
11850   unicoderanges={ readulong(f),readulong(f),readulong(f),readulong(f) },
11851   vendor=readstring(f,4),
11852   fsselection=readushort(f),
11853   firstcharindex=readushort(f),
11854   lastcharindex=readushort(f),
11855   typoascender=readshort(f),
11856   typodescender=readshort(f),
11857   typolinegap=readshort(f),
11858   winascent=readushort(f),
11859   windescent=readushort(f),
11860  }
11861  if version>=1 then
11862   windowsmetrics.codepageranges={ readulong(f),readulong(f) }
11863  end
11864  if version>=2 then
11865   windowsmetrics.xheight=readshort(f)
11866   windowsmetrics.capheight=readshort(f)
11867   windowsmetrics.defaultchar=readushort(f)
11868   windowsmetrics.breakchar=readushort(f)
11869  end
11870  windowsmetrics.weight=windowsmetrics.weightclass and weights[windowsmetrics.weightclass]
11871  windowsmetrics.width=windowsmetrics.widthclass and  widths [windowsmetrics.widthclass]
11872  windowsmetrics.panoseweight=panoseweights[windowsmetrics.panose[3]]
11873  windowsmetrics.panosewidth=panosewidths [windowsmetrics.panose[4]]
11874  fontdata.windowsmetrics=windowsmetrics
11875 else
11876  fontdata.windowsmetrics={}
11877 end
11878end
11879readers.head=function(f,fontdata)
11880 local tableoffset=gotodatatable(f,fontdata,"head",true)
11881 if tableoffset then
11882  local version=readulong(f)
11883  local fontversion=readulong(f)
11884  local fontheader={
11885   version=version,
11886   fontversion=number.to16dot16(fontversion),
11887   fontversionnumber=fontversion,
11888   checksum=readushort(f)*0x10000+readushort(f),
11889   magic=readulong(f),
11890   flags=readushort(f),
11891   units=readushort(f),
11892   created=readlongdatetime(f),
11893   modified=readlongdatetime(f),
11894   xmin=readshort(f),
11895   ymin=readshort(f),
11896   xmax=readshort(f),
11897   ymax=readshort(f),
11898   macstyle=readushort(f),
11899   smallpixels=readushort(f),
11900   directionhint=readshort(f),
11901   indextolocformat=readshort(f),
11902   glyphformat=readshort(f),
11903  }
11904  fontdata.fontheader=fontheader
11905 else
11906  fontdata.fontheader={}
11907 end
11908 fontdata.nofglyphs=0
11909end
11910readers.hhea=function(f,fontdata,specification)
11911 local tableoffset=gotodatatable(f,fontdata,"hhea",specification.details)
11912 if tableoffset then
11913  fontdata.horizontalheader={
11914   version=readulong(f),
11915   ascender=readfword(f),
11916   descender=readfword(f),
11917   linegap=readfword(f),
11918   maxadvancewidth=readufword(f),
11919   minleftsidebearing=readfword(f),
11920   minrightsidebearing=readfword(f),
11921   maxextent=readfword(f),
11922   caretsloperise=readshort(f),
11923   caretsloperun=readshort(f),
11924   caretoffset=readshort(f),
11925   reserved_1=readshort(f),
11926   reserved_2=readshort(f),
11927   reserved_3=readshort(f),
11928   reserved_4=readshort(f),
11929   metricdataformat=readshort(f),
11930   nofmetrics=readushort(f),
11931  }
11932 else
11933  fontdata.horizontalheader={
11934   nofmetrics=0,
11935  }
11936 end
11937end
11938readers.vhea=function(f,fontdata,specification)
11939 local tableoffset=gotodatatable(f,fontdata,"vhea",specification.details)
11940 if tableoffset then
11941  fontdata.verticalheader={
11942   version=readulong(f),
11943   ascender=readfword(f),
11944   descender=readfword(f),
11945   linegap=readfword(f),
11946   maxadvanceheight=readufword(f),
11947   mintopsidebearing=readfword(f),
11948   minbottomsidebearing=readfword(f),
11949   maxextent=readfword(f),
11950   caretsloperise=readshort(f),
11951   caretsloperun=readshort(f),
11952   caretoffset=readshort(f),
11953   reserved_1=readshort(f),
11954   reserved_2=readshort(f),
11955   reserved_3=readshort(f),
11956   reserved_4=readshort(f),
11957   metricdataformat=readshort(f),
11958   nofmetrics=readushort(f),
11959  }
11960 else
11961  fontdata.verticalheader={
11962   nofmetrics=0,
11963  }
11964 end
11965end
11966readers.maxp=function(f,fontdata,specification)
11967 local tableoffset=gotodatatable(f,fontdata,"maxp",specification.details)
11968 if tableoffset then
11969  local version=readulong(f)
11970  local nofglyphs=readushort(f)
11971  fontdata.nofglyphs=nofglyphs
11972  if version==0x00005000 then
11973   fontdata.maximumprofile={
11974    version=version,
11975    nofglyphs=nofglyphs,
11976   }
11977  elseif version==0x00010000 then
11978   fontdata.maximumprofile={
11979    version=version,
11980    nofglyphs=nofglyphs,
11981    points=readushort(f),
11982    contours=readushort(f),
11983    compositepoints=readushort(f),
11984    compositecontours=readushort(f),
11985    zones=readushort(f),
11986    twilightpoints=readushort(f),
11987    storage=readushort(f),
11988    functiondefs=readushort(f),
11989    instructiondefs=readushort(f),
11990    stackelements=readushort(f),
11991    sizeofinstructions=readushort(f),
11992    componentelements=readushort(f),
11993    componentdepth=readushort(f),
11994   }
11995  else
11996   fontdata.maximumprofile={
11997    version=version,
11998    nofglyphs=0,
11999   }
12000  end
12001 end
12002end
12003readers.hmtx=function(f,fontdata,specification)
12004 local tableoffset=gotodatatable(f,fontdata,"hmtx",specification.glyphs)
12005 if tableoffset then
12006  local horizontalheader=fontdata.horizontalheader
12007  local nofmetrics=horizontalheader.nofmetrics
12008  local glyphs=fontdata.glyphs
12009  local nofglyphs=fontdata.nofglyphs
12010  local width=0 
12011  local leftsidebearing=0
12012  for i=0,nofmetrics-1 do
12013   local glyph=glyphs[i]
12014   width=readshort(f) 
12015   leftsidebearing=readshort(f)
12016   glyph.width=width
12017  end
12018  for i=0,nofglyphs-1 do
12019   local glyph=glyphs[i]
12020   if not glyph.width then
12021    glyph.width=width
12022   end
12023  end
12024 end
12025end
12026readers.vmtx=function(f,fontdata,specification)
12027 local tableoffset=gotodatatable(f,fontdata,"vmtx",specification.glyphs)
12028 if tableoffset then
12029  local verticalheader=fontdata.verticalheader
12030  local nofmetrics=verticalheader.nofmetrics
12031  local glyphs=fontdata.glyphs
12032  local nofglyphs=fontdata.nofglyphs
12033  local vheight=0
12034  local vdefault=verticalheader.ascender-verticalheader.descender
12035  local topsidebearing=0
12036  for i=0,nofmetrics-1 do
12037   local glyph=glyphs[i]
12038   vheight=readushort(f)
12039   topsidebearing=readshort(f)
12040   if vheight~=0 and vheight~=vdefault then
12041    glyph.vheight=vheight
12042   end
12043   if topsidebearing~=0 then
12044    glyph.tsb=topsidebearing
12045   end
12046  end
12047  for i=nofmetrics,nofglyphs-1 do
12048   local glyph=glyphs[i]
12049   if vheight~=0 and vheight~=vdefault then
12050    glyph.vheight=vheight
12051   end
12052  end
12053 end
12054end
12055readers.vorg=function(f,fontdata,specification)
12056 reportskippedtable(f,fontdata,"vorg",specification.glyphs)
12057end
12058readers.post=function(f,fontdata,specification)
12059 local tableoffset=gotodatatable(f,fontdata,"post",true)
12060 if tableoffset then
12061  local version=readulong(f)
12062  fontdata.postscript={
12063   version=version,
12064   italicangle=readfixed(f),
12065   underlineposition=readfword(f),
12066   underlinethickness=readfword(f),
12067   monospaced=readulong(f),
12068   minmemtype42=readulong(f),
12069   maxmemtype42=readulong(f),
12070   minmemtype1=readulong(f),
12071   maxmemtype1=readulong(f),
12072  }
12073  if not specification.glyphs then
12074  elseif version==0x00010000 then
12075   for index=0,#standardromanencoding do
12076    glyphs[index].name=standardromanencoding[index]
12077   end
12078  elseif version==0x00020000 then
12079   local glyphs=fontdata.glyphs
12080   local nofglyphs=readushort(f)
12081   local indices={}
12082   local names={}
12083   local maxnames=0
12084   for i=0,nofglyphs-1 do
12085    local nameindex=readushort(f)
12086    if nameindex>=258 then
12087     maxnames=maxnames+1
12088     nameindex=nameindex-257
12089     indices[nameindex]=i
12090    else
12091     glyphs[i].name=standardromanencoding[nameindex]
12092    end
12093   end
12094   for i=1,maxnames do
12095    local mapping=indices[i]
12096    if not mapping then
12097     report("quit post name fetching at %a of %a: %s",i,maxnames,"no index")
12098     break
12099    else
12100     local length=readbyte(f)
12101     if length>0 then
12102      glyphs[mapping].name=readstring(f,length)
12103     else
12104     end
12105    end
12106   end
12107  end
12108 else
12109  fontdata.postscript={}
12110 end
12111end
12112readers.cff=function(f,fontdata,specification)
12113 reportskippedtable(f,fontdata,"cff",specification.glyphs)
12114end
12115local formatreaders={}
12116local duplicatestoo=true
12117local sequence={
12118 { 3,1,4 },
12119 { 3,10,12 },
12120 { 0,3,4 },
12121 { 0,3,12 },
12122 { 0,1,4 },
12123 { 0,1,12 },
12124 { 0,0,6 },
12125 { 3,0,6 },
12126 { 3,0,4 },
12127 { 0,5,14 },
12128 { 0,4,12 },
12129 { 3,10,13 },
12130}
12131local supported={}
12132for i=1,#sequence do
12133 local si=sequence[i]
12134 local sp,se,sf=si[1],si[2],si[3]
12135 local p=supported[sp]
12136 if not p then
12137  p={}
12138  supported[sp]=p
12139 end
12140 local e=p[se]
12141 if not e then
12142  e={}
12143  p[se]=e
12144 end
12145 e[sf]=true
12146end
12147formatreaders[4]=function(f,fontdata,offset)
12148 setposition(f,offset+2)
12149 local length=readushort(f) 
12150 local language=readushort(f)
12151 local nofsegments=readushort(f)/2
12152 skipshort(f,3)
12153 local mapping=fontdata.mapping
12154 local glyphs=fontdata.glyphs
12155 local duplicates=fontdata.duplicates
12156 local nofdone=0
12157 local endchars=readcardinaltable(f,nofsegments,ushort)
12158 local reserved=readushort(f) 
12159 local startchars=readcardinaltable(f,nofsegments,ushort)
12160 local deltas=readcardinaltable(f,nofsegments,ushort)
12161 local offsets=readcardinaltable(f,nofsegments,ushort)
12162 local size=(length-2*2-5*2-4*2*nofsegments)/2
12163 local indices=readcardinaltable(f,size-1,ushort)
12164 for segment=1,nofsegments do
12165  local startchar=startchars[segment]
12166  local endchar=endchars[segment]
12167  local offset=offsets[segment]
12168  local delta=deltas[segment]
12169  if startchar==0xFFFF and endchar==0xFFFF then
12170  elseif startchar==0xFFFF and offset==0 then
12171  elseif offset==0xFFFF then
12172  elseif offset==0 then
12173   if trace_cmap_details then
12174    report("format 4.%i segment %2i from %C upto %C at index %H",1,segment,startchar,endchar,(startchar+delta)%65536)
12175   end
12176   for unicode=startchar,endchar do
12177    local index=(unicode+delta)%65536
12178    if index and index>0 then
12179     local glyph=glyphs[index]
12180     if glyph then
12181      local gu=glyph.unicode
12182      if not gu then
12183       glyph.unicode=unicode
12184       nofdone=nofdone+1
12185      elseif gu~=unicode then
12186       if duplicatestoo then
12187        local d=duplicates[gu]
12188        if d then
12189         d[unicode]=true
12190        else
12191         duplicates[gu]={ [unicode]=true }
12192        end
12193       else
12194        report("duplicate case 1: %C %04i %s",unicode,index,glyphs[index].name)
12195       end
12196      end
12197      if not mapping[index] then
12198       mapping[index]=unicode
12199      end
12200     end
12201    end
12202   end
12203  else
12204   local shift=(segment-nofsegments+offset/2)-startchar
12205   if trace_cmap_details then
12206    report_cmap("format 4.%i segment %2i from %C upto %C at index %H",0,segment,startchar,endchar,(startchar+delta)%65536)
12207   end
12208   for unicode=startchar,endchar do
12209    local slot=shift+unicode
12210    local index=indices[slot]
12211    if index and index>0 then
12212     index=(index+delta)%65536
12213     local glyph=glyphs[index]
12214     if glyph then
12215      local gu=glyph.unicode
12216      if not gu then
12217       glyph.unicode=unicode
12218       nofdone=nofdone+1
12219      elseif gu~=unicode then
12220       if duplicatestoo then
12221        local d=duplicates[gu]
12222        if d then
12223         d[unicode]=true
12224        else
12225         duplicates[gu]={ [unicode]=true }
12226        end
12227       else
12228        report("duplicate case 2: %C %04i %s",unicode,index,glyphs[index].name)
12229       end
12230      end
12231      if not mapping[index] then
12232       mapping[index]=unicode
12233      end
12234     end
12235    end
12236   end
12237  end
12238 end
12239 return nofdone
12240end
12241formatreaders[6]=function(f,fontdata,offset)
12242 setposition(f,offset) 
12243 local format=readushort(f)
12244 local length=readushort(f)
12245 local language=readushort(f)
12246 local mapping=fontdata.mapping
12247 local glyphs=fontdata.glyphs
12248 local duplicates=fontdata.duplicates
12249 local start=readushort(f)
12250 local count=readushort(f)
12251 local stop=start+count-1
12252 local nofdone=0
12253 if trace_cmap_details then
12254  report_cmap("format 6 from %C to %C",2,start,stop)
12255 end
12256 for unicode=start,stop do
12257  local index=readushort(f)
12258  if index>0 then
12259   local glyph=glyphs[index]
12260   if glyph then
12261    local gu=glyph.unicode
12262    if not gu then
12263     glyph.unicode=unicode
12264     nofdone=nofdone+1
12265    elseif gu~=unicode then
12266    end
12267    if not mapping[index] then
12268     mapping[index]=unicode
12269    end
12270   end
12271  end
12272 end
12273 return nofdone
12274end
12275formatreaders[12]=function(f,fontdata,offset)
12276 setposition(f,offset+2+2+4+4) 
12277 local mapping=fontdata.mapping
12278 local glyphs=fontdata.glyphs
12279 local duplicates=fontdata.duplicates
12280 local nofgroups=readulong(f)
12281 local nofdone=0
12282 for i=1,nofgroups do
12283  local first=readulong(f)
12284  local last=readulong(f)
12285  local index=readulong(f)
12286  if trace_cmap_details then
12287   report_cmap("format 12 from %C to %C starts at index %i",first,last,index)
12288  end
12289  for unicode=first,last do
12290   local glyph=glyphs[index]
12291   if glyph then
12292    local gu=glyph.unicode
12293    if not gu then
12294     glyph.unicode=unicode
12295     nofdone=nofdone+1
12296    elseif gu~=unicode then
12297     local d=duplicates[gu]
12298     if d then
12299      d[unicode]=true
12300     else
12301      duplicates[gu]={ [unicode]=true }
12302     end
12303    end
12304    if not mapping[index] then
12305     mapping[index]=unicode
12306    end
12307   end
12308   index=index+1
12309  end
12310 end
12311 return nofdone
12312end
12313formatreaders[13]=function(f,fontdata,offset)
12314 setposition(f,offset+2+2+4+4) 
12315 local mapping=fontdata.mapping
12316 local glyphs=fontdata.glyphs
12317 local duplicates=fontdata.duplicates
12318 local nofgroups=readulong(f)
12319 local nofdone=0
12320 for i=1,nofgroups do
12321  local first=readulong(f)
12322  local last=readulong(f)
12323  local index=readulong(f)
12324  if first<privateoffset then
12325   if trace_cmap_details then
12326    report_cmap("format 13 from %C to %C get index %i",first,last,index)
12327   end
12328   local glyph=glyphs[index]
12329   local unicode=glyph.unicode
12330   if not unicode then
12331    unicode=first
12332    glyph.unicode=unicode
12333    first=first+1
12334   end
12335   local list=duplicates[unicode]
12336   mapping[index]=unicode
12337   if not list then
12338    list={}
12339    duplicates[unicode]=list
12340   end
12341   if last>=privateoffset then
12342    local limit=privateoffset-1
12343    report("format 13 from %C to %C pruned to %C",first,last,limit)
12344    last=limit
12345   end
12346   for unicode=first,last do
12347    list[unicode]=true
12348   end
12349   nofdone=nofdone+last-first+1
12350  else
12351   report("format 13 from %C to %C ignored",first,last)
12352  end
12353 end
12354 return nofdone
12355end
12356formatreaders[14]=function(f,fontdata,offset)
12357 if offset and offset~=0 then
12358  setposition(f,offset)
12359  local format=readushort(f)
12360  local length=readulong(f)
12361  local nofrecords=readulong(f)
12362  local records={}
12363  local variants={}
12364  local nofdone=0
12365  fontdata.variants=variants
12366  for i=1,nofrecords do
12367   records[i]={
12368    selector=readuint(f),
12369    default=readulong(f),
12370    other=readulong(f),
12371   }
12372  end
12373  for i=1,nofrecords do
12374   local record=records[i]
12375   local selector=record.selector
12376   local default=record.default
12377   local other=record.other
12378   local other=record.other
12379   if other~=0 then
12380    setposition(f,offset+other)
12381    local mapping={}
12382    local count=readulong(f)
12383    for i=1,count do
12384     mapping[readuint(f)]=readushort(f)
12385    end
12386    nofdone=nofdone+count
12387    variants[selector]=mapping
12388   end
12389  end
12390  return nofdone
12391 else
12392  return 0
12393 end
12394end
12395local function checkcmap(f,fontdata,records,platform,encoding,format)
12396 local pdata=records[platform]
12397 if not pdata then
12398  if trace_cmap_details then
12399   report_cmap("skipped, %s, p=%i e=%i f=%i","no platform",platform,encoding,format)
12400  end
12401  return 0
12402 end
12403 local edata=pdata[encoding]
12404 if not edata then
12405  if trace_cmap_details then
12406   report_cmap("skipped, %s, p=%i e=%i f=%i","no encoding",platform,encoding,format)
12407  end
12408  return 0
12409 end
12410 local fdata=edata[format]
12411 if not fdata then
12412  if trace_cmap_details then
12413   report_cmap("skipped, %s, p=%i e=%i f=%i","no format",platform,encoding,format)
12414  end
12415  return 0
12416 elseif type(fdata)~="number" then
12417  if trace_cmap_details then
12418   report_cmap("skipped, %s, p=%i e=%i f=%i","already done",platform,encoding,format)
12419  end
12420  return 0
12421 end
12422 edata[format]=true 
12423 local reader=formatreaders[format]
12424 if not reader then
12425  if trace_cmap_details then
12426   report_cmap("skipped, %s, p=%i e=%i f=%i","unsupported format",platform,encoding,format)
12427  end
12428  return 0
12429 end
12430 local n=reader(f,fontdata,fdata) or 0
12431 if trace_cmap_details or trace_cmap then
12432  local p=platforms[platform]
12433  local e=encodings[p]
12434  report_cmap("checked, platform %i (%s), encoding %i (%s), format %i, new unicodes %i",
12435   platform,p,encoding,e and e[encoding] or "?",format,n)
12436 end
12437 return n
12438end
12439function readers.cmap(f,fontdata,specification)
12440 local tableoffset=gotodatatable(f,fontdata,"cmap",specification.glyphs)
12441 if tableoffset then
12442  local version=readushort(f) 
12443  local noftables=readushort(f)
12444  local records={}
12445  local unicodecid=false
12446  local variantcid=false
12447  local variants={}
12448  local duplicates=fontdata.duplicates or {}
12449  fontdata.duplicates=duplicates
12450  for i=1,noftables do
12451   local platform=readushort(f)
12452   local encoding=readushort(f)
12453   local offset=readulong(f)
12454   local record=records[platform]
12455   if not record then
12456    records[platform]={
12457     [encoding]={
12458      offsets={ offset },
12459      formats={},
12460     }
12461    }
12462   else
12463    local subtables=record[encoding]
12464    if not subtables then
12465     record[encoding]={
12466      offsets={ offset },
12467      formats={},
12468     }
12469    else
12470     local offsets=subtables.offsets
12471     offsets[#offsets+1]=offset
12472    end
12473   end
12474  end
12475  if trace_cmap then
12476   report("found cmaps:")
12477  end
12478  for platform,record in sortedhash(records) do
12479   local p=platforms[platform]
12480   local e=encodings[p]
12481   local sp=supported[platform]
12482   local ps=p or "?"
12483   if trace_cmap then
12484    if sp then
12485     report("  platform %i: %s",platform,ps)
12486    else
12487     report("  platform %i: %s (unsupported)",platform,ps)
12488    end
12489   end
12490   for encoding,subtables in sortedhash(record) do
12491    local se=sp and sp[encoding]
12492    local es=e and e[encoding] or "?"
12493    if trace_cmap then
12494     if se then
12495      report("    encoding %i: %s",encoding,es)
12496     else
12497      report("    encoding %i: %s (unsupported)",encoding,es)
12498     end
12499    end
12500    local offsets=subtables.offsets
12501    local formats=subtables.formats
12502    for i=1,#offsets do
12503     local offset=tableoffset+offsets[i]
12504     setposition(f,offset)
12505     formats[readushort(f)]=offset
12506    end
12507    record[encoding]=formats
12508    if trace_cmap then
12509     local list=sortedkeys(formats)
12510     for i=1,#list do
12511      if not (se and se[list[i]]) then
12512       list[i]=list[i].." (unsupported)"
12513      end
12514     end
12515     report("      formats: % t",list)
12516    end
12517   end
12518  end
12519  local ok=false
12520  for i=1,#sequence do
12521   local si=sequence[i]
12522   local sp,se,sf=si[1],si[2],si[3]
12523   if checkcmap(f,fontdata,records,sp,se,sf)>0 then
12524    ok=true
12525   end
12526  end
12527  if not ok then
12528   report("no useable unicode cmap found")
12529  end
12530  fontdata.cidmaps={
12531   version=version,
12532   noftables=noftables,
12533   records=records,
12534  }
12535 else
12536  fontdata.cidmaps={}
12537 end
12538end
12539function readers.loca(f,fontdata,specification)
12540 reportskippedtable(f,fontdata,"loca",specification.glyphs)
12541end
12542function readers.glyf(f,fontdata,specification) 
12543 reportskippedtable(f,fontdata,"glyf",specification.glyphs)
12544end
12545function readers.colr(f,fontdata,specification)
12546 reportskippedtable(f,fontdata,"colr",specification.glyphs)
12547end
12548function readers.cpal(f,fontdata,specification)
12549 reportskippedtable(f,fontdata,"cpal",specification.glyphs)
12550end
12551function readers.svg(f,fontdata,specification)
12552 reportskippedtable(f,fontdata,"svg",specification.glyphs)
12553end
12554function readers.sbix(f,fontdata,specification)
12555 reportskippedtable(f,fontdata,"sbix",specification.glyphs)
12556end
12557function readers.cbdt(f,fontdata,specification)
12558 reportskippedtable(f,fontdata,"cbdt",specification.glyphs)
12559end
12560function readers.cblc(f,fontdata,specification)
12561 reportskippedtable(f,fontdata,"cblc",specification.glyphs)
12562end
12563function readers.ebdt(f,fontdata,specification)
12564 reportskippedtable(f,fontdata,"ebdt",specification.glyphs)
12565end
12566function readers.ebsc(f,fontdata,specification)
12567 reportskippedtable(f,fontdata,"ebsc",specification.glyphs)
12568end
12569function readers.eblc(f,fontdata,specification)
12570 reportskippedtable(f,fontdata,"eblc",specification.glyphs)
12571end
12572function readers.kern(f,fontdata,specification)
12573 local tableoffset=gotodatatable(f,fontdata,"kern",specification.kerns)
12574 if tableoffset then
12575  local version=readushort(f)
12576  local noftables=readushort(f)
12577  for i=1,noftables do
12578   local version=readushort(f)
12579   local length=readushort(f)
12580   local coverage=readushort(f)
12581   local format=rshift(coverage,8) 
12582   if format==0 then
12583    local nofpairs=readushort(f)
12584    local searchrange=readushort(f)
12585    local entryselector=readushort(f)
12586    local rangeshift=readushort(f)
12587    local kerns={}
12588    local glyphs=fontdata.glyphs
12589    for i=1,nofpairs do
12590     local left=readushort(f)
12591     local right=readushort(f)
12592     local kern=readfword(f)
12593     local glyph=glyphs[left]
12594     local kerns=glyph.kerns
12595     if kerns then
12596      kerns[right]=kern
12597     else
12598      glyph.kerns={ [right]=kern }
12599     end
12600    end
12601   elseif format==2 then
12602    report("todo: kern classes")
12603   else
12604    report("todo: kerns")
12605   end
12606  end
12607 end
12608end
12609function readers.gdef(f,fontdata,specification)
12610 reportskippedtable(f,fontdata,"gdef",specification.details)
12611end
12612function readers.gsub(f,fontdata,specification)
12613 reportskippedtable(f,fontdata,"gsub",specification.details)
12614end
12615function readers.gpos(f,fontdata,specification)
12616 reportskippedtable(f,fontdata,"gpos",specification.details)
12617end
12618function readers.math(f,fontdata,specification)
12619 reportskippedtable(f,fontdata,"math",specification.details)
12620end
12621local function getinfo(maindata,sub,platformnames,rawfamilynames,metricstoo,instancenames)
12622 local fontdata=sub and maindata.subfonts and maindata.subfonts[sub] or maindata
12623 local names=fontdata.names
12624 local info=nil
12625 if names then
12626  local metrics=fontdata.windowsmetrics or {}
12627  local postscript=fontdata.postscript  or {}
12628  local fontheader=fontdata.fontheader  or {}
12629  local cffinfo=fontdata.cffinfo  or {}
12630  local verticalheader=fontdata.verticalheader or {}
12631  local filename=fontdata.filename
12632  local weight=getname(fontdata,"weight") or (cffinfo and cffinfo.weight) or (metrics and metrics.weight)
12633  local width=getname(fontdata,"width")  or (cffinfo and cffinfo.width ) or (metrics and metrics.width )
12634  local fontname=getname(fontdata,"postscriptname")
12635  local fullname=getname(fontdata,"fullname")
12636  local family=getname(fontdata,"family")
12637  local subfamily=getname(fontdata,"subfamily")
12638  local familyname=getname(fontdata,"typographicfamily")
12639  local subfamilyname=getname(fontdata,"typographicsubfamily")
12640  local compatiblename=getname(fontdata,"compatiblefullname") 
12641  if rawfamilynames then
12642  else
12643   if not familyname then familyname=family end
12644   if not subfamilyname then subfamilyname=subfamily end
12645  end
12646  if platformnames then
12647   platformnames=fontdata.platformnames
12648  end
12649  if instancenames then
12650   local variabledata=fontdata.variabledata
12651   if variabledata then
12652    local instances=variabledata and variabledata.instances
12653    if instances then
12654     instancenames={}
12655     for i=1,#instances do
12656      instancenames[i]=lower(stripstring(instances[i].subfamily))
12657     end
12658    else
12659     instancenames=nil
12660    end
12661   else
12662    instancenames=nil
12663   end
12664  end
12665  info={ 
12666   subfontindex=fontdata.subfontindex or sub or 0,
12667   version=getname(fontdata,"version"),
12668   fontname=fontname,
12669   fullname=fullname,
12670   family=family,
12671   subfamily=subfamily,
12672   familyname=familyname,
12673   subfamilyname=subfamilyname,
12674   compatiblename=compatiblename,
12675   weight=weight and lower(weight),
12676   width=width and lower(width),
12677   pfmweight=metrics.weightclass or 400,
12678   pfmwidth=metrics.widthclass or 5,
12679   panosewidth=metrics.panosewidth,
12680   panoseweight=metrics.panoseweight,
12681   fstype=metrics.fstype or 0,
12682   italicangle=postscript.italicangle or 0,
12683   units=fontheader.units or 0,
12684   designsize=fontdata.designsize,
12685   minsize=fontdata.minsize,
12686   maxsize=fontdata.maxsize,
12687   boundingbox=fontheader and { fontheader.xmin or 0,fontheader.ymin or 0,fontheader.xmax or 0,fontheader.ymax or 0 } or nil,
12688   monospaced=(tonumber(postscript.monospaced or 0)>0) or metrics.panosewidth=="monospaced",
12689   averagewidth=metrics.averagewidth,
12690   xheight=metrics.xheight,
12691   capheight=metrics.capheight or fontdata.maxy,
12692   ascender=metrics.typoascender,
12693   descender=metrics.typodescender,
12694   ascent=metrics.winascent,
12695   descent=metrics.windescent,
12696   platformnames=platformnames or nil,
12697   instancenames=instancenames or nil,
12698   tableoffsets=fontdata.tableoffsets,
12699   defaultvheight=(verticalheader.ascender or 0)-(verticalheader.descender or 0)
12700  }
12701  if metricstoo then
12702   local keys={
12703    "version",
12704    "ascender","descender","linegap",
12705    "maxadvancewidth","maxadvanceheight","maxextent",
12706    "minbottomsidebearing","mintopsidebearing",
12707   }
12708   local h=fontdata.horizontalheader or {}
12709   local v=fontdata.verticalheader   or {}
12710   if h then
12711    local th={}
12712    local tv={}
12713    for i=1,#keys do
12714     local key=keys[i]
12715     th[key]=h[key] or 0
12716     tv[key]=v[key] or 0
12717    end
12718    info.horizontalmetrics=th
12719    info.verticalmetrics=tv
12720   end
12721  end
12722 elseif n then
12723  info={
12724   filename=fontdata.filename,
12725   comment="there is no info for subfont "..n,
12726  }
12727 else
12728  info={
12729   filename=fontdata.filename,
12730   comment="there is no info",
12731  }
12732 end
12733 return info
12734end
12735local function loadtables(f,specification,offset)
12736 if offset then
12737  setposition(f,offset)
12738 end
12739 local tables={}
12740 local basename=file.basename(specification.filename)
12741 local filesize=specification.filesize
12742 local filetime=specification.filetime
12743 local fontdata={ 
12744  filename=basename,
12745  filesize=filesize,
12746  filetime=filetime,
12747  version=readstring(f,4),
12748  noftables=readushort(f),
12749  searchrange=readushort(f),
12750  entryselector=readushort(f),
12751  rangeshift=readushort(f),
12752  tables=tables,
12753  foundtables=false,
12754 }
12755 for i=1,fontdata.noftables do
12756  local tag=lower(stripstring(readstring(f,4)))
12757  local checksum=readushort(f)*0x10000+readushort(f)
12758  local offset=readulong(f)
12759  local length=readulong(f)
12760  if offset+length>filesize then
12761   report("bad %a table in file %a",tag,basename)
12762  end
12763  tables[tag]={
12764   checksum=checksum,
12765   offset=offset,
12766   length=length,
12767  }
12768 end
12769 fontdata.foundtables=sortedkeys(tables)
12770 if tables.cff or tables.cff2 then
12771  fontdata.format="opentype"
12772 else
12773  fontdata.format="truetype"
12774 end
12775 return fontdata,tables
12776end
12777local function prepareglyps(fontdata)
12778 local glyphs=setmetatableindex(function(t,k)
12779  local v={
12780   index=k,
12781  }
12782  t[k]=v
12783  return v
12784 end)
12785 fontdata.glyphs=glyphs
12786 fontdata.mapping={}
12787end
12788local function readtable(tag,f,fontdata,specification,...)
12789 local reader=readers[tag]
12790 if reader then
12791  reader(f,fontdata,specification,...)
12792 end
12793end
12794local function readdata(f,offset,specification)
12795 local fontdata,tables=loadtables(f,specification,offset)
12796 if specification.glyphs then
12797  prepareglyps(fontdata)
12798 end
12799 fontdata.temporary={}
12800 readtable("name",f,fontdata,specification)
12801 local askedname=specification.askedname
12802 if askedname then
12803  local fullname=getname(fontdata,"fullname") or ""
12804  local cleanname=gsub(askedname,"[^a-zA-Z0-9]","")
12805  local foundname=gsub(fullname,"[^a-zA-Z0-9]","")
12806  if lower(cleanname)~=lower(foundname) then
12807   return 
12808  end
12809 end
12810 readtable("stat",f,fontdata,specification)
12811 readtable("avar",f,fontdata,specification)
12812 readtable("fvar",f,fontdata,specification)
12813 local variabledata=fontdata.variabledata
12814 if variabledata then
12815  local instances=variabledata.instances
12816  local axis=variabledata.axis
12817  if axis and (not instances or #instances==0) then
12818   instances={}
12819   variabledata.instances=instances
12820   local function add(n,subfamily,value)
12821    local values={}
12822    for i=1,#axis do
12823     local a=axis[i]
12824     values[i]={
12825      axis=a.tag,
12826      value=i==n and value or a.default,
12827     }
12828    end
12829    instances[#instances+1]={
12830     subfamily=subfamily,
12831     values=values,
12832    }
12833   end
12834   for i=1,#axis do
12835    local a=axis[i]
12836    local tag=a.tag
12837    add(i,"default"..tag,a.default)
12838    add(i,"minimum"..tag,a.minimum)
12839    add(i,"maximum"..tag,a.maximum)
12840   end
12841  end
12842 end
12843 if not specification.factors then
12844  local instance=specification.instance
12845  if type(instance)=="string" then
12846   local factors=helpers.getfactors(fontdata,instance)
12847   if factors then
12848    specification.factors=factors
12849    fontdata.factors=factors
12850    fontdata.instance=instance
12851    report("user instance: %s, factors: % t",instance,factors)
12852   else
12853    report("user instance: %s, bad factors",instance)
12854   end
12855  end
12856 end
12857 if not fontdata.factors then
12858  if fontdata.variabledata then
12859   local factors=helpers.getfactors(fontdata,true)
12860   if factors then
12861    specification.factors=factors
12862    fontdata.factors=factors
12863   end
12864  else
12865  end
12866 end
12867 readtable("os/2",f,fontdata,specification)
12868 readtable("head",f,fontdata,specification)
12869 readtable("maxp",f,fontdata,specification)
12870 readtable("hhea",f,fontdata,specification)
12871 readtable("vhea",f,fontdata,specification)
12872 readtable("hmtx",f,fontdata,specification)
12873 readtable("vmtx",f,fontdata,specification)
12874 readtable("vorg",f,fontdata,specification)
12875 readtable("post",f,fontdata,specification)
12876 readtable("mvar",f,fontdata,specification)
12877 readtable("hvar",f,fontdata,specification)
12878 readtable("vvar",f,fontdata,specification)
12879 readtable("gdef",f,fontdata,specification)
12880 readtable("cff",f,fontdata,specification)
12881 readtable("cff2",f,fontdata,specification)
12882 readtable("cmap",f,fontdata,specification)
12883 readtable("loca",f,fontdata,specification) 
12884 readtable("glyf",f,fontdata,specification) 
12885 readtable("colr",f,fontdata,specification)
12886 readtable("cpal",f,fontdata,specification)
12887 readtable("svg",f,fontdata,specification)
12888 readtable("sbix",f,fontdata,specification)
12889 readtable("cbdt",f,fontdata,specification)
12890 readtable("cblc",f,fontdata,specification)
12891 readtable("ebdt",f,fontdata,specification)
12892 readtable("eblc",f,fontdata,specification)
12893 readtable("kern",f,fontdata,specification)
12894 readtable("gsub",f,fontdata,specification)
12895 readtable("gpos",f,fontdata,specification)
12896 readtable("math",f,fontdata,specification)
12897 fontdata.locations=nil
12898 fontdata.cidmaps=nil
12899 fontdata.dictionaries=nil
12900 if specification.tableoffsets then
12901  fontdata.tableoffsets=tables
12902  setmetatableindex(tables,{
12903   version=fontdata.version,
12904   noftables=fontdata.noftables,
12905   searchrange=fontdata.searchrange,
12906   entryselector=fontdata.entryselector,
12907   rangeshift=fontdata.rangeshift,
12908  })
12909 end
12910 return fontdata
12911end
12912local function loadfontdata(specification)
12913 local filename=specification.filename
12914 local fileattr=lfs.attributes(filename)
12915 local filesize=fileattr and fileattr.size or 0
12916 local filetime=fileattr and fileattr.modification or 0
12917 local f=openfile(filename,true) 
12918 if not f then
12919  report("unable to open %a",filename)
12920 elseif filesize==0 then
12921  report("empty file %a",filename)
12922  closefile(f)
12923 else
12924  specification.filesize=filesize
12925  specification.filetime=filetime
12926  local version=readstring(f,4)
12927  local fontdata=nil
12928  if version=="OTTO" or version=="true" or version=="\0\1\0\0" then
12929   fontdata=readdata(f,0,specification)
12930  elseif version=="ttcf" then
12931   local subfont=tonumber(specification.subfont)
12932   local ttcversion=readulong(f)
12933   local nofsubfonts=readulong(f)
12934   local offsets=readcardinaltable(f,nofsubfonts,ulong)
12935   if subfont then 
12936    if subfont>=1 and subfont<=nofsubfonts then
12937     fontdata=readdata(f,offsets[subfont],specification)
12938    else
12939     report("no subfont %a in file %a",subfont,filename)
12940    end
12941   else
12942    subfont=specification.subfont
12943    if type(subfont)=="string" and subfont~="" then
12944     specification.askedname=subfont
12945     for i=1,nofsubfonts do
12946      fontdata=readdata(f,offsets[i],specification)
12947      if fontdata then
12948       fontdata.subfontindex=i
12949       report("subfont named %a has index %a",subfont,i)
12950       break
12951      end
12952     end
12953     if not fontdata then
12954      report("no subfont named %a",subfont)
12955     end
12956    else
12957     local subfonts={}
12958     fontdata={
12959      filename=filename,
12960      filesize=filesize,
12961      filetime=filetime,
12962      version=version,
12963      subfonts=subfonts,
12964      ttcversion=ttcversion,
12965      nofsubfonts=nofsubfonts,
12966     }
12967     for i=1,nofsubfonts do
12968      subfonts[i]=readdata(f,offsets[i],specification)
12969     end
12970    end
12971   end
12972  else
12973   report("unknown version %a in file %a",version,filename)
12974  end
12975  closefile(f)
12976  return fontdata or {}
12977 end
12978end
12979local function loadfont(specification,n,instance)
12980 if type(specification)=="string" then
12981  specification={
12982   filename=specification,
12983   info=true,
12984   details=true,
12985   glyphs=true,
12986   shapes=true,
12987   kerns=true,
12988   variable=true,
12989   globalkerns=true,
12990   lookups=true,
12991   subfont=n or true,
12992   tounicode=false,
12993   instance=instance
12994  }
12995 end
12996 if specification.shapes or specification.lookups or specification.kerns then
12997  specification.glyphs=true
12998 end
12999 if specification.glyphs then
13000  specification.details=true
13001 end
13002 if specification.details then
13003  specification.info=true 
13004 end
13005 if specification.platformnames then
13006  specification.platformnames=true 
13007 end
13008 if specification.instance or instance then
13009  specification.variable=true
13010  specification.instance=specification.instance or instance
13011 end
13012 local function message(str)
13013  report("fatal error in file %a: %s\n%s",specification.filename,str,debug and debug.traceback())
13014 end
13015 local ok,result=xpcall(loadfontdata,message,specification)
13016 if ok then
13017  return result
13018 end
13019end
13020function readers.loadshapes(filename,n,instance,streams)
13021 local fontdata=loadfont {
13022  filename=filename,
13023  shapes=true,
13024  streams=streams,
13025  variable=true,
13026  subfont=n,
13027  instance=instance,
13028 }
13029 if fontdata then
13030  for k,v in next,fontdata.glyphs do
13031   v.class=nil
13032   v.index=nil
13033   v.math=nil
13034  end
13035  local names=fontdata.names
13036  if names then
13037   for k,v in next,names do
13038    names[k]=fullstrip(v.content)
13039   end
13040  end
13041 end
13042 return fontdata and {
13043  filename=filename,
13044  format=fontdata.format,
13045  glyphs=fontdata.glyphs,
13046  units=fontdata.fontheader.units,
13047  cffinfo=fontdata.cffinfo,
13048  fontheader=fontdata.fontheader,
13049  horizontalheader=fontdata.horizontalheader,
13050  verticalheader=fontdata.verticalheader,
13051  maximumprofile=fontdata.maximumprofile,
13052  names=fontdata.names,
13053  postscript=fontdata.postscript,
13054 } or {
13055  filename=filename,
13056  format="unknown",
13057  glyphs={},
13058  units=0,
13059 }
13060end
13061function readers.loadfont(filename,n,instance)
13062 local fontdata=loadfont {
13063  filename=filename,
13064  glyphs=true,
13065  shapes=false,
13066  lookups=true,
13067  variable=true,
13068  subfont=n,
13069  instance=instance,
13070 }
13071 if fontdata then
13072  return {
13073   tableversion=tableversion,
13074   creator="context mkiv",
13075   size=fontdata.filesize,
13076   time=fontdata.filetime,
13077   glyphs=fontdata.glyphs,
13078   descriptions=fontdata.descriptions,
13079   format=fontdata.format,
13080   goodies={},
13081   metadata=getinfo(fontdata,n,false,false,true,true),
13082   properties={
13083    hasitalics=fontdata.hasitalics or false,
13084    maxcolorclass=fontdata.maxcolorclass,
13085    hascolor=fontdata.hascolor or false,
13086    instance=fontdata.instance,
13087    factors=fontdata.factors,
13088    nofsubfonts=fontdata.subfonts and #fontdata.subfonts or nil,
13089   },
13090   resources={
13091    filename=filename,
13092    private=privateoffset,
13093    duplicates=fontdata.duplicates  or {},
13094    features=fontdata.features or {},
13095    sublookups=fontdata.sublookups  or {},
13096    marks=fontdata.marks    or {},
13097    markclasses=fontdata.markclasses or {},
13098    marksets=fontdata.marksets or {},
13099    sequences=fontdata.sequences   or {},
13100    variants=fontdata.variants,
13101    version=getname(fontdata,"version"),
13102    cidinfo=fontdata.cidinfo,
13103    mathconstants=fontdata.mathconstants,
13104    colorpalettes=fontdata.colorpalettes,
13105    colorpaintdata=fontdata.colorpaintdata,
13106    colorpaintlist=fontdata.colorpaintlist,
13107    colorlinesdata=fontdata.colorlinesdata,
13108    coloraffinedata=fontdata.coloraffinedata,
13109    svgshapes=fontdata.svgshapes,
13110    pngshapes=fontdata.pngshapes,
13111    variabledata=fontdata.variabledata,
13112    foundtables=fontdata.foundtables,
13113   },
13114  }
13115 end
13116end
13117function readers.getinfo(filename,specification)
13118 local subfont=nil
13119 local platformnames=false
13120 local rawfamilynames=false
13121 local instancenames=true
13122 local tableoffsets=false
13123 if type(specification)=="table" then
13124  subfont=tonumber(specification.subfont)
13125  platformnames=specification.platformnames
13126  rawfamilynames=specification.rawfamilynames
13127  tableoffsets=specification.tableoffsets
13128 else
13129  subfont=tonumber(specification)
13130 end
13131 local fontdata=loadfont {
13132  filename=filename,
13133  details=true,
13134  platformnames=platformnames,
13135  instancenames=true,
13136  tableoffsets=tableoffsets,
13137 }
13138 if fontdata then
13139  local subfonts=fontdata.subfonts
13140  if not subfonts then
13141   return getinfo(fontdata,nil,platformnames,rawfamilynames,false,instancenames)
13142  elseif not subfont then
13143   local info={}
13144   for i=1,#subfonts do
13145    info[i]=getinfo(fontdata,i,platformnames,rawfamilynames,false,instancenames)
13146   end
13147   return info
13148  elseif subfont>=1 and subfont<=#subfonts then
13149   return getinfo(fontdata,subfont,platformnames,rawfamilynames,false,instancenames)
13150  else
13151   return {
13152    filename=filename,
13153    comment="there is no subfont "..subfont.." in this file"
13154   }
13155  end
13156 else
13157  return {
13158   filename=filename,
13159   comment="the file cannot be opened for reading",
13160  }
13161 end
13162end
13163function readers.rehash() 
13164 report("the %a helper is not yet implemented","rehash")
13165end
13166function readers.checkhash() 
13167 report("the %a helper is not yet implemented","checkhash")
13168end
13169function readers.pack() 
13170 report("the %a helper is not yet implemented","pack")
13171end
13172function readers.unpack(fontdata)
13173 report("the %a helper is not yet implemented","unpack")
13174end
13175function readers.expand(fontdata)
13176 report("the %a helper is not yet implemented","unpack")
13177end
13178function readers.compact(fontdata)
13179 report("the %a helper is not yet implemented","compact")
13180end
13181function readers.condense(fontdata)
13182 report("the %a helper is not yet implemented","condense")
13183end
13184local extenders={}
13185function readers.registerextender(extender)
13186 extenders[#extenders+1]=extender
13187end
13188function readers.extend(fontdata)
13189 for i=1,#extenders do
13190  local extender=extenders[i]
13191  local name=extender.name or "unknown"
13192  local action=extender.action
13193  if action then
13194   action(fontdata)
13195  end
13196 end
13197end
13198
13199end -- closure
13200
13201do -- begin closure to overcome local limits and interference
13202
13203if not modules then modules={} end modules ['font-cff']={
13204 version=1.001,
13205 optimize=true,
13206 comment="companion to font-ini.mkiv",
13207 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
13208 copyright="PRAGMA ADE / ConTeXt Development Team",
13209 license="see context related readme files"
13210}
13211local next,type,tonumber,rawget=next,type,tonumber,rawget
13212local byte,char,gmatch,sub=string.byte,string.char,string.gmatch,string.sub
13213local concat,insert,remove,unpack=table.concat,table.insert,table.remove,table.unpack
13214local floor,abs,round,ceil,min,max=math.floor,math.abs,math.round,math.ceil,math.min,math.max
13215local P,C,R,S,C,Cs,Ct=lpeg.P,lpeg.C,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Ct
13216local lpegmatch=lpeg.match
13217local formatters=string.formatters
13218local bytetable=string.bytetable
13219local idiv=number.idiv
13220local rshift,band,extract=bit32.rshift,bit32.band,bit32.extract
13221local readers=fonts.handlers.otf.readers
13222local streamreader=readers.streamreader
13223local readstring=streamreader.readstring
13224local readbyte=streamreader.readcardinal1  
13225local readushort=streamreader.readcardinal2  
13226local readuint=streamreader.readcardinal3  
13227local readulong=streamreader.readcardinal4  
13228local setposition=streamreader.setposition
13229local getposition=streamreader.getposition
13230local readbytetable=streamreader.readbytetable
13231directives.register("fonts.streamreader",function()
13232 streamreader=utilities.streams
13233 readstring=streamreader.readstring
13234 readbyte=streamreader.readcardinal1
13235 readushort=streamreader.readcardinal2
13236 readuint=streamreader.readcardinal3
13237 readulong=streamreader.readcardinal4
13238 setposition=streamreader.setposition
13239 getposition=streamreader.getposition
13240 readbytetable=streamreader.readbytetable
13241end)
13242local setmetatableindex=table.setmetatableindex
13243local trace_charstrings=false trackers.register("fonts.cff.charstrings",function(v) trace_charstrings=v end)
13244local report=logs.reporter("otf reader","cff")
13245local parsedictionaries
13246local parsecharstring
13247local parsecharstrings
13248local resetcharstrings
13249local parseprivates
13250local startparsing
13251local stopparsing
13252local defaultstrings={ [0]=
13253 ".notdef","space","exclam","quotedbl","numbersign","dollar","percent",
13254 "ampersand","quoteright","parenleft","parenright","asterisk","plus",
13255 "comma","hyphen","period","slash","zero","one","two","three","four",
13256 "five","six","seven","eight","nine","colon","semicolon","less",
13257 "equal","greater","question","at","A","B","C","D","E","F","G","H",
13258 "I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W",
13259 "X","Y","Z","bracketleft","backslash","bracketright","asciicircum",
13260 "underscore","quoteleft","a","b","c","d","e","f","g","h","i","j",
13261 "k","l","m","n","o","p","q","r","s","t","u","v","w","x","y",
13262 "z","braceleft","bar","braceright","asciitilde","exclamdown","cent",
13263 "sterling","fraction","yen","florin","section","currency",
13264 "quotesingle","quotedblleft","guillemotleft","guilsinglleft",
13265 "guilsinglright","fi","fl","endash","dagger","daggerdbl",
13266 "periodcentered","paragraph","bullet","quotesinglbase","quotedblbase",
13267 "quotedblright","guillemotright","ellipsis","perthousand","questiondown",
13268 "grave","acute","circumflex","tilde","macron","breve","dotaccent",
13269 "dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash",
13270 "AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae",
13271 "dotlessi","lslash","oslash","oe","germandbls","onesuperior",
13272 "logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn",
13273 "onequarter","divide","brokenbar","degree","thorn","threequarters",
13274 "twosuperior","registered","minus","eth","multiply","threesuperior",
13275 "copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring",
13276 "Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave",
13277 "Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute",
13278 "Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute",
13279 "Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron",
13280 "aacute","acircumflex","adieresis","agrave","aring","atilde",
13281 "ccedilla","eacute","ecircumflex","edieresis","egrave","iacute",
13282 "icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex",
13283 "odieresis","ograve","otilde","scaron","uacute","ucircumflex",
13284 "udieresis","ugrave","yacute","ydieresis","zcaron","exclamsmall",
13285 "Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall",
13286 "Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader",
13287 "onedotenleader","zerooldstyle","oneoldstyle","twooldstyle",
13288 "threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle",
13289 "sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior",
13290 "threequartersemdash","periodsuperior","questionsmall","asuperior",
13291 "bsuperior","centsuperior","dsuperior","esuperior","isuperior",
13292 "lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior",
13293 "tsuperior","ff","ffi","ffl","parenleftinferior","parenrightinferior",
13294 "Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall",
13295 "Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall",
13296 "Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall",
13297 "Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall",
13298 "Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah",
13299 "Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall",
13300 "Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall",
13301 "Dotaccentsmall","Macronsmall","figuredash","hypheninferior",
13302 "Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall","oneeighth",
13303 "threeeighths","fiveeighths","seveneighths","onethird","twothirds",
13304 "zerosuperior","foursuperior","fivesuperior","sixsuperior",
13305 "sevensuperior","eightsuperior","ninesuperior","zeroinferior",
13306 "oneinferior","twoinferior","threeinferior","fourinferior",
13307 "fiveinferior","sixinferior","seveninferior","eightinferior",
13308 "nineinferior","centinferior","dollarinferior","periodinferior",
13309 "commainferior","Agravesmall","Aacutesmall","Acircumflexsmall",
13310 "Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall",
13311 "Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall",
13312 "Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall",
13313 "Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall",
13314 "Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall",
13315 "Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall",
13316 "Thornsmall","Ydieresissmall","001.000","001.001","001.002","001.003",
13317 "Black","Bold","Book","Light","Medium","Regular","Roman","Semibold",
13318}
13319local standardnames={ [0]=
13320 false,false,false,false,false,false,false,false,false,false,false,
13321 false,false,false,false,false,false,false,false,false,false,false,
13322 false,false,false,false,false,false,false,false,false,false,
13323 "space","exclam","quotedbl","numbersign","dollar","percent",
13324 "ampersand","quoteright","parenleft","parenright","asterisk","plus",
13325 "comma","hyphen","period","slash","zero","one","two","three","four",
13326 "five","six","seven","eight","nine","colon","semicolon","less",
13327 "equal","greater","question","at","A","B","C","D","E","F","G","H",
13328 "I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W",
13329 "X","Y","Z","bracketleft","backslash","bracketright","asciicircum",
13330 "underscore","quoteleft","a","b","c","d","e","f","g","h","i","j",
13331 "k","l","m","n","o","p","q","r","s","t","u","v","w","x","y",
13332 "z","braceleft","bar","braceright","asciitilde",false,false,false,
13333 false,false,false,false,false,false,false,false,false,false,false,
13334 false,false,false,false,false,false,false,false,false,false,false,
13335 false,false,false,false,false,false,false,false,false,"exclamdown",
13336 "cent","sterling","fraction","yen","florin","section","currency",
13337 "quotesingle","quotedblleft","guillemotleft","guilsinglleft",
13338 "guilsinglright","fi","fl",false,"endash","dagger","daggerdbl",
13339 "periodcentered",false,"paragraph","bullet","quotesinglbase",
13340 "quotedblbase","quotedblright","guillemotright","ellipsis","perthousand",
13341 false,"questiondown",false,"grave","acute","circumflex","tilde",
13342 "macron","breve","dotaccent","dieresis",false,"ring","cedilla",false,
13343 "hungarumlaut","ogonek","caron","emdash",false,false,false,false,
13344 false,false,false,false,false,false,false,false,false,false,false,
13345 false,"AE",false,"ordfeminine",false,false,false,false,"Lslash",
13346 "Oslash","OE","ordmasculine",false,false,false,false,false,"ae",
13347 false,false,false,"dotlessi",false,false,"lslash","oslash","oe",
13348 "germandbls",false,false,false,false
13349}
13350local cffreaders={
13351 readbyte,
13352 readushort,
13353 readuint,
13354 readulong,
13355}
13356directives.register("fonts.streamreader",function()
13357 cffreaders={
13358  readbyte,
13359  readushort,
13360  readuint,
13361  readulong,
13362 }
13363end)
13364local function readheader(f)
13365 local offset=getposition(f)
13366 local major=readbyte(f)
13367 local header={
13368  offset=offset,
13369  major=major,
13370  minor=readbyte(f),
13371  size=readbyte(f),
13372 }
13373 if major==1 then
13374  header.dsize=readbyte(f)   
13375 elseif major==2 then
13376  header.dsize=readushort(f) 
13377 else
13378 end
13379 setposition(f,offset+header.size)
13380 return header
13381end
13382local function readlengths(f,longcount)
13383 local count=longcount and readulong(f) or readushort(f)
13384 if count==0 then
13385  return {}
13386 end
13387 local osize=readbyte(f)
13388 local read=cffreaders[osize]
13389 if not read then
13390  report("bad offset size: %i",osize)
13391  return {}
13392 end
13393 local lengths={}
13394 local previous=read(f)
13395 for i=1,count do
13396  local offset=read(f)
13397  local length=offset-previous
13398  if length<0 then
13399   report("bad offset: %i",length)
13400   length=0
13401  end
13402  lengths[i]=length
13403  previous=offset
13404 end
13405 return lengths
13406end
13407local function readfontnames(f)
13408 local names=readlengths(f)
13409 for i=1,#names do
13410  names[i]=readstring(f,names[i])
13411 end
13412 return names
13413end
13414local function readtopdictionaries(f)
13415 local dictionaries=readlengths(f)
13416 for i=1,#dictionaries do
13417  dictionaries[i]=readstring(f,dictionaries[i])
13418 end
13419 return dictionaries
13420end
13421local function readstrings(f)
13422 local lengths=readlengths(f)
13423 local strings=setmetatableindex({},defaultstrings)
13424 local index=#defaultstrings
13425 for i=1,#lengths do
13426  index=index+1
13427  strings[index]=readstring(f,lengths[i])
13428 end
13429 return strings
13430end
13431do
13432 local stack={}
13433 local top=0
13434 local result={}
13435 local strings={}
13436 local p_single=P("\00")/function()
13437   result.version=strings[stack[top]] or "unset"
13438   top=0
13439  end+P("\01")/function()
13440   result.notice=strings[stack[top]] or "unset"
13441   top=0
13442  end+P("\02")/function()
13443   result.fullname=strings[stack[top]] or "unset"
13444   top=0
13445  end+P("\03")/function()
13446   result.familyname=strings[stack[top]] or "unset"
13447   top=0
13448  end+P("\04")/function()
13449   result.weight=strings[stack[top]] or "unset"
13450   top=0
13451  end+P("\05")/function()
13452   result.fontbbox={ unpack(stack,1,4) }
13453   top=0
13454  end+P("\06")/function()
13455   result.bluevalues={ unpack(stack,1,top) }
13456   top=0
13457  end+P("\07")/function()
13458   result.otherblues={ unpack(stack,1,top) }
13459   top=0
13460  end+P("\08")/function()
13461   result.familyblues={ unpack(stack,1,top) }
13462   top=0
13463  end+P("\09")/function()
13464   result.familyotherblues={ unpack(stack,1,top) }
13465   top=0
13466  end+P("\10")/function()
13467   result.stdhw=stack[top]
13468   top=0
13469  end+P("\11")/function()
13470   result.stdvw=stack[top]
13471   top=0
13472  end+P("\13")/function()
13473   result.uniqueid=stack[top]
13474   top=0
13475  end+P("\14")/function()
13476   result.xuid=concat(stack,"",1,top)
13477   top=0
13478  end+P("\15")/function()
13479   result.charset=stack[top]
13480   top=0
13481  end+P("\16")/function()
13482   result.encoding=stack[top]
13483   top=0
13484  end+P("\17")/function() 
13485   result.charstrings=stack[top]
13486   top=0
13487  end+P("\18")/function()
13488   result.private={
13489    size=stack[top-1],
13490    offset=stack[top],
13491   }
13492   top=0
13493  end+P("\19")/function()
13494   result.subroutines=stack[top]
13495   top=0 
13496  end+P("\20")/function()
13497   result.defaultwidthx=stack[top]
13498   top=0 
13499  end+P("\21")/function()
13500   result.nominalwidthx=stack[top]
13501   top=0 
13502  end
13503+P("\24")/function() 
13504   result.vstore=stack[top]
13505   top=0
13506  end+P("\25")/function() 
13507   result.maxstack=stack[top]
13508   top=0
13509  end
13510 local p_double=P("\12")*(
13511  P("\00")/function()
13512   result.copyright=stack[top]
13513   top=0
13514  end+P("\01")/function()
13515   result.monospaced=stack[top]==1 and true or false 
13516   top=0
13517  end+P("\02")/function()
13518   result.italicangle=stack[top]
13519   top=0
13520  end+P("\03")/function()
13521   result.underlineposition=stack[top]
13522   top=0
13523  end+P("\04")/function()
13524   result.underlinethickness=stack[top]
13525   top=0
13526  end+P("\05")/function()
13527   result.painttype=stack[top]
13528   top=0
13529  end+P("\06")/function()
13530   result.charstringtype=stack[top]
13531   top=0
13532  end+P("\07")/function() 
13533   result.fontmatrix={ unpack(stack,1,6) }
13534   top=0
13535  end+P("\08")/function()
13536   result.strokewidth=stack[top]
13537   top=0
13538  end+P("\09")/function()
13539   result.bluescale=stack[top]
13540   top=0
13541  end+P("\10")/function()
13542   result.blueshift=stack[top]
13543   top=0
13544  end+P("\11")/function()
13545   result.bluefuzz=stack[top]
13546   top=0
13547  end+P("\12")/function()
13548   result.stemsnaph={ unpack(stack,1,top) }
13549   top=0
13550  end+P("\13")/function()
13551   result.stemsnapv={ unpack(stack,1,top) }
13552   top=0
13553  end+P("\20")/function()
13554   result.syntheticbase=stack[top]
13555   top=0
13556  end+P("\21")/function()
13557   result.postscript=strings[stack[top]] or "unset"
13558   top=0
13559  end+P("\22")/function()
13560   result.basefontname=strings[stack[top]] or "unset"
13561   top=0
13562  end+P("\21")/function()
13563   result.basefontblend=stack[top]
13564   top=0
13565  end+P("\30")/function()
13566   result.cid.registry=strings[stack[top-2]] or "unset"
13567   result.cid.ordering=strings[stack[top-1]] or "unset"
13568   result.cid.supplement=stack[top]
13569   top=0
13570  end+P("\31")/function()
13571   result.cid.fontversion=stack[top]
13572   top=0
13573  end+P("\32")/function()
13574   result.cid.fontrevision=stack[top]
13575   top=0
13576  end+P("\33")/function()
13577   result.cid.fonttype=stack[top]
13578   top=0
13579  end+P("\34")/function()
13580   result.cid.count=stack[top]
13581   top=0
13582  end+P("\35")/function()
13583   result.cid.uidbase=stack[top]
13584   top=0
13585  end+P("\36")/function() 
13586   result.cid.fdarray=stack[top]
13587   top=0
13588  end+P("\37")/function() 
13589   result.cid.fdselect=stack[top]
13590   top=0
13591  end+P("\38")/function()
13592   result.cid.fontname=strings[stack[top]] or "unset"
13593   top=0
13594  end
13595 )
13596 local remap_1={
13597  ["\x00"]="00",["\x01"]="01",["\x02"]="02",["\x03"]="03",["\x04"]="04",["\x05"]="05",["\x06"]="06",["\x07"]="07",["\x08"]="08",["\x09"]="09",["\x0A"]="0.",["\x0B"]="0E",["\x0C"]="0E-",["\x0D"]="0",["\x0E"]="0-",["\x0F"]="0",
13598  ["\x10"]="10",["\x11"]="11",["\x12"]="12",["\x13"]="13",["\x14"]="14",["\x15"]="15",["\x16"]="16",["\x17"]="17",["\x18"]="18",["\x19"]="19",["\x1A"]="1.",["\x1B"]="1E",["\x1C"]="1E-",["\x1D"]="1",["\x1E"]="1-",["\x1F"]="1",
13599  ["\x20"]="20",["\x21"]="21",["\x22"]="22",["\x23"]="23",["\x24"]="24",["\x25"]="25",["\x26"]="26",["\x27"]="27",["\x28"]="28",["\x29"]="29",["\x2A"]="2.",["\x2B"]="2E",["\x2C"]="2E-",["\x2D"]="2",["\x2E"]="2-",["\x2F"]="2",
13600  ["\x30"]="30",["\x31"]="31",["\x32"]="32",["\x33"]="33",["\x34"]="34",["\x35"]="35",["\x36"]="36",["\x37"]="37",["\x38"]="38",["\x39"]="39",["\x3A"]="3.",["\x3B"]="3E",["\x3C"]="3E-",["\x3D"]="3",["\x3E"]="3-",["\x3F"]="3",
13601  ["\x40"]="40",["\x41"]="41",["\x42"]="42",["\x43"]="43",["\x44"]="44",["\x45"]="45",["\x46"]="46",["\x47"]="47",["\x48"]="48",["\x49"]="49",["\x4A"]="4.",["\x4B"]="4E",["\x4C"]="4E-",["\x4D"]="4",["\x4E"]="4-",["\x4F"]="4",
13602  ["\x50"]="50",["\x51"]="51",["\x52"]="52",["\x53"]="53",["\x54"]="54",["\x55"]="55",["\x56"]="56",["\x57"]="57",["\x58"]="58",["\x59"]="59",["\x5A"]="5.",["\x5B"]="5E",["\x5C"]="5E-",["\x5D"]="5",["\x5E"]="5-",["\x5F"]="5",
13603  ["\x60"]="60",["\x61"]="61",["\x62"]="62",["\x63"]="63",["\x64"]="64",["\x65"]="65",["\x66"]="66",["\x67"]="67",["\x68"]="68",["\x69"]="69",["\x6A"]="6.",["\x6B"]="6E",["\x6C"]="6E-",["\x6D"]="6",["\x6E"]="6-",["\x6F"]="6",
13604  ["\x70"]="70",["\x71"]="71",["\x72"]="72",["\x73"]="73",["\x74"]="74",["\x75"]="75",["\x76"]="76",["\x77"]="77",["\x78"]="78",["\x79"]="79",["\x7A"]="7.",["\x7B"]="7E",["\x7C"]="7E-",["\x7D"]="7",["\x7E"]="7-",["\x7F"]="7",
13605  ["\x80"]="80",["\x81"]="81",["\x82"]="82",["\x83"]="83",["\x84"]="84",["\x85"]="85",["\x86"]="86",["\x87"]="87",["\x88"]="88",["\x89"]="89",["\x8A"]="8.",["\x8B"]="8E",["\x8C"]="8E-",["\x8D"]="8",["\x8E"]="8-",["\x8F"]="8",
13606  ["\x90"]="90",["\x91"]="91",["\x92"]="92",["\x93"]="93",["\x94"]="94",["\x95"]="95",["\x96"]="96",["\x97"]="97",["\x98"]="98",["\x99"]="99",["\x9A"]="9.",["\x9B"]="9E",["\x9C"]="9E-",["\x9D"]="9",["\x9E"]="9-",["\x9F"]="9",
13607  ["\xA0"]=".0",["\xA1"]=".1",["\xA2"]=".2",["\xA3"]=".3",["\xA4"]=".4",["\xA5"]=".5",["\xA6"]=".6",["\xA7"]=".7",["\xA8"]=".8",["\xA9"]=".9",["\xAA"]="..",["\xAB"]=".E",["\xAC"]=".E-",["\xAD"]=".",["\xAE"]=".-",["\xAF"]=".",
13608  ["\xB0"]="E0",["\xB1"]="E1",["\xB2"]="E2",["\xB3"]="E3",["\xB4"]="E4",["\xB5"]="E5",["\xB6"]="E6",["\xB7"]="E7",["\xB8"]="E8",["\xB9"]="E9",["\xBA"]="E.",["\xBB"]="EE",["\xBC"]="EE-",["\xBD"]="E",["\xBE"]="E-",["\xBF"]="E",
13609  ["\xC0"]="E-0",["\xC1"]="E-1",["\xC2"]="E-2",["\xC3"]="E-3",["\xC4"]="E-4",["\xC5"]="E-5",["\xC6"]="E-6",["\xC7"]="E-7",["\xC8"]="E-8",["\xC9"]="E-9",["\xCA"]="E-.",["\xCB"]="E-E",["\xCC"]="E-E-",["\xCD"]="E-",["\xCE"]="E--",["\xCF"]="E-",
13610  ["\xD0"]="-0",["\xD1"]="-1",["\xD2"]="-2",["\xD3"]="-3",["\xD4"]="-4",["\xD5"]="-5",["\xD6"]="-6",["\xD7"]="-7",["\xD8"]="-8",["\xD9"]="-9",["\xDA"]="-.",["\xDB"]="-E",["\xDC"]="-E-",["\xDD"]="-",["\xDE"]="--",["\xDF"]="-",
13611 }
13612 local remap_2={
13613  ["\x0F"]="0",["\x1F"]="1",["\x2F"]="2",["\x3F"]="3",["\x4F"]="4",
13614  ["\x5F"]="5",["\x6F"]="6",["\x7F"]="7",["\x8F"]="8",["\x9F"]="9",
13615 }
13616 local p_last_1=S("\x0F\x1F\x2F\x3F\x4F\x5F\x6F\x7F\x8F\x9F\xAF\xBF")
13617 local p_last_2=R("\xF0\xFF")
13618 local p_nibbles=P("\30")*Cs(((1-(p_last_1+p_last_2))/remap_1)^0*(p_last_1/remap_2+p_last_2/""))/function(n)
13619  top=top+1
13620  stack[top]=tonumber(n) or 0
13621 end
13622 local p_byte=C(R("\32\246"))/function(b0)
13623  top=top+1
13624  stack[top]=byte(b0)-139
13625 end
13626 local p_positive=C(R("\247\250"))*C(1)/function(b0,b1)
13627  top=top+1
13628  stack[top]=(byte(b0)-247)*256+byte(b1)+108
13629 end
13630 local p_negative=C(R("\251\254"))*C(1)/function(b0,b1)
13631  top=top+1
13632  stack[top]=-(byte(b0)-251)*256-byte(b1)-108
13633 end
13634 local p_short=P("\28")*C(1)*C(1)/function(b1,b2)
13635  top=top+1
13636  local n=0x100*byte(b1)+byte(b2)
13637  if n>=0x8000 then
13638   stack[top]=n-0xFFFF-1
13639  else
13640   stack[top]=n
13641  end
13642 end
13643 local p_long=P("\29")*C(1)*C(1)*C(1)*C(1)/function(b1,b2,b3,b4)
13644  top=top+1
13645  local n=0x1000000*byte(b1)+0x10000*byte(b2)+0x100*byte(b3)+byte(b4)
13646  if n>=0x8000000 then
13647   stack[top]=n-0xFFFFFFFF-1
13648  else
13649   stack[top]=n
13650  end
13651 end
13652 local p_unsupported=P(1)/function(detail)
13653  top=0
13654 end
13655 local p_dictionary=(
13656  p_byte+p_positive+p_negative+p_short+p_long+p_nibbles+p_single+p_double
13657+p_unsupported
13658 )^1
13659 parsedictionaries=function(data,dictionaries,version)
13660  stack={}
13661  strings=data.strings
13662  if trace_charstrings then
13663   report("charstring format %a",version)
13664  end
13665  for i=1,#dictionaries do
13666   top=0
13667   result=version=="cff" and {
13668    monospaced=false,
13669    italicangle=0,
13670    underlineposition=-100,
13671    underlinethickness=50,
13672    painttype=0,
13673    charstringtype=2,
13674    fontmatrix={ 0.001,0,0,0.001,0,0 },
13675    fontbbox={ 0,0,0,0 },
13676    strokewidth=0,
13677    charset=0,
13678    encoding=0,
13679    cid={
13680     fontversion=0,
13681     fontrevision=0,
13682     fonttype=0,
13683     count=8720,
13684    }
13685   } or {
13686    charstringtype=2,
13687    charset=0,
13688    vstore=0,
13689    cid={
13690    },
13691   }
13692   lpegmatch(p_dictionary,dictionaries[i])
13693   dictionaries[i]=result
13694  end
13695  result={}
13696  top=0
13697  stack={}
13698 end
13699 parseprivates=function(data,dictionaries)
13700  stack={}
13701  strings=data.strings
13702  for i=1,#dictionaries do
13703   local private=dictionaries[i].private
13704   if private and private.data then
13705    top=0
13706    result={
13707     forcebold=false,
13708     languagegroup=0,
13709     expansionfactor=0.06,
13710     initialrandomseed=0,
13711     subroutines=0,
13712     defaultwidthx=0,
13713     nominalwidthx=0,
13714     cid={
13715     },
13716    }
13717    lpegmatch(p_dictionary,private.data)
13718    private.data=result
13719   end
13720  end
13721  result={}
13722  top=0
13723  stack={}
13724 end
13725 local x=0
13726 local y=0
13727 local width=false
13728 local lsb=0
13729 local result={}
13730 local r=0
13731 local stems=0
13732 local globalbias=0
13733 local localbias=0
13734 local nominalwidth=0
13735 local defaultwidth=0
13736 local charset=false
13737 local globals=false
13738 local locals=false
13739 local depth=1
13740 local xmin=0
13741 local xmax=0
13742 local ymin=0
13743 local ymax=0
13744 local checked=false
13745 local keepcurve=false
13746 local version=2
13747 local regions=false
13748 local nofregions=0
13749 local region=false
13750 local factors=false
13751 local axis=false
13752 local vsindex=0
13753 local justpass=false
13754 local seacs={}
13755 local procidx=nil
13756 local function showstate(where,i,n)
13757  if i then
13758   local j=i+n-1
13759   report("%w%-10s : [%s] step",depth*2+2,where,concat(stack," ",i,j<=top and j or top))
13760  else
13761   report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top)
13762  end
13763 end
13764 local function showvalue(where,value,showstack)
13765  if showstack then
13766   report("%w%-10s : %s : [%s] n=%i",depth*2,where,tostring(value),concat(stack," ",1,top),top)
13767  else
13768   report("%w%-10s : %s",depth*2,where,tostring(value))
13769  end
13770 end
13771 local function xymoveto()
13772  if keepcurve then
13773   r=r+1
13774   result[r]={ x,y,"m" }
13775  end
13776  if checked then
13777   if x>xmax then xmax=x elseif x<xmin then xmin=x end
13778   if y>ymax then ymax=y elseif y<ymin then ymin=y end
13779  else
13780   xmin=x
13781   ymin=y
13782   xmax=x
13783   ymax=y
13784   checked=true
13785  end
13786 end
13787 local function xmoveto() 
13788  if keepcurve then
13789   r=r+1
13790   result[r]={ x,y,"m" }
13791  end
13792  if not checked then
13793   xmin=x
13794   ymin=y
13795   xmax=x
13796   ymax=y
13797   checked=true
13798  elseif x>xmax then
13799   xmax=x
13800  elseif x<xmin then
13801   xmin=x
13802  end
13803 end
13804 local function ymoveto() 
13805  if keepcurve then
13806   r=r+1
13807   result[r]={ x,y,"m" }
13808  end
13809  if not checked then
13810   xmin=x
13811   ymin=y
13812   xmax=x
13813   ymax=y
13814   checked=true
13815  elseif y>ymax then
13816   ymax=y
13817  elseif y<ymin then
13818   ymin=y
13819  end
13820 end
13821 local function moveto()
13822  if trace_charstrings then
13823   showstate("moveto")
13824  end
13825  top=0 
13826  xymoveto()
13827 end
13828 local function xylineto() 
13829  if keepcurve then
13830   r=r+1
13831   result[r]={ x,y,"l" }
13832  end
13833  if checked then
13834   if x>xmax then xmax=x elseif x<xmin then xmin=x end
13835   if y>ymax then ymax=y elseif y<ymin then ymin=y end
13836  else
13837   xmin=x
13838   ymin=y
13839   xmax=x
13840   ymax=y
13841   checked=true
13842  end
13843 end
13844 local function xlineto() 
13845  if keepcurve then
13846   r=r+1
13847   result[r]={ x,y,"l" }
13848  end
13849  if not checked then
13850   xmin=x
13851   ymin=y
13852   xmax=x
13853   ymax=y
13854   checked=true
13855  elseif x>xmax then
13856   xmax=x
13857  elseif x<xmin then
13858   xmin=x
13859  end
13860 end
13861 local function ylineto() 
13862  if keepcurve then
13863   r=r+1
13864   result[r]={ x,y,"l" }
13865  end
13866  if not checked then
13867   xmin=x
13868   ymin=y
13869   xmax=x
13870   ymax=y
13871   checked=true
13872  elseif y>ymax then
13873   ymax=y
13874  elseif y<ymin then
13875   ymin=y
13876  end
13877 end
13878 local function xycurveto(x1,y1,x2,y2,x3,y3,i,n) 
13879  if trace_charstrings then
13880   showstate("curveto",i,n)
13881  end
13882  if keepcurve then
13883   r=r+1
13884   result[r]={ x1,y1,x2,y2,x3,y3,"c" }
13885  end
13886  if checked then
13887   if x1>xmax then xmax=x1 elseif x1<xmin then xmin=x1 end
13888   if y1>ymax then ymax=y1 elseif y1<ymin then ymin=y1 end
13889  else
13890   xmin=x1
13891   ymin=y1
13892   xmax=x1
13893   ymax=y1
13894   checked=true
13895  end
13896  if x2>xmax then xmax=x2 elseif x2<xmin then xmin=x2 end
13897  if y2>ymax then ymax=y2 elseif y2<ymin then ymin=y2 end
13898  if x3>xmax then xmax=x3 elseif x3<xmin then xmin=x3 end
13899  if y3>ymax then ymax=y3 elseif y3<ymin then ymin=y3 end
13900 end
13901 local function rmoveto()
13902  if not width then
13903   if top>2 then
13904    width=stack[1]
13905    if trace_charstrings then
13906     showvalue("backtrack width",width)
13907    end
13908   else
13909    width=true
13910   end
13911  end
13912  if trace_charstrings then
13913   showstate("rmoveto")
13914  end
13915  x=x+stack[top-1] 
13916  y=y+stack[top]   
13917  top=0
13918  xymoveto()
13919 end
13920 local function hmoveto()
13921  if not width then
13922   if top>1 then
13923    width=stack[1]
13924    if trace_charstrings then
13925     showvalue("backtrack width",width)
13926    end
13927   else
13928    width=true
13929   end
13930  end
13931  if trace_charstrings then
13932   showstate("hmoveto")
13933  end
13934  x=x+stack[top] 
13935  top=0
13936  xmoveto()
13937 end
13938 local function vmoveto()
13939  if not width then
13940   if top>1 then
13941    width=stack[1]
13942    if trace_charstrings then
13943     showvalue("backtrack width",width)
13944    end
13945   else
13946    width=true
13947   end
13948  end
13949  if trace_charstrings then
13950   showstate("vmoveto")
13951  end
13952  y=y+stack[top] 
13953  top=0
13954  ymoveto()
13955 end
13956 local function rlineto()
13957  if trace_charstrings then
13958   showstate("rlineto")
13959  end
13960  for i=1,top,2 do
13961   x=x+stack[i]   
13962   y=y+stack[i+1] 
13963   xylineto()
13964  end
13965  top=0
13966 end
13967 local function hlineto() 
13968  if trace_charstrings then
13969   showstate("hlineto")
13970  end
13971  if top==1 then
13972   x=x+stack[1]
13973   xlineto()
13974  else
13975   local swap=true
13976   for i=1,top do
13977    if swap then
13978     x=x+stack[i]
13979     xlineto()
13980     swap=false
13981    else
13982     y=y+stack[i]
13983     ylineto()
13984     swap=true
13985    end
13986   end
13987  end
13988  top=0
13989 end
13990 local function vlineto() 
13991  if trace_charstrings then
13992   showstate("vlineto")
13993  end
13994  if top==1 then
13995   y=y+stack[1]
13996   ylineto()
13997  else
13998   local swap=false
13999   for i=1,top do
14000    if swap then
14001     x=x+stack[i]
14002     xlineto()
14003     swap=false
14004    else
14005     y=y+stack[i]
14006     ylineto()
14007     swap=true
14008    end
14009   end
14010  end
14011  top=0
14012 end
14013 local function rrcurveto()
14014  if trace_charstrings then
14015   showstate("rrcurveto")
14016  end
14017if top==6 then
14018 local ax=x+stack[1] 
14019 local ay=y+stack[2] 
14020 local bx=ax+stack[3] 
14021 local by=ay+stack[4] 
14022 x=bx+stack[5]  
14023 y=by+stack[6]  
14024 xycurveto(ax,ay,bx,by,x,y,1,6)
14025else
14026  for i=1,top,6 do
14027   local ax=x+stack[i]   
14028   local ay=y+stack[i+1] 
14029   local bx=ax+stack[i+2] 
14030   local by=ay+stack[i+3] 
14031   x=bx+stack[i+4]  
14032   y=by+stack[i+5]  
14033   xycurveto(ax,ay,bx,by,x,y,i,6)
14034  end
14035end
14036  top=0
14037 end
14038 local function hhcurveto()
14039  if trace_charstrings then
14040   showstate("hhcurveto")
14041  end
14042  local s=1
14043  if top%2~=0 then
14044   y=y+stack[1]     
14045   s=2
14046  end
14047if top==4 then
14048   local ax=x+stack[1]  
14049   local ay=y
14050   local bx=ax+stack[2] 
14051   local by=ay+stack[3] 
14052   x=bx+stack[4]  
14053   y=by
14054   xycurveto(ax,ay,bx,by,x,y,1,4)
14055else
14056  for i=s,top,4 do
14057   local ax=x+stack[i] 
14058   local ay=y
14059   local bx=ax+stack[i+1] 
14060   local by=ay+stack[i+2] 
14061   x=bx+stack[i+3]  
14062   y=by
14063   xycurveto(ax,ay,bx,by,x,y,i,4)
14064  end
14065end
14066  top=0
14067 end
14068 local function vvcurveto()
14069  if trace_charstrings then
14070   showstate("vvcurveto")
14071  end
14072  local s=1
14073  local d=0
14074  if top%2~=0 then
14075   d=stack[1]      
14076   s=2
14077  end
14078if top==4 then
14079 local ax=x+d
14080 local ay=y+stack[1]  
14081 local bx=ax+stack[2] 
14082 local by=ay+stack[3] 
14083 x=bx
14084 y=by+stack[4]  
14085 xycurveto(ax,ay,bx,by,x,y,1,4)
14086 d=0
14087else
14088  for i=s,top,4 do
14089   local ax=x+d
14090   local ay=y+stack[i] 
14091   local bx=ax+stack[i+1] 
14092   local by=ay+stack[i+2] 
14093   x=bx
14094   y=by+stack[i+3]  
14095   xycurveto(ax,ay,bx,by,x,y,i,4)
14096   d=0
14097  end
14098end
14099  top=0
14100 end
14101 local function xxcurveto(swap)
14102  local last=top%4~=0 and stack[top]
14103  if last then
14104   top=top-1
14105  end
14106if top==4 then
14107  local ax,ay,bx,by
14108  if swap then
14109   ax=x+stack[1]
14110   ay=y
14111   bx=ax+stack[2]
14112   by=ay+stack[3]
14113   y=by+stack[4]
14114   if last then
14115    x=bx+last
14116   else
14117    x=bx
14118   end
14119  else
14120   ax=x
14121   ay=y+stack[1]
14122   bx=ax+stack[2]
14123   by=ay+stack[3]
14124   x=bx+stack[4]
14125   if last then
14126    y=by+last
14127   else
14128    y=by
14129   end
14130  end
14131  xycurveto(ax,ay,bx,by,x,y,1,4)
14132else
14133  for i=1,top,4 do
14134   local ax,ay,bx,by
14135   if swap then
14136    ax=x+stack[i]
14137    ay=y
14138    bx=ax+stack[i+1]
14139    by=ay+stack[i+2]
14140    y=by+stack[i+3]
14141    if last and i+3==top then
14142     x=bx+last
14143    else
14144     x=bx
14145    end
14146    swap=false
14147   else
14148    ax=x
14149    ay=y+stack[i]
14150    bx=ax+stack[i+1]
14151    by=ay+stack[i+2]
14152    x=bx+stack[i+3]
14153    if last and i+3==top then
14154     y=by+last
14155    else
14156     y=by
14157    end
14158    swap=true
14159   end
14160   xycurveto(ax,ay,bx,by,x,y,i,4)
14161  end
14162end
14163  top=0
14164 end
14165 local function hvcurveto()
14166  if trace_charstrings then
14167   showstate("hvcurveto")
14168  end
14169  xxcurveto(true)
14170 end
14171 local function vhcurveto()
14172  if trace_charstrings then
14173   showstate("vhcurveto")
14174  end
14175  xxcurveto(false)
14176 end
14177 local function rcurveline()
14178  if trace_charstrings then
14179   showstate("rcurveline")
14180  end
14181  for i=1,top-2,6 do
14182   local ax=x+stack[i]   
14183   local ay=y+stack[i+1] 
14184   local bx=ax+stack[i+2] 
14185   local by=ay+stack[i+3] 
14186   x=bx+stack[i+4] 
14187   y=by+stack[i+5] 
14188   xycurveto(ax,ay,bx,by,x,y,i,6)
14189  end
14190  x=x+stack[top-1] 
14191  y=y+stack[top]   
14192  xylineto()
14193  top=0
14194 end
14195 local function rlinecurve()
14196  if trace_charstrings then
14197   showstate("rlinecurve")
14198  end
14199  if top>6 then
14200   for i=1,top-6,2 do
14201    x=x+stack[i]
14202    y=y+stack[i+1]
14203    xylineto()
14204   end
14205  end
14206  local ax=x+stack[top-5]
14207  local ay=y+stack[top-4]
14208  local bx=ax+stack[top-3]
14209  local by=ay+stack[top-2]
14210  x=bx+stack[top-1]
14211  y=by+stack[top]
14212  xycurveto(ax,ay,bx,by,x,y)
14213  top=0
14214 end
14215 local function flex() 
14216  if trace_charstrings then
14217   showstate("flex")
14218  end
14219  local ax=x+stack[1]  
14220  local ay=y+stack[2]  
14221  local bx=ax+stack[3]  
14222  local by=ay+stack[4]  
14223  local cx=bx+stack[5]  
14224  local cy=by+stack[6]  
14225  xycurveto(ax,ay,bx,by,cx,cy)
14226  local dx=cx+stack[7]  
14227  local dy=cy+stack[8]  
14228  local ex=dx+stack[9]  
14229  local ey=dy+stack[10] 
14230  x=ex+stack[11]  
14231  y=ey+stack[12]  
14232  xycurveto(dx,dy,ex,ey,x,y)
14233  top=0
14234 end
14235 local function hflex()
14236  if trace_charstrings then
14237   showstate("hflex")
14238  end
14239  local ax=x+stack[1] 
14240  local ay=y
14241  local bx=ax+stack[2] 
14242  local by=ay+stack[3] 
14243  local cx=bx+stack[4] 
14244  local cy=by
14245  xycurveto(ax,ay,bx,by,cx,cy)
14246  local dx=cx+stack[5] 
14247  local dy=by
14248  local ex=dx+stack[6] 
14249  local ey=y
14250  x=ex+stack[7]  
14251  xycurveto(dx,dy,ex,ey,x,y)
14252  top=0
14253 end
14254 local function hflex1()
14255  if trace_charstrings then
14256   showstate("hflex1")
14257  end
14258  local ax=x+stack[1] 
14259  local ay=y+stack[2] 
14260  local bx=ax+stack[3] 
14261  local by=ay+stack[4] 
14262  local cx=bx+stack[5] 
14263  local cy=by
14264  xycurveto(ax,ay,bx,by,cx,cy)
14265  local dx=cx+stack[6] 
14266  local dy=by
14267  local ex=dx+stack[7] 
14268  local ey=dy+stack[8] 
14269  x=ex+stack[9]  
14270  xycurveto(dx,dy,ex,ey,x,y)
14271  top=0
14272 end
14273 local function flex1()
14274  if trace_charstrings then
14275   showstate("flex1")
14276  end
14277  local ax=x+stack[1]  
14278  local ay=y+stack[2]  
14279  local bx=ax+stack[3]  
14280  local by=ay+stack[4]  
14281  local cx=bx+stack[5]  
14282  local cy=by+stack[6]  
14283  xycurveto(ax,ay,bx,by,cx,cy)
14284  local dx=cx+stack[7]  
14285  local dy=cy+stack[8]  
14286  local ex=dx+stack[9]  
14287  local ey=dy+stack[10] 
14288  if abs(ex-x)>abs(ey-y) then 
14289   x=ex+stack[11]
14290  else
14291   y=ey+stack[11]
14292  end
14293  xycurveto(dx,dy,ex,ey,x,y)
14294  top=0
14295 end
14296 local function getstem()
14297  if top==0 then
14298  elseif top%2~=0 then
14299   if width then
14300    remove(stack,1)
14301   else
14302    width=remove(stack,1)
14303    if trace_charstrings then
14304     showvalue("width",width)
14305    end
14306   end
14307   top=top-1
14308  end
14309  if trace_charstrings then
14310   showstate("stem")
14311  end
14312  stems=stems+idiv(top,2)
14313  top=0
14314 end
14315 local function getmask()
14316  if top==0 then
14317  elseif top%2~=0 then
14318   if width then
14319    remove(stack,1)
14320   else
14321    width=remove(stack,1)
14322    if trace_charstrings then
14323     showvalue("width",width)
14324    end
14325   end
14326   top=top-1
14327  end
14328  if trace_charstrings then
14329   showstate(operator==19 and "hintmark" or "cntrmask")
14330  end
14331  stems=stems+idiv(top,2)
14332  top=0
14333  if stems==0 then
14334  elseif stems<=8 then
14335   return 1
14336  else
14337   return idiv(stems+7,8)
14338  end
14339 end
14340 local function unsupported(t)
14341  if trace_charstrings then
14342   showstate("unsupported "..t)
14343  end
14344  top=0
14345 end
14346 local function unsupportedsub(t)
14347  if trace_charstrings then
14348   showstate("unsupported sub "..t)
14349  end
14350  top=0
14351 end
14352 local function getstem3()
14353  if trace_charstrings then
14354   showstate("stem3")
14355  end
14356  top=0
14357 end
14358 local function divide()
14359  if version=="cff" then
14360   local d=stack[top]
14361   top=top-1
14362   stack[top]=stack[top]/d
14363  end
14364 end
14365 local function closepath()
14366  if version=="cff" then
14367   if trace_charstrings then
14368    showstate("closepath")
14369   end
14370  end
14371  top=0
14372 end
14373 local function hsbw()
14374  if version=="cff" then
14375   if trace_charstrings then
14376    showstate("hsbw")
14377   end
14378   lsb=stack[top-1] or 0
14379   width=stack[top]
14380  end
14381  top=0
14382 end
14383 local function sbw()
14384  if version=="cff" then
14385   if trace_charstrings then
14386    showstate("sbw")
14387   end
14388   lsb=stack[top-3]
14389   width=stack[top-1]
14390  end
14391  top=0
14392 end
14393 local function seac()
14394  if version=="cff" then
14395   if trace_charstrings then
14396    showstate("seac")
14397   end
14398  end
14399  top=0
14400 end
14401 local popped=3
14402 local hints=3
14403 local function callothersubr()
14404  if version=="cff" then
14405   if trace_charstrings then
14406    showstate("callothersubr")
14407   end
14408   if stack[top]==hints then
14409    popped=stack[top-2]
14410   else
14411    popped=3
14412   end
14413   local t=stack[top-1]
14414   if t then
14415    top=top-(t+2)
14416    if top<0 then
14417     top=0
14418    end
14419   else
14420    top=0
14421   end
14422  else
14423   top=0
14424  end
14425 end
14426 local function pop()
14427  if version=="cff" then
14428   if trace_charstrings then
14429    showstate("pop")
14430   end
14431   top=top+1
14432   stack[top]=popped
14433  else
14434   top=0
14435  end
14436 end
14437 local function setcurrentpoint()
14438  if version=="cff" then
14439   if trace_charstrings then
14440    showstate("setcurrentpoint (unsupported)")
14441   end
14442   x=x+stack[top-1]
14443   y=y+stack[top]
14444  end
14445  top=0
14446 end
14447 local reginit=false
14448 local function updateregions(n) 
14449  if regions then
14450   local current=regions[n+1] or regions[1]
14451   nofregions=#current
14452   if axis and n~=reginit then
14453    factors={}
14454    for i=1,nofregions do
14455     local region=current[i]
14456     local s=1
14457     for j=1,#axis do
14458      local f=axis[j]
14459      local r=region[j]
14460      local start=r.start
14461      local peak=r.peak
14462      local stop=r.stop
14463      if start>peak or peak>stop then
14464      elseif start<0 and stop>0 and peak~=0 then
14465      elseif peak==0 then
14466      elseif f<start or f>stop then
14467       s=0
14468       break
14469      elseif f<peak then
14470       s=s*(f-start)/(peak-start)
14471      elseif f>peak then
14472       s=s*(stop-f)/(stop-peak)
14473      else
14474      end
14475     end
14476     factors[i]=s
14477    end
14478   end
14479  end
14480  reginit=n
14481 end
14482 local function setvsindex()
14483  local vsindex=stack[top]
14484  if trace_charstrings then
14485   showstate(formatters["vsindex %i"](vsindex))
14486  end
14487  updateregions(vsindex)
14488  top=top-1
14489 end
14490 local function blend()
14491  local n=stack[top]
14492  top=top-1
14493  if axis then
14494   if trace_charstrings then
14495    local t=top-nofregions*n
14496    local m=t-n
14497    for i=1,n do
14498     local k=m+i
14499     local d=m+n+(i-1)*nofregions
14500     local old=stack[k]
14501     local new=old
14502     for r=1,nofregions do
14503      new=new+stack[d+r]*factors[r]
14504     end
14505     stack[k]=new
14506     showstate(formatters["blend %i of %i: %s -> %s"](i,n,old,new))
14507    end
14508    top=t
14509   elseif n==1 then
14510    top=top-nofregions
14511    local v=stack[top]
14512    for r=1,nofregions do
14513     v=v+stack[top+r]*factors[r]
14514    end
14515    stack[top]=v
14516   else
14517    top=top-nofregions*n
14518    local d=top
14519    local k=top-n
14520    for i=1,n do
14521     k=k+1
14522     local v=stack[k]
14523     for r=1,nofregions do
14524      v=v+stack[d+r]*factors[r]
14525     end
14526     stack[k]=v
14527     d=d+nofregions
14528    end
14529   end
14530  else
14531   top=top-nofregions*n
14532  end
14533 end
14534 local actions={ [0]=unsupported,
14535  getstem,
14536  unsupported,
14537  getstem,
14538  vmoveto,
14539  rlineto,
14540  hlineto,
14541  vlineto,
14542  rrcurveto,
14543  unsupported,
14544  unsupported,
14545  unsupported,
14546  unsupported,
14547  hsbw,
14548  unsupported,
14549  setvsindex,
14550  blend,
14551  unsupported,
14552  getstem,
14553  getmask,
14554  getmask,
14555  rmoveto,
14556  hmoveto,
14557  getstem,
14558  rcurveline,
14559  rlinecurve,
14560  vvcurveto,
14561  hhcurveto,
14562  unsupported,
14563  unsupported,
14564  vhcurveto,
14565  hvcurveto,
14566 }
14567 local reverse={ [0]="unsupported",
14568  "getstem",
14569  "unsupported",
14570  "getstem",
14571  "vmoveto",
14572  "rlineto",
14573  "hlineto",
14574  "vlineto",
14575  "rrcurveto",
14576  "unsupported",
14577  "unsupported",
14578  "unsupported",
14579  "unsupported",
14580  "hsbw",
14581  "unsupported",
14582  "setvsindex",
14583  "blend",
14584  "unsupported",
14585  "getstem",
14586  "getmask",
14587  "getmask",
14588  "rmoveto",
14589  "hmoveto",
14590  "getstem",
14591  "rcurveline",
14592  "rlinecurve",
14593  "vvcurveto",
14594  "hhcurveto",
14595  "unsupported",
14596  "unsupported",
14597  "vhcurveto",
14598  "hvcurveto",
14599 }
14600 local subactions={
14601  [000]=dotsection,
14602  [001]=getstem3,
14603  [002]=getstem3,
14604  [006]=seac,
14605  [007]=sbw,
14606  [012]=divide,
14607  [016]=callothersubr,
14608  [017]=pop,
14609  [033]=setcurrentpoint,
14610  [034]=hflex,
14611  [035]=flex,
14612  [036]=hflex1,
14613  [037]=flex1,
14614 }
14615 local chars=setmetatableindex(function (t,k)
14616  local v=char(k)
14617  t[k]=v
14618  return v
14619 end)
14620 local c_endchar=chars[14]
14621 local encode={}
14622 local typeone=false
14623 setmetatableindex(encode,function(t,i)
14624  for i=-2048,-1130 do
14625   t[i]=char(28,band(rshift(i,8),0xFF),band(i,0xFF))
14626  end
14627  for i=-1131,-108 do
14628   local v=0xFB00-i-108
14629   t[i]=char(band(rshift(v,8),0xFF),band(v,0xFF))
14630  end
14631  for i=-107,107 do
14632   t[i]=chars[i+139]
14633  end
14634  for i=108,1131 do
14635   local v=0xF700+i-108
14636   t[i]=char(extract(v,8,8),extract(v,0,8))
14637  end
14638  for i=1132,2048 do
14639   t[i]=char(28,band(rshift(i,8),0xFF),band(i,0xFF))
14640  end
14641  setmetatableindex(encode,function(t,k)
14642   local r=round(k)
14643   local v=rawget(t,r)
14644   if v then
14645    return v
14646   end
14647   local v1=floor(k)
14648   local v2=floor((k-v1)*0x10000)
14649   return char(255,extract(v1,8,8),extract(v1,0,8),extract(v2,8,8),extract(v2,0,8))
14650  end)
14651  return t[i]
14652 end)
14653 readers.cffencoder=encode
14654 local function p_setvsindex()
14655  local vsindex=stack[top]
14656  updateregions(vsindex)
14657  top=top-1
14658 end
14659 local function p_blend()
14660  local n=stack[top]
14661  top=top-1
14662  if not axis then
14663  elseif n==1 then
14664   top=top-nofregions
14665   local v=stack[top]
14666   for r=1,nofregions do
14667    v=v+stack[top+r]*factors[r]
14668   end
14669   stack[top]=round(v)
14670  else
14671   top=top-nofregions*n
14672   local d=top
14673   local k=top-n
14674   for i=1,n do
14675    k=k+1
14676    local v=stack[k]
14677    for r=1,nofregions do
14678     v=v+stack[d+r]*factors[r]
14679    end
14680    stack[k]=round(v)
14681    d=d+nofregions
14682   end
14683  end
14684 end
14685 local function p_getstem()
14686  local n=0
14687  if top%2~=0 then
14688   n=1
14689  end
14690  if top>n then
14691   stems=stems+idiv(top-n,2)
14692  end
14693 end
14694 local function p_getmask()
14695  local n=0
14696  if top%2~=0 then
14697   n=1
14698  end
14699  if top>n then
14700   stems=stems+idiv(top-n,2)
14701  end
14702  if stems==0 then
14703   return 0
14704  elseif stems<=8 then
14705   return 1
14706  else
14707   return idiv(stems+7,8)
14708  end
14709 end
14710 local process
14711 local function call(scope,list,bias) 
14712  depth=depth+1
14713  if top==0 then
14714   showstate(formatters["unknown %s call %s, case %s"](scope,"?",1))
14715   top=0
14716  else
14717   local index=stack[top]+bias
14718   top=top-1
14719   if trace_charstrings then
14720    showvalue(scope,index,true)
14721   end
14722   local tab=list[index]
14723   if tab then
14724    process(tab)
14725   else
14726    showstate(formatters["unknown %s call %s, case %s"](scope,index,2))
14727    top=0
14728   end
14729  end
14730  depth=depth-1
14731 end
14732 process=function(tab)
14733  local i=1
14734  local n=#tab
14735  while i<=n do
14736   local t=tab[i]
14737   if t>=32 then
14738    top=top+1
14739    if t<=246 then
14740     stack[top]=t-139
14741     i=i+1
14742    elseif t<=250 then
14743     stack[top]=t*256-63124+tab[i+1]
14744     i=i+2
14745    elseif t<=254 then
14746     stack[top]=-t*256+64148-tab[i+1]
14747     i=i+2
14748    elseif typeone then
14749     local n=0x1000000*tab[i+1]+0x10000*tab[i+2]+0x100*tab[i+3]+tab[i+4]
14750     if n>=0x8000000 then
14751      n=n-0xFFFFFFFF-1
14752     end
14753     stack[top]=n
14754     i=i+5
14755    else
14756     local n1=0x100*tab[i+1]+tab[i+2]
14757     local n2=0x100*tab[i+3]+tab[i+4]
14758     if n1>=0x8000 then
14759      n1=n1-0x10000
14760     end
14761     stack[top]=n1+n2/0xFFFF
14762     i=i+5
14763    end
14764   elseif t==28 then
14765    top=top+1
14766    local n=0x100*tab[i+1]+tab[i+2]
14767    if n>=0x8000 then
14768     stack[top]=n-0x10000
14769    else
14770     stack[top]=n
14771    end
14772    i=i+3
14773   elseif t==11 then 
14774    if trace_charstrings then
14775     showstate("return")
14776    end
14777    return
14778   elseif t==10 then
14779    call("local",locals,localbias) 
14780    i=i+1
14781   elseif t==14 then 
14782    if width then
14783    elseif top>0 then
14784     width=stack[1]
14785     if trace_charstrings then
14786      showvalue("width",width)
14787     end
14788    else
14789     width=true
14790    end
14791    if trace_charstrings then
14792     showstate("endchar")
14793    end
14794    return
14795   elseif t==29 then
14796    call("global",globals,globalbias) 
14797    i=i+1
14798   elseif t==12 then
14799    i=i+1
14800    local t=tab[i]
14801    if justpass then
14802     if t>=34 and t<=37 then 
14803      for i=1,top do
14804       r=r+1;result[r]=encode[stack[i]]
14805      end
14806      r=r+1;result[r]=chars[12]
14807      r=r+1;result[r]=chars[t]
14808      top=0
14809     elseif t==6 then
14810      seacs[procidx]={
14811       asb=stack[1],
14812       adx=stack[2],
14813       ady=stack[3],
14814       base=stack[4],
14815       accent=stack[5],
14816       width=width,
14817       lsb=lsb,
14818      }
14819      top=0
14820     else
14821      local a=subactions[t]
14822      if a then
14823       a(t)
14824      else
14825       top=0
14826      end
14827     end
14828    else
14829     local a=subactions[t]
14830     if a then
14831      a(t)
14832     else
14833      if trace_charstrings then
14834       showvalue("<subaction>",t)
14835      end
14836      top=0
14837     end
14838    end
14839    i=i+1
14840   elseif justpass then
14841    if t==15 then
14842     p_setvsindex()
14843     i=i+1
14844    elseif t==16 then
14845     local s=p_blend() or 0
14846     i=i+s+1
14847    elseif t==1 or t==3 or t==18 or operation==23 then
14848     p_getstem() 
14849     if version=="cff" then
14850      if top>0 then
14851       for i=1,top do
14852        r=r+1;result[r]=encode[stack[i]]
14853       end
14854       top=0
14855      end
14856      r=r+1;result[r]=chars[t]
14857     else
14858      top=0
14859     end
14860     i=i+1
14861    elseif t==19 or t==20 then
14862     local s=p_getmask() or 0
14863     if true then
14864      if top>0 then
14865       for i=1,top do
14866        r=r+1;result[r]=encode[stack[i]]
14867       end
14868       top=0
14869      end
14870      r=r+1;result[r]=chars[t]
14871      for j=1,s do
14872       i=i+1
14873       r=r+1;result[r]=chars[tab[i]]
14874      end
14875     else
14876      i=i+s
14877      top=0
14878     end
14879     i=i+1
14880    elseif t==9 then
14881     top=0
14882     i=i+1
14883    elseif t==13 then
14884     hsbw()
14885     if true then
14886      r=r+1;result[r]=encode[lsb]
14887      r=r+1;result[r]=chars[22]
14888     else
14889     end
14890     i=i+1
14891    else
14892     if trace_charstrings then
14893      showstate(reverse[t] or "<action>")
14894     end
14895     if top>0 then
14896      if t==8 and top>48 then
14897       local n=0
14898       for i=1,top do
14899        if n==48 then
14900         r=r+1;result[r]=chars[t]
14901         n=1
14902        else
14903         n=n+1
14904        end
14905        r=r+1;result[r]=encode[stack[i]]
14906       end
14907      else
14908       for i=1,top do
14909        r=r+1;result[r]=encode[stack[i]]
14910       end
14911      end
14912      top=0
14913     end
14914     r=r+1;result[r]=chars[t]
14915     i=i+1
14916    end
14917   else
14918    local a=actions[t]
14919    if a then
14920     local s=a(t)
14921     if s then
14922      i=i+s+1
14923     else
14924      i=i+1
14925     end
14926    else
14927     if trace_charstrings then
14928      showstate(reverse[t] or "<action>")
14929     end
14930     top=0
14931     i=i+1
14932    end
14933   end
14934  end
14935 end
14936 local function setbias(globals,locals,nobias)
14937  if nobias then
14938   return 0,0
14939  else
14940   local g=#globals
14941   local l=#locals
14942   return
14943    ((g<1240 and 107) or (g<33900 and 1131) or 32768)+1,
14944    ((l<1240 and 107) or (l<33900 and 1131) or 32768)+1
14945  end
14946 end
14947 local function processshape(glyphs,tab,index,hack)
14948  if not tab then
14949   glyphs[index]={
14950    boundingbox={ 0,0,0,0 },
14951    width=0,
14952    name=charset and charset[index] or nil,
14953   }
14954   return
14955  end
14956  tab=bytetable(tab)
14957  x=0
14958  y=0
14959  width=false
14960  lsb=0
14961  r=0
14962  top=0
14963  stems=0
14964  result={} 
14965  popped=3
14966  procidx=index
14967  xmin=0
14968  xmax=0
14969  ymin=0
14970  ymax=0
14971  checked=false
14972  if trace_charstrings then
14973   report("glyph: %i",index)
14974   report("data : % t",tab)
14975  end
14976  if regions then
14977   updateregions(vsindex)
14978  end
14979  process(tab)
14980  if hack then
14981   return x,y
14982  end
14983  local boundingbox={
14984   round(xmin),
14985   round(ymin),
14986   round(xmax),
14987   round(ymax),
14988  }
14989  if width==true or width==false then
14990   width=defaultwidth
14991  else
14992   width=nominalwidth+width
14993  end
14994  local glyph=glyphs[index] 
14995  if justpass then
14996   r=r+1
14997   result[r]=c_endchar
14998   local stream=concat(result)
14999result=nil
15000   if glyph then
15001    glyph.stream=stream
15002    glyph.width=width
15003   else
15004    glyphs[index]={ stream=stream,width=width }
15005   end
15006  elseif glyph then
15007   glyph.segments=keepcurve~=false and result or nil
15008   glyph.boundingbox=boundingbox
15009   if not glyph.width then
15010    glyph.width=width
15011   end
15012   if charset and not glyph.name then
15013    glyph.name=charset[index]
15014   end
15015  elseif keepcurve then
15016   glyphs[index]={
15017    segments=result,
15018    boundingbox=boundingbox,
15019    width=width,
15020    name=charset and charset[index] or nil,
15021   }
15022result=nil
15023  else
15024   glyphs[index]={
15025    boundingbox=boundingbox,
15026    width=width,
15027    name=charset and charset[index] or nil,
15028   }
15029  end
15030  if trace_charstrings then
15031   report("width      : %s",tostring(width))
15032   report("boundingbox: % t",boundingbox)
15033  end
15034 end
15035 startparsing=function(fontdata,data,streams)
15036  reginit=false
15037  axis=false
15038  regions=data.regions
15039  justpass=streams==true
15040  popped=3
15041  seacs={}
15042  if regions then
15043   regions={}
15044   local deltas=data.deltas
15045   for i=1,#deltas do
15046    regions[i]=deltas[i].regions
15047   end
15048   axis=data.factors or false
15049  end
15050 end
15051 stopparsing=function(fontdata,data)
15052  stack={}
15053  glyphs=false
15054  result={}
15055  top=0
15056  locals=false
15057  globals=false
15058  strings=false
15059  popped=3
15060  seacs={}
15061 end
15062 local function setwidths(private)
15063  if not private then
15064   return 0,0
15065  end
15066  local privatedata=private.data
15067  if not privatedata then
15068   return 0,0
15069  end
15070  return privatedata.nominalwidthx or 0,privatedata.defaultwidthx or 0
15071 end
15072 parsecharstrings=function(fontdata,data,glphs,doshapes,tversion,streams,nobias,istypeone)
15073  local dictionary=data.dictionaries[1]
15074  local charstrings=dictionary.charstrings
15075  keepcurve=doshapes
15076  version=tversion
15077  typeone=istypeone or false
15078  strings=data.strings
15079  globals=data.routines or {}
15080  locals=dictionary.subroutines or {}
15081  charset=dictionary.charset
15082  vsindex=dictionary.vsindex or 0
15083  local glyphs=glphs or {}
15084  globalbias,localbias=setbias(globals,locals,nobias)
15085  nominalwidth,defaultwidth=setwidths(dictionary.private)
15086  if charstrings then
15087   startparsing(fontdata,data,streams)
15088   for index=1,#charstrings do
15089    processshape(glyphs,charstrings[index],index-1)
15090   end
15091   if justpass and next(seacs) then
15092    local charset=data.dictionaries[1].charset
15093    if charset then
15094     local lookup=table.swapped(charset)
15095     for index,v in next,seacs do
15096      local bindex=lookup[standardnames[v.base]]
15097      local aindex=lookup[standardnames[v.accent]]
15098      local bglyph=bindex and glyphs[bindex]
15099      local aglyph=aindex and glyphs[aindex]
15100      if bglyph and aglyph then
15101       local jp=justpass
15102       justpass=false
15103       local x,y=processshape(glyphs,charstrings[bindex+1],bindex,true)
15104       justpass=jp
15105       local base=bglyph.stream
15106       local accent=aglyph.stream
15107       local moveto=encode[-x-v.asb+v.adx]..chars[22]..encode[-y+v.ady]..chars[ 4]
15108       base=sub(base,1,#base-1)
15109       glyphs[index].stream=base..moveto..accent
15110      end
15111     end
15112    end
15113   end
15114   stopparsing(fontdata,data)
15115  else
15116   report("no charstrings")
15117  end
15118  return glyphs
15119 end
15120 parsecharstring=function(fontdata,data,dictionary,tab,glphs,index,doshapes,tversion,streams)
15121  keepcurve=doshapes
15122  version=tversion
15123  strings=data.strings
15124  globals=data.routines or {}
15125  locals=dictionary.subroutines or {}
15126  charset=false
15127  vsindex=dictionary.vsindex or 0
15128  local glyphs=glphs or {}
15129  justpass=streams==true
15130  seacs={}
15131  globalbias,localbias=setbias(globals,locals,nobias)
15132  nominalwidth,defaultwidth=setwidths(dictionary.private)
15133  processshape(glyphs,tab,index-1)
15134  return glyphs[index]
15135 end
15136end
15137local function readglobals(f,data,version)
15138 local routines=readlengths(f,version=="cff2")
15139 for i=1,#routines do
15140  routines[i]=readbytetable(f,routines[i])
15141 end
15142 data.routines=routines
15143end
15144local function readencodings(f,data)
15145 data.encodings={}
15146end
15147local function readcharsets(f,data,dictionary)
15148 local header=data.header
15149 local strings=data.strings
15150 local nofglyphs=data.nofglyphs
15151 local charsetoffset=dictionary.charset
15152 if charsetoffset and charsetoffset~=0 then
15153  setposition(f,header.offset+charsetoffset)
15154  local format=readbyte(f)
15155  local charset={ [0]=".notdef" }
15156  dictionary.charset=charset
15157  if format==0 then
15158   for i=1,nofglyphs do
15159    charset[i]=strings[readushort(f)]
15160   end
15161  elseif format==1 or format==2 then
15162   local readcount=format==1 and readbyte or readushort
15163   local i=1
15164   while i<=nofglyphs do
15165    local sid=readushort(f)
15166    local n=readcount(f)
15167    for s=sid,sid+n do
15168     charset[i]=strings[s]
15169     i=i+1
15170     if i>nofglyphs then
15171      break
15172     end
15173    end
15174   end
15175  else
15176   report("cff parser: unsupported charset format %a",format)
15177  end
15178 else
15179  dictionary.nocharset=true
15180  dictionary.charset=nil
15181 end
15182end
15183local function readprivates(f,data)
15184 local header=data.header
15185 local dictionaries=data.dictionaries
15186 local private=dictionaries[1].private
15187 if private then
15188  setposition(f,header.offset+private.offset)
15189  private.data=readstring(f,private.size)
15190 end
15191end
15192local function readlocals(f,data,dictionary,version)
15193 local header=data.header
15194 local private=dictionary.private
15195 if private then
15196  local subroutineoffset=private.data.subroutines
15197  if subroutineoffset~=0 then
15198   setposition(f,header.offset+private.offset+subroutineoffset)
15199   local subroutines=readlengths(f,version=="cff2")
15200   for i=1,#subroutines do
15201    subroutines[i]=readbytetable(f,subroutines[i])
15202   end
15203   dictionary.subroutines=subroutines
15204   private.data.subroutines=nil
15205  else
15206   dictionary.subroutines={}
15207  end
15208 else
15209  dictionary.subroutines={}
15210 end
15211end
15212local function readcharstrings(f,data,version)
15213 local header=data.header
15214 local dictionaries=data.dictionaries
15215 local dictionary=dictionaries[1]
15216 local stringtype=dictionary.charstringtype
15217 local offset=dictionary.charstrings
15218 if type(offset)~="number" then
15219 elseif stringtype==2 then
15220  setposition(f,header.offset+offset)
15221  local charstrings=readlengths(f,version=="cff2")
15222  local nofglyphs=#charstrings
15223  for i=1,nofglyphs do
15224   charstrings[i]=readstring(f,charstrings[i])
15225  end
15226  data.nofglyphs=nofglyphs
15227  dictionary.charstrings=charstrings
15228 else
15229  report("unsupported charstr type %i",stringtype)
15230  data.nofglyphs=0
15231  dictionary.charstrings={}
15232 end
15233end
15234local function readcidprivates(f,data)
15235 local header=data.header
15236 local dictionaries=data.dictionaries[1].cid.dictionaries
15237 for i=1,#dictionaries do
15238  local dictionary=dictionaries[i]
15239  local private=dictionary.private
15240  if private then
15241   setposition(f,header.offset+private.offset)
15242   private.data=readstring(f,private.size)
15243  end
15244 end
15245 parseprivates(data,dictionaries)
15246end
15247readers.parsecharstrings=parsecharstrings 
15248local function readnoselect(f,fontdata,data,glyphs,doshapes,version,streams)
15249 local dictionaries=data.dictionaries
15250 local dictionary=dictionaries[1]
15251 local cid=not dictionary.private and dictionary.cid
15252 readglobals(f,data,version)
15253 readcharstrings(f,data,version)
15254 if version=="cff2" then
15255  dictionary.charset=nil
15256 else
15257  readencodings(f,data)
15258  readcharsets(f,data,dictionary)
15259 end
15260 if cid then
15261  local fdarray=cid.fdarray
15262  if fdarray then
15263   setposition(f,data.header.offset+fdarray)
15264   local dictionaries=readlengths(f,version=="cff2")
15265   local nofdictionaries=#dictionaries
15266   if nofdictionaries>0 then
15267    for i=1,nofdictionaries do
15268     dictionaries[i]=readstring(f,dictionaries[i])
15269    end
15270    parsedictionaries(data,dictionaries)
15271    dictionary.private=dictionaries[1].private
15272    if nofdictionaries>1 then
15273     report("ignoring dictionaries > 1 in cid font")
15274    end
15275   end
15276  end
15277 end
15278 readprivates(f,data)
15279 parseprivates(data,data.dictionaries)
15280 readlocals(f,data,dictionary,version)
15281 startparsing(fontdata,data,streams)
15282 parsecharstrings(fontdata,data,glyphs,doshapes,version,streams,false)
15283 stopparsing(fontdata,data)
15284end
15285local function readfdselect(f,fontdata,data,glyphs,doshapes,version,streams)
15286 local header=data.header
15287 local dictionaries=data.dictionaries
15288 local dictionary=dictionaries[1]
15289 local cid=dictionary.cid
15290 local cidselect=cid and cid.fdselect
15291 readglobals(f,data,version)
15292 readcharstrings(f,data,version)
15293 if version~="cff2" then
15294  readencodings(f,data)
15295 end
15296 local charstrings=dictionary.charstrings
15297 local fdindex={}
15298 local nofglyphs=data.nofglyphs
15299 local maxindex=-1
15300 setposition(f,header.offset+cidselect)
15301 local format=readbyte(f)
15302 if format==1 then
15303  for i=0,nofglyphs do 
15304   local index=readbyte(f)
15305   fdindex[i]=index
15306   if index>maxindex then
15307    maxindex=index
15308   end
15309  end
15310 elseif format==3 then
15311  local nofranges=readushort(f)
15312  local first=readushort(f)
15313  local index=readbyte(f)
15314  while true do
15315   local last=readushort(f)
15316   if index>maxindex then
15317    maxindex=index
15318   end
15319   for i=first,last do
15320    fdindex[i]=index
15321   end
15322   if last>=nofglyphs then
15323    break
15324   else
15325    first=last+1
15326    index=readbyte(f)
15327   end
15328  end
15329 else
15330  report("unsupported fd index format %i",format)
15331 end
15332 if maxindex>=0 then
15333  local cidarray=cid.fdarray
15334  if cidarray then
15335   setposition(f,header.offset+cidarray)
15336   local dictionaries=readlengths(f,version=="cff2")
15337   if #dictionaries>0 then
15338    for i=1,#dictionaries do
15339     dictionaries[i]=readstring(f,dictionaries[i])
15340    end
15341    parsedictionaries(data,dictionaries)
15342    cid.dictionaries=dictionaries
15343    readcidprivates(f,data)
15344    for i=1,#dictionaries do
15345     readlocals(f,data,dictionaries[i],version)
15346    end
15347    startparsing(fontdata,data,streams)
15348    for i=1,#charstrings do
15349     local dictionary=dictionaries[fdindex[i]+1]
15350     if dictionary then
15351      parsecharstring(fontdata,data,dictionary,charstrings[i],glyphs,i,doshapes,version,streams)
15352     else
15353     end
15354    end
15355    stopparsing(fontdata,data)
15356   else
15357    report("no cid dictionaries")
15358   end
15359  else
15360   report("no cid array")
15361  end
15362 end
15363end
15364local gotodatatable=readers.helpers.gotodatatable
15365local function cleanup(data,dictionaries)
15366end
15367function readers.cff(f,fontdata,specification)
15368 local tableoffset=gotodatatable(f,fontdata,"cff",specification.details or specification.glyphs)
15369 if tableoffset then
15370  local header=readheader(f)
15371  if header.major~=1 then
15372   report("only version %s is supported for table %a",1,"cff")
15373   return
15374  end
15375  local glyphs=fontdata.glyphs
15376  local names=readfontnames(f)
15377  local dictionaries=readtopdictionaries(f)
15378  local strings=readstrings(f)
15379  local data={
15380   header=header,
15381   names=names,
15382   dictionaries=dictionaries,
15383   strings=strings,
15384   nofglyphs=fontdata.nofglyphs,
15385  }
15386  parsedictionaries(data,dictionaries,"cff")
15387  local dic=dictionaries[1]
15388  local cid=dic.cid
15389  local cffinfo={
15390   familyname=dic.familyname,
15391   fullname=dic.fullname,
15392   boundingbox=dic.boundingbox,
15393   weight=dic.weight,
15394   italicangle=dic.italicangle,
15395   underlineposition=dic.underlineposition,
15396   underlinethickness=dic.underlinethickness,
15397   defaultwidth=dic.defaultwidthx,
15398   nominalwidth=dic.nominalwidthx,
15399   monospaced=dic.monospaced,
15400  }
15401  fontdata.cidinfo=cid and {
15402   registry=cid.registry,
15403   ordering=cid.ordering,
15404   supplement=cid.supplement,
15405  }
15406  fontdata.cffinfo=cffinfo
15407  local all=specification.shapes or specification.streams or false
15408  if specification.glyphs or all then
15409   if cid and cid.fdselect then
15410    readfdselect(f,fontdata,data,glyphs,all,"cff",specification.streams)
15411   else
15412    readnoselect(f,fontdata,data,glyphs,all,"cff",specification.streams)
15413   end
15414  end
15415  local private=dic.private
15416  if private then
15417   local data=private.data
15418   if type(data)=="table" then
15419    cffinfo.defaultwidth=data.defaultwidthx or cffinfo.defaultwidth
15420    cffinfo.nominalwidth=data.nominalwidthx or cffinfo.nominalwidth
15421    cffinfo.bluevalues=data.bluevalues
15422    cffinfo.otherblues=data.otherblues
15423    cffinfo.familyblues=data.familyblues
15424    cffinfo.familyotherblues=data.familyotherblues
15425    cffinfo.bluescale=data.bluescale
15426    cffinfo.blueshift=data.blueshift
15427    cffinfo.bluefuzz=data.bluefuzz
15428    cffinfo.stdhw=data.stdhw
15429    cffinfo.stdvw=data.stdvw
15430    cffinfo.stemsnaph=data.stemsnaph
15431    cffinfo.stemsnapv=data.stemsnapv
15432   end
15433  end
15434  cleanup(data,dictionaries)
15435 end
15436end
15437function readers.cff2(f,fontdata,specification)
15438 local tableoffset=gotodatatable(f,fontdata,"cff2",specification.glyphs)
15439 if tableoffset then
15440  local header=readheader(f)
15441  if header.major~=2 then
15442   report("only version %s is supported for table %a",2,"cff2")
15443   return
15444  end
15445  local glyphs=fontdata.glyphs
15446  local dictionaries={ readstring(f,header.dsize) }
15447  local data={
15448   header=header,
15449   dictionaries=dictionaries,
15450   nofglyphs=fontdata.nofglyphs,
15451  }
15452  parsedictionaries(data,dictionaries,"cff2")
15453  local offset=dictionaries[1].vstore
15454  if offset>0 then
15455   local storeoffset=dictionaries[1].vstore+data.header.offset+2 
15456   local regions,deltas=readers.helpers.readvariationdata(f,storeoffset,factors)
15457   data.regions=regions
15458   data.deltas=deltas
15459  else
15460   data.regions={}
15461   data.deltas={}
15462  end
15463  data.factors=specification.factors
15464  local cid=data.dictionaries[1].cid
15465  local all=specification.shapes or specification.streams or false
15466  if cid and cid.fdselect then
15467   readfdselect(f,fontdata,data,glyphs,all,"cff2",specification.streams)
15468  else
15469   readnoselect(f,fontdata,data,glyphs,all,"cff2",specification.streams)
15470  end
15471  cleanup(data,dictionaries)
15472 end
15473end
15474
15475end -- closure
15476
15477do -- begin closure to overcome local limits and interference
15478
15479if not modules then modules={} end modules ['font-ttf']={
15480 version=1.001,
15481 optimize=true,
15482 comment="companion to font-ini.mkiv",
15483 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
15484 copyright="PRAGMA ADE / ConTeXt Development Team",
15485 license="see context related readme files"
15486}
15487local next,type,unpack=next,type,unpack
15488local band,rshift=bit32.band,bit32.rshift
15489local sqrt,round,abs,min,max=math.sqrt,math.round,math.abs,math.min,math.max
15490local char,rep=string.char,string.rep
15491local concat=table.concat
15492local idiv=number.idiv
15493local setmetatableindex=table.setmetatableindex
15494local report=logs.reporter("otf reader","ttf")
15495local trace_deltas=false
15496local readers=fonts.handlers.otf.readers
15497local streamreader=readers.streamreader
15498local setposition=streamreader.setposition
15499local getposition=streamreader.getposition
15500local skipbytes=streamreader.skip
15501local readbyte=streamreader.readcardinal1  
15502local readushort=streamreader.readcardinal2  
15503local readulong=streamreader.readcardinal4  
15504local readchar=streamreader.readinteger1   
15505local readshort=streamreader.readinteger2   
15506local read2dot14=streamreader.read2dot14  
15507local readinteger=streamreader.readinteger1
15508local readcardinaltable=streamreader.readcardinaltable
15509local readintegertable=streamreader.readintegertable
15510directives.register("fonts.streamreader",function()
15511 streamreader=utilities.streams
15512 setposition=streamreader.setposition
15513 getposition=streamreader.getposition
15514 skipbytes=streamreader.skip
15515 readbyte=streamreader.readcardinal1
15516 readushort=streamreader.readcardinal2
15517 readulong=streamreader.readcardinal4
15518 readchar=streamreader.readinteger1
15519 readshort=streamreader.readinteger2
15520 read2dot14=streamreader.read2dot14
15521 readinteger=streamreader.readinteger1
15522 readcardinaltable=streamreader.readcardinaltable
15523 readintegertable=streamreader.readintegertable
15524end)
15525local short=2
15526local ushort=2
15527local ulong=4
15528local helpers=readers.helpers
15529local gotodatatable=helpers.gotodatatable
15530local function mergecomposites(glyphs,shapes)
15531 local function merge(index,shape,components)
15532  local contours={}
15533  local points={}
15534  local nofcontours=0
15535  local nofpoints=0
15536  local offset=0
15537  local deltas=shape.deltas
15538  for i=1,#components do
15539   local component=components[i]
15540   local subindex=component.index
15541   local subshape=shapes[subindex]
15542   local subcontours=subshape.contours
15543   local subpoints=subshape.points
15544   if not subcontours then
15545    local subcomponents=subshape.components
15546    if subcomponents then
15547     subcontours,subpoints=merge(subindex,subshape,subcomponents)
15548    end
15549   end
15550   if subpoints then
15551    local matrix=component.matrix
15552    local xscale=matrix[1]
15553    local xrotate=matrix[2]
15554    local yrotate=matrix[3]
15555    local yscale=matrix[4]
15556    local xoffset=matrix[5]
15557    local yoffset=matrix[6]
15558    local count=#subpoints
15559    if xscale==1 and yscale==1 and xrotate==0 and yrotate==0 then
15560     for i=1,count do
15561      local p=subpoints[i]
15562      nofpoints=nofpoints+1
15563      points[nofpoints]={
15564       p[1]+xoffset,
15565       p[2]+yoffset,
15566       p[3]
15567      }
15568     end
15569    else
15570     for i=1,count do
15571      local p=subpoints[i]
15572      local x=p[1]
15573      local y=p[2]
15574      nofpoints=nofpoints+1
15575      points[nofpoints]={
15576       xscale*x+xrotate*y+xoffset,
15577       yscale*y+yrotate*x+yoffset,
15578       p[3]
15579      }
15580     end
15581    end
15582    local subcount=#subcontours
15583    if subcount==1 then
15584     nofcontours=nofcontours+1
15585     contours[nofcontours]=offset+subcontours[1]
15586    else
15587     for i=1,#subcontours do
15588      nofcontours=nofcontours+1
15589      contours[nofcontours]=offset+subcontours[i]
15590     end
15591    end
15592    offset=offset+count
15593   else
15594    report("missing contours composite %s, component %s of %s, glyph %s",index,i,#components,subindex)
15595   end
15596  end
15597  shape.points=points 
15598  shape.contours=contours
15599  shape.components=nil
15600  return contours,points
15601 end
15602 for index=0,#glyphs do
15603  local shape=shapes[index]
15604  if shape then
15605   local components=shape.components
15606   if components then
15607    merge(index,shape,components)
15608   end
15609  end
15610 end
15611end
15612local function readnothing(f)
15613 return {
15614  type="nothing",
15615 }
15616end
15617local function curveto(m_x,m_y,l_x,l_y,r_x,r_y) 
15618 return
15619  l_x+2/3*(m_x-l_x),l_y+2/3*(m_y-l_y),
15620  r_x+2/3*(m_x-r_x),r_y+2/3*(m_y-r_y),
15621  r_x,r_y,"c"
15622end
15623local xv={} 
15624local yv={} 
15625local function applyaxis(glyph,shape,deltas,dowidth)
15626 local points=shape.points
15627 if points then
15628  local nofpoints=#points
15629  local dw=0
15630  local dl=0
15631  for i=1,#deltas do
15632   local deltaset=deltas[i]
15633   local xvalues=deltaset.xvalues
15634   local yvalues=deltaset.yvalues
15635   if xvalues and yvalues then
15636    local dpoints=deltaset.points
15637    local factor=deltaset.factor
15638    if dpoints then
15639     local cnt=#dpoints
15640     if dowidth then
15641      cnt=cnt-4
15642     end
15643     if cnt>0 then
15644      local contours=shape.contours
15645      local nofcontours=#contours
15646      local first=1
15647      local firstindex=1
15648      for contour=1,nofcontours do
15649       local last=contours[contour]
15650       if last>=first then
15651        local lastindex=cnt
15652        if firstindex<cnt then
15653         for currentindex=firstindex,cnt do
15654          local found=dpoints[currentindex]
15655          if found<=first then
15656           firstindex=currentindex
15657          end
15658          if found==last then
15659           lastindex=currentindex
15660           break
15661          elseif found>last then
15662           while lastindex>1 and dpoints[lastindex]>last do
15663            lastindex=lastindex-1
15664           end
15665           break
15666          end
15667         end
15668        end
15669        local function find(i)
15670         local prv=lastindex
15671         for j=firstindex,lastindex do
15672          local nxt=dpoints[j] 
15673          if nxt==i then
15674           return false,j,false
15675          elseif nxt>i then
15676           return prv,false,j
15677          end
15678          prv=j
15679         end
15680         return prv,false,firstindex
15681        end
15682        for point=first,last do
15683         local d1,d2,d3=find(point)
15684         local p2=points[point]
15685         if d2 then
15686          xv[point]=xvalues[d2]
15687          yv[point]=yvalues[d2]
15688         else
15689          local n1=dpoints[d1]
15690          local n3=dpoints[d3]
15691          if n1>nofpoints then
15692           n1=nofpoints
15693          end
15694          if n3>nofpoints then
15695           n3=nofpoints
15696          end
15697          local p1=points[n1]
15698          local p3=points[n3]
15699          local p1x=p1[1]
15700          local p2x=p2[1]
15701          local p3x=p3[1]
15702          local p1y=p1[2]
15703          local p2y=p2[2]
15704          local p3y=p3[2]
15705          local x1=xvalues[d1]
15706          local y1=yvalues[d1]
15707          local x3=xvalues[d3]
15708          local y3=yvalues[d3]
15709          local fx
15710          local fy
15711          if p1x==p3x then
15712           if x1==x3 then
15713            fx=x1
15714           else
15715            fx=0
15716           end
15717          elseif p2x<=min(p1x,p3x) then
15718           if p1x<p3x then
15719            fx=x1
15720           else
15721            fx=x3
15722           end
15723          elseif p2x>=max(p1x,p3x) then
15724           if p1x>p3x then
15725            fx=x1
15726           else
15727            fx=x3
15728           end
15729          else
15730           fx=(p2x-p1x)/(p3x-p1x)
15731           fx=(1-fx)*x1+fx*x3
15732          end
15733          if p1y==p3y then
15734           if y1==y3 then
15735            fy=y1
15736           else
15737            fy=0
15738           end
15739          elseif p2y<=min(p1y,p3y) then
15740           if p1y<p3y then
15741            fy=y1
15742           else
15743            fy=y3
15744           end
15745          elseif p2y>=max(p1y,p3y) then
15746           if p1y>p3y then
15747            fy=y1
15748           else
15749            fy=y3
15750           end
15751          else
15752           fy=(p2y-p1y)/(p3y-p1y)
15753           fy=(1-fy)*y1+fy*y3
15754          end
15755          xv[point]=fx
15756          yv[point]=fy
15757         end
15758        end
15759        if lastindex<cnt then
15760         firstindex=lastindex+1
15761        end
15762       end
15763       first=last+1
15764      end
15765      for i=1,nofpoints do
15766       local pi=points[i]
15767       local fx=xv[i]
15768       local fy=yv[i]
15769       if fx~=0 then
15770        pi[1]=pi[1]+factor*fx
15771       end
15772       if fy~=0 then
15773        pi[2]=pi[2]+factor*fy
15774       end
15775      end
15776     else
15777      report("bad deltapoint data, maybe a missing hvar table")
15778     end
15779    else
15780     for i=1,nofpoints do
15781      local p=points[i]
15782      local x=xvalues[i]
15783      if x then
15784       local y=yvalues[i]
15785       if x~=0 then
15786        p[1]=p[1]+factor*x
15787       end
15788       if y~=0 then
15789        p[2]=p[2]+factor*y
15790       end
15791      else
15792       break
15793      end
15794     end
15795    end
15796    if dowidth then
15797     local h=nofpoints+2 
15798     local l=nofpoints+1
15799     local x=xvalues[h]
15800     if x then
15801      dw=dw+factor*x
15802     end
15803     local x=xvalues[l]
15804     if x then
15805      dl=dl+factor*x
15806     end
15807    end
15808   end
15809  end
15810  if dowidth then
15811   local width=glyph.width or 0
15812   glyph.width=width+dw-dl
15813  end
15814 else
15815  report("no points for glyph %a",glyph.name)
15816 end
15817end
15818local quadratic=false
15819local function contours2outlines_normal(glyphs,shapes)
15820 for index=0,#glyphs-1 do
15821  local shape=shapes[index]
15822  if shape then
15823   local glyph=glyphs[index]
15824   local contours=shape.contours
15825   local points=shape.points
15826   if contours then
15827    local nofcontours=#contours
15828    local segments={}
15829    local nofsegments=0
15830    glyph.segments=segments
15831    if nofcontours>0 then
15832     local px=0
15833     local py=0
15834     local first=1
15835     for i=1,nofcontours do
15836      local last=contours[i]
15837      if last>=first then
15838       local first_pt=points[first]
15839       local first_on=first_pt[3]
15840       if first==last then
15841        first_pt[3]="m" 
15842        nofsegments=nofsegments+1
15843        segments[nofsegments]=first_pt
15844       else 
15845        local first_on=first_pt[3]
15846        local last_pt=points[last]
15847        local last_on=last_pt[3]
15848        local start=1
15849        local control_pt=false
15850        if first_on then
15851         start=2
15852        else
15853         if last_on then
15854          first_pt=last_pt
15855         else
15856          first_pt={ (first_pt[1]+last_pt[1])/2,(first_pt[2]+last_pt[2])/2,false }
15857         end
15858         control_pt=first_pt
15859        end
15860        local x=first_pt[1]
15861        local y=first_pt[2]
15862        if not done then
15863         xmin=x
15864         ymin=y
15865         xmax=x
15866         ymax=y
15867         done=true
15868        end
15869        nofsegments=nofsegments+1
15870        segments[nofsegments]={ x,y,"m" } 
15871        if not quadratic then
15872         px=x
15873         py=y
15874        end
15875        local previous_pt=first_pt
15876        for i=first,last do
15877         local current_pt=points[i]
15878         local current_on=current_pt[3]
15879         local previous_on=previous_pt[3]
15880         if previous_on then
15881          if current_on then
15882           local x,y=current_pt[1],current_pt[2]
15883           nofsegments=nofsegments+1
15884           segments[nofsegments]={ x,y,"l" } 
15885           if not quadratic then
15886            px,py=x,y
15887           end
15888          else
15889           control_pt=current_pt
15890          end
15891         elseif current_on then
15892          local x1=control_pt[1]
15893          local y1=control_pt[2]
15894          local x2=current_pt[1]
15895          local y2=current_pt[2]
15896          nofsegments=nofsegments+1
15897          if quadratic then
15898           segments[nofsegments]={ x1,y1,x2,y2,"q" } 
15899          else
15900           x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2)
15901           segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } 
15902          end
15903          control_pt=false
15904         else
15905          local x2=(previous_pt[1]+current_pt[1])/2
15906          local y2=(previous_pt[2]+current_pt[2])/2
15907          local x1=control_pt[1]
15908          local y1=control_pt[2]
15909          nofsegments=nofsegments+1
15910          if quadratic then
15911           segments[nofsegments]={ x1,y1,x2,y2,"q" } 
15912          else
15913           x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2)
15914           segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } 
15915          end
15916          control_pt=current_pt
15917         end
15918         previous_pt=current_pt
15919        end
15920        if first_pt==last_pt then
15921        else
15922         nofsegments=nofsegments+1
15923         local x2=first_pt[1]
15924         local y2=first_pt[2]
15925         if not control_pt then
15926          segments[nofsegments]={ x2,y2,"l" } 
15927         elseif quadratic then
15928          local x1=control_pt[1]
15929          local y1=control_pt[2]
15930          segments[nofsegments]={ x1,y1,x2,y2,"q" } 
15931         else
15932          local x1=control_pt[1]
15933          local y1=control_pt[2]
15934          x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2)
15935          segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" }
15936         end
15937        end
15938       end
15939      end
15940      first=last+1
15941     end
15942    end
15943   end
15944  end
15945 end
15946end
15947local function contours2outlines_shaped(glyphs,shapes,keepcurve)
15948 for index=0,#glyphs-1 do
15949  local shape=shapes[index]
15950  if shape then
15951   local glyph=glyphs[index]
15952   local contours=shape.contours
15953   local points=shape.points
15954   if contours then
15955    local nofcontours=#contours
15956    local segments=keepcurve and {} or nil
15957    local nofsegments=0
15958    if keepcurve then
15959     glyph.segments=segments
15960    end
15961    if nofcontours>0 then
15962     local xmin,ymin,xmax,ymax,done=0,0,0,0,false
15963     local px,py=0,0 
15964     local first=1
15965     for i=1,nofcontours do
15966      local last=contours[i]
15967      if last>=first then
15968       local first_pt=points[first]
15969       local first_on=first_pt[3]
15970       if first==last then
15971        if keepcurve then
15972         first_pt[3]="m" 
15973         nofsegments=nofsegments+1
15974         segments[nofsegments]=first_pt
15975        end
15976       else 
15977        local first_on=first_pt[3]
15978        local last_pt=points[last]
15979        local last_on=last_pt[3]
15980        local start=1
15981        local control_pt=false
15982        if first_on then
15983         start=2
15984        else
15985         if last_on then
15986          first_pt=last_pt
15987         else
15988          first_pt={ (first_pt[1]+last_pt[1])/2,(first_pt[2]+last_pt[2])/2,false }
15989         end
15990         control_pt=first_pt
15991        end
15992        local x=first_pt[1]
15993        local y=first_pt[2]
15994        if not done then
15995         xmin,ymin,xmax,ymax=x,y,x,y
15996         done=true
15997        else
15998         if x<xmin then xmin=x elseif x>xmax then xmax=x end
15999         if y<ymin then ymin=y elseif y>ymax then ymax=y end
16000        end
16001        if keepcurve then
16002         nofsegments=nofsegments+1
16003         segments[nofsegments]={ x,y,"m" } 
16004        end
16005        if not quadratic then
16006         px=x
16007         py=y
16008        end
16009        local previous_pt=first_pt
16010        for i=first,last do
16011         local current_pt=points[i]
16012         local current_on=current_pt[3]
16013         local previous_on=previous_pt[3]
16014         if previous_on then
16015          if current_on then
16016           local x=current_pt[1]
16017           local y=current_pt[2]
16018           if x<xmin then xmin=x elseif x>xmax then xmax=x end
16019           if y<ymin then ymin=y elseif y>ymax then ymax=y end
16020           if keepcurve then
16021            nofsegments=nofsegments+1
16022            segments[nofsegments]={ x,y,"l" } 
16023           end
16024           if not quadratic then
16025            px=x
16026            py=y
16027           end
16028          else
16029           control_pt=current_pt
16030          end
16031         elseif current_on then
16032          local x1=control_pt[1]
16033          local y1=control_pt[2]
16034          local x2=current_pt[1]
16035          local y2=current_pt[2]
16036          if quadratic then
16037           if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
16038           if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
16039           if keepcurve then
16040            nofsegments=nofsegments+1
16041            segments[nofsegments]={ x1,y1,x2,y2,"q" } 
16042           end
16043          else
16044           x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2)
16045           if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
16046           if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
16047           if x2<xmin then xmin=x2 elseif x2>xmax then xmax=x2 end
16048           if y2<ymin then ymin=y2 elseif y2>ymax then ymax=y2 end
16049           if px<xmin then xmin=px elseif px>xmax then xmax=px end
16050           if py<ymin then ymin=py elseif py>ymax then ymax=py end
16051           if keepcurve then
16052            nofsegments=nofsegments+1
16053            segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } 
16054           end
16055          end
16056          control_pt=false
16057         else
16058          local x2=(previous_pt[1]+current_pt[1])/2
16059          local y2=(previous_pt[2]+current_pt[2])/2
16060          local x1=control_pt[1]
16061          local y1=control_pt[2]
16062          if quadratic then
16063           if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
16064           if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
16065           if keepcurve then
16066            nofsegments=nofsegments+1
16067            segments[nofsegments]={ x1,y1,x2,y2,"q" } 
16068           end
16069          else
16070           x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2)
16071           if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
16072           if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
16073           if x2<xmin then xmin=x2 elseif x2>xmax then xmax=x2 end
16074           if y2<ymin then ymin=y2 elseif y2>ymax then ymax=y2 end
16075           if px<xmin then xmin=px elseif px>xmax then xmax=px end
16076           if py<ymin then ymin=py elseif py>ymax then ymax=py end
16077           if keepcurve then
16078            nofsegments=nofsegments+1
16079            segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } 
16080           end
16081          end
16082          control_pt=current_pt
16083         end
16084         previous_pt=current_pt
16085        end
16086        if first_pt==last_pt then
16087        elseif not control_pt then
16088         if keepcurve then
16089          nofsegments=nofsegments+1
16090          segments[nofsegments]={ first_pt[1],first_pt[2],"l" } 
16091         end
16092        else
16093         local x1=control_pt[1]
16094         local y1=control_pt[2]
16095         local x2=first_pt[1]
16096         local y2=first_pt[2]
16097         if x1<xmin then xmin=x1 elseif x1>xmax then xmax=x1 end
16098         if y1<ymin then ymin=y1 elseif y1>ymax then ymax=y1 end
16099         if quadratic then
16100          if keepcurve then
16101           nofsegments=nofsegments+1
16102           segments[nofsegments]={ x1,y1,x2,y2,"q" } 
16103          end
16104         else
16105          x1,y1,x2,y2,px,py=curveto(x1,y1,px,py,x2,y2)
16106          if x2<xmin then xmin=x2 elseif x2>xmax then xmax=x2 end
16107          if y2<ymin then ymin=y2 elseif y2>ymax then ymax=y2 end
16108          if px<xmin then xmin=px elseif px>xmax then xmax=px end
16109          if py<ymin then ymin=py elseif py>ymax then ymax=py end
16110          if keepcurve then
16111           nofsegments=nofsegments+1
16112           segments[nofsegments]={ x1,y1,x2,y2,px,py,"c" } 
16113          end
16114         end
16115        end
16116       end
16117      end
16118      first=last+1
16119     end
16120     xmin=glyph.boundingbox[1]
16121     local dlsb=glyph.dlsb
16122     if dlsb then
16123      xmin=xmin+dlsb
16124      glyph.dlsb=nil 
16125     end
16126     glyph.boundingbox={ round(xmin),round(ymin),round(xmax),round(ymax) }
16127    end
16128   end
16129  end
16130 end
16131end
16132local c_zero=char(0)
16133local s_zero=char(0,0)
16134local function toushort(n)
16135 return char(band(rshift(n,8),0xFF),band(n,0xFF))
16136end
16137local function toshort(n)
16138 if n<0 then
16139  n=n+0x10000
16140 end
16141 return char(band(rshift(n,8),0xFF),band(n,0xFF))
16142end
16143local chars=setmetatableindex(function(t,k)
16144 for i=0,255 do local v=char(i) t[i]=v end return t[k]
16145end)
16146local function repackpoints(glyphs,shapes)
16147 local noboundingbox={ 0,0,0,0 }
16148 local result={} 
16149 local xpoints={} 
16150 local ypoints={} 
16151 for index=0,#glyphs do
16152  local shape=shapes[index]
16153  if shape then
16154   local r=0
16155   local glyph=glyphs[index]
16156   local contours=shape.contours
16157   local nofcontours=contours and #contours or 0
16158   local boundingbox=glyph.boundingbox or noboundingbox
16159   r=r+1 result[r]=toshort(nofcontours)
16160   r=r+1 result[r]=toshort(boundingbox[1]) 
16161   r=r+1 result[r]=toshort(boundingbox[2]) 
16162   r=r+1 result[r]=toshort(boundingbox[3]) 
16163   r=r+1 result[r]=toshort(boundingbox[4]) 
16164   if nofcontours>0 then
16165    for i=1,nofcontours do
16166     r=r+1 result[r]=toshort(contours[i]-1)
16167    end
16168    r=r+1 result[r]=s_zero 
16169    local points=shape.points
16170    local currentx=0
16171    local currenty=0
16172    local x=0
16173    local y=0
16174    local lastflag=nil
16175    local nofflags=0
16176    for i=1,#points do
16177     local pt=points[i]
16178     local px=pt[1]
16179     local py=pt[2]
16180     local fl=pt[3] and 0x01 or 0x00
16181     if px==currentx then
16182      fl=fl+0x10
16183     else
16184      local dx=round(px-currentx)
16185      x=x+1
16186      if dx<-255 or dx>255 then
16187       xpoints[x]=toshort(dx)
16188      elseif dx<0 then
16189       fl=fl+0x02
16190       xpoints[x]=chars[-dx]
16191      elseif dx>0 then
16192       fl=fl+0x12
16193       xpoints[x]=chars[dx]
16194      else
16195       fl=fl+0x02
16196       xpoints[x]=c_zero
16197      end
16198     end
16199     if py==currenty then
16200      fl=fl+0x20
16201     else
16202      local dy=round(py-currenty)
16203      y=y+1
16204      if dy<-255 or dy>255 then
16205       ypoints[y]=toshort(dy)
16206      elseif dy<0 then
16207       fl=fl+0x04
16208       ypoints[y]=chars[-dy]
16209      elseif dy>0 then
16210       fl=fl+0x24
16211       ypoints[y]=chars[dy]
16212      else
16213       fl=fl+0x04
16214       ypoints[y]=c_zero
16215      end
16216     end
16217     currentx=px
16218     currenty=py
16219     if lastflag==fl then
16220      if nofflags==255 then
16221       lastflag=lastflag+0x08
16222       r=r+1 result[r]=char(lastflag,nofflags-1)
16223       nofflags=1
16224       lastflag=fl
16225      else
16226       nofflags=nofflags+1
16227      end
16228     else 
16229      if nofflags==1 then
16230       r=r+1 result[r]=chars[lastflag]
16231      elseif nofflags==2 then
16232       r=r+1 result[r]=char(lastflag,lastflag)
16233      elseif nofflags>2 then
16234       lastflag=lastflag+0x08
16235       r=r+1 result[r]=char(lastflag,nofflags-1)
16236      end
16237      nofflags=1
16238      lastflag=fl
16239     end
16240    end
16241    if nofflags==1 then
16242     r=r+1 result[r]=chars[lastflag]
16243    elseif nofflags==2 then
16244     r=r+1 result[r]=char(lastflag,lastflag)
16245    elseif nofflags>2 then
16246     lastflag=lastflag+0x08
16247     r=r+1 result[r]=char(lastflag,nofflags-1)
16248    end
16249    r=r+1 result[r]=concat(xpoints,"",1,x)
16250    r=r+1 result[r]=concat(ypoints,"",1,y)
16251   end
16252   local stream=concat(result,"",1,r)
16253   local length=#stream
16254   local padding=idiv(length+3,4)*4-length
16255   if padding>0 then
16256    if padding==1 then
16257     padding="\0"
16258    elseif padding==2 then
16259     padding="\0\0"
16260    else
16261     padding="\0\0\0"
16262    end
16263    padding=stream..padding
16264   end
16265   glyph.stream=stream
16266  end
16267 end
16268end
16269local flags={}
16270local function readglyph(f,nofcontours) 
16271 local points={}
16272 local contours={} 
16273 for i=1,nofcontours do
16274  contours[i]=readshort(f)+1
16275 end
16276 local nofpoints=contours[nofcontours]
16277 local nofinstructions=readushort(f)
16278 skipbytes(f,nofinstructions)
16279 local i=1
16280 while i<=nofpoints do
16281  local flag=readbyte(f)
16282  flags[i]=flag
16283  if band(flag,0x08)~=0 then
16284   local n=readbyte(f)
16285   if n==1 then
16286    i=i+1
16287    flags[i]=flag
16288   else
16289    for j=1,n do
16290     i=i+1
16291     flags[i]=flag
16292    end
16293   end
16294  end
16295  i=i+1
16296 end
16297 local x=0
16298 for i=1,nofpoints do
16299  local flag=flags[i]
16300  if band(flag,0x02)~=0 then
16301   if band(flag,0x10)~=0 then
16302    x=x+readbyte(f)
16303   else
16304    x=x-readbyte(f)
16305   end
16306  elseif band(flag,0x10)~=0 then
16307  else
16308   x=x+readshort(f)
16309  end
16310  points[i]={ x,0,band(flag,0x01)~=0 }
16311 end
16312 local y=0
16313 for i=1,nofpoints do
16314  local flag=flags[i]
16315  if band(flag,0x04)~=0 then
16316   if band(flag,0x20)~=0 then
16317    y=y+readbyte(f)
16318   else
16319    y=y-readbyte(f)
16320   end
16321  elseif band(flag,0x20)~=0 then
16322  else
16323   y=y+readshort(f)
16324  end
16325  points[i][2]=y
16326 end
16327 return {
16328  type="glyph",
16329  points=points,
16330  contours=contours,
16331  nofpoints=nofpoints,
16332 }
16333end
16334local function readcomposite(f)
16335 local components={}
16336 local nofcomponents=0
16337 local instructions=false
16338 while true do
16339  local flags=readushort(f)
16340  local index=readushort(f)
16341  local f_xyarg=band(flags,0x0002)~=0
16342  local f_offset=band(flags,0x0800)~=0
16343  local xscale=1
16344  local xrotate=0
16345  local yrotate=0
16346  local yscale=1
16347  local xoffset=0
16348  local yoffset=0
16349  local base=false
16350  local reference=false
16351  if f_xyarg then
16352   if band(flags,0x0001)~=0 then 
16353    xoffset=readshort(f)
16354    yoffset=readshort(f)
16355   else
16356    xoffset=readchar(f) 
16357    yoffset=readchar(f) 
16358   end
16359  else
16360   if band(flags,0x0001)~=0 then 
16361    base=readshort(f)
16362    reference=readshort(f)
16363   else
16364    base=readchar(f) 
16365    reference=readchar(f) 
16366   end
16367  end
16368  if band(flags,0x0008)~=0 then 
16369   xscale=read2dot14(f)
16370   yscale=xscale
16371   if f_xyarg and f_offset then
16372    xoffset=xoffset*xscale
16373    yoffset=yoffset*yscale
16374   end
16375  elseif band(flags,0x0040)~=0 then 
16376   xscale=read2dot14(f)
16377   yscale=read2dot14(f)
16378   if f_xyarg and f_offset then
16379    xoffset=xoffset*xscale
16380    yoffset=yoffset*yscale
16381   end
16382  elseif band(flags,0x0080)~=0 then 
16383   xscale=read2dot14(f) 
16384   xrotate=read2dot14(f) 
16385   yrotate=read2dot14(f) 
16386   yscale=read2dot14(f) 
16387   if f_xyarg and f_offset then
16388    xoffset=xoffset*sqrt(xscale^2+yrotate^2) 
16389    yoffset=yoffset*sqrt(xrotate^2+yscale^2) 
16390   end
16391  end
16392  nofcomponents=nofcomponents+1
16393  components[nofcomponents]={
16394   index=index,
16395   usemine=band(flags,0x0200)~=0,
16396   round=band(flags,0x0006)~=0,
16397   base=base,
16398   reference=reference,
16399   matrix={ xscale,xrotate,yrotate,yscale,xoffset,yoffset },
16400  }
16401  if band(flags,0x0100)~=0 then
16402   instructions=true
16403  end
16404  if band(flags,0x0020)==0 then 
16405   break
16406  end
16407 end
16408 return {
16409  type="composite",
16410  components=components,
16411 }
16412end
16413function readers.loca(f,fontdata,specification)
16414 if specification.glyphs then
16415  local datatable=fontdata.tables.loca
16416  if datatable then
16417   local offset=fontdata.tables.glyf.offset
16418   local format=fontdata.fontheader.indextolocformat
16419   local profile=fontdata.maximumprofile
16420   local nofglyphs=profile and profile.nofglyphs
16421   local locations={}
16422   setposition(f,datatable.offset)
16423   if format==1 then
16424    if not nofglyphs then
16425     nofglyphs=idiv(datatable.length,4)-1
16426    end
16427    for i=0,nofglyphs do
16428     locations[i]=offset+readulong(f)
16429    end
16430    fontdata.nofglyphs=nofglyphs
16431   else
16432    if not nofglyphs then
16433     nofglyphs=idiv(datatable.length,2)-1
16434    end
16435    for i=0,nofglyphs do
16436     locations[i]=offset+readushort(f)*2
16437    end
16438   end
16439   fontdata.nofglyphs=nofglyphs
16440   fontdata.locations=locations
16441  end
16442 end
16443end
16444function readers.glyf(f,fontdata,specification) 
16445 local tableoffset=gotodatatable(f,fontdata,"glyf",specification.glyphs)
16446 if tableoffset then
16447  local locations=fontdata.locations
16448  if locations then
16449   local glyphs=fontdata.glyphs
16450   local nofglyphs=fontdata.nofglyphs
16451   local filesize=fontdata.filesize
16452   local nothing={ 0,0,0,0 }
16453   local shapes={}
16454   local loadshapes=specification.shapes or specification.instance or specification.streams
16455   for index=0,nofglyphs-1 do
16456    local location=locations[index]
16457    local length=locations[index+1]-location
16458    if location>=filesize then
16459     report("discarding %s glyphs due to glyph location bug",nofglyphs-index+1)
16460     fontdata.nofglyphs=index-1
16461     fontdata.badfont=true
16462     break
16463    elseif length>0 then
16464     setposition(f,location)
16465     local nofcontours=readshort(f)
16466     glyphs[index].boundingbox={
16467      readshort(f),
16468      readshort(f),
16469      readshort(f),
16470      readshort(f),
16471     }
16472     if not loadshapes then
16473     elseif nofcontours==0 then
16474      shapes[index]=readnothing(f)
16475     elseif nofcontours>0 then
16476      shapes[index]=readglyph(f,nofcontours)
16477     else
16478      shapes[index]=readcomposite(f,nofcontours)
16479     end
16480    else
16481     if loadshapes then
16482      shapes[index]=readnothing(f)
16483     end
16484     glyphs[index].boundingbox=nothing
16485    end
16486   end
16487   if loadshapes then
16488    if readers.gvar then
16489     readers.gvar(f,fontdata,specification,glyphs,shapes)
16490    end
16491    mergecomposites(glyphs,shapes)
16492    if specification.instance then
16493     if specification.streams then
16494      repackpoints(glyphs,shapes)
16495     else
16496      contours2outlines_shaped(glyphs,shapes,specification.shapes)
16497     end
16498    elseif specification.shapes then
16499     if specification.streams then
16500      repackpoints(glyphs,shapes)
16501     else
16502      contours2outlines_normal(glyphs,shapes)
16503     end
16504    elseif specification.streams then
16505     repackpoints(glyphs,shapes)
16506    end
16507   end
16508  end
16509 end
16510end
16511local function readtuplerecord(f,nofaxis)
16512 local record={}
16513 for i=1,nofaxis do
16514  record[i]=read2dot14(f)
16515 end
16516 return record
16517end
16518local function readpoints(f)
16519 local count=readbyte(f)
16520 if count==0 then
16521  return nil,0 
16522 else
16523  if count<128 then
16524  elseif band(count,0x80)~=0 then
16525   count=band(count,0x7F)*256+readbyte(f)
16526  else
16527  end
16528  local points={}
16529  local p=0
16530  local n=1 
16531  while p<count do
16532   local control=readbyte(f)
16533   local runreader=band(control,0x80)~=0 and readushort or readbyte
16534   local runlength=band(control,0x7F)
16535   for i=1,runlength+1 do
16536    n=n+runreader(f)
16537    p=p+1
16538    points[p]=n
16539   end
16540  end
16541  return points,p
16542 end
16543end
16544local function readdeltas(f,nofpoints)
16545 local deltas={}
16546 local p=0
16547 while nofpoints>0 do
16548  local control=readbyte(f)
16549  if control then
16550   local allzero=band(control,0x80)~=0
16551   local runlength=band(control,0x3F)+1
16552   if allzero then
16553    for i=1,runlength do
16554     p=p+1
16555     deltas[p]=0
16556    end
16557   else
16558    local runreader=band(control,0x40)~=0 and readshort or readinteger
16559    for i=1,runlength do
16560     p=p+1
16561     deltas[p]=runreader(f)
16562    end
16563   end
16564   nofpoints=nofpoints-runlength
16565  else
16566   break
16567  end
16568 end
16569 if p>0 then
16570  return deltas
16571 else
16572 end
16573end
16574function readers.gvar(f,fontdata,specification,glyphdata,shapedata)
16575 local instance=specification.instance
16576 if not instance then
16577  return
16578 end
16579 local factors=specification.factors
16580 if not factors then
16581  return
16582 end
16583 local tableoffset=gotodatatable(f,fontdata,"gvar",specification.variable or specification.shapes)
16584 if tableoffset then
16585  local version=readulong(f) 
16586  local nofaxis=readushort(f)
16587  local noftuples=readushort(f)
16588  local tupleoffset=tableoffset+readulong(f)
16589  local nofglyphs=readushort(f)
16590  local flags=readushort(f)
16591  local dataoffset=tableoffset+readulong(f)
16592  local data={}
16593  local tuples={}
16594  local glyphdata=fontdata.glyphs
16595  local dowidth=not fontdata.variabledata.hvarwidths
16596  if band(flags,0x0001)~=0  then
16597   for i=1,nofglyphs+1 do
16598    data[i]=dataoffset+readulong(f)
16599   end
16600  else
16601   for i=1,nofglyphs+1 do
16602    data[i]=dataoffset+2*readushort(f)
16603   end
16604  end
16605  if noftuples>0 then
16606   setposition(f,tupleoffset)
16607   for i=1,noftuples do
16608    tuples[i]=readtuplerecord(f,nofaxis)
16609   end
16610  end
16611  local nextoffset=false
16612  local startoffset=data[1]
16613  for i=1,nofglyphs do 
16614   nextoffset=data[i+1]
16615   local glyph=glyphdata[i-1]
16616   local name=trace_deltas and glyph.name
16617   if startoffset==nextoffset then
16618    if name then
16619     report("no deltas for glyph %a",name)
16620    end
16621   else
16622    local shape=shapedata[i-1] 
16623    if not shape then
16624     if name then
16625      report("no shape for glyph %a",name)
16626     end
16627    else
16628     lastoffset=startoffset
16629     setposition(f,startoffset)
16630     local flags=readushort(f)
16631     local count=band(flags,0x0FFF)
16632     local offset=startoffset+readushort(f) 
16633     local deltas={}
16634     local allpoints=(shape.nofpoints or 0) 
16635     local shared=false
16636     local nofshared=0
16637     if band(flags,0x8000)~=0  then
16638      local current=getposition(f)
16639      setposition(f,offset)
16640      shared,nofshared=readpoints(f)
16641      offset=getposition(f)
16642      setposition(f,current)
16643     end
16644     for j=1,count do
16645      local size=readushort(f) 
16646      local flags=readushort(f)
16647      local index=band(flags,0x0FFF)
16648      local haspeak=band(flags,0x8000)~=0
16649      local intermediate=band(flags,0x4000)~=0
16650      local private=band(flags,0x2000)~=0
16651      local peak=nil
16652      local start=nil
16653      local stop=nil
16654      local xvalues=nil
16655      local yvalues=nil
16656      local points=shared 
16657      local nofpoints=nofshared
16658      if haspeak then
16659       peak=readtuplerecord(f,nofaxis)
16660      else
16661       if index+1>#tuples then
16662        report("error, bad tuple index",index)
16663       end
16664       peak=tuples[index+1] 
16665      end
16666      if intermediate then
16667       start=readtuplerecord(f,nofaxis)
16668       stop=readtuplerecord(f,nofaxis)
16669      end
16670      if size>0 then
16671       local current=getposition(f)
16672       setposition(f,offset)
16673       if private then
16674        points,nofpoints=readpoints(f)
16675       end 
16676       if nofpoints==0 then
16677        nofpoints=allpoints+4
16678       end
16679       if nofpoints>0 then
16680        xvalues=readdeltas(f,nofpoints)
16681        yvalues=readdeltas(f,nofpoints)
16682       end
16683       offset=offset+size
16684       setposition(f,current)
16685      end
16686      if not xvalues and not yvalues then
16687       points=nil
16688      end
16689      local s=1
16690      for i=1,nofaxis do
16691       local f=factors[i]
16692       local peak=peak  and peak [i] or 0
16693       local start=start and start[i] or (peak<0 and peak or 0)
16694       local stop=stop  and stop [i] or (peak>0 and peak or 0)
16695       if start>peak or peak>stop then
16696       elseif start<0 and stop>0 and peak~=0 then
16697       elseif peak==0 then
16698       elseif f<start or f>stop then
16699        s=0
16700        break
16701       elseif f<peak then
16702        s=s*(f-start)/(peak-start)
16703       elseif f>peak then
16704        s=s*(stop-f)/(stop-peak)
16705       else
16706       end
16707      end
16708      if s==0 then
16709       if name then
16710        report("no deltas applied for glyph %a",name)
16711       end
16712      else
16713       deltas[#deltas+1]={
16714        factor=s,
16715        points=points,
16716        xvalues=xvalues,
16717        yvalues=yvalues,
16718       }
16719      end
16720     end
16721     if shape.type=="glyph" then
16722      applyaxis(glyph,shape,deltas,dowidth)
16723     else
16724      shape.deltas=deltas
16725     end
16726    end
16727   end
16728   startoffset=nextoffset
16729  end
16730 end
16731end
16732
16733end -- closure
16734
16735do -- begin closure to overcome local limits and interference
16736
16737if not modules then modules={} end modules ['font-dsp']={
16738 version=1.001,
16739 optimize=true,
16740 comment="companion to font-ini.mkiv",
16741 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
16742 copyright="PRAGMA ADE / ConTeXt Development Team",
16743 license="see context related readme files"
16744}
16745local next,type,tonumber=next,type,tonumber
16746local band=bit32.band
16747local extract=bit32.extract
16748local bor=bit32.bor
16749local lshift=bit32.lshift
16750local rshift=bit32.rshift
16751local gsub=string.gsub
16752local lower=string.lower
16753local sub=string.sub
16754local strip=string.strip
16755local tohash=table.tohash
16756local concat=table.concat
16757local copy=table.copy
16758local reversed=table.reversed
16759local sort=table.sort
16760local insert=table.insert
16761local round=math.round
16762local settings_to_hash=utilities.parsers.settings_to_hash_colon_too
16763local setmetatableindex=table.setmetatableindex
16764local formatters=string.formatters
16765local sortedkeys=table.sortedkeys
16766local sortedhash=table.sortedhash
16767local sequenced=table.sequenced
16768local report=logs.reporter("otf reader")
16769local readers=fonts.handlers.otf.readers
16770local streamreader=readers.streamreader
16771local setposition=streamreader.setposition
16772local getposition=streamreader.getposition
16773local readuinteger=streamreader.readcardinal1
16774local readushort=streamreader.readcardinal2
16775local readulong=streamreader.readcardinal4
16776local readinteger=streamreader.readinteger1
16777local readshort=streamreader.readinteger2
16778local readstring=streamreader.readstring
16779local readtag=streamreader.readtag
16780local readbytes=streamreader.readbytes
16781local readfixed=streamreader.readfixed4
16782local read2dot14=streamreader.read2dot14
16783local skipshort=streamreader.skipshort
16784local skipbytes=streamreader.skip
16785local readbytetable=streamreader.readbytetable
16786local readbyte=streamreader.readbyte
16787local readcardinaltable=streamreader.readcardinaltable
16788local readintegertable=streamreader.readintegertable
16789local readfword=readshort
16790local short=2
16791local ushort=2
16792local ulong=4
16793directives.register("fonts.streamreader",function()
16794 streamreader=utilities.streams
16795 setposition=streamreader.setposition
16796 getposition=streamreader.getposition
16797 readuinteger=streamreader.readcardinal1
16798 readushort=streamreader.readcardinal2
16799 readulong=streamreader.readcardinal4
16800 readinteger=streamreader.readinteger1
16801 readshort=streamreader.readinteger2
16802 readstring=streamreader.readstring
16803 readtag=streamreader.readtag
16804 readbytes=streamreader.readbytes
16805 readfixed=streamreader.readfixed4
16806 read2dot14=streamreader.read2dot14
16807 skipshort=streamreader.skipshort
16808 skipbytes=streamreader.skip
16809 readbytetable=streamreader.readbytetable
16810 readbyte=streamreader.readbyte
16811 readcardinaltable=streamreader.readcardinaltable
16812 readintegertable=streamreader.readintegertable
16813 readfword=readshort
16814end)
16815local gsubhandlers={}
16816local gposhandlers={}
16817readers.gsubhandlers=gsubhandlers
16818readers.gposhandlers=gposhandlers
16819local helpers=readers.helpers
16820local gotodatatable=helpers.gotodatatable
16821local setvariabledata=helpers.setvariabledata
16822local lookupidoffset=-1 
16823local classes={
16824 "base",
16825 "ligature",
16826 "mark",
16827 "component",
16828}
16829local gsubtypes={
16830 "single",
16831 "multiple",
16832 "alternate",
16833 "ligature",
16834 "context",
16835 "chainedcontext",
16836 "extension",
16837 "reversechainedcontextsingle",
16838}
16839local gpostypes={
16840 "single",
16841 "pair",
16842 "cursive",
16843 "marktobase",
16844 "marktoligature",
16845 "marktomark",
16846 "context",
16847 "chainedcontext",
16848 "extension",
16849}
16850local chaindirections={
16851 context=0,
16852 chainedcontext=1,
16853 reversechainedcontextsingle=-1,
16854}
16855local function setmetrics(data,where,tag,d)
16856 local w=data[where]
16857 if w then
16858  local v=w[tag]
16859  if v then
16860   w[tag]=v+d
16861  end
16862 end
16863end
16864local variabletags={
16865 hasc=function(data,d) setmetrics(data,"windowsmetrics","typoascender",d) end,
16866 hdsc=function(data,d) setmetrics(data,"windowsmetrics","typodescender",d) end,
16867 hlgp=function(data,d) setmetrics(data,"windowsmetrics","typolinegap",d) end,
16868 hcla=function(data,d) setmetrics(data,"windowsmetrics","winascent",d) end,
16869 hcld=function(data,d) setmetrics(data,"windowsmetrics","windescent",d) end,
16870 vasc=function(data,d) setmetrics(data,"vhea not done","ascent",d) end,
16871 vdsc=function(data,d) setmetrics(data,"vhea not done","descent",d) end,
16872 vlgp=function(data,d) setmetrics(data,"vhea not done","linegap",d) end,
16873 xhgt=function(data,d) setmetrics(data,"windowsmetrics","xheight",d) end,
16874 cpht=function(data,d) setmetrics(data,"windowsmetrics","capheight",d) end,
16875 sbxs=function(data,d) setmetrics(data,"windowsmetrics","subscriptxsize",d) end,
16876 sbys=function(data,d) setmetrics(data,"windowsmetrics","subscriptysize",d) end,
16877 sbxo=function(data,d) setmetrics(data,"windowsmetrics","subscriptxoffset",d) end,
16878 sbyo=function(data,d) setmetrics(data,"windowsmetrics","subscriptyoffset",d) end,
16879 spxs=function(data,d) setmetrics(data,"windowsmetrics","superscriptxsize",d) end,
16880 spys=function(data,d) setmetrics(data,"windowsmetrics","superscriptysize",d) end,
16881 spxo=function(data,d) setmetrics(data,"windowsmetrics","superscriptxoffset",d) end,
16882 spyo=function(data,d) setmetrics(data,"windowsmetrics","superscriptyoffset",d) end,
16883 strs=function(data,d) setmetrics(data,"windowsmetrics","strikeoutsize",d) end,
16884 stro=function(data,d) setmetrics(data,"windowsmetrics","strikeoutpos",d) end,
16885 unds=function(data,d) setmetrics(data,"postscript","underlineposition",d) end,
16886 undo=function(data,d) setmetrics(data,"postscript","underlinethickness",d) end,
16887}
16888local read_cardinal={
16889 streamreader.readcardinal1,
16890 streamreader.readcardinal2,
16891 streamreader.readcardinal3,
16892 streamreader.readcardinal4,
16893}
16894local read_integer={
16895 streamreader.readinteger1,
16896 streamreader.readinteger2,
16897 streamreader.readinteger3,
16898 streamreader.readinteger4,
16899}
16900directives.register("fonts.streamreader",function()
16901 read_cardinal={
16902  streamreader.readcardinal1,
16903  streamreader.readcardinal2,
16904  streamreader.readcardinal3,
16905  streamreader.readcardinal4,
16906 }
16907 read_integer={
16908  streamreader.readinteger1,
16909  streamreader.readinteger2,
16910  streamreader.readinteger3,
16911  streamreader.readinteger4,
16912 }
16913end)
16914local lookupnames={
16915 gsub={
16916  single="gsub_single",
16917  multiple="gsub_multiple",
16918  alternate="gsub_alternate",
16919  ligature="gsub_ligature",
16920  context="gsub_context",
16921  chainedcontext="gsub_contextchain",
16922  reversechainedcontextsingle="gsub_reversecontextchain",
16923 },
16924 gpos={
16925  single="gpos_single",
16926  pair="gpos_pair",
16927  cursive="gpos_cursive",
16928  marktobase="gpos_mark2base",
16929  marktoligature="gpos_mark2ligature",
16930  marktomark="gpos_mark2mark",
16931  context="gpos_context",
16932  chainedcontext="gpos_contextchain",
16933 }
16934}
16935local lookupflags=setmetatableindex(function(t,k)
16936 local v={
16937  band(k,0x0008)~=0 and true or false,
16938  band(k,0x0004)~=0 and true or false,
16939  band(k,0x0002)~=0 and true or false,
16940  band(k,0x0001)~=0 and true or false,
16941 }
16942 t[k]=v
16943 return v
16944end)
16945local function axistofactors(str)
16946 local t=settings_to_hash(str)
16947 for k,v in next,t do
16948  t[k]=tonumber(v) or v 
16949 end
16950 return t
16951end
16952local hash=table.setmetatableindex(function(t,k)
16953 local v=sequenced(axistofactors(k),",")
16954 t[k]=v
16955 return v
16956end)
16957helpers.normalizedaxishash=hash
16958local cleanname=fonts.names and fonts.names.cleanname or function(name)
16959 return name and (gsub(lower(name),"[^%a%d]","")) or nil
16960end
16961helpers.cleanname=cleanname
16962function helpers.normalizedaxis(str)
16963 return hash[str] or str
16964end
16965local function getaxisscale(segments,minimum,default,maximum,user)
16966 if not minimum or not default or not maximum then
16967  return false
16968 end
16969 if user<minimum then
16970  user=minimum
16971 elseif user>maximum then
16972  user=maximum
16973 end
16974 if user<default then
16975  default=- (default-user)/(default-minimum)
16976 elseif user>default then
16977  default=(user-default)/(maximum-default)
16978 else
16979  default=0
16980 end
16981 if not segments then
16982  return default
16983 end
16984 local e
16985 for i=1,#segments do
16986  local s=segments[i]
16987  if type(s)~="number" then
16988   return default
16989  elseif s[1]>=default then
16990   if s[2]==default then
16991    return default
16992   else
16993    e=i
16994    break
16995   end
16996  end
16997 end
16998 if e then
16999  local b=segments[e-1]
17000  local e=segments[e]
17001  return b[2]+(e[2]-b[2])*(default-b[1])/(e[1]-b[1])
17002 else
17003  return false
17004 end
17005end
17006local function getfactors(data,instancespec)
17007 if instancespec==true then
17008 elseif type(instancespec)~="string" or instancespec=="" then
17009  return
17010 end
17011 local variabledata=data.variabledata
17012 if not variabledata then
17013  return
17014 end
17015 local instances=variabledata.instances
17016 local axis=variabledata.axis
17017 local segments=variabledata.segments
17018 if instances and axis then
17019  local values
17020  if instancespec==true then
17021   values={}
17022   for i=1,#axis do
17023    values[i]={
17024     value=axis[i].default,
17025    }
17026   end
17027  else
17028   for i=1,#instances do
17029    local instance=instances[i]
17030    if cleanname(instance.subfamily)==instancespec then
17031     values=instance.values
17032     break
17033    end
17034   end
17035  end
17036  if values then
17037   local factors={}
17038   for i=1,#axis do
17039    local a=axis[i]
17040    factors[i]=getaxisscale(segments,a.minimum,a.default,a.maximum,values[i].value)
17041   end
17042   return factors
17043  end
17044  local values=axistofactors(hash[instancespec] or instancespec)
17045  if values then
17046   local factors={}
17047   for i=1,#axis do
17048    local a=axis[i]
17049    local d=a.default
17050    factors[i]=getaxisscale(segments,a.minimum,d,a.maximum,values[a.name or a.tag] or d)
17051   end
17052   return factors
17053  end
17054 end
17055end
17056local function getscales(regions,factors)
17057 local scales={}
17058 for i=1,#regions do
17059  local region=regions[i]
17060  local s=1
17061  for j=1,#region do
17062   local axis=region[j]
17063   local f=factors[j]
17064   local start=axis.start
17065   local peak=axis.peak
17066   local stop=axis.stop
17067   if start>peak or peak>stop then
17068   elseif start<0 and stop>0 and peak~=0 then
17069   elseif peak==0 then
17070   elseif f<start or f>stop then
17071    s=0
17072    break
17073   elseif f<peak then
17074    s=s*(f-start)/(peak-start)
17075   elseif f>peak then
17076    s=s*(stop-f)/(stop-peak)
17077   else
17078   end
17079  end
17080  scales[i]=s
17081 end
17082 return scales
17083end
17084helpers.getaxisscale=getaxisscale
17085helpers.getfactors=getfactors
17086helpers.getscales=getscales
17087helpers.axistofactors=axistofactors
17088local function readvariationdata(f,storeoffset,factors) 
17089 local position=getposition(f)
17090 setposition(f,storeoffset)
17091 local format=readushort(f)
17092 local regionoffset=storeoffset+readulong(f)
17093 local nofdeltadata=readushort(f)
17094 local deltadata=readcardinaltable(f,nofdeltadata,ulong)
17095 setposition(f,regionoffset)
17096 local nofaxis=readushort(f)
17097 local nofregions=readushort(f)
17098 local regions={}
17099 for i=1,nofregions do 
17100  local t={}
17101  for i=1,nofaxis do
17102   t[i]={ 
17103    start=read2dot14(f),
17104    peak=read2dot14(f),
17105    stop=read2dot14(f),
17106   }
17107  end
17108  regions[i]=t
17109 end
17110  for i=1,nofdeltadata do
17111   setposition(f,storeoffset+deltadata[i])
17112   local nofdeltasets=readushort(f)
17113   local nofshorts=readushort(f)
17114   local nofregions=readushort(f)
17115   local usedregions={}
17116   local deltas={}
17117   for i=1,nofregions do
17118    usedregions[i]=regions[readushort(f)+1]
17119   end
17120   for i=1,nofdeltasets do
17121    local t=readintegertable(f,nofshorts,short)
17122    for i=nofshorts+1,nofregions do
17123     t[i]=readinteger(f)
17124    end
17125    deltas[i]=t
17126   end
17127   deltadata[i]={
17128    regions=usedregions,
17129    deltas=deltas,
17130    scales=factors and getscales(usedregions,factors) or nil,
17131   }
17132  end
17133 setposition(f,position)
17134 return regions,deltadata
17135end
17136helpers.readvariationdata=readvariationdata
17137local function readcoverage(f,offset,simple)
17138 setposition(f,offset)
17139 local coverageformat=readushort(f)
17140 if coverageformat==1 then
17141  local nofcoverage=readushort(f)
17142  if simple then
17143   if nofcoverage==1 then
17144    return { readushort(f) }
17145   elseif nofcoverage==2 then
17146    return { readushort(f),readushort(f) }
17147   else
17148    return readcardinaltable(f,nofcoverage,ushort)
17149   end
17150  elseif nofcoverage==1 then
17151   return { [readushort(f)]=0 }
17152  elseif nofcoverage==2 then
17153   return { [readushort(f)]=0,[readushort(f)]=1 }
17154  else
17155   local coverage={}
17156   for i=0,nofcoverage-1 do
17157    coverage[readushort(f)]=i 
17158   end
17159   return coverage
17160  end
17161 elseif coverageformat==2 then
17162  local nofranges=readushort(f)
17163  local coverage={}
17164  local n=simple and 1 or 0 
17165  for i=1,nofranges do
17166   local firstindex=readushort(f)
17167   local lastindex=readushort(f)
17168   local coverindex=readushort(f)
17169   if simple then
17170    for i=firstindex,lastindex do
17171     coverage[n]=i
17172     n=n+1
17173    end
17174   else
17175    for i=firstindex,lastindex do
17176     coverage[i]=n
17177     n=n+1
17178    end
17179   end
17180  end
17181  return coverage
17182 else
17183  report("unknown coverage format %a ",coverageformat)
17184  return {}
17185 end
17186end
17187local function readclassdef(f,offset,preset)
17188 setposition(f,offset)
17189 local classdefformat=readushort(f)
17190 local classdef={}
17191 if type(preset)=="number" then
17192  for k=0,preset-1 do
17193   classdef[k]=1
17194  end
17195 end
17196 if classdefformat==1 then
17197  local index=readushort(f)
17198  local nofclassdef=readushort(f)
17199  for i=1,nofclassdef do
17200   classdef[index]=readushort(f)+1
17201   index=index+1
17202  end
17203 elseif classdefformat==2 then
17204  local nofranges=readushort(f)
17205  local n=0
17206  for i=1,nofranges do
17207   local firstindex=readushort(f)
17208   local lastindex=readushort(f)
17209   local class=readushort(f)+1
17210   for i=firstindex,lastindex do
17211    classdef[i]=class
17212   end
17213  end
17214 else
17215  report("unknown classdef format %a ",classdefformat)
17216 end
17217 if type(preset)=="table" then
17218  for k in next,preset do
17219   if not classdef[k] then
17220    classdef[k]=1
17221   end
17222  end
17223 end
17224 return classdef
17225end
17226local function classtocoverage(defs)
17227 if defs then
17228  local list={}
17229  for index,class in next,defs do
17230   local c=list[class]
17231   if c then
17232    c[#c+1]=index
17233   else
17234    list[class]={ index }
17235   end
17236  end
17237  return list
17238 end
17239end
17240local skips={ [0]=0,
17241 1,
17242 1,
17243 2,
17244 1,
17245 2,
17246 2,
17247 3,
17248 2,
17249 2,
17250 3,
17251 2,
17252 3,
17253 3,
17254 4,
17255}
17256local function readvariation(f,offset)
17257 local p=getposition(f)
17258 setposition(f,offset)
17259 local outer=readushort(f)
17260 local inner=readushort(f)
17261 local format=readushort(f)
17262 setposition(f,p)
17263 if format==0x8000 then
17264  return outer,inner
17265 end
17266end
17267local function readposition(f,format,mainoffset,getdelta)
17268 if format==0 then
17269  return false
17270 end
17271 if format==0x04 then
17272  local h=readshort(f)
17273  if h==0 then
17274   return true 
17275  else
17276   return { 0,0,h,0 }
17277  end
17278 end
17279 if format==0x05 then
17280  local x=readshort(f)
17281  local h=readshort(f)
17282  if x==0 and h==0 then
17283   return true 
17284  else
17285   return { x,0,h,0 }
17286  end
17287 end
17288 if format==0x44 then
17289  local h=readshort(f)
17290  if getdelta then
17291   local d=readshort(f) 
17292   if d>0 then
17293    local outer,inner=readvariation(f,mainoffset+d)
17294    if outer then
17295     h=h+getdelta(outer,inner)
17296    end
17297   end
17298  else
17299   skipshort(f,1)
17300  end
17301  if h==0 then
17302   return true 
17303  else
17304   return { 0,0,h,0 }
17305  end
17306 end
17307 local x=band(format,0x1)~=0 and readshort(f) or 0 
17308 local y=band(format,0x2)~=0 and readshort(f) or 0 
17309 local h=band(format,0x4)~=0 and readshort(f) or 0 
17310 local v=band(format,0x8)~=0 and readshort(f) or 0 
17311 if format>=0x10 then
17312  local X=band(format,0x10)~=0 and skipshort(f) or 0
17313  local Y=band(format,0x20)~=0 and skipshort(f) or 0
17314  local H=band(format,0x40)~=0 and skipshort(f) or 0
17315  local V=band(format,0x80)~=0 and skipshort(f) or 0
17316  local s=skips[extract(format,4,4)]
17317  if s>0 then
17318   skipshort(f,s)
17319  end
17320  if getdelta then
17321   if X>0 then
17322    local outer,inner=readvariation(f,mainoffset+X)
17323    if outer then
17324     x=x+getdelta(outer,inner)
17325    end
17326   end
17327   if Y>0 then
17328    local outer,inner=readvariation(f,mainoffset+Y)
17329    if outer then
17330     y=y+getdelta(outer,inner)
17331    end
17332   end
17333   if H>0 then
17334    local outer,inner=readvariation(f,mainoffset+H)
17335    if outer then
17336     h=h+getdelta(outer,inner)
17337    end
17338   end
17339   if V>0 then
17340    local outer,inner=readvariation(f,mainoffset+V)
17341    if outer then
17342     v=v+getdelta(outer,inner)
17343    end
17344   end
17345  end
17346  return { x,y,h,v }
17347 elseif x==0 and y==0 and h==0 and v==0 then
17348  return true 
17349 else
17350  return { x,y,h,v }
17351 end
17352end
17353local function readanchor(f,offset,getdelta) 
17354 if not offset or offset==0 then
17355  return nil 
17356 end
17357 setposition(f,offset)
17358 local format=readshort(f) 
17359 local x=readshort(f)
17360 local y=readshort(f)
17361 if format==3 then
17362  if getdelta then
17363   local X=readshort(f)
17364   local Y=readshort(f)
17365   if X>0 then
17366    local outer,inner=readvariation(f,offset+X)
17367    if outer then
17368     x=x+getdelta(outer,inner)
17369    end
17370   end
17371   if Y>0 then
17372    local outer,inner=readvariation(f,offset+Y)
17373    if outer then
17374     y=y+getdelta(outer,inner)
17375    end
17376   end
17377  else
17378   skipshort(f,2)
17379  end
17380  return { x,y } 
17381 else
17382  return { x,y }
17383 end
17384end
17385local function readfirst(f,offset)
17386 if offset then
17387  setposition(f,offset)
17388 end
17389 return { readushort(f) }
17390end
17391local function readarray(f,offset)
17392 if offset then
17393  setposition(f,offset)
17394 end
17395 local n=readushort(f)
17396 if n==1 then
17397  return { readushort(f) },1
17398 elseif n>0 then
17399  return readcardinaltable(f,n,ushort),n
17400 end
17401end
17402local function readcoveragearray(f,offset,t,simple)
17403 if not t then
17404  return nil
17405 end
17406 local n=#t
17407 if n==0 then
17408  return nil
17409 end
17410 for i=1,n do
17411  t[i]=readcoverage(f,offset+t[i],simple)
17412 end
17413 return t
17414end
17415local function covered(subset,all)
17416 local used,u
17417 for i=1,#subset do
17418  local s=subset[i]
17419  if all[s] then
17420   if used then
17421    u=u+1
17422    used[u]=s
17423   else
17424    u=1
17425    used={ s }
17426   end
17427  end
17428 end
17429 return used
17430end
17431local function readlookuparray(f,noflookups,nofcurrent)
17432 local lookups={}
17433 if noflookups>0 then
17434  local length=0
17435  for i=1,noflookups do
17436   local index=readushort(f)+1
17437   if index>length then
17438    length=index
17439   end
17440   local lookup=readushort(f)+1
17441   local list=lookups[index]
17442   if list then
17443    list[#list+1]=lookup
17444   else
17445    lookups[index]={ lookup }
17446   end
17447  end
17448  for index=1,length do
17449   if not lookups[index] then
17450    lookups[index]=false
17451   end
17452  end
17453 end
17454 return lookups
17455end
17456local function unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
17457 local tableoffset=lookupoffset+offset
17458 setposition(f,tableoffset)
17459 local subtype=readushort(f)
17460 if subtype==1 then
17461  local coverage=readushort(f)
17462  local subclasssets=readarray(f)
17463  local rules={}
17464  if subclasssets then
17465   coverage=readcoverage(f,tableoffset+coverage,true)
17466   for i=1,#subclasssets do
17467    local offset=subclasssets[i]
17468    if offset>0 then
17469     local firstcoverage=coverage[i]
17470     local rulesoffset=tableoffset+offset
17471     local subclassrules=readarray(f,rulesoffset)
17472     for rule=1,#subclassrules do
17473      setposition(f,rulesoffset+subclassrules[rule])
17474      local nofcurrent=readushort(f)
17475      local noflookups=readushort(f)
17476      local current={ { firstcoverage } }
17477      for i=2,nofcurrent do
17478       current[i]={ readushort(f) }
17479      end
17480      local lookups=readlookuparray(f,noflookups,nofcurrent)
17481      rules[#rules+1]={
17482       current=current,
17483       lookups=lookups
17484      }
17485     end
17486    end
17487   end
17488  else
17489   report("empty subclassset in %a subtype %i","unchainedcontext",subtype)
17490  end
17491  return {
17492   format="glyphs",
17493   rules=rules,
17494  }
17495 elseif subtype==2 then
17496  local coverage=readushort(f)
17497  local currentclassdef=readushort(f)
17498  local subclasssets=readarray(f)
17499  local rules={}
17500  if subclasssets then
17501   coverage=readcoverage(f,tableoffset+coverage)
17502   currentclassdef=readclassdef(f,tableoffset+currentclassdef,coverage)
17503   local currentclasses=classtocoverage(currentclassdef,fontdata.glyphs)
17504   for class=1,#subclasssets do
17505    local offset=subclasssets[class]
17506    if offset>0 then
17507     local firstcoverage=currentclasses[class]
17508     if firstcoverage then
17509      firstcoverage=covered(firstcoverage,coverage) 
17510      if firstcoverage then
17511       local rulesoffset=tableoffset+offset
17512       local subclassrules=readarray(f,rulesoffset)
17513       for rule=1,#subclassrules do
17514        setposition(f,rulesoffset+subclassrules[rule])
17515        local nofcurrent=readushort(f)
17516        local noflookups=readushort(f)
17517        local current={ firstcoverage }
17518        for i=2,nofcurrent do
17519         current[i]=currentclasses[readushort(f)+1]
17520        end
17521        local lookups=readlookuparray(f,noflookups,nofcurrent)
17522        rules[#rules+1]={
17523         current=current,
17524         lookups=lookups
17525        }
17526       end
17527      else
17528       report("no coverage")
17529      end
17530     else
17531      report("no coverage class")
17532     end
17533    end
17534   end
17535  else
17536   report("empty subclassset in %a subtype %i","unchainedcontext",subtype)
17537  end
17538  return {
17539   format="class",
17540   rules=rules,
17541  }
17542 elseif subtype==3 then
17543  local nofglyphs=readushort(f)
17544  local noflookups=readushort(f)
17545  local current=readcardinaltable(f,nofglyphs,ushort)
17546  local lookups=readlookuparray(f,noflookups,#current)
17547  current=readcoveragearray(f,tableoffset,current,true)
17548  return {
17549   format="coverage",
17550   rules={
17551    {
17552     current=current,
17553     lookups=lookups,
17554    }
17555   }
17556  }
17557 else
17558  report("unsupported subtype %a in %a %s",subtype,"unchainedcontext",what)
17559 end
17560end
17561local function chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
17562 local tableoffset=lookupoffset+offset
17563 setposition(f,tableoffset)
17564 local subtype=readushort(f)
17565 if subtype==1 then
17566  local coverage=readushort(f)
17567  local subclasssets=readarray(f)
17568  local rules={}
17569  if subclasssets then
17570   coverage=readcoverage(f,tableoffset+coverage,true)
17571   for i=1,#subclasssets do
17572    local offset=subclasssets[i]
17573    if offset>0 then
17574     local firstcoverage=coverage[i]
17575     local rulesoffset=tableoffset+offset
17576     local subclassrules=readarray(f,rulesoffset)
17577     for rule=1,#subclassrules do
17578      setposition(f,rulesoffset+subclassrules[rule])
17579      local nofbefore=readushort(f)
17580      local before
17581      if nofbefore>0 then
17582       before={}
17583       for i=1,nofbefore do
17584        before[i]={ readushort(f) }
17585       end
17586      end
17587      local nofcurrent=readushort(f)
17588      local current={ { firstcoverage } }
17589      for i=2,nofcurrent do
17590       current[i]={ readushort(f) }
17591      end
17592      local nofafter=readushort(f)
17593      local after
17594      if nofafter>0 then
17595       after={}
17596       for i=1,nofafter do
17597        after[i]={ readushort(f) }
17598       end
17599      end
17600      local noflookups=readushort(f)
17601      local lookups=readlookuparray(f,noflookups,nofcurrent)
17602      rules[#rules+1]={
17603       before=before,
17604       current=current,
17605       after=after,
17606       lookups=lookups,
17607      }
17608     end
17609    end
17610   end
17611  else
17612   report("empty subclassset in %a subtype %i","chainedcontext",subtype)
17613  end
17614  return {
17615   format="glyphs",
17616   rules=rules,
17617  }
17618 elseif subtype==2 then
17619  local coverage=readushort(f)
17620  local beforeclassdef=readushort(f)
17621  local currentclassdef=readushort(f)
17622  local afterclassdef=readushort(f)
17623  local subclasssets=readarray(f)
17624  local rules={}
17625  if subclasssets then
17626   local coverage=readcoverage(f,tableoffset+coverage)
17627   local beforeclassdef=readclassdef(f,tableoffset+beforeclassdef,nofglyphs)
17628   local currentclassdef=readclassdef(f,tableoffset+currentclassdef,coverage)
17629   local afterclassdef=readclassdef(f,tableoffset+afterclassdef,nofglyphs)
17630   local beforeclasses=classtocoverage(beforeclassdef,fontdata.glyphs)
17631   local currentclasses=classtocoverage(currentclassdef,fontdata.glyphs)
17632   local afterclasses=classtocoverage(afterclassdef,fontdata.glyphs)
17633   for class=1,#subclasssets do
17634    local offset=subclasssets[class]
17635    if offset>0 then
17636     local firstcoverage=currentclasses[class]
17637     if firstcoverage then
17638      firstcoverage=covered(firstcoverage,coverage) 
17639      if firstcoverage then
17640       local rulesoffset=tableoffset+offset
17641       local subclassrules=readarray(f,rulesoffset)
17642       for rule=1,#subclassrules do
17643        setposition(f,rulesoffset+subclassrules[rule])
17644        local nofbefore=readushort(f)
17645        local before
17646        if nofbefore>0 then
17647         before={}
17648         for i=1,nofbefore do
17649          before[i]=beforeclasses[readushort(f)+1]
17650         end
17651        end
17652        local nofcurrent=readushort(f)
17653        local current={ firstcoverage }
17654        for i=2,nofcurrent do
17655         current[i]=currentclasses[readushort(f)+1]
17656        end
17657        local nofafter=readushort(f)
17658        local after
17659        if nofafter>0 then
17660         after={}
17661         for i=1,nofafter do
17662          after[i]=afterclasses[readushort(f)+1]
17663         end
17664        end
17665        local noflookups=readushort(f)
17666        local lookups=readlookuparray(f,noflookups,nofcurrent)
17667        rules[#rules+1]={
17668         before=before,
17669         current=current,
17670         after=after,
17671         lookups=lookups,
17672        }
17673       end
17674      else
17675       report("no coverage")
17676      end
17677     else
17678      report("class is not covered")
17679     end
17680    end
17681   end
17682  else
17683   report("empty subclassset in %a subtype %i","chainedcontext",subtype)
17684  end
17685  return {
17686   format="class",
17687   rules=rules,
17688  }
17689 elseif subtype==3 then
17690  local before=readarray(f)
17691  local current=readarray(f)
17692  local after=readarray(f)
17693  local noflookups=readushort(f)
17694  local lookups=current and readlookuparray(f,noflookups,#current)
17695  if lookups then
17696   before=readcoveragearray(f,tableoffset,before,true)
17697   current=readcoveragearray(f,tableoffset,current,true)
17698   after=readcoveragearray(f,tableoffset,after,true)
17699   return {
17700    format="coverage",
17701    rules={
17702     {
17703      before=before,
17704      current=current,
17705      after=after,
17706      lookups=lookups,
17707     }
17708    }
17709   }
17710  else
17711   report("confusing subtype %a in %a %s",subtype,"chainedcontext",what)
17712  end
17713 else
17714  report("unsupported subtype %a in %a %s",subtype,"chainedcontext",what)
17715 end
17716end
17717local function extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,types,handlers,what)
17718 local tableoffset=lookupoffset+offset
17719 setposition(f,tableoffset)
17720 local subtype=readushort(f)
17721 if subtype==1 then
17722  local lookuptype=types[readushort(f)]
17723  local faroffset=readulong(f)
17724  local handler=handlers[lookuptype]
17725  if handler then
17726   return handler(f,fontdata,lookupid,tableoffset+faroffset,0,glyphs,nofglyphs),lookuptype
17727  else
17728   report("no handler for lookuptype %a subtype %a in %s %s",lookuptype,subtype,what,"extension")
17729  end
17730 else
17731  report("unsupported subtype %a in %s %s",subtype,what,"extension")
17732 end
17733end
17734function gsubhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
17735 local tableoffset=lookupoffset+offset
17736 setposition(f,tableoffset)
17737 local subtype=readushort(f)
17738 if subtype==1 then
17739  local coverage=readushort(f)
17740  local delta=readshort(f) 
17741  local coverage=readcoverage(f,tableoffset+coverage) 
17742  for index in next,coverage do
17743   local newindex=(index+delta)%65536 
17744   if index>nofglyphs or newindex>nofglyphs then
17745    report("invalid index in %s format %i: %i -> %i (max %i)","single",subtype,index,newindex,nofglyphs)
17746    coverage[index]=nil
17747   else
17748    coverage[index]=newindex
17749   end
17750  end
17751  return {
17752   coverage=coverage
17753  }
17754 elseif subtype==2 then 
17755  local coverage=readushort(f)
17756  local nofreplacements=readushort(f)
17757  local replacements=readcardinaltable(f,nofreplacements,ushort)
17758  local coverage=readcoverage(f,tableoffset+coverage) 
17759  for index,newindex in next,coverage do
17760   newindex=newindex+1
17761   if index>nofglyphs or newindex>nofglyphs then
17762    report("invalid index in %s format %i: %i -> %i (max %i)","single",subtype,index,newindex,nofglyphs)
17763    coverage[index]=nil
17764   else
17765    coverage[index]=replacements[newindex]
17766   end
17767  end
17768  return {
17769   coverage=coverage
17770  }
17771 else
17772  report("unsupported subtype %a in %a substitution",subtype,"single")
17773 end
17774end
17775local function sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
17776 local tableoffset=lookupoffset+offset
17777 setposition(f,tableoffset)
17778 local subtype=readushort(f)
17779 if subtype==1 then
17780  local coverage=readushort(f)
17781  local nofsequence=readushort(f)
17782  local sequences=readcardinaltable(f,nofsequence,ushort)
17783  for i=1,nofsequence do
17784   setposition(f,tableoffset+sequences[i])
17785   sequences[i]=readcardinaltable(f,readushort(f),ushort)
17786  end
17787  local coverage=readcoverage(f,tableoffset+coverage)
17788  for index,newindex in next,coverage do
17789   newindex=newindex+1
17790   if index>nofglyphs or newindex>nofglyphs then
17791    report("invalid index in %s format %i: %i -> %i (max %i)",what,subtype,index,newindex,nofglyphs)
17792    coverage[index]=nil
17793   else
17794    coverage[index]=sequences[newindex]
17795   end
17796  end
17797  return {
17798   coverage=coverage
17799  }
17800 else
17801  report("unsupported subtype %a in %a substitution",subtype,what)
17802 end
17803end
17804function gsubhandlers.multiple(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
17805 return sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"multiple")
17806end
17807function gsubhandlers.alternate(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
17808 return sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"alternate")
17809end
17810function gsubhandlers.ligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
17811 local tableoffset=lookupoffset+offset
17812 setposition(f,tableoffset)
17813 local subtype=readushort(f)
17814 if subtype==1 then
17815  local coverage=readushort(f)
17816  local nofsets=readushort(f)
17817  local ligatures=readcardinaltable(f,nofsets,ushort)
17818  for i=1,nofsets do
17819   local offset=lookupoffset+offset+ligatures[i]
17820   setposition(f,offset)
17821   local n=readushort(f)
17822   if n==1 then
17823    ligatures[i]={ offset+readushort(f) }
17824   else
17825    local l={}
17826    for i=1,n do
17827     l[i]=offset+readushort(f)
17828    end
17829    ligatures[i]=l
17830   end
17831  end
17832  local coverage=readcoverage(f,tableoffset+coverage)
17833  for index,newindex in next,coverage do
17834   local hash={}
17835   local ligatures=ligatures[newindex+1]
17836   for i=1,#ligatures do
17837    local offset=ligatures[i]
17838    setposition(f,offset)
17839    local lig=readushort(f)
17840    local cnt=readushort(f)
17841    local hsh=hash
17842    for i=2,cnt do
17843     local c=readushort(f)
17844     local h=hsh[c]
17845     if not h then
17846      h={}
17847      hsh[c]=h
17848     end
17849     hsh=h
17850    end
17851    hsh.ligature=lig
17852   end
17853   coverage[index]=hash
17854  end
17855  return {
17856   coverage=coverage
17857  }
17858 else
17859  report("unsupported subtype %a in %a substitution",subtype,"ligature")
17860 end
17861end
17862function gsubhandlers.context(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
17863 return unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"substitution"),"context"
17864end
17865function gsubhandlers.chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
17866 return chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"substitution"),"chainedcontext"
17867end
17868function gsubhandlers.extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
17869 return extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,gsubtypes,gsubhandlers,"substitution")
17870end
17871function gsubhandlers.reversechainedcontextsingle(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
17872 local tableoffset=lookupoffset+offset
17873 setposition(f,tableoffset)
17874 local subtype=readushort(f)
17875 if subtype==1 then 
17876  local current=readfirst(f)
17877  local before=readarray(f)
17878  local after=readarray(f)
17879  local replacements=readarray(f)
17880  current=readcoveragearray(f,tableoffset,current,true)
17881  before=readcoveragearray(f,tableoffset,before,true)
17882  after=readcoveragearray(f,tableoffset,after,true)
17883  return {
17884   format="reversecoverage",
17885   rules={
17886    {
17887     before=before,
17888     current=current,
17889     after=after,
17890     replacements=replacements,
17891    }
17892   }
17893  },"reversechainedcontextsingle"
17894 else
17895  report("unsupported subtype %a in %a substitution",subtype,"reversechainedcontextsingle")
17896 end
17897end
17898local function readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta)
17899 local done={}
17900 for i=1,#sets do
17901  local offset=sets[i]
17902  local reused=done[offset]
17903  if not reused then
17904   offset=tableoffset+offset
17905   setposition(f,offset)
17906   local n=readushort(f)
17907   reused={}
17908   for i=1,n do
17909    reused[i]={
17910     readushort(f),
17911     readposition(f,format1,offset,getdelta),
17912     readposition(f,format2,offset,getdelta),
17913    }
17914   end
17915   done[offset]=reused
17916  end
17917  sets[i]=reused
17918 end
17919 return sets
17920end
17921local function readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,mainoffset,getdelta)
17922 local classlist1={}
17923 for i=1,nofclasses1 do
17924  local classlist2={}
17925  classlist1[i]=classlist2
17926  for j=1,nofclasses2 do
17927   local one=readposition(f,format1,mainoffset,getdelta)
17928   local two=readposition(f,format2,mainoffset,getdelta)
17929   if one or two then
17930    classlist2[j]={ one,two }
17931   else
17932    classlist2[j]=false
17933   end
17934  end
17935 end
17936 return classlist1
17937end
17938function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
17939 local tableoffset=lookupoffset+offset
17940 setposition(f,tableoffset)
17941 local subtype=readushort(f)
17942 local getdelta=fontdata.temporary.getdelta
17943 if subtype==1 then
17944  local coverage=readushort(f)
17945  local format=readushort(f)
17946  local value=readposition(f,format,tableoffset,getdelta)
17947  local coverage=readcoverage(f,tableoffset+coverage)
17948  for index,newindex in next,coverage do
17949   coverage[index]=value 
17950  end
17951  return {
17952   format="single",
17953   coverage=coverage,
17954  }
17955 elseif subtype==2 then
17956  local coverage=readushort(f)
17957  local format=readushort(f)
17958  local nofvalues=readushort(f)
17959  local values={}
17960  for i=1,nofvalues do
17961   values[i]=readposition(f,format,tableoffset,getdelta)
17962  end
17963  local coverage=readcoverage(f,tableoffset+coverage)
17964  for index,newindex in next,coverage do
17965   coverage[index]=values[newindex+1]
17966  end
17967  return {
17968   format="single",
17969   coverage=coverage,
17970  }
17971 else
17972  report("unsupported subtype %a in %a positioning",subtype,"single")
17973 end
17974end
17975function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
17976 local tableoffset=lookupoffset+offset
17977 setposition(f,tableoffset)
17978 local subtype=readushort(f)
17979 local getdelta=fontdata.temporary.getdelta
17980 if subtype==1 then
17981  local coverage=readushort(f)
17982  local format1=readushort(f)
17983  local format2=readushort(f)
17984  local sets=readarray(f)
17985     sets=readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta)
17986     coverage=readcoverage(f,tableoffset+coverage)
17987  local shared={} 
17988  for index,newindex in next,coverage do
17989   local set=sets[newindex+1]
17990   local hash={}
17991   for i=1,#set do
17992    local value=set[i]
17993    if value then
17994     local other=value[1]
17995     local share=shared[value]
17996     if share==nil then
17997      local first=value[2]
17998      local second=value[3]
17999      if first or second then
18000       share={ first,second or nil } 
18001      else
18002       share=false
18003      end
18004      shared[value]=share
18005     end
18006     hash[other]=share or nil 
18007    end
18008   end
18009   coverage[index]=hash
18010  end
18011  return {
18012   shared=shared and true or nil,
18013   format="pair",
18014   coverage=coverage,
18015  }
18016 elseif subtype==2 then
18017  local coverage=readushort(f)
18018  local format1=readushort(f)
18019  local format2=readushort(f)
18020  local classdef1=readushort(f)
18021  local classdef2=readushort(f)
18022  local nofclasses1=readushort(f) 
18023  local nofclasses2=readushort(f) 
18024  local classlist=readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,tableoffset,getdelta)
18025     coverage=readcoverage(f,tableoffset+coverage)
18026     classdef1=readclassdef(f,tableoffset+classdef1,coverage)
18027     classdef2=readclassdef(f,tableoffset+classdef2,nofglyphs)
18028  local usedcoverage={}
18029  local shared={} 
18030  for g1,c1 in next,classdef1 do
18031   if coverage[g1] then
18032    local l1=classlist[c1]
18033    if l1 then
18034     local hash={}
18035     for paired,class in next,classdef2 do
18036      local offsets=l1[class]
18037      if offsets then
18038       local first=offsets[1]
18039       local second=offsets[2]
18040       if first or second then
18041        local s1=shared[first]
18042        if s1==nil then
18043         s1={}
18044         shared[first]=s1
18045        end
18046        local s2=s1[second]
18047        if s2==nil then
18048         s2={ first,second or nil }
18049         s1[second]=s2
18050        end
18051        hash[paired]=s2
18052       end
18053      end
18054     end
18055     usedcoverage[g1]=hash
18056    end
18057   end
18058  end
18059  return {
18060   shared=shared and true or nil,
18061   format="pair",
18062   coverage=usedcoverage,
18063  }
18064 elseif subtype==3 then
18065  report("yet unsupported subtype %a in %a positioning",subtype,"pair")
18066 else
18067  report("unsupported subtype %a in %a positioning",subtype,"pair")
18068 end
18069end
18070function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
18071 local tableoffset=lookupoffset+offset
18072 setposition(f,tableoffset)
18073 local subtype=readushort(f)
18074 local getdelta=fontdata.temporary.getdelta
18075 if subtype==1 then
18076  local coverage=tableoffset+readushort(f)
18077  local nofrecords=readushort(f)
18078  local records={}
18079  for i=1,nofrecords do
18080   local entry=readushort(f)
18081   local exit=readushort(f)
18082   records[i]={
18083    entry~=0 and (tableoffset+entry) or false,
18084    exit~=0 and (tableoffset+exit ) or nil,
18085   }
18086  end
18087  local cc=(fontdata.temporary.cursivecount or 0)+1
18088  fontdata.temporary.cursivecount=cc
18089  cc="cc-"..cc
18090  coverage=readcoverage(f,coverage)
18091  for i=1,nofrecords do
18092   local r=records[i]
18093   records[i]={
18094    cc,
18095    readanchor(f,r[1],getdelta) or false,
18096    readanchor(f,r[2],getdelta) or nil,
18097   }
18098  end
18099  for index,newindex in next,coverage do
18100   coverage[index]=records[newindex+1]
18101  end
18102  return {
18103   coverage=coverage,
18104  }
18105 else
18106  report("unsupported subtype %a in %a positioning",subtype,"cursive")
18107 end
18108end
18109local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,ligature)
18110 local tableoffset=lookupoffset+offset
18111 setposition(f,tableoffset)
18112 local subtype=readushort(f)
18113 local getdelta=fontdata.temporary.getdelta
18114 if subtype==1 then
18115  local markcoverage=tableoffset+readushort(f)
18116  local basecoverage=tableoffset+readushort(f)
18117  local nofclasses=readushort(f)
18118  local markoffset=tableoffset+readushort(f)
18119  local baseoffset=tableoffset+readushort(f)
18120  local markcoverage=readcoverage(f,markcoverage)
18121  local basecoverage=readcoverage(f,basecoverage,true)
18122  setposition(f,markoffset)
18123  local markclasses={}
18124  local nofmarkclasses=readushort(f)
18125  local lastanchor=fontdata.lastanchor or 0
18126  local usedanchors={}
18127  for i=1,nofmarkclasses do
18128   local class=readushort(f)+1
18129   local offset=readushort(f)
18130   if offset==0 then
18131    markclasses[i]=false
18132   else
18133    markclasses[i]={ class,markoffset+offset }
18134   end
18135   usedanchors[class]=true
18136  end
18137  for i=1,nofmarkclasses do
18138   local mc=markclasses[i]
18139   if mc then
18140    mc[2]=readanchor(f,mc[2],getdelta)
18141   end
18142  end
18143  setposition(f,baseoffset)
18144  local nofbaserecords=readushort(f)
18145  local baserecords={}
18146  if ligature then
18147   for i=1,nofbaserecords do 
18148    local offset=readushort(f)
18149    if offset==0 then
18150     baserecords[i]=false
18151    else
18152     baserecords[i]=baseoffset+offset
18153    end
18154   end
18155   for i=1,nofbaserecords do
18156    local recordoffset=baserecords[i]
18157    if recordoffset then
18158     setposition(f,recordoffset)
18159     local nofcomponents=readushort(f)
18160     local components={}
18161     for i=1,nofcomponents do
18162      local classes={}
18163      for i=1,nofclasses do
18164       local offset=readushort(f)
18165       if offset~=0 then
18166        classes[i]=recordoffset+offset
18167       else
18168        classes[i]=false
18169       end
18170      end
18171      components[i]=classes
18172     end
18173     baserecords[i]=components
18174    end
18175   end
18176   local baseclasses={} 
18177   for i=1,nofclasses do
18178    baseclasses[i]={}
18179   end
18180   for i=1,nofbaserecords do
18181    local components=baserecords[i]
18182    if components then
18183     local b=basecoverage[i]
18184     for c=1,#components do
18185      local classes=components[c]
18186      if classes then
18187       for i=1,nofclasses do
18188        local anchor=readanchor(f,classes[i],getdelta)
18189        local bclass=baseclasses[i]
18190        local bentry=bclass[b]
18191        if bentry then
18192         bentry[c]=anchor
18193        else
18194         bclass[b]={ [c]=anchor }
18195        end
18196       end
18197      end
18198     end
18199    end
18200   end
18201   for index,newindex in next,markcoverage do
18202    markcoverage[index]=markclasses[newindex+1] or nil
18203   end
18204   return {
18205    format="ligature",
18206    baseclasses=baseclasses,
18207    coverage=markcoverage,
18208   }
18209  else
18210   for i=1,nofbaserecords do
18211    local r={}
18212    for j=1,nofclasses do
18213     local offset=readushort(f)
18214     if offset==0 then
18215      r[j]=false
18216     else
18217      r[j]=baseoffset+offset
18218     end
18219    end
18220    baserecords[i]=r
18221   end
18222   local baseclasses={} 
18223   for i=1,nofclasses do
18224    baseclasses[i]={}
18225   end
18226   for i=1,nofbaserecords do
18227    local r=baserecords[i]
18228    local b=basecoverage[i]
18229    for j=1,nofclasses do
18230     baseclasses[j][b]=readanchor(f,r[j],getdelta)
18231    end
18232   end
18233   for index,newindex in next,markcoverage do
18234    markcoverage[index]=markclasses[newindex+1] or nil
18235   end
18236   return {
18237    format="base",
18238    baseclasses=baseclasses,
18239    coverage=markcoverage,
18240   }
18241  end
18242 else
18243  report("unsupported subtype %a in",subtype)
18244 end
18245end
18246function gposhandlers.marktobase(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
18247 return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
18248end
18249function gposhandlers.marktoligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
18250 return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,true)
18251end
18252function gposhandlers.marktomark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
18253 return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
18254end
18255function gposhandlers.context(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
18256 return unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"positioning"),"context"
18257end
18258function gposhandlers.chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
18259 return chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"positioning"),"chainedcontext"
18260end
18261function gposhandlers.extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
18262 return extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,gpostypes,gposhandlers,"positioning")
18263end
18264do
18265 local plugins={}
18266 function plugins.size(f,fontdata,tableoffset,feature)
18267  if fontdata.designsize then
18268  else
18269   local function check(offset)
18270    setposition(f,offset)
18271    local designsize=readushort(f)
18272    if designsize>0 then 
18273     local fontstyleid=readushort(f)
18274     local guimenuid=readushort(f)
18275     local minsize=readushort(f)
18276     local maxsize=readushort(f)
18277     if minsize==0 and maxsize==0 and fontstyleid==0 and guimenuid==0 then
18278      minsize=designsize
18279      maxsize=designsize
18280     end
18281     if designsize>=minsize and designsize<=maxsize then
18282      return minsize,maxsize,designsize
18283     end
18284    end
18285   end
18286   local minsize,maxsize,designsize=check(tableoffset+feature.offset+feature.parameters)
18287   if not designsize then
18288    minsize,maxsize,designsize=check(tableoffset+feature.parameters)
18289    if designsize then
18290     report("bad size feature in %a, falling back to wrong offset",fontdata.filename or "?")
18291    else
18292     report("bad size feature in %a,",fontdata.filename or "?")
18293    end
18294   end
18295   if designsize then
18296    fontdata.minsize=minsize
18297    fontdata.maxsize=maxsize
18298    fontdata.designsize=designsize
18299   end
18300  end
18301 end
18302 local function reorderfeatures(fontdata,scripts,features)
18303  local scriptlangs={}
18304  local featurehash={}
18305  local featureorder={}
18306  for script,languages in next,scripts do
18307   for language,record in next,languages do
18308    local hash={}
18309    local list=record.featureindices
18310    for k=1,#list do
18311     local index=list[k]
18312     local feature=features[index]
18313     local lookups=feature.lookups
18314     local tag=feature.tag
18315     if tag then
18316      hash[tag]=true
18317     end
18318     if lookups then
18319      for i=1,#lookups do
18320       local lookup=lookups[i]
18321       local o=featureorder[lookup]
18322       if o then
18323        local okay=true
18324        for i=1,#o do
18325         if o[i]==tag then
18326          okay=false
18327          break
18328         end
18329        end
18330        if okay then
18331         o[#o+1]=tag
18332        end
18333       else
18334        featureorder[lookup]={ tag }
18335       end
18336       local f=featurehash[lookup]
18337       if f then
18338        local h=f[tag]
18339        if h then
18340         local s=h[script]
18341         if s then
18342          s[language]=true
18343         else
18344          h[script]={ [language]=true }
18345         end
18346        else
18347         f[tag]={ [script]={ [language]=true } }
18348        end
18349       else
18350        featurehash[lookup]={ [tag]={ [script]={ [language]=true } } }
18351       end
18352       local h=scriptlangs[tag]
18353       if h then
18354        local s=h[script]
18355        if s then
18356         s[language]=true
18357        else
18358         h[script]={ [language]=true }
18359        end
18360       else
18361        scriptlangs[tag]={ [script]={ [language]=true } }
18362       end
18363      end
18364     end
18365    end
18366   end
18367  end
18368  return scriptlangs,featurehash,featureorder
18369 end
18370 local function readscriplan(f,fontdata,scriptoffset)
18371  setposition(f,scriptoffset)
18372  local nofscripts=readushort(f)
18373  local scripts={}
18374  for i=1,nofscripts do
18375   scripts[readtag(f)]=scriptoffset+readushort(f)
18376  end
18377  local languagesystems=setmetatableindex("table")
18378  for script,offset in next,scripts do
18379   setposition(f,offset)
18380   local defaultoffset=readushort(f)
18381   local noflanguages=readushort(f)
18382   local languages={}
18383   if defaultoffset>0 then
18384    languages.dflt=languagesystems[offset+defaultoffset]
18385   end
18386   for i=1,noflanguages do
18387    local language=readtag(f)
18388    local offset=offset+readushort(f)
18389    languages[language]=languagesystems[offset]
18390   end
18391   scripts[script]=languages
18392  end
18393  for offset,usedfeatures in next,languagesystems do
18394   if offset>0 then
18395    setposition(f,offset)
18396    local featureindices={}
18397    usedfeatures.featureindices=featureindices
18398    usedfeatures.lookuporder=readushort(f) 
18399    usedfeatures.requiredindex=readushort(f) 
18400    local noffeatures=readushort(f)
18401    for i=1,noffeatures do
18402     featureindices[i]=readushort(f)+1
18403    end
18404   end
18405  end
18406  return scripts
18407 end
18408 local function readfeatures(f,fontdata,featureoffset)
18409  setposition(f,featureoffset)
18410  local features={}
18411  local noffeatures=readushort(f)
18412  for i=1,noffeatures do
18413   features[i]={
18414    tag=readtag(f),
18415    offset=readushort(f)
18416   }
18417  end
18418  for i=1,noffeatures do
18419   local feature=features[i]
18420   local offset=featureoffset+feature.offset
18421   setposition(f,offset)
18422   local parameters=readushort(f) 
18423   local noflookups=readushort(f)
18424   if noflookups>0 then
18425    local lookups=readcardinaltable(f,noflookups,ushort)
18426    feature.lookups=lookups
18427    for j=1,noflookups do
18428     lookups[j]=lookups[j]+1
18429    end
18430   end
18431   if parameters>0 then
18432    feature.parameters=parameters
18433    local plugin=plugins[feature.tag]
18434    if plugin then
18435     plugin(f,fontdata,featureoffset,feature)
18436    end
18437   end
18438  end
18439  return features
18440 end
18441 local function readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder,nofmarkclasses)
18442  setposition(f,lookupoffset)
18443  local noflookups=readushort(f)
18444  local lookups=readcardinaltable(f,noflookups,ushort)
18445  for lookupid=1,noflookups do
18446   local offset=lookups[lookupid]
18447   setposition(f,lookupoffset+offset)
18448   local subtables={}
18449   local typebits=readushort(f)
18450   local flagbits=readushort(f)
18451   local lookuptype=lookuptypes[typebits]
18452   local lookupflags=lookupflags[flagbits]
18453   local nofsubtables=readushort(f)
18454   for j=1,nofsubtables do
18455    subtables[j]=offset+readushort(f) 
18456   end
18457   local markclass=band(flagbits,0x0010)~=0 
18458   local markset=rshift(flagbits,8)
18459   if markclass then
18460    markclass=readushort(f) 
18461   end
18462   if markset>0 then
18463    markclass=nofmarkclasses+markset
18464   end
18465   lookups[lookupid]={
18466    type=lookuptype,
18467    flags=lookupflags,
18468    name=lookupid,
18469    subtables=subtables,
18470    markclass=markclass,
18471    features=featurehash[lookupid],
18472    order=featureorder[lookupid],
18473   }
18474  end
18475  return lookups
18476 end
18477 local f_lookupname=formatters["%s_%s_%s"]
18478 local function resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset)
18479  local sequences=fontdata.sequences  or {}
18480  local sublookuplist=fontdata.sublookups or {}
18481  fontdata.sequences=sequences
18482  fontdata.sublookups=sublookuplist
18483  local nofsublookups=#sublookuplist
18484  local nofsequences=#sequences 
18485  local lastsublookup=nofsublookups
18486  local lastsequence=nofsequences
18487  local lookupnames=lookupnames[what]
18488  local sublookuphash={}
18489  local sublookupcheck={}
18490  local glyphs=fontdata.glyphs
18491  local nofglyphs=fontdata.nofglyphs or #glyphs
18492  local noflookups=#lookups
18493  local lookupprefix=sub(what,2,2)
18494  local usedlookups=false
18495  local allsteps={} 
18496  for lookupid=1,noflookups do
18497   local lookup=lookups[lookupid]
18498   local lookuptype=lookup.type
18499   local subtables=lookup.subtables
18500   local features=lookup.features
18501   local handler=lookuphandlers[lookuptype]
18502   if handler then
18503    local nofsubtables=#subtables
18504    local order=lookup.order
18505    local flags=lookup.flags
18506    if flags[1] then flags[1]="mark" end
18507    if flags[2] then flags[2]="ligature" end
18508    if flags[3] then flags[3]="base" end
18509    local markclass=lookup.markclass
18510    if nofsubtables>0 then
18511     local steps={}
18512     local nofsteps=0
18513     local oldtype=nil
18514     for s=1,nofsubtables do
18515      local step,lt=handler(f,fontdata,lookupid,lookupoffset,subtables[s],glyphs,nofglyphs)
18516      if lt then
18517       lookuptype=lt
18518       if oldtype and lt~=oldtype then
18519        report("messy %s lookup type %a and %a",what,lookuptype,oldtype)
18520       end
18521       oldtype=lookuptype
18522      end
18523      if not step then
18524       report("unsupported %s lookup type %a",what,lookuptype)
18525      else
18526       nofsteps=nofsteps+1
18527       steps[nofsteps]=step
18528       local rules=step.rules
18529       if rules then
18530        allsteps[#allsteps+1]=step 
18531        for i=1,#rules do
18532         local rule=rules[i]
18533         local before=rule.before
18534         local current=rule.current
18535         local after=rule.after
18536         local replacements=rule.replacements
18537         if before then
18538          for i=1,#before do
18539           before[i]=tohash(before[i])
18540          end
18541          rule.before=reversed(before)
18542         end
18543         if current then
18544          if replacements then
18545           local first=current[1]
18546           local hash={}
18547           local repl={}
18548           for i=1,#first do
18549            local c=first[i]
18550            hash[c]=true
18551            repl[c]=replacements[i]
18552           end
18553           rule.current={ hash }
18554           rule.replacements=repl
18555          else
18556           for i=1,#current do
18557            current[i]=tohash(current[i])
18558           end
18559          end
18560         else
18561         end
18562         if after then
18563          for i=1,#after do
18564           after[i]=tohash(after[i])
18565          end
18566         end
18567         if usedlookups then
18568          local lookups=rule.lookups
18569          if lookups then
18570           for k,v in next,lookups do
18571            if v then
18572             for k,v in next,v do
18573              usedlookups[v]=usedlookups[v]+1
18574             end
18575            end
18576           end
18577          end
18578         end
18579        end
18580       end
18581      end
18582     end
18583     if nofsteps~=nofsubtables then
18584      report("bogus subtables removed in %s lookup type %a",what,lookuptype)
18585     end
18586     lookuptype=lookupnames[lookuptype] or lookuptype
18587     if features then
18588      nofsequences=nofsequences+1
18589      local l={
18590       index=nofsequences,
18591       name=f_lookupname(lookupprefix,"s",lookupid+lookupidoffset),
18592       steps=steps,
18593       nofsteps=nofsteps,
18594       type=lookuptype,
18595       markclass=markclass or nil,
18596       flags=flags,
18597       order=order,
18598       features=features,
18599      }
18600      sequences[nofsequences]=l
18601      lookup.done=l
18602     else
18603      nofsublookups=nofsublookups+1
18604      local l={
18605       index=nofsublookups,
18606       name=f_lookupname(lookupprefix,"l",lookupid+lookupidoffset),
18607       steps=steps,
18608       nofsteps=nofsteps,
18609       type=lookuptype,
18610       markclass=markclass or nil,
18611       flags=flags,
18612      }
18613      sublookuplist[nofsublookups]=l
18614      sublookuphash[lookupid]=nofsublookups
18615      sublookupcheck[lookupid]=0
18616      lookup.done=l
18617     end
18618    else
18619     report("no subtables for lookup %a",lookupid)
18620    end
18621   else
18622    report("no handler for lookup %a with type %a",lookupid,lookuptype)
18623   end
18624  end
18625  if usedlookups then
18626   report("used %s lookups: % t",what,sortedkeys(usedlookups))
18627  end
18628  local reported={}
18629  local function report_issue(i,what,step,kind)
18630    report("rule %i in step %i of %s has %s lookups",i,step,what,kind)
18631  end
18632   for s=1,#allsteps do    
18633    local step=allsteps[s] 
18634    local rules=step.rules
18635    if rules then
18636     for i=1,#rules do
18637      local rule=rules[i]
18638      local rlookups=rule.lookups
18639      if not rlookups then
18640       report_issue(i,what,s,"no")
18641      elseif not next(rlookups) then
18642       rule.lookups=nil
18643      else
18644       local length=#rlookups
18645       for index=1,length do
18646        local lookuplist=rlookups[index]
18647        if lookuplist then
18648         local length=#lookuplist
18649         local found={}
18650         local noffound=0
18651         for index=1,length do
18652          local lookupid=lookuplist[index]
18653          if lookupid then
18654           local h=sublookuphash[lookupid]
18655           if not h then
18656            local lookup=lookups[lookupid]
18657            if lookup then
18658             local d=lookup.done
18659             if d then
18660              nofsublookups=nofsublookups+1
18661              local l={
18662               index=nofsublookups,
18663               name=f_lookupname(lookupprefix,"d",lookupid+lookupidoffset),
18664               derived=true,
18665               steps=d.steps,
18666               nofsteps=d.nofsteps,
18667               type=d.lookuptype or "gsub_single",
18668               markclass=d.markclass or nil,
18669               flags=d.flags,
18670              }
18671              sublookuplist[nofsublookups]=copy(l) 
18672              sublookuphash[lookupid]=nofsublookups
18673              sublookupcheck[lookupid]=1
18674              h=nofsublookups
18675             else
18676              report_issue(i,what,s,"missing")
18677              rule.lookups=nil
18678              break
18679             end
18680            else
18681             report_issue(i,what,s,"bad")
18682             rule.lookups=nil
18683             break
18684            end
18685           else
18686            sublookupcheck[lookupid]=sublookupcheck[lookupid]+1
18687           end
18688           if h then
18689            noffound=noffound+1
18690            found[noffound]=h
18691           end
18692          end
18693         end
18694         rlookups[index]=noffound>0 and found or false
18695        else
18696         rlookups[index]=false
18697        end
18698       end
18699      end
18700     end
18701    end
18702   end
18703  for i,n in sortedhash(sublookupcheck) do
18704   local l=lookups[i]
18705   local t=l.type
18706   if n==0 and t~="extension" then
18707    local d=l.done
18708    report("%s lookup %s of type %a is not used",what,d and d.name or l.name,t)
18709   end
18710  end
18711 end
18712 local function loadvariations(f,fontdata,variationsoffset,lookuptypes,featurehash,featureorder)
18713  setposition(f,variationsoffset)
18714  local version=readulong(f) 
18715  local nofrecords=readulong(f)
18716  local records={}
18717  for i=1,nofrecords do
18718   records[i]={
18719    conditions=readulong(f),
18720    substitutions=readulong(f),
18721   }
18722  end
18723  for i=1,nofrecords do
18724   local record=records[i]
18725   local offset=record.conditions
18726   if offset==0 then
18727    record.condition=nil
18728    record.matchtype="always"
18729   else
18730    local offset=variationsoffset+offset
18731    setposition(f,offset)
18732    local nofconditions=readushort(f)
18733    local conditions={}
18734    for i=1,nofconditions do
18735     conditions[i]=offset+readulong(f)
18736    end
18737    record.conditions=conditions
18738    record.matchtype="condition"
18739   end
18740  end
18741  for i=1,nofrecords do
18742   local record=records[i]
18743   if record.matchtype=="condition" then
18744    local conditions=record.conditions
18745    for i=1,#conditions do
18746     setposition(f,conditions[i])
18747     conditions[i]={
18748      format=readushort(f),
18749      axis=readushort(f),
18750      minvalue=read2dot14(f),
18751      maxvalue=read2dot14(f),
18752     }
18753    end
18754   end
18755  end
18756  for i=1,nofrecords do
18757   local record=records[i]
18758   local offset=record.substitutions
18759   if offset==0 then
18760    record.substitutions={}
18761   else
18762    setposition(f,variationsoffset+offset)
18763    local version=readulong(f)
18764    local nofsubstitutions=readushort(f)
18765    local substitutions={}
18766    for i=1,nofsubstitutions do
18767     substitutions[readushort(f)]=readulong(f)
18768    end
18769    for index,alternates in sortedhash(substitutions) do
18770     if index==0 then
18771      record.substitutions=false
18772     else
18773      local tableoffset=variationsoffset+offset+alternates
18774      setposition(f,tableoffset)
18775      local parameters=readulong(f) 
18776      local noflookups=readushort(f)
18777      local lookups=readcardinaltable(f,noflookups,ushort)
18778      record.substitutions=lookups
18779     end
18780    end
18781   end
18782  end
18783  setvariabledata(fontdata,"features",records)
18784 end
18785 local function readscripts(f,fontdata,what,lookuptypes,lookuphandlers,lookupstoo)
18786  local tableoffset=gotodatatable(f,fontdata,what,true)
18787  if tableoffset then
18788   local version=readulong(f)
18789   local scriptoffset=tableoffset+readushort(f)
18790   local featureoffset=tableoffset+readushort(f)
18791   local lookupoffset=tableoffset+readushort(f)
18792   local variationsoffset=version>0x00010000 and readulong(f) or 0
18793   if not scriptoffset then
18794    return
18795   end
18796   local scripts=readscriplan(f,fontdata,scriptoffset)
18797   local features=readfeatures(f,fontdata,featureoffset)
18798   local scriptlangs,featurehash,featureorder=reorderfeatures(fontdata,scripts,features)
18799   if fontdata.features then
18800    fontdata.features[what]=scriptlangs
18801   else
18802    fontdata.features={ [what]=scriptlangs }
18803   end
18804   if not lookupstoo then
18805    return
18806   end
18807   local markclasses=fontdata.markclasses
18808   local marksets=fontdata.marksets
18809   local nofmarkclasses=(markclasses and #markclasses or 0)-(marksets and #marksets or 0)
18810   local lookups=readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder,nofmarkclasses)
18811   if lookups then
18812    resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset)
18813   end
18814   if variationsoffset>0 then
18815    loadvariations(f,fontdata,tableoffset+variationsoffset,lookuptypes,featurehash,featureorder)
18816   end
18817  end
18818 end
18819 local function checkkerns(f,fontdata,specification)
18820  local datatable=fontdata.tables.kern
18821  if not datatable then
18822   return 
18823  end
18824  local features=fontdata.features
18825  local gposfeatures=features and features.gpos
18826  local name
18827  if not gposfeatures or not gposfeatures.kern then
18828   name="kern"
18829  elseif specification.globalkerns then
18830   name="globalkern"
18831  else
18832   report("ignoring global kern table, using gpos kern feature")
18833   return
18834  end
18835  setposition(f,datatable.offset)
18836  local version=readushort(f)
18837  local noftables=readushort(f)
18838  if noftables>1 then
18839   report("adding global kern table as gpos feature %a",name)
18840   local kerns=setmetatableindex("table")
18841   for i=1,noftables do
18842    local version=readushort(f)
18843    local length=readushort(f)
18844    local coverage=readushort(f)
18845    local format=rshift(coverage,8) 
18846    if format==0 then
18847     local nofpairs=readushort(f)
18848     local searchrange=readushort(f)
18849     local entryselector=readushort(f)
18850     local rangeshift=readushort(f)
18851     for i=1,nofpairs do
18852      kerns[readushort(f)][readushort(f)]=readfword(f)
18853     end
18854    elseif format==2 then
18855    else
18856    end
18857   end
18858   local feature={ dflt={ dflt=true } }
18859   if not features then
18860    fontdata.features={ gpos={ [name]=feature } }
18861   elseif not gposfeatures then
18862    fontdata.features.gpos={ [name]=feature }
18863   else
18864    gposfeatures[name]=feature
18865   end
18866   local sequences=fontdata.sequences
18867   if not sequences then
18868    sequences={}
18869    fontdata.sequences=sequences
18870   end
18871   local nofsequences=#sequences+1
18872   sequences[nofsequences]={
18873    index=nofsequences,
18874    name=name,
18875    steps={
18876     {
18877      coverage=kerns,
18878      format="kern",
18879     },
18880    },
18881    nofsteps=1,
18882    type="gpos_pair",
18883    flags={ false,false,false,false },
18884    order={ name },
18885    features={ [name]=feature },
18886   }
18887  else
18888   report("ignoring empty kern table of feature %a",name)
18889  end
18890 end
18891 function readers.gsub(f,fontdata,specification)
18892  if specification.details then
18893   readscripts(f,fontdata,"gsub",gsubtypes,gsubhandlers,specification.lookups)
18894  end
18895 end
18896 function readers.gpos(f,fontdata,specification)
18897  if specification.details then
18898   readscripts(f,fontdata,"gpos",gpostypes,gposhandlers,specification.lookups)
18899   if specification.lookups then
18900    checkkerns(f,fontdata,specification)
18901   end
18902  end
18903 end
18904end
18905function readers.gdef(f,fontdata,specification)
18906 if not specification.glyphs then
18907  return
18908 end
18909 local datatable=fontdata.tables.gdef
18910 if datatable then
18911  local tableoffset=datatable.offset
18912  setposition(f,tableoffset)
18913  local version=readulong(f)
18914  local classoffset=readushort(f)
18915  local attachmentoffset=readushort(f) 
18916  local ligaturecarets=readushort(f) 
18917  local markclassoffset=readushort(f)
18918  local marksetsoffset=version>=0x00010002 and readushort(f) or 0
18919  local varsetsoffset=version>=0x00010003 and readulong(f) or 0
18920  local glyphs=fontdata.glyphs
18921  local marks={}
18922  local markclasses=setmetatableindex("table")
18923  local marksets=setmetatableindex("table")
18924  fontdata.marks=marks
18925  fontdata.markclasses=markclasses
18926  fontdata.marksets=marksets
18927  if classoffset~=0 then
18928   setposition(f,tableoffset+classoffset)
18929   local classformat=readushort(f)
18930   if classformat==1 then
18931    local firstindex=readushort(f)
18932    local lastindex=firstindex+readushort(f)-1
18933    for index=firstindex,lastindex do
18934     local class=classes[readushort(f)]
18935     if class=="mark" then
18936      marks[index]=true
18937     end
18938     glyphs[index].class=class
18939    end
18940   elseif classformat==2 then
18941    local nofranges=readushort(f)
18942    for i=1,nofranges do
18943     local firstindex=readushort(f)
18944     local lastindex=readushort(f)
18945     local class=classes[readushort(f)]
18946     if class then
18947      for index=firstindex,lastindex do
18948       glyphs[index].class=class
18949       if class=="mark" then
18950        marks[index]=true
18951       end
18952      end
18953     end
18954    end
18955   end
18956  end
18957  if markclassoffset~=0 then
18958   setposition(f,tableoffset+markclassoffset)
18959   local classformat=readushort(f)
18960   if classformat==1 then
18961    local firstindex=readushort(f)
18962    local lastindex=firstindex+readushort(f)-1
18963    for index=firstindex,lastindex do
18964     markclasses[readushort(f)][index]=true
18965    end
18966   elseif classformat==2 then
18967    local nofranges=readushort(f)
18968    for i=1,nofranges do
18969     local firstindex=readushort(f)
18970     local lastindex=readushort(f)
18971     local class=markclasses[readushort(f)]
18972     for index=firstindex,lastindex do
18973      class[index]=true
18974     end
18975    end
18976   end
18977  end
18978  if marksetsoffset~=0 then
18979   local nofmarkclasses=fontdata.markclasses and #fontdata.markclasses or 0
18980   marksetsoffset=tableoffset+marksetsoffset
18981   setposition(f,marksetsoffset)
18982   local format=readushort(f)
18983   if format==1 then
18984    local nofsets=readushort(f)
18985    local sets=readcardinaltable(f,nofsets,ulong)
18986    for i=1,nofsets do
18987     local offset=sets[i]
18988     if offset~=0 then
18989      markclasses[nofmarkclasses+i]=readcoverage(f,marksetsoffset+offset)
18990      marksets[i]={}
18991     end
18992    end
18993   end
18994  end
18995  local factors=specification.factors
18996  if (specification.variable or factors) and varsetsoffset~=0 then
18997   local regions,deltas=readvariationdata(f,tableoffset+varsetsoffset,factors)
18998   if factors then
18999    fontdata.temporary.getdelta=function(outer,inner)
19000     local delta=deltas[outer+1]
19001     if delta then
19002      local d=delta.deltas[inner+1]
19003      if d then
19004       local scales=delta.scales
19005       local dd=0
19006       for i=1,#scales do
19007        local di=d[i]
19008        if di then
19009         dd=dd+scales[i]*di
19010        else
19011         break
19012        end
19013       end
19014       return round(dd)
19015      end
19016     end
19017     return 0
19018    end
19019   end
19020  end
19021 end
19022end
19023local function readmathvalue(f)
19024 local v=readshort(f)
19025 skipshort(f,1) 
19026 return v
19027end
19028local function readmathconstants(f,fontdata,offset)
19029 setposition(f,offset)
19030 fontdata.mathconstants={
19031  ScriptPercentScaleDown=readshort(f),
19032  ScriptScriptPercentScaleDown=readshort(f),
19033  DelimitedSubFormulaMinHeight=readushort(f),
19034  DisplayOperatorMinHeight=readushort(f),
19035  MathLeading=readmathvalue(f),
19036  AxisHeight=readmathvalue(f),
19037  AccentBaseHeight=readmathvalue(f),
19038  FlattenedAccentBaseHeight=readmathvalue(f),
19039  SubscriptShiftDown=readmathvalue(f),
19040  SubscriptTopMax=readmathvalue(f),
19041  SubscriptBaselineDropMin=readmathvalue(f),
19042  SuperscriptShiftUp=readmathvalue(f),
19043  SuperscriptShiftUpCramped=readmathvalue(f),
19044  SuperscriptBottomMin=readmathvalue(f),
19045  SuperscriptBaselineDropMax=readmathvalue(f),
19046  SubSuperscriptGapMin=readmathvalue(f),
19047  SuperscriptBottomMaxWithSubscript=readmathvalue(f),
19048  SpaceAfterScript=readmathvalue(f),
19049  UpperLimitGapMin=readmathvalue(f),
19050  UpperLimitBaselineRiseMin=readmathvalue(f),
19051  LowerLimitGapMin=readmathvalue(f),
19052  LowerLimitBaselineDropMin=readmathvalue(f),
19053  StackTopShiftUp=readmathvalue(f),
19054  StackTopDisplayStyleShiftUp=readmathvalue(f),
19055  StackBottomShiftDown=readmathvalue(f),
19056  StackBottomDisplayStyleShiftDown=readmathvalue(f),
19057  StackGapMin=readmathvalue(f),
19058  StackDisplayStyleGapMin=readmathvalue(f),
19059  StretchStackTopShiftUp=readmathvalue(f),
19060  StretchStackBottomShiftDown=readmathvalue(f),
19061  StretchStackGapAboveMin=readmathvalue(f),
19062  StretchStackGapBelowMin=readmathvalue(f),
19063  FractionNumeratorShiftUp=readmathvalue(f),
19064  FractionNumeratorDisplayStyleShiftUp=readmathvalue(f),
19065  FractionDenominatorShiftDown=readmathvalue(f),
19066  FractionDenominatorDisplayStyleShiftDown=readmathvalue(f),
19067  FractionNumeratorGapMin=readmathvalue(f),
19068  FractionNumeratorDisplayStyleGapMin=readmathvalue(f),
19069  FractionRuleThickness=readmathvalue(f),
19070  FractionDenominatorGapMin=readmathvalue(f),
19071  FractionDenominatorDisplayStyleGapMin=readmathvalue(f),
19072  SkewedFractionHorizontalGap=readmathvalue(f),
19073  SkewedFractionVerticalGap=readmathvalue(f),
19074  OverbarVerticalGap=readmathvalue(f),
19075  OverbarRuleThickness=readmathvalue(f),
19076  OverbarExtraAscender=readmathvalue(f),
19077  UnderbarVerticalGap=readmathvalue(f),
19078  UnderbarRuleThickness=readmathvalue(f),
19079  UnderbarExtraDescender=readmathvalue(f),
19080  RadicalVerticalGap=readmathvalue(f),
19081  RadicalDisplayStyleVerticalGap=readmathvalue(f),
19082  RadicalRuleThickness=readmathvalue(f),
19083  RadicalExtraAscender=readmathvalue(f),
19084  RadicalKernBeforeDegree=readmathvalue(f),
19085  RadicalKernAfterDegree=readmathvalue(f),
19086  RadicalDegreeBottomRaisePercent=readshort(f),
19087 }
19088end
19089local function readmathglyphinfo(f,fontdata,offset)
19090 setposition(f,offset)
19091 local italics=readushort(f)
19092 local accents=readushort(f)
19093 local extensions=readushort(f)
19094 local kerns=readushort(f)
19095 local glyphs=fontdata.glyphs
19096 if italics~=0 then
19097  setposition(f,offset+italics)
19098  local coverage=readushort(f)
19099  local nofglyphs=readushort(f)
19100  coverage=readcoverage(f,offset+italics+coverage,true)
19101  setposition(f,offset+italics+4)
19102  for i=1,nofglyphs do
19103   local italic=readmathvalue(f)
19104   if italic~=0 then
19105    local glyph=glyphs[coverage[i]]
19106    local math=glyph.math
19107    if not math then
19108     glyph.math={ italic=italic }
19109    else
19110     math.italic=italic
19111    end
19112   end
19113  end
19114  fontdata.hasitalics=true
19115 end
19116 if accents~=0 then
19117  setposition(f,offset+accents)
19118  local coverage=readushort(f)
19119  local nofglyphs=readushort(f)
19120  coverage=readcoverage(f,offset+accents+coverage,true)
19121  setposition(f,offset+accents+4)
19122  for i=1,nofglyphs do
19123   local accent=readmathvalue(f)
19124   if accent~=0 then
19125    local glyph=glyphs[coverage[i]]
19126    local math=glyph.math
19127    if not math then
19128     glyph.math={ accent=accent }
19129    else
19130     math.accent=accent 
19131    end
19132   end
19133  end
19134 end
19135 if extensions~=0 then
19136  setposition(f,offset+extensions)
19137 end
19138 if kerns~=0 then
19139  local kernoffset=offset+kerns
19140  setposition(f,kernoffset)
19141  local coverage=readushort(f)
19142  local nofglyphs=readushort(f)
19143  if nofglyphs>0 then
19144   local function get(offset)
19145    setposition(f,kernoffset+offset)
19146    local n=readushort(f)
19147    if n==0 then
19148     local k=readmathvalue(f)
19149     if k==0 then
19150     else
19151      return { { kern=k } }
19152     end
19153    else
19154     local l={}
19155     for i=1,n do
19156      l[i]={ height=readmathvalue(f) }
19157     end
19158     for i=1,n do
19159      l[i].kern=readmathvalue(f)
19160     end
19161     l[n+1]={ kern=readmathvalue(f) }
19162     return l
19163    end
19164   end
19165   local kernsets={}
19166   for i=1,nofglyphs do
19167    local topright=readushort(f)
19168    local topleft=readushort(f)
19169    local bottomright=readushort(f)
19170    local bottomleft=readushort(f)
19171    kernsets[i]={
19172     topright=topright~=0 and topright or nil,
19173     topleft=topleft~=0 and topleft  or nil,
19174     bottomright=bottomright~=0 and bottomright or nil,
19175     bottomleft=bottomleft~=0 and bottomleft  or nil,
19176    }
19177   end
19178   coverage=readcoverage(f,kernoffset+coverage,true)
19179   for i=1,nofglyphs do
19180    local kernset=kernsets[i]
19181    if next(kernset) then
19182     local k=kernset.topright if k then kernset.topright=get(k) end
19183     local k=kernset.topleft  if k then kernset.topleft=get(k) end
19184     local k=kernset.bottomright if k then kernset.bottomright=get(k) end
19185     local k=kernset.bottomleft  if k then kernset.bottomleft=get(k) end
19186     if next(kernset) then
19187      local glyph=glyphs[coverage[i]]
19188      local math=glyph.math
19189      if math then
19190       math.kerns=kernset
19191      else
19192       glyph.math={ kerns=kernset }
19193      end
19194     end
19195    end
19196   end
19197  end
19198 end
19199end
19200local function readmathvariants(f,fontdata,offset)
19201 setposition(f,offset)
19202 local glyphs=fontdata.glyphs
19203 local minoverlap=readushort(f)
19204 local vcoverage=readushort(f)
19205 local hcoverage=readushort(f)
19206 local vnofglyphs=readushort(f)
19207 local hnofglyphs=readushort(f)
19208 local vconstruction=readcardinaltable(f,vnofglyphs,ushort)
19209 local hconstruction=readcardinaltable(f,hnofglyphs,ushort)
19210 fontdata.mathconstants.MinConnectorOverlap=minoverlap
19211 local function get(offset,coverage,nofglyphs,construction,kvariants,kparts,kitalic,korientation,orientation)
19212  if coverage~=0 and nofglyphs>0 then
19213   local coverage=readcoverage(f,offset+coverage,true)
19214   local n=0
19215   for i=1,nofglyphs do
19216    local c=construction[i]
19217    if c~=0 then
19218     local index=coverage[i]
19219     local glyph=glyphs[index]
19220     local math=glyph.math
19221     setposition(f,offset+c)
19222     local assembly=readushort(f)
19223     local nofvariants=readushort(f)
19224     if nofvariants>0 then
19225      local variants,v=nil,0
19226      for i=1,nofvariants do
19227       local variant=readushort(f)
19228       if variant==index then
19229        n=n+1
19230       elseif variants then
19231        v=v+1
19232        variants[v]=variant
19233       else
19234        v=1
19235        variants={ variant }
19236       end
19237       skipshort(f)
19238      end
19239      if not variants or not next(variants) then
19240      elseif not math then
19241       math={ [kvariants]=variants }
19242       glyph.math=math
19243      else
19244       math[kvariants]=variants
19245      end
19246     end
19247     if assembly~=0 then
19248      setposition(f,offset+c+assembly)
19249      local italic=readmathvalue(f)
19250      local nofparts=readushort(f)
19251      local parts={}
19252      for i=1,nofparts do
19253       local p={
19254        glyph=readushort(f),
19255        start=readushort(f),
19256        ["end"]=readushort(f),
19257        advance=readushort(f),
19258       }
19259       local flags=readushort(f)
19260       if band(flags,0x0001)~=0 then
19261        p.extender=1 
19262       end
19263       parts[i]=p
19264      end
19265      if not math then
19266       math={
19267        [kparts]=parts
19268       }
19269       glyph.math=math
19270      else
19271       math[kparts]=parts
19272      end
19273      if italic and italic~=0 then
19274       math[kitalic]=italic
19275      end
19276      if korientation and orientation then
19277       math[korientation]=orientation
19278      end
19279     end
19280    end
19281   end
19282   if n>0 then
19283    report("discarding %i self referencing %s variant entries",n,orientation)
19284   end
19285   for index=1,#glyphs do
19286    local g=glyphs[index]
19287    local m=g.math
19288    if m then
19289     local v=m[kvariants]
19290     if v then
19291      local done={ [index]=true }
19292      local size=#v
19293      local i=1
19294      while i<=size do
19295       local vi=v[i]
19296       if done[vi] then
19297        report("discarding %sdirect circular %s variant index 0x%04X for index 0x%04X, %C","",orientation,vi,index,tonumber(g.unicode) or 0xFFFD)
19298        table.remove(v,i)
19299        size=size-1
19300        goto NEXT
19301       else
19302        local gg=glyphs[vi]
19303        if gg then
19304         local mm=gg.math
19305         if mm then
19306          local vv=mm[kvariants]
19307          if vv then
19308           report("discarding %sdirect circular %s variant index 0x%04X for index 0x%04X, %C","in",orientation,vi,index,tonumber(g.unicode) or 0xFFFD)
19309           table.remove(v,i)
19310           size=size-1
19311           goto NEXT
19312          end
19313         end
19314        end
19315        done[vi]=true
19316       end
19317       i=i+1
19318        ::NEXT::
19319      end
19320     end
19321    end
19322   end
19323  end
19324 end
19325 get(offset,hcoverage,hnofglyphs,hconstruction,"hvariants","hparts","hitalic",nil,"horizontal")
19326 get(offset,vcoverage,vnofglyphs,vconstruction,"vvariants","vparts","vitalic",nil,"vertical")
19327end
19328function readers.math(f,fontdata,specification)
19329 local tableoffset=gotodatatable(f,fontdata,"math",specification.glyphs)
19330 if tableoffset then
19331  local version=readulong(f)
19332  local constants=readushort(f)
19333  local glyphinfo=readushort(f)
19334  local variants=readushort(f)
19335  if constants==0 then
19336   report("the math table of %a has no constants",fontdata.filename)
19337  else
19338   readmathconstants(f,fontdata,tableoffset+constants)
19339  end
19340  if glyphinfo~=0 then
19341   readmathglyphinfo(f,fontdata,tableoffset+glyphinfo)
19342  end
19343  if variants~=0 then
19344   readmathvariants(f,fontdata,tableoffset+variants)
19345  end
19346 end
19347end
19348function readers.colr(f,fontdata,specification)
19349 local tableoffset=gotodatatable(f,fontdata,"colr",specification.glyphs)
19350 if tableoffset then
19351  local version=readushort(f)
19352		if version==0 then
19353			
19354		elseif version==1 then
19355			report("table version %a of %a is %s supported for font %s",version,"colr","partially",fontdata.filename)
19356		else
19357			report("table version %a of %a is %s supported for font %s",version,"colr","not",fontdata.filename)
19358			return
19359		end
19360  if not fontdata.tables.cpal then
19361   report("color table %a in font %a has no mandate %a table","colr",fontdata.filename,"cpal")
19362   fontdata.colorpalettes={}
19363  end
19364  local glyphs=fontdata.glyphs
19365  local nofglyphs=readushort(f)
19366  local baseoffset=readulong(f)
19367  local layeroffset=readulong(f)
19368  local noflayers=readushort(f)
19369  local layerrecords={}
19370  local maxclass=0
19371  setposition(f,tableoffset+layeroffset)
19372  for i=1,noflayers do
19373   local slot=readushort(f)
19374   local class=readushort(f)
19375   if class<0xFFFF then
19376    class=class+1
19377    if class>maxclass then
19378     maxclass=class
19379    end
19380   end
19381   layerrecords[i]={
19382    slot=slot,
19383    class=class,
19384   }
19385  end
19386  fontdata.maxcolorclass=maxclass
19387  setposition(f,tableoffset+baseoffset)
19388  for i=0,nofglyphs-1 do
19389   local glyphindex=readushort(f)
19390   local firstlayer=readushort(f)
19391   local noflayers=readushort(f)
19392   local t={}
19393   for i=1,noflayers do
19394    t[i]=layerrecords[firstlayer+i]
19395   end
19396   glyphs[glyphindex].colors=t
19397  end
19398 end
19399 fontdata.hascolor=true
19400end
19401function readers.cpal(f,fontdata,specification)
19402 local tableoffset=gotodatatable(f,fontdata,"cpal",specification.glyphs)
19403 if tableoffset then
19404  local version=readushort(f)
19405  local nofpaletteentries=readushort(f)
19406  local nofpalettes=readushort(f)
19407  local nofcolorrecords=readushort(f)
19408  local firstcoloroffset=readulong(f)
19409  local colorrecords={}
19410  local palettes=readcardinaltable(f,nofpalettes,ushort)
19411  if version==1 then
19412   local palettettypesoffset=readulong(f)
19413   local palettelabelsoffset=readulong(f)
19414   local paletteentryoffset=readulong(f)
19415  end
19416  setposition(f,tableoffset+firstcoloroffset)
19417  for i=1,nofcolorrecords do
19418   local b,g,r,a=readbytes(f,4)
19419   colorrecords[i]={
19420    r,g,b,a~=255 and a or nil,
19421   }
19422  end
19423  for i=1,nofpalettes do
19424   local p={}
19425   local o=palettes[i]
19426   for j=1,nofpaletteentries do
19427    p[j]=colorrecords[o+j]
19428   end
19429   palettes[i]=p
19430  end
19431  fontdata.colorpalettes=palettes
19432 end
19433end
19434local compress=gzip and gzip.compress
19435local compressed=compress and gzip.compressed
19436function readers.svg(f,fontdata,specification)
19437 local tableoffset=gotodatatable(f,fontdata,"svg",specification.glyphs)
19438 if tableoffset then
19439  local version=readushort(f)
19440  local glyphs=fontdata.glyphs
19441  local indexoffset=tableoffset+readulong(f)
19442  local reserved=readulong(f)
19443  setposition(f,indexoffset)
19444  local nofentries=readushort(f)
19445  local entries={}
19446  for i=1,nofentries do
19447   entries[i]={
19448    first=readushort(f),
19449    last=readushort(f),
19450    offset=indexoffset+readulong(f),
19451    length=readulong(f),
19452   }
19453  end
19454  for i=1,nofentries do
19455   local entry=entries[i]
19456   setposition(f,entry.offset)
19457   local data=readstring(f,entry.length)
19458   if compressed and not compressed(data) then
19459    data=compress(data)
19460   end
19461   entries[i]={
19462    first=entry.first,
19463    last=entry.last,
19464    data=data
19465   }
19466  end
19467  fontdata.svgshapes=entries
19468 end
19469 fontdata.hascolor=true
19470end
19471function readers.sbix(f,fontdata,specification)
19472 local tableoffset=gotodatatable(f,fontdata,"sbix",specification.glyphs)
19473 if tableoffset then
19474  local version=readushort(f)
19475  local flags=readushort(f)
19476  local nofstrikes=readulong(f)
19477  local strikes={}
19478  local nofglyphs=fontdata.nofglyphs
19479  for i=1,nofstrikes do
19480   strikes[i]=readulong(f)
19481  end
19482  local shapes={}
19483  local done=0
19484  for i=1,nofstrikes do
19485   local strikeoffset=strikes[i]+tableoffset
19486   setposition(f,strikeoffset)
19487   strikes[i]={
19488    ppem=readushort(f),
19489    ppi=readushort(f),
19490    offset=strikeoffset
19491   }
19492  end
19493  sort(strikes,function(a,b)
19494   if b.ppem==a.ppem then
19495    return b.ppi<a.ppi
19496   else
19497    return b.ppem<a.ppem
19498   end
19499  end)
19500  local glyphs={}
19501  local delayed=CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 or fonts.handlers.typethree
19502  for i=1,nofstrikes do
19503   local strike=strikes[i]
19504   local strikeppem=strike.ppem
19505   local strikeppi=strike.ppi
19506   local strikeoffset=strike.offset
19507   setposition(f,strikeoffset)
19508   for i=0,nofglyphs do
19509    glyphs[i]=readulong(f)
19510   end
19511   local glyphoffset=glyphs[0]
19512   for i=0,nofglyphs-1 do
19513    local nextoffset=glyphs[i+1]
19514    if not shapes[i] then
19515     local datasize=nextoffset-glyphoffset
19516     if datasize>0 then
19517      setposition(f,strikeoffset+glyphoffset)
19518      local x=readshort(f)
19519      local y=readshort(f)
19520      local tag=readtag(f) 
19521      local size=datasize-8
19522      local data=nil
19523      local offset=nil
19524      if delayed then
19525       offset=getposition(f)
19526       data=nil
19527      else
19528       data=readstring(f,size)
19529       size=nil
19530      end
19531      shapes[i]={
19532       x=x,
19533       y=y,
19534       o=offset,
19535       s=size,
19536       data=data,
19537      }
19538      done=done+1
19539      if done==nofglyphs then
19540       break
19541      end
19542     end
19543    end
19544    glyphoffset=nextoffset
19545   end
19546  end
19547  fontdata.pngshapes=shapes
19548 end
19549end
19550do
19551 local function getmetrics(f)
19552  return {
19553   ascender=readinteger(f),
19554   descender=readinteger(f),
19555   widthmax=readuinteger(f),
19556   caretslopedumerator=readinteger(f),
19557   caretslopedenominator=readinteger(f),
19558   caretoffset=readinteger(f),
19559   minorigin=readinteger(f),
19560   minadvance=readinteger(f),
19561   maxbefore=readinteger(f),
19562   minafter=readinteger(f),
19563   pad1=readinteger(f),
19564   pad2=readinteger(f),
19565  }
19566 end
19567 local function getbigmetrics(f)
19568  return {
19569   height=readuinteger(f),
19570   width=readuinteger(f),
19571   horiBearingX=readinteger(f),
19572   horiBearingY=readinteger(f),
19573   horiAdvance=readuinteger(f),
19574   vertBearingX=readinteger(f),
19575   vertBearingY=readinteger(f),
19576   vertAdvance=readuinteger(f),
19577  }
19578 end
19579 local function getsmallmetrics(f)
19580  return {
19581   height=readuinteger(f),
19582   width=readuinteger(f),
19583   bearingX=readinteger(f),
19584   bearingY=readinteger(f),
19585   advance=readuinteger(f),
19586  }
19587 end
19588 function readers.cblc(f,fontdata,specification)
19589  local ctdttableoffset=gotodatatable(f,fontdata,"cbdt",specification.glyphs)
19590  if not ctdttableoffset then
19591   return
19592  end
19593  local cblctableoffset=gotodatatable(f,fontdata,"cblc",specification.glyphs)
19594  if cblctableoffset then
19595   local majorversion=readushort(f)
19596   local minorversion=readushort(f)
19597   local nofsizetables=readulong(f)
19598   local sizetables={}
19599   local shapes={}
19600   local subtables={}
19601   for i=1,nofsizetables do
19602    sizetables[i]={
19603     subtables=readulong(f),
19604     indexsize=readulong(f),
19605     nofsubtables=readulong(f),
19606     colorref=readulong(f),
19607     hormetrics=getmetrics(f),
19608     vermetrics=getmetrics(f),
19609     firstindex=readushort(f),
19610     lastindex=readushort(f),
19611     ppemx=readbyte(f),
19612     ppemy=readbyte(f),
19613     bitdepth=readbyte(f),
19614     flags=readbyte(f),
19615    }
19616   end
19617   sort(sizetables,function(a,b)
19618    if b.ppemx==a.ppemx then
19619     return b.bitdepth<a.bitdepth
19620    else
19621     return b.ppemx<a.ppemx
19622    end
19623   end)
19624   for i=1,nofsizetables do
19625    local s=sizetables[i]
19626    local d=false
19627    for j=s.firstindex,s.lastindex do
19628     if not shapes[j] then
19629      shapes[j]=i
19630      d=true
19631     end
19632    end
19633    if d then
19634     s.used=true
19635    end
19636   end
19637   for i=1,nofsizetables do
19638    local s=sizetables[i]
19639    if s.used then
19640     local offset=s.subtables
19641     setposition(f,cblctableoffset+offset)
19642     for j=1,s.nofsubtables do
19643      local firstindex=readushort(f)
19644      local lastindex=readushort(f)
19645      local tableoffset=readulong(f)+offset
19646      for k=firstindex,lastindex do
19647       if shapes[k]==i then
19648        local s=subtables[tableoffset]
19649        if not s then
19650         s={
19651          firstindex=firstindex,
19652          lastindex=lastindex,
19653         }
19654         subtables[tableoffset]=s
19655        end
19656        shapes[k]=s
19657       end
19658      end
19659     end
19660    end
19661   end
19662   for offset,subtable in sortedhash(subtables) do
19663    local tabletype=readushort(f)
19664    subtable.format=readushort(f)
19665    local baseoffset=readulong(f)+ctdttableoffset
19666    local offsets={}
19667    local metrics=nil
19668    if tabletype==1 then
19669     for i=subtable.firstindex,subtable.lastindex do
19670      offsets[i]=readulong(f)+baseoffset
19671     end
19672     skipbytes(f,4)
19673    elseif tabletype==2 then
19674     local size=readulong(f)
19675     local done=baseoffset
19676     metrics=getbigmetrics(f)
19677     for i=subtable.firstindex,subtable.lastindex do
19678      offsets[i]=done
19679      done=done+size
19680     end
19681    elseif tabletype==3 then
19682     local n=subtable.lastindex-subtable.firstindex+2
19683     for i=subtable.firstindex,subtable.lastindex do
19684      offsets[i]=readushort(f)+baseoffset
19685     end
19686     if math.odd(n) then
19687      skipbytes(f,4)
19688     else
19689      skipbytes(f,2)
19690     end
19691    elseif tabletype==4 then
19692     for i=1,readulong(f) do
19693      offsets[readushort(f)]=readushort(f)+baseoffset
19694     end
19695    elseif tabletype==5 then
19696     local size=readulong(f)
19697     local done=baseoffset
19698     metrics=getbigmetrics(f)
19699     local n=readulong(f)
19700     for i=1,n do
19701      offsets[readushort(f)]=done
19702      done=done+size
19703     end
19704     if math.odd(n) then
19705      skipbytes(f,2)
19706     end
19707    else
19708     return 
19709    end
19710    subtable.offsets=offsets
19711    subtable.metrics=metrics
19712   end
19713   local default={ width=0,height=0 }
19714   local glyphs=fontdata.glyphs
19715   local delayed=CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 or fonts.handlers.typethree
19716   for index,subtable in sortedhash(shapes) do
19717    if type(subtable)=="table" then
19718     local data=nil
19719     local size=nil
19720     local metrics=default
19721     local format=subtable.format
19722     local offset=subtable.offsets[index]
19723     setposition(f,offset)
19724     if format==17 then
19725      metrics=getsmallmetrics(f)
19726      size=true
19727     elseif format==18 then
19728      metrics=getbigmetrics(f)
19729      size=true
19730     elseif format==19 then
19731      metrics=subtable.metrics
19732      size=true
19733     else
19734     end
19735     if size then
19736      size=readulong(f)
19737      if delayed then
19738       offset=getposition(f)
19739       data=nil
19740      else
19741       offset=nil
19742       data=readstring(f,size)
19743       size=nil
19744      end
19745     else
19746      offset=nil
19747     end
19748     local x=metrics.width
19749     local y=metrics.height
19750     shapes[index]={
19751      x=x,
19752      y=y,
19753      o=offset,
19754      s=size,
19755      data=data,
19756     }
19757     local glyph=glyphs[index]
19758     if not glyph.boundingbox then
19759      local width=glyph.width
19760      local height=width*y/x
19761      glyph.boundingbox={ 0,0,width,height }
19762     end
19763    else
19764     shapes[index]={
19765      x=0,
19766      y=0,
19767      data="",
19768     }
19769    end
19770   end
19771   fontdata.pngshapes=shapes 
19772  end
19773 end
19774 function readers.cbdt(f,fontdata,specification)
19775 end
19776end
19777function readers.stat(f,fontdata,specification)
19778 local tableoffset=gotodatatable(f,fontdata,"stat",true) 
19779 if tableoffset then
19780  local extras=fontdata.extras
19781  local version=readulong(f) 
19782  local axissize=readushort(f)
19783  local nofaxis=readushort(f)
19784  local axisoffset=readulong(f)
19785  local nofvalues=readushort(f)
19786  local valuesoffset=readulong(f)
19787  local fallbackname=extras[readushort(f)] 
19788  local axis={}
19789  local values={}
19790  setposition(f,tableoffset+axisoffset)
19791  for i=1,nofaxis do
19792   local tag=readtag(f)
19793   axis[i]={
19794    tag=tag,
19795    name=lower(extras[readushort(f)] or tag),
19796    ordering=readushort(f),
19797    variants={}
19798   }
19799  end
19800  setposition(f,tableoffset+valuesoffset)
19801  for i=1,nofvalues do
19802   values[i]=readushort(f)
19803  end
19804  for i=1,nofvalues do
19805   setposition(f,tableoffset+valuesoffset+values[i])
19806   local format=readushort(f)
19807   local index=readushort(f)+1
19808   local flags=readushort(f)
19809   local name=lower(extras[readushort(f)] or "no name")
19810   local value=readfixed(f)
19811   local variant
19812   if format==1 then
19813    variant={
19814     flags=flags,
19815     name=name,
19816     value=value,
19817    }
19818   elseif format==2 then
19819    variant={
19820     flags=flags,
19821     name=name,
19822     value=value,
19823     minimum=readfixed(f),
19824     maximum=readfixed(f),
19825    }
19826   elseif format==3 then
19827    variant={
19828     flags=flags,
19829     name=name,
19830     value=value,
19831     link=readfixed(f),
19832    }
19833   end
19834   insert(axis[index].variants,variant)
19835  end
19836  sort(axis,function(a,b)
19837   return a.ordering<b.ordering
19838  end)
19839  for i=1,#axis do
19840   local a=axis[i]
19841   sort(a.variants,function(a,b)
19842    return a.name<b.name
19843   end)
19844   a.ordering=nil
19845  end
19846  setvariabledata(fontdata,"designaxis",axis)
19847  setvariabledata(fontdata,"fallbackname",fallbackname)
19848 end
19849end
19850function readers.avar(f,fontdata,specification)
19851 local tableoffset=gotodatatable(f,fontdata,"avar",true) 
19852 if tableoffset then
19853  local function collect()
19854   local nofvalues=readushort(f)
19855   local values={}
19856   local lastfrom=false
19857   local lastto=false
19858   for i=1,nofvalues do
19859    local from=read2dot14(f)
19860    local to=read2dot14(f)
19861    if lastfrom and from<=lastfrom then
19862    elseif lastto and to>=lastto then
19863    else
19864     values[#values+1]={ from,to }
19865     lastfrom,lastto=from,to
19866    end
19867   end
19868   nofvalues=#values
19869   if nofvalues>2 then
19870    local some=values[1]
19871    if some[1]==-1 and some[2]==-1 then
19872     some=values[nofvalues]
19873     if some[1]==1 and some[2]==1 then
19874      for i=2,nofvalues-1 do
19875       some=values[i]
19876       if some[1]==0 and some[2]==0 then
19877        return values
19878       end
19879      end
19880     end
19881    end
19882   end
19883   return false
19884  end
19885  local version=readulong(f) 
19886  local reserved=readushort(f)
19887  local nofaxis=readushort(f)
19888  local segments={}
19889  for i=1,nofaxis do
19890   segments[i]=collect()
19891  end
19892  setvariabledata(fontdata,"segments",segments)
19893 end
19894end
19895function readers.fvar(f,fontdata,specification)
19896 local tableoffset=gotodatatable(f,fontdata,"fvar",true) 
19897 if tableoffset then
19898  local version=readulong(f) 
19899  local offsettoaxis=tableoffset+readushort(f)
19900  local reserved=skipshort(f)
19901  local nofaxis=readushort(f)
19902  local sizeofaxis=readushort(f)
19903  local nofinstances=readushort(f)
19904  local sizeofinstances=readushort(f)
19905  local extras=fontdata.extras
19906  local axis={}
19907  local instances={}
19908  setposition(f,offsettoaxis)
19909  for i=1,nofaxis do
19910   axis[i]={
19911    tag=readtag(f),
19912    minimum=readfixed(f),
19913    default=readfixed(f),
19914    maximum=readfixed(f),
19915    flags=readushort(f),
19916    name=lower(extras[readushort(f)] or "bad name"),
19917   }
19918   local n=sizeofaxis-20
19919   if n>0 then
19920    skipbytes(f,n)
19921   elseif n<0 then
19922   end
19923  end
19924  local nofbytes=2+2+2+nofaxis*4
19925  local readpsname=nofbytes<=sizeofinstances
19926  local skippable=sizeofinstances-nofbytes
19927  for i=1,nofinstances do
19928   local subfamid=readushort(f)
19929   local flags=readushort(f) 
19930   local values={}
19931   for i=1,nofaxis do
19932    values[i]={
19933     axis=axis[i].tag,
19934     value=readfixed(f),
19935    }
19936   end
19937   local psnameid=readpsname and readushort(f) or 0xFFFF
19938   if subfamid==2 or subfamid==17 then
19939   elseif subfamid==0xFFFF then
19940    subfamid=nil
19941   elseif subfamid<=256 or subfamid>=32768 then
19942    subfamid=nil 
19943   end
19944   if psnameid==6 then
19945   elseif psnameid==0xFFFF then
19946    psnameid=nil
19947   elseif psnameid<=256 or psnameid>=32768 then
19948    psnameid=nil 
19949   end
19950   instances[i]={
19951    subfamily=extras[subfamid],
19952    psname=psnameid and extras[psnameid] or nil,
19953    values=values,
19954   }
19955   if skippable>0 then
19956    skipbytes(f,skippable)
19957   end
19958  end
19959  setvariabledata(fontdata,"axis",axis)
19960  setvariabledata(fontdata,"instances",instances)
19961 end
19962end
19963function readers.hvar(f,fontdata,specification)
19964 local factors=specification.factors
19965 if not factors then
19966  return
19967 end
19968 local tableoffset=gotodatatable(f,fontdata,"hvar",specification.variable)
19969 if not tableoffset then
19970  return
19971 end
19972 local version=readulong(f) 
19973 local variationoffset=tableoffset+readulong(f) 
19974 local advanceoffset=tableoffset+readulong(f)
19975 local lsboffset=tableoffset+readulong(f)
19976 local rsboffset=tableoffset+readulong(f)
19977 local regions={}
19978 local variations={}
19979 local innerindex={} 
19980 local outerindex={} 
19981 local deltas={}
19982 if variationoffset>0 then
19983  regions,deltas=readvariationdata(f,variationoffset,factors)
19984 end
19985 if not regions then
19986  return
19987 end
19988 if advanceoffset>0 then
19989  setposition(f,advanceoffset)
19990  local format=readushort(f) 
19991  local mapcount=readushort(f)
19992  local entrysize=rshift(band(format,0x0030),4)+1
19993  local nofinnerbits=band(format,0x000F)+1 
19994  local innermask=lshift(1,nofinnerbits)-1
19995  local readcardinal=read_cardinal[entrysize] 
19996  for i=0,mapcount-1 do
19997   local mapdata=readcardinal(f)
19998   outerindex[i]=rshift(mapdata,nofinnerbits)
19999   innerindex[i]=band(mapdata,innermask)
20000  end
20001  setvariabledata(fontdata,"hvarwidths",true)
20002  local glyphs=fontdata.glyphs
20003  for i=0,fontdata.nofglyphs-1 do
20004   local glyph=glyphs[i]
20005   local width=glyph.width
20006   if width then
20007    local outer=outerindex[i] or 0
20008    local inner=innerindex[i] or i
20009    if outer and inner then 
20010     local delta=deltas[outer+1]
20011     if delta then
20012      local d=delta.deltas[inner+1]
20013      if d then
20014       local scales=delta.scales
20015       local deltaw=0
20016       for i=1,#scales do
20017        local di=d[i]
20018        if di then
20019         deltaw=deltaw+scales[i]*di
20020        else
20021         break 
20022        end
20023       end
20024       glyph.width=width+round(deltaw)
20025      end
20026     end
20027    end
20028   end
20029  end
20030 end
20031end
20032function readers.vvar(f,fontdata,specification)
20033 if not specification.variable then
20034  return
20035 end
20036end
20037function readers.mvar(f,fontdata,specification)
20038 local tableoffset=gotodatatable(f,fontdata,"mvar",specification.variable)
20039 if tableoffset then
20040  local version=readulong(f) 
20041  local reserved=skipshort(f,1)
20042  local recordsize=readushort(f)
20043  local nofrecords=readushort(f)
20044  local offsettostore=tableoffset+readushort(f)
20045  local dimensions={}
20046  local factors=specification.factors
20047  if factors then
20048   local regions,deltas=readvariationdata(f,offsettostore,factors)
20049   for i=1,nofrecords do
20050    local tag=readtag(f)
20051    local var=variabletags[tag]
20052    if var then
20053     local outer=readushort(f)
20054     local inner=readushort(f)
20055     local delta=deltas[outer+1]
20056     if delta then
20057      local d=delta.deltas[inner+1]
20058      if d then
20059       local scales=delta.scales
20060       local dd=0
20061       for i=1,#scales do
20062        dd=dd+scales[i]*d[i]
20063       end
20064       var(fontdata,round(dd))
20065      end
20066     end
20067    else
20068     skipshort(f,2)
20069    end
20070    if recordsize>8 then 
20071     skipbytes(recordsize-8)
20072    end
20073   end
20074  end
20075 end
20076end
20077function readers.dsig(f,fontdata,specification)
20078end
20079
20080end -- closure
20081
20082do -- begin closure to overcome local limits and interference
20083
20084if not modules then modules={} end modules ['font-oti']={
20085 version=1.001,
20086 comment="companion to font-ini.mkiv",
20087 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
20088 copyright="PRAGMA ADE / ConTeXt Development Team",
20089 license="see context related readme files"
20090}
20091local lower=string.lower
20092local fonts=fonts
20093local constructors=fonts.constructors
20094local otf=constructors.handlers.otf
20095local otffeatures=constructors.features.otf
20096local registerotffeature=otffeatures.register
20097local otftables=otf.tables or {}
20098otf.tables=otftables
20099local allocate=utilities.storage.allocate
20100registerotffeature {
20101 name="features",
20102 description="initialization of feature handler",
20103 default=true,
20104}
20105local function setmode(tfmdata,value)
20106 if value then
20107  tfmdata.properties.mode=lower(value)
20108 end
20109end
20110otf.modeinitializer=setmode
20111local function setlanguage(tfmdata,value)
20112 if value then
20113  local cleanvalue=lower(value)
20114  local languages=otftables and otftables.languages
20115  local properties=tfmdata.properties
20116  if not languages then
20117   properties.language=cleanvalue
20118  elseif languages[value] then
20119   properties.language=cleanvalue
20120  else
20121   properties.language="dflt"
20122  end
20123 end
20124end
20125local function setscript(tfmdata,value)
20126 if value then
20127  local cleanvalue=lower(value)
20128  local scripts=otftables and otftables.scripts
20129  local properties=tfmdata.properties
20130  if not scripts then
20131   properties.script=cleanvalue
20132  elseif scripts[value] then
20133   properties.script=cleanvalue
20134  else
20135   properties.script="dflt"
20136  end
20137 end
20138end
20139registerotffeature {
20140 name="mode",
20141 description="mode",
20142 initializers={
20143  base=setmode,
20144  node=setmode,
20145  plug=setmode,
20146 }
20147}
20148registerotffeature {
20149 name="language",
20150 description="language",
20151 initializers={
20152  base=setlanguage,
20153  node=setlanguage,
20154  plug=setlanguage,
20155 }
20156}
20157registerotffeature {
20158 name="script",
20159 description="script",
20160 initializers={
20161  base=setscript,
20162  node=setscript,
20163  plug=setscript,
20164 }
20165}
20166otftables.featuretypes=allocate {
20167 gpos_single="position",
20168 gpos_pair="position",
20169 gpos_cursive="position",
20170 gpos_mark2base="position",
20171 gpos_mark2ligature="position",
20172 gpos_mark2mark="position",
20173 gpos_context="position",
20174 gpos_contextchain="position",
20175 gsub_single="substitution",
20176 gsub_multiple="substitution",
20177 gsub_alternate="substitution",
20178 gsub_ligature="substitution",
20179 gsub_context="substitution",
20180 gsub_contextchain="substitution",
20181 gsub_reversecontextchain="substitution",
20182 gsub_reversesub="substitution",
20183}
20184function otffeatures.checkeddefaultscript(featuretype,autoscript,scripts)
20185 if featuretype=="position" then
20186  local default=scripts.dflt
20187  if default then
20188   if autoscript=="position" or autoscript==true then
20189    return default
20190   else
20191    report_otf("script feature %s not applied, enable default positioning")
20192   end
20193  else
20194  end
20195 elseif featuretype=="substitution" then
20196  local default=scripts.dflt
20197  if default then
20198   if autoscript=="substitution" or autoscript==true then
20199    return default
20200   end
20201  end
20202 end
20203end
20204function otffeatures.checkeddefaultlanguage(featuretype,autolanguage,languages)
20205 if featuretype=="position" then
20206  local default=languages.dflt
20207  if default then
20208   if autolanguage=="position" or autolanguage==true then
20209    return default
20210   else
20211    report_otf("language feature %s not applied, enable default positioning")
20212   end
20213  else
20214  end
20215 elseif featuretype=="substitution" then
20216  local default=languages.dflt
20217  if default then
20218   if autolanguage=="substitution" or autolanguage==true then
20219    return default
20220   end
20221  end
20222 end
20223end
20224
20225end -- closure
20226
20227do -- begin closure to overcome local limits and interference
20228
20229if not modules then modules={} end modules ["font-ott"]={
20230 version=1.001,
20231 comment="companion to font-ini.mkiv",
20232 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
20233 copyright="PRAGMA ADE / ConTeXt Development Team",
20234 license="see context related readme files",
20235}
20236local type,next,tonumber,tostring,rawget,rawset=type,next,tonumber,tostring,rawget,rawset
20237local gsub,lower,format,match,gmatch,find=string.gsub,string.lower,string.format,string.match,string.gmatch,string.find
20238local sequenced=table.sequenced
20239local is_boolean=string.is_boolean
20240local setmetatableindex=table.setmetatableindex
20241local setmetatablenewindex=table.setmetatablenewindex
20242local allocate=utilities.storage.allocate
20243local fonts=fonts
20244local otf=fonts.handlers.otf
20245local otffeatures=otf.features
20246local tables=otf.tables or {}
20247otf.tables=tables
20248local statistics=otf.statistics or {}
20249otf.statistics=statistics
20250local scripts=allocate {
20251 ["adlm"]="adlam",
20252 ["aghb"]="caucasian albanian",
20253 ["ahom"]="ahom",
20254 ["arab"]="arabic",
20255 ["armi"]="imperial aramaic",
20256 ["armn"]="armenian",
20257 ["avst"]="avestan",
20258 ["bali"]="balinese",
20259 ["bamu"]="bamum",
20260 ["bass"]="bassa vah",
20261 ["batk"]="batak",
20262 ["beng"]="bengali",
20263 ["bhks"]="bhaiksuki",
20264 ["bng2"]="bengali variant 2",
20265 ["bopo"]="bopomofo",
20266 ["brah"]="brahmi",
20267 ["brai"]="braille",
20268 ["bugi"]="buginese",
20269 ["buhd"]="buhid",
20270 ["byzm"]="byzantine music",
20271 ["cakm"]="chakma",
20272 ["cans"]="canadian syllabics",
20273 ["cari"]="carian",
20274 ["cham"]="cham",
20275 ["cher"]="cherokee",
20276 ["chrs"]="chorasmian",
20277 ["copt"]="coptic",
20278 ["cpmn"]="cypro-minoan",
20279 ["cprt"]="cypriot syllabary",
20280 ["cyrl"]="cyrillic",
20281 ["dev2"]="devanagari variant 2",
20282 ["deva"]="devanagari",
20283 ["diak"]="dives akuru",
20284 ["dogr"]="dogra",
20285 ["dsrt"]="deseret",
20286 ["dupl"]="duployan",
20287 ["egyp"]="egyptian heiroglyphs",
20288 ["elba"]="elbasan",
20289 ["elym"]="elymaic",
20290 ["ethi"]="ethiopic",
20291 ["geor"]="georgian",
20292 ["gjr2"]="gujarati variant 2",
20293 ["glag"]="glagolitic",
20294 ["gong"]="gunjala gondi",
20295 ["gonm"]="masaram gondi",
20296 ["goth"]="gothic",
20297 ["gran"]="grantha",
20298 ["grek"]="greek",
20299 ["gujr"]="gujarati",
20300 ["gur2"]="gurmukhi variant 2",
20301 ["guru"]="gurmukhi",
20302 ["hang"]="hangul",
20303 ["hani"]="cjk ideographic",
20304 ["hano"]="hanunoo",
20305 ["hatr"]="hatran",
20306 ["hebr"]="hebrew",
20307 ["hluw"]="anatolian hieroglyphs",
20308 ["hmng"]="pahawh hmong",
20309 ["hmnp"]="nyiakeng puachue hmong",
20310 ["hung"]="old hungarian",
20311 ["ital"]="old italic",
20312 ["jamo"]="hangul jamo",
20313 ["java"]="javanese",
20314 ["kali"]="kayah li",
20315 ["kana"]="hiragana and katakana",
20316 ["khar"]="kharosthi",
20317 ["khmr"]="khmer",
20318 ["khoj"]="khojki",
20319 ["kits"]="khitan small script",
20320 ["knd2"]="kannada variant 2",
20321 ["knda"]="kannada",
20322 ["kthi"]="kaithi",
20323 ["lana"]="tai tham",
20324 ["lao" ]="lao",
20325 ["latn"]="latin",
20326 ["lepc"]="lepcha",
20327 ["limb"]="limbu",
20328 ["lina"]="linear a",
20329 ["linb"]="linear b",
20330 ["lisu"]="lisu",
20331 ["lyci"]="lycian",
20332 ["lydi"]="lydian",
20333 ["mahj"]="mahajani",
20334 ["maka"]="makasar",
20335 ["mand"]="mandaic and mandaean",
20336 ["mani"]="manichaean",
20337 ["marc"]="marchen",
20338 ["math"]="mathematical alphanumeric symbols",
20339 ["medf"]="medefaidrin",
20340 ["mend"]="mende kikakui",
20341 ["merc"]="meroitic cursive",
20342 ["mero"]="meroitic hieroglyphs",
20343 ["mlm2"]="malayalam variant 2",
20344 ["mlym"]="malayalam",
20345 ["modi"]="modi",
20346 ["mong"]="mongolian",
20347 ["mroo"]="mro",
20348 ["mtei"]="meitei Mayek",
20349 ["mult"]="multani",
20350 ["musc"]="musical symbols",
20351 ["mym2"]="myanmar variant 2",
20352 ["mymr"]="myanmar",
20353 ["nand"]="nandinagari",
20354 ["narb"]="old north arabian",
20355 ["nbat"]="nabataean",
20356 ["newa"]="newa",
20357 ["nko" ]='n"ko',
20358 ["nshu"]="nüshu",
20359 ["ogam"]="ogham",
20360 ["olck"]="ol chiki",
20361 ["orkh"]="old turkic and orkhon runic",
20362 ["ory2"]="odia variant 2",
20363 ["orya"]="odia",
20364 ["osge"]="osage",
20365 ["osma"]="osmanya",
20366 ["ougr"]="old uyghur",
20367 ["palm"]="palmyrene",
20368 ["pauc"]="pau cin hau",
20369 ["perm"]="old permic",
20370 ["phag"]="phags-pa",
20371 ["phli"]="inscriptional pahlavi",
20372 ["phlp"]="psalter pahlavi",
20373 ["phnx"]="phoenician",
20374 ["plrd"]="miao",
20375 ["prti"]="inscriptional parthian",
20376 ["rjng"]="rejang",
20377 ["rohg"]="hanifi rohingya",
20378 ["runr"]="runic",
20379 ["samr"]="samaritan",
20380 ["sarb"]="old south arabian",
20381 ["saur"]="saurashtra",
20382 ["sgnw"]="sign writing",
20383 ["shaw"]="shavian",
20384 ["shrd"]="sharada",
20385 ["sidd"]="siddham",
20386 ["sind"]="khudawadi",
20387 ["sinh"]="sinhala",
20388 ["sogd"]="sogdian",
20389 ["sogo"]="old sogdian",
20390 ["sora"]="sora sompeng",
20391 ["soyo"]="soyombo",
20392 ["sund"]="sundanese",
20393 ["sylo"]="syloti nagri",
20394 ["syrc"]="syriac",
20395 ["tagb"]="tagbanwa",
20396 ["takr"]="takri",
20397 ["tale"]="tai le",
20398 ["talu"]="new tai lue",
20399 ["taml"]="tamil",
20400 ["tang"]="tangut",
20401 ["tavt"]="tai viet",
20402 ["tel2"]="telugu variant 2",
20403 ["telu"]="telugu",
20404 ["tfng"]="tifinagh",
20405 ["tglg"]="tagalog",
20406 ["thaa"]="thaana",
20407 ["thai"]="thai",
20408 ["tibt"]="tibetan",
20409 ["tirh"]="tirhuta",
20410 ["tnsa"]="tangsa",
20411 ["tml2"]="tamil variant 2",
20412 ["toto"]="toto",
20413 ["ugar"]="ugaritic cuneiform",
20414 ["vai" ]="vai",
20415 ["wara"]="warang citi",
20416 ["wcho"]="wancho",
20417 ["xpeo"]="old persian cuneiform",
20418 ["xsux"]="sumero-akkadian cuneiform",
20419 ["yezi"]="yezidi",
20420 ["yi"  ]="yi",
20421 ["zanb"]="zanabazar square",
20422}
20423local languages=allocate {
20424 ["aba" ]="abaza",
20425 ["abk" ]="abkhazian",
20426 ["ach" ]="acholi",
20427 ["acr" ]="achi",
20428 ["ady" ]="adyghe",
20429 ["afk" ]="afrikaans",
20430 ["afr" ]="afar",
20431 ["agw" ]="agaw",
20432 ["aio" ]="aiton",
20433 ["aka" ]="akan",
20434 ["akb" ]="batak angkola",
20435 ["als" ]="alsatian",
20436 ["alt" ]="altai",
20437 ["amh" ]="amharic",
20438 ["ang" ]="anglo-saxon",
20439 ["apph"]="phonetic transcription—americanist conventions",
20440 ["ara" ]="arabic",
20441 ["arg" ]="aragonese",
20442 ["ari" ]="aari",
20443 ["ark" ]="rakhine",
20444 ["asm" ]="assamese",
20445 ["ast" ]="asturian",
20446 ["ath" ]="athapaskan",
20447 ["avn" ]="avatime",
20448 ["avr" ]="avar",
20449 ["awa" ]="awadhi",
20450 ["aym" ]="aymara",
20451 ["azb" ]="torki",
20452 ["aze" ]="azerbaijani",
20453 ["bad" ]="badaga",
20454 ["bad0"]="banda",
20455 ["bag" ]="baghelkhandi",
20456 ["bal" ]="balkar",
20457 ["ban" ]="balinese",
20458 ["bar" ]="bavarian",
20459 ["bau" ]="baulé",
20460 ["bbc" ]="batak toba",
20461 ["bbr" ]="berber",
20462 ["bch" ]="bench",
20463 ["bcr" ]="bible cree",
20464 ["bdy" ]="bandjalang",
20465 ["bel" ]="belarussian",
20466 ["bem" ]="bemba",
20467 ["ben" ]="bengali",
20468 ["bgc" ]="haryanvi",
20469 ["bgq" ]="bagri",
20470 ["bgr" ]="bulgarian",
20471 ["bhi" ]="bhili",
20472 ["bho" ]="bhojpuri",
20473 ["bik" ]="bikol",
20474 ["bil" ]="bilen",
20475 ["bis" ]="bislama",
20476 ["bjj" ]="kanauji",
20477 ["bkf" ]="blackfoot",
20478 ["bli" ]="baluchi",
20479 ["blk" ]="pa'o karen",
20480 ["bln" ]="balante",
20481 ["blt" ]="balti",
20482 ["bmb" ]="bambara (bamanankan)",
20483 ["bml" ]="bamileke",
20484 ["bos" ]="bosnian",
20485 ["bpy" ]="bishnupriya manipuri",
20486 ["bre" ]="breton",
20487 ["brh" ]="brahui",
20488 ["bri" ]="braj bhasha",
20489 ["brm" ]="burmese",
20490 ["brx" ]="bodo",
20491 ["bsh" ]="bashkir",
20492 ["bsk" ]="burushaski",
20493 ["bta" ]="batak alas kluet",
20494 ["btd" ]="batak dairi (pakpak)",
20495 ["bti" ]="beti",
20496 ["btm" ]="batak mandailing",
20497 ["bts" ]="batak simalungun",
20498 ["btx" ]="batak karo",
20499 ["bug" ]="bugis",
20500 ["byv" ]="medumba",
20501 ["cak" ]="kaqchikel",
20502 ["cat" ]="catalan",
20503 ["cbk" ]="zamboanga chavacano",
20504 ["cchn"]="chinantec",
20505 ["ceb" ]="cebuano",
20506 ["cgg" ]="chiga",
20507 ["cha" ]="chamorro",
20508 ["che" ]="chechen",
20509 ["chg" ]="chaha gurage",
20510 ["chh" ]="chattisgarhi",
20511 ["chi" ]="chichewa (chewa, nyanja)",
20512 ["chk" ]="chukchi",
20513 ["chk0"]="chuukese",
20514 ["cho" ]="choctaw",
20515 ["chp" ]="chipewyan",
20516 ["chr" ]="cherokee",
20517 ["chu" ]="chuvash",
20518 ["chy" ]="cheyenne",
20519 ["cja" ]="western cham",
20520 ["cjm" ]="eastern cham",
20521 ["cmr" ]="comorian",
20522 ["cop" ]="coptic",
20523 ["cor" ]="cornish",
20524 ["cos" ]="corsican",
20525 ["cpp" ]="creoles",
20526 ["cre" ]="cree",
20527 ["crr" ]="carrier",
20528 ["crt" ]="crimean tatar",
20529 ["csb" ]="kashubian",
20530 ["csl" ]="church slavonic",
20531 ["csy" ]="czech",
20532 ["ctg" ]="chittagonian",
20533 ["ctt" ]="wayanad chetti",
20534 ["cuk" ]="san blas kuna",
20535 ["dag" ]="dagbani",
20536 ["dan" ]="danish",
20537 ["dar" ]="dargwa",
20538 ["dax" ]="dayi",
20539 ["dcr" ]="woods cree",
20540 ["deu" ]="german",
20541 ["dgo" ]="dogri (individual language)",
20542 ["dgr" ]="dogri (macro language)",
20543 ["dhg" ]="dhangu",
20544 ["dhv" ]="divehi (dhivehi, maldivian)",
20545 ["diq" ]="dimli",
20546 ["div" ]="divehi (dhivehi, maldivian)",
20547 ["djr" ]="zarma",
20548 ["djr0"]="djambarrpuyngu",
20549 ["dng" ]="dangme",
20550 ["dnj" ]="dan",
20551 ["dnk" ]="dinka",
20552 ["dri" ]="dari",
20553 ["duj" ]="dhuwal",
20554 ["dun" ]="dungan",
20555 ["dzn" ]="dzongkha",
20556 ["ebi" ]="ebira",
20557 ["ecr" ]="eastern cree",
20558 ["edo" ]="edo",
20559 ["efi" ]="efik",
20560 ["ell" ]="greek",
20561 ["emk" ]="eastern maninkakan",
20562 ["eng" ]="english",
20563 ["erz" ]="erzya",
20564 ["esp" ]="spanish",
20565 ["esu" ]="central yupik",
20566 ["eti" ]="estonian",
20567 ["euq" ]="basque",
20568 ["evk" ]="evenki",
20569 ["evn" ]="even",
20570 ["ewe" ]="ewe",
20571 ["fan" ]="french antillean",
20572 ["fan0"]=" fang",
20573 ["far" ]="persian",
20574 ["fat" ]="fanti",
20575 ["fin" ]="finnish",
20576 ["fji" ]="fijian",
20577 ["fle" ]="dutch (flemish)",
20578 ["fmp" ]="fe’fe’",
20579 ["fne" ]="forest nenets",
20580 ["fon" ]="fon",
20581 ["fos" ]="faroese",
20582 ["fra" ]="french",
20583 ["frc" ]="cajun french",
20584 ["fri" ]="frisian",
20585 ["frl" ]="friulian",
20586 ["frp" ]="arpitan",
20587 ["fta" ]="futa",
20588 ["ful" ]="fulah",
20589 ["fuv" ]="nigerian fulfulde",
20590 ["gad" ]="ga",
20591 ["gae" ]="scottish gaelic (gaelic)",
20592 ["gag" ]="gagauz",
20593 ["gal" ]="galician",
20594 ["gar" ]="garshuni",
20595 ["gaw" ]="garhwali",
20596 ["gez" ]="ge'ez",
20597 ["gih" ]="githabul",
20598 ["gil" ]="gilyak",
20599 ["gil0"]="kiribati (gilbertese)",
20600 ["gkp" ]="kpelle (guinea)",
20601 ["glk" ]="gilaki",
20602 ["gmz" ]="gumuz",
20603 ["gnn" ]="gumatj",
20604 ["gog" ]="gogo",
20605 ["gon" ]="gondi",
20606 ["grn" ]="greenlandic",
20607 ["gro" ]="garo",
20608 ["gua" ]="guarani",
20609 ["guc" ]="wayuu",
20610 ["guf" ]="gupapuyngu",
20611 ["guj" ]="gujarati",
20612 ["guz" ]="gusii",
20613 ["hai" ]="haitian (haitian creole)",
20614 ["hai0"]="haida",
20615 ["hal" ]="halam",
20616 ["har" ]="harauti",
20617 ["hau" ]="hausa",
20618 ["haw" ]="hawaiian",
20619 ["hay" ]="haya",
20620 ["haz" ]="hazaragi",
20621 ["hmz" ]="hmong shuat",
20622 ["hbn" ]="hammer-banna",
20623 ["hei" ]="heiltsuk",
20624 ["her" ]="herero",
20625 ["hil" ]="hiligaynon",
20626 ["hin" ]="hindi",
20627 ["hma" ]="high mari",
20628 ["hmn" ]="hmong",
20629 ["hmo" ]="hiri motu",
20630 ["hnd" ]="hindko",
20631 ["ho"  ]="ho",
20632 ["hri" ]="harari",
20633 ["hrv" ]="croatian",
20634 ["hun" ]="hungarian",
20635 ["hye" ]="armenian",
20636 ["hye0"]="armenian east",
20637 ["iba" ]="iban",
20638 ["ibb" ]="ibibio",
20639 ["ibo" ]="igbo",
20640 ["ido" ]="ido",
20641 ["ijo" ]="ijo languages",
20642 ["ile" ]="interlingue",
20643 ["ilo" ]="ilokano",
20644 ["ina" ]="interlingua",
20645 ["ind" ]="indonesian",
20646 ["ing" ]="ingush",
20647 ["inu" ]="inuktitut",
20648 ["inuk"]="nunavik inuktitut",
20649 ["ipk" ]="inupiat",
20650 ["ipph"]="phonetic transcription—ipa conventions",
20651 ["iri" ]="irish",
20652 ["irt" ]="irish traditional",
20653 ["uri" ]="irula",
20654 ["isl" ]="icelandic",
20655 ["ism" ]="inari sami",
20656 ["ita" ]="italian",
20657 ["iwr" ]="hebrew",
20658 ["jam" ]="jamaican creole",
20659 ["jan" ]="japanese",
20660 ["jav" ]="javanese",
20661 ["jbo" ]="lojban",
20662 ["jct" ]="krymchak",
20663 ["jii" ]="yiddish",
20664 ["jud" ]="ladino",
20665 ["jul" ]="jula",
20666 ["kab" ]="kabardian",
20667 ["kab0"]="kabyle",
20668 ["kac" ]="kachchi",
20669 ["kal" ]="kalenjin",
20670 ["kan" ]="kannada",
20671 ["kar" ]="karachay",
20672 ["kat" ]="georgian",
20673 ["kaw" ]="kawi (old javanese)",
20674 ["kaz" ]="kazakh",
20675 ["kde" ]="makonde",
20676 ["kea" ]="kabuverdianu (crioulo)",
20677 ["keb" ]="kebena",
20678 ["kek" ]="kekchi",
20679 ["kge" ]="khutsuri georgian",
20680 ["kha" ]="khakass",
20681 ["khk" ]="khanty-kazim",
20682 ["khm" ]="khmer",
20683 ["khs" ]="khanty-shurishkar",
20684 ["kht" ]="khamti shan",
20685 ["khv" ]="khanty-vakhi",
20686 ["khw" ]="khowar",
20687 ["kik" ]="kikuyu (gikuyu)",
20688 ["kir" ]="kirghiz (kyrgyz)",
20689 ["kis" ]="kisii",
20690 ["kiu" ]="kirmanjki",
20691 ["kjd" ]="southern kiwai",
20692 ["kjp" ]="eastern pwo karen",
20693 ["kjz" ]="bumthangkha",
20694 ["kkn" ]="kokni",
20695 ["klm" ]="kalmyk",
20696 ["kmb" ]="kamba",
20697 ["kmn" ]="kumaoni",
20698 ["kmo" ]="komo",
20699 ["kms" ]="komso",
20700 ["kmz" ]="khorasani turkic",
20701 ["knr" ]="kanuri",
20702 ["kod" ]="kodagu",
20703 ["koh" ]="korean old hangul",
20704 ["kok" ]="konkani",
20705 ["kom" ]="komi",
20706 ["kon" ]="kikongo",
20707 ["kon0"]="kongo",
20708 ["kop" ]="komi-permyak",
20709 ["kor" ]="korean",
20710 ["kos" ]="kosraean",
20711 ["koz" ]="komi-zyrian",
20712 ["kpl" ]="kpelle",
20713 ["kri" ]="krio",
20714 ["krk" ]="karakalpak",
20715 ["krl" ]="karelian",
20716 ["krm" ]="karaim",
20717 ["krn" ]="karen",
20718 ["krt" ]="koorete",
20719 ["ksh" ]="kashmiri",
20720 ["ksh0"]="ripuarian",
20721 ["ksi" ]="khasi",
20722 ["ksm" ]="kildin sami",
20723 ["ksw" ]="s’gaw karen",
20724 ["kua" ]="kuanyama",
20725 ["kui" ]="kui",
20726 ["kul" ]="kulvi",
20727 ["kum" ]="kumyk",
20728 ["kur" ]="kurdish",
20729 ["kuu" ]="kurukh",
20730 ["kuy" ]="kuy",
20731 ["kwk" ]="kwakʼwala",
20732 ["kyk" ]="koryak",
20733 ["kyu" ]="western kayah",
20734 ["lad" ]="ladin",
20735 ["lah" ]="lahuli",
20736 ["lak" ]="lak",
20737 ["lam" ]="lambani",
20738 ["lao" ]="lao",
20739 ["lat" ]="latin",
20740 ["laz" ]="laz",
20741 ["lcr" ]="l-cree",
20742 ["ldk" ]="ladakhi",
20743 ["lef" ]="lelemi",
20744 ["lez" ]="lezgi",
20745 ["lij" ]="ligurian",
20746 ["lim" ]="limburgish",
20747 ["lin" ]="lingala",
20748 ["lis" ]="lisu",
20749 ["ljp" ]="lampung",
20750 ["lki" ]="laki",
20751 ["lma" ]="low mari",
20752 ["lmb" ]="limbu",
20753 ["lmo" ]="lombard",
20754 ["lmw" ]="lomwe",
20755 ["lom" ]="loma",
20756 ["lpo" ]="lipo",
20757 ["lrc" ]="luri",
20758 ["lsb" ]="lower sorbian",
20759 ["lsm" ]="lule sami",
20760 ["lth" ]="lithuanian",
20761 ["ltz" ]="luxembourgish",
20762 ["lua" ]="luba-lulua",
20763 ["lub" ]="luba-katanga",
20764 ["lug" ]="ganda",
20765 ["luh" ]="luyia",
20766 ["luo" ]="luo",
20767 ["lvi" ]="latvian",
20768 ["mad" ]="madura",
20769 ["mag" ]="magahi",
20770 ["mah" ]="marshallese",
20771 ["maj" ]="majang",
20772 ["mak" ]="makhuwa",
20773 ["mal" ]="malayalam",
20774 ["mam" ]="mam",
20775 ["man" ]="mansi",
20776 ["map" ]="mapudungun",
20777 ["mar" ]="marathi",
20778 ["maw" ]="marwari",
20779 ["mbn" ]="mbundu",
20780 ["mbo" ]="mbo",
20781 ["mch" ]="manchu",
20782 ["mcr" ]="moose cree",
20783 ["mde" ]="mende",
20784 ["mdr" ]="mandar",
20785 ["men" ]="me'en",
20786 ["mer" ]="meru",
20787 ["mfa" ]="pattani malay",
20788 ["mfe" ]="morisyen",
20789 ["min" ]="minangkabau",
20790 ["miz" ]="mizo",
20791 ["mkd" ]="macedonian",
20792 ["mkr" ]="makasar",
20793 ["mkw" ]="kituba",
20794 ["mle" ]="male",
20795 ["mlg" ]="malagasy",
20796 ["mln" ]="malinke",
20797 ["mlr" ]="malayalam reformed",
20798 ["mly" ]="malay",
20799 ["mnd" ]="mandinka",
20800 ["mng" ]="mongolian",
20801 ["mni" ]="manipuri",
20802 ["mnk" ]="maninka",
20803 ["mnx" ]="manx",
20804 ["moh" ]="mohawk",
20805 ["mok" ]="moksha",
20806 ["mol" ]="moldavian",
20807 ["mon" ]="mon",
20808 ["mnw" ]="thailand mon",
20809 ["mor" ]="moroccan",
20810 ["mos" ]="mossi",
20811 ["mri" ]="maori",
20812 ["mth" ]="maithili",
20813 ["mts" ]="maltese",
20814 ["mun" ]="mundari",
20815 ["mus" ]="muscogee",
20816 ["mwl" ]="mirandese",
20817 ["mww" ]="hmong daw",
20818 ["myn" ]="mayan",
20819 ["mzn" ]="mazanderani",
20820 ["nag" ]="naga-assamese",
20821 ["nah" ]="nahuatl",
20822 ["nan" ]="nanai",
20823 ["nap" ]="neapolitan",
20824 ["nas" ]="naskapi",
20825 ["nau" ]="nauruan",
20826 ["nav" ]="navajo",
20827 ["ncr" ]="n-cree",
20828 ["ndb" ]="ndebele",
20829 ["ndc" ]="ndau",
20830 ["ndg" ]="ndonga",
20831 ["nds" ]="low saxon",
20832 ["nep" ]="nepali",
20833 ["new" ]="newari",
20834 ["nga" ]="ngbaka",
20835 ["ngr" ]="nagari",
20836 ["nhc" ]="norway house cree",
20837 ["nis" ]="nisi",
20838 ["niu" ]="niuean",
20839 ["nkl" ]="nyankole",
20840 ["nko" ]="n'ko",
20841 ["nld" ]="dutch",
20842 ["noe" ]="nimadi",
20843 ["nog" ]="nogai",
20844 ["nor" ]="norwegian",
20845 ["nov" ]="novial",
20846 ["nsm" ]="northern sami",
20847 ["nso" ]="northern sotho",
20848 ["nta" ]="northern tai",
20849 ["nto" ]="esperanto",
20850 ["nym" ]="nyamwezi",
20851 ["nyn" ]="norwegian nynorsk",
20852 ["nza" ]="mbembe tigon",
20853 ["oci" ]="occitan",
20854 ["ocr" ]="oji-cree",
20855 ["ojb" ]="ojibway",
20856 ["ori" ]="odia",
20857 ["oro" ]="oromo",
20858 ["oss" ]="ossetian",
20859 ["paa" ]="palestinian aramaic",
20860 ["pag" ]="pangasinan",
20861 ["pal" ]="pali",
20862 ["pam" ]="pampangan",
20863 ["pan" ]="punjabi",
20864 ["pap" ]="palpa",
20865 ["pap0"]="papiamentu",
20866 ["pas" ]="pashto",
20867 ["pau" ]="palauan",
20868 ["pcc" ]="bouyei",
20869 ["pcd" ]="picard",
20870 ["pdc" ]="pennsylvania german",
20871 ["pgr" ]="polytonic greek",
20872 ["phk" ]="phake",
20873 ["pih" ]="norfolk",
20874 ["pil" ]="filipino",
20875 ["plg" ]="palaung",
20876 ["plk" ]="polish",
20877 ["pms" ]="piemontese",
20878 ["pnb" ]="western panjabi",
20879 ["poh" ]="pocomchi",
20880 ["pon" ]="pohnpeian",
20881 ["pro" ]="provencal",
20882 ["ptg" ]="portuguese",
20883 ["pwo" ]="western pwo karen",
20884 ["qin" ]="chin",
20885 ["quc" ]="k’iche’",
20886 ["quh" ]="quechua (bolivia)",
20887 ["quz" ]="quechua",
20888 ["qvi" ]="quechua (ecuador)",
20889 ["qwh" ]="quechua (peru)",
20890 ["raj" ]="rajasthani",
20891 ["rar" ]="rarotongan",
20892 ["rbu" ]="russian buriat",
20893 ["rcr" ]="r-cree",
20894 ["rej" ]="rejang",
20895 ["rhg" ]="rohingya",
20896 ["ria" ]="riang",
20897 ["rif" ]="tarifit",
20898 ["rit" ]="ritarungo",
20899 ["rkw" ]="arakwal",
20900 ["rms" ]="romansh",
20901 ["rmy" ]="vlax romani",
20902 ["rom" ]="romanian",
20903 ["roy" ]="romany",
20904 ["rsy" ]="rusyn",
20905 ["rtm" ]="rotuman",
20906 ["rua" ]="kinyarwanda",
20907 ["run" ]="rundi",
20908 ["rup" ]="aromanian",
20909 ["rus" ]="russian",
20910 ["sad" ]="sadri",
20911 ["san" ]="sanskrit",
20912 ["sas" ]="sasak",
20913 ["sat" ]="santali",
20914 ["say" ]="sayisi",
20915 ["scn" ]="sicilian",
20916 ["sco" ]="scots",
20917 ["scs" ]="north slavey",
20918 ["sek" ]="sekota",
20919 ["sel" ]="selkup",
20920 ["sfm" ]="small flowery miao",
20921 ["sga" ]="old irish",
20922 ["sgo" ]="sango",
20923 ["sgs" ]="samogitian",
20924 ["shi" ]="tachelhit",
20925 ["shn" ]="shan",
20926 ["sib" ]="sibe",
20927 ["sid" ]="sidamo",
20928 ["sig" ]="silte gurage",
20929 ["sks" ]="skolt sami",
20930 ["sky" ]="slovak",
20931 ["sla" ]="slavey",
20932 ["slv" ]="slovenian",
20933 ["sml" ]="somali",
20934 ["smo" ]="samoan",
20935 ["sna" ]="sena",
20936 ["sna0"]="shona",
20937 ["snd" ]="sindhi",
20938 ["snh" ]="sinhala (sinhalese)",
20939 ["snk" ]="soninke",
20940 ["sog" ]="sodo gurage",
20941 ["sop" ]="songe",
20942 ["sot" ]="southern sotho",
20943 ["sqi" ]="albanian",
20944 ["srb" ]="serbian",
20945 ["srd" ]="sardinian",
20946 ["srk" ]="saraiki",
20947 ["srr" ]="serer",
20948 ["ssl" ]="south slavey",
20949 ["ssm" ]="southern sami",
20950 ["stq" ]="saterland frisian",
20951 ["suk" ]="sukuma",
20952 ["sun" ]="sundanese",
20953 ["sur" ]="suri",
20954 ["sva" ]="svan",
20955 ["sve" ]="swedish",
20956 ["swa" ]="swadaya aramaic",
20957 ["swk" ]="swahili",
20958 ["swz" ]="swati",
20959 ["sxt" ]="sutu",
20960 ["sxu" ]="upper saxon",
20961 ["syl" ]="sylheti",
20962 ["syr" ]="syriac",
20963 ["syre"]="estrangela syriac",
20964 ["syrj"]="western syriac",
20965 ["syrn"]="eastern syriac",
20966 ["szl" ]="silesian",
20967 ["tab" ]="tabasaran",
20968 ["taj" ]="tajiki",
20969 ["tam" ]="tamil",
20970 ["tat" ]="tatar",
20971 ["tcr" ]="th-cree",
20972 ["tdd" ]="dehong dai",
20973 ["tel" ]="telugu",
20974 ["tet" ]="tetum",
20975 ["tgl" ]="tagalog",
20976 ["tgn" ]="tongan",
20977 ["tgr" ]="tigre",
20978 ["tgy" ]="tigrinya",
20979 ["tha" ]="thai",
20980 ["tht" ]="tahitian",
20981 ["tib" ]="tibetan",
20982 ["tiv" ]="tiv",
20983 ["tj;" ]="tai laing",
20984 ["tkm" ]="turkmen",
20985 ["tli" ]="tlingit",
20986 ["tmh" ]="tamashek",
20987 ["tmn" ]="temne",
20988 ["tna" ]="tswana",
20989 ["tne" ]="tundra nenets",
20990 ["tng" ]="tonga",
20991 ["tod" ]="todo",
20992 ["tod0"]="toma",
20993 ["tpi" ]="tok pisin",
20994 ["trk" ]="turkish",
20995 ["tsg" ]="tsonga",
20996 ["tsj" ]="tshangla",
20997 ["tua" ]="turoyo aramaic",
20998 ["tul" ]="tulu",
20999 ["tum" ]="tumbuka",
21000 ["tuv" ]="tuvin",
21001 ["tvl" ]="tuvalu",
21002 ["twi" ]="twi",
21003 ["tyz" ]="tày",
21004 ["tzm" ]="tamazight",
21005 ["tzo" ]="tzotzil",
21006 ["udm" ]="udmurt",
21007 ["ukr" ]="ukrainian",
21008 ["umb" ]="umbundu",
21009 ["urd" ]="urdu",
21010 ["usb" ]="upper sorbian",
21011 ["uyg" ]="uyghur",
21012 ["uzb" ]="uzbek",
21013 ["vec" ]="venetian",
21014 ["ven" ]="venda",
21015 ["vit" ]="vietnamese",
21016 ["vol" ]="volapük",
21017 ["vro" ]="võro",
21018 ["wa"  ]="wa",
21019 ["wag" ]="wagdi",
21020 ["war" ]="waray-waray",
21021 ["wci" ]="waci gbe",
21022 ["wcr" ]="west-cree",
21023 ["wel" ]="welsh",
21024 ["wlf" ]="wolof",
21025 ["wln" ]="walloon",
21026 ["wtm" ]="mewati",
21027 ["xbd" ]="",
21028 ["xhs" ]="xhosa",
21029 ["xjb" ]="minjangbal",
21030 ["xkf" ]="khengkha",
21031 ["xog" ]="soga",
21032 ["xpe" ]="kpelle (liberia)",
21033 ["xub" ]="bette kuruma",
21034 ["xuj" ]="jennu kuruma",
21035 ["yak" ]="sakha",
21036 ["yao" ]="yao",
21037 ["yap" ]="yapese",
21038 ["yba" ]="yoruba",
21039 ["ycr" ]="y-cree",
21040 ["ygp" ]="gepo",
21041 ["yic" ]="yi classic",
21042 ["yim" ]="yi modern",
21043 ["yna" ]="aluo",
21044 ["ywq" ]="wuding-luquan",
21045 ["zea" ]="zealandic",
21046 ["zgh" ]="standard morrocan tamazigh",
21047 ["zha" ]="zhuang",
21048 ["zhh" ]="chinese, hong kong sar",
21049 ["zho" ]="chinese traditional, macao",
21050 ["zhp" ]="chinese phonetic",
21051 ["zhs" ]="chinese simplified",
21052 ["zht" ]="chinese traditional",
21053 ["znd" ]="zande",
21054 ["zul" ]="zulu",
21055 ["zza" ]="zazaki",
21056}
21057local features=allocate {
21058 ["aalt"]="access all alternates",
21059 ["abvf"]="above-base forms",
21060 ["abvm"]="above-base mark positioning",
21061 ["abvs"]="above-base substitutions",
21062 ["afrc"]="alternative fractions",
21063 ["akhn"]="akhands",
21064 ["blwf"]="below-base forms",
21065 ["blwm"]="below-base mark positioning",
21066 ["blws"]="below-base substitutions",
21067 ["c2pc"]="petite capitals from capitals",
21068 ["c2sc"]="small capitals from capitals",
21069 ["calt"]="contextual alternates",
21070 ["case"]="case-sensitive forms",
21071 ["ccmp"]="glyph composition/decomposition",
21072 ["cfar"]="conjunct form after ro",
21073 ["chws"]="contextual half-width spacing",
21074 ["cjct"]="conjunct forms",
21075 ["clig"]="contextual ligatures",
21076 ["cpct"]="centered cjk punctuation",
21077 ["cpsp"]="capital spacing",
21078 ["cswh"]="contextual swash",
21079 ["curs"]="cursive positioning",
21080 ["dflt"]="default processing",
21081 ["dist"]="distances",
21082 ["dlig"]="discretionary ligatures",
21083 ["dnom"]="denominators",
21084 ["dtls"]="dotless forms",
21085 ["expt"]="expert forms",
21086 ["falt"]="final glyph alternates",
21087 ["fin2"]="terminal forms #2",
21088 ["fin3"]="terminal forms #3",
21089 ["fina"]="terminal forms",
21090 ["flac"]="flattened accents over capitals",
21091 ["frac"]="fractions",
21092 ["fwid"]="full width",
21093 ["half"]="half forms",
21094 ["haln"]="halant forms",
21095 ["halt"]="alternate half width",
21096 ["hist"]="historical forms",
21097 ["hkna"]="horizontal kana alternates",
21098 ["hlig"]="historical ligatures",
21099 ["hngl"]="hangul",
21100 ["hojo"]="hojo kanji forms",
21101 ["hwid"]="half width",
21102 ["init"]="initial forms",
21103 ["isol"]="isolated forms",
21104 ["ital"]="italics",
21105 ["jalt"]="justification alternatives",
21106 ["jp04"]="jis2004 forms",
21107 ["jp78"]="jis78 forms",
21108 ["jp83"]="jis83 forms",
21109 ["jp90"]="jis90 forms",
21110 ["kern"]="kerning",
21111 ["lfbd"]="left bounds",
21112 ["liga"]="standard ligatures",
21113 ["ljmo"]="leading jamo forms",
21114 ["lnum"]="lining figures",
21115 ["locl"]="localized forms",
21116 ["ltra"]="left-to-right alternates",
21117 ["ltrm"]="left-to-right mirrored forms",
21118 ["mark"]="mark positioning",
21119 ["med2"]="medial forms #2",
21120 ["medi"]="medial forms",
21121 ["mgrk"]="mathematical greek",
21122 ["mkmk"]="mark to mark positioning",
21123 ["mset"]="mark positioning via substitution",
21124 ["nalt"]="alternate annotation forms",
21125 ["nlck"]="nlc kanji forms",
21126 ["nukt"]="nukta forms",
21127 ["numr"]="numerators",
21128 ["onum"]="old style figures",
21129 ["opbd"]="optical bounds",
21130 ["ordn"]="ordinals",
21131 ["ornm"]="ornaments",
21132 ["palt"]="proportional alternate width",
21133 ["pcap"]="petite capitals",
21134 ["pkna"]="proportional kana",
21135 ["pnum"]="proportional figures",
21136 ["pref"]="pre-base forms",
21137 ["pres"]="pre-base substitutions",
21138 ["pstf"]="post-base forms",
21139 ["psts"]="post-base substitutions",
21140 ["pwid"]="proportional widths",
21141 ["qwid"]="quarter widths",
21142 ["rand"]="randomize",
21143 ["rclt"]="required contextual alternates",
21144 ["rkrf"]="rakar forms",
21145 ["rlig"]="required ligatures",
21146 ["rphf"]="reph form",
21147 ["rtbd"]="right bounds",
21148 ["rtla"]="right-to-left alternates",
21149 ["rtlm"]="right to left mirrored forms",
21150 ["ruby"]="ruby notation forms",
21151 ["rvrn"]="required variation alternates",
21152 ["salt"]="stylistic alternates",
21153 ["sinf"]="scientific inferiors",
21154 ["size"]="optical size",
21155 ["smcp"]="small capitals",
21156 ["smpl"]="simplified forms",
21157 ["ssty"]="script style",
21158 ["stch"]="stretching glyph decomposition",
21159 ["subs"]="subscript",
21160 ["sups"]="superscript",
21161 ["swsh"]="swash",
21162 ["titl"]="titling",
21163 ["tjmo"]="trailing jamo forms",
21164 ["tnam"]="traditional name forms",
21165 ["tnum"]="tabular figures",
21166 ["trad"]="traditional forms",
21167 ["twid"]="third widths",
21168 ["unic"]="unicase",
21169 ["valt"]="alternate vertical metrics",
21170 ["vatu"]="vattu variants",
21171 ["vchw"]="vertical contextual half-width spacing",
21172 ["vert"]="vertical writing",
21173 ["vhal"]="alternate vertical half metrics",
21174 ["vjmo"]="vowel jamo forms",
21175 ["vkna"]="vertical kana alternates",
21176 ["vkrn"]="vertical kerning",
21177 ["vpal"]="proportional alternate vertical metrics",
21178 ["vrtr"]="vertical alternates for rotation",
21179 ["vrt2"]="vertical rotation",
21180 ["zero"]="slashed zero",
21181 ["trep"]="traditional tex replacements",
21182 ["tlig"]="traditional tex ligatures",
21183 ["ss.."]="stylistic set ..",
21184 ["cv.."]="character variant ..",
21185 ["js.."]="justification ..",
21186 ["dv.."]="devanagari ..",
21187 ["ml.."]="malayalam ..",
21188}
21189local baselines=allocate {
21190 ["hang"]="hanging baseline",
21191 ["icfb"]="ideographic character face bottom edge baseline",
21192 ["icft"]="ideographic character face tope edige baseline",
21193 ["ideo"]="ideographic em-box bottom edge baseline",
21194 ["idtp"]="ideographic em-box top edge baseline",
21195 ["math"]="mathematical centered baseline",
21196 ["romn"]="roman baseline"
21197}
21198tables.scripts=scripts
21199tables.languages=languages
21200tables.features=features
21201tables.baselines=baselines
21202local acceptscripts=true  directives.register("otf.acceptscripts",function(v) acceptscripts=v end)
21203local acceptlanguages=true  directives.register("otf.acceptlanguages",function(v) acceptlanguages=v end)
21204local report_checks=logs.reporter("fonts","checks")
21205if otffeatures.features then
21206 for k,v in next,otffeatures.features do
21207  features[k]=v
21208 end
21209 otffeatures.features=features
21210end
21211local function swapped(h)
21212 local r={}
21213 for k,v in next,h do
21214  r[gsub(v,"[^a-z0-9]","")]=k 
21215 end
21216 return r
21217end
21218local verbosescripts=allocate(swapped(scripts  ))
21219local verboselanguages=allocate(swapped(languages))
21220local verbosefeatures=allocate(swapped(features ))
21221local verbosebaselines=allocate(swapped(baselines))
21222local function resolve(t,k)
21223 if k then
21224  k=gsub(lower(k),"[^a-z0-9]","")
21225  local v=rawget(t,k)
21226  if v then
21227   return v
21228  end
21229 end
21230end
21231setmetatableindex(verbosescripts,resolve)
21232setmetatableindex(verboselanguages,resolve)
21233setmetatableindex(verbosefeatures,resolve)
21234setmetatableindex(verbosebaselines,resolve)
21235setmetatableindex(scripts,function(t,k)
21236 if k then
21237  k=lower(k)
21238  if k=="dflt" then
21239   return k
21240  end
21241  local v=rawget(t,k)
21242  if v then
21243   return v
21244  end
21245  k=gsub(k," ","")
21246  v=rawget(t,v)
21247  if v then
21248   return v
21249  elseif acceptscripts then
21250   report_checks("registering extra script %a",k)
21251   rawset(t,k,k)
21252   return k
21253  end
21254 end
21255 return "dflt"
21256end)
21257setmetatableindex(languages,function(t,k)
21258 if k then
21259  k=lower(k)
21260  if k=="dflt" then
21261   return k
21262  end
21263  local v=rawget(t,k)
21264  if v then
21265   return v
21266  end
21267  k=gsub(k," ","")
21268  v=rawget(t,v)
21269  if v then
21270   return v
21271  elseif acceptlanguages then
21272   report_checks("registering extra language %a",k)
21273   rawset(t,k,k)
21274   return k
21275  end
21276 end
21277 return "dflt"
21278end)
21279if setmetatablenewindex then
21280 setmetatablenewindex(languages,"ignore")
21281 setmetatablenewindex(scripts,"ignore")
21282 setmetatablenewindex(baselines,"ignore")
21283end
21284local function resolve(t,k)
21285 if k then
21286  k=lower(k)
21287  local v=rawget(t,k)
21288  if v then
21289   return v
21290  end
21291  k=gsub(k," ","")
21292  local v=rawget(t,k)
21293  if v then
21294   return v
21295  end
21296  local tag,dd=match(k,"(..)(%d+)")
21297  if tag and dd then
21298   local v=rawget(t,tag)
21299   if v then
21300    return v 
21301   else
21302    local v=rawget(t,tag.."..") 
21303    if v then
21304     return (gsub(v,"%.%.",tonumber(dd))) 
21305    end
21306   end
21307  end
21308 end
21309 return k 
21310end
21311setmetatableindex(features,resolve)
21312local function assign(t,k,v)
21313 if k and v then
21314  v=lower(v)
21315  rawset(t,k,v)
21316 end
21317end
21318if setmetatablenewindex then
21319 setmetatablenewindex(features,assign)
21320end
21321local checkers={
21322 rand=function(v)
21323  return v==true and "random" or v
21324 end
21325}
21326if not storage then
21327 return
21328end
21329local usedfeatures=statistics.usedfeatures or {}
21330statistics.usedfeatures=usedfeatures
21331table.setmetatableindex(usedfeatures,function(t,k) if k then local v={} t[k]=v return v end end) 
21332storage.register("fonts/otf/usedfeatures",usedfeatures,"fonts.handlers.otf.statistics.usedfeatures" )
21333local normalizedaxis=otf.readers.helpers.normalizedaxis or function(s) return s end
21334function otffeatures.normalize(features,wrap) 
21335 if features then
21336  local h={}
21337  for key,value in next,features do
21338   local k=lower(key)
21339   if k=="language" then
21340    local v=gsub(lower(value),"[^a-z0-9]","")
21341    h.language=rawget(verboselanguages,v) or (languages[v] and v) or "dflt" 
21342   elseif k=="script" then
21343    local v=gsub(lower(value),"[^a-z0-9]","")
21344    h.script=rawget(verbosescripts,v) or (scripts[v] and v) or "dflt" 
21345   elseif k=="axis" then
21346    h[k]=normalizedaxis(value)
21347   else
21348    local uk=usedfeatures[key]
21349    local uv=uk[value]
21350    if uv then
21351    else
21352     uv=tonumber(value) 
21353     if uv then
21354     elseif type(value)=="string" then
21355      local b=is_boolean(value)
21356      if type(b)=="nil" then
21357       if wrap and find(value,",") then
21358        uv="{"..lower(value).."}"
21359       else
21360        uv=lower(value)
21361       end
21362      else
21363       uv=b
21364      end
21365     elseif type(value)=="table" then
21366      uv=sequenced(t,",")
21367     else
21368      uv=value
21369     end
21370     if not rawget(features,k) then
21371      k=rawget(verbosefeatures,k) or k
21372     end
21373     local c=checkers[k]
21374     if c then
21375      uv=c(uv) or vc
21376     end
21377     uk[value]=uv
21378    end
21379    h[k]=uv
21380   end
21381  end
21382  return h
21383 end
21384end
21385
21386end -- closure
21387
21388do -- begin closure to overcome local limits and interference
21389
21390if not modules then modules={} end modules ['font-otl']={
21391 version=1.001,
21392 comment="companion to font-ini.mkiv",
21393 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
21394 copyright="PRAGMA ADE / ConTeXt Development Team",
21395 license="see context related readme files",
21396}
21397local lower=string.lower
21398local type,next,tonumber,tostring,unpack=type,next,tonumber,tostring,unpack
21399local abs=math.abs
21400local derivetable,sortedhash,remove=table.derive,table.sortedhash,table.remove
21401local formatters=string.formatters
21402local setmetatableindex=table.setmetatableindex
21403local allocate=utilities.storage.allocate
21404local registertracker=trackers.register
21405local registerdirective=directives.register
21406local starttiming=statistics.starttiming
21407local stoptiming=statistics.stoptiming
21408local elapsedtime=statistics.elapsedtime
21409local findbinfile=resolvers.findbinfile
21410local trace_loading=false  registertracker("otf.loading",function(v) trace_loading=v end)
21411local trace_features=false  registertracker("otf.features",function(v) trace_features=v end)
21412local trace_defining=false  registertracker("fonts.defining",function(v) trace_defining=v end)
21413local report_otf=logs.reporter("fonts","otf loading")
21414local fonts=fonts
21415local otf=fonts.handlers.otf
21416otf.version=3.144 
21417otf.cache=containers.define("fonts","otl",otf.version,true)
21418otf.svgcache=containers.define("fonts","svg",otf.version,true)
21419otf.pngcache=containers.define("fonts","png",otf.version,true)
21420otf.pdfcache=containers.define("fonts","pdf",otf.version,true)
21421otf.mpscache=containers.define("fonts","mps",otf.version,true)
21422otf.svgenabled=false
21423otf.pngenabled=false
21424local otfreaders=otf.readers
21425local hashes=fonts.hashes
21426local definers=fonts.definers
21427local readers=fonts.readers
21428local constructors=fonts.constructors
21429local otffeatures=constructors.features.otf
21430local registerotffeature=otffeatures.register
21431local otfenhancers=constructors.enhancers.otf
21432local registerotfenhancer=otfenhancers.register
21433local forceload=false
21434local cleanup=0  
21435local syncspace=true
21436local forcenotdef=false
21437local privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000 
21438local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes
21439local wildcard="*"
21440local default="dflt"
21441local formats=fonts.formats
21442formats.otf="opentype"
21443formats.ttf="truetype"
21444formats.ttc="truetype"
21445registerdirective("fonts.otf.loader.cleanup",function(v) cleanup=tonumber(v) or (v and 1) or 0 end)
21446registerdirective("fonts.otf.loader.force",function(v) forceload=v end)
21447registerdirective("fonts.otf.loader.syncspace",function(v) syncspace=v end)
21448registerdirective("fonts.otf.loader.forcenotdef",function(v) forcenotdef=v end)
21449registerotfenhancer("check extra features",function() end)
21450local checkmemory=utilities.lua and utilities.lua.checkmemory
21451local threshold=100 
21452local tracememory=false
21453registertracker("fonts.otf.loader.memory",function(v) tracememory=v end)
21454if not checkmemory then 
21455 local collectgarbage=collectgarbage
21456 checkmemory=function(previous,threshold) 
21457  local current=collectgarbage("count")
21458  if previous then
21459   local checked=(threshold or 64)*1024
21460   if current-previous>checked then
21461    collectgarbage("collect")
21462    current=collectgarbage("count")
21463   end
21464  end
21465  return current
21466 end
21467end
21468function otf.load(filename,sub,instance)
21469 local base=file.basename(file.removesuffix(filename))
21470 local name=file.removesuffix(base) 
21471 local attr=lfs.attributes(filename)
21472 local size=attr and attr.size or 0
21473 local time=attr and attr.modification or 0
21474 if sub=="" then
21475  sub=false
21476 end
21477 local hash=name
21478 if sub then
21479  hash=hash.."-"..sub
21480 end
21481 if instance then
21482  hash=hash.."-"..instance
21483 end
21484 hash=containers.cleanname(hash)
21485 local data=containers.read(otf.cache,hash)
21486 local reload=not data or data.size~=size or data.time~=time or data.tableversion~=otfreaders.tableversion
21487 if forceload then
21488  report_otf("forced reload of %a due to hard coded flag",filename)
21489  reload=true
21490 end
21491 if reload then
21492  report_otf("loading %a, hash %a",filename,hash)
21493  starttiming(otfreaders,true)
21494  data=otfreaders.loadfont(filename,sub or 1,instance) 
21495  if data then
21496   local used=checkmemory()
21497   local resources=data.resources
21498   local svgshapes=resources.svgshapes
21499   local pngshapes=resources.pngshapes
21500   if cleanup==0 then
21501    checkmemory(used,threshold,tracememory)
21502   end
21503   if svgshapes then
21504    resources.svgshapes=nil
21505    if otf.svgenabled then
21506     local timestamp=os.date()
21507     containers.write(otf.svgcache,hash,{
21508      svgshapes=svgshapes,
21509      timestamp=timestamp,
21510     })
21511     data.properties.svg={
21512      hash=hash,
21513      timestamp=timestamp,
21514     }
21515    end
21516    if cleanup>1 then
21517     collectgarbage("collect")
21518    else
21519     checkmemory(used,threshold,tracememory)
21520    end
21521   end
21522   if pngshapes then
21523    resources.pngshapes=nil
21524    if otf.pngenabled then
21525     local timestamp=os.date()
21526     containers.write(otf.pngcache,hash,{
21527      pngshapes=pngshapes,
21528      timestamp=timestamp,
21529     })
21530     data.properties.png={
21531      hash=hash,
21532      timestamp=timestamp,
21533     }
21534    end
21535    if cleanup>1 then
21536     collectgarbage("collect")
21537    else
21538     checkmemory(used,threshold,tracememory)
21539    end
21540   end
21541   otfreaders.compact(data)
21542   if cleanup==0 then
21543    checkmemory(used,threshold,tracememory)
21544   end
21545   otfreaders.rehash(data,"unicodes")
21546   otfreaders.addunicodetable(data)
21547   otfreaders.extend(data)
21548   if cleanup==0 then
21549    checkmemory(used,threshold,tracememory)
21550   end
21551   if context then
21552    otfreaders.condense(data)
21553   end
21554   otfreaders.pack(data)
21555   report_otf("loading done")
21556   report_otf("saving %a in cache",filename)
21557   data=containers.write(otf.cache,hash,data)
21558   if cleanup>1 then
21559    collectgarbage("collect")
21560   else
21561    checkmemory(used,threshold,tracememory)
21562   end
21563   stoptiming(otfreaders)
21564   if elapsedtime then
21565    report_otf("loading, optimizing, packing and caching time %s",elapsedtime(otfreaders))
21566   end
21567   if cleanup>3 then
21568    collectgarbage("collect")
21569   else
21570    checkmemory(used,threshold,tracememory)
21571   end
21572   data=containers.read(otf.cache,hash) 
21573   if cleanup>2 then
21574    collectgarbage("collect")
21575   else
21576    checkmemory(used,threshold,tracememory)
21577   end
21578  else
21579   stoptiming(otfreaders)
21580   data=nil
21581   report_otf("loading failed due to read error")
21582  end
21583 end
21584 if data then
21585  if trace_defining then
21586   report_otf("loading from cache using hash %a",hash)
21587  end
21588  otfreaders.unpack(data)
21589  otfreaders.expand(data) 
21590  otfreaders.addunicodetable(data)
21591  otfenhancers.apply(data,filename,data)
21592  if applyruntimefixes then
21593   applyruntimefixes(filename,data) 
21594  end
21595  data.metadata.math=data.resources.mathconstants
21596  local classes=data.resources.classes
21597  if not classes then
21598   local descriptions=data.descriptions
21599   classes=setmetatableindex(function(t,k)
21600    local d=descriptions[k]
21601    local v=(d and d.class or "base") or false
21602    t[k]=v
21603    return v
21604   end)
21605   data.resources.classes=classes
21606  end
21607 end
21608 return data
21609end
21610function otf.setfeatures(tfmdata,features)
21611 local okay=constructors.initializefeatures("otf",tfmdata,features,trace_features,report_otf)
21612 if okay then
21613  return constructors.collectprocessors("otf",tfmdata,features,trace_features,report_otf)
21614 else
21615  return {} 
21616 end
21617end
21618local function copytotfm(data,cache_id,wipemath)
21619 if data then
21620  local metadata=data.metadata
21621  local properties=derivetable(data.properties)
21622  local descriptions=derivetable(data.descriptions)
21623  local goodies=derivetable(data.goodies)
21624  local characters={} 
21625  local parameters={}
21626  local mathparameters={}
21627  local resources=data.resources
21628  local unicodes=resources.unicodes
21629  local spaceunits=500
21630  local spacer="space"
21631  local designsize=metadata.designsize or 100
21632  local minsize=metadata.minsize or designsize
21633  local maxsize=metadata.maxsize or designsize
21634  local mathspecs=metadata.math
21635  if designsize==0 then
21636   designsize=100
21637   minsize=100
21638   maxsize=100
21639  end
21640  for unicode in next,data.descriptions do 
21641   characters[unicode]={}
21642  end
21643  local filename=constructors.checkedfilename(resources)
21644  local fontname=metadata.fontname
21645  local fullname=metadata.fullname or fontname
21646  local psname=fontname or fullname
21647  local subfont=metadata.subfontindex
21648  local units=metadata.units or 1000
21649  if units==0 then 
21650   units=1000 
21651   metadata.units=1000
21652   report_otf("changing %a units to %a",0,units)
21653  end
21654  if not mathspecs then
21655  elseif wipemath then
21656   local wiped=false
21657   local features=resources.features
21658   local sequences=resources.sequences
21659   if features then
21660    local gsub=features.gsub
21661    if gsub and gsub.ssty then
21662     gsub.ssty=nil
21663     wiped=true
21664    end
21665   end
21666   if sequences then
21667    for i=#sequences,1,-1 do
21668     local sequence=sequences[i]
21669     local features=sequence.features
21670     if features then
21671      if features.ssty then
21672       remove(sequences,i)
21673       wiped=true
21674      end
21675     end
21676    end
21677   end
21678   if resources.mathconstants then
21679    resources.mathconstants=nil
21680    wiped=true
21681   end
21682   if resources.math then
21683    metadata.math=nil
21684    wiped=true
21685   end
21686   if wiped then
21687    report_otf("math data wiped from %a",fullname)
21688   end
21689  else
21690   for name,value in next,mathspecs do
21691    mathparameters[name]=value
21692   end
21693   for unicode,character in next,characters do
21694    local d=descriptions[unicode] 
21695    local m=d.math
21696    if m then
21697     local italic=m.italic
21698     local vitalic=m.vitalic
21699     local variants=m.hvariants
21700     local parts=m.hparts
21701     if variants then
21702      local c=character
21703      for i=1,#variants do
21704       local un=variants[i]
21705       c.next=un
21706       c=characters[un]
21707      end 
21708      c.horiz_variants=parts
21709     elseif parts then
21710      character.horiz_variants=parts
21711      italic=m.hitalic
21712     end
21713     local variants=m.vvariants
21714     local parts=m.vparts
21715     if variants then
21716      local c=character
21717      for i=1,#variants do
21718       local un=variants[i]
21719       c.next=un
21720       c=characters[un]
21721      end 
21722      c.vert_variants=parts
21723     elseif parts then
21724      character.vert_variants=parts
21725     end
21726     if italic and italic~=0 then
21727      character.italic=italic
21728     end
21729     if vitalic and vitalic~=0 then
21730      character.vert_italic=vitalic
21731     end
21732     local accent=m.accent 
21733     if accent then
21734      character.accent=accent
21735     end
21736     local kerns=m.kerns
21737     if kerns then
21738      character.mathkerns=kerns
21739     end
21740    end
21741   end
21742  end
21743  local monospaced=metadata.monospaced
21744  local charwidth=metadata.averagewidth 
21745  local charxheight=metadata.xheight 
21746  local italicangle=metadata.italicangle
21747  local hasitalics=metadata.hasitalics
21748  properties.monospaced=monospaced
21749  properties.hasitalics=hasitalics
21750  parameters.italicangle=italicangle
21751  parameters.charwidth=charwidth
21752  parameters.charxheight=charxheight
21753  local space=0x0020
21754  local emdash=0x2014
21755  if monospaced then
21756   if descriptions[space] then
21757    spaceunits,spacer=descriptions[space].width,"space"
21758   end
21759   if not spaceunits and descriptions[emdash] then
21760    spaceunits,spacer=descriptions[emdash].width,"emdash"
21761   end
21762   if not spaceunits and charwidth then
21763    spaceunits,spacer=charwidth,"charwidth"
21764   end
21765  else
21766   if descriptions[space] then
21767    spaceunits,spacer=descriptions[space].width,"space"
21768   end
21769   if not spaceunits and descriptions[emdash] then
21770    spaceunits,spacer=descriptions[emdash].width/2,"emdash/2"
21771   end
21772   if not spaceunits and charwidth then
21773    spaceunits,spacer=charwidth,"charwidth"
21774   end
21775  end
21776  spaceunits=tonumber(spaceunits) or units/2
21777  parameters.slant=0
21778  parameters.space=spaceunits   
21779  parameters.space_stretch=1*units/2   
21780  parameters.space_shrink=1*units/3   
21781  parameters.x_height=2*units/5   
21782  parameters.quad=units    
21783  if spaceunits<2*units/5 then
21784  end
21785  if italicangle and italicangle~=0 then
21786   parameters.italicangle=italicangle
21787   parameters.italicfactor=math.cos(math.rad(90+italicangle))
21788   parameters.slant=- math.tan(italicangle*math.pi/180)
21789  end
21790  if monospaced then
21791   parameters.space_stretch=0
21792   parameters.space_shrink=0
21793  elseif syncspace then 
21794   parameters.space_stretch=spaceunits/2
21795   parameters.space_shrink=spaceunits/3
21796  end
21797  parameters.extra_space=parameters.space_shrink 
21798  if charxheight then
21799   parameters.x_height=charxheight
21800  else
21801   local x=0x0078
21802   if x then
21803    local x=descriptions[x]
21804    if x then
21805     parameters.x_height=x.height
21806    end
21807   end
21808  end
21809  parameters.designsize=(designsize/10)*65536
21810  parameters.minsize=(minsize/10)*65536
21811  parameters.maxsize=(maxsize/10)*65536
21812  parameters.ascender=abs(metadata.ascender  or 0)
21813  parameters.descender=abs(metadata.descender or 0)
21814  parameters.units=units
21815  parameters.vheight=metadata.defaultvheight
21816  properties.space=spacer
21817  properties.format=data.format or formats.otf
21818  properties.filename=filename
21819  properties.fontname=fontname
21820  properties.fullname=fullname
21821  properties.psname=psname
21822  properties.name=filename or fullname
21823  properties.subfont=subfont
21824if not CONTEXTLMTXMODE or CONTEXTLMTXMODE==0 then
21825 properties.encodingbytes=2
21826elseif CONTEXTLMTXMODE then
21827 local duplicates=resources and resources.duplicates
21828 if duplicates then
21829  local maxindex=data.nofglyphs or metadata.nofglyphs
21830  if maxindex then
21831   for u,d in sortedhash(duplicates) do
21832    local du=descriptions[u]
21833    if du then
21834     for uu in sortedhash(d) do
21835      maxindex=maxindex+1
21836      descriptions[uu].dupindex=du.index
21837      descriptions[uu].index=maxindex
21838     end
21839    else
21840    end
21841   end
21842  end
21843 end
21844end
21845  properties.private=properties.private or data.private or privateoffset
21846  return {
21847   characters=characters,
21848   descriptions=descriptions,
21849   parameters=parameters,
21850   mathparameters=mathparameters,
21851   resources=resources,
21852   properties=properties,
21853   goodies=goodies,
21854  }
21855 end
21856end
21857local converters={
21858 woff={
21859  cachename="webfonts",
21860  action=otf.readers.woff2otf,
21861 }
21862}
21863local function checkconversion(specification)
21864 local filename=specification.filename
21865 local converter=converters[lower(file.suffix(filename))]
21866 if converter then
21867  local base=file.basename(filename)
21868  local name=file.removesuffix(base)
21869  local attr=lfs.attributes(filename)
21870  local size=attr and attr.size or 0
21871  local time=attr and attr.modification or 0
21872  if size>0 then
21873   local cleanname=containers.cleanname(name)
21874   local cachename=caches.setfirstwritablefile(cleanname,converter.cachename)
21875   if not io.exists(cachename) or (time~=lfs.attributes(cachename).modification) then
21876    report_otf("caching font %a in %a",filename,cachename)
21877    converter.action(filename,cachename) 
21878    lfs.touch(cachename,time,time)
21879   end
21880   specification.filename=cachename
21881  end
21882 end
21883end
21884local function otftotfm(specification)
21885 local cache_id=specification.hash
21886 local tfmdata=containers.read(constructors.cache,cache_id)
21887 if not tfmdata then
21888  checkconversion(specification) 
21889  local name=specification.name
21890  local sub=specification.sub
21891  local subindex=specification.subindex
21892  local filename=specification.filename
21893  local features=specification.features.normal
21894  local instance=specification.instance or (features and features.axis)
21895  local rawdata=otf.load(filename,sub,instance)
21896  if rawdata and next(rawdata) then
21897   local descriptions=rawdata.descriptions
21898   rawdata.lookuphash={} 
21899   tfmdata=copytotfm(rawdata,cache_id,features and features.wipemath)
21900   if tfmdata and next(tfmdata) then
21901    local features=constructors.checkedfeatures("otf",features)
21902    local shared=tfmdata.shared
21903    if not shared then
21904     shared={}
21905     tfmdata.shared=shared
21906    end
21907    shared.rawdata=rawdata
21908    shared.dynamics={}
21909    tfmdata.changed={}
21910    shared.features=features
21911    shared.processes=otf.setfeatures(tfmdata,features)
21912   end
21913  end
21914  containers.write(constructors.cache,cache_id,tfmdata)
21915 end
21916 return tfmdata
21917end
21918local function read_from_otf(specification)
21919 local tfmdata=otftotfm(specification)
21920 if tfmdata then
21921  tfmdata.properties.name=specification.name
21922  tfmdata.properties.sub=specification.sub
21923  tfmdata.properties.id=specification.id
21924  tfmdata=constructors.scale(tfmdata,specification)
21925  local allfeatures=tfmdata.shared.features or specification.features.normal
21926  constructors.applymanipulators("otf",tfmdata,allfeatures,trace_features,report_otf)
21927  constructors.setname(tfmdata,specification) 
21928  fonts.loggers.register(tfmdata,file.suffix(specification.filename),specification)
21929 end
21930 return tfmdata
21931end
21932function otf.collectlookups(rawdata,kind,script,language)
21933 if not kind then
21934  return
21935 end
21936 if not script then
21937  script=default
21938 end
21939 if not language then
21940  language=default
21941 end
21942 local lookupcache=rawdata.lookupcache
21943 if not lookupcache then
21944  lookupcache={}
21945  rawdata.lookupcache=lookupcache
21946 end
21947 local kindlookup=lookupcache[kind]
21948 if not kindlookup then
21949  kindlookup={}
21950  lookupcache[kind]=kindlookup
21951 end
21952 local scriptlookup=kindlookup[script]
21953 if not scriptlookup then
21954  scriptlookup={}
21955  kindlookup[script]=scriptlookup
21956 end
21957 local languagelookup=scriptlookup[language]
21958 if not languagelookup then
21959  local sequences=rawdata.resources.sequences
21960  local featuremap={}
21961  local featurelist={}
21962  if sequences then
21963   for s=1,#sequences do
21964    local sequence=sequences[s]
21965    local features=sequence.features
21966    if features then
21967     features=features[kind]
21968     if features then
21969      features=features[script] or features[wildcard]
21970      if features then
21971       features=features[language] or features[wildcard]
21972       if features then
21973        if not featuremap[sequence] then
21974         featuremap[sequence]=true
21975         featurelist[#featurelist+1]=sequence
21976        end
21977       end
21978      end
21979     end
21980    end
21981   end
21982   if #featurelist==0 then
21983    featuremap,featurelist=false,false
21984   end
21985  else
21986   featuremap,featurelist=false,false
21987  end
21988  languagelookup={ featuremap,featurelist }
21989  scriptlookup[language]=languagelookup
21990 end
21991 return unpack(languagelookup)
21992end
21993local function getgsub(tfmdata,k,kind,value)
21994 local shared=tfmdata.shared
21995 local rawdata=shared and shared.rawdata
21996 if rawdata then
21997  local sequences=rawdata.resources.sequences
21998  if sequences then
21999   local properties=tfmdata.properties
22000   local validlookups,lookuplist=otf.collectlookups(rawdata,kind,properties.script,properties.language)
22001   if validlookups then
22002    for i=1,#lookuplist do
22003     local lookup=lookuplist[i]
22004     local steps=lookup.steps
22005     local nofsteps=lookup.nofsteps
22006     for i=1,nofsteps do
22007      local coverage=steps[i].coverage
22008      if coverage then
22009       local found=coverage[k]
22010       if found then
22011        return found,lookup.type
22012       end
22013      end
22014     end
22015    end
22016   end
22017  end
22018 end
22019end
22020otf.getgsub=getgsub 
22021function otf.getsubstitution(tfmdata,k,kind,value)
22022 local found,kind=getgsub(tfmdata,k,kind,value)
22023 if not found then
22024 elseif kind=="gsub_single" then
22025  return found
22026 elseif kind=="gsub_alternate" then
22027  local choice=tonumber(value) or 1 
22028  return found[choice] or found[1] or k
22029 end
22030 return k
22031end
22032otf.getalternate=otf.getsubstitution
22033function otf.getmultiple(tfmdata,k,kind)
22034 local found,kind=getgsub(tfmdata,k,kind)
22035 if found and kind=="gsub_multiple" then
22036  return found
22037 end
22038 return { k }
22039end
22040function otf.getkern(tfmdata,left,right,kind)
22041 local kerns=getgsub(tfmdata,left,kind or "kern",true) 
22042 if kerns then
22043  local found=kerns[right]
22044  local kind=type(found)
22045  if kind=="table" then
22046   found=found[1][3] 
22047  elseif kind~="number" then
22048   found=false
22049  end
22050  if found then
22051   return found*tfmdata.parameters.factor
22052  end
22053 end
22054 return 0
22055end
22056local function check_otf(forced,specification,suffix)
22057 local name=specification.name
22058 if forced then
22059  name=specification.forcedname 
22060 end
22061 local fullname=findbinfile(name,suffix) or ""
22062 if fullname=="" then
22063  fullname=fonts.names.getfilename(name,suffix) or ""
22064 end
22065 if fullname~="" and not fonts.names.ignoredfile(fullname) then
22066  specification.filename=fullname
22067  return read_from_otf(specification)
22068 end
22069end
22070local function opentypereader(specification,suffix)
22071 local forced=specification.forced or ""
22072 if formats[forced] then
22073  return check_otf(true,specification,forced)
22074 else
22075  return check_otf(false,specification,suffix)
22076 end
22077end
22078readers.opentype=opentypereader 
22079function readers.otf(specification) return opentypereader(specification,"otf") end
22080function readers.ttf(specification) return opentypereader(specification,"ttf") end
22081function readers.ttc(specification) return opentypereader(specification,"ttf") end
22082function readers.woff(specification)
22083 checkconversion(specification)
22084 opentypereader(specification,"")
22085end
22086function otf.scriptandlanguage(tfmdata,attr)
22087 local properties=tfmdata.properties
22088 return properties.script or "dflt",properties.language or "dflt"
22089end
22090local function justset(coverage,unicode,replacement)
22091 coverage[unicode]=replacement
22092end
22093otf.coverup={
22094 stepkey="steps",
22095 actions={
22096  chainsubstitution=justset,
22097  chainposition=justset,
22098  substitution=justset,
22099  alternate=justset,
22100  multiple=justset,
22101  kern=justset,
22102  pair=justset,
22103  single=justset,
22104  ligature=function(coverage,unicode,ligature)
22105   local first=ligature[1]
22106   local tree=coverage[first]
22107   if not tree then
22108    tree={}
22109    coverage[first]=tree
22110   end
22111   for i=2,#ligature do
22112    local l=ligature[i]
22113    local t=tree[l]
22114    if not t then
22115     t={}
22116     tree[l]=t
22117    end
22118    tree=t
22119   end
22120   tree.ligature=unicode
22121  end,
22122 },
22123 register=function(coverage,featuretype,format)
22124  return {
22125   format=format,
22126   coverage=coverage,
22127  }
22128 end
22129}
22130
22131end -- closure
22132
22133do -- begin closure to overcome local limits and interference
22134
22135if not modules then modules={} end modules ['font-oto']={ 
22136 version=1.001,
22137 comment="companion to font-ini.mkiv",
22138 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
22139 copyright="PRAGMA ADE / ConTeXt Development Team",
22140 license="see context related readme files"
22141}
22142local concat,unpack=table.concat,table.unpack
22143local insert,remove=table.insert,table.remove
22144local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip
22145local type,next,tonumber,tostring=type,next,tonumber,tostring
22146local trace_baseinit=false  trackers.register("otf.baseinit",function(v) trace_baseinit=v end)
22147local trace_singles=false  trackers.register("otf.singles",function(v) trace_singles=v end)
22148local trace_multiples=false  trackers.register("otf.multiples",function(v) trace_multiples=v end)
22149local trace_alternatives=false  trackers.register("otf.alternatives",function(v) trace_alternatives=v end)
22150local trace_ligatures=false  trackers.register("otf.ligatures",function(v) trace_ligatures=v end)
22151local trace_kerns=false  trackers.register("otf.kerns",function(v) trace_kerns=v end)
22152local trace_preparing=false  trackers.register("otf.preparing",function(v) trace_preparing=v end)
22153local report_prepare=logs.reporter("fonts","otf prepare")
22154local fonts=fonts
22155local otf=fonts.handlers.otf
22156local otffeatures=otf.features
22157local registerotffeature=otffeatures.register
22158otf.defaultbasealternate="none" 
22159local getprivate=fonts.constructors.getprivate
22160local wildcard="*"
22161local default="dflt"
22162local formatters=string.formatters
22163local f_unicode=formatters["%U"]
22164local f_uniname=formatters["%U (%s)"]
22165local f_unilist=formatters["% t (% t)"]
22166local function gref(descriptions,n)
22167 if type(n)=="number" then
22168  local name=descriptions[n].name
22169  if name then
22170   return f_uniname(n,name)
22171  else
22172   return f_unicode(n)
22173  end
22174 elseif n then
22175  local num={}
22176  local nam={}
22177  local j=0
22178  for i=1,#n do
22179   local ni=n[i]
22180   if tonumber(ni) then 
22181    j=j+1
22182    local di=descriptions[ni]
22183    num[j]=f_unicode(ni)
22184    nam[j]=di and di.name or "-"
22185   end
22186  end
22187  return f_unilist(num,nam)
22188 else
22189  return "<error in base mode tracing>"
22190 end
22191end
22192local function cref(feature,sequence)
22193 return formatters["feature %a, type %a, (chain) lookup %a"](feature,sequence.type,sequence.name)
22194end
22195local function report_substitution(feature,sequence,descriptions,unicode,substitution)
22196 if unicode==substitution then
22197  report_prepare("%s: base substitution %s maps onto itself",
22198   cref(feature,sequence),
22199   gref(descriptions,unicode))
22200 else
22201  report_prepare("%s: base substitution %s => %S",
22202   cref(feature,sequence),
22203   gref(descriptions,unicode),
22204   gref(descriptions,substitution))
22205 end
22206end
22207local function report_alternate(feature,sequence,descriptions,unicode,replacement,value,comment)
22208 if unicode==replacement then
22209  report_prepare("%s: base alternate %s maps onto itself",
22210   cref(feature,sequence),
22211   gref(descriptions,unicode))
22212 else
22213  report_prepare("%s: base alternate %s => %s (%S => %S)",
22214   cref(feature,sequence),
22215   gref(descriptions,unicode),
22216   replacement and gref(descriptions,replacement),
22217   value,
22218   comment)
22219 end
22220end
22221local function report_ligature(feature,sequence,descriptions,unicode,ligature)
22222 report_prepare("%s: base ligature %s => %S",
22223  cref(feature,sequence),
22224  gref(descriptions,ligature),
22225  gref(descriptions,unicode))
22226end
22227local function report_kern(feature,sequence,descriptions,unicode,otherunicode,value)
22228 report_prepare("%s: base kern %s + %s => %S",
22229  cref(feature,sequence),
22230  gref(descriptions,unicode),
22231  gref(descriptions,otherunicode),
22232  value)
22233end
22234local basehash,basehashes,applied={},1,{}
22235local function registerbasehash(tfmdata)
22236 local properties=tfmdata.properties
22237 local hash=concat(applied," ")
22238 local base=basehash[hash]
22239 if not base then
22240  basehashes=basehashes+1
22241  base=basehashes
22242  basehash[hash]=base
22243 end
22244 properties.basehash=base
22245 properties.fullname=(properties.fullname or properties.name).."-"..base
22246 applied={}
22247end
22248local function registerbasefeature(feature,value)
22249 applied[#applied+1]=feature.."="..tostring(value)
22250end
22251local function makefake(tfmdata,name,present)
22252 local private=getprivate(tfmdata)
22253 local character={ intermediate=true,ligatures={} }
22254 tfmdata.resources.unicodes[name]=private
22255 tfmdata.characters[private]=character
22256 tfmdata.descriptions[private]={ name=name }
22257 present[name]=private
22258 return character
22259end
22260local function make_1(present,tree,name)
22261 if tonumber(tree) then
22262  present[name]=v
22263 else
22264  for k,v in next,tree do
22265   if k=="ligature" then
22266    present[name]=v
22267   else
22268    make_1(present,v,name.."_"..k)
22269   end
22270  end
22271 end
22272end
22273local function make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v)
22274 local character=characters[preceding]
22275 if not character then
22276  if trace_baseinit then
22277   report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding)
22278  end
22279  character=makefake(tfmdata,name,present)
22280 end
22281 local ligatures=character.ligatures
22282 if ligatures then
22283  ligatures[unicode]={ char=v }
22284 else
22285  character.ligatures={ [unicode]={ char=v } }
22286 end
22287 if done then
22288  local d=done[name]
22289  if not d then
22290   done[name]={ "dummy",v }
22291  else
22292   d[#d+1]=v
22293  end
22294 end
22295end
22296local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done)
22297 if tonumber(tree) then
22298  make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,tree)
22299 else
22300  for k,v in next,tree do
22301   if k=="ligature" then
22302    make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v)
22303   else
22304    local code=present[name] or unicode
22305    local name=name.."_"..k
22306    make_2(present,tfmdata,characters,v,name,code,k,done)
22307   end
22308  end
22309 end
22310end
22311local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
22312 local characters=tfmdata.characters
22313 local descriptions=tfmdata.descriptions
22314 local resources=tfmdata.resources
22315 local changed=tfmdata.changed
22316 local ligatures={}
22317 local alternate=tonumber(value) or true and 1
22318 local defaultalt=otf.defaultbasealternate
22319 local trace_singles=trace_baseinit and trace_singles
22320 local trace_alternatives=trace_baseinit and trace_alternatives
22321 local trace_ligatures=trace_baseinit and trace_ligatures
22322 if not changed then
22323  changed={}
22324  tfmdata.changed=changed
22325 end
22326 for i=1,#lookuplist do
22327  local sequence=lookuplist[i]
22328  local steps=sequence.steps
22329  local kind=sequence.type
22330  if kind=="gsub_single" then
22331   for i=1,#steps do
22332    for unicode,data in next,steps[i].coverage do
22333     if unicode~=data and not changed[unicode] then
22334      changed[unicode]=data
22335     end
22336     if trace_singles then
22337      report_substitution(feature,sequence,descriptions,unicode,data)
22338     end
22339    end
22340   end
22341  elseif kind=="gsub_alternate" then
22342   for i=1,#steps do
22343    for unicode,data in next,steps[i].coverage do
22344     local replacement=data[alternate]
22345     if replacement then
22346      if unicode~=replacement and not changed[unicode] then
22347       changed[unicode]=replacement
22348      end
22349      if trace_alternatives then
22350       report_alternate(feature,sequence,descriptions,unicode,replacement,value,"normal")
22351      end
22352     elseif defaultalt=="first" then
22353      replacement=data[1]
22354      if unicode~=replacement and not changed[unicode] then
22355       changed[unicode]=replacement
22356      end
22357      if trace_alternatives then
22358       report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt)
22359      end
22360     elseif defaultalt=="last" then
22361      replacement=data[#data]
22362      if unicode~=replacement and not changed[unicode] then
22363       changed[unicode]=replacement
22364      end
22365      if trace_alternatives then
22366       report_alternate(feature,sequence,descriptions,unicode,replacement,value,defaultalt)
22367      end
22368     else
22369      if trace_alternatives then
22370       report_alternate(feature,sequence,descriptions,unicode,replacement,value,"unknown")
22371      end
22372     end
22373    end
22374   end
22375  elseif kind=="gsub_ligature" then
22376   for i=1,#steps do
22377    for unicode,data in next,steps[i].coverage do
22378     ligatures[#ligatures+1]={ unicode,data,"" } 
22379     if trace_ligatures then
22380      report_ligature(feature,sequence,descriptions,unicode,data)
22381     end
22382    end
22383   end
22384  end
22385 end
22386 local nofligatures=#ligatures
22387 if nofligatures>0 then
22388  local characters=tfmdata.characters
22389  local present={}
22390  local done=trace_baseinit and trace_ligatures and {}
22391  for i=1,nofligatures do
22392   local ligature=ligatures[i]
22393   local unicode=ligature[1]
22394   local tree=ligature[2]
22395   make_1(present,tree,"ctx_"..unicode)
22396  end
22397  for i=1,nofligatures do
22398   local ligature=ligatures[i]
22399   local unicode=ligature[1]
22400   local tree=ligature[2]
22401   local lookupname=ligature[3]
22402   make_2(present,tfmdata,characters,tree,"ctx_"..unicode,unicode,unicode,done,sequence)
22403  end
22404 end
22405end
22406local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist)
22407 local characters=tfmdata.characters
22408 local descriptions=tfmdata.descriptions
22409 local resources=tfmdata.resources
22410 local properties=tfmdata.properties
22411 local traceindeed=trace_baseinit and trace_kerns
22412 for i=1,#lookuplist do
22413  local sequence=lookuplist[i]
22414  local steps=sequence.steps
22415  local kind=sequence.type
22416  local format=sequence.format
22417  if kind=="gpos_pair" then
22418   for i=1,#steps do
22419    local step=steps[i]
22420    local format=step.format
22421    if format=="kern" or format=="move" then
22422     for unicode,data in next,steps[i].coverage do
22423      local character=characters[unicode]
22424      local kerns=character.kerns
22425      if not kerns then
22426       kerns={}
22427       character.kerns=kerns
22428      end
22429      if traceindeed then
22430       for otherunicode,kern in next,data do
22431        if not kerns[otherunicode] and kern~=0 then
22432         kerns[otherunicode]=kern
22433         report_kern(feature,sequence,descriptions,unicode,otherunicode,kern)
22434        end
22435       end
22436      else
22437       for otherunicode,kern in next,data do
22438        if not kerns[otherunicode] and kern~=0 then
22439         kerns[otherunicode]=kern
22440        end
22441       end
22442      end
22443     end
22444    else
22445     for unicode,data in next,steps[i].coverage do
22446      local character=characters[unicode]
22447      local kerns=character.kerns
22448      for otherunicode,kern in next,data do
22449       local other=kern[2]
22450       if other==true or (not other and not (kerns and kerns[otherunicode])) then
22451        local kern=kern[1]
22452        if kern==true then
22453        elseif kern[1]~=0 or kern[2]~=0 or kern[4]~=0 then
22454        else
22455         kern=kern[3]
22456         if kern~=0 then
22457          if kerns then
22458           kerns[otherunicode]=kern
22459          else
22460           kerns={ [otherunicode]=kern }
22461           character.kerns=kerns
22462          end
22463          if traceindeed then
22464           report_kern(feature,sequence,descriptions,unicode,otherunicode,kern)
22465          end
22466         end
22467        end
22468       end
22469      end
22470     end
22471    end
22472   end
22473  end
22474 end
22475end
22476local function initializehashes(tfmdata)
22477end
22478local function checkmathreplacements(tfmdata,fullname,fixitalics)
22479 if tfmdata.mathparameters then
22480  local characters=tfmdata.characters
22481  local changed=tfmdata.changed
22482  if next(changed) then
22483   if trace_preparing or trace_baseinit then
22484    report_prepare("checking math replacements for %a",fullname)
22485   end
22486   for unicode,replacement in next,changed do
22487    local u=characters[unicode]
22488    local r=characters[replacement]
22489    if u and r then
22490     local n=u.next
22491     local v=u.vert_variants
22492     local h=u.horiz_variants
22493     if fixitalics then
22494      local ui=u.italic
22495      if ui and not r.italic then
22496       if trace_preparing then
22497        report_prepare("using %i units of italic correction from %C for %U",ui,unicode,replacement)
22498       end
22499       r.italic=ui 
22500      end
22501     end
22502     if n and not r.next then
22503      if trace_preparing then
22504       report_prepare("forcing %s for %C substituted by %U","incremental step",unicode,replacement)
22505      end
22506      r.next=n
22507     end
22508     if v and not r.vert_variants then
22509      if trace_preparing then
22510       report_prepare("forcing %s for %C substituted by %U","vertical variants",unicode,replacement)
22511      end
22512      r.vert_variants=v
22513     end
22514     if h and not r.horiz_variants then
22515      if trace_preparing then
22516       report_prepare("forcing %s for %C substituted by %U","horizontal variants",unicode,replacement)
22517      end
22518      r.horiz_variants=h
22519     end
22520    else
22521     if trace_preparing then
22522      report_prepare("error replacing %C by %U",unicode,replacement)
22523     end
22524    end
22525   end
22526  end
22527 end
22528end
22529local function featuresinitializer(tfmdata,value)
22530 if true then 
22531  local starttime=trace_preparing and os.clock()
22532  local features=tfmdata.shared.features
22533  local fullname=tfmdata.properties.fullname or "?"
22534  if features then
22535   initializehashes(tfmdata)
22536   local collectlookups=otf.collectlookups
22537   local rawdata=tfmdata.shared.rawdata
22538   local properties=tfmdata.properties
22539   local script=properties.script
22540   local language=properties.language
22541   local rawresources=rawdata.resources
22542   local rawfeatures=rawresources and rawresources.features
22543   local basesubstitutions=rawfeatures and rawfeatures.gsub
22544   local basepositionings=rawfeatures and rawfeatures.gpos
22545   local substitutionsdone=false
22546   local positioningsdone=false
22547   if basesubstitutions or basepositionings then
22548    local sequences=tfmdata.resources.sequences
22549    for s=1,#sequences do
22550     local sequence=sequences[s]
22551     local sfeatures=sequence.features
22552     if sfeatures then
22553      local order=sequence.order
22554      if order then
22555       for i=1,#order do 
22556        local feature=order[i]
22557        local value=features[feature]
22558        if value then
22559         local validlookups,lookuplist=collectlookups(rawdata,feature,script,language)
22560         if not validlookups then
22561         elseif basesubstitutions and basesubstitutions[feature] then
22562          if trace_preparing then
22563           report_prepare("filtering base %s feature %a for %a with value %a","sub",feature,fullname,value)
22564          end
22565          preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
22566          registerbasefeature(feature,value)
22567          substitutionsdone=true
22568         elseif basepositionings and basepositionings[feature] then
22569          if trace_preparing then
22570           report_prepare("filtering base %a feature %a for %a with value %a","pos",feature,fullname,value)
22571          end
22572          preparepositionings(tfmdata,feature,value,validlookups,lookuplist)
22573          registerbasefeature(feature,value)
22574          positioningsdone=true
22575         end
22576        end
22577       end
22578      end
22579     end
22580    end
22581   end
22582   if substitutionsdone then
22583    checkmathreplacements(tfmdata,fullname,features.fixitalics)
22584   end
22585   registerbasehash(tfmdata)
22586  end
22587  if trace_preparing then
22588   report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,fullname)
22589  end
22590 end
22591end
22592registerotffeature {
22593 name="features",
22594 description="features",
22595 default=true,
22596 initializers={
22597  base=featuresinitializer,
22598 }
22599}
22600otf.basemodeinitializer=featuresinitializer
22601
22602end -- closure
22603
22604do -- begin closure to overcome local limits and interference
22605
22606if not modules then modules={} end modules ['font-otj']={
22607 version=1.001,
22608 optimize=true,
22609 comment="companion to font-lib.mkiv",
22610 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
22611 copyright="PRAGMA ADE / ConTeXt Development Team",
22612 license="see context related readme files",
22613}
22614if not nodes.properties then return end
22615local next,rawget,tonumber=next,rawget,tonumber
22616local fastcopy=table.fastcopy
22617local registertracker=trackers.register
22618local registerdirective=directives.register
22619local trace_injections=false  registertracker("fonts.injections",function(v) trace_injections=v end)
22620local trace_marks=false  registertracker("fonts.injections.marks",function(v) trace_marks=v end)
22621local trace_cursive=false  registertracker("fonts.injections.cursive",function(v) trace_cursive=v end)
22622local trace_spaces=false  registertracker("fonts.injections.spaces",function(v) trace_spaces=v end)
22623local report_injections=logs.reporter("fonts","injections")
22624local report_spaces=logs.reporter("fonts","spaces")
22625local attributes,nodes,node=attributes,nodes,node
22626fonts=fonts
22627local hashes=fonts.hashes
22628local fontdata=hashes.identifiers
22629local fontmarks=hashes.marks
22630nodes.injections=nodes.injections or {}
22631local injections=nodes.injections
22632local tracers=nodes.tracers
22633local setcolor=tracers and tracers.colors.set
22634local resetcolor=tracers and tracers.colors.reset
22635local nodecodes=nodes.nodecodes
22636local glyph_code=nodecodes.glyph
22637local disc_code=nodecodes.disc
22638local kern_code=nodecodes.kern
22639local glue_code=nodecodes.glue
22640local nuts=nodes.nuts
22641local nodepool=nuts.pool
22642local tonode=nuts.tonode
22643local tonut=nuts.tonut
22644local setfield=nuts.setfield
22645local getnext=nuts.getnext
22646local getprev=nuts.getprev
22647local getid=nuts.getid
22648local getfont=nuts.getfont
22649local getchar=nuts.getchar
22650local getoffsets=nuts.getoffsets
22651local getboth=nuts.getboth
22652local getdisc=nuts.getdisc
22653local setdisc=nuts.setdisc
22654local getreplace=nuts.getreplace
22655local setreplace=nuts.setreplace
22656local setoffsets=nuts.setoffsets
22657local ischar=nuts.ischar
22658local getkern=nuts.getkern
22659local setkern=nuts.setkern
22660local setlink=nuts.setlink
22661local setwidth=nuts.setwidth
22662local getwidth=nuts.getwidth
22663local nextchar=nuts.traversers.char
22664local nextglue=nuts.traversers.glue
22665local insertnodebefore=nuts.insertbefore
22666local insertnodeafter=nuts.insertafter
22667local properties=nodes.properties.data
22668local fontkern=nuts.pool and nuts.pool.fontkern   
22669local italickern=nuts.pool and nuts.pool.italickern 
22670local useitalickerns=false 
22671directives.register("fonts.injections.useitalics",function(v)
22672 if v then
22673  report_injections("using italics for space kerns (tracing only)")
22674 end
22675 useitalickerns=v
22676end)
22677if not fontkern then 
22678 local thekern=nuts.new("kern",0) 
22679 local setkern=nuts.setkern
22680 local copy_node=nuts.copy
22681 fontkern=function(k)
22682  local n=copy_node(thekern)
22683  setkern(n,k)
22684  return n
22685 end
22686end
22687if not italickern then 
22688 local thekern=nuts.new("kern",3) 
22689 local setkern=nuts.setkern
22690 local copy_node=nuts.copy
22691 italickern=function(k)
22692  local n=copy_node(thekern)
22693  setkern(n,k)
22694  return n
22695 end
22696end
22697function injections.installnewkern() end 
22698local nofregisteredkerns=0
22699local nofregisteredpositions=0
22700local nofregisteredmarks=0
22701local nofregisteredcursives=0
22702local keepregisteredcounts=false
22703function injections.keepcounts()
22704 keepregisteredcounts=true
22705end
22706function injections.resetcounts()
22707 nofregisteredkerns=0
22708 nofregisteredpositions=0
22709 nofregisteredmarks=0
22710 nofregisteredcursives=0
22711 keepregisteredcounts=false
22712end
22713function injections.reset(n)
22714 local p=rawget(properties,n)
22715 if p then
22716  p.injections=false 
22717 else
22718  properties[n]=false 
22719 end
22720end
22721function injections.copy(target,source)
22722 local sp=rawget(properties,source)
22723 if sp then
22724  local tp=rawget(properties,target)
22725  local si=sp.injections
22726  if si then
22727   si=fastcopy(si)
22728   if tp then
22729    tp.injections=si
22730   else
22731    properties[target]={
22732     injections=si,
22733    }
22734   end
22735  elseif tp then
22736   tp.injections=false 
22737  else
22738   properties[target]={ injections={} }
22739  end
22740 else
22741  local tp=rawget(properties,target)
22742  if tp then
22743   tp.injections=false 
22744  else
22745   properties[target]=false 
22746  end
22747 end
22748end
22749function injections.setligaindex(n,index) 
22750 local p=rawget(properties,n)
22751 if p then
22752  local i=p.injections
22753  if i then
22754   i.ligaindex=index
22755  else
22756   p.injections={
22757    ligaindex=index
22758   }
22759  end
22760 else
22761  properties[n]={
22762   injections={
22763    ligaindex=index
22764   }
22765  }
22766 end
22767end
22768function injections.getligaindex(n,default)
22769 local p=rawget(properties,n)
22770 if p then
22771  local i=p.injections
22772  if i then
22773   return i.ligaindex or default
22774  end
22775 end
22776 return default
22777end
22778function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext,r2lflag)
22779 local dx=factor*(exit[1]-entry[1])
22780 local dy=-factor*(exit[2]-entry[2])
22781 local ws=tfmstart.width
22782 local wn=tfmnext.width
22783 nofregisteredcursives=nofregisteredcursives+1
22784 if rlmode<0 then
22785  dx=-(dx+wn)
22786 else
22787  dx=dx-ws
22788 end
22789 if dx==0 then
22790  dx=0
22791 end
22792 local p=rawget(properties,start)
22793 if p then
22794  local i=p.injections
22795  if i then
22796   i.cursiveanchor=true
22797  else
22798   p.injections={
22799    cursiveanchor=true,
22800   }
22801  end
22802 else
22803  properties[start]={
22804   injections={
22805    cursiveanchor=true,
22806   },
22807  }
22808 end
22809 local p=rawget(properties,nxt)
22810 if p then
22811  local i=p.injections
22812  if i then
22813   i.cursivex=dx
22814   i.cursivey=dy
22815  else
22816   p.injections={
22817    cursivex=dx,
22818    cursivey=dy,
22819   }
22820  end
22821 else
22822  properties[nxt]={
22823   injections={
22824    cursivex=dx,
22825    cursivey=dy,
22826   },
22827  }
22828 end
22829 return dx,dy,nofregisteredcursives
22830end
22831function injections.setposition(kind,current,factor,rlmode,spec,injection)
22832 local x=factor*(spec[1] or 0)
22833 local y=factor*(spec[2] or 0)
22834 local w=factor*(spec[3] or 0)
22835 local h=factor*(spec[4] or 0)
22836 if x~=0 or w~=0 or y~=0 or h~=0 then 
22837  local yoffset=y-h
22838  local leftkern=x   
22839  local rightkern=w-x  
22840  if leftkern~=0 or rightkern~=0 or yoffset~=0 then
22841   nofregisteredpositions=nofregisteredpositions+1
22842   if rlmode and rlmode<0 then
22843    leftkern,rightkern=rightkern,leftkern
22844   end
22845   if not injection then
22846    injection="injections"
22847   end
22848   local p=rawget(properties,current)
22849   if p then
22850    local i=p[injection]
22851    if i then
22852     if leftkern~=0 then
22853      i.leftkern=(i.leftkern  or 0)+leftkern
22854     end
22855     if rightkern~=0 then
22856      i.rightkern=(i.rightkern or 0)+rightkern
22857     end
22858     if yoffset~=0 then
22859      i.yoffset=(i.yoffset or 0)+yoffset
22860     end
22861    elseif leftkern~=0 or rightkern~=0 then
22862     p[injection]={
22863      leftkern=leftkern,
22864      rightkern=rightkern,
22865      yoffset=yoffset,
22866     }
22867    else
22868     p[injection]={
22869      yoffset=yoffset,
22870     }
22871    end
22872   elseif leftkern~=0 or rightkern~=0 then
22873    properties[current]={
22874     [injection]={
22875      leftkern=leftkern,
22876      rightkern=rightkern,
22877      yoffset=yoffset,
22878     },
22879    }
22880   else
22881    properties[current]={
22882     [injection]={
22883      yoffset=yoffset,
22884     },
22885    }
22886   end
22887   return x,y,w,h,nofregisteredpositions
22888   end
22889 end
22890 return x,y,w,h 
22891end
22892function injections.setkern(current,factor,rlmode,x,injection)
22893 local dx=factor*x
22894 if dx~=0 then
22895  nofregisteredkerns=nofregisteredkerns+1
22896  local p=rawget(properties,current)
22897  if not injection then
22898   injection="injections"
22899  end
22900  if p then
22901   local i=p[injection]
22902   if i then
22903    i.leftkern=dx+(i.leftkern or 0)
22904   else
22905    p[injection]={
22906     leftkern=dx,
22907    }
22908   end
22909  else
22910   properties[current]={
22911    [injection]={
22912     leftkern=dx,
22913    },
22914   }
22915  end
22916  return dx,nofregisteredkerns
22917 else
22918  return 0,0
22919 end
22920end
22921function injections.setmove(current,factor,rlmode,x,injection)
22922 local dx=factor*x
22923 if dx~=0 then
22924  nofregisteredkerns=nofregisteredkerns+1
22925  local p=rawget(properties,current)
22926  if not injection then
22927   injection="injections"
22928  end
22929  if rlmode and rlmode<0 then
22930   if p then
22931    local i=p[injection]
22932    if i then
22933     i.rightkern=dx+(i.rightkern or 0)
22934    else
22935     p[injection]={
22936      rightkern=dx,
22937     }
22938    end
22939   else
22940    properties[current]={
22941     [injection]={
22942      rightkern=dx,
22943     },
22944    }
22945   end
22946  else
22947   if p then
22948    local i=p[injection]
22949    if i then
22950     i.leftkern=dx+(i.leftkern or 0)
22951    else
22952     p[injection]={
22953      leftkern=dx,
22954     }
22955    end
22956   else
22957    properties[current]={
22958     [injection]={
22959      leftkern=dx,
22960     },
22961    }
22962   end
22963  end
22964  return dx,nofregisteredkerns
22965 else
22966  return 0,0
22967 end
22968end
22969function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark) 
22970 local dx=factor*(ba[1]-ma[1])
22971 local dy=factor*(ba[2]-ma[2])
22972 nofregisteredmarks=nofregisteredmarks+1
22973 if rlmode>=0 then
22974  dx=tfmbase.width-dx 
22975 end
22976 local p=rawget(properties,start)
22977 if p then
22978  local i=p.injections
22979  if i then
22980   if i.markmark then
22981   else
22982    i.markx=dx
22983    i.marky=dy
22984    i.markdir=rlmode or 0
22985    i.markbase=nofregisteredmarks
22986    i.markbasenode=base
22987    i.markmark=mkmk
22988    i.checkmark=checkmark
22989   end
22990  else
22991   p.injections={
22992    markx=dx,
22993    marky=dy,
22994    markdir=rlmode or 0,
22995    markbase=nofregisteredmarks,
22996    markbasenode=base,
22997    markmark=mkmk,
22998    checkmark=checkmark,
22999   }
23000  end
23001 else
23002  properties[start]={
23003   injections={
23004    markx=dx,
23005    marky=dy,
23006    markdir=rlmode or 0,
23007    markbase=nofregisteredmarks,
23008    markbasenode=base,
23009    markmark=mkmk,
23010    checkmark=checkmark,
23011   },
23012  }
23013 end
23014 return dx,dy,nofregisteredmarks
23015end
23016local function dir(n)
23017 return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset"
23018end
23019local function showchar(n,nested)
23020 local char=getchar(n)
23021 report_injections("%wfont %s, char %U, glyph %c",nested and 2 or 0,getfont(n),char,char)
23022end
23023local function show(n,what,nested,symbol)
23024 if n then
23025  local p=rawget(properties,n)
23026  if p then
23027   local i=p[what]
23028   if i then
23029    local leftkern=i.leftkern  or 0
23030    local rightkern=i.rightkern or 0
23031    local yoffset=i.yoffset   or 0
23032    local markx=i.markx  or 0
23033    local marky=i.marky  or 0
23034    local markdir=i.markdir   or 0
23035    local markbase=i.markbase  or 0
23036    local cursivex=i.cursivex  or 0
23037    local cursivey=i.cursivey  or 0
23038    local ligaindex=i.ligaindex or 0
23039    local cursbase=i.cursiveanchor
23040    local margin=nested and 4 or 2
23041    if rightkern~=0 or yoffset~=0 then
23042     report_injections("%w%s pair: lx %p, rx %p, dy %p",margin,symbol,leftkern,rightkern,yoffset)
23043    elseif leftkern~=0 then
23044     report_injections("%w%s kern: dx %p",margin,symbol,leftkern)
23045    end
23046    if markx~=0 or marky~=0 or markbase~=0 then
23047     report_injections("%w%s mark: dx %p, dy %p, dir %s, base %s",margin,symbol,markx,marky,markdir,markbase~=0 and "yes" or "no")
23048    end
23049    if cursivex~=0 or cursivey~=0 then
23050     if cursbase then
23051      report_injections("%w%s curs: base dx %p, dy %p",margin,symbol,cursivex,cursivey)
23052     else
23053      report_injections("%w%s curs: dx %p, dy %p",margin,symbol,cursivex,cursivey)
23054     end
23055    elseif cursbase then
23056     report_injections("%w%s curs: base",margin,symbol)
23057    end
23058    if ligaindex~=0 then
23059     report_injections("%w%s liga: index %i",margin,symbol,ligaindex)
23060    end
23061   end
23062  end
23063 end
23064end
23065local function showsub(n,what,where)
23066 report_injections("begin subrun: %s",where)
23067 for n in nextchar,n do
23068  showchar(n,where)
23069  show(n,what,where," ")
23070 end
23071 report_injections("end subrun")
23072end
23073local function trace(head,where)
23074 report_injections()
23075 report_injections("begin run %s: %s kerns, %s positions, %s marks and %s cursives registered",
23076  where or "",nofregisteredkerns,nofregisteredpositions,nofregisteredmarks,nofregisteredcursives)
23077 local n=head
23078 while n do
23079  local id=getid(n)
23080  if id==glyph_code then
23081   showchar(n)
23082   show(n,"injections",false," ")
23083   show(n,"preinjections",false,"<")
23084   show(n,"postinjections",false,">")
23085   show(n,"replaceinjections",false,"=")
23086   show(n,"emptyinjections",false,"*")
23087  elseif id==disc_code then
23088   local pre,post,replace=getdisc(n)
23089   if pre then
23090    showsub(pre,"preinjections","pre")
23091   end
23092   if post then
23093    showsub(post,"postinjections","post")
23094   end
23095   if replace then
23096    showsub(replace,"replaceinjections","replace")
23097   end
23098   show(n,"emptyinjections",false,"*")
23099  end
23100  n=getnext(n)
23101 end
23102 report_injections("end run")
23103end
23104local function show_result(head)
23105 local current=head
23106 local skipping=false
23107 while current do
23108  local id=getid(current)
23109  if id==glyph_code then
23110   local w=getwidth(current)
23111   local x,y=getoffsets(current)
23112   report_injections("char: %C, width %p, xoffset %p, yoffset %p",getchar(current),w,x,y)
23113   skipping=false
23114  elseif id==kern_code then
23115   report_injections("kern: %p",getkern(current))
23116   skipping=false
23117  elseif not skipping then
23118   report_injections()
23119   skipping=true
23120  end
23121  current=getnext(current)
23122 end
23123 report_injections()
23124end
23125local function inject_kerns_only(head,where)
23126 if trace_injections then
23127  trace(head,"kerns")
23128 end
23129 local current=head
23130 local prev=nil
23131 local next=nil
23132 local prevdisc=nil
23133 local pre=nil 
23134 local post=nil 
23135 local replace=nil 
23136 local pretail=nil 
23137 local posttail=nil 
23138 local replacetail=nil 
23139 while current do
23140  local next=getnext(current)
23141  local char,id=ischar(current)
23142  if char then
23143   local p=rawget(properties,current)
23144   if p then
23145    local i=p.injections
23146    if i then
23147     local leftkern=i.leftkern
23148     if leftkern and leftkern~=0 then
23149      if prev and getid(prev)==glue_code then
23150       if useitalickerns then
23151        head=insertnodebefore(head,current,italickern(leftkern))
23152       else
23153        setwidth(prev,getwidth(prev)+leftkern)
23154       end
23155      else
23156       head=insertnodebefore(head,current,fontkern(leftkern))
23157      end
23158     end
23159    end
23160    if prevdisc then
23161     local done=false
23162     if post then
23163      local i=p.postinjections
23164      if i then
23165       local leftkern=i.leftkern
23166       if leftkern and leftkern~=0 then
23167        setlink(posttail,fontkern(leftkern))
23168        done=true
23169       end
23170      end
23171     end
23172     if replace then
23173      local i=p.replaceinjections
23174      if i then
23175       local leftkern=i.leftkern
23176       if leftkern and leftkern~=0 then
23177        setlink(replacetail,fontkern(leftkern))
23178        done=true
23179       end
23180      end
23181     else
23182      local i=p.emptyinjections
23183      if i then
23184       local leftkern=i.leftkern
23185       if leftkern and leftkern~=0 then
23186        replace=fontkern(leftkern)
23187        done=true
23188       end
23189      end
23190     end
23191     if done then
23192      setdisc(prevdisc,pre,post,replace)
23193     end
23194    end
23195   end
23196   prevdisc=nil
23197  elseif char==false then
23198   prevdisc=nil
23199  elseif id==disc_code then
23200   pre,post,replace,pretail,posttail,replacetail=getdisc(current,true)
23201   local done=false
23202   if pre then
23203    for n in nextchar,pre do
23204     local p=rawget(properties,n)
23205     if p then
23206      local i=p.injections or p.preinjections
23207      if i then
23208       local leftkern=i.leftkern
23209       if leftkern and leftkern~=0 then
23210        pre=insertnodebefore(pre,n,fontkern(leftkern))
23211        done=true
23212       end
23213      end
23214     end
23215    end
23216   end
23217   if post then
23218    for n in nextchar,post do
23219     local p=rawget(properties,n)
23220     if p then
23221      local i=p.injections or p.postinjections
23222      if i then
23223       local leftkern=i.leftkern
23224       if leftkern and leftkern~=0 then
23225        post=insertnodebefore(post,n,fontkern(leftkern))
23226        done=true
23227       end
23228      end
23229     end
23230    end
23231   end
23232   if replace then
23233    for n in nextchar,replace do
23234     local p=rawget(properties,n)
23235     if p then
23236      local i=p.injections or p.replaceinjections
23237      if i then
23238       local leftkern=i.leftkern
23239       if leftkern and leftkern~=0 then
23240        replace=insertnodebefore(replace,n,fontkern(leftkern))
23241        done=true
23242       end
23243      end
23244     end
23245    end
23246   end
23247   if done then
23248    setdisc(current,pre,post,replace)
23249   end
23250   prevdisc=current
23251  else
23252   prevdisc=nil
23253  end
23254  prev=current
23255  current=next
23256 end
23257 if keepregisteredcounts then
23258  keepregisteredcounts=false
23259 else
23260  nofregisteredkerns=0
23261 end
23262 if trace_injections then
23263  show_result(head)
23264 end
23265 return head
23266end
23267local function inject_positions_only(head,where)
23268 if trace_injections then
23269  trace(head,"positions")
23270 end
23271 local current=head
23272 local prev=nil
23273 local next=nil
23274 local prevdisc=nil
23275 local prevglyph=nil
23276 local pre=nil 
23277 local post=nil 
23278 local replace=nil 
23279 local pretail=nil 
23280 local posttail=nil 
23281 local replacetail=nil 
23282 while current do
23283  local next=getnext(current)
23284  local char,id=ischar(current)
23285  if char then
23286   local p=rawget(properties,current)
23287   if p then
23288    local i=p.injections
23289    if i then
23290     local yoffset=i.yoffset
23291     if yoffset and yoffset~=0 then
23292      setoffsets(current,false,yoffset)
23293     end
23294     local leftkern=i.leftkern
23295     local rightkern=i.rightkern
23296     if leftkern and leftkern~=0 then
23297      if rightkern and leftkern==-rightkern then
23298       setoffsets(current,leftkern,false)
23299       rightkern=0
23300      elseif prev and getid(prev)==glue_code then
23301       if useitalickerns then
23302        head=insertnodebefore(head,current,italickern(leftkern))
23303       else
23304        setwidth(prev,getwidth(prev)+leftkern)
23305       end
23306      else
23307       head=insertnodebefore(head,current,fontkern(leftkern))
23308      end
23309     end
23310     if rightkern and rightkern~=0 then
23311      if next and getid(next)==glue_code then
23312       if useitalickerns then
23313        insertnodeafter(head,current,italickern(rightkern))
23314       else
23315        setwidth(next,getwidth(next)+rightkern)
23316       end
23317      else
23318       insertnodeafter(head,current,fontkern(rightkern))
23319      end
23320     end
23321    elseif next then
23322     local i=p.emptyinjections
23323     if i then
23324      local rightkern=i.rightkern
23325      if rightkern and rightkern~=0 and getid(next)==disc_code then
23326       local replace=getreplace(next)
23327       if replace then
23328       else
23329        setreplace(next,fontkern(rightkern))
23330       end
23331      end
23332     end
23333    end
23334    if prevdisc then
23335     local done=false
23336     if post then
23337      local i=p.postinjections
23338      if i then
23339       local leftkern=i.leftkern
23340       if leftkern and leftkern~=0 then
23341        setlink(posttail,fontkern(leftkern))
23342        done=true
23343       end
23344      end
23345     end
23346     if replace then
23347      local i=p.replaceinjections
23348      if i then
23349       local leftkern=i.leftkern
23350       if leftkern and leftkern~=0 then
23351        setlink(replacetail,fontkern(leftkern))
23352        done=true
23353       end
23354      end
23355     else
23356      local i=p.emptyinjections
23357      if i then
23358       local leftkern=i.leftkern
23359       if leftkern and leftkern~=0 then
23360        replace=fontkern(leftkern)
23361        done=true
23362       end
23363      end
23364     end
23365     if done then
23366      setdisc(prevdisc,pre,post,replace)
23367     end
23368    end
23369   end
23370   prevdisc=nil
23371   prevglyph=current
23372  elseif char==false then
23373   prevdisc=nil
23374   prevglyph=current
23375  elseif id==disc_code then
23376   pre,post,replace,pretail,posttail,replacetail=getdisc(current,true)
23377   local done=false
23378   if pre then
23379    for n in nextchar,pre do
23380     local p=rawget(properties,n)
23381     if p then
23382      local i=p.injections or p.preinjections
23383      if i then
23384       local yoffset=i.yoffset
23385       if yoffset and yoffset~=0 then
23386        setoffsets(n,false,yoffset)
23387       end
23388       local leftkern=i.leftkern
23389       if leftkern and leftkern~=0 then
23390        pre=insertnodebefore(pre,n,fontkern(leftkern))
23391        done=true
23392       end
23393       local rightkern=i.rightkern
23394       if rightkern and rightkern~=0 then
23395        insertnodeafter(pre,n,fontkern(rightkern))
23396        done=true
23397       end
23398      end
23399     end
23400    end
23401   end
23402   if post then
23403    for n in nextchar,post do
23404     local p=rawget(properties,n)
23405     if p then
23406      local i=p.injections or p.postinjections
23407      if i then
23408       local yoffset=i.yoffset
23409       if yoffset and yoffset~=0 then
23410        setoffsets(n,false,yoffset)
23411       end
23412       local leftkern=i.leftkern
23413       if leftkern and leftkern~=0 then
23414        post=insertnodebefore(post,n,fontkern(leftkern))
23415        done=true
23416       end
23417       local rightkern=i.rightkern
23418       if rightkern and rightkern~=0 then
23419        insertnodeafter(post,n,fontkern(rightkern))
23420        done=true
23421       end
23422      end
23423     end
23424    end
23425   end
23426   if replace then
23427    for n in nextchar,replace do
23428     local p=rawget(properties,n)
23429     if p then
23430      local i=p.injections or p.replaceinjections
23431      if i then
23432       local yoffset=i.yoffset
23433       if yoffset and yoffset~=0 then
23434        setoffsets(n,false,yoffset)
23435       end
23436       local leftkern=i.leftkern
23437       if leftkern and leftkern~=0 then
23438        replace=insertnodebefore(replace,n,fontkern(leftkern))
23439        done=true
23440       end
23441       local rightkern=i.rightkern
23442       if rightkern and rightkern~=0 then
23443        insertnodeafter(replace,n,fontkern(rightkern))
23444        done=true
23445       end
23446      end
23447     end
23448    end
23449   end
23450   if prevglyph then
23451    if pre then
23452     local p=rawget(properties,prevglyph)
23453     if p then
23454      local i=p.preinjections
23455      if i then
23456       local rightkern=i.rightkern
23457       if rightkern and rightkern~=0 then
23458        pre=insertnodebefore(pre,pre,fontkern(rightkern))
23459        done=true
23460       end
23461      end
23462     end
23463    end
23464    if replace then
23465     local p=rawget(properties,prevglyph)
23466     if p then
23467      local i=p.replaceinjections
23468      if i then
23469       local rightkern=i.rightkern
23470       if rightkern and rightkern~=0 then
23471        replace=insertnodebefore(replace,replace,fontkern(rightkern))
23472        done=true
23473       end
23474      end
23475     end
23476    end
23477   end
23478   if done then
23479    setdisc(current,pre,post,replace)
23480   end
23481   prevglyph=nil
23482   prevdisc=current
23483  else
23484   prevglyph=nil
23485   prevdisc=nil
23486  end
23487  prev=current
23488  current=next
23489 end
23490 if keepregisteredcounts then
23491  keepregisteredcounts=false
23492 else
23493  nofregisteredpositions=0
23494 end
23495 if trace_injections then
23496  show_result(head)
23497 end
23498 return head
23499end
23500local function showoffset(n,flag)
23501 local x,y=getoffsets(n)
23502 if x~=0 or y~=0 then
23503  setcolor(n,"darkgray")
23504 end
23505end
23506local function inject_everything(head,where)
23507 if trace_injections then
23508  trace(head,"everything")
23509 end
23510 local hascursives=nofregisteredcursives>0
23511 local hasmarks=nofregisteredmarks>0
23512 local current=head
23513 local last=nil
23514 local prev=nil
23515 local next=nil
23516 local prevdisc=nil
23517 local prevglyph=nil
23518 local pre=nil 
23519 local post=nil 
23520 local replace=nil 
23521 local pretail=nil 
23522 local posttail=nil 
23523 local replacetail=nil
23524 local cursiveanchor=nil
23525 local minc=0
23526 local maxc=0
23527 local glyphs={}
23528 local marks={}
23529 local nofmarks=0
23530 local function processmark(p,n,pn) 
23531  local px,py=getoffsets(p)
23532  local nx,ny=getoffsets(n)
23533  local ox=0
23534  local rightkern=nil
23535  local pp=rawget(properties,p)
23536  if pp then
23537   pp=pp.injections
23538   if pp then
23539    rightkern=pp.rightkern
23540   end
23541  end
23542  local markdir=pn.markdir
23543  if rightkern then 
23544   ox=px-(pn.markx or 0)-rightkern
23545   if markdir and markdir<0 then
23546    if not pn.markmark then
23547     ox=ox+(pn.leftkern or 0)
23548    end
23549   else
23550    if false then
23551     local leftkern=pp.leftkern
23552     if leftkern then
23553      ox=ox-leftkern
23554     end
23555    end
23556   end
23557  else
23558   ox=px-(pn.markx or 0)
23559   if markdir and markdir<0 then
23560    if not pn.markmark then
23561     local leftkern=pn.leftkern
23562     if leftkern then
23563      ox=ox+leftkern 
23564     end
23565    end
23566   end
23567   if pn.checkmark then
23568    local wn=getwidth(n) 
23569    if wn and wn~=0 then
23570     wn=wn/2
23571     if trace_injections then
23572      report_injections("correcting non zero width mark %C",getchar(n))
23573     end
23574     insertnodebefore(n,n,fontkern(-wn))
23575     insertnodeafter(n,n,fontkern(-wn))
23576    end
23577   end
23578  end
23579  local oy=ny+py+(pn.marky or 0)
23580  if not pn.markmark then
23581   local yoffset=pn.yoffset
23582   if yoffset then
23583    oy=oy+yoffset 
23584   end
23585  end
23586  setoffsets(n,ox,oy)
23587  if trace_marks then
23588   showoffset(n,true)
23589  end
23590 end
23591 while current do
23592  local next=getnext(current)
23593  local char,id=ischar(current)
23594  if char then
23595   local p=rawget(properties,current)
23596   if p then
23597    local i=p.injections
23598    if i then
23599     local pm=i.markbasenode
23600     if pm then
23601      nofmarks=nofmarks+1
23602      marks[nofmarks]=current
23603     else
23604      local yoffset=i.yoffset
23605      if yoffset and yoffset~=0 then
23606       setoffsets(current,false,yoffset)
23607      end
23608      if hascursives then
23609       local cursivex=i.cursivex
23610       if cursivex then
23611        if cursiveanchor then
23612         if cursivex~=0 then
23613          i.leftkern=(i.leftkern or 0)+cursivex
23614         end
23615         if maxc==0 then
23616          minc=1
23617          maxc=1
23618          glyphs[1]=cursiveanchor
23619         else
23620          maxc=maxc+1
23621          glyphs[maxc]=cursiveanchor
23622         end
23623         properties[cursiveanchor].cursivedy=i.cursivey 
23624         last=current
23625        else
23626         maxc=0
23627        end
23628       elseif maxc>0 then
23629        local nx,ny=getoffsets(current)
23630        for i=maxc,minc,-1 do
23631         local ti=glyphs[i]
23632         ny=ny+properties[ti].cursivedy
23633         setoffsets(ti,false,ny) 
23634         if trace_cursive then
23635          showoffset(ti)
23636         end
23637        end
23638        maxc=0
23639        cursiveanchor=nil
23640       end
23641       if i.cursiveanchor then
23642        cursiveanchor=current 
23643       else
23644        if maxc>0 then
23645         local nx,ny=getoffsets(current)
23646         for i=maxc,minc,-1 do
23647          local ti=glyphs[i]
23648          ny=ny+properties[ti].cursivedy
23649          setoffsets(ti,false,ny) 
23650          if trace_cursive then
23651           showoffset(ti)
23652          end
23653         end
23654         maxc=0
23655        end
23656        cursiveanchor=nil
23657       end
23658      end
23659      local leftkern=i.leftkern
23660      local rightkern=i.rightkern
23661      if leftkern and leftkern~=0 then
23662       if rightkern and leftkern==-rightkern then
23663        setoffsets(current,leftkern,false)
23664        rightkern=0
23665       elseif prev and getid(prev)==glue_code then
23666        if useitalickerns then
23667         head=insertnodebefore(head,current,italickern(leftkern))
23668        else
23669         setwidth(prev,getwidth(prev)+leftkern)
23670        end
23671       else
23672        head=insertnodebefore(head,current,fontkern(leftkern))
23673       end
23674      end
23675      if rightkern and rightkern~=0 then
23676       if next and getid(next)==glue_code then
23677        if useitalickerns then
23678         insertnodeafter(head,current,italickern(rightkern))
23679        else
23680         setwidth(next,getwidth(next)+rightkern)
23681        end
23682       else
23683        insertnodeafter(head,current,fontkern(rightkern))
23684       end
23685      end
23686     end
23687    elseif next then
23688     local i=p.emptyinjections
23689     if i then
23690      local rightkern=i.rightkern
23691      if rightkern and rightkern~=0 and getid(next)==disc_code then
23692       local replace=getreplace(next)
23693       if replace then
23694       else
23695        setreplace(next,fontkern(rightkern))
23696       end
23697      end
23698     end
23699    end
23700    if prevdisc then
23701     if p then
23702      local done=false
23703      if post then
23704       local i=p.postinjections
23705       if i then
23706        local leftkern=i.leftkern
23707        if leftkern and leftkern~=0 then
23708         setlink(posttail,fontkern(leftkern))
23709         done=true
23710        end
23711       end
23712      end
23713      if replace then
23714       local i=p.replaceinjections
23715       if i then
23716        local leftkern=i.leftkern
23717        if leftkern and leftkern~=0 then
23718         setlink(replacetail,fontkern(leftkern))
23719         done=true
23720        end
23721       end
23722      else
23723       local i=p.emptyinjections
23724       if i then
23725        local leftkern=i.leftkern
23726        if leftkern and leftkern~=0 then
23727         replace=fontkern(leftkern)
23728         done=true
23729        end
23730       end
23731      end
23732      if done then
23733       setdisc(prevdisc,pre,post,replace)
23734      end
23735     end
23736    end
23737   else
23738    if hascursives and maxc>0 then
23739     local nx,ny=getoffsets(current)
23740     for i=maxc,minc,-1 do
23741      local ti=glyphs[i]
23742      ny=ny+properties[ti].cursivedy
23743      local xi,yi=getoffsets(ti)
23744      setoffsets(ti,xi,yi+ny) 
23745     end
23746     maxc=0
23747     cursiveanchor=nil
23748    end
23749   end
23750   prevdisc=nil
23751   prevglyph=current
23752  elseif char==false then
23753   prevdisc=nil
23754   prevglyph=current
23755  elseif id==disc_code then
23756   pre,post,replace,pretail,posttail,replacetail=getdisc(current,true)
23757   local done=false
23758   if pre then
23759    for n in nextchar,pre do
23760     local p=rawget(properties,n)
23761     if p then
23762      local i=p.injections or p.preinjections
23763      if i then
23764       local yoffset=i.yoffset
23765       if yoffset and yoffset~=0 then
23766        setoffsets(n,false,yoffset)
23767       end
23768       local leftkern=i.leftkern
23769       if leftkern and leftkern~=0 then
23770        pre=insertnodebefore(pre,n,fontkern(leftkern))
23771        done=true
23772       end
23773       local rightkern=i.rightkern
23774       if rightkern and rightkern~=0 then
23775        insertnodeafter(pre,n,fontkern(rightkern))
23776        done=true
23777       end
23778       if hasmarks then
23779        local pm=i.markbasenode
23780        if pm then
23781         processmark(pm,n,i)
23782        end
23783       end
23784      end
23785     end
23786    end
23787   end
23788   if post then
23789    for n in nextchar,post do
23790     local p=rawget(properties,n)
23791     if p then
23792      local i=p.injections or p.postinjections
23793      if i then
23794       local yoffset=i.yoffset
23795       if yoffset and yoffset~=0 then
23796        setoffsets(n,false,yoffset)
23797       end
23798       local leftkern=i.leftkern
23799       if leftkern and leftkern~=0 then
23800        post=insertnodebefore(post,n,fontkern(leftkern))
23801        done=true
23802       end
23803       local rightkern=i.rightkern
23804       if rightkern and rightkern~=0 then
23805        insertnodeafter(post,n,fontkern(rightkern))
23806        done=true
23807       end
23808       if hasmarks then
23809        local pm=i.markbasenode
23810        if pm then
23811         processmark(pm,n,i)
23812        end
23813       end
23814      end
23815     end
23816    end
23817   end
23818   if replace then
23819    for n in nextchar,replace do
23820     local p=rawget(properties,n)
23821     if p then
23822      local i=p.injections or p.replaceinjections
23823      if i then
23824       local yoffset=i.yoffset
23825       if yoffset and yoffset~=0 then
23826        setoffsets(n,false,yoffset)
23827       end
23828       local leftkern=i.leftkern
23829       if leftkern and leftkern~=0 then
23830        replace=insertnodebefore(replace,n,fontkern(leftkern))
23831        done=true
23832       end
23833       local rightkern=i.rightkern
23834       if rightkern and rightkern~=0 then
23835        insertnodeafter(replace,n,fontkern(rightkern))
23836        done=true
23837       end
23838       if hasmarks then
23839        local pm=i.markbasenode
23840        if pm then
23841         processmark(pm,n,i)
23842        end
23843       end
23844      end
23845     end
23846    end
23847   end
23848   if prevglyph then
23849    if pre then
23850     local p=rawget(properties,prevglyph)
23851     if p then
23852      local i=p.preinjections
23853      if i then
23854       local rightkern=i.rightkern
23855       if rightkern and rightkern~=0 then
23856        pre=insertnodebefore(pre,pre,fontkern(rightkern))
23857        done=true
23858       end
23859      end
23860     end
23861    end
23862    if replace then
23863     local p=rawget(properties,prevglyph)
23864     if p then
23865      local i=p.replaceinjections
23866      if i then
23867       local rightkern=i.rightkern
23868       if rightkern and rightkern~=0 then
23869        replace=insertnodebefore(replace,replace,fontkern(rightkern))
23870        done=true
23871       end
23872      end
23873     end
23874    end
23875   end
23876   if done then
23877    setdisc(current,pre,post,replace)
23878   end
23879   prevglyph=nil
23880   prevdisc=current
23881  else
23882   prevglyph=nil
23883   prevdisc=nil
23884  end
23885  prev=current
23886  current=next
23887 end
23888 if hascursives and maxc>0 then
23889  local nx,ny=getoffsets(last)
23890  for i=maxc,minc,-1 do
23891   local ti=glyphs[i]
23892   ny=ny+properties[ti].cursivedy
23893   setoffsets(ti,false,ny) 
23894   if trace_cursive then
23895    showoffset(ti)
23896   end
23897  end
23898 end
23899 if nofmarks>0 then
23900  for i=1,nofmarks do
23901   local m=marks[i]
23902   local p=rawget(properties,m)
23903   local i=p.injections
23904   local b=i.markbasenode
23905   processmark(b,m,i)
23906  end
23907 elseif hasmarks then
23908 end
23909 if keepregisteredcounts then
23910  keepregisteredcounts=false
23911 else
23912  nofregisteredkerns=0
23913  nofregisteredpositions=0
23914  nofregisteredmarks=0
23915  nofregisteredcursives=0
23916 end
23917 if trace_injections then
23918  show_result(head)
23919 end
23920 return head
23921end
23922local triggers=false
23923function nodes.injections.setspacekerns(font,sequence)
23924 if triggers then
23925  triggers[font]=sequence
23926 else
23927  triggers={ [font]=sequence }
23928 end
23929end
23930local getthreshold
23931if context then
23932
23933--removed
23934
23935else
23936 injections.threshold=0
23937 getthreshold=function(font)
23938  local p=fontdata[font].parameters
23939  local f=p.factor
23940  local s=p.spacing
23941  local t=injections.threshold*(s and s.width or p.space or 0)-2
23942  return t>0 and t or 0,f
23943 end
23944end
23945injections.getthreshold=getthreshold
23946function injections.isspace(n,threshold,id)
23947 if (id or getid(n))==glue_code then
23948  local w=getwidth(n)
23949  if threshold and w>threshold then 
23950   return 32
23951  end
23952 end
23953end
23954local getspaceboth=getboth
23955function injections.installgetspaceboth(gb)
23956 getspaceboth=gb or getboth
23957end
23958local function injectspaces(head)
23959 if not triggers then
23960  return head
23961 end
23962 local lastfont=nil
23963 local spacekerns=nil
23964 local leftkerns=nil
23965 local rightkerns=nil
23966 local factor=0
23967 local threshold=0
23968 local leftkern=false
23969 local rightkern=false
23970 local function updatefont(font,trig)
23971  leftkerns=trig.left
23972  rightkerns=trig.right
23973  lastfont=font
23974  threshold,
23975  factor=getthreshold(font)
23976 end
23977 for n in nextglue,head do
23978  local prev,next=getspaceboth(n)
23979  local prevchar=prev and ischar(prev)
23980  local nextchar=next and ischar(next)
23981  if nextchar then
23982   local font=getfont(next)
23983   local trig=triggers[font]
23984   if trig then
23985    if lastfont~=font then
23986     updatefont(font,trig)
23987    end
23988    if rightkerns then
23989     rightkern=rightkerns[nextchar]
23990    end
23991   end
23992  end
23993  if prevchar then
23994   local font=getfont(prev)
23995   local trig=triggers[font]
23996   if trig then
23997    if lastfont~=font then
23998     updatefont(font,trig)
23999    end
24000    if leftkerns then
24001     leftkern=leftkerns[prevchar]
24002    end
24003   end
24004  end
24005  if leftkern then
24006   local old=getwidth(n)
24007   if old>threshold then
24008    if rightkern then
24009     if useitalickerns then
24010      local lnew=leftkern*factor
24011      local rnew=rightkern*factor
24012      if trace_spaces then
24013       report_spaces("%C [%p + %p + %p] %C",prevchar,lnew,old,rnew,nextchar)
24014      end
24015      head=insertnodebefore(head,n,italickern(lnew))
24016      insertnodeafter(head,n,italickern(rnew))
24017     else
24018      local new=old+(leftkern+rightkern)*factor
24019      if trace_spaces then
24020       report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar)
24021      end
24022      setwidth(n,new)
24023     end
24024     rightkern=false
24025    else
24026     if useitalickerns then
24027      local new=leftkern*factor
24028      if trace_spaces then
24029       report_spaces("%C [%p + %p]",prevchar,old,new)
24030      end
24031      insertnodeafter(head,n,italickern(new)) 
24032     else
24033      local new=old+leftkern*factor
24034      if trace_spaces then
24035       report_spaces("%C [%p -> %p]",prevchar,old,new)
24036      end
24037      setwidth(n,new)
24038     end
24039    end
24040   end
24041   leftkern=false
24042  elseif rightkern then
24043   local old=getwidth(n)
24044   if old>threshold then
24045    if useitalickerns then
24046     local new=rightkern*factor
24047     if trace_spaces then
24048      report_spaces("[%p + %p] %C",old,new,nextchar)
24049     end
24050     insertnodeafter(head,n,italickern(new))
24051    else
24052     local new=old+rightkern*factor
24053     if trace_spaces then
24054      report_spaces("[%p -> %p] %C",old,new,nextchar)
24055     end
24056     setwidth(n,new)
24057    end
24058   else
24059   end
24060   rightkern=false
24061  end
24062 end
24063 triggers=false
24064 return head
24065end
24066function injections.handler(head,where)
24067 if triggers then
24068  head=injectspaces(head)
24069 end
24070 if nofregisteredmarks>0 or nofregisteredcursives>0 then
24071  if trace_injections then
24072   report_injections("injection variant %a","everything")
24073  end
24074  return inject_everything(head,where)
24075 elseif nofregisteredpositions>0 then
24076  if trace_injections then
24077   report_injections("injection variant %a","positions")
24078  end
24079  return inject_positions_only(head,where)
24080 elseif nofregisteredkerns>0 then
24081  if trace_injections then
24082   report_injections("injection variant %a","kerns")
24083  end
24084  return inject_kerns_only(head,where)
24085 else
24086  return head
24087 end
24088end
24089
24090end -- closure
24091
24092do -- begin closure to overcome local limits and interference
24093
24094if not modules then modules={} end modules ['font-oup']={
24095 version=1.001,
24096 comment="companion to font-ini.mkiv",
24097 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
24098 copyright="PRAGMA ADE / ConTeXt Development Team",
24099 license="see context related readme files"
24100}
24101local next,type=next,type
24102local P,R,S=lpeg.P,lpeg.R,lpeg.S
24103local lpegmatch=lpeg.match
24104local insert,remove,copy,unpack=table.insert,table.remove,table.copy,table.unpack
24105local find=string.find
24106local idiv=number.idiv
24107local formatters=string.formatters
24108local sortedkeys=table.sortedkeys
24109local sortedhash=table.sortedhash
24110local tohash=table.tohash
24111local setmetatableindex=table.setmetatableindex
24112local report_error=logs.reporter("otf reader","error")
24113local report_markwidth=logs.reporter("otf reader","markwidth")
24114local report_cleanup=logs.reporter("otf reader","cleanup")
24115local report_optimizations=logs.reporter("otf reader","merges")
24116local report_unicodes=logs.reporter("otf reader","unicodes")
24117local trace_markwidth=false  trackers.register("otf.markwidth",function(v) trace_markwidth=v end)
24118local trace_cleanup=false  trackers.register("otf.cleanups",function(v) trace_cleanups=v end)
24119local trace_optimizations=false  trackers.register("otf.optimizations",function(v) trace_optimizations=v end)
24120local trace_unicodes=false  trackers.register("otf.unicodes",function(v) trace_unicodes=v end)
24121local readers=fonts.handlers.otf.readers
24122local privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000 
24123local f_private=formatters["P%05X"]
24124local f_unicode=formatters["U%05X"]
24125local f_index=formatters["I%05X"]
24126local f_character_y=formatters["%C"]
24127local f_character_n=formatters["[ %C ]"]
24128local check_duplicates=true 
24129local check_soft_hyphen=context 
24130directives.register("otf.checksofthyphen",function(v)
24131 check_soft_hyphen=v 
24132end)
24133local function replaced(list,index,replacement)
24134 if type(list)=="number" then
24135  return replacement
24136 elseif type(replacement)=="table" then
24137  local t={}
24138  local n=index-1
24139  for i=1,n do
24140   t[i]=list[i]
24141  end
24142  for i=1,#replacement do
24143   n=n+1
24144   t[n]=replacement[i]
24145  end
24146  for i=index+1,#list do
24147   n=n+1
24148   t[n]=list[i]
24149  end
24150 else
24151  list[index]=replacement
24152  return list
24153 end
24154end
24155local function unifyresources(fontdata,indices)
24156 local descriptions=fontdata.descriptions
24157 local resources=fontdata.resources
24158 if not descriptions or not resources then
24159  return
24160 end
24161 local nofindices=#indices
24162 local variants=fontdata.resources.variants
24163 if variants then
24164  for selector,unicodes in next,variants do
24165   for unicode,index in next,unicodes do
24166    unicodes[unicode]=indices[index]
24167   end
24168  end
24169 end
24170 local function remark(marks)
24171  if marks then
24172   local newmarks={}
24173   for k,v in next,marks do
24174    local u=indices[k]
24175    if u then
24176     newmarks[u]=v
24177    elseif trace_optimizations then
24178     report_optimizations("discarding mark %i",k)
24179    end
24180   end
24181   return newmarks
24182  end
24183 end
24184 local marks=resources.marks
24185 if marks then
24186  resources.marks=remark(marks)
24187 end
24188 local markclasses=resources.markclasses
24189 if markclasses then
24190  for class,marks in next,markclasses do
24191   markclasses[class]=remark(marks)
24192  end
24193 end
24194 local marksets=resources.marksets
24195 if marksets then
24196  for class,marks in next,marksets do
24197   marksets[class]=remark(marks)
24198  end
24199 end
24200 local done={}
24201 local duplicates=check_duplicates and resources.duplicates
24202 if duplicates and not next(duplicates) then
24203  duplicates=false
24204 end
24205 local function recover(cover) 
24206  for i=1,#cover do
24207   local c=cover[i]
24208   if not done[c] then
24209    local t={}
24210    for k,v in next,c do
24211     local ug=indices[k]
24212     if ug then
24213      t[ug]=v
24214     else
24215      report_error("case %i, bad index in unifying %s: %s of %s",1,"coverage",k,nofindices)
24216     end
24217    end
24218    cover[i]=t
24219    done[c]=d
24220   end
24221  end
24222 end
24223 local function recursed(c,kind) 
24224  local t={}
24225  for g,d in next,c do
24226   if type(d)=="table" then
24227    local ug=indices[g]
24228    if ug then
24229     t[ug]=recursed(d,kind)
24230    else
24231     report_error("case %i, bad index in unifying %s: %s of %s",1,kind,g,nofindices)
24232    end
24233   else
24234    t[g]=indices[d] 
24235   end
24236  end
24237  return t
24238 end
24239 local function unifythem(sequences)
24240  if not sequences then
24241   return
24242  end
24243  for i=1,#sequences do
24244   local sequence=sequences[i]
24245   local kind=sequence.type
24246   local steps=sequence.steps
24247   local features=sequence.features
24248   if steps then
24249    for i=1,#steps do
24250     local step=steps[i]
24251     if kind=="gsub_single" then
24252      local c=step.coverage
24253      if c then
24254       local t1=done[c]
24255       if not t1 then
24256        t1={}
24257        if duplicates then
24258         for g1,d1 in next,c do
24259          local ug1=indices[g1]
24260          if ug1 then
24261           local ud1=indices[d1]
24262           if ud1 then
24263            t1[ug1]=ud1
24264            local dg1=duplicates[ug1]
24265            if dg1 then
24266             for u in next,dg1 do
24267              t1[u]=ud1
24268             end
24269            end
24270           else
24271            report_error("case %i, bad index in unifying %s: %s of %s",3,kind,d1,nofindices)
24272           end
24273          else
24274           report_error("case %i, bad index in unifying %s: %s of %s",1,kind,g1,nofindices)
24275          end
24276         end
24277        else
24278         for g1,d1 in next,c do
24279          local ug1=indices[g1]
24280          if ug1 then
24281           t1[ug1]=indices[d1]
24282          else
24283           report_error("fuzzy case %i in unifying %s: %i",2,kind,g1)
24284          end
24285         end
24286        end
24287        done[c]=t1
24288       end
24289       step.coverage=t1
24290      end
24291     elseif kind=="gpos_pair" then
24292      local c=step.coverage
24293      if c then
24294       local t1=done[c]
24295       if not t1 then
24296        t1={}
24297        for g1,d1 in next,c do
24298         local ug1=indices[g1]
24299         if ug1 then
24300          local t2=done[d1]
24301          if not t2 then
24302           t2={}
24303           for g2,d2 in next,d1 do
24304            local ug2=indices[g2]
24305            if ug2 then
24306             t2[ug2]=d2
24307            else
24308             report_error("case %i, bad index in unifying %s: %s of %s",1,kind,g2,nofindices,nofindices)
24309            end
24310           end
24311           done[d1]=t2
24312          end
24313          t1[ug1]=t2
24314         else
24315          report_error("case %i, bad index in unifying %s: %s of %s",2,kind,g1,nofindices)
24316         end
24317        end
24318        done[c]=t1
24319       end
24320       step.coverage=t1
24321      end
24322     elseif kind=="gsub_ligature" then
24323      local c=step.coverage
24324      if c then
24325       step.coverage=recursed(c,kind)
24326      end
24327     elseif kind=="gsub_alternate" or kind=="gsub_multiple" then
24328      local c=step.coverage
24329      if c then
24330       local t1=done[c]
24331       if not t1 then
24332        t1={}
24333        if duplicates then
24334         for g1,d1 in next,c do
24335          for i=1,#d1 do
24336           local d1i=d1[i]
24337           local d1u=indices[d1i]
24338           if d1u then
24339            d1[i]=d1u
24340           else
24341            report_error("case %i, bad index in unifying %s: %s of %s",1,kind,i,d1i,nofindices)
24342           end
24343          end
24344          local ug1=indices[g1]
24345          if ug1 then
24346           t1[ug1]=d1
24347           local dg1=duplicates[ug1]
24348           if dg1 then
24349            for u in next,dg1 do
24350             t1[u]=copy(d1)
24351            end
24352           end
24353          else
24354           report_error("case %i, bad index in unifying %s: %s of %s",2,kind,g1,nofindices)
24355          end
24356         end
24357        else
24358         for g1,d1 in next,c do
24359          for i=1,#d1 do
24360           local d1i=d1[i]
24361           local d1u=indices[d1i]
24362           if d1u then
24363            d1[i]=d1u
24364           else
24365            report_error("case %i, bad index in unifying %s: %s of %s",2,kind,d1i,nofindices)
24366           end
24367          end
24368          t1[indices[g1]]=d1
24369         end
24370        end
24371        done[c]=t1
24372       end
24373       step.coverage=t1
24374      end
24375     elseif kind=="gpos_single" then
24376      local c=step.coverage
24377      if c then
24378       local t1=done[c]
24379       if not t1 then
24380        t1={}
24381        if duplicates then
24382         for g1,d1 in next,c do
24383          local ug1=indices[g1]
24384          if ug1 then
24385           t1[ug1]=d1
24386           local dg1=duplicates[ug1]
24387           if dg1 then
24388            for u in next,dg1 do
24389             t1[u]=d1
24390            end
24391           end
24392          else
24393           report_error("case %i, bad index in unifying %s: %s of %s",1,kind,g1,nofindices)
24394          end
24395         end
24396        else
24397         for g1,d1 in next,c do
24398          local ug1=indices[g1]
24399          if ug1 then
24400           t1[ug1]=d1
24401          else
24402           report_error("case %i, bad index in unifying %s: %s of %s",2,kind,g1,nofindices)
24403          end
24404         end
24405        end
24406        done[c]=t1
24407       end
24408       step.coverage=t1
24409      end
24410     elseif kind=="gpos_mark2base" or kind=="gpos_mark2mark" or kind=="gpos_mark2ligature" then
24411      local c=step.coverage
24412      if c then
24413       local t1=done[c]
24414       if not t1 then
24415        t1={}
24416        for g1,d1 in next,c do
24417         local ug1=indices[g1]
24418         if ug1 then
24419          t1[ug1]=d1
24420         else
24421          report_error("case %i, bad index in unifying %s: %s of %s",1,kind,g1,nofindices)
24422         end
24423        end
24424        done[c]=t1
24425       end
24426       step.coverage=t1
24427      end
24428      local c=step.baseclasses
24429      if c then
24430       local t1=done[c]
24431       if not t1 then
24432        for g1,d1 in next,c do
24433         local t2=done[d1]
24434         if not t2 then
24435          t2={}
24436          for g2,d2 in next,d1 do
24437           local ug2=indices[g2]
24438           if ug2 then
24439            t2[ug2]=d2
24440           else
24441            report_error("case %i, bad index in unifying %s: %s of %s",2,kind,g2,nofindices)
24442           end
24443          end
24444          done[d1]=t2
24445         end
24446         c[g1]=t2
24447        end
24448        done[c]=c
24449       end
24450      end
24451     elseif kind=="gpos_cursive" then
24452      local c=step.coverage
24453      if c then
24454       local t1=done[c]
24455       if not t1 then
24456        t1={}
24457        if duplicates then
24458         for g1,d1 in next,c do
24459          local ug1=indices[g1]
24460          if ug1 then
24461           t1[ug1]=d1
24462           local dg1=duplicates[ug1]
24463           if dg1 then
24464            for u in next,dg1 do
24465             t1[u]=copy(d1)
24466            end
24467           end
24468          else
24469           report_error("case %i, bad index in unifying %s: %s of %s",1,kind,g1,nofindices)
24470          end
24471         end
24472        else
24473         for g1,d1 in next,c do
24474          local ug1=indices[g1]
24475          if ug1 then
24476           t1[ug1]=d1
24477          else
24478           report_error("case %i, bad index in unifying %s: %s of %s",2,kind,g1,nofindices)
24479          end
24480         end
24481        end
24482        done[c]=t1
24483       end
24484       step.coverage=t1
24485      end
24486     end
24487     local rules=step.rules
24488     if rules then
24489      for i=1,#rules do
24490       local rule=rules[i]
24491       local before=rule.before   if before  then recover(before)  end
24492       local after=rule.after if after   then recover(after)   end
24493       local current=rule.current  if current then recover(current) end
24494       local replacements=rule.replacements
24495       if replacements then
24496        if not done[replacements] then
24497         local r={}
24498         for k,v in next,replacements do
24499          r[indices[k]]=indices[v]
24500         end
24501         rule.replacements=r
24502         done[replacements]=r
24503        end
24504       end
24505      end
24506     end
24507    end
24508   end
24509    end
24510 end
24511 unifythem(resources.sequences)
24512 unifythem(resources.sublookups)
24513end
24514local function copyduplicates(fontdata)
24515 if check_duplicates then
24516  local descriptions=fontdata.descriptions
24517  local resources=fontdata.resources
24518  local duplicates=resources.duplicates
24519  if check_soft_hyphen then
24520   local dh=descriptions[0x2D]
24521   if dh then
24522    local ds=descriptions[0xAD]
24523    if not ds or ds.width~=dh.width then
24524     descriptions[0xAD]=nil
24525     if ds then
24526      if trace_unicodes then
24527       report_unicodes("patching soft hyphen")
24528      end
24529     else
24530      if trace_unicodes then
24531       report_unicodes("adding soft hyphen")
24532      end
24533     end
24534     if not duplicates then
24535      duplicates={}
24536      resources.duplicates=duplicates
24537     end
24538     local d=duplicates[0x2D]
24539     if d then
24540      d[0xAD]=true
24541     else
24542      duplicates[0x2D]={ [0xAD]=true }
24543     end
24544    end
24545   end
24546  end
24547  if duplicates then
24548     for u,d in next,duplicates do
24549    local du=descriptions[u]
24550    if du then
24551     local t={ f_character_y(u),"@",f_index(du.index),"->" }
24552     local n=0
24553     local m=25
24554     for u in next,d do
24555      if descriptions[u] then
24556       if n<m then
24557        t[n+4]=f_character_n(u)
24558       end
24559      else
24560       local c=copy(du)
24561       c.unicode=u 
24562       descriptions[u]=c
24563       if n<m then
24564        t[n+4]=f_character_y(u)
24565       end
24566      end
24567      n=n+1
24568     end
24569     if trace_unicodes then
24570      if n<=m then
24571       report_unicodes("%i : % t",n,t)
24572      else
24573       report_unicodes("%i : % t ...",n,t)
24574      end
24575     end
24576    else
24577    end
24578   end
24579  end
24580 end
24581end
24582local ignore={ 
24583 ["notdef"]=true,
24584 [".notdef"]=true,
24585 ["null"]=true,
24586 [".null"]=true,
24587 ["nonmarkingreturn"]=true,
24588}
24589local function checklookups(fontdata,missing,nofmissing)
24590 local descriptions=fontdata.descriptions
24591 local resources=fontdata.resources
24592 if missing and nofmissing and nofmissing<=0 then
24593  return
24594 end
24595 local singles={}
24596 local alternates={}
24597 local ligatures={}
24598 if not missing then
24599  missing={}
24600  nofmissing=0
24601  for u,d in next,descriptions do
24602   if not d.unicode then
24603    nofmissing=nofmissing+1
24604    missing[u]=true
24605   end
24606  end
24607 end
24608 local function collectthem(sequences)
24609  if not sequences then
24610   return
24611  end
24612  for i=1,#sequences do
24613   local sequence=sequences[i]
24614   local kind=sequence.type
24615   local steps=sequence.steps
24616   if steps then
24617    for i=1,#steps do
24618     local step=steps[i]
24619     if kind=="gsub_single" then
24620      local c=step.coverage
24621      if c then
24622       singles[#singles+1]=c
24623      end
24624     elseif kind=="gsub_alternate" then
24625      local c=step.coverage
24626      if c then
24627       alternates[#alternates+1]=c
24628      end
24629     elseif kind=="gsub_ligature" then
24630      local c=step.coverage
24631      if c then
24632       ligatures[#ligatures+1]=c
24633      end
24634     end
24635    end
24636   end
24637  end
24638 end
24639 collectthem(resources.sequences)
24640 collectthem(resources.sublookups)
24641 local loops=0
24642 while true do
24643  loops=loops+1
24644  local old=nofmissing
24645  for i=1,#singles do
24646   local c=singles[i]
24647   for g1,g2 in next,c do
24648    if missing[g1] then
24649     local u2=descriptions[g2].unicode
24650     if u2 then
24651      missing[g1]=false
24652      descriptions[g1].unicode=u2
24653      nofmissing=nofmissing-1
24654     end
24655    end
24656    if missing[g2] then
24657     local u1=descriptions[g1].unicode
24658     if u1 then
24659      missing[g2]=false
24660      descriptions[g2].unicode=u1
24661      nofmissing=nofmissing-1
24662     end
24663    end
24664   end
24665  end
24666  for i=1,#alternates do
24667   local c=alternates[i]
24668   for g1,d1 in next,c do
24669    if missing[g1] then
24670     for i=1,#d1 do
24671      local g2=d1[i]
24672      local u2=descriptions[g2].unicode
24673      if u2 then
24674       missing[g1]=false
24675       descriptions[g1].unicode=u2
24676       nofmissing=nofmissing-1
24677      end
24678     end
24679    end
24680    if not missing[g1] then
24681     for i=1,#d1 do
24682      local g2=d1[i]
24683      if missing[g2] then
24684       local u1=descriptions[g1].unicode
24685       if u1 then
24686        missing[g2]=false
24687        descriptions[g2].unicode=u1
24688        nofmissing=nofmissing-1
24689       end
24690      end
24691     end
24692    end
24693   end
24694  end
24695  if nofmissing<=0 then
24696   if trace_unicodes then
24697    report_unicodes("all missings done in %s loops",loops)
24698   end
24699   return
24700  elseif old==nofmissing then
24701   break
24702  end
24703 end
24704 local t,n 
24705 local function recursed(c)
24706  for g,d in next,c do
24707   if g~="ligature" then
24708    local u=descriptions[g].unicode
24709    if u then
24710     n=n+1
24711     t[n]=u
24712     recursed(d)
24713     n=n-1
24714    end
24715   elseif missing[d] then
24716    local l={}
24717    local m=0
24718    for i=1,n do
24719     local u=t[i]
24720     if type(u)=="table" then
24721      for i=1,#u do
24722       m=m+1
24723       l[m]=u[i]
24724      end
24725     else
24726      m=m+1
24727      l[m]=u
24728     end
24729    end
24730    missing[d]=false
24731    descriptions[d].unicode=l
24732    nofmissing=nofmissing-1
24733   end
24734  end
24735 end
24736 if nofmissing>0 then
24737  t={}
24738  n=0
24739  local loops=0
24740  while true do
24741   loops=loops+1
24742   local old=nofmissing
24743   for i=1,#ligatures do
24744    recursed(ligatures[i])
24745   end
24746   if nofmissing<=0 then
24747    if trace_unicodes then
24748     report_unicodes("all missings done in %s loops",loops)
24749    end
24750    return
24751   elseif old==nofmissing then
24752    break
24753   end
24754  end
24755  t=nil
24756  n=0
24757 end
24758 if trace_unicodes and nofmissing>0 then
24759  local done={}
24760  for i,r in next,missing do
24761   if r then
24762    local data=descriptions[i]
24763    local name=data and data.name or f_index(i)
24764    if not ignore[name] then
24765     done[name]=true
24766    end
24767   end
24768  end
24769  if next(done) then
24770   report_unicodes("not unicoded: % t",sortedkeys(done))
24771  end
24772 end
24773 for k,v in next,descriptions do
24774  local math=v.math
24775  if math then
24776   local variants=math.variants
24777   local parts=math.parts
24778   local unicode=v.unicode
24779   if variants then
24780    if unicode then
24781     for i=1,#variants do
24782      local v=descriptions[variants[i]]
24783      if not v then
24784      elseif v.unicode then
24785      else
24786       v.unicode=unicode
24787      end
24788     end
24789    end
24790   end
24791   if parts then
24792    parts[idiv(#parts,2)+1].unicode=unicode
24793   end
24794  end
24795 end
24796end
24797local firstprivate=fonts.privateoffsets and fonts.privateoffsets.textbase or 0xF0000
24798local puafirst=0xE000
24799local pualast=0xF8FF
24800local function unifymissing(fontdata)
24801 if not fonts.mappings then
24802  require("font-map")
24803  require("font-agl")
24804 end
24805 local unicodes={}
24806 local resources=fontdata.resources
24807 resources.unicodes=unicodes
24808 for unicode,d in next,fontdata.descriptions do
24809  if unicode<privateoffset then
24810   if unicode>=puafirst and unicode<=pualast then
24811   else
24812    local name=d.name
24813    if name then
24814     unicodes[name]=unicode
24815    end
24816   end
24817  else
24818  end
24819 end
24820 fonts.mappings.addtounicode(fontdata,fontdata.filename,checklookups)
24821 resources.unicodes=nil
24822end
24823local function unifyglyphs(fontdata,usenames)
24824 local private=fontdata.private or privateoffset
24825 local glyphs=fontdata.glyphs
24826 local indices={}
24827 local descriptions={}
24828 local names=usenames and {}
24829 local resources=fontdata.resources
24830 local zero=glyphs[0]
24831 local zerocode=zero.unicode
24832 local nofglyphs=#glyphs
24833 if not zerocode then
24834  zerocode=private
24835  zero.unicode=zerocode
24836  private=private+1
24837 end
24838 descriptions[zerocode]=zero
24839 if names then
24840  local name=glyphs[0].name or f_private(zerocode)
24841  indices[0]=name
24842  names[name]=zerocode
24843 else
24844  indices[0]=zerocode
24845 end
24846 if names then
24847  for index=1,nofglyphs do
24848   local glyph=glyphs[index]
24849   local unicode=glyph.unicode 
24850   if not unicode then
24851    unicode=private
24852    local name=glyph.name or f_private(unicode)
24853    indices[index]=name
24854    names[name]=unicode
24855    private=private+1
24856   elseif unicode>=firstprivate then
24857    unicode=private
24858    local name=glyph.name or f_private(unicode)
24859    indices[index]=name
24860    names[name]=unicode
24861    private=private+1
24862   elseif unicode>=puafirst and unicode<=pualast then
24863    local name=glyph.name or f_private(unicode)
24864    indices[index]=name
24865    names[name]=unicode
24866   elseif descriptions[unicode] then
24867    unicode=private
24868    local name=glyph.name or f_private(unicode)
24869    indices[index]=name
24870    names[name]=unicode
24871    private=private+1
24872   else
24873    local name=glyph.name or f_unicode(unicode)
24874    indices[index]=name
24875    names[name]=unicode
24876   end
24877   descriptions[unicode]=glyph
24878  end
24879 elseif trace_unicodes then
24880  for index=1,nofglyphs do
24881   local glyph=glyphs[index]
24882   local unicode=glyph.unicode 
24883   if not unicode then
24884    unicode=private
24885    indices[index]=unicode
24886    private=private+1
24887   elseif unicode>=firstprivate then
24888    local name=glyph.name
24889    if name then
24890     report_unicodes("moving glyph %a indexed %05X from private %U to %U ",name,index,unicode,private)
24891    else
24892     report_unicodes("moving glyph indexed %05X from private %U to %U ",index,unicode,private)
24893    end
24894    unicode=private
24895    indices[index]=unicode
24896    private=private+1
24897   elseif unicode>=puafirst and unicode<=pualast then
24898    local name=glyph.name
24899    if name then
24900     report_unicodes("keeping private unicode %U for glyph %a indexed %05X",unicode,name,index)
24901    else
24902     report_unicodes("keeping private unicode %U for glyph indexed %05X",unicode,index)
24903    end
24904    indices[index]=unicode
24905   elseif descriptions[unicode] then
24906    local name=glyph.name
24907    if name then
24908     report_unicodes("assigning duplicate unicode %U to %U for glyph %a indexed %05X ",unicode,private,name,index)
24909    else
24910     report_unicodes("assigning duplicate unicode %U to %U for glyph indexed %05X ",unicode,private,index)
24911    end
24912    unicode=private
24913    indices[index]=unicode
24914    private=private+1
24915   else
24916    indices[index]=unicode
24917   end
24918   descriptions[unicode]=glyph
24919  end
24920 else
24921  for index=1,nofglyphs do
24922   local glyph=glyphs[index]
24923   local unicode=glyph.unicode 
24924   if not unicode then
24925    unicode=private
24926    indices[index]=unicode
24927    private=private+1
24928   elseif unicode>=firstprivate then
24929    local name=glyph.name
24930    unicode=private
24931    indices[index]=unicode
24932    private=private+1
24933   elseif unicode>=puafirst and unicode<=pualast then
24934    local name=glyph.name
24935    indices[index]=unicode
24936   elseif descriptions[unicode] then
24937    local name=glyph.name
24938    unicode=private
24939    indices[index]=unicode
24940    private=private+1
24941   else
24942    indices[index]=unicode
24943   end
24944   descriptions[unicode]=glyph
24945  end
24946 end
24947 if LUATEXENGINE=="luametatex" then
24948  for index=1,nofglyphs do
24949   local math=glyphs[index].math
24950   if math then
24951    local list=math.parts
24952    if list then
24953     for i=1,#list do local l=list[i] l.glyph=indices[l.glyph] end
24954    end
24955    local list=math.variants
24956    if list then
24957     for i=1,#list do list[i]=indices[list[i]] end
24958    end
24959   end
24960  end
24961 else
24962  for index=1,nofglyphs do
24963   local math=glyphs[index].math
24964   if math then
24965    local list=math.vparts
24966    if list then
24967     for i=1,#list do local l=list[i] l.glyph=indices[l.glyph] end
24968    end
24969    local list=math.hparts
24970    if list then
24971     for i=1,#list do local l=list[i] l.glyph=indices[l.glyph] end
24972    end
24973    local list=math.vvariants
24974    if list then
24975     for i=1,#list do list[i]=indices[list[i]] end
24976    end
24977    local list=math.hvariants
24978    if list then
24979     for i=1,#list do list[i]=indices[list[i]] end
24980    end
24981   end
24982  end
24983 end
24984 local colorpalettes=resources.colorpalettes
24985 if colorpalettes then
24986  for index=1,nofglyphs do
24987   local colors=glyphs[index].colors
24988   if colors then
24989    for i=1,#colors do
24990     local c=colors[i]
24991     if c then 
24992      c.slot=indices[c.slot]
24993     end
24994    end
24995   end
24996  end
24997 end
24998 fontdata.private=private
24999 fontdata.glyphs=nil
25000 fontdata.names=names
25001 fontdata.descriptions=descriptions
25002 fontdata.hashmethod=hashmethod
25003 fontdata.nofglyphs=nofglyphs
25004 return indices,names
25005end
25006local stripredundant  do
25007 local p_hex=R("af","AF","09")
25008 local p_digit=R("09")
25009 local p_done=S("._-")^0+P(-1)
25010 local p_style=P(".")
25011 local p_alpha=R("az","AZ")
25012 local p_ALPHA=R("AZ")
25013 local p_crappyname=(
25014  lpeg.utfchartabletopattern({ "uni","u" },true)*S("Xx_")^0*p_hex^1
25015+lpeg.utfchartabletopattern({ "identity","glyph","jamo" },true)*p_hex^1
25016+lpeg.utfchartabletopattern({ "index","afii" },true)*p_digit^1
25017+p_digit*p_hex^3+p_alpha*p_digit^1
25018+P("aj")*p_digit^1+P("eh_")*(p_digit^1+p_ALPHA*p_digit^1)+(1-P("_"))^1*P("_uni")*p_hex^1+P("_")*P(1)^1
25019 )*p_done
25020 if context then
25021  local forcekeep=false
25022  directives.register("otf.keepnames",function(v)
25023   report_cleanup("keeping weird glyph names, expect larger files and more memory usage")
25024   forcekeep=v
25025  end)
25026  local function stripvariants(descriptions,list)
25027   local n=list and #list or 0
25028   if n>0 then
25029    for i=1,n do
25030     local g=list[i]
25031     if g then
25032      local d=descriptions[g]
25033      if d and d.name then
25034       d.name=nil
25035       n=n+1
25036      end
25037     end
25038    end
25039   end
25040   return n
25041  end
25042  local function stripparts(descriptions,list)
25043   local n=list and #list or 0
25044   if n>0 then
25045    for i=1,n do
25046     local g=list[i].glyph
25047     if g then
25048      local d=descriptions[g]
25049      if d and d.name then
25050       d.name=nil
25051       n=n+1
25052      end
25053     end
25054    end
25055   end
25056   return n
25057  end
25058  local function collectsimple(fontdata)
25059   return nil
25060  end
25061  stripredundant=function(fontdata)
25062   local descriptions=fontdata.descriptions
25063   if descriptions then
25064    local n=0
25065    local c=0
25066    for unicode,d in next,descriptions do
25067     local m=d.math
25068     if m then
25069      n=n+stripvariants(descriptions,m.vvariants)
25070      n=n+stripvariants(descriptions,m.hvariants)
25071      n=n+stripparts   (descriptions,m.vparts)
25072      n=n+stripparts   (descriptions,m.hparts)
25073     end
25074    end
25075    if forcekeep then
25076     for unicode,d in next,descriptions do
25077      if d.class=="base" then
25078       d.class=nil
25079       c=c+1
25080      end
25081     end
25082    else
25083     local keeplist=collectsimple(fontdata)
25084     for unicode,d in next,descriptions do
25085      local name=d.name
25086      if name then
25087       if keeplist and keeplist[name] then
25088       elseif lpegmatch(p_crappyname,name) then
25089        d.name=nil
25090        n=n+1
25091       end
25092      end
25093      if d.class=="base" then
25094       d.class=nil
25095       c=c+1
25096      end
25097     end
25098    end
25099    if trace_cleanup then
25100     if n>0 then
25101      report_cleanup("%s bogus names removed (verbose unicode)",n)
25102     end
25103     if c>0 then
25104      report_cleanup("%s base class tags removed (default is base)",c)
25105     end
25106    end
25107   end
25108  end
25109 else
25110  stripredundant=function(fontdata)
25111   local descriptions=fontdata.descriptions
25112   if descriptions then
25113    if fonts.privateoffsets.keepnames then
25114     for unicode,d in next,descriptions do
25115      if d.class=="base" then
25116       d.class=nil
25117      end
25118     end
25119    else
25120     for unicode,d in next,descriptions do
25121      local name=d.name
25122      if name then
25123       if lpegmatch(p_crappyname,name) then
25124        d.name=nil
25125       end
25126      end
25127      if d.class=="base" then
25128       d.class=nil
25129      end
25130     end
25131    end
25132   end
25133  end
25134 end
25135 readers.stripredundant=stripredundant
25136end
25137function readers.getcomponents(fontdata) 
25138 local resources=fontdata.resources
25139 if resources then
25140  local sequences=resources.sequences
25141  if sequences then
25142   local collected={}
25143   for i=1,#sequences do
25144    local sequence=sequences[i]
25145    if sequence.type=="gsub_ligature" then
25146     local steps=sequence.steps
25147     if steps then
25148      local l={}
25149      local function traverse(p,k,v)
25150       if k=="ligature" then
25151        collected[v]={ unpack(l) }
25152       elseif tonumber(v) then
25153        insert(l,k)
25154        collected[v]={ unpack(l) }
25155        remove(l)
25156       else
25157        insert(l,k)
25158        for k,vv in next,v do
25159         traverse(p,k,vv)
25160        end
25161        remove(l)
25162       end
25163      end
25164      for i=1,#steps do
25165       local c=steps[i].coverage
25166       if c then
25167        for k,v in next,c do
25168         traverse(k,k,v)
25169        end
25170       end
25171      end
25172     end
25173    end
25174   end
25175   if next(collected) then
25176    while true do
25177     local done=false
25178     for k,v in next,collected do
25179      for i=1,#v do
25180       local vi=v[i]
25181       if vi==k then
25182        collected[k]=nil
25183        break
25184       else
25185        local c=collected[vi]
25186        if c then
25187         done=true
25188         local t={}
25189         local n=i-1
25190         for j=1,n do
25191          t[j]=v[j]
25192         end
25193         for j=1,#c do
25194          n=n+1
25195          t[n]=c[j]
25196         end
25197         for j=i+1,#v do
25198          n=n+1
25199          t[n]=v[j]
25200         end
25201         collected[k]=t
25202         break
25203        end
25204       end
25205      end
25206     end
25207     if not done then
25208      break
25209     end
25210    end
25211    return collected
25212   end
25213  end
25214 end
25215end
25216readers.unifymissing=unifymissing
25217function readers.rehash(fontdata,hashmethod) 
25218 if not (fontdata and fontdata.glyphs) then
25219  return
25220 elseif hashmethod=="indices" then
25221  fontdata.hashmethod="indices"
25222 elseif hashmethod=="names" then
25223  fontdata.hashmethod="names"
25224  local indices=unifyglyphs(fontdata,true)
25225  unifyresources(fontdata,indices)
25226  copyduplicates(fontdata)
25227  unifymissing(fontdata)
25228 else
25229  fontdata.hashmethod="unicodes"
25230  local indices=unifyglyphs(fontdata)
25231  unifyresources(fontdata,indices)
25232  copyduplicates(fontdata)
25233  unifymissing(fontdata)
25234  stripredundant(fontdata)
25235 end
25236end
25237function readers.checkhash(fontdata)
25238 local hashmethod=fontdata.hashmethod
25239 if hashmethod=="unicodes" then
25240  fontdata.names=nil 
25241 elseif hashmethod=="names" and fontdata.names then
25242  unifyresources(fontdata,fontdata.names)
25243  copyduplicates(fontdata)
25244  fontdata.hashmethod="unicodes"
25245  fontdata.names=nil 
25246 else
25247  readers.rehash(fontdata,"unicodes")
25248 end
25249end
25250function readers.addunicodetable(fontdata)
25251 local resources=fontdata.resources
25252 local unicodes=resources.unicodes
25253 if not unicodes then
25254  local descriptions=fontdata.descriptions
25255  if descriptions then
25256   unicodes={}
25257   resources.unicodes=unicodes
25258   for u,d in next,descriptions do
25259    local n=d.name
25260    if n then
25261     unicodes[n]=u
25262    end
25263   end
25264  end
25265 end
25266end
25267local concat,sort=table.concat,table.sort
25268local next,type,tostring=next,type,tostring
25269local criterium=1
25270local threshold=0
25271local trace_packing=false  trackers.register("otf.packing",function(v) trace_packing=v end)
25272local trace_loading=false  trackers.register("otf.loading",function(v) trace_loading=v end)
25273local report_otf=logs.reporter("fonts","otf loading")
25274local function tabstr_normal(t)
25275 local s={}
25276 local n=0
25277 for k,v in next,t do
25278  n=n+1
25279  if type(v)=="table" then
25280   s[n]=k..">"..tabstr_normal(v)
25281  elseif v==true then
25282   s[n]=k.."+" 
25283  elseif v then
25284   s[n]=k.."="..v
25285  else
25286   s[n]=k.."-" 
25287  end
25288 end
25289 if n==0 then
25290  return ""
25291 elseif n==1 then
25292  return s[1]
25293 else
25294  sort(s) 
25295  return concat(s,",")
25296 end
25297end
25298local function tabstr_flat(t)
25299 local s={}
25300 local n=0
25301 for k,v in next,t do
25302  n=n+1
25303  s[n]=k.."="..v
25304 end
25305 if n==0 then
25306  return ""
25307 elseif n==1 then
25308  return s[1]
25309 else
25310  sort(s) 
25311  return concat(s,",")
25312 end
25313end
25314local function tabstr_mixed(t) 
25315 local n=#t
25316 if n==0 then
25317  return ""
25318 elseif n==1 then
25319  local k=t[1]
25320  if k==true then
25321   return "++" 
25322  elseif k==false then
25323   return "--" 
25324  else
25325   return tostring(k) 
25326  end
25327 else
25328  local s={}
25329  for i=1,n do
25330   local k=t[i]
25331   if k==true then
25332    s[i]="++" 
25333   elseif k==false then
25334    s[i]="--" 
25335   else
25336    s[i]=k 
25337   end
25338  end
25339  return concat(s,",")
25340 end
25341end
25342local function tabstr_boolean(t)
25343 local s={}
25344 local n=0
25345 for k,v in next,t do
25346  n=n+1
25347  if v then
25348   s[n]=k.."+"
25349  else
25350   s[n]=k.."-"
25351  end
25352 end
25353 if n==0 then
25354  return ""
25355 elseif n==1 then
25356  return s[1]
25357 else
25358  sort(s) 
25359  return concat(s,",")
25360 end
25361end
25362function readers.pack(data)
25363 if data then
25364  local h,t,c={},{},{}
25365  local hh,tt,cc={},{},{}
25366  local nt,ntt=0,0
25367  local function pack_normal(v)
25368   local tag=tabstr_normal(v)
25369   local ht=h[tag]
25370   if ht then
25371    c[ht]=c[ht]+1
25372    return ht
25373   else
25374    nt=nt+1
25375    t[nt]=v
25376    h[tag]=nt
25377    c[nt]=1
25378    return nt
25379   end
25380  end
25381  local function pack_normal_cc(v)
25382   local tag=tabstr_normal(v)
25383   local ht=h[tag]
25384   if ht then
25385    c[ht]=c[ht]+1
25386    return ht
25387   else
25388    v[1]=0
25389    nt=nt+1
25390    t[nt]=v
25391    h[tag]=nt
25392    c[nt]=1
25393    return nt
25394   end
25395  end
25396  local function pack_flat(v)
25397   local tag=tabstr_flat(v)
25398   local ht=h[tag]
25399   if ht then
25400    c[ht]=c[ht]+1
25401    return ht
25402   else
25403    nt=nt+1
25404    t[nt]=v
25405    h[tag]=nt
25406    c[nt]=1
25407    return nt
25408   end
25409  end
25410  local function pack_indexed(v)
25411   local tag=concat(v," ")
25412   local ht=h[tag]
25413   if ht then
25414    c[ht]=c[ht]+1
25415    return ht
25416   else
25417    nt=nt+1
25418    t[nt]=v
25419    h[tag]=nt
25420    c[nt]=1
25421    return nt
25422   end
25423  end
25424  local function pack_mixed(v)
25425   local tag=tabstr_mixed(v)
25426   local ht=h[tag]
25427   if ht then
25428    c[ht]=c[ht]+1
25429    return ht
25430   else
25431    nt=nt+1
25432    t[nt]=v
25433    h[tag]=nt
25434    c[nt]=1
25435    return nt
25436   end
25437  end
25438  local function pack_boolean(v)
25439   local tag=tabstr_boolean(v)
25440   local ht=h[tag]
25441   if ht then
25442    c[ht]=c[ht]+1
25443    return ht
25444   else
25445    nt=nt+1
25446    t[nt]=v
25447    h[tag]=nt
25448    c[nt]=1
25449    return nt
25450   end
25451  end
25452  local function pack_final(v)
25453   if c[v]<=criterium then
25454    return t[v]
25455   else
25456    local hv=hh[v]
25457    if hv then
25458     return hv
25459    else
25460     ntt=ntt+1
25461     tt[ntt]=t[v]
25462     hh[v]=ntt
25463     cc[ntt]=c[v]
25464     return ntt
25465    end
25466   end
25467  end
25468  local function pack_final_cc(v)
25469   if c[v]<=criterium then
25470    return t[v]
25471   else
25472    local hv=hh[v]
25473    if hv then
25474     return hv
25475    else
25476     ntt=ntt+1
25477     tt[ntt]=t[v]
25478     hh[v]=ntt
25479     cc[ntt]=c[v]
25480     return ntt
25481    end
25482   end
25483  end
25484  local function success(stage,pass)
25485   if nt==0 then
25486    if trace_loading or trace_packing then
25487     report_otf("pack quality: nothing to pack")
25488    end
25489    return false
25490   elseif nt>=threshold then
25491    local one=0
25492    local two=0
25493    local rest=0
25494    if pass==1 then
25495     for k,v in next,c do
25496      if v==1 then
25497       one=one+1
25498      elseif v==2 then
25499       two=two+1
25500      else
25501       rest=rest+1
25502      end
25503     end
25504    else
25505     for k,v in next,cc do
25506      if v>20 then
25507       rest=rest+1
25508      elseif v>10 then
25509       two=two+1
25510      else
25511       one=one+1
25512      end
25513     end
25514     data.tables=tt
25515    end
25516    if trace_loading or trace_packing then
25517     report_otf("pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)",
25518      stage,pass,one+two+rest,one,two,rest,criterium)
25519    end
25520    return true
25521   else
25522    if trace_loading or trace_packing then
25523     report_otf("pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)",
25524      stage,pass,nt,threshold)
25525    end
25526    return false
25527   end
25528  end
25529  local function packers(pass)
25530   if pass==1 then
25531    return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc
25532   else
25533    return pack_final,pack_final,pack_final,pack_final,pack_final,pack_final_cc
25534   end
25535  end
25536  local resources=data.resources
25537  local sequences=resources.sequences
25538  local sublookups=resources.sublookups
25539  local features=resources.features
25540  local palettes=resources.colorpalettes
25541  local variable=resources.variabledata
25542  local chardata=characters and characters.data
25543  local descriptions=data.descriptions or data.glyphs
25544  if not descriptions then
25545   return
25546  end
25547  for pass=1,2 do
25548   if trace_packing then
25549    report_otf("start packing: stage 1, pass %s",pass)
25550   end
25551   local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc=packers(pass)
25552   for unicode,description in next,descriptions do
25553    local boundingbox=description.boundingbox
25554    if boundingbox then
25555     description.boundingbox=pack_indexed(boundingbox)
25556    end
25557    local math=description.math
25558    if math then
25559     local kerns=math.kerns
25560     if kerns then
25561      for tag,kern in next,kerns do
25562       kerns[tag]=pack_normal(kern)
25563      end
25564     end
25565    end
25566   end
25567   local function packthem(sequences)
25568    for i=1,#sequences do
25569     local sequence=sequences[i]
25570     local kind=sequence.type
25571     local steps=sequence.steps
25572     local order=sequence.order
25573     local features=sequence.features
25574     local flags=sequence.flags
25575     if steps then
25576      for i=1,#steps do
25577       local step=steps[i]
25578       if kind=="gpos_pair" then
25579        local c=step.coverage
25580        if c then
25581         if step.format~="pair" then
25582          for g1,d1 in next,c do
25583           c[g1]=pack_normal(d1)
25584          end
25585         elseif step.shared then
25586          local shared={}
25587          for g1,d1 in next,c do
25588           for g2,d2 in next,d1 do
25589            if not shared[d2] then
25590             local f=d2[1] if f and f~=true then d2[1]=pack_indexed(f) end
25591             local s=d2[2] if s and s~=true then d2[2]=pack_indexed(s) end
25592             shared[d2]=true
25593            end
25594           end
25595          end
25596          if pass==2 then
25597           step.shared=nil 
25598          end
25599         else
25600          for g1,d1 in next,c do
25601           for g2,d2 in next,d1 do
25602            local f=d2[1] if f and f~=true then d2[1]=pack_indexed(f) end
25603            local s=d2[2] if s and s~=true then d2[2]=pack_indexed(s) end
25604           end
25605          end
25606         end
25607        end
25608       elseif kind=="gpos_single" then
25609        local c=step.coverage
25610        if c then
25611         if step.format=="single" then
25612          for g1,d1 in next,c do
25613           if d1 and d1~=true then
25614            c[g1]=pack_indexed(d1)
25615           end
25616          end
25617         else
25618          step.coverage=pack_normal(c)
25619         end
25620        end
25621       elseif kind=="gpos_cursive" then
25622        local c=step.coverage
25623        if c then
25624         for g1,d1 in next,c do
25625          local f=d1[2] if f then d1[2]=pack_indexed(f) end
25626          local s=d1[3] if s then d1[3]=pack_indexed(s) end
25627         end
25628        end
25629       elseif kind=="gpos_mark2base" or kind=="gpos_mark2mark" then
25630        local c=step.baseclasses
25631        if c then
25632         for g1,d1 in next,c do
25633          for g2,d2 in next,d1 do
25634           d1[g2]=pack_indexed(d2)
25635          end
25636         end
25637        end
25638        local c=step.coverage
25639        if c then
25640         for g1,d1 in next,c do
25641          d1[2]=pack_indexed(d1[2])
25642         end
25643        end
25644       elseif kind=="gpos_mark2ligature" then
25645        local c=step.baseclasses
25646        if c then
25647         for g1,d1 in next,c do
25648          for g2,d2 in next,d1 do
25649           for g3,d3 in next,d2 do
25650            d2[g3]=pack_indexed(d3)
25651           end
25652          end
25653         end
25654        end
25655        local c=step.coverage
25656        if c then
25657         for g1,d1 in next,c do
25658          d1[2]=pack_indexed(d1[2])
25659         end
25660        end
25661       end
25662       local rules=step.rules
25663       if rules then
25664        for i=1,#rules do
25665         local rule=rules[i]
25666         local r=rule.before    if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
25667         local r=rule.after  if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
25668         local r=rule.current   if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
25669         local r=rule.replacements if r then rule.replacements=pack_flat   (r) end
25670        end
25671       end
25672      end
25673     end
25674     if order then
25675      sequence.order=pack_indexed(order)
25676     end
25677     if features then
25678      for script,feature in next,features do
25679       features[script]=pack_normal(feature)
25680      end
25681     end
25682     if flags then
25683      sequence.flags=pack_normal(flags)
25684     end
25685      end
25686   end
25687   if sequences then
25688    packthem(sequences)
25689   end
25690   if sublookups then
25691    packthem(sublookups)
25692   end
25693   if features then
25694    for k,list in next,features do
25695     for feature,spec in next,list do
25696      list[feature]=pack_normal(spec)
25697     end
25698    end
25699   end
25700   if palettes then
25701    for i=1,#palettes do
25702     local p=palettes[i]
25703     for j=1,#p do
25704      p[j]=pack_indexed(p[j])
25705     end
25706    end
25707   end
25708   if variable then
25709    local instances=variable.instances
25710    if instances then
25711     for i=1,#instances do
25712      local v=instances[i].values
25713      for j=1,#v do
25714       v[j]=pack_normal(v[j])
25715      end
25716     end
25717    end
25718    local function packdeltas(main)
25719     if main then
25720      local deltas=main.deltas
25721      if deltas then
25722       for i=1,#deltas do
25723        local di=deltas[i]
25724        local d=di.deltas
25725        for j=1,#d do
25726         d[j]=pack_indexed(d[j])
25727        end
25728        di.regions=pack_indexed(di.regions)
25729       end
25730      end
25731      local regions=main.regions
25732      if regions then
25733       for i=1,#regions do
25734        local r=regions[i]
25735        for j=1,#r do
25736         r[j]=pack_normal(r[j])
25737        end
25738       end
25739      end
25740     end
25741    end
25742    packdeltas(variable.global)
25743    packdeltas(variable.horizontal)
25744    packdeltas(variable.vertical)
25745    packdeltas(variable.metrics)
25746   end
25747   if not success(1,pass) then
25748    return
25749   end
25750  end
25751  if nt>0 then
25752   for pass=1,2 do
25753    if trace_packing then
25754     report_otf("start packing: stage 2, pass %s",pass)
25755    end
25756    local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc=packers(pass)
25757    for unicode,description in next,descriptions do
25758     local math=description.math
25759     if math then
25760      local kerns=math.kerns
25761      if kerns then
25762       math.kerns=pack_normal(kerns)
25763      end
25764     end
25765    end
25766    local function packthem(sequences)
25767     for i=1,#sequences do
25768      local sequence=sequences[i]
25769      local kind=sequence.type
25770      local steps=sequence.steps
25771      local features=sequence.features
25772      if steps then
25773       for i=1,#steps do
25774        local step=steps[i]
25775        if kind=="gpos_pair" then
25776         local c=step.coverage
25777         if c then
25778          if step.format=="pair" then
25779           for g1,d1 in next,c do
25780            for g2,d2 in next,d1 do
25781             d1[g2]=pack_normal(d2)
25782            end
25783           end
25784          end
25785         end
25786        elseif kind=="gpos_mark2ligature" then
25787         local c=step.baseclasses 
25788         if c then
25789          for g1,d1 in next,c do
25790           for g2,d2 in next,d1 do
25791            d1[g2]=pack_normal(d2)
25792           end
25793          end
25794         end
25795        end
25796        local rules=step.rules
25797        if rules then
25798         for i=1,#rules do
25799          local rule=rules[i]
25800          local r=rule.before  if r then rule.before=pack_normal(r) end
25801          local r=rule.after   if r then rule.after=pack_normal(r) end
25802          local r=rule.current if r then rule.current=pack_normal(r) end
25803         end
25804        end
25805       end
25806      end
25807      if features then
25808       sequence.features=pack_normal(features)
25809      end
25810       end
25811    end
25812    if sequences then
25813     packthem(sequences)
25814    end
25815    if sublookups then
25816     packthem(sublookups)
25817    end
25818    if variable then
25819     local function unpackdeltas(main)
25820      if main then
25821       local regions=main.regions
25822       if regions then
25823        main.regions=pack_normal(regions)
25824       end
25825      end
25826     end
25827     unpackdeltas(variable.global)
25828     unpackdeltas(variable.horizontal)
25829     unpackdeltas(variable.vertical)
25830     unpackdeltas(variable.metrics)
25831    end
25832   end
25833   for pass=1,2 do
25834    if trace_packing then
25835     report_otf("start packing: stage 3, pass %s",pass)
25836    end
25837    local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc=packers(pass)
25838    local function packthem(sequences)
25839     for i=1,#sequences do
25840      local sequence=sequences[i]
25841      local kind=sequence.type
25842      local steps=sequence.steps
25843      local features=sequence.features
25844      if steps then
25845       for i=1,#steps do
25846        local step=steps[i]
25847        if kind=="gpos_pair" then
25848         local c=step.coverage
25849         if c then
25850          if step.format=="pair" then
25851           for g1,d1 in next,c do
25852            c[g1]=pack_normal(d1)
25853           end
25854          end
25855         end
25856        elseif kind=="gpos_cursive" then
25857         local c=step.coverage
25858         if c then
25859          for g1,d1 in next,c do
25860           c[g1]=pack_normal_cc(d1)
25861          end
25862         end
25863        end
25864       end
25865      end
25866     end
25867    end
25868    if sequences then
25869     packthem(sequences)
25870    end
25871    if sublookups then
25872     packthem(sublookups)
25873    end
25874   end
25875  end
25876 end
25877end
25878local unpacked_mt={
25879 __index=function(t,k)
25880   t[k]=false
25881   return k 
25882  end
25883}
25884function readers.unpack(data)
25885 if data then
25886  local tables=data.tables
25887  if tables then
25888   local resources=data.resources
25889   local descriptions=data.descriptions or data.glyphs
25890   local sequences=resources.sequences
25891   local sublookups=resources.sublookups
25892   local features=resources.features
25893   local palettes=resources.colorpalettes
25894   local variable=resources.variabledata
25895   local unpacked={}
25896   setmetatable(unpacked,unpacked_mt)
25897   for unicode,description in next,descriptions do
25898    local tv=tables[description.boundingbox]
25899    if tv then
25900     description.boundingbox=tv
25901    end
25902    local math=description.math
25903    if math then
25904     local kerns=math.kerns
25905     if kerns then
25906      local tm=tables[kerns]
25907      if tm then
25908       math.kerns=tm
25909       kerns=unpacked[tm]
25910      end
25911      if kerns then
25912       for k,kern in next,kerns do
25913        local tv=tables[kern]
25914        if tv then
25915         kerns[k]=tv
25916        end
25917       end
25918      end
25919     end
25920    end
25921   end
25922   local function unpackthem(sequences)
25923    for i=1,#sequences do
25924     local sequence=sequences[i]
25925     local kind=sequence.type
25926     local steps=sequence.steps
25927     local order=sequence.order
25928     local features=sequence.features
25929     local flags=sequence.flags
25930     local markclass=sequence.markclass
25931     if features then
25932      local tv=tables[features]
25933      if tv then
25934       sequence.features=tv
25935       features=tv
25936      end
25937      for script,feature in next,features do
25938       local tv=tables[feature]
25939       if tv then
25940        features[script]=tv
25941       end
25942      end
25943     end
25944     if steps then
25945      for i=1,#steps do
25946       local step=steps[i]
25947       if kind=="gpos_pair" then
25948        local c=step.coverage
25949        if c then
25950         if step.format=="pair" then
25951          for g1,d1 in next,c do
25952           local tv=tables[d1]
25953           if tv then
25954            c[g1]=tv
25955            d1=tv
25956           end
25957           for g2,d2 in next,d1 do
25958            local tv=tables[d2]
25959            if tv then
25960             d1[g2]=tv
25961             d2=tv
25962            end
25963            local f=tables[d2[1]] if f then d2[1]=f end
25964            local s=tables[d2[2]] if s then d2[2]=s end
25965           end
25966          end
25967         else
25968          for g1,d1 in next,c do
25969           local tv=tables[d1]
25970           if tv then
25971            c[g1]=tv
25972           end
25973          end
25974         end
25975        end
25976       elseif kind=="gpos_single" then
25977        local c=step.coverage
25978        if c then
25979         if step.format=="single" then
25980          for g1,d1 in next,c do
25981           local tv=tables[d1]
25982           if tv then
25983            c[g1]=tv
25984           end
25985          end
25986         else
25987          local tv=tables[c]
25988          if tv then
25989           step.coverage=tv
25990          end
25991         end
25992        end
25993       elseif kind=="gpos_cursive" then
25994        local c=step.coverage
25995        if c then
25996         for g1,d1 in next,c do
25997          local tv=tables[d1]
25998          if tv then
25999           d1=tv
26000           c[g1]=d1
26001          end
26002          local f=tables[d1[2]] if f then d1[2]=f end
26003          local s=tables[d1[3]] if s then d1[3]=s end
26004         end
26005        end
26006       elseif kind=="gpos_mark2base" or kind=="gpos_mark2mark" then
26007        local c=step.baseclasses
26008        if c then
26009         for g1,d1 in next,c do
26010          for g2,d2 in next,d1 do
26011           local tv=tables[d2]
26012           if tv then
26013            d1[g2]=tv
26014           end
26015          end
26016         end
26017        end
26018        local c=step.coverage
26019        if c then
26020         for g1,d1 in next,c do
26021          local tv=tables[d1[2]]
26022          if tv then
26023           d1[2]=tv
26024          end
26025         end
26026        end
26027       elseif kind=="gpos_mark2ligature" then
26028        local c=step.baseclasses
26029        if c then
26030         for g1,d1 in next,c do
26031          for g2,d2 in next,d1 do
26032           local tv=tables[d2] 
26033           if tv then
26034            d2=tv
26035            d1[g2]=d2
26036           end
26037           for g3,d3 in next,d2 do
26038            local tv=tables[d2[g3]]
26039            if tv then
26040             d2[g3]=tv
26041            end
26042           end
26043          end
26044         end
26045        end
26046        local c=step.coverage
26047        if c then
26048         for g1,d1 in next,c do
26049          local tv=tables[d1[2]]
26050          if tv then
26051           d1[2]=tv
26052          end
26053         end
26054        end
26055       end
26056       local rules=step.rules
26057       if rules then
26058        for i=1,#rules do
26059         local rule=rules[i]
26060         local before=rule.before
26061         if before then
26062          local tv=tables[before]
26063          if tv then
26064           rule.before=tv
26065           before=tv
26066          end
26067          for i=1,#before do
26068           local tv=tables[before[i]]
26069           if tv then
26070            before[i]=tv
26071           end
26072          end
26073         end
26074         local after=rule.after
26075         if after then
26076          local tv=tables[after]
26077          if tv then
26078           rule.after=tv
26079           after=tv
26080          end
26081          for i=1,#after do
26082           local tv=tables[after[i]]
26083           if tv then
26084            after[i]=tv
26085           end
26086          end
26087         end
26088         local current=rule.current
26089         if current then
26090          local tv=tables[current]
26091          if tv then
26092           rule.current=tv
26093           current=tv
26094          end
26095          for i=1,#current do
26096           local tv=tables[current[i]]
26097           if tv then
26098            current[i]=tv
26099           end
26100          end
26101         end
26102         local replacements=rule.replacements
26103         if replacements then
26104          local tv=tables[replacements]
26105          if tv then
26106           rule.replacements=tv
26107          end
26108         end
26109        end
26110       end
26111      end
26112     end
26113     if order then
26114      local tv=tables[order]
26115      if tv then
26116       sequence.order=tv
26117      end
26118     end
26119     if flags then
26120      local tv=tables[flags]
26121      if tv then
26122       sequence.flags=tv
26123      end
26124     end
26125      end
26126   end
26127   if sequences then
26128    unpackthem(sequences)
26129   end
26130   if sublookups then
26131    unpackthem(sublookups)
26132   end
26133   if features then
26134    for k,list in next,features do
26135     for feature,spec in next,list do
26136      local tv=tables[spec]
26137      if tv then
26138       list[feature]=tv
26139      end
26140     end
26141    end
26142   end
26143   if palettes then
26144    for i=1,#palettes do
26145     local p=palettes[i]
26146     for j=1,#p do
26147      local tv=tables[p[j]]
26148      if tv then
26149       p[j]=tv
26150      end
26151     end
26152    end
26153   end
26154   if variable then
26155    local instances=variable.instances
26156    if instances then
26157     for i=1,#instances do
26158      local v=instances[i].values
26159      for j=1,#v do
26160       local tv=tables[v[j]]
26161       if tv then
26162        v[j]=tv
26163       end
26164      end
26165     end
26166    end
26167    local function unpackdeltas(main)
26168     if main then
26169      local deltas=main.deltas
26170      if deltas then
26171       for i=1,#deltas do
26172        local di=deltas[i]
26173        local d=di.deltas
26174        local r=di.regions
26175        for j=1,#d do
26176         local tv=tables[d[j]]
26177         if tv then
26178          d[j]=tv
26179         end
26180        end
26181        local tv=di.regions
26182        if tv then
26183         di.regions=tv
26184        end
26185       end
26186      end
26187      local regions=main.regions
26188      if regions then
26189       local tv=tables[regions]
26190       if tv then
26191        main.regions=tv
26192        regions=tv
26193       end
26194       for i=1,#regions do
26195        local r=regions[i]
26196        for j=1,#r do
26197         local tv=tables[r[j]]
26198         if tv then
26199          r[j]=tv
26200         end
26201        end
26202       end
26203      end
26204     end
26205    end
26206    unpackdeltas(variable.global)
26207    unpackdeltas(variable.horizontal)
26208    unpackdeltas(variable.vertical)
26209    unpackdeltas(variable.metrics)
26210   end
26211   data.tables=nil
26212  end
26213 end
26214end
26215local mt={
26216 __index=function(t,k) 
26217  if k=="height" then
26218   local ht=t.boundingbox[4]
26219   return ht<0 and 0 or ht
26220  elseif k=="depth" then
26221   local dp=-t.boundingbox[2]
26222   return dp<0 and 0 or dp
26223  elseif k=="width" then
26224   return 0
26225  elseif k=="name" then 
26226   return forcenotdef and ".notdef"
26227  end
26228 end
26229}
26230local function sameformat(sequence,steps,first,nofsteps,kind)
26231 return true
26232end
26233local function mergesteps_1(lookup,strict)
26234 local steps=lookup.steps
26235 local nofsteps=lookup.nofsteps
26236 local first=steps[1]
26237 if strict then
26238  local f=first.format
26239  for i=2,nofsteps do
26240   if steps[i].format~=f then
26241    if trace_optimizations then
26242     report_optimizations("not merging %a steps of %a lookup %a, different formats",nofsteps,lookup.type,lookup.name)
26243    end
26244    return 0
26245   end
26246  end
26247 end
26248 if trace_optimizations then
26249  report_optimizations("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
26250 end
26251 local target=first.coverage
26252 for i=2,nofsteps do
26253  local c=steps[i].coverage
26254  if c then
26255   for k,v in next,c do
26256    if not target[k] then
26257     target[k]=v
26258    end
26259   end
26260  end
26261 end
26262 lookup.nofsteps=1
26263 lookup.merged=true
26264 lookup.steps={ first }
26265 return nofsteps-1
26266end
26267local function mergesteps_2(lookup)
26268 local steps=lookup.steps
26269 local nofsteps=lookup.nofsteps
26270 local first=steps[1]
26271 if strict then
26272  local f=first.format
26273  for i=2,nofsteps do
26274   if steps[i].format~=f then
26275    if trace_optimizations then
26276     report_optimizations("not merging %a steps of %a lookup %a, different formats",nofsteps,lookup.type,lookup.name)
26277    end
26278    return 0
26279   end
26280  end
26281 end
26282 if trace_optimizations then
26283  report_optimizations("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
26284 end
26285 local target=first.coverage
26286 for i=2,nofsteps do
26287  local c=steps[i].coverage
26288  if c then
26289   for k,v in next,c do
26290    local tk=target[k]
26291    if tk then
26292     for kk,vv in next,v do
26293      if tk[kk]==nil then
26294       tk[kk]=vv
26295      end
26296     end
26297    else
26298     target[k]=v
26299    end
26300   end
26301  end
26302 end
26303 lookup.nofsteps=1
26304 lookup.merged=true
26305 lookup.steps={ first }
26306 return nofsteps-1
26307end
26308local function mergesteps_3(lookup,strict) 
26309 local steps=lookup.steps
26310 local nofsteps=lookup.nofsteps
26311 if trace_optimizations then
26312  report_optimizations("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
26313 end
26314 local coverage={}
26315 for i=1,nofsteps do
26316  local c=steps[i].coverage
26317  if c then
26318   for k,v in next,c do
26319    local tk=coverage[k] 
26320    if tk then
26321     if trace_optimizations then
26322      report_optimizations("quitting merge due to multiple checks")
26323     end
26324     return nofsteps
26325    else
26326     coverage[k]=v
26327    end
26328   end
26329  end
26330 end
26331 local first=steps[1]
26332 local baseclasses={} 
26333 for i=1,nofsteps do
26334  local offset=i*10  
26335  local step=steps[i]
26336  for k,v in sortedhash(step.baseclasses) do
26337   baseclasses[offset+k]=v
26338  end
26339  for k,v in next,step.coverage do
26340   v[1]=offset+v[1]
26341  end
26342 end
26343 first.baseclasses=baseclasses
26344 first.coverage=coverage
26345 lookup.nofsteps=1
26346 lookup.merged=true
26347 lookup.steps={ first }
26348 return nofsteps-1
26349end
26350local function nested(old,new)
26351 for k,v in next,old do
26352  if k=="ligature" then
26353   if not new.ligature then
26354    new.ligature=v
26355   end
26356  else
26357   local n=new[k]
26358   if n then
26359    nested(v,n)
26360   else
26361    new[k]=v
26362   end
26363  end
26364 end
26365end
26366local function mergesteps_4(lookup) 
26367 local steps=lookup.steps
26368 local nofsteps=lookup.nofsteps
26369 local first=steps[1]
26370 if trace_optimizations then
26371  report_optimizations("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
26372 end
26373 local target=first.coverage
26374 for i=2,nofsteps do
26375  local c=steps[i].coverage
26376  if c then
26377   for k,v in next,c do
26378    local tk=target[k]
26379    if tk then
26380     nested(v,tk)
26381    else
26382     target[k]=v
26383    end
26384   end
26385  end
26386 end
26387 lookup.nofsteps=1
26388 lookup.steps={ first }
26389 return nofsteps-1
26390end
26391local function mergesteps_5(lookup) 
26392 local steps=lookup.steps
26393 local nofsteps=lookup.nofsteps
26394 local first=steps[1]
26395 if trace_optimizations then
26396  report_optimizations("merging %a steps of %a lookup %a",nofsteps,lookup.type,lookup.name)
26397 end
26398 local target=first.coverage
26399 local hash=nil
26400 for k,v in next,target do
26401  hash=v[1]
26402  break
26403 end
26404 for i=2,nofsteps do
26405  local c=steps[i].coverage
26406  if c then
26407   for k,v in next,c do
26408    local tk=target[k]
26409    if tk then
26410     if not tk[2] then
26411      tk[2]=v[2]
26412     end
26413     if not tk[3] then
26414      tk[3]=v[3]
26415     end
26416    else
26417     target[k]=v
26418     v[1]=hash
26419    end
26420   end
26421  end
26422 end
26423 lookup.nofsteps=1
26424 lookup.merged=true
26425 lookup.steps={ first }
26426 return nofsteps-1
26427end
26428local function checkkerns(lookup)
26429 local steps=lookup.steps
26430 local nofsteps=lookup.nofsteps
26431 local kerned=0
26432 for i=1,nofsteps do
26433  local step=steps[i]
26434  if step.format=="pair" then
26435   local coverage=step.coverage
26436   local kerns=true
26437   for g1,d1 in next,coverage do
26438    if d1==true then
26439    elseif not d1 then
26440    elseif d1[1]~=0 or d1[2]~=0 or d1[4]~=0 then
26441     kerns=false
26442     break
26443    end
26444   end
26445   if kerns then
26446    if trace_optimizations then
26447     report_optimizations("turning pairs of step %a of %a lookup %a into kerns",i,lookup.type,lookup.name)
26448    end
26449    local c={}
26450    for g1,d1 in next,coverage do
26451     if d1 and d1~=true then
26452      c[g1]=d1[3]
26453     end
26454    end
26455    step.coverage=c
26456    step.format="move"
26457    kerned=kerned+1
26458   end
26459  end
26460 end
26461 return kerned
26462end
26463local strip_pairs=true
26464local compact_pairs=true
26465local compact_singles=true
26466local merge_pairs=true
26467local merge_singles=true
26468local merge_substitutions=true
26469local merge_alternates=true
26470local merge_multiples=true
26471local merge_ligatures=true
26472local merge_cursives=true
26473local merge_marks=true
26474directives.register("otf.strip.pairs",function(v) strip_pairs=v end)
26475directives.register("otf.compact.pairs",function(v) compact_pairs=v end)
26476directives.register("otf.compact.singles",function(v) compact_singles=v end)
26477directives.register("otf.merge.pairs",function(v) merge_pairs=v end)
26478directives.register("otf.merge.singles",function(v) merge_singles=v end)
26479directives.register("otf.merge.substitutions",function(v) merge_substitutions=v end)
26480directives.register("otf.merge.alternates",function(v) merge_alternates=v end)
26481directives.register("otf.merge.multiples",function(v) merge_multiples=v end)
26482directives.register("otf.merge.ligatures",function(v) merge_ligatures=v end)
26483directives.register("otf.merge.cursives",function(v) merge_cursives=v end)
26484directives.register("otf.merge.marks",function(v) merge_marks=v end)
26485local function checkpairs(lookup)
26486 local steps=lookup.steps
26487 local nofsteps=lookup.nofsteps
26488 local kerned=0
26489 local function onlykerns(step)
26490  local coverage=step.coverage
26491  for g1,d1 in next,coverage do
26492   for g2,d2 in next,d1 do
26493    if d2[2] then
26494     return false
26495    else
26496     local v=d2[1]
26497     if v==true then
26498     elseif v and (v[1]~=0 or v[2]~=0 or v[4]~=0) then
26499      return false
26500     end
26501    end
26502   end
26503  end
26504  return coverage
26505 end
26506 for i=1,nofsteps do
26507  local step=steps[i]
26508  if step.format=="pair" then
26509   local coverage=onlykerns(step)
26510   if coverage then
26511    if trace_optimizations then
26512     report_optimizations("turning pairs of step %a of %a lookup %a into kerns",i,lookup.type,lookup.name)
26513    end
26514    for g1,d1 in next,coverage do
26515     local d={}
26516     for g2,d2 in next,d1 do
26517      local v=d2[1]
26518      if v==true then
26519      elseif v then
26520       d[g2]=v[3] 
26521      end
26522     end
26523     coverage[g1]=d
26524    end
26525    step.format="move"
26526    kerned=kerned+1
26527   end
26528  end
26529 end
26530 return kerned
26531end
26532local function strippairs(lookup)
26533 local steps=lookup.steps
26534 local nofsteps=lookup.nofsteps
26535 local stripped=0
26536 for i=1,nofsteps do
26537  local step=steps[i]
26538  if step.format=="pair" then
26539   local coverage=step.coverage
26540   for g1,d1 in next,coverage do
26541    for g2,d2 in next,d1 do
26542     if d2[2] then
26543     elseif d2[1]==true then
26544      d1[g2]=nil
26545      stripped=stripped+1
26546     end
26547    end
26548   end
26549  end
26550 end
26551 return stripped
26552end
26553function readers.compact(data)
26554 if not data or data.compacted then
26555  return
26556 else
26557  data.compacted=true
26558 end
26559 local resources=data.resources
26560 local stripped=0
26561 local merged=0
26562 local kerned=0
26563 local allsteps=0
26564 local function compact(what)
26565  local lookups=resources[what]
26566  if lookups then
26567   for i=1,#lookups do
26568    local lookup=lookups[i]
26569    local nofsteps=lookup.nofsteps
26570    local kind=lookup.type
26571    allsteps=allsteps+nofsteps
26572    if nofsteps>1 then
26573     local merg=merged
26574     if kind=="gsub_single" then
26575      if merge_substitutions then
26576       merged=merged+mergesteps_1(lookup)
26577      end
26578     elseif kind=="gsub_alternate" then
26579      if merge_alternates then
26580       merged=merged+mergesteps_1(lookup)
26581      end
26582     elseif kind=="gsub_multiple" then
26583      if merge_multiples then
26584       merged=merged+mergesteps_1(lookup)
26585      end
26586     elseif kind=="gsub_ligature" then
26587      if merge_ligatures then
26588       merged=merged+mergesteps_4(lookup)
26589      end
26590     elseif kind=="gpos_single" then
26591      if merge_singles then
26592       merged=merged+mergesteps_1(lookup,true)
26593      end
26594      if compact_singles then
26595       kerned=kerned+checkkerns(lookup)
26596      end
26597     elseif kind=="gpos_pair" then
26598      if strip_pairs then
26599       stripped=stripped+strippairs(lookup) 
26600      end
26601      if merge_pairs then
26602       merged=merged+mergesteps_2(lookup)
26603      end
26604      if compact_pairs then
26605       kerned=kerned+checkpairs(lookup)
26606      end
26607     elseif kind=="gpos_cursive" then
26608      if merge_cursives then
26609       merged=merged+mergesteps_5(lookup)
26610      end
26611     elseif kind=="gpos_mark2mark" or kind=="gpos_mark2base" or kind=="gpos_mark2ligature" then
26612      if merge_marks then
26613       merged=merged+mergesteps_3(lookup)
26614      end
26615     end
26616     if merg~=merged then
26617      lookup.merged=true
26618     end
26619    elseif nofsteps==1 then
26620     local kern=kerned
26621     if kind=="gpos_single" then
26622      if compact_singles then
26623       kerned=kerned+checkkerns(lookup)
26624      end
26625     elseif kind=="gpos_pair" then
26626      if compact_pairs then
26627       kerned=kerned+checkpairs(lookup)
26628      end
26629     end
26630     if kern~=kerned then
26631     end
26632    end
26633   end
26634  elseif trace_optimizations then
26635   report_optimizations("no lookups in %a",what)
26636  end
26637 end
26638 compact("sequences")
26639 compact("sublookups")
26640 if trace_optimizations then
26641  if stripped>0 then
26642   report_optimizations("%i zero positions stripped before merging",stripped)
26643  end
26644  if merged>0 then
26645   report_optimizations("%i steps of %i removed due to merging",merged,allsteps)
26646  end
26647  if kerned>0 then
26648   report_optimizations("%i steps of %i steps turned from pairs into kerns",kerned,allsteps)
26649  end
26650 end
26651end
26652if CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 then
26653 local done=0
26654 local function condense_1(k,v,t)
26655  if type(v)=="table" then
26656   local u=false
26657   local l=false
26658   for k,v in next,v do
26659    if k=="ligature" then
26660     l=v
26661     if u then
26662      break
26663     end
26664    elseif u then
26665     break
26666    else
26667     u=true
26668    end
26669   end
26670   if l and not u then
26671    t[k]=l
26672    done=done+1
26673   end
26674   if u then
26675    for k,vv in next,v do
26676     if k~="ligature" then
26677      condense_1(k,vv,v)
26678     end
26679    end
26680   end
26681  end
26682 end
26683 local function condensesteps_1(lookup)
26684  done=0
26685  if lookup.type=="gsub_ligature" then
26686   local steps=lookup.steps
26687   if steps then
26688    for i=1,#steps do
26689     local step=steps[i]
26690     local coverage=step.coverage
26691     if coverage then
26692      for k,v in next,coverage do
26693       if condense_1(k,v,coverage) then
26694        coverage[k]=v.ligature
26695        done=done+1
26696       end
26697      end
26698     end
26699    end
26700   end
26701  end
26702  return done
26703 end
26704 function readers.condense(data)
26705  if not data or data.condensed then
26706   return
26707  else
26708   data.condensed=true
26709  end
26710  local resources=data.resources
26711  local condensed=0
26712  local function condense(what)
26713   local lookups=resources[what]
26714   if lookups then
26715    for i=1,#lookups do
26716     condensed=condensed+condensesteps_1(lookups[i])
26717    end
26718   elseif trace_optimizations then
26719    report_optimizations("no lookups in %a",what)
26720   end
26721  end
26722  condense("sequences")
26723  condense("sublookups")
26724  if trace_optimizations then
26725   if condensed>0 then
26726    report_optimizations("%i ligatures condensed",condensed)
26727   end
26728  end
26729 end
26730end
26731local function mergesteps(t,k)
26732 if k=="merged" then
26733  local merged={}
26734  for i=1,#t do
26735   local step=t[i]
26736   local coverage=step.coverage
26737   for k in next,coverage do
26738    local m=merged[k]
26739    if m then
26740     m[2]=i
26741    else
26742     merged[k]={ i,i }
26743    end
26744   end
26745  end
26746  t.merged=merged
26747  return merged
26748 end
26749end
26750local function checkmerge(sequence)
26751 local steps=sequence.steps
26752 if steps then
26753  setmetatableindex(steps,mergesteps)
26754 end
26755end
26756local function checkflags(sequence,resources)
26757 if not sequence.skiphash then
26758  local flags=sequence.flags
26759  if flags then
26760   local skipmark=flags[1]
26761   local skipligature=flags[2]
26762   local skipbase=flags[3]
26763   local markclass=sequence.markclass
26764   local skipsome=skipmark or skipligature or skipbase or markclass or false
26765   if skipsome then
26766    sequence.skiphash=setmetatableindex(function(t,k)
26767     local c=resources.classes[k] 
26768     local v=c==skipmark
26769         or (markclass and c=="mark" and not markclass[k])
26770         or c==skipligature
26771         or c==skipbase
26772         or false
26773     t[k]=v
26774     return v
26775    end)
26776   else
26777    sequence.skiphash=false
26778   end
26779  else
26780   sequence.skiphash=false
26781  end
26782 end
26783end
26784local function checksteps(sequence)
26785 local steps=sequence.steps
26786 if steps then
26787  for i=1,#steps do
26788   steps[i].index=i
26789  end
26790 end
26791end
26792if fonts.helpers then
26793 fonts.helpers.checkmerge=checkmerge
26794 fonts.helpers.checkflags=checkflags
26795 fonts.helpers.checksteps=checksteps 
26796end
26797function readers.expand(data)
26798 if not data or data.expanded then
26799  return
26800 else
26801  data.expanded=true
26802 end
26803 local resources=data.resources
26804 local sublookups=resources.sublookups
26805 local sequences=resources.sequences 
26806 local markclasses=resources.markclasses
26807 local descriptions=data.descriptions
26808 if descriptions then
26809  local defaultwidth=resources.defaultwidth  or 0
26810  local defaultheight=resources.defaultheight or 0
26811  local defaultdepth=resources.defaultdepth  or 0
26812  local basename=trace_markwidth and file.basename(resources.filename)
26813  for u,d in next,descriptions do
26814   local bb=d.boundingbox
26815   local wd=d.width
26816   if d.class=="mark" then
26817    if trace_markwidth and wd~=0 then
26818     report_markwidth("mark %a with width %b found in %a",d.name or "<noname>",wd,basename)
26819    end
26820    d.width=0
26821   elseif not wd then
26822    d.width=defaultwidth
26823   end
26824   if bb then
26825    local ht=bb[4]
26826    local dp=-bb[2]
26827    if ht==0 or ht<0 then
26828    else
26829     d.height=ht
26830    end
26831    if dp==0 or dp<0 then
26832    else
26833     d.depth=dp
26834    end
26835   end
26836  end
26837 end
26838 local function expandlookups(sequences,whatever)
26839  if sequences then
26840   for i=1,#sequences do
26841    local sequence=sequences[i]
26842    local steps=sequence.steps
26843    if steps then
26844     local nofsteps=sequence.nofsteps
26845     local kind=sequence.type
26846     local markclass=sequence.markclass
26847     if markclass then
26848      if not markclasses then
26849       report_warning("missing markclasses")
26850       sequence.markclass=false
26851      else
26852       sequence.markclass=markclasses[markclass]
26853      end
26854     end
26855     for i=1,nofsteps do
26856      local step=steps[i]
26857      local baseclasses=step.baseclasses
26858      if baseclasses then
26859       local coverage=step.coverage
26860       for k,v in next,coverage do
26861        v[1]=baseclasses[v[1]] 
26862       end
26863      elseif kind=="gpos_cursive" then
26864       local coverage=step.coverage
26865       for k,v in next,coverage do
26866        v[1]=coverage 
26867       end
26868      end
26869      local rules=step.rules
26870      if rules then
26871       local rulehash={ n=0 } 
26872       local rulesize=0
26873       local coverage={}
26874       local lookuptype=sequence.type
26875       local nofrules=#rules
26876       step.coverage=coverage 
26877       for currentrule=1,nofrules do
26878        local rule=rules[currentrule]
26879        local current=rule.current
26880        local before=rule.before
26881        local after=rule.after
26882        local replacements=rule.replacements or false
26883        local sequence={}
26884        local nofsequences=0
26885        if before then
26886         for n=1,#before do
26887          nofsequences=nofsequences+1
26888          sequence[nofsequences]=before[n]
26889         end
26890        end
26891        local start=nofsequences+1
26892        for n=1,#current do
26893         nofsequences=nofsequences+1
26894         sequence[nofsequences]=current[n]
26895        end
26896        local stop=nofsequences
26897        if after then
26898         for n=1,#after do
26899          nofsequences=nofsequences+1
26900          sequence[nofsequences]=after[n]
26901         end
26902        end
26903        local lookups=rule.lookups or false
26904        local subtype=nil
26905        if lookups then
26906         for i=1,#lookups do
26907          local lookups=lookups[i]
26908          if lookups then
26909           for k,v in next,lookups do 
26910            local lookup=sublookups[v]
26911if not lookup and whatever then
26912 lookup=whatever[v]
26913end
26914            if lookup then
26915             lookups[k]=lookup
26916             if not subtype then
26917              subtype=lookup.type
26918             end
26919            else
26920            end
26921           end
26922          end
26923         end
26924        end
26925        if sequence[1] then 
26926         sequence.n=#sequence 
26927         local ruledata={
26928          currentrule,
26929          lookuptype,
26930          sequence,
26931          start,
26932          stop,
26933          lookups,
26934          replacements,
26935          subtype,
26936         }
26937         rulesize=rulesize+1
26938         rulehash[rulesize]=ruledata
26939         rulehash.n=rulesize
26940         if true then 
26941          for unic in next,sequence[start] do
26942           local cu=coverage[unic]
26943           if cu then
26944            local n=#cu+1
26945            cu[n]=ruledata
26946            cu.n=n
26947           else
26948            coverage[unic]={ ruledata,n=1 }
26949           end
26950          end
26951         else
26952          for unic in next,sequence[start] do
26953           local cu=coverage[unic]
26954           if cu then
26955           else
26956            coverage[unic]=rulehash
26957           end
26958          end
26959         end
26960        end
26961       end
26962      end
26963     end
26964     checkmerge(sequence)
26965     checkflags(sequence,resources)
26966     checksteps(sequence)
26967    end
26968   end
26969  end
26970 end
26971 expandlookups(sequences)
26972 expandlookups(sublookups,sequences)
26973end
26974
26975end -- closure
26976
26977do -- begin closure to overcome local limits and interference
26978
26979if not modules then modules={} end modules ['font-ota']={
26980 version=1.001,
26981 comment="companion to font-ini.mkiv",
26982 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
26983 copyright="PRAGMA ADE / ConTeXt Development Team",
26984 license="see context related readme files"
26985}
26986local type=type
26987local setmetatableindex=table.setmetatableindex
26988if not trackers then trackers={ register=function() end } end
26989local fonts,nodes,node=fonts,nodes,node
26990local allocate=utilities.storage.allocate
26991local otf=fonts.handlers.otf
26992local analyzers=fonts.analyzers
26993local initializers=allocate()
26994local methods=allocate()
26995analyzers.initializers=initializers
26996analyzers.methods=methods
26997local nuts=nodes.nuts
26998local tonut=nuts.tonut
26999local getnext=nuts.getnext
27000local getprev=nuts.getprev
27001local getprev=nuts.getprev
27002local getprop=nuts.getprop
27003local setprop=nuts.setprop
27004local getsubtype=nuts.getsubtype
27005local getchar=nuts.getchar
27006local ischar=nuts.ischar
27007local endofmath=nuts.endofmath
27008local nodecodes=nodes.nodecodes
27009local disc_code=nodecodes.disc
27010local math_code=nodecodes.math
27011local fontdata=fonts.hashes.identifiers
27012local categories=characters and characters.categories or {} 
27013local chardata=characters and characters.data
27014local otffeatures=fonts.constructors.features.otf
27015local registerotffeature=otffeatures.register
27016local setstate=nuts.setstate
27017local getstate=nuts.getstate
27018if not setstate or not getstate then
27019 setstate=function(n,v)
27020  setprop(n,"state",v)
27021 end
27022 getstate=function(n,v)
27023  local s=getprop(n,"state")
27024  if v then
27025   return s==v
27026  else
27027   return s
27028  end
27029 end
27030 nuts.setstate=setstate
27031 nuts.getstate=getstate
27032end
27033local s_init=1 local s_rphf=7
27034local s_medi=2 local s_half=8
27035local s_fina=3 local s_pref=9
27036local s_isol=4 local s_blwf=10
27037local s_mark=5 local s_pstf=11
27038local s_rest=6
27039local states=allocate {
27040 init=s_init,
27041 medi=s_medi,
27042 med2=s_medi,
27043 fina=s_fina,
27044 fin2=s_fina,
27045 fin3=s_fina,
27046 isol=s_isol,
27047 mark=s_mark,
27048 rest=s_rest,
27049 rphf=s_rphf,
27050 half=s_half,
27051 pref=s_pref,
27052 blwf=s_blwf,
27053 pstf=s_pstf,
27054}
27055local features=allocate {
27056 init=s_init,
27057 medi=s_medi,
27058 med2=s_medi,
27059 fina=s_fina,
27060 fin2=s_fina,
27061 fin3=s_fina,
27062 isol=s_isol,
27063 rphf=s_rphf,
27064 half=s_half,
27065 pref=s_pref,
27066 blwf=s_blwf,
27067 pstf=s_pstf,
27068}
27069analyzers.states=states
27070analyzers.features=features
27071analyzers.useunicodemarks=false
27072function analyzers.setstate(head,font)
27073 local useunicodemarks=analyzers.useunicodemarks
27074 local tfmdata=fontdata[font]
27075 local descriptions=tfmdata.descriptions
27076 local first,last,current,n,done=nil,nil,head,0,false 
27077 current=tonut(current)
27078 while current do
27079  local char,id=ischar(current,font)
27080  if char and not getstate(current) then
27081   done=true
27082   local d=descriptions[char]
27083   if d then
27084    if d.class=="mark" then
27085     done=true
27086     setstate(current,s_mark)
27087    elseif useunicodemarks and categories[char]=="mn" then
27088     done=true
27089     setstate(current,s_mark)
27090    elseif n==0 then
27091     first,last,n=current,current,1
27092     setstate(current,s_init)
27093    else
27094     last,n=current,n+1
27095     setstate(current,s_medi)
27096    end
27097   else 
27098    if first and first==last then
27099     setstate(last,s_isol)
27100    elseif last then
27101     setstate(last,s_fina)
27102    end
27103    first,last,n=nil,nil,0
27104   end
27105  elseif char==false then
27106   if first and first==last then
27107    setstate(last,s_isol)
27108   elseif last then
27109    setstate(last,s_fina)
27110   end
27111   first,last,n=nil,nil,0
27112   if id==math_code then
27113    current=endofmath(current)
27114   end
27115  elseif id==disc_code then
27116   setstate(current,s_medi)
27117   last=current
27118  else 
27119   if first and first==last then
27120    setstate(last,s_isol)
27121   elseif last then
27122    setstate(last,s_fina)
27123   end
27124   first,last,n=nil,nil,0
27125   if id==math_code then
27126    current=endofmath(current)
27127   end
27128  end
27129  current=getnext(current)
27130 end
27131 if first and first==last then
27132  setstate(last,s_isol)
27133 elseif last then
27134  setstate(last,s_fina)
27135 end
27136 return head,done
27137end
27138local function analyzeinitializer(tfmdata,value) 
27139 local script,language=otf.scriptandlanguage(tfmdata) 
27140 local action=initializers[script]
27141 if not action then
27142 elseif type(action)=="function" then
27143  return action(tfmdata,value)
27144 else
27145  local action=action[language]
27146  if action then
27147   return action(tfmdata,value)
27148  end
27149 end
27150end
27151local function analyzeprocessor(head,font,attr)
27152 local tfmdata=fontdata[font]
27153 local script,language=otf.scriptandlanguage(tfmdata,attr)
27154 local action=methods[script]
27155 if not action then
27156 elseif type(action)=="function" then
27157  return action(head,font,attr)
27158 else
27159  action=action[language]
27160  if action then
27161   return action(head,font,attr)
27162  end
27163 end
27164 return head,false
27165end
27166registerotffeature {
27167 name="analyze",
27168 description="analysis of character classes",
27169 default=true,
27170 initializers={
27171  node=analyzeinitializer,
27172 },
27173 processors={
27174  position=1,
27175  node=analyzeprocessor,
27176 }
27177}
27178methods.latn=analyzers.setstate
27179local arab_warned={}
27180local function warning(current,what)
27181 local char=getchar(current)
27182 if not arab_warned[char] then
27183  log.report("analyze","arab: character %C has no %a class",char,what)
27184  arab_warned[char]=true
27185 end
27186end
27187local mappers=allocate {
27188 l=s_init,
27189 d=s_medi,
27190 c=s_medi,
27191 r=s_fina,
27192 u=s_isol,
27193}
27194local classifiers=characters.classifiers
27195if not classifiers then
27196 local f_arabic,l_arabic=characters.blockrange("arabic")
27197 local f_syriac,l_syriac=characters.blockrange("syriac")
27198 local f_mandiac,l_mandiac=characters.blockrange("mandiac")
27199 local f_nko,l_nko=characters.blockrange("nko")
27200 local f_ext_a,l_ext_a=characters.blockrange("arabicextendeda")
27201 classifiers=setmetatableindex(function(t,k)
27202  if type(k)=="number" then
27203   local c=chardata[k]
27204   local v=false
27205   if c then
27206    local arabic=c.arabic
27207    if arabic then
27208     v=mappers[arabic]
27209     if not v then
27210      log.report("analyze","error in mapping arabic %C",k)
27211      v=false
27212     end
27213    elseif (k>=f_arabic  and k<=l_arabic)  or
27214        (k>=f_syriac  and k<=l_syriac)  or
27215        (k>=f_mandiac and k<=l_mandiac) or
27216        (k>=f_nko  and k<=l_nko)  or
27217        (k>=f_ext_a   and k<=l_ext_a)   then
27218     if categories[k]=="mn" then
27219      v=s_mark
27220     else
27221      v=s_rest
27222     end
27223    end
27224   end
27225   t[k]=v
27226   return v
27227  end
27228 end)
27229 characters.classifiers=classifiers
27230end
27231function methods.arab(head,font,attr)
27232 local first,last,c_first,c_last
27233 local current=head
27234 local done=false
27235 current=tonut(current)
27236 while current do
27237  local char,id=ischar(current,font)
27238  if char and not getstate(current) then
27239   done=true
27240   local classifier=classifiers[char]
27241   if not classifier then
27242    if last then
27243     if c_last==s_medi or c_last==s_fina then
27244      setstate(last,s_fina)
27245     else
27246      warning(last,"fina")
27247      setstate(last,s_error)
27248     end
27249     first,last=nil,nil
27250    elseif first then
27251     if c_first==s_medi or c_first==s_fina then
27252      setstate(first,s_isol)
27253     else
27254      warning(first,"isol")
27255      setstate(first,s_error)
27256     end
27257     first=nil
27258    end
27259   elseif classifier==s_mark then
27260    setstate(current,s_mark)
27261   elseif classifier==s_isol then
27262    if last then
27263     if c_last==s_medi or c_last==s_fina then
27264      setstate(last,s_fina)
27265     else
27266      warning(last,"fina")
27267      setstate(last,s_error)
27268     end
27269     first,last=nil,nil
27270    elseif first then
27271     if c_first==s_medi or c_first==s_fina then
27272      setstate(first,s_isol)
27273     else
27274      warning(first,"isol")
27275      setstate(first,s_error)
27276     end
27277     first=nil
27278    end
27279    setstate(current,s_isol)
27280   elseif classifier==s_medi then
27281    if first then
27282     last=current
27283     c_last=classifier
27284     setstate(current,s_medi)
27285    else
27286     setstate(current,s_init)
27287     first=current
27288     c_first=classifier
27289    end
27290   elseif classifier==s_fina then
27291    if last then
27292     if getstate(last)~=s_init then
27293      setstate(last,s_medi)
27294     end
27295     setstate(current,s_fina)
27296     first,last=nil,nil
27297    elseif first then
27298     setstate(current,s_fina)
27299     first=nil
27300    else
27301     setstate(current,s_isol)
27302    end
27303   else 
27304    setstate(current,s_rest)
27305    if last then
27306     if c_last==s_medi or c_last==s_fina then
27307      setstate(last,s_fina)
27308     else
27309      warning(last,"fina")
27310      setstate(last,s_error)
27311     end
27312     first,last=nil,nil
27313    elseif first then
27314     if c_first==s_medi or c_first==s_fina then
27315      setstate(first,s_isol)
27316     else
27317      warning(first,"isol")
27318      setstate(first,s_error)
27319     end
27320     first=nil
27321    end
27322   end
27323  else
27324   if last then
27325    if c_last==s_medi or c_last==s_fina then
27326     setstate(last,s_fina)
27327    else
27328     warning(last,"fina")
27329     setstate(last,s_error)
27330    end
27331    first,last=nil,nil
27332   elseif first then
27333    if c_first==s_medi or c_first==s_fina then
27334     setstate(first,s_isol)
27335    else
27336     warning(first,"isol")
27337     setstate(first,s_error)
27338    end
27339    first=nil
27340   end
27341   if id==math_code then 
27342    current=endofmath(current)
27343   end
27344  end
27345  current=getnext(current)
27346 end
27347 if last then
27348  if c_last==s_medi or c_last==s_fina then
27349   setstate(last,s_fina)
27350  else
27351   warning(last,"fina")
27352   setstate(last,s_error)
27353  end
27354 elseif first then
27355  if c_first==s_medi or c_first==s_fina then
27356   setstate(first,s_isol)
27357  else
27358   warning(first,"isol")
27359   setstate(first,s_error)
27360  end
27361 end
27362 return head,done
27363end
27364methods.syrc=methods.arab
27365methods.mand=methods.arab
27366methods.nko=methods.arab
27367do
27368 local joining=setmetatableindex(function(t,k)
27369  if type(k)=="number" then
27370   local c=chardata[k]
27371   local v=false
27372   if c then
27373    local mongolian=c.mongolian
27374    v=mongolian
27375   end
27376   t[k]=v
27377   return v
27378  end
27379 end)
27380 function methods.mong(head,font,attr)
27381  local first,last
27382  local current=head
27383  local done=false
27384  local prevjoin=nil
27385  local prestate=nil
27386  current=tonut(current)
27387  local function wrapup()
27388   if last then
27389    if last~=first then
27390     local s=getstate(last)
27391     if s==s_medi then
27392      setstate(last,s_fina)
27393     elseif s==s_init then
27394      setstate(last,s_isol)
27395     end
27396    end
27397    last=nil
27398    first=nil
27399    prevjoin=nil
27400    prestate=nil
27401   end
27402  end
27403  while current do
27404   local char,id=ischar(current,font)
27405   if char and not getstate(current) then
27406    local currjoin=joining[char]
27407    done=true
27408    if not last then
27409     setstate(current,s_isol)
27410     prevjoin=currjoin
27411     first=current
27412     last=current
27413     prevstate=s_isol
27414    elseif currjoin=="t" then
27415     last=current
27416    elseif prevjoin=="d" or prevjoin=="jc" or prevjoin=="l" then
27417     if currjoin=="d" or prevjoin=="jc" or prevjoin=="r" then
27418      local s=getstate(last)
27419      if s==s_isol then
27420       setstate(last,s_init)
27421      elseif s==s_fina then
27422       setstate(last,s_medi)
27423      end
27424      setstate(current,s_fina)
27425      prevstate=s_fina
27426     elseif prevjoin=="nj" or prevjoin=="l" then
27427      local s=getstate(last)
27428      if s==s_medi then
27429       setstate(last,s_fina)
27430      elseif s==s_init then
27431       setstate(last,s_isol)
27432      end
27433      setstate(current,s_isol)
27434      prevstate=s_isol
27435     end
27436     prevjoin=currjoin
27437     last=current
27438    elseif prevjoin=="nj" or prevjoin=="r" then
27439     if s==s_medi then
27440      setstate(last,s_fina)
27441     elseif s==s_init then
27442      setstate(last,s_isol)
27443     end
27444     setstate(current,s_isol)
27445     prevjoin=currjoin
27446     prevstate=s_isol
27447     last=current
27448    elseif last then
27449     wrapup()
27450    end
27451   else
27452    if last then
27453     wrapup()
27454    end
27455    if id==math_code then 
27456     current=endofmath(current)
27457    end
27458   end
27459   current=getnext(current)
27460  end
27461  if last then
27462   wrapup()
27463  end
27464  return head,done
27465 end
27466end
27467directives.register("otf.analyze.useunicodemarks",function(v)
27468 analyzers.useunicodemarks=v
27469end)
27470
27471end -- closure
27472
27473do -- begin closure to overcome local limits and interference
27474
27475if not modules then modules={} end modules ['font-ots']={ 
27476 version=1.001,
27477 optimize=true,
27478 comment="companion to font-ini.mkiv",
27479 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
27480 copyright="PRAGMA ADE / ConTeXt Development Team",
27481 license="see context related readme files",
27482}
27483local type,next,tonumber=type,next,tonumber
27484local random=math.random
27485local formatters=string.formatters
27486local insert=table.insert
27487local registertracker=trackers.register
27488local logs=logs
27489local trackers=trackers
27490local nodes=nodes
27491local attributes=attributes
27492local fonts=fonts
27493local otf=fonts.handlers.otf
27494local tracers=nodes.tracers
27495local trace_singles=false  registertracker("otf.singles",function(v) trace_singles=v end)
27496local trace_multiples=false  registertracker("otf.multiples",function(v) trace_multiples=v end)
27497local trace_alternatives=false  registertracker("otf.alternatives",function(v) trace_alternatives=v end)
27498local trace_ligatures=false  registertracker("otf.ligatures",function(v) trace_ligatures=v end)
27499local trace_contexts=false  registertracker("otf.contexts",function(v) trace_contexts=v end)
27500local trace_marks=false  registertracker("otf.marks",function(v) trace_marks=v end)
27501local trace_kerns=false  registertracker("otf.kerns",function(v) trace_kerns=v end)
27502local trace_cursive=false  registertracker("otf.cursive",function(v) trace_cursive=v end)
27503local trace_preparing=false  registertracker("otf.preparing",function(v) trace_preparing=v end)
27504local trace_bugs=false  registertracker("otf.bugs",function(v) trace_bugs=v end)
27505local trace_details=false  registertracker("otf.details",function(v) trace_details=v end)
27506local trace_steps=false  registertracker("otf.steps",function(v) trace_steps=v end)
27507local trace_skips=false  registertracker("otf.skips",function(v) trace_skips=v end)
27508local trace_plugins=false  registertracker("otf.plugins",function(v) trace_plugins=v end)
27509local trace_chains=false  registertracker("otf.chains",function(v) trace_chains=v end)
27510local trace_kernruns=false  registertracker("otf.kernruns",function(v) trace_kernruns=v end)
27511local trace_compruns=false  registertracker("otf.compruns",function(v) trace_compruns=v end)
27512local trace_testruns=false  registertracker("otf.testruns",function(v) trace_testruns=v end)
27513local forcediscretionaries=false
27514local forcepairadvance=false 
27515local repeatablemultiples=context or false
27516directives.register("otf.forcediscretionaries",function(v) forcediscretionaries=v end)
27517directives.register("otf.forcepairadvance",function(v) forcepairadvance=v end)
27518local report_direct=logs.reporter("fonts","otf direct")
27519local report_subchain=logs.reporter("fonts","otf subchain")
27520local report_chain=logs.reporter("fonts","otf chain")
27521local report_process=logs.reporter("fonts","otf process")
27522local report_warning=logs.reporter("fonts","otf warning")
27523local report_run=logs.reporter("fonts","otf run")
27524registertracker("otf.substitutions","otf.singles","otf.multiples","otf.alternatives","otf.ligatures")
27525registertracker("otf.positions","otf.marks","otf.kerns","otf.cursive")
27526registertracker("otf.actions","otf.substitutions","otf.positions")
27527registertracker("otf.sample","otf.steps","otf.substitutions","otf.positions","otf.analyzing")
27528registertracker("otf.sample.silent","otf.steps=silent","otf.substitutions","otf.positions","otf.analyzing")
27529local nuts=nodes.nuts
27530local getnext=nuts.getnext
27531local setnext=nuts.setnext
27532local getprev=nuts.getprev
27533local setprev=nuts.setprev
27534local getboth=nuts.getboth
27535local setboth=nuts.setboth
27536local getid=nuts.getid
27537local getstate=nuts.getstate
27538local getsubtype=nuts.getsubtype
27539local getchar=nuts.getchar
27540local setchar=nuts.setchar
27541local getdisc=nuts.getdisc
27542local setdisc=nuts.setdisc
27543local getreplace=nuts.getreplace
27544local setlink=nuts.setlink
27545local getwidth=nuts.getwidth
27546local getattr=nuts.getattr
27547local getglyphdata=nuts.getglyphdata
27548local components=nuts.components
27549local copynocomponents=components.copynocomponents
27550local copyonlyglyphs=components.copyonlyglyphs
27551local countcomponents=components.count
27552local setcomponents=components.set
27553local getcomponents=components.get
27554local flushcomponents=components.flush
27555local ischar=nuts.ischar
27556local usesfont=nuts.usesfont
27557local insertnodeafter=nuts.insertafter
27558local copynode=nuts.copy
27559local copynodelist=nuts.copylist
27560local removenode=nuts.remove
27561local findnodetail=nuts.tail
27562local flushnodelist=nuts.flushlist
27563local flushnode=nuts.flushnode
27564local endofmath=nuts.endofmath
27565local startofpar=nuts.startofpar
27566local setmetatable=setmetatable
27567local setmetatableindex=table.setmetatableindex
27568local nextnode=nuts.traversers.node
27569local nodecodes=nodes.nodecodes
27570local glyphcodes=nodes.glyphcodes
27571local glyph_code=nodecodes.glyph
27572local glue_code=nodecodes.glue
27573local disc_code=nodecodes.disc
27574local math_code=nodecodes.math
27575local dir_code=nodecodes.dir
27576local par_code=nodecodes.par
27577local lefttoright_code=nodes.dirvalues.lefttoright
27578local righttoleft_code=nodes.dirvalues.righttoleft
27579local discretionarydisc_code=nodes.disccodes.discretionary
27580local a_noligature=attributes.private("noligature")
27581local injections=nodes.injections
27582local setmark=injections.setmark
27583local setcursive=injections.setcursive
27584local setkern=injections.setkern
27585local setmove=injections.setmove
27586local setposition=injections.setposition
27587local resetinjection=injections.reset
27588local copyinjection=injections.copy
27589local setligaindex=injections.setligaindex
27590local getligaindex=injections.getligaindex
27591local fontdata=fonts.hashes.identifiers
27592local fontfeatures=fonts.hashes.features
27593local otffeatures=fonts.constructors.features.otf
27594local registerotffeature=otffeatures.register
27595local onetimemessage=fonts.loggers.onetimemessage or function() end
27596local getrandom=utilities and utilities.randomizer and utilities.randomizer.get
27597otf.defaultnodealternate="none"
27598local tfmdata=false
27599local characters=false
27600local descriptions=false
27601local marks=false
27602local classes=false
27603local currentfont=false
27604local factor=0
27605local threshold=0
27606local checkmarks=false
27607local discs=false
27608local spaces=false
27609local sweepnode=nil
27610local sweephead={} 
27611local notmatchpre={} 
27612local notmatchpost={} 
27613local notmatchreplace={} 
27614local handlers={}
27615local isspace=injections.isspace
27616local getthreshold=injections.getthreshold
27617local checkstep=(tracers and tracers.steppers.check) or function() end
27618local registerstep=(tracers and tracers.steppers.register) or function() end
27619local registermessage=(tracers and tracers.steppers.message)  or function() end
27620local function logprocess(...)
27621 if trace_steps then
27622  registermessage(...)
27623  if trace_steps=="silent" then
27624   return
27625  end
27626 end
27627 report_direct(...)
27628end
27629local function logwarning(...)
27630 report_direct(...)
27631end
27632local gref  do
27633 local f_unicode=formatters["U+%X"]   
27634 local f_uniname=formatters["U+%X (%s)"] 
27635 local f_unilist=formatters["% t"]
27636 gref=function(n) 
27637  if type(n)=="number" then
27638   local description=descriptions[n]
27639   local name=description and description.name
27640   if name then
27641    return f_uniname(n,name)
27642   else
27643    return f_unicode(n)
27644   end
27645  elseif n then
27646   local t={}
27647   for i=1,#n do
27648    local ni=n[i]
27649    if tonumber(ni) then 
27650     local di=descriptions[ni]
27651     local nn=di and di.name
27652     if nn then
27653      t[#t+1]=f_uniname(ni,nn)
27654     else
27655      t[#t+1]=f_unicode(ni)
27656     end
27657    end
27658   end
27659   return f_unilist(t)
27660  else
27661   return "<error in node mode tracing>"
27662  end
27663 end
27664end
27665local function cref(dataset,sequence,index)
27666 if not dataset then
27667  return "no valid dataset"
27668 end
27669 local merged=sequence.merged and "merged " or ""
27670 if index and index>1 then
27671  return formatters["feature %a, type %a, %schain lookup %a, index %a"](
27672   dataset[4],sequence.type,merged,sequence.name,index)
27673 else
27674  return formatters["feature %a, type %a, %schain lookup %a"](
27675   dataset[4],sequence.type,merged,sequence.name)
27676 end
27677end
27678local function pref(dataset,sequence)
27679 return formatters["feature %a, type %a, %slookup %a"](
27680  dataset[4],sequence.type,sequence.merged and "merged " or "",sequence.name)
27681end
27682local function mref(rlmode)
27683 if not rlmode or rlmode>=0 then
27684  return "l2r"
27685 else
27686  return "r2l"
27687 end
27688end
27689local function flattendisk(head,disc)
27690 local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true)
27691 local prev,next=getboth(disc)
27692 local ishead=head==disc
27693 setdisc(disc)
27694 flushnode(disc)
27695 if pre then
27696  flushnodelist(pre)
27697 end
27698 if post then
27699  flushnodelist(post)
27700 end
27701 if ishead then
27702  if replace then
27703   if next then
27704    setlink(replacetail,next)
27705   end
27706   return replace,replace
27707  elseif next then
27708   return next,next
27709  else
27710  end
27711 else
27712  if replace then
27713   if next then
27714    setlink(replacetail,next)
27715   end
27716   setlink(prev,replace)
27717   return head,replace
27718  else
27719   setlink(prev,next) 
27720   return head,next
27721  end
27722 end
27723end
27724local function appenddisc(disc,list)
27725 local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true)
27726 local posthead=list
27727 local replacehead=copynodelist(list)
27728 if post then
27729  setlink(posttail,posthead)
27730 else
27731  post=posthead
27732 end
27733 if replace then
27734  setlink(replacetail,replacehead)
27735 else
27736  replace=replacehead
27737 end
27738 setdisc(disc,pre,post,replace)
27739end
27740local function markstoligature(head,start,stop,char)
27741 if start==stop and getchar(start)==char then
27742  return head,start
27743 else
27744  local prev=getprev(start)
27745  local next=getnext(stop)
27746  setprev(start)
27747  setnext(stop)
27748  local base=copynocomponents(start,copyinjection)
27749  if head==start then
27750   head=base
27751  end
27752  resetinjection(base)
27753  setchar(base,char)
27754  setcomponents(base,start)
27755  setlink(prev,base,next)
27756  flushcomponents(start)
27757  return head,base
27758 end
27759end
27760local no_left_ligature_code=1
27761local no_right_ligature_code=2
27762local no_left_kern_code=4
27763local no_right_kern_code=8
27764local hasglyphoption=function(n,c)
27765 if c==no_left_ligature_code or c==no_right_ligature_code then
27766  return getattr(n,a_noligature)==1
27767 else
27768  return false
27769 end
27770end
27771local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfound,hasmarks) 
27772 if hasglyphoption(start,no_right_ligature_code) then
27773  return head,start
27774 end
27775 if start==stop and getchar(start)==char then
27776  resetinjection(start)
27777  setchar(start,char)
27778  return head,start
27779 end
27780 local prev=getprev(start)
27781 local next=getnext(stop)
27782 local comp=start
27783 setprev(start)
27784 setnext(stop)
27785 local base=copynocomponents(start,copyinjection)
27786 if start==head then
27787  head=base
27788 end
27789 resetinjection(base)
27790 setchar(base,char)
27791 setcomponents(base,comp)
27792 setlink(prev,base,next)
27793 if not discfound then
27794  local deletemarks=not skiphash or hasmarks
27795  local components=start 
27796  local baseindex=0
27797  local componentindex=0
27798  local head=base
27799  local current=base
27800  while start do
27801   local char=getchar(start)
27802   if not marks[char] then
27803    baseindex=baseindex+componentindex
27804    componentindex=countcomponents(start,marks)
27805    elseif not deletemarks or (skiphash and skiphash[char]) then
27806    setligaindex(start,baseindex+getligaindex(start,componentindex))
27807    if trace_marks then
27808     logwarning("%s: keep ligature mark %s, gets index %s",pref(dataset,sequence),gref(char),getligaindex(start))
27809    end
27810    local n=copynode(start)
27811    copyinjection(n,start) 
27812    head,current=insertnodeafter(head,current,n) 
27813   elseif trace_marks then
27814    logwarning("%s: delete ligature mark %s",pref(dataset,sequence),gref(char))
27815   end
27816   start=getnext(start)
27817  end
27818  local start=getnext(current)
27819  while start do
27820   local char=ischar(start)
27821   if char then
27822    if marks[char] then
27823     setligaindex(start,baseindex+getligaindex(start,componentindex))
27824     if trace_marks then
27825      logwarning("%s: set ligature mark %s, gets index %s",pref(dataset,sequence),gref(char),getligaindex(start))
27826     end
27827     start=getnext(start)
27828    else
27829     break
27830    end
27831   else
27832    break
27833   end
27834  end
27835  flushcomponents(components)
27836 else
27837  local discprev,discnext=getboth(discfound)
27838  if discprev and discnext then
27839   local pre,post,replace,pretail,posttail,replacetail=getdisc(discfound,true)
27840   if not replace then
27841    local prev=getprev(base)
27842    local copied=copyonlyglyphs(comp)
27843    if pre then
27844     setlink(discprev,pre)
27845    else
27846     setnext(discprev) 
27847    end
27848    pre=comp 
27849    if post then
27850     setlink(posttail,discnext)
27851     setprev(post) 
27852    else
27853     post=discnext
27854     setprev(discnext) 
27855    end
27856    setlink(prev,discfound,next)
27857    setboth(base)
27858    setcomponents(base,copied)
27859    replace=base
27860    if forcediscretionaries then
27861     setdisc(discfound,pre,post,replace,discretionarydisc_code)
27862    else
27863     setdisc(discfound,pre,post,replace)
27864    end
27865    base=prev
27866   end
27867  end
27868 end
27869 return head,base
27870end
27871local function multiple_glyphs(head,start,multiple,skiphash,what,stop) 
27872 local nofmultiples=#multiple
27873 if nofmultiples>0 then
27874  local first=start
27875  resetinjection(start)
27876  setchar(start,multiple[1])
27877  if nofmultiples>1 then
27878   for i=2,nofmultiples do
27879    local n=copynode(start) 
27880    resetinjection(n)
27881    setchar(n,multiple[i])
27882    insertnodeafter(head,start,n)
27883    start=n
27884   end
27885  end
27886  if what~=true and repeatablemultiples then
27887   local kind=type(what)
27888   local m,f,l
27889   if kind=="string" then
27890    local what,n=string.match(what,"^repeat(.-)[:=](%d+)$")
27891    if what=="middle" then
27892     m=tonumber(n)
27893    elseif what=="first" then
27894     f=tonumber(n)
27895    elseif what=="last" then
27896     l=tonumber(n)
27897    end
27898   elseif kind=="table" then
27899      m=what.middle
27900      f=what.first
27901      l=what.last
27902   end
27903   if f or m or l then
27904    if m and m>1 and nofmultiples==3 then
27905     local middle=getnext(first)
27906     for i=2,m do
27907      local n=copynode(middle) 
27908      resetinjection(n)
27909      insertnodeafter(head,first,n)
27910     end
27911    end
27912    if f and f>1 then
27913     for i=2,f do
27914      local n=copynode(first) 
27915      resetinjection(n)
27916      insertnodeafter(head,first,n)
27917     end
27918    end
27919    if l and l>1 then
27920     for i=2,l do
27921      local n=copynode(start) 
27922      resetinjection(n)
27923      insertnodeafter(head,start,n)
27924      start=n
27925     end
27926    end
27927   end
27928  end
27929  return head,start,true
27930 else
27931  if trace_multiples then
27932   logprocess("no multiple for %s",gref(getchar(start)))
27933  end
27934  return head,start,false
27935 end
27936end
27937local function get_alternative_glyph(start,alternatives,value)
27938 local n=#alternatives
27939 if n==1 then
27940  return alternatives[1],trace_alternatives and "1 (only one present)"
27941 elseif value=="random" then
27942  local r=getrandom and getrandom("glyph",1,n) or random(1,n)
27943  return alternatives[r],trace_alternatives and formatters["value %a, taking %a"](value,r)
27944 elseif value=="first" then
27945  return alternatives[1],trace_alternatives and formatters["value %a, taking %a"](value,1)
27946 elseif value=="last" then
27947  return alternatives[n],trace_alternatives and formatters["value %a, taking %a"](value,n)
27948 end
27949 value=value==true and 1 or tonumber(value)
27950 if type(value)~="number" then
27951  return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
27952 end
27953 if value>n then
27954  local defaultalt=otf.defaultnodealternate
27955  if defaultalt=="first" then
27956   return alternatives[n],trace_alternatives and formatters["invalid value %s, taking %a"](value,1)
27957  elseif defaultalt=="last" then
27958   return alternatives[1],trace_alternatives and formatters["invalid value %s, taking %a"](value,n)
27959  else
27960   return false,trace_alternatives and formatters["invalid value %a, %s"](value,"out of range")
27961  end
27962 elseif value==0 then
27963  return getchar(start),trace_alternatives and formatters["invalid value %a, %s"](value,"no change")
27964 elseif value<1 then
27965  return alternatives[1],trace_alternatives and formatters["invalid value %a, taking %a"](value,1)
27966 else
27967  return alternatives[value],trace_alternatives and formatters["value %a, taking %a"](value,value)
27968 end
27969end
27970function handlers.gsub_single(head,start,dataset,sequence,replacement)
27971 if trace_singles then
27972  logprocess("%s: replacing %s by single %s",pref(dataset,sequence),gref(getchar(start)),gref(replacement))
27973 end
27974 resetinjection(start)
27975 setchar(start,replacement)
27976 return head,start,true
27977end
27978function handlers.gsub_alternate(head,start,dataset,sequence,alternative)
27979 local kind=dataset[4]
27980 local what=dataset[1]
27981 local value=what==true and tfmdata.shared.features[kind] or what
27982 local choice,comment=get_alternative_glyph(start,alternative,value)
27983 if choice then
27984  if trace_alternatives then
27985   logprocess("%s: replacing %s by alternative %a to %s, %s",pref(dataset,sequence),gref(getchar(start)),gref(choice),comment)
27986  end
27987  resetinjection(start)
27988  setchar(start,choice)
27989 else
27990  if trace_alternatives then
27991   logwarning("%s: no variant %a for %s, %s",pref(dataset,sequence),value,gref(getchar(start)),comment)
27992  end
27993 end
27994 return head,start,true
27995end
27996function handlers.gsub_multiple(head,start,dataset,sequence,multiple,rlmode,skiphash)
27997 if trace_multiples then
27998  logprocess("%s: replacing %s by multiple %s",pref(dataset,sequence),gref(getchar(start)),gref(multiple))
27999 end
28000 return multiple_glyphs(head,start,multiple,skiphash,dataset[1])
28001end
28002function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skiphash)
28003 local current=getnext(start)
28004 if not current then
28005  return head,start,false,nil
28006 end
28007 local stop=nil
28008 local startchar=getchar(start)
28009 if skiphash and skiphash[startchar] then
28010  while current do
28011   local char=ischar(current,currentfont)
28012   if char then
28013    local lg=not tonumber(ligature) and ligature[char]
28014    if lg then
28015     stop=current
28016     ligature=lg
28017     current=getnext(current)
28018    else
28019     break
28020    end
28021   else
28022    break
28023   end
28024  end
28025  if stop then
28026   local ligature=tonumber(ligature) or ligature.ligature
28027   if ligature then
28028    if trace_ligatures then
28029     local stopchar=getchar(stop)
28030     head,start=markstoligature(head,start,stop,ligature)
28031     logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(getchar(start)))
28032    else
28033     head,start=markstoligature(head,start,stop,ligature)
28034    end
28035    return head,start,true,false
28036   else
28037   end
28038  end
28039 else
28040  local discfound=false
28041  local hasmarks=marks[startchar]
28042  while current do
28043   local char,id=ischar(current,currentfont)
28044   if char then
28045    if skiphash and skiphash[char] then
28046     current=getnext(current)
28047    else
28048     local lg=not tonumber(ligature) and ligature[char]
28049     if lg then
28050      if marks[char] then
28051       hasmarks=true
28052      end
28053      stop=current 
28054      ligature=lg
28055      current=getnext(current)
28056     else
28057      break
28058     end
28059    end
28060   elseif char==false then
28061    break
28062   elseif id==disc_code then
28063    discfound=current
28064    break
28065   else
28066    break
28067   end
28068  end
28069  if discfound then
28070   local pre,post,replace=getdisc(discfound)
28071   local match
28072   if replace then
28073    local char=ischar(replace,currentfont)
28074    if char and (not tonumber(ligature) and ligature[char]) then
28075     match=true
28076    end
28077   end
28078   if not match and pre then
28079    local char=ischar(pre,currentfont)
28080    if char and (not tonumber(ligature) and ligature[char]) then
28081     match=true
28082    end
28083   end
28084   if not match and not pre or not replace then
28085    local n=getnext(discfound)
28086    local char=ischar(n,currentfont)
28087    if char and (not tonumber(ligature) and ligature[char]) then
28088     match=true
28089    end
28090   end
28091   if match then
28092    local ishead=head==start
28093    local prev=getprev(start)
28094    if stop then
28095     setnext(stop)
28096     local copy=copynodelist(start)
28097     local tail=stop 
28098     local liat=findnodetail(copy)
28099     if pre then
28100      setlink(liat,pre)
28101     end
28102     if replace then
28103      setlink(tail,replace)
28104     end
28105     pre=copy
28106     replace=start
28107    else
28108     setnext(start)
28109     local copy=copynode(start)
28110     if pre then
28111      setlink(copy,pre)
28112     end
28113     if replace then
28114      setlink(start,replace)
28115     end
28116     pre=copy
28117     replace=start
28118    end
28119    setdisc(discfound,pre,post,replace)
28120    if prev then
28121     setlink(prev,discfound)
28122    else
28123     setprev(discfound)
28124     head=discfound
28125    end
28126    start=discfound
28127    return head,start,true,true
28128   end
28129  end
28130  local ligature=tonumber(ligature) or ligature.ligature
28131  if ligature then
28132   if stop then
28133    if trace_ligatures then
28134     local stopchar=getchar(stop)
28135     head,start=toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks)
28136     logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(ligature))
28137    else
28138     head,start=toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks)
28139    end
28140   else
28141    resetinjection(start)
28142    setchar(start,ligature)
28143    if trace_ligatures then
28144     logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(ligature))
28145    end
28146   end
28147   return head,start,true,false
28148  else
28149  end
28150 end
28151 return head,start,false,false
28152end
28153function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection)
28154 if hasglyphoption(start,no_right_kern_code) then
28155  return head,start,false
28156 else
28157  local startchar=getchar(start)
28158  local format=step.format
28159  if format=="single" or type(kerns)=="table" then 
28160   local dx,dy,w,h=setposition(0,start,factor,rlmode,kerns,injection)
28161   if trace_kerns then
28162    logprocess("%s: shifting single %s by %s xy (%p,%p) and wh (%p,%p)",pref(dataset,sequence),gref(startchar),format,dx,dy,w,h)
28163   end
28164  else
28165   local k=(format=="move" and setmove or setkern)(start,factor,rlmode,kerns,injection)
28166   if trace_kerns then
28167    logprocess("%s: shifting single %s by %s %p",pref(dataset,sequence),gref(startchar),format,k)
28168   end
28169  end
28170  return head,start,true
28171 end
28172end
28173function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection)
28174 if hasglyphoption(start,no_right_kern_code) then
28175  return head,start,false
28176 else
28177  local snext=getnext(start)
28178  if not snext then
28179   return head,start,false
28180  else
28181   local prev=start
28182   while snext do
28183    local nextchar=ischar(snext,currentfont)
28184    if nextchar then
28185     if skiphash and skiphash[nextchar] then 
28186      prev=snext
28187      snext=getnext(snext)
28188     else
28189      local krn=kerns[nextchar]
28190      if not krn then
28191       break
28192      end
28193      local format=step.format
28194      if format=="pair" then
28195       local a=krn[1]
28196       local b=krn[2]
28197       if a==true then
28198       elseif a then 
28199        local x,y,w,h=setposition(1,start,factor,rlmode,a,injection)
28200        if trace_kerns then
28201         local startchar=getchar(start)
28202         logprocess("%s: shifting first of pair %s and %s by xy (%p,%p) and wh (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")
28203        end
28204       end
28205       if b==true then
28206        start=snext 
28207       elseif b then 
28208        local x,y,w,h=setposition(2,snext,factor,rlmode,b,injection)
28209        if trace_kerns then
28210         local startchar=getchar(start)
28211         logprocess("%s: shifting second of pair %s and %s by xy (%p,%p) and wh (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")
28212        end
28213        start=snext 
28214       elseif forcepairadvance then
28215        start=snext 
28216       end
28217       return head,start,true
28218      elseif krn~=0 then
28219       local k=(format=="move" and setmove or setkern)(snext,factor,rlmode,krn,injection)
28220       if trace_kerns then
28221        logprocess("%s: inserting %s %p between %s and %s as %s",pref(dataset,sequence),format,k,gref(getchar(prev)),gref(nextchar),injection or "injections")
28222       end
28223       return head,start,true
28224      else 
28225       break
28226      end
28227     end
28228    else
28229     break
28230    end
28231   end
28232   return head,start,false
28233  end
28234 end
28235end
28236function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode,skiphash)
28237 local markchar=getchar(start)
28238 if marks[markchar] then
28239  local base=getprev(start) 
28240  if base then
28241   local basechar=ischar(base,currentfont)
28242   if basechar then
28243    if marks[basechar] then
28244     while base do
28245      base=getprev(base)
28246      if base then
28247       basechar=ischar(base,currentfont)
28248       if basechar then
28249        if not marks[basechar] then
28250         break
28251        end
28252       else
28253        if trace_bugs then
28254         logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),1)
28255        end
28256        return head,start,false
28257       end
28258      else
28259       if trace_bugs then
28260        logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2)
28261       end
28262       return head,start,false
28263      end
28264     end
28265    end
28266    local ba=markanchors[1][basechar]
28267    if ba then
28268     local ma=markanchors[2]
28269     local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
28270     if trace_marks then
28271      logprocess("%s, bound %s, anchoring mark %s to basechar %s => (%p,%p)",
28272       pref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy)
28273     end
28274     return head,start,true
28275    elseif trace_bugs then
28276     logwarning("%s: mark %s is not anchored to %s",pref(dataset,sequence),gref(markchar),gref(basechar))
28277    end
28278   elseif trace_bugs then
28279    logwarning("%s: nothing preceding, case %i",pref(dataset,sequence),1)
28280   end
28281  elseif trace_bugs then
28282   logwarning("%s: nothing preceding, case %i",pref(dataset,sequence),2)
28283  end
28284 elseif trace_bugs then
28285  logwarning("%s: mark %s is no mark",pref(dataset,sequence),gref(markchar))
28286 end
28287 return head,start,false
28288end
28289function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlmode,skiphash)
28290 local markchar=getchar(start)
28291 if marks[markchar] then
28292  local base=getprev(start) 
28293  if base then
28294   local basechar=ischar(base,currentfont)
28295   if basechar then
28296    if marks[basechar] then
28297     while base do
28298      base=getprev(base)
28299      if base then
28300       basechar=ischar(base,currentfont)
28301       if basechar then
28302        if not marks[basechar] then
28303         break
28304        end
28305       else
28306        if trace_bugs then
28307         logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),1)
28308        end
28309        return head,start,false
28310       end
28311      else
28312       if trace_bugs then
28313        logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2)
28314       end
28315       return head,start,false
28316      end
28317     end
28318    end
28319    local ba=markanchors[1][basechar]
28320    if ba then
28321     local ma=markanchors[2]
28322     if ma then
28323      local index=getligaindex(start)
28324      ba=ba[index]
28325      if ba then
28326       local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
28327       if trace_marks then
28328        logprocess("%s, index %s, bound %s, anchoring mark %s to baselig %s at index %s => (%p,%p)",
28329         pref(dataset,sequence),index,bound,gref(markchar),gref(basechar),index,dx,dy)
28330       end
28331       return head,start,true
28332      else
28333       if trace_bugs then
28334        logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(dataset,sequence),gref(markchar),gref(basechar),index)
28335       end
28336      end
28337     end
28338    elseif trace_bugs then
28339     onetimemessage(currentfont,basechar,"no base anchors")
28340    end
28341   elseif trace_bugs then
28342    logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),1)
28343   end
28344  elseif trace_bugs then
28345   logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),2)
28346  end
28347 elseif trace_bugs then
28348  logwarning("%s: mark %s is no mark",pref(dataset,sequence),gref(markchar))
28349 end
28350 return head,start,false
28351end
28352function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode,skiphash)
28353 local markchar=getchar(start)
28354 if marks[markchar] then
28355  local base=getprev(start) 
28356  local slc=getligaindex(start)
28357  if slc then 
28358   while base do
28359    local blc=getligaindex(base)
28360    if blc and blc~=slc then
28361     base=getprev(base)
28362    else
28363     break
28364    end
28365   end
28366  end
28367  if base then
28368   local basechar=ischar(base,currentfont)
28369   if basechar then 
28370    local ba=markanchors[1][basechar] 
28371    if ba then
28372     local ma=markanchors[2]
28373     local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks)
28374     if trace_marks then
28375      logprocess("%s, bound %s, anchoring mark %s to basemark %s => (%p,%p)",
28376       pref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy)
28377     end
28378     return head,start,true
28379    end
28380   end
28381  end
28382 elseif trace_bugs then
28383  logwarning("%s: mark %s is no mark",pref(dataset,sequence),gref(markchar))
28384 end
28385 return head,start,false
28386end
28387function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,skiphash,step) 
28388 local startchar=getchar(start)
28389 if marks[startchar] then
28390  if trace_cursive then
28391   logprocess("%s: ignoring cursive for mark %s",pref(dataset,sequence),gref(startchar))
28392  end
28393 else
28394  local nxt=getnext(start)
28395  while nxt do
28396   local nextchar=ischar(nxt,currentfont)
28397   if not nextchar then
28398    break
28399   elseif marks[nextchar] then 
28400    nxt=getnext(nxt)
28401   else
28402    local exit=exitanchors[3]
28403    if exit then
28404     local entry=exitanchors[1][nextchar]
28405     if entry then
28406      entry=entry[2]
28407      if entry then
28408       local r2lflag=sequence.flags[4] 
28409       local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar],r2lflag)
28410       if trace_cursive then
28411        logprocess("%s: moving %s to %s cursive (%p,%p) using bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,bound,mref(rlmode))
28412       end
28413       return head,start,true
28414      end
28415     end
28416    end
28417    break
28418   end
28419  end
28420 end
28421 return head,start,false
28422end
28423local chainprocs={}
28424local function logprocess(...)
28425 if trace_steps then
28426  registermessage(...)
28427  if trace_steps=="silent" then
28428   return
28429  end
28430 end
28431 report_subchain(...)
28432end
28433local logwarning=report_subchain
28434local function logprocess(...)
28435 if trace_steps then
28436  registermessage(...)
28437  if trace_steps=="silent" then
28438   return
28439  end
28440 end
28441 report_chain(...)
28442end
28443local logwarning=report_chain
28444local function reversesub(head,start,stop,dataset,sequence,replacements,rlmode,skiphash)
28445 local char=getchar(start)
28446 local replacement=replacements[char]
28447 if replacement then
28448  if trace_singles then
28449   logprocess("%s: single reverse replacement of %s by %s",cref(dataset,sequence),gref(char),gref(replacement))
28450  end
28451  resetinjection(start)
28452  setchar(start,replacement)
28453  return head,start,true
28454 else
28455  return head,start,false
28456 end
28457end
28458chainprocs.reversesub=reversesub
28459local function reportzerosteps(dataset,sequence)
28460 logwarning("%s: no steps",cref(dataset,sequence))
28461end
28462local function reportmoresteps(dataset,sequence)
28463 logwarning("%s: more than 1 step",cref(dataset,sequence))
28464end
28465local function getmapping(dataset,sequence,currentlookup)
28466 local steps=currentlookup.steps
28467 local nofsteps=currentlookup.nofsteps
28468 if nofsteps==0 then
28469  reportzerosteps(dataset,sequence)
28470  currentlookup.mapping=false
28471  return false
28472 else
28473  if nofsteps>1 then
28474   reportmoresteps(dataset,sequence)
28475  end
28476  local mapping=steps[1].coverage
28477  currentlookup.mapping=mapping
28478  currentlookup.format=steps[1].format
28479  return mapping
28480 end
28481end
28482function chainprocs.gsub_remove(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
28483 if trace_chains then
28484  logprocess("%s: removing character %s",cref(dataset,sequence,chainindex),gref(getchar(start)))
28485 end
28486 head,start=removenode(head,start,true)
28487 return head,getprev(start),true
28488end
28489function chainprocs.gsub_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
28490 local mapping=currentlookup.mapping
28491 if mapping==nil then
28492  mapping=getmapping(dataset,sequence,currentlookup)
28493 end
28494 if mapping then
28495  local current=start
28496  while current do
28497   local currentchar=ischar(current)
28498   if currentchar then
28499    local replacement=mapping[currentchar]
28500    if not replacement or replacement=="" then
28501     if trace_bugs then
28502      logwarning("%s: no single for %s",cref(dataset,sequence,chainindex),gref(currentchar))
28503     end
28504    else
28505     if trace_singles then
28506      logprocess("%s: replacing single %s by %s",cref(dataset,sequence,chainindex),gref(currentchar),gref(replacement))
28507     end
28508     resetinjection(current)
28509     setchar(current,replacement)
28510    end
28511    return head,start,true
28512   elseif currentchar==false then
28513    break
28514   elseif current==stop then
28515    break
28516   else
28517    current=getnext(current)
28518   end
28519  end
28520 end
28521 return head,start,false
28522end
28523function chainprocs.gsub_alternate(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
28524 local mapping=currentlookup.mapping
28525 if mapping==nil then
28526  mapping=getmapping(dataset,sequence,currentlookup)
28527 end
28528 if mapping then
28529  local kind=dataset[4]
28530  local what=dataset[1]
28531  local value=what==true and tfmdata.shared.features[kind] or what 
28532  local current=start
28533  while current do
28534   local currentchar=ischar(current)
28535   if currentchar then
28536    local alternatives=mapping[currentchar]
28537    if alternatives then
28538     local choice,comment=get_alternative_glyph(current,alternatives,value)
28539     if choice then
28540      if trace_alternatives then
28541       logprocess("%s: replacing %s by alternative %a to %s, %s",cref(dataset,sequence),gref(currentchar),choice,gref(choice),comment)
28542      end
28543      resetinjection(start)
28544      setchar(start,choice)
28545     else
28546      if trace_alternatives then
28547       logwarning("%s: no variant %a for %s, %s",cref(dataset,sequence),value,gref(currentchar),comment)
28548      end
28549     end
28550    end
28551    return head,start,true
28552   elseif currentchar==false then
28553    break
28554   elseif current==stop then
28555    break
28556   else
28557    current=getnext(current)
28558   end
28559  end
28560 end
28561 return head,start,false
28562end
28563function chainprocs.gsub_multiple(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
28564 local mapping=currentlookup.mapping
28565 if mapping==nil then
28566  mapping=getmapping(dataset,sequence,currentlookup)
28567 end
28568 if mapping then
28569  local startchar=getchar(start)
28570  local replacement=mapping[startchar]
28571  if not replacement or replacement=="" then
28572   if trace_bugs then
28573    logwarning("%s: no multiple for %s",cref(dataset,sequence),gref(startchar))
28574   end
28575  else
28576   if trace_multiples then
28577    logprocess("%s: replacing %s by multiple characters %s",cref(dataset,sequence),gref(startchar),gref(replacement))
28578   end
28579   return multiple_glyphs(head,start,replacement,skiphash,dataset[1],stop)
28580  end
28581 end
28582 return head,start,false
28583end
28584function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
28585 local mapping=currentlookup.mapping
28586 if mapping==nil then
28587  mapping=getmapping(dataset,sequence,currentlookup)
28588 end
28589 if mapping then
28590  local startchar=getchar(start)
28591  local ligatures=mapping[startchar]
28592  if not ligatures then
28593   if trace_bugs then
28594    logwarning("%s: no ligatures starting with %s",cref(dataset,sequence,chainindex),gref(startchar))
28595   end
28596  else
28597   local hasmarks=marks[startchar]
28598   local current=getnext(start)
28599   local discfound=false
28600   local last=stop
28601   local nofreplacements=1
28602   while current do
28603    local id=getid(current)
28604    if id==disc_code then
28605     if not discfound then
28606      discfound=current
28607     end
28608     if current==stop then
28609      break 
28610     else
28611      current=getnext(current)
28612     end
28613    else
28614     local schar=getchar(current)
28615     if skiphash and skiphash[schar] then
28616       current=getnext(current)
28617     else
28618      local lg=not tonumber(ligatures) and ligatures[schar]
28619      if lg then
28620       ligatures=lg
28621       last=current
28622       nofreplacements=nofreplacements+1
28623       if marks[char] then
28624        hasmarks=true
28625       end
28626       if current==stop then
28627        break
28628       else
28629        current=getnext(current)
28630       end
28631      else
28632       break
28633      end
28634     end
28635    end
28636   end
28637   local ligature=tonumber(ligatures) or ligatures.ligature
28638   if ligature then
28639    if chainindex then
28640     stop=last
28641    end
28642    if trace_ligatures then
28643     if start==stop then
28644      logprocess("%s: replacing character %s by ligature %s case 3",cref(dataset,sequence,chainindex),gref(startchar),gref(ligature))
28645     else
28646      logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(dataset,sequence,chainindex),gref(startchar),gref(getchar(stop)),gref(ligature))
28647     end
28648    end
28649    head,start=toligature(head,start,stop,ligature,dataset,sequence,skiphash,discfound,hasmarks)
28650    return head,start,true,nofreplacements,discfound
28651   elseif trace_bugs then
28652    if start==stop then
28653     logwarning("%s: replacing character %s by ligature fails",cref(dataset,sequence,chainindex),gref(startchar))
28654    else
28655     logwarning("%s: replacing character %s upto %s by ligature fails",cref(dataset,sequence,chainindex),gref(startchar),gref(getchar(stop)))
28656    end
28657   end
28658  end
28659 end
28660 return head,start,false,0,false
28661end
28662function chainprocs.gpos_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
28663 if not hasglyphoption(start,no_right_kern_code) then
28664  local mapping=currentlookup.mapping
28665  if mapping==nil then
28666   mapping=getmapping(dataset,sequence,currentlookup)
28667  end
28668  if mapping then
28669   local startchar=getchar(start)
28670   local kerns=mapping[startchar]
28671   if kerns then
28672    local format=currentlookup.format
28673    if format=="single" then
28674     local dx,dy,w,h=setposition(0,start,factor,rlmode,kerns) 
28675     if trace_kerns then
28676      logprocess("%s: shifting single %s by %s (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),format,dx,dy,w,h)
28677     end
28678    else 
28679     local k=(format=="move" and setmove or setkern)(start,factor,rlmode,kerns,injection)
28680     if trace_kerns then
28681      logprocess("%s: shifting single %s by %s %p",cref(dataset,sequence),gref(startchar),format,k)
28682     end
28683    end
28684    return head,start,true
28685   end
28686  end
28687 end
28688 return head,start,false
28689end
28690function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
28691 if not hasglyphoption(start,no_right_kern_code) then
28692  local mapping=currentlookup.mapping
28693  if mapping==nil then
28694   mapping=getmapping(dataset,sequence,currentlookup)
28695  end
28696  if mapping then
28697   local snext=getnext(start)
28698   if snext then
28699    local startchar=getchar(start)
28700    local kerns=mapping[startchar] 
28701    if kerns then
28702     local prev=start
28703     while snext do
28704      local nextchar=ischar(snext,currentfont)
28705      if not nextchar then
28706       break
28707      end
28708      if skiphash and skiphash[nextchar] then
28709       prev=snext
28710       snext=getnext(snext)
28711      else
28712       local krn=kerns[nextchar]
28713       if not krn then
28714        break
28715       end
28716       local format=currentlookup.format
28717       if format=="pair" then
28718        local a=krn[1]
28719        local b=krn[2]
28720        if a==true then
28721        elseif a then
28722         local x,y,w,h=setposition(1,start,factor,rlmode,a,"injections") 
28723         if trace_kerns then
28724          local startchar=getchar(start)
28725          logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)
28726         end
28727        end
28728        if b==true then
28729         start=snext 
28730        elseif b then 
28731         local x,y,w,h=setposition(2,snext,factor,rlmode,b,"injections")
28732         if trace_kerns then
28733          local startchar=getchar(start)
28734          logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)
28735         end
28736         start=snext 
28737        elseif forcepairadvance then
28738         start=snext 
28739        end
28740        return head,start,true
28741       elseif krn~=0 then
28742        local k=(format=="move" and setmove or setkern)(snext,factor,rlmode,krn)
28743        if trace_kerns then
28744         logprocess("%s: inserting %s %p between %s and %s",cref(dataset,sequence),format,k,gref(getchar(prev)),gref(nextchar))
28745        end
28746        return head,start,true
28747       else
28748        break
28749       end
28750      end
28751     end
28752    end
28753   end
28754  end
28755 end
28756 return head,start,false
28757end
28758function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
28759 local mapping=currentlookup.mapping
28760 if mapping==nil then
28761  mapping=getmapping(dataset,sequence,currentlookup)
28762 end
28763 if mapping then
28764  local markchar=getchar(start)
28765  if marks[markchar] then
28766   local markanchors=mapping[markchar] 
28767   if markanchors then
28768    local base=getprev(start) 
28769    if base then
28770     local basechar=ischar(base,currentfont)
28771     if basechar then
28772      if marks[basechar] then
28773       while base do
28774        base=getprev(base)
28775        if base then
28776         local basechar=ischar(base,currentfont)
28777         if basechar then
28778          if not marks[basechar] then
28779           break
28780          end
28781         else
28782          if trace_bugs then
28783           logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),1)
28784          end
28785          return head,start,false
28786         end
28787        else
28788         if trace_bugs then
28789          logwarning("%s: no base for mark %s, case %i",pref(dataset,sequence),gref(markchar),2)
28790         end
28791         return head,start,false
28792        end
28793       end
28794      end
28795      local ba=markanchors[1][basechar]
28796      if ba then
28797       local ma=markanchors[2]
28798       if ma then
28799        local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
28800        if trace_marks then
28801         logprocess("%s, bound %s, anchoring mark %s to basechar %s => (%p,%p)",
28802          cref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy)
28803        end
28804        return head,start,true
28805       end
28806      end
28807     elseif trace_bugs then
28808      logwarning("%s: prev node is no char, case %i",cref(dataset,sequence),1)
28809     end
28810    elseif trace_bugs then
28811     logwarning("%s: prev node is no char, case %i",cref(dataset,sequence),2)
28812    end
28813   elseif trace_bugs then
28814    logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar))
28815   end
28816  elseif trace_bugs then
28817   logwarning("%s: mark %s is no mark",cref(dataset,sequence),gref(markchar))
28818  end
28819 end
28820 return head,start,false
28821end
28822function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
28823 local mapping=currentlookup.mapping
28824 if mapping==nil then
28825  mapping=getmapping(dataset,sequence,currentlookup)
28826 end
28827 if mapping then
28828  local markchar=getchar(start)
28829  if marks[markchar] then
28830   local markanchors=mapping[markchar] 
28831   if markanchors then
28832    local base=getprev(start) 
28833    if base then
28834     local basechar=ischar(base,currentfont)
28835     if basechar then
28836      if marks[basechar] then
28837       while base do
28838        base=getprev(base)
28839        if base then
28840         local basechar=ischar(base,currentfont)
28841         if basechar then
28842          if not marks[basechar] then
28843           break
28844          end
28845         else
28846          if trace_bugs then
28847           logwarning("%s: no base for mark %s, case %i",cref(dataset,sequence),markchar,1)
28848          end
28849          return head,start,false
28850         end
28851        else
28852         if trace_bugs then
28853          logwarning("%s: no base for mark %s, case %i",cref(dataset,sequence),markchar,2)
28854         end
28855         return head,start,false
28856        end
28857       end
28858      end
28859      local ba=markanchors[1][basechar]
28860      if ba then
28861       local ma=markanchors[2]
28862       if ma then
28863        local index=getligaindex(start)
28864        ba=ba[index]
28865        if ba then
28866         local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
28867         if trace_marks then
28868          logprocess("%s, bound %s, anchoring mark %s to baselig %s at index %s => (%p,%p)",
28869           cref(dataset,sequence),a or bound,gref(markchar),gref(basechar),index,dx,dy)
28870         end
28871         return head,start,true
28872        end
28873       end
28874      end
28875     elseif trace_bugs then
28876      logwarning("%s, prev node is no char, case %i",cref(dataset,sequence),1)
28877     end
28878    elseif trace_bugs then
28879     logwarning("%s, prev node is no char, case %i",cref(dataset,sequence),2)
28880    end
28881   elseif trace_bugs then
28882    logwarning("%s, mark %s has no anchors",cref(dataset,sequence),gref(markchar))
28883   end
28884  elseif trace_bugs then
28885   logwarning("%s, mark %s is no mark",cref(dataset,sequence),gref(markchar))
28886  end
28887 end
28888 return head,start,false
28889end
28890function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
28891 local mapping=currentlookup.mapping
28892 if mapping==nil then
28893  mapping=getmapping(dataset,sequence,currentlookup)
28894 end
28895 if mapping then
28896  local markchar=getchar(start)
28897  if marks[markchar] then
28898   local markanchors=mapping[markchar] 
28899   if markanchors then
28900    local base=getprev(start) 
28901    local slc=getligaindex(start)
28902    if slc then 
28903     while base do
28904      local blc=getligaindex(base)
28905      if blc and blc~=slc then
28906       base=getprev(base)
28907      else
28908       break
28909      end
28910     end
28911    end
28912    if base then 
28913     local basechar=ischar(base,currentfont)
28914     if basechar then
28915      local ba=markanchors[1][basechar]
28916      if ba then
28917       local ma=markanchors[2]
28918       if ma then
28919        local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks)
28920        if trace_marks then
28921         logprocess("%s, bound %s, anchoring mark %s to basemark %s => (%p,%p)",
28922          cref(dataset,sequence),bound,gref(markchar),gref(basechar),dx,dy)
28923        end
28924        return head,start,true
28925       end
28926      end
28927     elseif trace_bugs then
28928      logwarning("%s: prev node is no mark, case %i",cref(dataset,sequence),1)
28929     end
28930    elseif trace_bugs then
28931     logwarning("%s: prev node is no mark, case %i",cref(dataset,sequence),2)
28932    end
28933   elseif trace_bugs then
28934    logwarning("%s: mark %s has no anchors",cref(dataset,sequence),gref(markchar))
28935   end
28936  elseif trace_bugs then
28937   logwarning("%s: mark %s is no mark",cref(dataset,sequence),gref(markchar))
28938  end
28939 end
28940 return head,start,false
28941end
28942function chainprocs.gpos_cursive(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
28943 local mapping=currentlookup.mapping
28944 if mapping==nil then
28945  mapping=getmapping(dataset,sequence,currentlookup)
28946 end
28947 if mapping then
28948  local startchar=getchar(start)
28949  local exitanchors=mapping[startchar] 
28950  if exitanchors then
28951   if marks[startchar] then
28952    if trace_cursive then
28953     logprocess("%s: ignoring cursive for mark %s",pref(dataset,sequence),gref(startchar))
28954    end
28955   else
28956    local nxt=getnext(start)
28957    while nxt do
28958     local nextchar=ischar(nxt,currentfont)
28959     if not nextchar then
28960      break
28961     elseif marks[nextchar] then
28962      nxt=getnext(nxt)
28963     else
28964      local exit=exitanchors[3]
28965      if exit then
28966       local entry=exitanchors[1][nextchar]
28967       if entry then
28968        entry=entry[2]
28969        if entry then
28970         local r2lflag=sequence.flags[4] 
28971         local dx,dy,bound=setcursive(start,nxt,factor,rlmode,exit,entry,characters[startchar],characters[nextchar],r2lflag)
28972         if trace_cursive then
28973          logprocess("%s: moving %s to %s cursive (%p,%p) using bound %s in %s mode",pref(dataset,sequence),gref(startchar),gref(nextchar),dx,dy,bound,mref(rlmode))
28974         end
28975         return head,start,true
28976        end
28977       end
28978      elseif trace_bugs then
28979       onetimemessage(currentfont,startchar,"no entry anchors")
28980      end
28981      break
28982     end
28983    end
28984   end
28985  elseif trace_cursive and trace_details then
28986   logprocess("%s, cursive %s is already done",pref(dataset,sequence),gref(getchar(start)),alreadydone)
28987  end
28988 end
28989 return head,start,false
28990end
28991local function show_skip(dataset,sequence,char,ck,class)
28992 logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(dataset,sequence),gref(char),class,ck[1],ck[8] or ck[2])
28993end
28994local userkern=nuts.pool and nuts.pool.newkern 
28995do if not userkern then 
28996 local thekern=nuts.new("kern",1) 
28997 local setkern=nuts.setkern    
28998 userkern=function(k)
28999  local n=copynode(thekern)
29000  setkern(n,k)
29001  return n
29002 end
29003end end
29004local function checked(head)
29005 local current=head
29006 while current do
29007  if getid(current)==glue_code then
29008   local kern=userkern(getwidth(current))
29009   if head==current then
29010    local next=getnext(current)
29011    if next then
29012     setlink(kern,next)
29013    end
29014    flushnode(current)
29015    head=kern
29016    current=next
29017   else
29018    local prev,next=getboth(current)
29019    setlink(prev,kern,next)
29020    flushnode(current)
29021    current=next
29022   end
29023  else
29024   current=getnext(current)
29025  end
29026 end
29027 return head
29028end
29029local function setdiscchecked(d,pre,post,replace)
29030 if pre  then pre=checked(pre)  end
29031 if post then post=checked(post) end
29032 if replace then replace=checked(replace) end
29033 setdisc(d,pre,post,replace)
29034end
29035local noflags={ false,false,false,false }
29036local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck,where)
29037 local size=ck[5]-ck[4]+1
29038 local chainlookups=ck[6]
29039 local done=false
29040 if chainlookups then
29041  if size==1 then
29042   local chainlookup=chainlookups[1]
29043   if chainlookup then
29044    for j=1,#chainlookup do
29045     local chainstep=chainlookup[j]
29046     if chainstep then
29047      local chainkind=chainstep.type
29048      local chainproc=chainprocs[chainkind]
29049      if chainproc then
29050       local ok
29051       head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,1)
29052       if ok then
29053        done=true
29054       end
29055      else
29056       logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind)
29057      end
29058     else
29059      logprocess("%s: has an issue (1)",cref(dataset,sequence))
29060     end
29061    end
29062   else
29063   end
29064   else
29065   local i=1
29066   local laststart=start
29067   local nofchainlookups=#chainlookups 
29068   while start do
29069    if skiphash then 
29070     while start do
29071      local char=ischar(start,currentfont)
29072      if char then
29073       if skiphash and skiphash[char] then
29074        start=getnext(start)
29075       else
29076        break
29077       end
29078      else
29079       break
29080      end
29081     end
29082    end
29083    local chainlookup=chainlookups[i]
29084    if chainlookup then
29085     for j=1,#chainlookup do
29086      local chainstep=chainlookup[j]
29087      if chainstep then
29088       local chainkind=chainstep.type
29089       local chainproc=chainprocs[chainkind]
29090       if chainproc then
29091        local ok,n
29092        head,start,ok,n=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,i)
29093        if ok then
29094         done=true
29095         if n and n>1 and i+n>nofchainlookups then
29096          i=size 
29097          break
29098         end
29099        end
29100       else
29101        logprocess("%s: %s is not yet supported (2)",cref(dataset,sequence),chainkind)
29102       end
29103      else
29104       logprocess("%s: has an issue (2)",cref(dataset,sequence))
29105      end
29106     end
29107    else
29108    end
29109    i=i+1
29110    if i>size or not start then
29111     break
29112    elseif start then
29113     laststart=start
29114     start=getnext(start)
29115    end
29116   end
29117   if not start then
29118    start=laststart
29119   end
29120  end
29121 else
29122  local replacements=ck[7]
29123  if replacements then
29124   head,start,done=reversesub(head,start,last,dataset,sequence,replacements,rlmode,skiphash)
29125  else
29126   done=true
29127   if trace_contexts then
29128    logprocess("%s: skipping match @ %i",cref(dataset,sequence),where)
29129   end
29130  end
29131 end
29132 return head,start,done
29133end
29134local function chaindisk(head,start,dataset,sequence,rlmode,skiphash,ck)
29135 if not start then
29136  return head,start,false
29137 end
29138 local startishead=start==head
29139 local seq=ck[3]
29140 local f=ck[4]
29141 local l=ck[5]
29142 local s=#seq
29143 local done=false
29144 local sweepnode=sweepnode
29145 local sweeptype=sweeptype
29146 local sweepoverflow=false
29147 local checkdisc=getprev(head)
29148 local keepdisc=not sweepnode
29149 local lookaheaddisc=nil
29150 local backtrackdisc=nil
29151 local current=start
29152 local last=start
29153 local prev=getprev(start)
29154 local hasglue=false
29155 local useddisc=nil   
29156 local usedstart=start
29157 local i=f
29158 while i<=l do
29159  local id=getid(current)
29160  if id==glyph_code then
29161   i=i+1
29162   last=current
29163   current=getnext(current)
29164  elseif id==glue_code then
29165   i=i+1
29166   last=current
29167   current=getnext(current)
29168   hasglue=true
29169  elseif id==disc_code then
29170   if keepdisc then
29171    keepdisc=false
29172    lookaheaddisc=current
29173    local replace=getreplace(current)
29174    if not replace then
29175     sweepoverflow=true
29176     sweepnode=current
29177     current=getnext(current)
29178    else
29179     while replace and i<=l do
29180      if getid(replace)==glyph_code then
29181       i=i+1
29182      end
29183      replace=getnext(replace)
29184     end
29185     current=getnext(replace)
29186    end
29187    last=current
29188   else
29189    head,current=flattendisk(head,current)
29190   end
29191  else
29192   last=current
29193   current=getnext(current)
29194  end
29195  if current then
29196  elseif sweepoverflow then
29197   break
29198  elseif sweeptype=="post" or sweeptype=="replace" then
29199   current=getnext(sweepnode)
29200   if current then
29201    sweeptype=nil
29202    sweepoverflow=true
29203   else
29204    break
29205   end
29206  else
29207   break 
29208  end
29209 end
29210 if sweepoverflow then
29211  local prev=current and getprev(current)
29212  if not current or prev~=sweepnode then
29213   local head=getnext(sweepnode)
29214   local tail=nil
29215   if prev then
29216    tail=prev
29217    setprev(current,sweepnode)
29218   else
29219    tail=findnodetail(head)
29220   end
29221   setnext(sweepnode,current)
29222   setprev(head)
29223   setnext(tail)
29224   appenddisc(sweepnode,head)
29225  end
29226 end
29227 if l<s then
29228  local i=l
29229  local t=sweeptype=="post" or sweeptype=="replace"
29230  while current and i<s do
29231   local id=getid(current)
29232   if id==glyph_code then
29233    i=i+1
29234    current=getnext(current)
29235   elseif id==glue_code then
29236    i=i+1
29237    current=getnext(current)
29238    hasglue=true
29239   elseif id==disc_code then
29240    if keepdisc then
29241     keepdisc=false
29242     if notmatchpre[current]~=notmatchreplace[current] then
29243      lookaheaddisc=current
29244     end
29245     local replace=getreplace(current)
29246     while replace and i<s do
29247      if getid(replace)==glyph_code then
29248       i=i+1
29249      end
29250      replace=getnext(replace)
29251     end
29252     current=getnext(current)
29253    elseif notmatchpre[current]~=notmatchreplace[current] then
29254     head,current=flattendisk(head,current)
29255    else
29256     current=getnext(current) 
29257    end
29258   else
29259    current=getnext(current)
29260   end
29261   if not current and t then
29262    current=getnext(sweepnode)
29263    if current then
29264     sweeptype=nil
29265    end
29266   end
29267  end
29268 end
29269 if f>1 then
29270  local current=prev
29271  local i=f
29272  local t=sweeptype=="pre" or sweeptype=="replace"
29273  if not current and t and current==checkdisc then
29274   current=getprev(sweepnode)
29275  end
29276  while current and i>1 do 
29277   local id=getid(current)
29278   if id==glyph_code then
29279    i=i-1
29280   elseif id==glue_code then
29281    i=i-1
29282    hasglue=true
29283   elseif id==disc_code then
29284    if keepdisc then
29285     keepdisc=false
29286     if notmatchpost[current]~=notmatchreplace[current] then
29287      backtrackdisc=current
29288     end
29289     local replace=getreplace(current)
29290     while replace and i>1 do
29291      if getid(replace)==glyph_code then
29292       i=i-1
29293      end
29294      replace=getnext(replace)
29295     end
29296    elseif notmatchpost[current]~=notmatchreplace[current] then
29297     head,current=flattendisk(head,current)
29298    end
29299   end
29300   current=getprev(current)
29301   if t and current==checkdisc then
29302    current=getprev(sweepnode)
29303   end
29304  end
29305 end
29306 local done=false
29307 if lookaheaddisc then
29308  local cf=start
29309  local cl=getprev(lookaheaddisc)
29310  local cprev=getprev(start)
29311  local insertedmarks=0
29312  while cprev do
29313   local char=ischar(cf,currentfont)
29314   if char and marks[char] then
29315    insertedmarks=insertedmarks+1
29316    cf=cprev
29317    startishead=cf==head
29318    cprev=getprev(cprev)
29319   else
29320    break
29321   end
29322  end
29323  setlink(cprev,lookaheaddisc)
29324  setprev(cf)
29325  setnext(cl)
29326  if startishead then
29327   head=lookaheaddisc
29328  end
29329  local pre,post,replace=getdisc(lookaheaddisc)
29330  local new=copynodelist(cf) 
29331  local cnew=new
29332  if pre then
29333   setlink(findnodetail(cf),pre)
29334  end
29335  if replace then
29336   local tail=findnodetail(new)
29337   setlink(tail,replace)
29338  end
29339  for i=1,insertedmarks do
29340   cnew=getnext(cnew)
29341  end
29342  cl=start
29343  local clast=cnew
29344  for i=f,l do
29345   cl=getnext(cl)
29346   clast=getnext(clast)
29347  end
29348  if not notmatchpre[lookaheaddisc] then
29349   local ok=false
29350   cf,start,ok=chainrun(cf,start,cl,dataset,sequence,rlmode,skiphash,ck,1)
29351   if ok then
29352    done=true
29353   end
29354  end
29355  if not notmatchreplace[lookaheaddisc] then
29356   local ok=false
29357   new,cnew,ok=chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck,2)
29358   if ok then
29359    done=true
29360   end
29361  end
29362  if hasglue then
29363   setdiscchecked(lookaheaddisc,cf,post,new)
29364  else
29365   setdisc(lookaheaddisc,cf,post,new)
29366  end
29367  start=getprev(lookaheaddisc)
29368  useddisc=lookaheaddisc 
29369  sweephead[cf]=getnext(clast) or false
29370  sweephead[new]=getnext(cl) or false
29371 elseif backtrackdisc then
29372  local cf=getnext(backtrackdisc)
29373  local cl=start
29374  local cnext=getnext(start)
29375  local insertedmarks=0
29376  while cnext do
29377   local char=ischar(cnext,currentfont)
29378   if char and marks[char] then
29379    insertedmarks=insertedmarks+1
29380    cl=cnext
29381    cnext=getnext(cnext)
29382   else
29383    break
29384   end
29385  end
29386  setlink(backtrackdisc,cnext)
29387  setprev(cf)
29388  setnext(cl)
29389  local pre,post,replace,pretail,posttail,replacetail=getdisc(backtrackdisc,true)
29390  local new=copynodelist(cf)
29391  local cnew=findnodetail(new)
29392  for i=1,insertedmarks do
29393   cnew=getprev(cnew)
29394  end
29395  local clast=cnew
29396  for i=f,l do
29397   clast=getnext(clast)
29398  end
29399  if not notmatchpost[backtrackdisc] then
29400   local ok=false
29401   cf,start,ok=chainrun(cf,start,last,dataset,sequence,rlmode,skiphash,ck,3)
29402   if ok then
29403    done=true
29404   end
29405  end
29406  if not notmatchreplace[backtrackdisc] then
29407   local ok=false
29408   new,cnew,ok=chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck,4)
29409   if ok then
29410    done=true
29411   end
29412  end
29413  if post then
29414   setlink(posttail,cf)
29415  else
29416   post=cf
29417  end
29418  if replace then
29419   setlink(replacetail,new)
29420  else
29421   replace=new
29422  end
29423  if hasglue then
29424   setdiscchecked(backtrackdisc,pre,post,replace)
29425  else
29426   setdisc(backtrackdisc,pre,post,replace)
29427  end
29428  start=getprev(backtrackdisc)
29429  useddisc=backtrackdisc 
29430  sweephead[post]=getnext(clast) or false
29431  sweephead[replace]=getnext(last) or false
29432 else
29433  local ok=false
29434  head,start,ok=chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck,5)
29435  if ok then
29436   done=true
29437  end
29438 end
29439 if useddisc and start~=usedstart then 
29440    start=getnext(start)           
29441 end                  
29442 return head,start,done,useddisc           
29443end
29444local chaintrac do
29445 local level=0
29446 local last={}
29447 chaintrac=function(head,start,dataset,sequence,rlmode,skiphash,ck,match,discseen,sweepnode)
29448  if dataset then
29449   level=level+1
29450   last[level]=start
29451   local rule=ck[1]
29452   local lookuptype=ck[8] or ck[2]
29453   local nofseq=#ck[3] 
29454   local first=ck[4]
29455   local last=ck[5]
29456   local char=getchar(start)
29457   logwarning("+ %i : %s: rule %s %s at char %s for (%s,%s,%s) chars, lookuptype %a, %sdisc seen, %ssweeping",
29458    level,cref(dataset,sequence),rule,match and "matches" or "nomatch",
29459    gref(char),first-1,last-first+1,nofseq-last,lookuptype,
29460    discseen and "" or "no ",sweepnode and "" or "not ")
29461  else
29462   local what=start and "done" or "continue"
29463   local where=head==last[level] and "same" or "different"
29464   local char=getchar(head)
29465   if char then
29466    logwarning("- %i : %s at char %s, %s node",level,what,gref(char),where)
29467   else
29468    logwarning("- %i : %s, %s node",level,what,where)
29469   end
29470   level=level-1
29471  end
29472 end
29473end
29474local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,skiphash)
29475 if not contexts then
29476  return head,start,false
29477 end
29478 local sweepnode=sweepnode
29479 local sweeptype=sweeptype
29480 local postreplace
29481 local prereplace
29482 local checkdisc
29483 local discseen  
29484 if sweeptype then
29485  if sweeptype=="replace" then
29486   postreplace=true
29487   prereplace=true
29488  else
29489   postreplace=sweeptype=="post"
29490   prereplace=sweeptype=="pre"
29491  end
29492  checkdisc=getprev(head)
29493 end
29494 local currentfont=currentfont
29495 local skipped   
29496 local startprev,
29497    startnext=getboth(start)
29498 local done
29499 local nofcontexts=contexts.n 
29500 local startchar=nofcontext==1 or ischar(start,currentfont) 
29501 for k=1,nofcontexts do 
29502  local ck=contexts[k]
29503  local seq=ck[3]
29504  local f=ck[4] 
29505  local last=start
29506  if not startchar or not seq[f][startchar] then
29507   goto next
29508  end
29509  local s=seq.n 
29510  if s==1 then
29511  else
29512   local l=ck[5] 
29513   local current=start
29514   if l>f then
29515    local discfound 
29516    local n=f+1
29517    last=startnext 
29518    while n<=l do
29519     if postreplace and not last then
29520      last=getnext(sweepnode)
29521      sweeptype=nil
29522     end
29523     if last then
29524      local char,id=ischar(last,currentfont)
29525      if char then
29526       if seq[n][char] then
29527        if n<l then
29528         last=getnext(last)
29529        end
29530        n=n+1
29531       elseif skiphash and skiphash[char] then
29532        skipped=true
29533        if trace_skips then
29534         show_skip(dataset,sequence,char,ck,classes[char])
29535        end
29536        last=getnext(last)
29537       elseif discfound then
29538        notmatchreplace[discfound]=true
29539        if notmatchpre[discfound] then
29540         goto next
29541        else
29542         break
29543        end
29544       else
29545        goto next
29546       end
29547      elseif char==false then
29548       if discfound then
29549        notmatchreplace[discfound]=true
29550        if notmatchpre[discfound] then
29551         goto next
29552        else
29553         break
29554        end
29555       else
29556        goto next
29557       end
29558      elseif id==disc_code then
29559       discseen=true
29560       discfound=last
29561       notmatchpre[last]=nil
29562       notmatchpost[last]=true
29563       notmatchreplace[last]=nil
29564       local pre,post,replace=getdisc(last)
29565       if pre then
29566        local n=n
29567        while pre do
29568         if seq[n][getchar(pre)] then
29569          n=n+1
29570          if n>l then
29571           break
29572          end
29573          pre=getnext(pre)
29574         else
29575          notmatchpre[last]=true
29576          break
29577         end
29578        end
29579        if n<=l then
29580         notmatchpre[last]=true
29581        end
29582       else
29583        notmatchpre[last]=true
29584       end
29585       if replace then
29586        while replace do
29587         if seq[n][getchar(replace)] then
29588          n=n+1
29589          if n>l then
29590           break
29591          end
29592          replace=getnext(replace)
29593         else
29594          notmatchreplace[last]=true
29595          if notmatchpre[last] then
29596           goto next
29597          else
29598           break
29599          end
29600         end
29601        end
29602        if notmatchpre[last] then
29603         goto next
29604        end
29605       end
29606       last=getnext(last)
29607      else
29608       goto next
29609      end
29610     else
29611      goto next
29612     end
29613    end
29614   end
29615   if f>1 then
29616     local prev=startprev
29617     if prereplace and prev==checkdisc then
29618      prev=getprev(sweepnode)
29619     end
29620     if prev then
29621      local discfound 
29622      local n=f-1
29623      while n>=1 do
29624       if prev then
29625        local char,id=ischar(prev,currentfont)
29626        if char then
29627         if seq[n][char] then
29628          if n>1 then
29629           prev=getprev(prev)
29630          end
29631          n=n-1
29632         elseif skiphash and skiphash[char] then
29633          skipped=true
29634          if trace_skips then
29635           show_skip(dataset,sequence,char,ck,classes[char])
29636          end
29637          prev=getprev(prev)
29638         elseif discfound then
29639          notmatchreplace[discfound]=true
29640          if notmatchpost[discfound] then
29641           goto next
29642          else
29643           break
29644          end
29645         else
29646          goto next
29647         end
29648        elseif char==false then
29649         if discfound then
29650          notmatchreplace[discfound]=true
29651          if notmatchpost[discfound] then
29652           goto next
29653          end
29654         else
29655          goto next
29656         end
29657         break
29658        elseif id==disc_code then
29659         discseen=true
29660         discfound=prev
29661         notmatchpre[prev]=true
29662         notmatchpost[prev]=nil
29663         notmatchreplace[prev]=nil
29664         local pre,post,replace,pretail,posttail,replacetail=getdisc(prev,true)
29665         if pre~=start and post~=start and replace~=start then
29666          if post then
29667           local n=n
29668           while posttail do
29669            if seq[n][getchar(posttail)] then
29670             n=n-1
29671             if posttail==post or n<1 then
29672              break
29673             else
29674              posttail=getprev(posttail)
29675             end
29676            else
29677             notmatchpost[prev]=true
29678             break
29679            end
29680           end
29681           if n>=1 then
29682            notmatchpost[prev]=true
29683           end
29684          else
29685           notmatchpost[prev]=true
29686          end
29687          if replace then
29688           while replacetail do
29689            if seq[n][getchar(replacetail)] then
29690             n=n-1
29691             if replacetail==replace or n<1 then
29692              break
29693             else
29694              replacetail=getprev(replacetail)
29695             end
29696            else
29697             notmatchreplace[prev]=true
29698             if notmatchpost[prev] then
29699              goto next
29700             else
29701              break
29702             end
29703            end
29704           end
29705          else
29706          end
29707         end
29708         prev=getprev(prev)
29709        elseif id==glue_code then
29710         local sn=seq[n]
29711         if (sn[32] and spaces[prev]) or sn[0xFFFC] then
29712          n=n-1
29713          prev=getprev(prev)
29714         else
29715          goto next
29716         end
29717        elseif seq[n][0xFFFC] then
29718         n=n-1
29719         prev=getprev(prev)
29720        else
29721         goto next
29722        end
29723       else
29724        goto next
29725       end
29726      end
29727     else
29728      goto next
29729     end
29730   end
29731   if s>l then
29732    local current=last and getnext(last)
29733    if not current and postreplace then
29734     current=getnext(sweepnode)
29735    end
29736    if current then
29737     local discfound 
29738     local n=l+1
29739     while n<=s do
29740      if current then
29741       local char,id=ischar(current,currentfont)
29742       if char then
29743        if seq[n][char] then
29744         if n<s then
29745          current=getnext(current)
29746         end
29747         n=n+1
29748        elseif skiphash and skiphash[char] then
29749         skipped=true
29750         if trace_skips then
29751          show_skip(dataset,sequence,char,ck,classes[char])
29752         end
29753         current=getnext(current)
29754        elseif discfound then
29755         notmatchreplace[discfound]=true
29756         if notmatchpre[discfound] then
29757          goto next
29758         else
29759          break
29760         end
29761        else
29762         goto next
29763        end
29764       elseif char==false then
29765        if discfound then
29766         notmatchreplace[discfound]=true
29767         if notmatchpre[discfound] then
29768          goto next
29769         else
29770          break
29771         end
29772        else
29773         goto next
29774        end
29775       elseif id==disc_code then
29776        discseen=true
29777        discfound=current
29778        notmatchpre[current]=nil
29779        notmatchpost[current]=true
29780        notmatchreplace[current]=nil
29781        local pre,post,replace=getdisc(current)
29782        if pre then
29783         local n=n
29784         while pre do
29785          if seq[n][getchar(pre)] then
29786           n=n+1
29787           if n>s then
29788            break
29789           else
29790            pre=getnext(pre)
29791           end
29792          else
29793           notmatchpre[current]=true
29794           break
29795          end
29796         end
29797         if n<=s then
29798          notmatchpre[current]=true
29799         end
29800        else
29801         notmatchpre[current]=true
29802        end
29803        if replace then
29804         while replace do
29805          if seq[n][getchar(replace)] then
29806           n=n+1
29807           if n>s then
29808            break
29809           else
29810            replace=getnext(replace)
29811           end
29812          else
29813           notmatchreplace[current]=true
29814           if notmatchpre[current] then
29815            goto next
29816           else
29817            break
29818           end
29819          end
29820         end
29821        else
29822        end
29823        current=getnext(current)
29824       elseif id==glue_code then
29825        local sn=seq[n]
29826        if (sn[32] and spaces[current]) or sn[0xFFFC] then
29827         n=n+1
29828         current=getnext(current)
29829        else
29830         goto next
29831        end
29832       elseif seq[n][0xFFFC] then
29833        n=n+1
29834        current=getnext(current)
29835       else
29836        goto next
29837       end
29838      else
29839       goto next
29840      end
29841     end
29842    else
29843     goto next
29844    end
29845   end
29846  end
29847  if trace_contexts then
29848   chaintrac(head,start,dataset,sequence,rlmode,skipped and skiphash,ck,true,discseen,sweepnode)
29849  end
29850  if discseen or sweepnode then
29851   head,start,done=chaindisk(head,start,dataset,sequence,rlmode,skipped and skiphash,ck)
29852  else
29853   head,start,done=chainrun(head,start,last,dataset,sequence,rlmode,skipped and skiphash,ck,6)
29854  end
29855  if trace_contexts then
29856   chaintrac(start,done)
29857  end
29858  if done then
29859   break
29860  end
29861   ::next::
29862 end
29863 if discseen then
29864  notmatchpre={}
29865  notmatchpost={}
29866  notmatchreplace={}
29867 end
29868 return head,start,done
29869end
29870handlers.gsub_context=handle_contextchain
29871handlers.gsub_contextchain=handle_contextchain
29872handlers.gsub_reversecontextchain=handle_contextchain
29873handlers.gpos_contextchain=handle_contextchain
29874handlers.gpos_context=handle_contextchain
29875local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash)
29876 local steps=currentlookup.steps
29877 local nofsteps=currentlookup.nofsteps
29878 local char=getchar(start)
29879 if nofsteps==1 then
29880  local s=steps[1]
29881  local l=s.coverage[char]
29882  if l then
29883   return handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash)
29884  end
29885 else
29886  for i=1,nofsteps do
29887   local s=steps[i]
29888   local l=s.coverage[char]
29889   if l then
29890    local h,s,d=handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash)
29891    if d then
29892     return h,s,d
29893    end
29894   end
29895  end
29896 end
29897 return head,start,false
29898end
29899chainprocs.gsub_context=chained_contextchain
29900chainprocs.gsub_contextchain=chained_contextchain
29901chainprocs.gsub_reversecontextchain=chained_contextchain
29902chainprocs.gpos_contextchain=chained_contextchain
29903chainprocs.gpos_context=chained_contextchain
29904local missing=setmetatableindex("table")
29905local logwarning=report_process
29906local resolved={} 
29907local function logprocess(...)
29908 if trace_steps then
29909  registermessage(...)
29910  if trace_steps=="silent" then
29911   return
29912  end
29913 end
29914 report_process(...)
29915end
29916local sequencelists=setmetatableindex(function(t,font)
29917 local sequences=fontdata[font].resources.sequences
29918 if not sequences or not next(sequences) then
29919  sequences=false
29920 end
29921 t[font]=sequences
29922 return sequences
29923end)
29924do 
29925 local autofeatures=fonts.analyzers.features
29926 local featuretypes=otf.tables.featuretypes
29927 local defaultscript=otf.features.checkeddefaultscript
29928 local defaultlanguage=otf.features.checkeddefaultlanguage
29929 local wildcard="*"
29930 local default="dflt"
29931 local function initialize(sequence,script,language,enabled,autoscript,autolanguage)
29932  local features=sequence.features
29933  if features then
29934   local order=sequence.order
29935   if order then
29936    local featuretype=featuretypes[sequence.type or "unknown"]
29937    for i=1,#order do
29938     local kind=order[i]
29939     local valid=enabled[kind]
29940     if valid then
29941      local scripts=features[kind]
29942      local languages=scripts and (
29943       scripts[script] or
29944       scripts[wildcard] or
29945       (autoscript and defaultscript(featuretype,autoscript,scripts))
29946      )
29947      local enabled=languages and (
29948       languages[language] or
29949       languages[wildcard] or
29950       (autolanguage and defaultlanguage(featuretype,autolanguage,languages))
29951      )
29952      if enabled then
29953       return { valid,autofeatures[kind] or false,sequence,kind }
29954      end
29955     end
29956    end
29957   else
29958   end
29959  end
29960  return false
29961 end
29962 function otf.dataset(tfmdata,font) 
29963  local shared=tfmdata.shared
29964  local properties=tfmdata.properties
29965  local language=properties.language or "dflt"
29966  local script=properties.script   or "dflt"
29967  local enabled=shared.features
29968  local autoscript=enabled and enabled.autoscript
29969  local autolanguage=enabled and enabled.autolanguage
29970  local res=resolved[font]
29971  if not res then
29972   res={}
29973   resolved[font]=res
29974  end
29975  local rs=res[script]
29976  if not rs then
29977   rs={}
29978   res[script]=rs
29979  end
29980  local rl=rs[language]
29981  if not rl then
29982   rl={
29983   }
29984   rs[language]=rl
29985   local sequences=tfmdata.resources.sequences
29986   if sequences then
29987    for s=1,#sequences do
29988     local v=enabled and initialize(sequences[s],script,language,enabled,autoscript,autolanguage)
29989     if v then
29990      rl[#rl+1]=v
29991     end
29992    end
29993   end
29994  end
29995  return rl
29996 end
29997end
29998local function report_disc(what,n)
29999 report_run("%s: %s > %s",what,n,languages.serializediscretionary(n))
30000end
30001local function kernrun(disc,k_run,font,attr,...)
30002 if trace_kernruns then
30003  report_disc("kern",disc)
30004 end
30005 local prev,next=getboth(disc)
30006 local nextstart=next
30007 local done=false
30008 local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true)
30009 local prevmarks=prev
30010 while prevmarks do
30011  local char=ischar(prevmarks,font)
30012  if char and marks[char] then
30013   prevmarks=getprev(prevmarks)
30014  else
30015   break
30016  end
30017 end
30018 if prev and not ischar(prev,font) then  
30019  prev=false
30020 end
30021 if next and not ischar(next,font) then  
30022  next=false
30023 end
30024 if pre then
30025  if k_run(pre,"injections",nil,font,attr,...) then
30026   done=true
30027  end
30028  if prev then
30029   setlink(prev,pre)
30030   if k_run(prevmarks,"preinjections",pre,font,attr,...) then 
30031    done=true
30032   end
30033   setprev(pre)
30034   setlink(prev,disc)
30035  end
30036 end
30037 if post then
30038  if k_run(post,"injections",nil,font,attr,...) then
30039   done=true
30040  end
30041  if next then
30042   setlink(posttail,next)
30043   if k_run(posttail,"postinjections",next,font,attr,...) then
30044    done=true
30045   end
30046   setnext(posttail)
30047   setlink(disc,next)
30048  end
30049 end
30050 if replace then
30051  if k_run(replace,"injections",nil,font,attr,...) then
30052   done=true
30053  end
30054  if prev then
30055   setlink(prev,replace)
30056   if k_run(prevmarks,"replaceinjections",replace,font,attr,...) then 
30057    done=true
30058   end
30059   setprev(replace)
30060   setlink(prev,disc)
30061  end
30062  if next then
30063   setlink(replacetail,next)
30064   if k_run(replacetail,"replaceinjections",next,font,attr,...) then
30065    done=true
30066   end
30067   setnext(replacetail)
30068   setlink(disc,next)
30069  end
30070 elseif prev and next then
30071  setlink(prev,next)
30072  if k_run(prevmarks,"emptyinjections",next,font,attr,...) then
30073   done=true
30074  end
30075  setlink(prev,disc,next)
30076 end
30077 if done and trace_testruns then
30078  report_disc("done",disc)
30079 end
30080 return nextstart
30081end
30082local function comprun(disc,c_run,...) 
30083 if trace_compruns then
30084  report_disc("comp",disc)
30085 end
30086 local pre,post,replace=getdisc(disc)
30087 local renewed=false
30088 if pre then
30089  sweepnode=disc
30090  sweeptype="pre" 
30091  local new,done=c_run(pre,...)
30092  if done then
30093   pre=new
30094   renewed=true
30095  end
30096 end
30097 if post then
30098  sweepnode=disc
30099  sweeptype="post"
30100  local new,done=c_run(post,...)
30101  if done then
30102   post=new
30103   renewed=true
30104  end
30105 end
30106 if replace then
30107  sweepnode=disc
30108  sweeptype="replace"
30109  local new,done=c_run(replace,...)
30110  if done then
30111   replace=new
30112   renewed=true
30113  end
30114 end
30115 sweepnode=nil
30116 sweeptype=nil
30117 if renewed then
30118  if trace_testruns then
30119   report_disc("done",disc)
30120  end
30121  setdisc(disc,pre,post,replace)
30122 end
30123 return getnext(disc)
30124end
30125local test_flatten_start=2 
30126directives.register("otf.testrun.forceflatten",function(v)
30127 test_flatten_start=v and 1 or 2
30128end)
30129local function testrun(disc,t_run,c_run,...)
30130 if trace_testruns then
30131  report_disc("test",disc)
30132 end
30133 local prev,next=getboth(disc)
30134 if not next then
30135  return
30136 end
30137 local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true)
30138 local renewed=false
30139 if post or replace then 
30140  if post then
30141   setlink(posttail,next)
30142  else
30143   post=next
30144  end
30145  if replace then
30146   setlink(replacetail,next)
30147  else
30148   replace=next
30149  end
30150  local d_post=t_run(post,next,...)
30151  local d_replace=t_run(replace,next,...)
30152  if d_post>0 or d_replace>0 then
30153   local d=d_replace>d_post and d_replace or d_post
30154   local head=getnext(disc) 
30155   local tail=head
30156   for i=test_flatten_start,d do
30157    local nx=getnext(tail)
30158    local id=getid(nx)
30159    if id==disc_code then
30160     head,tail=flattendisk(head,nx)
30161    elseif id==glyph_code then
30162     tail=nx
30163    else
30164     break
30165    end
30166   end
30167   next=getnext(tail)
30168   setnext(tail)
30169   setprev(head)
30170   local new=copynodelist(head)
30171   if posttail then
30172    setlink(posttail,head)
30173   else
30174    post=head
30175   end
30176   if replacetail then
30177    setlink(replacetail,new)
30178   else
30179    replace=new
30180   end
30181  else
30182   if posttail then
30183    setnext(posttail)
30184   else
30185    post=nil
30186   end
30187   if replacetail then
30188    setnext(replacetail)
30189   else
30190    replace=nil
30191   end
30192  end
30193  setlink(disc,next)
30194 end
30195 if trace_testruns then
30196  report_disc("more",disc)
30197 end
30198 if pre then
30199  sweepnode=disc
30200  sweeptype="pre"
30201  local new,ok=c_run(pre,...)
30202  if ok then
30203   pre=new
30204   renewed=true
30205  end
30206 end
30207 if post then
30208  sweepnode=disc
30209  sweeptype="post"
30210  local new,ok=c_run(post,...)
30211  if ok then
30212   post=new
30213   renewed=true
30214  end
30215 end
30216 if replace then
30217  sweepnode=disc
30218  sweeptype="replace"
30219  local new,ok=c_run(replace,...)
30220  if ok then
30221   replace=new
30222   renewed=true
30223  end
30224 end
30225 sweepnode=nil
30226 sweeptype=nil
30227 if renewed then
30228  setdisc(disc,pre,post,replace)
30229  if trace_testruns then
30230   report_disc("done",disc)
30231  end
30232 end
30233 return getnext(disc)
30234end
30235local nesting=0
30236local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
30237 local done=false
30238 local sweep=sweephead[head]
30239 local start
30240 if sweep then
30241  start=sweep
30242  sweephead[head]=false
30243 else
30244  start=head
30245 end
30246 while start do
30247  local char,id=ischar(start,font)
30248  if char then
30249   local a 
30250   if attr then
30251    a=getglyphdata(start)
30252   end
30253   if not a or (a==attr) then
30254    local lookupmatch=lookupcache[char]
30255    if lookupmatch then
30256     local ok
30257     head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step)
30258     if ok then
30259      done=true
30260     end
30261    end
30262    if start then
30263     start=getnext(start)
30264    end
30265   else
30266    start=getnext(start)
30267   end
30268  elseif char==false then
30269   return head,done
30270  elseif sweep then
30271   return head,done
30272  else
30273   start=getnext(start)
30274  end
30275 end
30276 return head,done
30277end
30278local function t_run_single(start,stop,font,attr,lookupcache)
30279 local lastd=nil
30280 while start~=stop do
30281  local char=ischar(start,font)
30282  if char then
30283   local a 
30284   if attr then
30285    a=getglyphdata(start)
30286   end
30287   local startnext=getnext(start)
30288   if not a or (a==attr) then
30289    local lookupmatch=lookupcache[char]
30290    if lookupmatch then
30291     local s=startnext
30292     local ss=nil
30293     local sstop=s==stop
30294     if not s then
30295      s=ss
30296      ss=nil
30297     end
30298     while getid(s)==disc_code do
30299      ss=getnext(s)
30300      s=getreplace(s)
30301      if not s then
30302       s=ss
30303       ss=nil
30304      end
30305     end
30306     local l=nil
30307     local d=0
30308     while s do
30309      local char=ischar(s,font)
30310      if char then
30311       local lg=not tonumber(lookupmatch) and lookupmatch[char]
30312       if lg then
30313        if sstop then
30314         d=1
30315        elseif d>0 then
30316         d=d+1
30317        end
30318        l=lg
30319        s=getnext(s)
30320        sstop=s==stop
30321        if not s then
30322         s=ss
30323         ss=nil
30324        end
30325        while getid(s)==disc_code do
30326         ss=getnext(s)
30327         s=getreplace(s)
30328         if not s then
30329          s=ss
30330          ss=nil
30331         end
30332        end
30333        lookupmatch=lg
30334       else
30335        break
30336       end
30337      else
30338       break
30339      end
30340     end
30341     if l and (tonumber(l) or l.ligature) then 
30342      lastd=d
30343     end
30344    else
30345    end
30346   else
30347   end
30348   if lastd then
30349    return lastd
30350   end
30351   start=startnext
30352  else
30353   break
30354  end
30355 end
30356 return 0
30357end
30358local function k_run_single(sub,injection,last,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
30359 local a 
30360 if attr then
30361  a=getglyphdata(sub)
30362 end
30363 if not a or (a==attr) then
30364  for n in nextnode,sub do 
30365   if n==last then
30366    break
30367   end
30368   local char=ischar(n,font)
30369   if char then
30370    local lookupmatch=lookupcache[char]
30371    if lookupmatch then
30372     local h,d,ok=handler(sub,n,dataset,sequence,lookupmatch,rlmode,skiphash,step,injection)
30373     if ok then
30374      return true
30375     end
30376    end
30377   end
30378  end
30379 end
30380end
30381local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
30382 local done=false
30383 local sweep=sweephead[head]
30384 local start
30385 if sweep then
30386  start=sweep
30387  sweephead[head]=false
30388 else
30389  start=head
30390 end
30391 while start do
30392  local char=ischar(start,font)
30393  if char then
30394   local a 
30395   if attr then
30396    a=getglyphdata(start)
30397   end
30398   if not a or (a==attr) then
30399    for i=1,nofsteps do
30400     local step=steps[i]
30401     local lookupcache=step.coverage
30402     local lookupmatch=lookupcache[char]
30403     if lookupmatch then
30404      local ok
30405      head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step)
30406      if ok then
30407       done=true
30408       break
30409      elseif not start then
30410       break
30411      end
30412     end
30413    end
30414    if start then
30415     start=getnext(start)
30416    end
30417   else
30418    start=getnext(start)
30419   end
30420  elseif char==false then
30421   return head,done
30422  elseif sweep then
30423   return head,done
30424  else
30425   start=getnext(start)
30426  end
30427 end
30428 return head,done
30429end
30430local function t_run_multiple(start,stop,font,attr,steps,nofsteps)
30431 local lastd=nil
30432 while start~=stop do
30433  local char=ischar(start,font)
30434  if char then
30435   local a 
30436   if attr then
30437    a=getglyphdata(start)
30438   end
30439   local startnext=getnext(start)
30440   if not a or (a==attr) then
30441    for i=1,nofsteps do
30442     local step=steps[i]
30443     local lookupcache=step.coverage
30444     local lookupmatch=lookupcache[char]
30445     if lookupmatch then
30446      local s=startnext
30447      local ss=nil
30448      local sstop=s==stop
30449      if not s then
30450       s=ss
30451       ss=nil
30452      end
30453      while getid(s)==disc_code do
30454       ss=getnext(s)
30455       s=getreplace(s)
30456       if not s then
30457        s=ss
30458        ss=nil
30459       end
30460      end
30461      local l=nil
30462      local d=0
30463      while s do
30464       local char=ischar(s)
30465       if char then
30466        local lg=not tonumber(lookupmatch) and lookupmatch[char]
30467        if lg then
30468         if sstop then
30469          d=1
30470         elseif d>0 then
30471          d=d+1
30472         end
30473         l=lg
30474         s=getnext(s)
30475         sstop=s==stop
30476         if not s then
30477          s=ss
30478          ss=nil
30479         end
30480         while getid(s)==disc_code do
30481          ss=getnext(s)
30482          s=getreplace(s)
30483          if not s then
30484           s=ss
30485           ss=nil
30486          end
30487         end
30488         lookupmatch=lg
30489        else
30490         break
30491        end
30492       else
30493        break
30494       end
30495      end
30496      if l and (tonumber(l) or l.ligature) then
30497       lastd=d
30498      end
30499     end
30500    end
30501   else
30502   end
30503   if lastd then
30504    return lastd
30505   end
30506   start=startnext
30507  else
30508   break
30509  end
30510 end
30511 return 0
30512end
30513local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
30514 local a 
30515 if attr then
30516  a=getglyphdata(sub)
30517 end
30518 if not a or (a==attr) then
30519  for n in nextnode,sub do 
30520   if n==last then
30521    break
30522   end
30523   local char=ischar(n)
30524   if char then
30525    for i=1,nofsteps do
30526     local step=steps[i]
30527     local lookupcache=step.coverage
30528     local lookupmatch=lookupcache[char]
30529     if lookupmatch then
30530      local h,d,ok=handler(sub,n,dataset,sequence,lookupmatch,rlmode,skiphash,step,injection) 
30531      if ok then
30532       return true
30533      end
30534     end
30535    end
30536   end
30537  end
30538 end
30539end
30540local txtdirstate,pardirstate  do 
30541 local getdirection=nuts.getdirection
30542 txtdirstate=function(start,stack,top,rlparmode)
30543  local dir,pop=getdirection(start)
30544  if pop then
30545   if top==1 then
30546    return 0,rlparmode
30547   else
30548    top=top-1
30549    if stack[top]==righttoleft_code then
30550     return top,-1
30551    else
30552     return top,1
30553    end
30554   end
30555  elseif dir==lefttoright_code then
30556   top=top+1
30557   stack[top]=lefttoright_code
30558   return top,1
30559  elseif dir==righttoleft_code then
30560   top=top+1
30561   stack[top]=righttoleft_code
30562   return top,-1
30563  else
30564   return top,rlparmode
30565  end
30566 end
30567 pardirstate=function(start)
30568  local dir=getdirection(start)
30569  if dir==lefttoright_code then
30570   return 1,1
30571  elseif dir==righttoleft_code then
30572   return -1,-1
30573  else
30574   return 0,0
30575  end
30576 end
30577end
30578otf.helpers=otf.helpers or {}
30579otf.helpers.txtdirstate=txtdirstate
30580otf.helpers.pardirstate=pardirstate
30581do
30582 local fastdisc=true
30583 local testdics=false
30584 directives.register("otf.fastdisc",function(v) fastdisc=v end)
30585 local otfdataset=nil 
30586 local getfastdisc={ __index=function(t,k)
30587  local v=usesfont(k,currentfont)
30588  t[k]=v
30589  return v
30590 end }
30591 local getfastspace={ __index=function(t,k)
30592  local v=isspace(k,threshold) or false
30593  t[k]=v
30594  return v
30595 end }
30596 function otf.featuresprocessor(head,font,attr,direction,n)
30597  local sequences=sequencelists[font] 
30598  nesting=nesting+1
30599  if nesting==1 then
30600   currentfont=font
30601   tfmdata=fontdata[font]
30602   descriptions=tfmdata.descriptions 
30603   characters=tfmdata.characters   
30604   local resources=tfmdata.resources
30605   marks=resources.marks
30606   classes=resources.classes
30607   threshold,
30608   factor=getthreshold(font)
30609   checkmarks=tfmdata.properties.checkmarks
30610   if not otfdataset then
30611    otfdataset=otf.dataset
30612   end
30613   discs=fastdisc and n and n>1 and setmetatable({},getfastdisc) 
30614   spaces=setmetatable({},getfastspace)
30615  elseif currentfont~=font then
30616   report_warning("nested call with a different font, level %s, quitting",nesting)
30617   nesting=nesting-1
30618   return head,false
30619  end
30620  if trace_steps then
30621   checkstep(head)
30622  end
30623  local initialrl=0
30624  if getid(head)==par_code and startofpar(head) then
30625   initialrl=pardirstate(head)
30626  elseif direction==righttoleft_code then
30627   initialrl=-1
30628  end
30629  local datasets=otfdataset(tfmdata,font,attr)
30630  local dirstack={ nil } 
30631  sweephead={}
30632  for s=1,#datasets do
30633   local dataset=datasets[s]
30634   local attribute=dataset[2]
30635   local sequence=dataset[3] 
30636   local rlparmode=initialrl
30637   local topstack=0
30638   local typ=sequence.type
30639   local gpossing=typ=="gpos_single" or typ=="gpos_pair" 
30640   local forcetestrun=typ=="gsub_ligature" 
30641   local handler=handlers[typ] 
30642   local steps=sequence.steps
30643   local nofsteps=sequence.nofsteps
30644   local skiphash=sequence.skiphash
30645   if not steps then
30646    local h,ok=handler(head,dataset,sequence,initialrl,font,attr)
30647    if h and h~=head then
30648     head=h
30649    end
30650   elseif typ=="gsub_reversecontextchain" then
30651    local start=findnodetail(head)
30652    local rlmode=0 
30653    local merged=steps.merged
30654    while start do
30655     local char=ischar(start,font)
30656     if char then
30657      local m=merged[char]
30658      if m then
30659       local a 
30660       if attr then
30661        a=getglyphdata(start)
30662       end
30663       if not a or (a==attr) then
30664        for i=m[1],m[2] do
30665         local step=steps[i]
30666         local lookupcache=step.coverage
30667         local lookupmatch=lookupcache[char]
30668         if lookupmatch then
30669          local ok
30670          head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step)
30671          if ok then
30672           break
30673          end
30674         end
30675        end
30676        if start then
30677         start=getprev(start)
30678        end
30679       else
30680        start=getprev(start)
30681       end
30682      else
30683       start=getprev(start)
30684      end
30685     else
30686      start=getprev(start)
30687     end
30688    end
30689   else
30690    local start=head
30691    local rlmode=initialrl
30692    if nofsteps==1 then 
30693     local step=steps[1]
30694     local lookupcache=step.coverage
30695     while start do
30696      local char,id=ischar(start,font)
30697      if char then
30698       local lookupmatch=lookupcache[char]
30699       if lookupmatch then
30700        local a 
30701        if attr then
30702         if getglyphdata(start)==attr and (not attribute or getstate(start,attribute)) then
30703          a=true
30704         end
30705        elseif not attribute or getstate(start,attribute) then
30706         a=true
30707        end
30708        if a then
30709         local ok,df
30710         head,start,ok,df=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step)
30711         if df then
30712         elseif start then
30713          start=getnext(start)
30714         end
30715        else
30716         start=getnext(start)
30717        end
30718       else
30719          start=getnext(start)
30720       end
30721      elseif char==false or id==glue_code then
30722       start=getnext(start)
30723      elseif id==disc_code then
30724       if not discs or discs[start]==true then
30725        if gpossing then
30726         start=kernrun(start,k_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
30727        elseif forcetestrun then
30728         start=testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
30729        else
30730         start=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
30731        end
30732       else
30733        start=getnext(start)
30734       end
30735      elseif id==math_code then
30736       start=getnext(endofmath(start))
30737      elseif id==dir_code then
30738       topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode)
30739       start=getnext(start)
30740      else
30741       start=getnext(start)
30742      end
30743     end
30744    else
30745     local merged=steps.merged
30746     while start do
30747      local char,id=ischar(start,font)
30748      if char then
30749       local m=merged[char]
30750       if m then
30751        local a 
30752        if attr then
30753         if getglyphdata(start)==attr and (not attribute or getstate(start,attribute)) then
30754          a=true
30755         end
30756        elseif not attribute or getstate(start,attribute) then
30757         a=true
30758        end
30759        if a then
30760         local ok,df
30761         for i=m[1],m[2] do
30762          local step=steps[i]
30763          local lookupcache=step.coverage
30764          local lookupmatch=lookupcache[char]
30765          if lookupmatch then
30766           head,start,ok,df=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step)
30767           if df then
30768            break
30769           elseif ok then
30770            break
30771           elseif not start then
30772            break
30773           end
30774          end
30775         end
30776         if df then
30777         elseif start then
30778          start=getnext(start)
30779         end
30780        else
30781         start=getnext(start)
30782        end
30783       else
30784        start=getnext(start)
30785       end
30786      elseif char==false or id==glue_code then
30787       start=getnext(start)
30788      elseif id==disc_code then
30789       if not discs or discs[start]==true then
30790        if gpossing then
30791         start=kernrun(start,k_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
30792        elseif forcetestrun then
30793         start=testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
30794        else
30795         start=comprun(start,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
30796        end
30797       else
30798        start=getnext(start)
30799       end
30800      elseif id==math_code then
30801       start=getnext(endofmath(start))
30802      elseif id==dir_code then
30803       topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode)
30804       start=getnext(start)
30805      else
30806       start=getnext(start)
30807      end
30808     end
30809    end
30810   end
30811   if trace_steps then 
30812    registerstep(head)
30813   end
30814  end
30815  nesting=nesting-1
30816  return head
30817 end
30818 function otf.datasetpositionprocessor(head,font,direction,dataset)
30819  currentfont=font
30820  tfmdata=fontdata[font]
30821  descriptions=tfmdata.descriptions 
30822  characters=tfmdata.characters   
30823  local resources=tfmdata.resources
30824  marks=resources.marks
30825  classes=resources.classes
30826  threshold,
30827  factor=getthreshold(font)
30828  checkmarks=tfmdata.properties.checkmarks
30829  if type(dataset)=="number" then
30830   dataset=otfdataset(tfmdata,font,0)[dataset]
30831  end
30832  local sequence=dataset[3] 
30833  local typ=sequence.type
30834  local handler=handlers[typ] 
30835  local steps=sequence.steps
30836  local nofsteps=sequence.nofsteps
30837  local done=false
30838  local dirstack={ nil } 
30839  local start=head
30840  local initialrl=(direction==righttoleft_code) and -1 or 0
30841  local rlmode=initialrl
30842  local rlparmode=initialrl
30843  local topstack=0
30844  local merged=steps.merged
30845  local position=0
30846  while start do
30847   local char,id=ischar(start,font)
30848   if char then
30849    position=position+1
30850    local m=merged[char]
30851    if m then
30852     for i=m[1],m[2] do
30853      local step=steps[i]
30854      local lookupcache=step.coverage
30855      local lookupmatch=lookupcache[char]
30856      if lookupmatch then
30857       local ok
30858       head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,skiphash,step)
30859       if ok then
30860        break
30861       elseif not start then
30862        break
30863       end
30864      end
30865     end
30866     if start then
30867      start=getnext(start)
30868     end
30869    else
30870     start=getnext(start)
30871    end
30872   elseif char==false or id==glue_code then
30873    start=getnext(start)
30874   elseif id==math_code then
30875    start=getnext(endofmath(start))
30876   elseif id==dir_code then
30877    topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode)
30878    start=getnext(start)
30879   else
30880    start=getnext(start)
30881   end
30882  end
30883  return head
30884 end
30885end
30886do
30887 local plugins={}
30888 otf.plugins=plugins
30889 local report=logs.reporter("fonts")
30890 local warned=false
30891 local okay={ text=true }
30892 function otf.registerplugin(name,f)
30893  if type(name)=="string" and type(f)=="function" then
30894   plugins[name]={ name,f }
30895   if okay[name] then
30896   else
30897    report("plugin %a has been loaded, please be aware of possible side effects",name)
30898    if not warned then
30899     if logs.pushtarget then
30900      logs.pushtarget("log")
30901     end
30902     report("Plugins are not officially supported unless stated otherwise. This is because")
30903     report("they bypass the regular font handling and therefore some features in ConTeXt")
30904     report("(especially those related to fonts) might not work as expected or might not work")
30905     report("at all. Some plugins are for testing and development only and might change")
30906     report("whenever we feel the need for it.")
30907     report()
30908     if logs.poptarget then
30909      logs.poptarget()
30910     end
30911     warned=true
30912    end
30913   end
30914  end
30915 end
30916 function otf.plugininitializer(tfmdata,value)
30917  if type(value)=="string" then
30918   tfmdata.shared.plugin=plugins[value]
30919  end
30920 end
30921 function otf.pluginprocessor(head,font,dynamic,direction) 
30922  local s=fontdata[font].shared
30923  local p=s and s.plugin
30924  if p then
30925   if trace_plugins then
30926    report_process("applying plugin %a",p[1])
30927   end
30928   return p[2](head,font,dynamic,direction)
30929  else
30930   return head,false
30931  end
30932 end
30933end
30934function otf.featuresinitializer(tfmdata,value)
30935end
30936registerotffeature {
30937 name="features",
30938 description="features",
30939 default=true,
30940 initializers={
30941  position=1,
30942  node=otf.featuresinitializer,
30943  plug=otf.plugininitializer,
30944 },
30945 processors={
30946  node=otf.featuresprocessor,
30947  plug=otf.pluginprocessor,
30948 }
30949}
30950local function markinitializer(tfmdata,value)
30951 local properties=tfmdata.properties
30952 properties.checkmarks=value
30953end
30954registerotffeature {
30955 name="checkmarks",
30956 description="check mark widths",
30957 default=true,
30958 initializers={
30959  node=markinitializer,
30960 },
30961}
30962otf.handlers=handlers
30963if context then
30964
30965--removed
30966
30967else
30968end
30969local setspacekerns=nodes.injections.setspacekerns if not setspacekerns then os.exit() end
30970local tag="kern"
30971 function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,attr)
30972  local shared=fontdata[font].shared
30973  local features=shared and shared.features
30974  local enabled=features and features.spacekern and features[tag]
30975  if enabled then
30976   setspacekerns(font,sequence)
30977  end
30978  return head,enabled
30979 end
30980local function hasspacekerns(data)
30981 local resources=data.resources
30982 local sequences=resources.sequences
30983 local validgpos=resources.features.gpos
30984 if validgpos and sequences then
30985  for i=1,#sequences do
30986   local sequence=sequences[i]
30987   local steps=sequence.steps
30988   if steps and sequence.features[tag] then
30989    local kind=sequence.type
30990    if kind=="gpos_pair" or kind=="gpos_single" then
30991     for i=1,#steps do
30992      local step=steps[i]
30993      local coverage=step.coverage
30994      local rules=step.rules
30995      if rules then
30996      elseif not coverage then
30997      elseif kind=="gpos_single" then
30998      elseif kind=="gpos_pair" then
30999       local format=step.format
31000       if format=="move" or format=="kern" then
31001        local kerns=coverage[32]
31002        if kerns then
31003         return true
31004        end
31005        for k,v in next,coverage do
31006         if v[32] then
31007          return true
31008         end
31009        end
31010       elseif format=="pair" then
31011        local kerns=coverage[32]
31012        if kerns then
31013         for k,v in next,kerns do
31014          local one=v[1]
31015          if one and one~=true then
31016           return true
31017          end
31018         end
31019        end
31020        for k,v in next,coverage do
31021         local kern=v[32]
31022         if kern then
31023          local one=kern[1]
31024          if one and one~=true then
31025           return true
31026          end
31027         end
31028        end
31029       end
31030      end
31031     end
31032    end
31033   end
31034  end
31035 end
31036 return false
31037end
31038otf.readers.registerextender {
31039 name="spacekerns",
31040 action=function(data)
31041  data.properties.hasspacekerns=hasspacekerns(data)
31042 end
31043}
31044local function spaceinitializer(tfmdata,value) 
31045 local resources=tfmdata.resources
31046 local spacekerns=resources and resources.spacekerns
31047 if value and spacekerns==nil then
31048  local rawdata=tfmdata.shared and tfmdata.shared.rawdata
31049  local properties=rawdata.properties
31050  if properties and properties.hasspacekerns then
31051   local sequences=resources.sequences
31052   local validgpos=resources.features.gpos
31053   if validgpos and sequences then
31054    local left={}
31055    local right={}
31056    local last=0
31057    local feat=nil
31058    for i=1,#sequences do
31059     local sequence=sequences[i]
31060     local steps=sequence.steps
31061     if steps then
31062      local kern=sequence.features[tag]
31063      if kern then
31064       local kind=sequence.type
31065       if kind=="gpos_pair" or kind=="gpos_single" then
31066        if feat then
31067         for script,languages in next,kern do
31068          local f=feat[script]
31069          if f then
31070           for l in next,languages do
31071            f[l]=true
31072           end
31073          else
31074           feat[script]=languages
31075          end
31076         end
31077        else
31078         feat=kern
31079        end
31080        for i=1,#steps do
31081         local step=steps[i]
31082         local coverage=step.coverage
31083         local rules=step.rules
31084         if rules then
31085         elseif not coverage then
31086         elseif kind=="gpos_single" then
31087         elseif kind=="gpos_pair" then
31088          local format=step.format
31089          if format=="move" or format=="kern" then
31090           local kerns=coverage[32]
31091           if kerns then
31092            for k,v in next,kerns do
31093             right[k]=v
31094            end
31095           end
31096           for k,v in next,coverage do
31097            local kern=v[32]
31098            if kern then
31099             left[k]=kern
31100            end
31101           end
31102          elseif format=="pair" then
31103           local kerns=coverage[32]
31104           if kerns then
31105            for k,v in next,kerns do
31106             local one=v[1]
31107             if one and one~=true then
31108              right[k]=one[3]
31109             end
31110            end
31111           end
31112           for k,v in next,coverage do
31113            local kern=v[32]
31114            if kern then
31115             local one=kern[1]
31116             if one and one~=true then
31117              left[k]=one[3]
31118             end
31119            end
31120           end
31121          end
31122         end
31123        end
31124        last=i
31125       end
31126      else
31127      end
31128     end
31129    end
31130    left=next(left)  and left  or false
31131    right=next(right) and right or false
31132    if left or right then
31133     spacekerns={
31134      left=left,
31135      right=right,
31136     }
31137     if last>0 then
31138      local triggersequence={
31139       features={ [tag]=feat or { dflt={ dflt=true,} } },
31140       flags=noflags,
31141       name="trigger_space_kerns",
31142       order={ tag },
31143       type="trigger_space_kerns",
31144       left=left,
31145       right=right,
31146      }
31147      insert(sequences,last,triggersequence)
31148     end
31149    end
31150   end
31151  end
31152  resources.spacekerns=spacekerns
31153 end
31154 return spacekerns
31155end
31156registerotffeature {
31157 name="spacekern",
31158 description="space kern injection",
31159 default=true,
31160 initializers={
31161  node=spaceinitializer,
31162 },
31163}
31164
31165end -- closure
31166
31167do -- begin closure to overcome local limits and interference
31168
31169if not modules then modules={} end modules ['font-otc']={
31170 version=1.001,
31171 comment="companion to font-ini.mkiv",
31172 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
31173 copyright="PRAGMA ADE / ConTeXt Development Team",
31174 license="see context related readme files"
31175}
31176local insert,sortedkeys,sortedhash,tohash=table.insert,table.sortedkeys,table.sortedhash,table.tohash
31177local type,next,tonumber=type,next,tonumber
31178local lpegmatch=lpeg.match
31179local utfbyte,utflen=utf.byte,utf.len
31180local sortedhash=table.sortedhash
31181local trace_loading=false  trackers.register("otf.loading",function(v) trace_loading=v end)
31182local report_otf=logs.reporter("fonts","otf loading")
31183local fonts=fonts
31184local otf=fonts.handlers.otf
31185local registerotffeature=otf.features.register
31186local setmetatableindex=table.setmetatableindex
31187local fonthelpers=fonts.helpers
31188local checkmerge=fonthelpers.checkmerge
31189local checkflags=fonthelpers.checkflags
31190local checksteps=fonthelpers.checksteps
31191local normalized={
31192 substitution="substitution",
31193 single="substitution",
31194 ligature="ligature",
31195 alternate="alternate",
31196 multiple="multiple",
31197 kern="kern",
31198 pair="pair",
31199 single="single",
31200 chainsubstitution="chainsubstitution",
31201 chainposition="chainposition",
31202}
31203local types={
31204 substitution="gsub_single",
31205 ligature="gsub_ligature",
31206 alternate="gsub_alternate",
31207 multiple="gsub_multiple",
31208 kern="gpos_pair",
31209 pair="gpos_pair",
31210 single="gpos_single",
31211 chainsubstitution="gsub_contextchain",
31212 chainposition="gpos_contextchain",
31213}
31214local names={
31215 gsub_single="gsub",
31216 gsub_multiple="gsub",
31217 gsub_alternate="gsub",
31218 gsub_ligature="gsub",
31219 gsub_context="gsub",
31220 gsub_contextchain="gsub",
31221 gsub_reversecontextchain="gsub",
31222 gpos_single="gpos",
31223 gpos_pair="gpos",
31224 gpos_cursive="gpos",
31225 gpos_mark2base="gpos",
31226 gpos_mark2ligature="gpos",
31227 gpos_mark2mark="gpos",
31228 gpos_context="gpos",
31229 gpos_contextchain="gpos",
31230}
31231setmetatableindex(types,function(t,k) t[k]=k return k end) 
31232local everywhere={ ["*"]={ ["*"]=true } } 
31233local noflags={ false,false,false,false }
31234local function getrange(sequences,category)
31235 local count=#sequences
31236 local first=nil
31237 local last=nil
31238 for i=1,count do
31239  local t=sequences[i].type
31240  if t and names[t]==category then
31241   if not first then
31242    first=i
31243   end
31244   last=i
31245  end
31246 end
31247 return first or 1,last or count
31248end
31249local function validspecification(specification,name)
31250 local dataset=specification.dataset
31251 if dataset then
31252 elseif specification[1] then
31253  dataset=specification
31254  specification={ dataset=dataset }
31255 else
31256  dataset={ { data=specification.data } }
31257  specification.data=nil
31258  specification.coverage=dataset
31259  specification.dataset=dataset
31260 end
31261 local first=dataset[1]
31262 if first then
31263  first=first.data
31264 end
31265 if not first then
31266  report_otf("invalid feature specification, no dataset")
31267  return
31268 end
31269 if type(name)~="string" then
31270  name=specification.name or first.name
31271 end
31272 if type(name)~="string" then
31273  report_otf("invalid feature specification, no name")
31274  return
31275 end
31276 local n=#dataset
31277 if n>0 then
31278  for i=1,n do
31279   setmetatableindex(dataset[i],specification)
31280  end
31281  return specification,name
31282 end
31283end
31284local function addfeature(data,feature,specifications,prepareonly,filename)
31285 if not specifications then
31286  report_otf("missing specification")
31287  return
31288 end
31289 local descriptions=data.descriptions
31290 local resources=data.resources
31291 if not descriptions or not resources then
31292  report_otf("missing specification")
31293  return
31294 end
31295 local features=resources.features
31296 local sequences=resources.sequences
31297 if not features or not sequences then
31298  report_otf("missing specification")
31299  return
31300 end
31301 local alreadydone=resources.alreadydone
31302 if not alreadydone then
31303  alreadydone={}
31304  resources.alreadydone=alreadydone
31305 end
31306 if alreadydone[specifications] then
31307  return
31308 else
31309  alreadydone[specifications]=true
31310 end
31311 local fontfeatures=resources.features or everywhere
31312 local unicodes=resources.unicodes
31313 local splitter=lpeg.splitter(" ",unicodes)
31314 local done=0
31315 local skip=0
31316 local aglunicodes=false
31317 local privateslot=fonthelpers.privateslot
31318 local specifications=validspecification(specifications,feature)
31319 if not specifications then
31320  return
31321 end
31322 local p=lpeg.P("P")*(lpeg.patterns.hexdigit^1/function(s) return tonumber(s,16) end)*lpeg.P(-1)
31323 local function tounicode(code)
31324  if not code then
31325   return
31326  end
31327  if type(code)=="number" then
31328   return code
31329  end
31330  local u=unicodes[code]
31331  if u then
31332   return u
31333  end
31334  if utflen(code)==1 then
31335   u=utfbyte(code)
31336   if u then
31337    return u
31338   end
31339  end
31340  if privateslot then
31341   u=privateslot(code) 
31342   if u then
31343    return u
31344   end
31345  end
31346  local u=lpegmatch(p,code)
31347  if u then
31348   return u
31349  end
31350  if not aglunicodes then
31351   aglunicodes=fonts.encodings.agl.unicodes 
31352  end
31353  local u=aglunicodes[code]
31354  if u then
31355   return u
31356  end
31357 end
31358 local coverup=otf.coverup
31359 local coveractions=coverup.actions
31360 local stepkey=coverup.stepkey
31361 local register=coverup.register
31362 local function prepare_substitution(list,featuretype,nocheck)
31363  local coverage={}
31364  local cover=coveractions[featuretype]
31365  for code,replacement in next,list do
31366   local unicode=tounicode(code)
31367   local description=descriptions[unicode]
31368   if not nocheck and not description then
31369    skip=skip+1
31370   else
31371    if type(replacement)=="table" then
31372     replacement=replacement[1]
31373    end
31374    replacement=tounicode(replacement)
31375    if replacement and (nocheck or descriptions[replacement]) then
31376     cover(coverage,unicode,replacement)
31377     done=done+1
31378    else
31379     skip=skip+1
31380    end
31381   end
31382  end
31383  return coverage
31384 end
31385 local function prepare_alternate(list,featuretype,nocheck)
31386  local coverage={}
31387  local cover=coveractions[featuretype]
31388  for code,replacement in next,list do
31389   local unicode=tounicode(code)
31390   local description=descriptions[unicode]
31391   if not nocheck and not description then
31392    skip=skip+1
31393   elseif type(replacement)=="table" then
31394    local r={}
31395    for i=1,#replacement do
31396     local u=tounicode(replacement[i])
31397     r[i]=(nocheck or descriptions[u]) and u or unicode
31398    end
31399    cover(coverage,unicode,r)
31400    done=done+1
31401   else
31402    local u=tounicode(replacement)
31403    if u then
31404     cover(coverage,unicode,{ u })
31405     done=done+1
31406    else
31407     skip=skip+1
31408    end
31409   end
31410  end
31411  return coverage
31412 end
31413 local function prepare_multiple(list,featuretype,nocheck)
31414  local coverage={}
31415  local cover=coveractions[featuretype]
31416  for code,replacement in next,list do
31417   local unicode=tounicode(code)
31418   local description=descriptions[unicode]
31419   if not nocheck and not description then
31420    skip=skip+1
31421   elseif type(replacement)=="table" then
31422    local r={}
31423    local n=0
31424    for i=1,#replacement do
31425     local u=tounicode(replacement[i])
31426     if nocheck or descriptions[u] then
31427      n=n+1
31428      r[n]=u
31429     end
31430    end
31431    if n>0 then
31432     cover(coverage,unicode,r)
31433     done=done+1
31434    else
31435     skip=skip+1
31436    end
31437   else
31438    local u=tounicode(replacement)
31439    if u then
31440     cover(coverage,unicode,{ u })
31441     done=done+1
31442    else
31443     skip=skip+1
31444    end
31445   end
31446  end
31447  return coverage
31448 end
31449 local function prepare_ligature(list,featuretype,nocheck)
31450  local coverage={}
31451  local cover=coveractions[featuretype]
31452  for code,ligature in next,list do
31453   local unicode=tounicode(code)
31454   local description=descriptions[unicode]
31455   if not nocheck and not description then
31456    skip=skip+1
31457   else
31458    if type(ligature)=="string" then
31459     ligature={ lpegmatch(splitter,ligature) }
31460    end
31461    local present=true
31462    for i=1,#ligature do
31463     local l=ligature[i]
31464     local u=tounicode(l)
31465     if nocheck or descriptions[u] then
31466      ligature[i]=u
31467     else
31468      present=false
31469      break
31470     end
31471    end
31472    if present then
31473     cover(coverage,unicode,ligature)
31474     done=done+1
31475    else
31476     skip=skip+1
31477    end
31478   end
31479  end
31480  return coverage
31481 end
31482 local function resetspacekerns()
31483  data.properties.hasspacekerns=true
31484  data.resources .spacekerns=nil
31485 end
31486 local function prepare_kern(list,featuretype,nocheck)
31487  local coverage={}
31488  local cover=coveractions[featuretype]
31489  local isspace=false
31490  for code,replacement in next,list do
31491   local unicode=tounicode(code)
31492   local description=descriptions[unicode]
31493   if not nocheck and not description then
31494    skip=skip+1
31495   elseif type(replacement)=="table" then
31496    local r={}
31497    for k,v in next,replacement do
31498     local u=tounicode(k)
31499     if u then
31500      r[u]=v
31501      if u==32 then
31502       isspace=true
31503      end
31504     end
31505    end
31506    if next(r) then
31507     cover(coverage,unicode,r)
31508     done=done+1
31509     if unicode==32 then
31510      isspace=true
31511     end
31512    else
31513     skip=skip+1
31514    end
31515   else
31516    skip=skip+1
31517   end
31518  end
31519  if isspace then
31520   resetspacekerns()
31521  end
31522  return coverage
31523 end
31524 local function prepare_pair(list,featuretype,nocheck)
31525  local coverage={}
31526  local cover=coveractions[featuretype]
31527  if cover then
31528   for code,replacement in next,list do
31529    local unicode=tounicode(code)
31530    local description=descriptions[unicode]
31531    if not nocheck and not description then
31532     skip=skip+1
31533    elseif type(replacement)=="table" then
31534     local r={}
31535     for k,v in next,replacement do
31536      local u=tounicode(k)
31537      if u then
31538       r[u]=v
31539       if u==32 then
31540        isspace=true
31541       end
31542      end
31543     end
31544     if next(r) then
31545      cover(coverage,unicode,r)
31546      done=done+1
31547      if unicode==32 then
31548       isspace=true
31549      end
31550     else
31551      skip=skip+1
31552     end
31553    else
31554     skip=skip+1
31555    end
31556   end
31557   if isspace then
31558    resetspacekerns()
31559   end
31560  else
31561   report_otf("unknown cover type %a",featuretype)
31562  end
31563  return coverage
31564 end
31565 local prepare_single=prepare_pair 
31566 local function hassteps(lookups)
31567  if lookups then
31568   for i=1,#lookups do
31569    local l=lookups[i]
31570    if l then
31571     for j=1,#l do
31572      local l=l[j]
31573      if l then
31574       local n=l.nofsteps
31575       if not n then
31576        return true
31577       elseif n>0 then
31578        return true
31579       end
31580      end
31581     end
31582    end
31583   end
31584  end
31585  return false
31586 end
31587 local function prepare_chain(list,featuretype,sublookups,nocheck)
31588  local rules=list.rules
31589  local coverage={}
31590  if rules then
31591   local lookuptype=types[featuretype]
31592   for nofrules=1,#rules do
31593    local rule=rules[nofrules]
31594    local current=rule.current
31595    local before=rule.before
31596    local after=rule.after
31597    local replacements=rule.replacements or false
31598    local sequence={}
31599    local nofsequences=0
31600    if before then
31601     for n=1,#before do
31602      nofsequences=nofsequences+1
31603      sequence[nofsequences]=before[n]
31604     end
31605    end
31606    local start=nofsequences+1
31607    for n=1,#current do
31608     nofsequences=nofsequences+1
31609     sequence[nofsequences]=current[n]
31610    end
31611    local stop=nofsequences
31612    if after then
31613     for n=1,#after do
31614      nofsequences=nofsequences+1
31615      sequence[nofsequences]=after[n]
31616     end
31617    end
31618    local lookups=rule.lookups or false
31619    local subtype=nil
31620    if lookups and sublookups then
31621     if #lookups>0 then
31622      local ns=stop-start+1
31623      for i=1,ns do
31624       if lookups[i]==nil then
31625        lookups[i]=0
31626       end
31627      end
31628     end
31629     local l={}
31630     for k,v in sortedhash(lookups) do
31631      local t=type(v)
31632      if t=="table" then
31633       for i=1,#v do
31634        local vi=v[i]
31635        if type(vi)~="table" then
31636         v[i]={ vi }
31637        end
31638       end
31639       l[k]=v
31640      elseif t=="number" then
31641       local lookup=sublookups[v]
31642       if lookup then
31643        l[k]={ lookup }
31644        if not subtype then
31645         subtype=lookup.type
31646        end
31647       elseif v==0 then
31648        l[k]={ { type="gsub_remove",nosteps=true } }
31649       else
31650        l[k]=false 
31651       end
31652      else
31653       l[k]=false 
31654      end
31655     end
31656     if nocheck then
31657      rule.lookups=l 
31658     end
31659     lookups=l
31660    end
31661    if nofsequences>0 then
31662     if hassteps(lookups) then
31663      local hashed={}
31664      for i=1,nofsequences do
31665       local t={}
31666       local s=sequence[i]
31667       for i=1,#s do
31668        local u=tounicode(s[i])
31669        if u then
31670         t[u]=true
31671        end
31672       end
31673       hashed[i]=t
31674      end
31675      sequence=hashed
31676      local ruleset={
31677       nofrules,
31678       lookuptype,
31679       sequence,
31680       start,
31681       stop,
31682       lookups,
31683       replacements,
31684       subtype,
31685      }
31686      for unic in sortedhash(sequence[start]) do
31687       local cu=coverage[unic]
31688       if cu then
31689        local n=cu.n+1
31690        cu[n]=ruleset
31691        cu.n=n
31692       else
31693        coverage[unic]={
31694         ruleset,
31695         n=1,
31696        }
31697       end
31698      end
31699      sequence.n=nofsequences
31700     else
31701     end
31702    end
31703   end
31704  end
31705  return coverage
31706 end
31707 local dataset=specifications.dataset
31708 local function report(name,category,position,first,last,sequences)
31709  report_otf("injecting name %a of category %a at position %i in [%i,%i] of [%i,%i]",
31710   name,category,position,first,last,1,#sequences)
31711 end
31712 local function inject(specification,sequences,sequence,first,last,category,name)
31713  local position=specification.position or false
31714  if not position then
31715   position=specification.prepend
31716   if position==true then
31717    if trace_loading then
31718     report(name,category,first,first,last,sequences)
31719    end
31720    insert(sequences,first,sequence)
31721    return
31722   end
31723  end
31724  if not position then
31725   position=specification.append
31726   if position==true then
31727    if trace_loading then
31728     report(name,category,last+1,first,last,sequences)
31729    end
31730    insert(sequences,last+1,sequence)
31731    return
31732   end
31733  end
31734  local kind=type(position)
31735  if kind=="string" then
31736   local index=false
31737   for i=first,last do
31738    local s=sequences[i]
31739    local f=s.features
31740    if f then
31741     for k in sortedhash(f) do 
31742      if k==position then
31743       index=i
31744       break
31745      end
31746     end
31747     if index then
31748      break
31749     end
31750    end
31751   end
31752   if index then
31753    position=index
31754   else
31755    position=last+1
31756   end
31757  elseif kind=="number" then
31758   if position<0 then
31759    position=last-position+1
31760   end
31761   if position>last then
31762    position=last+1
31763   elseif position<first then
31764    position=first
31765   end
31766  else
31767   position=last+1
31768  end
31769  if trace_loading then
31770   report(name,category,position,first,last,sequences)
31771  end
31772  insert(sequences,position,sequence)
31773 end
31774 for s=1,#dataset do
31775  local specification=dataset[s]
31776  local valid=specification.valid 
31777  local files=specification.files
31778  if files and filename then
31779   local name=string.lower(file.basename(filename))
31780   local okay=files[name]
31781   if not okay then
31782    for i=1,#files do
31783     if name==files[i] then
31784      okay=true
31785      break
31786     end
31787    end
31788   end
31789   if okay then
31790   else
31791    return
31792   end
31793  end
31794  local feature=specification.name or feature
31795  if not feature or feature=="" then
31796   report_otf("no valid name given for extra feature")
31797  elseif not valid or valid(data,specification,feature) then 
31798   local initialize=specification.initialize
31799   if initialize then
31800    specification.initialize=initialize(specification,data) and initialize or nil
31801   end
31802   local askedfeatures=specification.features or everywhere
31803   local askedsteps=specification.steps or specification.subtables or { specification.data } or {}
31804   local featuretype=specification.type or "substitution"
31805   local featureaction=false
31806   local featureflags=specification.flags or noflags
31807   local nocheck=specification.nocheck
31808   local mapping=specification.mapping
31809   local featureorder=specification.order or { feature }
31810   local featurechain=(featuretype=="chainsubstitution" or featuretype=="chainposition") and 1 or 0
31811   local nofsteps=0
31812   local steps={}
31813   local sublookups=specification.lookups
31814   local category=nil
31815   local steptype=nil
31816   local sequence=nil
31817   if fonts.handlers.otf.handlers[featuretype] then
31818    featureaction=true 
31819   else
31820    featuretype=normalized[specification.type or "substitution"] or "substitution"
31821   end
31822   checkflags(specification,resources)
31823   for k,v in next,askedfeatures do
31824    if v[1] then
31825     askedfeatures[k]=tohash(v)
31826    end
31827   end
31828   if featureflags[1] then featureflags[1]="mark" end
31829   if featureflags[2] then featureflags[2]="ligature" end
31830   if featureflags[3] then featureflags[3]="base" end
31831   if featureaction then
31832    category="gsub"
31833    sequence={
31834     features={ [feature]=askedfeatures },
31835     flags=featureflags,
31836     name=feature,
31837     order=featureorder,
31838     type=featuretype,
31839     nofsteps=0,
31840    }
31841   else
31842    if sublookups then
31843     local s={}
31844     for i=1,#sublookups do
31845      local specification=sublookups[i]
31846      local askedsteps=specification.steps or specification.subtables or { specification.data } or {}
31847      local featuretype=normalized[specification.type or "substitution"] or "substitution"
31848      local featureflags=specification.flags or noflags
31849      local nofsteps=0
31850      local steps={}
31851      for i=1,#askedsteps do
31852       local list=askedsteps[i]
31853       local coverage=nil
31854       local format=nil
31855       if featuretype=="substitution" then
31856        coverage=prepare_substitution(list,featuretype,nocheck)
31857       elseif featuretype=="ligature" then
31858        coverage=prepare_ligature(list,featuretype,nocheck)
31859       elseif featuretype=="alternate" then
31860        coverage=prepare_alternate(list,featuretype,nocheck)
31861       elseif featuretype=="multiple" then
31862        coverage=prepare_multiple(list,featuretype,nocheck)
31863       elseif featuretype=="kern" or featuretype=="move" then
31864        format=featuretype
31865        coverage=prepare_kern(list,featuretype,nocheck)
31866       elseif featuretype=="pair" then
31867        format="pair"
31868        coverage=prepare_pair(list,featuretype,nocheck)
31869       elseif featuretype=="single" then
31870        format="single"
31871        coverage=prepare_single(list,featuretype,nocheck)
31872       end
31873       if coverage and next(coverage) then
31874        nofsteps=nofsteps+1
31875        steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources)
31876       end
31877      end
31878      checkmerge(specification)
31879      checksteps(specification)
31880      s[i]={
31881       [stepkey]=steps,
31882       nofsteps=nofsteps,
31883       flags=featureflags,
31884       type=types[featuretype],
31885      }
31886     end
31887     sublookups=s
31888    end
31889    for i=1,#askedsteps do
31890     local list=askedsteps[i]
31891     local coverage=nil
31892     local format=nil
31893     if type(list)=="function" then
31894      list=list(data,specification,list,i) 
31895     end
31896     if not list then
31897     elseif featuretype=="substitution" then
31898      category="gsub"
31899      coverage=(mapping and list) or prepare_substitution(list,featuretype,nocheck)
31900     elseif featuretype=="ligature" then
31901      category="gsub"
31902      coverage=prepare_ligature(list,featuretype,nocheck)
31903     elseif featuretype=="alternate" then
31904      category="gsub"
31905      coverage=prepare_alternate(list,featuretype,nocheck)
31906     elseif featuretype=="multiple" then
31907      category="gsub"
31908      coverage=prepare_multiple(list,featuretype,nocheck)
31909     elseif featuretype=="kern" or featuretype=="move" then
31910      category="gpos"
31911      format=featuretype
31912      coverage=prepare_kern(list,featuretype,nocheck)
31913     elseif featuretype=="pair" then
31914      category="gpos"
31915      format="pair"
31916      coverage=prepare_pair(list,featuretype,nocheck)
31917     elseif featuretype=="single" then
31918      category="gpos"
31919      format="single"
31920      coverage=prepare_single(list,featuretype,nocheck)
31921     elseif featuretype=="chainsubstitution" then
31922      category="gsub"
31923      coverage=prepare_chain(list,featuretype,sublookups,nocheck)
31924     elseif featuretype=="chainposition" then
31925      category="gpos"
31926      coverage=prepare_chain(list,featuretype,sublookups,nocheck)
31927     else
31928      report_otf("not registering feature %a, unknown category",feature)
31929      return
31930     end
31931     if coverage and next(coverage) then
31932      nofsteps=nofsteps+1
31933      steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources)
31934     end
31935    end
31936    if nofsteps>0 then
31937     sequence={
31938      chain=featurechain,
31939      features={ [feature]=askedfeatures },
31940      flags=featureflags,
31941      name=feature,
31942      order=featureorder,
31943      [stepkey]=steps,
31944      nofsteps=nofsteps,
31945      type=specification.handler or types[featuretype],
31946     }
31947     if prepareonly then
31948      return sequence
31949     end
31950    end
31951   end
31952   if sequence then
31953    checkflags(sequence,resources)
31954    checkmerge(sequence)
31955    checksteps(sequence)
31956    local first,last=getrange(sequences,category)
31957    inject(specification,sequences,sequence,first,last,category,feature)
31958    local features=fontfeatures[category]
31959    if not features then
31960     features={}
31961     fontfeatures[category]=features
31962    end
31963    local k=features[feature]
31964    if not k then
31965     k={}
31966     features[feature]=k
31967    end
31968    for script,languages in next,askedfeatures do
31969     local kk=k[script]
31970     if not kk then
31971      kk={}
31972      k[script]=kk
31973     end
31974     for language,value in next,languages do
31975      kk[language]=value
31976     end
31977    end
31978   end
31979  end
31980 end
31981 if trace_loading then
31982  report_otf("registering feature %a, affected glyphs %a, skipped glyphs %a",feature,done,skip)
31983 end
31984end
31985otf.enhancers.addfeature=addfeature
31986local extrafeatures={}
31987local knownfeatures={}
31988function otf.addfeature(name,specification)
31989 if type(name)=="table" then
31990  specification=name
31991 end
31992 if type(specification)~="table" then
31993  report_otf("invalid feature specification, no valid table")
31994  return
31995 end
31996 specification,name=validspecification(specification,name)
31997 if name and specification then
31998  local slot=knownfeatures[name]
31999  if not slot then
32000   slot=#extrafeatures+1
32001   knownfeatures[name]=slot
32002  elseif specification.overload==false then
32003   slot=#extrafeatures+1
32004   knownfeatures[name]=slot
32005  else
32006  end
32007  specification.name=name 
32008  extrafeatures[slot]=specification
32009 end
32010end
32011local function enhance(data,filename,raw)
32012 for slot=1,#extrafeatures do
32013  local specification=extrafeatures[slot]
32014  addfeature(data,specification.name,specification,nil,filename)
32015 end
32016end
32017otf.enhancers.enhance=enhance
32018otf.enhancers.register("check extra features",enhance)
32019
32020end -- closure
32021
32022do -- begin closure to overcome local limits and interference
32023
32024if not modules then modules={} end modules ['font-osd']={ 
32025 version=1.001,
32026 comment="companion to font-ini.mkiv",
32027 author="Kai Eigner, TAT Zetwerk / Hans Hagen, PRAGMA ADE",
32028 copyright="TAT Zetwerk / PRAGMA ADE / ConTeXt Development Team",
32029 license="see context related readme files"
32030}
32031local insert,remove,imerge,copy,tohash=table.insert,table.remove,table.imerge,table.copy,table.tohash
32032local next,type,rawget=next,type,rawget
32033local formatters=string.formatters
32034local settings_to_hash=utilities.parsers.settings_to_hash
32035local report=logs.reporter("otf","devanagari")
32036fonts=fonts       or {}
32037fonts.analyzers=fonts.analyzers   or {}
32038fonts.analyzers.methods=fonts.analyzers.methods or { node={ otf={} } }
32039local otf=fonts.handlers.otf
32040local handlers=otf.handlers
32041local methods=fonts.analyzers.methods
32042local otffeatures=fonts.constructors.features.otf
32043local registerotffeature=otffeatures.register
32044local trace_steps=false
32045local nuts=nodes.nuts
32046local getnext=nuts.getnext
32047local getprev=nuts.getprev
32048local getboth=nuts.getboth
32049local getid=nuts.getid
32050local getchar=nuts.getchar
32051local getfont=nuts.getfont
32052local getsubtype=nuts.getsubtype
32053local setlink=nuts.setlink
32054local setnext=nuts.setnext
32055local setprev=nuts.setprev
32056local setchar=nuts.setchar
32057local getprop=nuts.getprop
32058local setprop=nuts.setprop
32059local getstate=nuts.getstate
32060local setstate=nuts.setstate
32061local ischar=nuts.ischar
32062local insertnodeafter=nuts.insertafter
32063local copy_node=nuts.copy
32064local remove_node=nuts.remove
32065local flushlist=nuts.flushlist
32066local flushnode=nuts.flushnode
32067local copyinjection=nodes.injections.copy 
32068local unsetvalue=attributes.unsetvalue
32069local fontdata=fonts.hashes.identifiers
32070local a_syllabe="syllable"  
32071local a_reordered="reordered" 
32072local dotted_circle=0x25CC
32073local c_nbsp=0x00A0
32074local c_zwnj=0x200C
32075local c_zwj=0x200D
32076local states=fonts.analyzers.states 
32077local s_rphf=states.rphf
32078local s_half=states.half
32079local s_pref=states.pref
32080local s_blwf=states.blwf
32081local s_pstf=states.pstf
32082local s_init=states.init
32083local replace_all_nbsp=nil
32084replace_all_nbsp=function(head) 
32085 replace_all_nbsp=typesetters and typesetters.characters and typesetters.characters.replacenbspaces or function(head)
32086  return head
32087 end
32088 return replace_all_nbsp(head)
32089end
32090local processcharacters=nil
32091local logprocess=nil
32092if context then
32093
32094--removed
32095
32096else
32097 function processcharacters(head,font)
32098  local processors=fontdata[font].shared.processes
32099  for i=1,#processors do
32100   head=processors[i](head,font,0)
32101  end
32102  return head
32103 end
32104 logprocess=function(str)
32105 end
32106end
32107local indicgroups=characters and characters.indicgroups
32108if not indicgroups and characters then
32109 local indic={
32110  c={},
32111  i={},
32112  d={},
32113  m={},
32114  s={},
32115  o={},
32116 }
32117 local indicmarks={
32118  l={},
32119  t={},
32120  b={},
32121  r={},
32122  s={},
32123 }
32124 local indicclasses={
32125  nukta={},
32126  halant={},
32127  ra={},
32128  anudatta={},
32129 }
32130 local indicorders={
32131  bp={},
32132  ap={},
32133  bs={},
32134  as={},
32135  bh={},
32136  ah={},
32137  bm={},
32138  am={},
32139 }
32140 for k,v in next,characters.data do
32141  local i=v.indic
32142  if i then
32143   indic[i][k]=true
32144   i=v.indicmark
32145   if i then
32146    if i=="s" then
32147     local s=v.specials
32148     indicmarks[i][k]={ s[2],s[3] }
32149    else
32150     indicmarks[i][k]=true
32151    end
32152   end
32153   i=v.indicclass
32154   if i then
32155    indicclasses[i][k]=true
32156   end
32157   i=v.indicorder
32158   if i then
32159    indicorders[i][k]=true
32160   end
32161  end
32162 end
32163 indicgroups={
32164  consonant=indic.c,
32165  independent_vowel=indic.i,
32166  dependent_vowel=indic.d,
32167  vowel_modifier=indic.m,
32168  stress_tone_mark=indic.s,
32169  pre_mark=indicmarks.l,
32170  above_mark=indicmarks.t,
32171  below_mark=indicmarks.b,
32172  post_mark=indicmarks.r,
32173  twopart_mark=indicmarks.s,
32174  nukta=indicclasses.nukta,
32175  halant=indicclasses.halant,
32176  ra=indicclasses.ra,
32177  anudatta=indicclasses.anudatta,
32178  before_postscript=indicorders.bp,
32179  after_postscript=indicorders.ap,
32180  before_half=indicorders.bh,
32181  after_half=indicorders.ah,
32182  before_subscript=indicorders.bs,
32183  after_subscript=indicorders.as,
32184  before_main=indicorders.bm,
32185  after_main=indicorders.am,
32186 }
32187 indic=nil
32188 indicmarks=nil
32189 indicclasses=nil
32190 indicorders=nil
32191 characters.indicgroups=indicgroups
32192end
32193local consonant=indicgroups.consonant
32194local independent_vowel=indicgroups.independent_vowel
32195local dependent_vowel=indicgroups.dependent_vowel
32196local vowel_modifier=indicgroups.vowel_modifier
32197local stress_tone_mark=indicgroups.stress_tone_mark
32198local pre_mark=indicgroups.pre_mark
32199local above_mark=indicgroups.above_mark
32200local below_mark=indicgroups.below_mark
32201local post_mark=indicgroups.post_mark
32202local twopart_mark=indicgroups.twopart_mark
32203local nukta=indicgroups.nukta
32204local halant=indicgroups.halant
32205local ra=indicgroups.ra
32206local anudatta=indicgroups.anudatta
32207local before_postscript=indicgroups.before_postscript
32208local after_postscript=indicgroups.after_postscript
32209local before_half=indicgroups.before_half
32210local after_half=indicgroups.after_half
32211local before_subscript=indicgroups.before_subscript
32212local after_subscript=indicgroups.after_subscript
32213local before_main=indicgroups.before_main
32214local after_main=indicgroups.after_main
32215local mark_pre_above_below_post=table.merged (
32216 pre_mark,
32217 above_mark,
32218 below_mark,
32219 post_mark
32220)
32221local mark_above_below_post=table.merged (
32222 above_mark,
32223 below_mark,
32224 post_mark
32225)
32226local devanagarihash=table.setmetatableindex(function(t,k)
32227 local v=fontdata[k].resources.devanagari or false
32228 t[k]=v
32229 return v
32230end)
32231local zw_char={ 
32232 [c_zwnj]=true,
32233 [c_zwj ]=true,
32234}
32235local dflt_true={
32236 dflt=true,
32237}
32238local two_defaults={}
32239local one_defaults={}
32240local false_flags={ false,false,false,false }
32241local sequence_reorder_matras={
32242 features={ dv01=two_defaults },
32243 flags=false_flags,
32244 name="dv01_reorder_matras",
32245 order={ "dv01" },
32246 type="devanagari_reorder_matras",
32247 nofsteps=1,
32248 steps={
32249  {
32250   coverage=pre_mark,
32251  }
32252 }
32253}
32254local sequence_reorder_reph={
32255 features={ dv02=two_defaults },
32256 flags=false_flags,
32257 name="dv02_reorder_reph",
32258 order={ "dv02" },
32259 type="devanagari_reorder_reph",
32260 nofsteps=1,
32261 steps={
32262  {
32263   coverage={},
32264  }
32265 }
32266}
32267local sequence_reorder_pre_base_reordering_consonants={
32268 features={ dv03=one_defaults },
32269 flags=false_flags,
32270 name="dv03_reorder_pre_base_reordering_consonants",
32271 order={ "dv03" },
32272 type="devanagari_reorder_pre_base_reordering_consonants",
32273 nofsteps=1,
32274 steps={
32275  {
32276   coverage={},
32277  }
32278 }
32279}
32280local sequence_remove_joiners={
32281 features={ dv04=one_defaults },
32282 flags=false_flags,
32283 name="dv04_remove_joiners",
32284 order={ "dv04" },
32285 type="devanagari_remove_joiners",
32286 nofsteps=1,
32287 steps={
32288  {
32289     coverage=zw_char,
32290  },
32291 }
32292}
32293local basic_shaping_forms={
32294 akhn=true,
32295 blwf=true,
32296 cjct=true,
32297 half=true,
32298 nukt=true,
32299 pref=true,
32300 pstf=true,
32301 rkrf=true,
32302 rphf=true,
32303 vatu=true,
32304 locl=true,
32305}
32306local valid={
32307 abvs=true,
32308 akhn=true,
32309 blwf=true,
32310 calt=true,
32311 cjct=true,
32312 half=true,
32313 haln=true,
32314 nukt=true,
32315 pref=true,
32316 pres=true,
32317 pstf=true,
32318 psts=true,
32319 rkrf=true,
32320 rphf=true,
32321 vatu=true,
32322 pres=true,
32323 abvs=true,
32324 blws=true,
32325 psts=true,
32326 haln=true,
32327 calt=true,
32328 locl=true,
32329}
32330local scripts={}
32331local scripts_one={ "deva","mlym","beng","gujr","guru","knda","orya","taml","telu" }
32332local scripts_two={ "dev2","mlm2","bng2","gjr2","gur2","knd2","ory2","tml2","tel2" }
32333local nofscripts=#scripts_one
32334for i=1,nofscripts do
32335 local one=scripts_one[i]
32336 local two=scripts_two[i]
32337 scripts[one]=true
32338 scripts[two]=true
32339 two_defaults[two]=dflt_true
32340 one_defaults[one]=dflt_true
32341 one_defaults[two]=dflt_true
32342end
32343local function valid_one(s) for i=1,nofscripts do if s[scripts_one[i]] then return true end end end
32344local function valid_two(s) for i=1,nofscripts do if s[scripts_two[i]] then return true end end end
32345local function initializedevanagi(tfmdata)
32346 local script,language=otf.scriptandlanguage(tfmdata,attr) 
32347 if scripts[script] then
32348  local resources=tfmdata.resources
32349  local devanagari=resources.devanagari
32350  if not devanagari then
32351   report("adding features to font")
32352   local gsubfeatures=resources.features.gsub
32353   local sequences=resources.sequences
32354   local sharedfeatures=tfmdata.shared.features
32355   gsubfeatures["dv01"]=two_defaults 
32356   gsubfeatures["dv02"]=two_defaults 
32357   gsubfeatures["dv03"]=one_defaults 
32358   gsubfeatures["dv04"]=one_defaults
32359   local reorder_pre_base_reordering_consonants=copy(sequence_reorder_pre_base_reordering_consonants)
32360   local reorder_reph=copy(sequence_reorder_reph)
32361   local reorder_matras=copy(sequence_reorder_matras)
32362   local remove_joiners=copy(sequence_remove_joiners)
32363   local lastmatch=0
32364   for s=1,#sequences do 
32365    local features=sequences[s].features
32366    if features then
32367     for k,v in next,features do
32368      if k=="locl" then
32369       local steps=sequences[s].steps
32370       local nofsteps=sequences[s].nofsteps
32371       for i=1,nofsteps do
32372        local step=steps[i]
32373        local coverage=step.coverage
32374        if coverage then
32375         for k,v in next,pre_mark do
32376          local locl=coverage[k]
32377          if locl then
32378           if #locl>0 then
32379            for j=1,#locl do
32380             local ck=locl[j]
32381             local f=ck[4]
32382             local chainlookups=ck[6]
32383             if chainlookups then
32384              local chainlookup=chainlookups[f]
32385              for j=1,#chainlookup do
32386               local chainstep=chainlookup[j]
32387               local steps=chainstep.steps
32388               local nofsteps=chainstep.nofsteps
32389               for i=1,nofsteps do
32390                local step=steps[i]
32391                local coverage=step.coverage
32392                if coverage then
32393                 locl=coverage[k]
32394                end
32395               end
32396              end
32397             end
32398            end
32399           else
32400           end
32401           if locl then
32402            reorder_matras.steps[1].coverage[locl]=true
32403           end
32404          end
32405         end
32406        end
32407       end
32408      end
32409      if basic_shaping_forms[k] then
32410       lastmatch=lastmatch+1
32411       if s~=lastmatch then
32412        insert(sequences,lastmatch,remove(sequences,s))
32413       end
32414      end
32415     end
32416    end
32417   end
32418   local insertindex=lastmatch+1
32419   if tfmdata.properties.language then
32420    dflt_true[tfmdata.properties.language]=true
32421   end
32422   insert(sequences,insertindex,reorder_pre_base_reordering_consonants)
32423   insert(sequences,insertindex,reorder_reph)
32424   insert(sequences,insertindex,reorder_matras)
32425   insert(sequences,insertindex,remove_joiners)
32426   local blwfcache={}
32427   local vatucache={}
32428   local pstfcache={}
32429   local seqsubset={}
32430   local rephstep={ coverage={} } 
32431   local devanagari={
32432    reph=false,
32433    vattu=false,
32434    blwfcache=blwfcache,
32435    vatucache=vatucache,
32436    pstfcache=pstfcache,
32437    seqsubset=seqsubset,
32438    reorderreph=rephstep,
32439   }
32440   reorder_reph.steps={ rephstep }
32441   local pre_base_reordering_consonants={}
32442   reorder_pre_base_reordering_consonants.steps[1].coverage=pre_base_reordering_consonants
32443   resources.devanagari=devanagari
32444   for s=1,#sequences do
32445    local sequence=sequences[s]
32446    local steps=sequence.steps
32447    local nofsteps=sequence.nofsteps
32448    local features=sequence.features
32449    local has_rphf=features.rphf
32450    local has_blwf=features.blwf
32451    local has_vatu=features.vatu
32452    local has_pstf=features.pstf
32453    if has_rphf and has_rphf[script] then
32454     devanagari.reph=true
32455    elseif (has_blwf and has_blwf[script]) or (has_vatu and has_vatu[script]) then
32456     devanagari.vattu=true
32457     for i=1,nofsteps do
32458      local step=steps[i]
32459      local coverage=step.coverage
32460      if coverage then
32461       for k,v in next,coverage do
32462        for h,w in next,halant do
32463         if v[h] and not blwfcache[k] then
32464          blwfcache[k]=v
32465         end
32466         if has_vatu and has_vatu[script] and not vatucache[k] then
32467          vatucache[k]=v
32468         end
32469        end
32470       end
32471      end
32472     end
32473    elseif has_pstf and has_pstf[script] then
32474     for i=1,nofsteps do
32475      local step=steps[i]
32476      local coverage=step.coverage
32477      if coverage then
32478       for k,v in next,coverage do
32479        if not pstfcache[k] then
32480         pstfcache[k]=v
32481        end
32482       end
32483       for k,v in next,ra do
32484        local r=coverage[k]
32485        if r then
32486         local found=false
32487         if #r>0 then
32488          for j=1,#r do
32489           local ck=r[j]
32490           local f=ck[4]
32491           local chainlookups=ck[6]
32492           if chainlookups then
32493            local chainlookup=chainlookups[f]
32494            if chainlookup then
32495             for j=1,#chainlookup do
32496              local chainstep=chainlookup[j]
32497              local steps=chainstep.steps
32498              local nofsteps=chainstep.nofsteps
32499              for i=1,nofsteps do
32500               local step=steps[i]
32501               local coverage=step.coverage
32502               if coverage then
32503                local h=coverage[k]
32504                if h then
32505                 for k,v in next,h do
32506                  if v then
32507                   found=tonumber(v) or v.ligature
32508                   if found then
32509                    pre_base_reordering_consonants[found]=true
32510                    break
32511                   end
32512                  end
32513                 end
32514                 if found then
32515                  break
32516                 end
32517                end
32518               end
32519              end
32520             end
32521            end
32522           end
32523          end
32524         else
32525          for k,v in next,r do
32526           if v then
32527            found=tonumber(v) or v.ligature
32528            if found then
32529             pre_base_reordering_consonants[found]=true
32530             break
32531            end
32532           end
32533          end
32534         end
32535         if found then
32536          break
32537         end
32538        end
32539       end
32540      end
32541     end
32542    end
32543    for kind,spec in next,features do
32544     if valid[kind] and valid_two(spec)then
32545      for i=1,nofsteps do
32546       local step=steps[i]
32547       local coverage=step.coverage
32548       if coverage then
32549        local reph=false
32550        local base=false
32551        if kind=="rphf" then
32552         for k,v in next,ra do
32553          local r=coverage[k]
32554          if r then
32555           base=k
32556           local h=false
32557           if #r>0 then
32558            for j=1,#r do
32559             local ck=r[j]
32560             local f=ck[4]
32561             local chainlookups=ck[6]
32562             if chainlookups then
32563              local chainlookup=chainlookups[f]
32564              for j=1,#chainlookup do
32565               local chainstep=chainlookup[j]
32566               local steps=chainstep.steps
32567               local nofsteps=chainstep.nofsteps
32568               for i=1,nofsteps do
32569                local step=steps[i]
32570                local coverage=step.coverage
32571                if coverage then
32572                 local r=coverage[k]
32573                 if r then
32574                  for k,v in next,halant do
32575                   local h=r[k]
32576                   if h then
32577                    reph=tonumber(h) or h.ligature or false
32578                    break
32579                   end
32580                  end
32581                  if h then
32582                   break
32583                  end
32584                 end
32585                end
32586               end
32587              end
32588             end
32589            end
32590           else
32591            for k,v in next,halant do
32592             local h=r[k]
32593             if h then
32594              reph=tonumber(h) or h.ligature or false
32595              break
32596             end
32597            end
32598           end
32599           if reph then
32600            break
32601           end
32602          end
32603         end
32604        end
32605         seqsubset[#seqsubset+1]={ kind,coverage,reph,base }
32606       end
32607      end
32608     end
32609     if kind=="pref" then
32610      local steps=sequence.steps
32611      local nofsteps=sequence.nofsteps
32612      for i=1,nofsteps do
32613       local step=steps[i]
32614       local coverage=step.coverage
32615       if coverage then
32616        for k,v in next,halant do
32617         local h=coverage[k]
32618         if h then
32619          local found=false
32620          if #h>0 then
32621           for j=1,#h do
32622            local ck=h[j]
32623            local f=ck[4]
32624            local chainlookups=ck[6]
32625            if chainlookups then
32626             local chainlookup=chainlookups[f]
32627             for j=1,#chainlookup do
32628              local chainstep=chainlookup[j]
32629              local steps=chainstep.steps
32630              local nofsteps=chainstep.nofsteps
32631              for i=1,nofsteps do
32632               local step=steps[i]
32633               local coverage=step.coverage
32634               if coverage then
32635                local h=coverage[k]
32636                if h then
32637                 for k,v in next,h do
32638                  if v then
32639                   found=tonumber(v) or v.ligature
32640                   if found then
32641                    pre_base_reordering_consonants[found]=true
32642                    break
32643                   end
32644                  end
32645                 end
32646                 if found then
32647                  break
32648                 end
32649                end
32650               end
32651              end
32652             end
32653            end
32654           end
32655          else
32656           for k,v in next,h do
32657            found=v and (tonumber(v) or v.ligature)
32658            if found then
32659             pre_base_reordering_consonants[found]=true
32660             break
32661            end
32662           end
32663          end
32664          if found then
32665           break
32666          end
32667         end
32668        end
32669       end
32670      end
32671     end
32672    end
32673   end
32674   if two_defaults[script] then
32675    sharedfeatures["dv01"]=true 
32676    sharedfeatures["dv02"]=true 
32677    sharedfeatures["dv03"]=true 
32678    sharedfeatures["dv04"]=true 
32679   elseif one_defaults[script] then
32680    sharedfeatures["dv03"]=true 
32681    sharedfeatures["dv04"]=true 
32682   end
32683  end
32684 end
32685end
32686registerotffeature {
32687 name="devanagari",
32688 description="inject additional features",
32689 default=true,
32690 initializers={
32691  node=initializedevanagi,
32692 },
32693}
32694local function initializeconjuncts(tfmdata,value)
32695 if value then
32696  local resources=tfmdata.resources
32697  local devanagari=resources.devanagari
32698  if devanagari then
32699   local conjuncts="auto" 
32700   local movematra="auto" 
32701   if type(value)=="string" and value~="auto" then
32702    value=settings_to_hash(value)
32703    conjuncts=rawget(value,"conjuncts") or conjuncts
32704    movematra=rawget(value,"movematra") or movematra
32705   end
32706   if conjuncts=="auto" then
32707    conjuncts="mixed" 
32708   end
32709   if movematra=="auto" and
32710      script=="mlym" or
32711      script=="taml" then
32712    movematra="leftbeforebase"
32713   else
32714    movematra="default"
32715   end
32716   devanagari.conjuncts=conjuncts
32717   devanagari.movematra=movematra
32718   if trace_steps then
32719    report("conjuncts %a, movematra %a",conjuncts,movematra)
32720   end
32721  end
32722 end
32723end
32724registerotffeature {
32725 name="indic",
32726 description="control indic",
32727 default="auto",
32728 initializers={
32729  node=initializeconjuncts,
32730 },
32731}
32732local show_syntax_errors=false
32733local function inject_syntax_error(head,current,char)
32734 local signal=copy_node(current)
32735 copyinjection(signal,current)
32736 if pre_mark[char] then
32737  setchar(signal,dotted_circle)
32738 else
32739  setchar(current,dotted_circle)
32740 end
32741 return insertnodeafter(head,current,signal)
32742end
32743local function initialize_one(font,attr) 
32744 local tfmdata=fontdata[font]
32745 local datasets=otf.dataset(tfmdata,font,attr) 
32746 local devanagaridata=datasets.devanagari
32747 if not devanagaridata then
32748  devanagaridata={
32749   reph=false,
32750   vattu=false,
32751   blwfcache={},
32752   vatucache={},
32753   pstfcache={},
32754  }
32755  datasets.devanagari=devanagaridata
32756  local resources=tfmdata.resources
32757  local devanagari=resources.devanagari
32758  for s=1,#datasets do
32759   local dataset=datasets[s]
32760   if dataset and dataset[1] then 
32761    local kind=dataset[4]
32762    if kind=="rphf" then
32763     devanagaridata.reph=true
32764    elseif kind=="blwf" or kind=="vatu" then
32765     devanagaridata.vattu=true
32766     devanagaridata.blwfcache=devanagari.blwfcache
32767     devanagaridata.vatucache=devanagari.vatucache
32768     devanagaridata.pstfcache=devanagari.pstfcache
32769    end
32770   end
32771  end
32772 end
32773 return devanagaridata.reph,devanagaridata.vattu,devanagaridata.blwfcache,devanagaridata.vatucache,devanagaridata.pstfcache
32774end
32775local function contextchain(contexts,n)
32776 local char=getchar(n)
32777 if not contexts.n then
32778  return contexts[char]
32779 else
32780  for k=1,#contexts do
32781   local ck=contexts[k]
32782   local seq=ck[3]
32783   local f=ck[4]
32784   local l=ck[5]
32785   if (l-f)==1 and seq[f+1][char] then
32786    local ok=true
32787    local c=n
32788    for i=l+1,#seq do
32789     c=getnext(c)
32790     if not c or not seq[i][ischar(c)] then
32791      ok=false
32792      break
32793     end
32794    end
32795    if ok then
32796     c=getprev(n)
32797     for i=1,f-1 do
32798      c=getprev(c)
32799      if not c or not seq[f-i][ischar(c)] then
32800       ok=false
32801      end
32802     end
32803    end
32804    if ok then
32805     return true
32806    end
32807   end
32808  end
32809  return false
32810 end
32811end
32812local function order_matras(c)
32813 local cn=getnext(c)
32814 local char=getchar(cn)
32815 while dependent_vowel[char] do
32816  local next=getnext(cn)
32817  local cc=c
32818  local cchar=getchar(cc)
32819  while cc~=cn do
32820   if (above_mark[char] and (below_mark[cchar] or post_mark[cchar])) or (below_mark[char] and (post_mark[cchar])) then
32821    local prev,next=getboth(cn)
32822    if next then
32823     setprev(next,prev)
32824    end
32825    setnext(prev,next)
32826    setnext(getprev(cc),cn)
32827    setprev(cn,getprev(cc))
32828    setnext(cn,cc)
32829    setprev(cc,cn)
32830    break
32831   end
32832   cc=getnext(cc)
32833   cchar=getchar(cc)
32834  end
32835  cn=next
32836  char=getchar(cn)
32837 end
32838end
32839local swapped=table.swapped(states)
32840local function reorder_one(head,start,stop,font,attr,nbspaces)
32841 local reph,vattu,blwfcache,vatucache,pstfcache=initialize_one(font,attr)
32842 local current=start
32843 local n=getnext(start)
32844 local base=nil
32845 local firstcons=nil
32846 local lastcons=nil
32847 local basefound=false
32848 if reph and ra[getchar(start)] and halant[getchar(n)] then
32849  if n==stop then
32850   return head,stop,nbspaces
32851  end
32852  if getchar(getnext(n))==c_zwj then
32853   current=start
32854  else
32855   current=getnext(n)
32856   setstate(start,s_rphf)
32857  end
32858 end
32859 if getchar(current)==c_nbsp then
32860  if current==stop then
32861   stop=getprev(stop)
32862   head=remove_node(head,current)
32863   flushnode(current)
32864   if trace_steps then
32865    logprocess("reorder one, remove nbsp")
32866   end
32867   return head,stop,nbspaces
32868  else
32869   nbspaces=nbspaces+1
32870   base=current
32871   firstcons=current
32872   lastcons=current
32873   current=getnext(current)
32874   if current~=stop then
32875    local char=getchar(current)
32876    if nukta[char] then
32877     current=getnext(current)
32878     char=getchar(current)
32879    end
32880    if char==c_zwj and current~=stop then
32881     local next=getnext(current)
32882     if next~=stop and halant[getchar(next)] then
32883      current=next
32884      next=getnext(current)
32885      local tmp=next and getnext(next) or nil 
32886      local changestop=next==stop
32887      local tempcurrent=copy_node(next)
32888      copyinjection(tempcurrent,next)
32889      local nextcurrent=copy_node(current)
32890      copyinjection(nextcurrent,current) 
32891      setlink(tempcurrent,nextcurrent)
32892      setstate(tempcurrent,s_blwf)
32893      tempcurrent=processcharacters(tempcurrent,font)
32894      setstate(tempcurrent,unsetvalue)
32895      if getchar(next)==getchar(tempcurrent) then
32896       flushlist(tempcurrent)
32897       if show_syntax_errors then
32898        head,current=inject_syntax_error(head,current,char)
32899       end
32900      else
32901       setchar(current,getchar(tempcurrent)) 
32902       local freenode=getnext(current)
32903       setlink(current,tmp)
32904       flushnode(freenode)
32905       flushlist(tempcurrent)
32906       if changestop then
32907        stop=current
32908       end
32909      end
32910      if trace_steps then
32911       logprocess("reorder one, handle nbsp")
32912      end
32913     end
32914    end
32915   end
32916  end
32917 end
32918 while not basefound do
32919  local char=getchar(current)
32920  if consonant[char] then
32921   setstate(current,s_half)
32922   if not firstcons then
32923    firstcons=current
32924   end
32925   lastcons=current
32926   if not base then
32927    base=current
32928   elseif blwfcache[char] then
32929    setstate(current,s_blwf)
32930   elseif pstfcache[char] then
32931    setstate(current,s_pstf)
32932   else
32933    base=current
32934   end
32935  end
32936  basefound=current==stop
32937  current=getnext(current)
32938 end
32939 if base~=lastcons then
32940  local np=base
32941  local n=getnext(base)
32942  local ch=getchar(n)
32943  if nukta[ch] then
32944   np=n
32945   n=getnext(n)
32946   ch=getchar(n)
32947  end
32948  if halant[ch] then
32949   if lastcons~=stop then
32950    local ln=getnext(lastcons)
32951    if nukta[getchar(ln)] then
32952     lastcons=ln
32953    end
32954   end
32955   local nn=getnext(n)
32956   local ln=getnext(lastcons) 
32957   setlink(np,nn)
32958   setnext(lastcons,n)
32959   if ln then
32960    setprev(ln,n)
32961   end
32962   setnext(n,ln)
32963   setprev(n,lastcons)
32964   if lastcons==stop then
32965    stop=n
32966   end
32967   if trace_steps then
32968    logprocess("reorder one, handle halant")
32969   end
32970  end
32971 end
32972 n=getnext(start)
32973 if n~=stop and ra[getchar(start)] and halant[getchar(n)] and not zw_char[getchar(getnext(n))] then
32974  local matra=base
32975  if base~=stop then
32976   local next=getnext(base)
32977   if dependent_vowel[getchar(next)] then
32978    matra=next
32979   end
32980  end
32981  local sp=getprev(start)
32982  local nn=getnext(n)
32983  local mn=getnext(matra)
32984  setlink(sp,nn)
32985  setlink(matra,start)
32986  setlink(n,mn)
32987  if head==start then
32988   head=nn
32989  end
32990  start=nn
32991  if matra==stop then
32992   stop=n
32993  end
32994  if trace_steps then
32995   logprocess("reorder one, handle matra")
32996  end
32997 end
32998 local current=start
32999 while current~=stop do
33000  local next=getnext(current)
33001  if next~=stop and halant[getchar(next)] and getchar(getnext(next))==c_zwnj then
33002   setstate(current,unsetvalue)
33003  end
33004  current=next
33005 end
33006 if base~=stop and getstate(base) then 
33007  local next=getnext(base)
33008  if halant[getchar(next)] and not (next~=stop and getchar(getnext(next))==c_zwj) then
33009   setstate(base,unsetvalue)
33010  end
33011 end
33012 local current,allreordered,moved=start,false,{ [base]=true }
33013 local a,b,p,bn=base,base,base,getnext(base)
33014 if base~=stop and nukta[getchar(bn)] then
33015  a,b,p=bn,bn,bn
33016 end
33017 while not allreordered do
33018  local c=current
33019  local n=getnext(current)
33020  local l=nil 
33021  if c~=stop then
33022   local ch=getchar(n)
33023   if nukta[ch] then
33024    c=n
33025    n=getnext(n)
33026    ch=getchar(n)
33027   end
33028   if c~=stop then
33029    if halant[ch] then
33030     c=n
33031     n=getnext(n)
33032     ch=getchar(n)
33033    end
33034    local tpm=twopart_mark[ch]
33035    if tpm then
33036     while tpm do
33037      local extra=copy_node(n)
33038      copyinjection(extra,n)
33039      ch=tpm[1]
33040      setchar(n,ch)
33041      setchar(extra,tpm[2])
33042      head=insertnodeafter(head,current,extra)
33043      tpm=twopart_mark[ch]
33044     end
33045     if trace_steps then
33046      logprocess("reorder one, handle mark")
33047     end
33048    end
33049    while c~=stop and dependent_vowel[ch] do
33050     c=n
33051     n=getnext(n)
33052     ch=getchar(n)
33053    end
33054    if c~=stop then
33055     if vowel_modifier[ch] then
33056      c=n
33057      n=getnext(n)
33058      ch=getchar(n)
33059     end
33060     if c~=stop and stress_tone_mark[ch] then
33061      c=n
33062      n=getnext(n)
33063     end
33064    end
33065   end
33066  end
33067  local bp=getprev(firstcons)
33068  local cn=getnext(current)
33069  local last=getnext(c)
33070  local done=false
33071  while cn~=last do
33072   if pre_mark[getchar(cn)] then
33073    if devanagarihash[font].movematra=="leftbeforebase" then
33074     local prev,next=getboth(cn)
33075     setlink(prev,next)
33076     if cn==stop then
33077      stop=getprev(cn)
33078     end
33079     if base==start then
33080        if head==start then
33081         head=cn
33082        end
33083        start=cn
33084     end
33085     setlink(getprev(base),cn)
33086     setlink(cn,base)
33087     cn=next
33088    else
33089     if bp then
33090      setnext(bp,cn)
33091     end
33092     local prev,next=getboth(cn)
33093     if next then
33094      setprev(next,prev)
33095     end
33096     setnext(prev,next)
33097     if cn==stop then
33098      stop=prev
33099     end
33100     setprev(cn,bp)
33101     setlink(cn,firstcons)
33102     if firstcons==start then
33103      if head==start then
33104       head=cn
33105      end
33106      start=cn
33107     end
33108     cn=next
33109    end
33110    done=true
33111   elseif current~=base and dependent_vowel[getchar(cn)] then
33112    local prev,next=getboth(cn)
33113    if next then
33114     setprev(next,prev)
33115    end
33116    setnext(prev,next)
33117    if cn==stop then
33118     stop=prev
33119    end
33120    setlink(b,cn,getnext(b))
33121    order_matras(cn)
33122    cn=next
33123    done=true
33124   elseif current==base and dependent_vowel[getchar(cn)] then
33125    local cnn=getnext(cn)
33126    order_matras(cn)
33127    cn=cnn
33128    while cn~=last and dependent_vowel[getchar(cn)] do
33129     cn=getnext(cn)
33130    end
33131   else
33132    cn=getnext(cn)
33133   end
33134  end
33135  allreordered=c==stop
33136  current=getnext(c)
33137  if done and trace_steps then
33138   logprocess("reorder one, matra")
33139  end
33140 end
33141 if reph or vattu then
33142  local current=start
33143  local cns=nil
33144  local done=false
33145  while current~=stop do
33146   local c=current
33147   local n=getnext(current)
33148   if ra[getchar(current)] and halant[getchar(n)] then
33149    c=n
33150    n=getnext(n)
33151    local b,bn=base,base
33152    while bn~=stop  do
33153     local next=getnext(bn)
33154     if dependent_vowel[getchar(next)] then
33155      b=next
33156     end
33157     bn=next
33158    end
33159    if getstate(current,s_rphf) then
33160     if b~=current then
33161      if current==start then
33162       if head==start then
33163        head=n
33164       end
33165       start=n
33166      end
33167      if b==stop then
33168       stop=c
33169      end
33170      local prev=getprev(current)
33171      setlink(prev,n)
33172      local next=getnext(b)
33173      setlink(c,next)
33174      setlink(b,current)
33175      done=true
33176     end
33177    elseif cns and getnext(cns)~=current then
33178     local cp=getprev(current)
33179     local cnsn=getnext(cns)
33180     setlink(cp,n)
33181     setlink(cns,current) 
33182     setlink(c,cnsn)
33183     done=true
33184     if c==stop then
33185      stop=cp
33186      break
33187     end
33188     current=getprev(n)
33189    end
33190   else
33191    local char=getchar(current)
33192    if consonant[char] then
33193     cns=current
33194     local next=getnext(cns)
33195     if halant[getchar(next)] then
33196      cns=next
33197     end
33198     if not vatucache[char] then
33199      next=getnext(cns)
33200      while dependent_vowel[getchar(next)] do
33201       cns=next
33202       next=getnext(cns)
33203      end
33204     end
33205    elseif char==c_nbsp then
33206     nbspaces=nbspaces+1
33207     cns=current
33208     local next=getnext(cns)
33209     if halant[getchar(next)] then
33210      cns=next
33211     end
33212     if not vatucache[char] then
33213      next=getnext(cns)
33214      while dependent_vowel[getchar(next)] do
33215       cns=next
33216       next=getnext(cns)
33217      end
33218     end
33219    end
33220   end
33221   current=getnext(current)
33222  end
33223  if done and trace_steps then
33224   logprocess("reorder one, handle reph and vata") 
33225  end
33226 end
33227 if getchar(base)==c_nbsp then
33228  nbspaces=nbspaces-1
33229  if base==stop then
33230   stop=getprev(stop)
33231  end
33232  head=remove_node(head,base)
33233  flushnode(base)
33234 end
33235 return head,stop,nbspaces
33236end
33237function handlers.devanagari_reorder_matras(head,start) 
33238 local current=start 
33239 local startfont=getfont(start)
33240 local startattr=getprop(start,a_syllabe)
33241 while current do
33242  local char=ischar(current,startfont)
33243  local next=getnext(current)
33244  if char and getprop(current,a_syllabe)==startattr then
33245   if halant[char] then 
33246    if next then
33247     local char=ischar(next,startfont)
33248     if char and zw_char[char] and getprop(next,a_syllabe)==startattr then
33249      current=next
33250      next=getnext(current)
33251     end
33252    end
33253    local startnext=getnext(start)
33254    head=remove_node(head,start)
33255    setlink(start,next)
33256    setlink(current,start)
33257    start=startnext
33258    if trace_steps then
33259     logprocess("reorder matra")
33260    end
33261    break
33262   end
33263  else
33264   break
33265  end
33266  current=next
33267 end
33268 return head,start,true
33269end
33270local rephbase={}
33271function handlers.devanagari_reorder_reph(head,start)
33272 local current=getnext(start)
33273 local startnext=nil
33274 local startprev=nil
33275 local startfont=getfont(start)
33276 local startattr=getprop(start,a_syllabe)
33277 ::step_1::
33278 local char=ischar(start,startfont)
33279 local rephbase=rephbase[startfont][char]
33280 if char and after_subscript[rephbase] then
33281  goto step_5
33282 end
33283 ::step_2::
33284 if char and not after_postscript[rephbase] then
33285  while current do
33286   local char=ischar(current,startfont)
33287   if char and getprop(current,a_syllabe)==startattr then
33288    if halant[char] then
33289     if trace_steps then
33290      logprocess("reorder reph, handling halant")
33291     end
33292     local next=getnext(current)
33293     if next then
33294      local nextchar=ischar(next,startfont)
33295      if nextchar and zw_char[nextchar] and getprop(next,a_syllabe)==startattr then
33296       current=next
33297       next=getnext(current)
33298      end
33299     end
33300     startnext=getnext(start)
33301     head=remove_node(head,start)
33302     setlink(start,next)
33303     setlink(current,start)
33304     start=startnext
33305     startattr=getprop(start,a_syllabe)
33306     break
33307    end
33308    current=getnext(current)
33309   else
33310    break
33311   end
33312  end
33313 end
33314 ::step_3::
33315 if not startnext then
33316  if char and after_main[rephbase] then
33317   current=getnext(start)
33318   while current do
33319    local char=ischar(current,startfont)
33320    if char and getprop(current,a_syllabe)==startattr then
33321     if consonant[char] and not getstate(current,s_pref) then
33322      if trace_steps then
33323       logprocess("reorder reph, handling consonant")
33324      end
33325      startnext=getnext(start)
33326      head=remove_node(head,start)
33327      setlink(current,start)
33328      setlink(start,getnext(current))
33329      start=startnext
33330      startattr=getprop(start,a_syllabe)
33331      break
33332     end
33333     current=getnext(current)
33334    else
33335     break
33336    end
33337   end
33338  end
33339 end
33340 ::step_4::
33341 if not startnext then
33342  if char and before_postscript[rephbase] then
33343   current=getnext(start)
33344   local c=nil
33345   while current do
33346    local char=ischar(current,startfont)
33347    if char and getprop(current,a_syllabe)==startattr then
33348     if getstate(current,s_pstf) then 
33349      if trace_steps then
33350       logprocess("reorder reph, before postscript, post base")
33351      end
33352      startnext=getnext(start)
33353      head=remove_node(head,start)
33354      setlink(getprev(current),start)
33355      setlink(start,current)
33356      start=startnext
33357      startattr=getprop(start,a_syllabe)
33358      break
33359     elseif not c and (vowel_modifier[char] or stress_tone_mark[char]) then
33360      c=current
33361     end
33362     current=getnext(current)
33363    else
33364     if c then
33365      if trace_steps then
33366       logprocess("reorder reph, before postscript")
33367      end
33368      startnext=getnext(start)
33369      head=remove_node(head,start)
33370      setlink(getprev(c),start)
33371      setlink(start,c)
33372      start=startnext
33373      startattr=getprop(start,a_syllabe)
33374     end
33375     break
33376    end
33377   end
33378  end
33379 end
33380 ::step_5::
33381 if not startnext then
33382  current=getnext(start)
33383  local c=nil
33384  while current do
33385   local char=ischar(current,startfont)
33386   if char and getprop(current,a_syllabe)==startattr then
33387    local state=getstate(current)
33388    if before_subscript[rephbase] and (state==s_blwf or state==s_pstf) then
33389     c=current
33390     if trace_steps then
33391      logprocess("reorder reph, before subscript")
33392     end
33393    elseif after_subscript[rephbase] and (state==s_pstf) then
33394     if trace_steps then
33395      logprocess("reorder reph, after subscript")
33396     end
33397     c=current
33398    end
33399    current=getnext(current)
33400   else
33401    break
33402   end
33403  end
33404  if c then
33405   startnext=getnext(start)
33406   head=remove_node(head,start)
33407   setlink(getprev(c),start)
33408   setlink(start,c)
33409   start=startnext
33410   startattr=getprop(start,a_syllabe)
33411  end
33412 end
33413 ::step_6::
33414 if not startnext then
33415  current=start
33416  local next=getnext(current)
33417  while next do
33418   local nextchar=ischar(next,startfont)
33419   if nextchar and getprop(next,a_syllabe)==startattr then
33420    current=next
33421    next=getnext(current)
33422   else
33423    break
33424   end
33425  end
33426  if start~=current then
33427   if trace_steps then
33428    logprocess("reorder reph, to end")
33429   end
33430   startnext=getnext(start)
33431   head=remove_node(head,start)
33432   setlink(start,getnext(current))
33433   setlink(current,start)
33434   start=startnext
33435  end
33436 end
33437 return head,start,true
33438end
33439function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start)
33440 if getprop(start,a_reordered) then
33441  return head,start,true
33442 end
33443 local current=start 
33444 local startfont=getfont(start)
33445 local startattr=getprop(start,a_syllabe)
33446 while current do
33447  local char=ischar(current,startfont)
33448  local next=getnext(current)
33449  if char and getprop(current,a_syllabe)==startattr then
33450   if halant[char] then 
33451    if trace_steps then
33452     logprocess("reorder pre base consonants, handle halant")
33453    end
33454    if next then
33455     local char=ischar(next,startfont)
33456     if char and zw_char[char] and getprop(next,a_syllabe)==startattr then
33457      current=next
33458      next=getnext(current)
33459     end
33460    end
33461    local startnext=getnext(start)
33462    head=remove_node(head,start)
33463    setlink(start,next)
33464    setlink(current,start)
33465    setprop(start,"reordered",true)
33466    start=startnext
33467    return head,start,true
33468   end
33469  else
33470   break
33471  end
33472  current=next
33473 end
33474 local startattr=getprop(start,a_syllabe)
33475 local current=getprev(start)
33476 while current and getprop(current,a_syllabe)==startattr do
33477  local char=ischar(current)
33478  if (not dependent_vowel[char] and (not getstate(current) or getstate(current,s_init))) then
33479   if trace_steps then
33480    logprocess("reorder pre base consonants, handle vowel or initial")
33481   end
33482   startnext=getnext(start)
33483   head=remove_node(head,start)
33484   if current==head then
33485    setlink(start,current)
33486    head=start
33487   else
33488    setlink(getprev(current),start)
33489    setlink(start,current)
33490   end
33491   setprop(start,"reordered",true)
33492   start=startnext
33493   break
33494  end
33495  current=getprev(current)
33496 end
33497 return head,start,true
33498end
33499function handlers.devanagari_remove_joiners(head,start,kind,lookupname,replacement)
33500 local stop=getnext(start)
33501 local font=getfont(start)
33502 local last=start
33503 while stop do
33504  local char=ischar(stop,font)
33505  if char and (char==c_zwnj or char==c_zwj) then
33506   last=stop
33507   stop=getnext(stop)
33508  else
33509   break
33510  end
33511 end
33512 local prev=getprev(start)
33513 if stop then
33514  setnext(last)
33515  setlink(prev,stop)
33516 elseif prev then
33517  setnext(prev)
33518 end
33519 if head==start then
33520  head=stop
33521 end
33522 flushlist(start)
33523 if trace_steps then
33524  logprocess("remove joiners")
33525 end
33526 return head,stop,true
33527end
33528local function initialize_two(font,attr)
33529 local devanagari=fontdata[font].resources.devanagari
33530 if devanagari then
33531  return devanagari.seqsubset or {},devanagari.reorderreph or {}
33532 else
33533  return {},{}
33534 end
33535end
33536local function reorder_two(head,start,stop,font,attr,nbspaces) 
33537 local seqsubset,reorderreph=initialize_two(font,attr)
33538 local halfpos=nil
33539 local basepos=nil
33540 local subpos=nil
33541 local postpos=nil
33542 reorderreph.coverage={} 
33543 rephbase[font]={} 
33544 for i=1,#seqsubset do
33545  local subset=seqsubset[i]
33546  local kind=subset[1]
33547  local lookupcache=subset[2]
33548  if kind=="rphf" then
33549   local reph=subset[3]
33550   local base=subset[4]
33551   reorderreph.coverage[reph]=true 
33552   rephbase[font][reph]=base
33553   local current=start
33554   local last=getnext(stop)
33555   while current~=last do
33556    if current~=stop then
33557     local c=getchar(current)
33558     local found=lookupcache[c]
33559     if found then
33560      local next=getnext(current)
33561      if contextchain(found,next) then 
33562       local afternext=next~=stop and getnext(next)
33563       if afternext and zw_char[getchar(afternext)] then 
33564        current=afternext 
33565       elseif current==start then
33566        setstate(current,s_rphf)
33567        current=next 
33568       else
33569        current=next 
33570       end
33571      end
33572     end
33573    end
33574    current=getnext(current)
33575   end
33576  elseif kind=="pref" then
33577   local current=start
33578   local last=getnext(stop)
33579   while current~=last do
33580    if current~=stop then
33581     local c=getchar(current)
33582     local found=lookupcache[c]
33583     if found then 
33584      local next=getnext(current)
33585      if contextchain(found,next) then
33586       if not getstate(current) and not getstate(next) then 
33587        setstate(current,s_pref)
33588        setstate(next,s_pref)
33589        current=next
33590       end
33591      end
33592     end
33593    end
33594    current=getnext(current)
33595   end
33596  elseif kind=="half" then 
33597   local current=start
33598   local last=getnext(stop)
33599   while current~=last do
33600    if current~=stop then
33601     local c=getchar(current)
33602     local found=lookupcache[c]
33603     if found then
33604      local next=getnext(current)
33605      if contextchain(found,next) then
33606       if next~=stop and getchar(getnext(next))==c_zwnj then 
33607        current=next
33608       elseif not getstate(current) then 
33609        setstate(current,s_half)
33610        if not halfpos then
33611         halfpos=current
33612        end
33613       end
33614       current=getnext(current)
33615      end
33616     end
33617    end
33618    current=getnext(current)
33619   end
33620  elseif kind=="blwf" or kind=="vatu" then 
33621   local current=start
33622   local last=getnext(stop)
33623   while current~=last do
33624    if current~=stop then
33625     local c=getchar(current)
33626     local found=lookupcache[c]
33627     if found then
33628      local next=getnext(current)
33629      if contextchain(found,next) then
33630       if not getstate(current) and not getstate(next) then 
33631        setstate(current,s_blwf)
33632        setstate(next,s_blwf)
33633        current=next
33634        subpos=current
33635       end
33636      end
33637     end
33638    end
33639    current=getnext(current)
33640   end
33641  elseif kind=="pstf" then 
33642   local current=start
33643   local last=getnext(stop)
33644   while current~=last do
33645    if current~=stop then
33646     local c=getchar(current)
33647     local found=lookupcache[c]
33648     if found then
33649      local next=getnext(current)
33650      if contextchain(found,next) then
33651       if not getstate(current) and not getstate(next) then 
33652        setstate(current,s_pstf)
33653        setstate(next,s_pstf)
33654        current=next
33655        postpos=current
33656       end
33657      end
33658     end
33659    end
33660    current=getnext(current)
33661   end
33662  end
33663 end
33664 local current,base,firstcons,subnotafterbase,postnotafterbase=start,nil,nil,nil,nil
33665 if getstate(start,s_rphf) then
33666  current=getnext(getnext(start))
33667 end
33668 if current~=getnext(stop) and getchar(current)==c_nbsp then
33669  if current==stop then
33670   stop=getprev(stop)
33671   head=remove_node(head,current)
33672   flushnode(current)
33673   if trace_steps then
33674    logprocess("reorder two, remove nbsp")
33675   end
33676   return head,stop,nbspaces
33677  else
33678   nbspaces=nbspaces+1
33679   base=current
33680   current=getnext(current)
33681   if current~=stop then
33682    local char=getchar(current)
33683    if nukta[char] then
33684     current=getnext(current)
33685     char=getchar(current)
33686    end
33687    if char==c_zwj then
33688     local next=getnext(current)
33689     if current~=stop and next~=stop and halant[getchar(next)] then
33690      current=next
33691      next=getnext(current)
33692      local tmp=getnext(next)
33693      local changestop=next==stop
33694      setnext(next)
33695      setstate(current,s_pref)
33696      current=processcharacters(current,font)
33697      setstate(current,s_blwf)
33698      current=processcharacters(current,font)
33699      setstate(current,s_pstf)
33700      current=processcharacters(current,font)
33701      setstate(current,unsetvalue)
33702      if halant[getchar(current)] then
33703       setnext(getnext(current),tmp)
33704       if show_syntax_errors then
33705        head,current=inject_syntax_error(head,current,char)
33706       end
33707      else
33708       setnext(current,tmp) 
33709       if changestop then
33710        stop=current
33711       end
33712      end
33713     end
33714    end
33715   end
33716   if trace_steps then
33717    logprocess("reorder two, handle nbsp")
33718   end
33719  end
33720 else 
33721  local last=getnext(stop)
33722  while current~=last do 
33723   local next=getnext(current)
33724   if current==subpos then
33725    subnotafterbase=current
33726   end
33727   if current==postpos then
33728    postnotafterbase=current
33729   end
33730   if consonant[getchar(current)] then
33731    if not (current~=stop and next~=stop and halant[getchar(next)] and getchar(getnext(next))==c_zwj) then
33732     if not firstcons then
33733      firstcons=current
33734     end
33735     local a=getstate(current)
33736     if not (a==s_blwf or a==s_pstf or (a~=s_rphf and a~=s_blwf and ra[getchar(current)])) then
33737      base=current
33738      if subnotafterbase then
33739       subpos=base
33740      end
33741      if postnotafterbase then
33742       postpos=base
33743      end
33744     end
33745    end
33746   end
33747   current=next
33748  end
33749  if not base then
33750   base=firstcons
33751  end
33752 end
33753 if not base then
33754  if getstate(start,s_rphf) then
33755   setstate(start,unsetvalue)
33756  end
33757  return head,stop,nbspaces
33758 else
33759  if getstate(base) then 
33760   setstate(base,unsetvalue)  
33761  end
33762  basepos=base
33763 end
33764 if not halfpos then
33765  halfpos=base
33766 end
33767 if not subpos then
33768  subpos=base
33769 end
33770 if not postpos then
33771  postpos=subpos or base
33772 end
33773 local moved={}
33774 local current=start
33775 local last=getnext(stop)
33776 while current~=last do
33777  local char=getchar(current)
33778  local target=nil
33779  local cn=getnext(current)
33780  local tpm=twopart_mark[char]
33781  if tpm then
33782   while tpm do
33783    local extra=copy_node(current)
33784    copyinjection(extra,current)
33785    char=tpm[1]
33786    setchar(current,char)
33787    setchar(extra,tpm[2])
33788    head=insertnodeafter(head,current,extra)
33789    tpm=twopart_mark[char]
33790   end
33791   if tpm and trace_steps then
33792    logprocess("reorder two, handle matra")
33793   end
33794  end
33795   if not moved[current] and dependent_vowel[char] then
33796   if pre_mark[char] then 
33797    moved[current]=true
33798    local prev,next=getboth(current)
33799    setlink(prev,next)
33800    if current==stop then
33801     stop=getprev(current)
33802    end
33803    local pos
33804    if before_main[char] then
33805     pos=basepos
33806    else
33807     pos=halfpos
33808    end
33809    local ppos=getprev(pos) 
33810    while ppos and getprop(ppos,a_syllabe)==getprop(pos,a_syllabe) do
33811     if getstate(ppos,s_pref) then
33812      pos=ppos
33813     end
33814     ppos=getprev(ppos)
33815    end
33816    local ppos=getprev(pos) 
33817    while ppos and getprop(ppos,a_syllabe)==getprop(pos,a_syllabe) and halant[ischar(ppos)] do
33818     ppos=getprev(ppos)
33819     if ppos and getprop(ppos,a_syllabe)==getprop(pos,a_syllabe) and consonant[ischar(ppos)] then
33820      pos=ppos
33821      ppos=getprev(ppos)
33822     else
33823      break
33824     end
33825    end
33826    if pos==start then
33827     if head==start then
33828      head=current
33829     end
33830     start=current
33831    end
33832    setlink(getprev(pos),current)
33833    setlink(current,pos)
33834    if trace_steps then
33835     logprocess("reorder two, handle pre mark")
33836    end
33837   elseif above_mark[char] then
33838    target=subpos
33839    if postpos==subpos then
33840     postpos=current
33841    end
33842    subpos=current
33843   elseif below_mark[char] then
33844    target=subpos
33845    if postpos==subpos then
33846     postpos=current
33847    end
33848    subpos=current
33849   elseif post_mark[char] then
33850    local n=getnext(postpos) 
33851    while n do
33852     local v=ischar(n,font)
33853     if nukta[v] or stress_tone_mark[v] or vowel_modifier[v] then
33854      postpos=n
33855     else
33856      break
33857     end
33858     n=getnext(n)
33859    end
33860    target=postpos
33861    postpos=current
33862   end
33863   if mark_above_below_post[char] then
33864    local prev=getprev(current)
33865    if prev~=target then
33866     local next=getnext(current)
33867     setlink(prev,next)
33868     if current==stop then
33869      stop=prev
33870     end
33871     setlink(current,getnext(target))
33872     setlink(target,current)
33873     if trace_steps then
33874      logprocess("reorder two, handle mark")
33875     end
33876    end
33877   end
33878  end
33879  current=cn
33880 end
33881 local current=getnext(start)
33882 local last=getnext(stop)
33883 while current~=last do
33884  local char=getchar(current)
33885  local cn=getnext(current)
33886  if halant[char] and ra[ischar(cn)] and (not getstate(cn,s_rphf)) and (not getstate(cn,s_blwf)) then
33887   if after_main[ischar(cn)] then
33888    local prev=getprev(current)
33889    local next=getnext(cn)
33890    local bpn=getnext(basepos)
33891    while bpn and dependent_vowel[ischar(bpn)] do
33892     basepos=bpn
33893     bpn=getnext(bpn)
33894    end
33895    if basepos~=prev then
33896     setlink(prev,next)
33897     setlink(cn,getnext(basepos))
33898     setlink(basepos,current)
33899     if cn==stop then
33900      stop=prev
33901     end
33902     cn=next
33903     if trace_steps then
33904      logprocess("reorder two, handle halant and ra")
33905     end
33906    end
33907   end
33908  end
33909  current=cn
33910 end
33911 local current=start
33912 local c=nil
33913 while current~=stop do
33914  local char=getchar(current)
33915  if halant[char] or stress_tone_mark[char] then
33916   if not c then
33917    c=current
33918   end
33919  else
33920   c=nil
33921  end
33922  local next=getnext(current)
33923  if c and nukta[getchar(next)] then
33924   if head==c then
33925    head=next
33926   end
33927   if stop==next then
33928    stop=current
33929   end
33930   setlink(getprev(c),next)
33931   local nextnext=getnext(next)
33932   setnext(current,nextnext)
33933   local nextnextnext=getnext(nextnext)
33934   if nextnextnext then
33935    setprev(nextnextnext,current)
33936   end
33937   setlink(nextnext,c)
33938   if trace_steps then
33939    logprocess("reorder two, handle nukta")
33940   end
33941  end
33942  if stop==current then break end
33943  current=getnext(current)
33944 end
33945 if getchar(base)==c_nbsp then
33946  if base==stop then
33947   stop=getprev(stop)
33948  end
33949  nbspaces=nbspaces-1
33950  head=remove_node(head,base)
33951  flushnode(base)
33952  if trace_steps then
33953   logprocess("reorder two, handle nbsp")
33954  end
33955 end
33956 return head,stop,nbspaces
33957end
33958local separator={}
33959imerge(separator,consonant)
33960imerge(separator,independent_vowel)
33961imerge(separator,dependent_vowel)
33962imerge(separator,vowel_modifier)
33963imerge(separator,stress_tone_mark)
33964for k,v in next,nukta  do separator[k]=true end
33965for k,v in next,halant do separator[k]=true end
33966local function analyze_next_chars_one(c,font,variant)
33967 local n=getnext(c)
33968 if not n then
33969  return c
33970 end
33971 local v=ischar(n,font)
33972 if variant==1 then
33973  if v and nukta[v] then
33974   n=getnext(n)
33975   if n then
33976    v=ischar(n,font)
33977   end
33978  end
33979  if n and v then
33980   local nn=getnext(n)
33981   if nn then
33982    local vv=ischar(nn,font)
33983    if vv then
33984     local nnn=getnext(nn)
33985     if nnn then
33986      local vvv=ischar(nnn,font)
33987      if vvv then
33988       if vv==c_zwj and consonant[vvv] then
33989        c=nnn
33990       elseif (vv==c_zwnj or vv==c_zwj) and halant[vvv] then
33991        local nnnn=getnext(nnn)
33992        if nnnn then
33993         local vvvv=ischar(nnnn,font)
33994         if vvvv and consonant[vvvv] then
33995          c=nnnn
33996         end
33997        end
33998       end
33999      end
34000     end
34001    end
34002   end
34003  end
34004 elseif variant==2 then
34005  if v and nukta[v] then
34006   c=n
34007  end
34008  n=getnext(c)
34009  if n then
34010   v=ischar(n,font)
34011   if v then
34012    local nn=getnext(n)
34013    if nn then
34014     local vv=ischar(nn,font)
34015     if vv and zw_char[v] then
34016      n=nn
34017      v=vv
34018      nn=getnext(nn)
34019      vv=nn and ischar(nn,font)
34020     end
34021     if vv and halant[v] and consonant[vv] then
34022      c=nn
34023     end
34024    end
34025   end
34026  end
34027 end
34028 n=getnext(c)
34029 if not n then
34030  return c
34031 end
34032 v=ischar(n,font)
34033 if not v then
34034  return c
34035 end
34036 local already_pre_mark   
34037 local already_above_mark 
34038 local already_below_mark 
34039 local already_post_mark  
34040 while dependent_vowel[v] do
34041  local vowels=twopart_mark[v]
34042  if vowels then
34043   for k=1,#vowels do
34044    local v=vowels[k]
34045    if pre_mark[v] and not already_pre_mark then
34046     already_pre_mark=true
34047    elseif above_mark[v] and not already_above_mark then
34048     already_above_mark=true
34049    elseif below_mark[v] and not already_below_mark then
34050     already_below_mark=true
34051    elseif post_mark[v] and not already_post_mark then
34052     already_post_mark=true
34053    elseif devanagarihash[font].conjuncts=="continue" then
34054    else
34055     return c
34056    end
34057   end
34058  else
34059   if pre_mark[v] and not already_pre_mark then
34060    already_pre_mark=true
34061   elseif post_mark[v] and not already_post_mark then
34062     already_post_mark=true
34063   elseif below_mark[v] and not already_below_mark then
34064    already_below_mark=true
34065   elseif above_mark[v] and not already_above_mark then
34066    already_above_mark=true
34067   elseif devanagarihash[font].conjuncts=="continue" then
34068   else
34069    return c
34070   end
34071  end
34072  c=n
34073  n=getnext(c)
34074  if not n then
34075   return c
34076  end
34077  v=ischar(n,font)
34078  if not v then
34079   return c
34080  end
34081 end
34082 if nukta[v] then
34083  c=n
34084  n=getnext(c)
34085  if not n then
34086   return c
34087  end
34088  v=ischar(n,font)
34089  if not v then
34090   return c
34091  end
34092 end
34093 if halant[v] then
34094  c=n
34095  n=getnext(c)
34096  if not n then
34097   return c
34098  end
34099  v=ischar(n,font)
34100  if not v then
34101   return c
34102  end
34103 end
34104 if vowel_modifier[v] then
34105  c=n
34106  n=getnext(c)
34107  if not n then
34108   return c
34109  end
34110  v=ischar(n,font)
34111  if not v then
34112   return c
34113  end
34114 end
34115 if stress_tone_mark[v] then
34116  c=n
34117  n=getnext(c)
34118  if not n then
34119   return c
34120  end
34121  v=ischar(n,font)
34122  if not v then
34123   return c
34124  end
34125 end
34126 if stress_tone_mark[v] then
34127  return n
34128 else
34129  return c
34130 end
34131end
34132local function analyze_next_chars_two(c,font)
34133 local n=getnext(c)
34134 if not n then
34135  return c
34136 end
34137 local v=ischar(n,font)
34138 if v and nukta[v] then
34139  c=n
34140 end
34141 n=c
34142 while true do
34143  local nn=getnext(n)
34144  if nn then
34145   local vv=ischar(nn,font)
34146   if vv then
34147    if halant[vv] then
34148     n=nn
34149     local nnn=getnext(nn)
34150     if nnn then
34151      local vvv=ischar(nnn,font)
34152      if vvv and zw_char[vvv] then
34153       n=nnn
34154      end
34155     end
34156    elseif vv==c_zwnj or vv==c_zwj then
34157     local nnn=getnext(nn)
34158     if nnn then
34159      local vvv=ischar(nnn,font)
34160      if vvv and halant[vvv] then
34161       n=nnn
34162      end
34163     end
34164    else
34165     break
34166    end
34167    local nn=getnext(n)
34168    if nn then
34169     local vv=ischar(nn,font)
34170     if vv and consonant[vv] then
34171      n=nn
34172      local nnn=getnext(nn)
34173      if nnn then
34174       local vvv=ischar(nnn,font)
34175       if vvv and nukta[vvv] then
34176        n=nnn
34177       end
34178      end
34179      c=n
34180     else
34181      break
34182     end
34183    else
34184     break
34185    end
34186   else
34187    break
34188   end
34189  else
34190   break
34191  end
34192 end
34193 if not c then
34194  return
34195 end
34196 n=getnext(c)
34197 if not n then
34198  return c
34199 end
34200 v=ischar(n,font)
34201 if not v then
34202  return c
34203 end
34204 if anudatta[v] then
34205  c=n
34206  n=getnext(c)
34207  if not n then
34208   return c
34209  end
34210  v=ischar(n,font)
34211  if not v then
34212   return c
34213  end
34214 end
34215 if halant[v] then
34216  c=n
34217  n=getnext(c)
34218  if not n then
34219   return c
34220  end
34221  v=ischar(n,font)
34222  if not v then
34223   return c
34224  end
34225  if v==c_zwnj or v==c_zwj then
34226   c=n
34227   n=getnext(c)
34228   if not n then
34229    return c
34230   end
34231   v=ischar(n,font)
34232   if not v then
34233    return c
34234   end
34235  end
34236 else
34237  local already_pre_mark   
34238  local already_above_mark 
34239  local already_below_mark 
34240  local already_post_mark
34241  while dependent_vowel[v] do
34242   local vowels=twopart_mark[v]
34243   if vowels then
34244    for k=1,#vowels do
34245     local v=vowels[k]
34246     if pre_mark[v] and not already_pre_mark then
34247      already_pre_mark=true
34248     elseif above_mark[v] and not already_above_mark then
34249      already_above_mark=true
34250     elseif below_mark[v] and not already_below_mark then
34251      already_below_mark=true
34252     elseif post_mark[v] and not already_post_mark then
34253      already_post_mark=true
34254     elseif devanagarihash[font].conjuncts=="continue" then
34255     else
34256      return c
34257     end
34258    end
34259   else
34260    if pre_mark[v] and not already_pre_mark then
34261     already_pre_mark=true
34262    elseif post_mark[v] and not already_post_mark then
34263        already_post_mark=true
34264    elseif below_mark[v] and not already_below_mark then
34265     already_below_mark=true
34266    elseif above_mark[v] and not already_above_mark then
34267     already_above_mark=true
34268    elseif devanagarihash[font].conjuncts=="continue" then
34269    else
34270     return c
34271    end
34272   end
34273   c=n
34274   n=getnext(c)
34275   if not n then
34276    return c
34277   end
34278   v=ischar(n,font)
34279   if not v then
34280    return c
34281   end
34282  end
34283  if nukta[v] then
34284   c=n
34285   n=getnext(c)
34286   if not n then
34287    return c
34288   end
34289   v=ischar(n,font)
34290   if not v then
34291    return c
34292   end
34293  end
34294  if halant[v] then
34295   c=n
34296   n=getnext(c)
34297   if not n then
34298    return c
34299   end
34300   v=ischar(n,font)
34301   if not v then
34302    return c
34303   end
34304  end
34305 end
34306 if vowel_modifier[v] then
34307  c=n
34308  n=getnext(c)
34309  if not n then
34310   return c
34311  end
34312  v=ischar(n,font)
34313  if not v then
34314   return c
34315  end
34316 end
34317 if stress_tone_mark[v] then
34318  c=n
34319  n=getnext(c)
34320  if not n then
34321   return c
34322  end
34323  v=ischar(n,font)
34324  if not v then
34325   return c
34326  end
34327 end
34328 if stress_tone_mark[v] then
34329  return n
34330 else
34331  return c
34332 end
34333end
34334local function method_one(head,font,attr)
34335 local current=head
34336 local start=true
34337 local done=false
34338 local nbspaces=0
34339 local syllabe=0
34340 while current do
34341  local char=ischar(current,font)
34342  if char then
34343   done=true
34344   local syllablestart=current
34345   local syllableend=nil
34346   local c=current
34347   local n=getnext(c)
34348   local first=char
34349   if n and ra[first] then
34350    local second=ischar(n,font)
34351    if second and halant[second] then
34352     local n=getnext(n)
34353     if n then
34354      local third=ischar(n,font)
34355      if third then
34356       c=n
34357       first=third
34358      end
34359     end
34360    end
34361   end
34362   local standalone=first==c_nbsp
34363   if standalone then
34364    local prev=getprev(current)
34365    if prev then
34366     local prevchar=ischar(prev,font)
34367     if not prevchar then
34368     elseif not separator[prevchar] then
34369     else
34370      standalone=false
34371     end
34372    else
34373    end
34374   end
34375   if standalone then
34376    local syllableend=analyze_next_chars_one(c,font,2)
34377    current=getnext(syllableend)
34378    if syllablestart~=syllableend then
34379     head,current,nbspaces=reorder_one(head,syllablestart,syllableend,font,attr,nbspaces)
34380     current=getnext(current)
34381    end
34382   else
34383    if consonant[char] then
34384     local prevc=true
34385     while prevc do
34386      prevc=false
34387      local n=getnext(current)
34388      if not n then
34389       break
34390      end
34391      local v=ischar(n,font)
34392      if not v then
34393       break
34394      end
34395      if nukta[v] then
34396       n=getnext(n)
34397       if not n then
34398        break
34399       end
34400       v=ischar(n,font)
34401       if not v then
34402        break
34403       end
34404      end
34405      if halant[v] then
34406       n=getnext(n)
34407       if not n then
34408        break
34409       end
34410       v=ischar(n,font)
34411       if not v then
34412        break
34413       end
34414       if v==c_zwnj or v==c_zwj then
34415        n=getnext(n)
34416        if not n then
34417         break
34418        end
34419        v=ischar(n,font)
34420        if not v then
34421         break
34422        end
34423       end
34424       if consonant[v] then
34425        prevc=true
34426        current=n
34427       end
34428      end
34429     end
34430     local n=getnext(current)
34431     if n then
34432      local v=ischar(n,font)
34433      if v and nukta[v] then
34434       current=n
34435       n=getnext(current)
34436      end
34437     end
34438     syllableend=current
34439     current=n
34440     if current then
34441      local v=ischar(current,font)
34442      if not v then
34443      elseif halant[v] then
34444       local n=getnext(current)
34445       if n then
34446        local v=ischar(n,font)
34447        if v and zw_char[v] then
34448         syllableend=n
34449         current=getnext(n)
34450        else
34451         syllableend=current
34452         current=n
34453        end
34454       else
34455        syllableend=current
34456        current=n
34457       end
34458      else
34459       if dependent_vowel[v] then
34460        syllableend=current
34461        current=getnext(current)
34462        v=ischar(current,font)
34463       end
34464       if v and vowel_modifier[v] then
34465        syllableend=current
34466        current=getnext(current)
34467        v=ischar(current,font)
34468       end
34469       if v and stress_tone_mark[v] then
34470        syllableend=current
34471        current=getnext(current)
34472       end
34473      end
34474     end
34475     if syllablestart~=syllableend then
34476      if syllableend then
34477       syllabe=syllabe+1
34478       local c=syllablestart
34479       local n=getnext(syllableend)
34480       while c~=n do
34481        setprop(c,a_syllabe,syllabe)
34482        c=getnext(c)
34483       end
34484      end
34485      head,current,nbspaces=reorder_one(head,syllablestart,syllableend,font,attr,nbspaces)
34486      current=getnext(current)
34487     end
34488    elseif independent_vowel[char] then
34489     syllableend=current
34490     current=getnext(current)
34491     if current then
34492      local v=ischar(current,font)
34493      if v then
34494       if vowel_modifier[v] then
34495        syllableend=current
34496        current=getnext(current)
34497        v=ischar(current,font)
34498       end
34499       if v and stress_tone_mark[v] then
34500        syllableend=current
34501        current=getnext(current)
34502       end
34503      end
34504     end
34505    else
34506     if show_syntax_errors then
34507      local mark=mark_pre_above_below_post[char]
34508      if mark then
34509       head,current=inject_syntax_error(head,current,char)
34510      end
34511     end
34512     current=getnext(current)
34513    end
34514   end
34515  else
34516   current=getnext(current)
34517  end
34518  start=false
34519 end
34520 if nbspaces>0 then
34521  head=replace_all_nbsp(head)
34522 end
34523 current=head
34524 local n=0
34525 while current do
34526  local char=ischar(current,font)
34527  if char then
34528   if n==0 and not getstate(current) then
34529    setstate(current,s_init)
34530   end
34531   n=n+1
34532  else
34533   n=0
34534  end
34535  current=getnext(current)
34536 end
34537 return head,done
34538end
34539local function method_two(head,font,attr)
34540 local current=head
34541 local start=true
34542 local done=false
34543 local syllabe=0
34544 local nbspaces=0
34545 while current do
34546  local syllablestart=nil
34547  local syllableend=nil
34548  local char=ischar(current,font)
34549  if char then
34550   done=true
34551   syllablestart=current
34552   local c=current
34553   local n=getnext(current)
34554   if n and ra[char] then
34555    local nextchar=ischar(n,font)
34556    if nextchar and halant[nextchar] then
34557     local n=getnext(n)
34558     if n then
34559      local nextnextchar=ischar(n,font)
34560      if nextnextchar then
34561       c=n
34562       char=nextnextchar
34563      end
34564     end
34565    end
34566   end
34567   if independent_vowel[char] then
34568    current=analyze_next_chars_one(c,font,1)
34569    syllableend=current
34570   else
34571    local standalone=char==c_nbsp
34572    if standalone then
34573     nbspaces=nbspaces+1
34574     local p=getprev(current)
34575     if not p then
34576     elseif ischar(p,font) then
34577     elseif not separator[getchar(p)] then
34578     else
34579      standalone=false
34580     end
34581    end
34582    if standalone then
34583     current=analyze_next_chars_one(c,font,2)
34584     syllableend=current
34585    elseif consonant[getchar(current)] then
34586     current=analyze_next_chars_two(current,font) 
34587     syllableend=current
34588    end
34589   end
34590  end
34591  if syllableend then
34592   syllabe=syllabe+1
34593   local c=syllablestart
34594   local n=getnext(syllableend)
34595   while c~=n do
34596    setprop(c,a_syllabe,syllabe)
34597    c=getnext(c)
34598   end
34599  end
34600  if syllableend and syllablestart~=syllableend then
34601   head,current,nbspaces=reorder_two(head,syllablestart,syllableend,font,attr,nbspaces)
34602  end
34603  if not syllableend and show_syntax_errors then
34604   local char=ischar(current,font)
34605   if char and not getstate(current) then 
34606    local mark=mark_pre_above_below_post[char]
34607    if mark then
34608     head,current=inject_syntax_error(head,current,char)
34609    end
34610   end
34611  end
34612  start=false
34613  current=getnext(current)
34614 end
34615 if nbspaces>0 then
34616  head=replace_all_nbsp(head)
34617 end
34618 current=head
34619 local n=0
34620 while current do
34621  local char=ischar(current,font)
34622  if char then
34623   if n==0 and not getstate(current) then 
34624    setstate(current,s_init)
34625   end
34626   n=n+1
34627  else
34628   n=0
34629  end
34630  current=getnext(current)
34631 end
34632 return head,done
34633end
34634for i=1,nofscripts do
34635 methods[scripts_one[i]]=method_one
34636 methods[scripts_two[i]]=method_two
34637end
34638
34639end -- closure
34640
34641do -- begin closure to overcome local limits and interference
34642
34643if not modules then modules={} end modules ['font-ocl']={
34644 version=1.001,
34645 comment="companion to font-ini.mkiv",
34646 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
34647 copyright="PRAGMA ADE / ConTeXt Development Team",
34648 license="see context related readme files"
34649}
34650local tostring,tonumber,next=tostring,tonumber,next
34651local round,max=math.round,math.round
34652local gsub,find=string.gsub,string.find
34653local sortedkeys,sortedhash,concat=table.sortedkeys,table.sortedhash,table.concat
34654local setmetatableindex=table.setmetatableindex
34655local formatters=string.formatters
34656local tounicode=fonts.mappings.tounicode
34657local helpers=fonts.helpers
34658local charcommand=helpers.commands.char
34659local rightcommand=helpers.commands.right
34660local leftcommand=helpers.commands.left
34661local downcommand=helpers.commands.down
34662local otf=fonts.handlers.otf
34663local otfregister=otf.features.register
34664local f_color=formatters["%.3f %.3f %.3f rg"]
34665local f_gray=formatters["%.3f g"]
34666if context then
34667
34668--removed
34669
34670else
34671 local tounicode=fonts.mappings.tounicode16
34672 function otf.getactualtext(s)
34673  return
34674   "/Span << /ActualText <feff"..s.."> >> BDC",
34675   "EMC"
34676 end
34677end
34678local sharedpalettes={}
34679local hash=setmetatableindex(function(t,k)
34680 local v={ "pdf","direct",k }
34681 t[k]=v
34682 return v
34683end)
34684if context then
34685
34686--removed
34687
34688else 
34689 function otf.registerpalette(name,values)
34690  sharedpalettes[name]=values
34691  for i=1,#values do
34692   local v=values[i]
34693   if v then
34694    values[i]=hash[f_color(
34695     max(round((v.r or 0)*255),255)/255,
34696     max(round((v.g or 0)*255),255)/255,
34697     max(round((v.b or 0)*255),255)/255
34698    )]
34699   end
34700  end
34701 end
34702end
34703local function convert(t,k)
34704 local v={}
34705 for i=1,#k do
34706  local p=k[i]
34707  local r,g,b=p[1],p[2],p[3]
34708  if r==g and g==b then
34709   v[i]=hash[f_gray(r/255)]
34710  else
34711   v[i]=hash[f_color(r/255,g/255,b/255)]
34712  end
34713 end
34714 t[k]=v
34715 return v
34716end
34717local mode={ "pdf","mode","font" }
34718local push={ "pdf","page","q" }
34719local pop={ "pdf","page","Q" }
34720local function initializeoverlay(tfmdata,kind,value)
34721 if value then
34722  local resources=tfmdata.resources
34723  local palettes=resources.colorpalettes
34724  if palettes then
34725   local converted=resources.converted
34726   if not converted then
34727    converted=setmetatableindex(convert)
34728    resources.converted=converted
34729   end
34730   local colorvalues=sharedpalettes[value]
34731   local default=false 
34732   if colorvalues then
34733    default=colorvalues[#colorvalues]
34734   else
34735    colorvalues=converted[palettes[tonumber(value) or 1] or palettes[1]] or {}
34736   end
34737   local classes=#colorvalues
34738   if classes==0 then
34739    return
34740   end
34741   local characters=tfmdata.characters
34742   local descriptions=tfmdata.descriptions
34743   local properties=tfmdata.properties
34744   properties.virtualized=true
34745   tfmdata.fonts={
34746    { id=0 }
34747   }
34748   local getactualtext=otf.getactualtext
34749   local b,e=getactualtext(tounicode(0xFFFD))
34750   local actualb={ "pdf","page",b } 
34751   local actuale={ "pdf","page",e }
34752   for unicode,character in next,characters do
34753    local description=descriptions[unicode]
34754    if description then
34755     local colorlist=description.colors
34756     if colorlist then
34757      local u=description.unicode or characters[unicode].unicode
34758      local w=character.width or 0
34759      local s=#colorlist
34760      local goback=w~=0 and leftcommand[w] or nil 
34761      local t={
34762       mode,
34763       not u and actualb or { "pdf","page",(getactualtext(tounicode(u))) },
34764       push,
34765      }
34766      local n=3
34767      local l=nil
34768      for i=1,s do
34769       local entry=colorlist[i]
34770       local v=colorvalues[entry.class] or default
34771       if v and l~=v then
34772        n=n+1 t[n]=v
34773        l=v
34774       end
34775       n=n+1 t[n]=charcommand[entry.slot]
34776       if s>1 and i<s and goback then
34777        n=n+1 t[n]=goback
34778       end
34779      end
34780      n=n+1 t[n]=pop
34781      n=n+1 t[n]=actuale
34782      character.commands=t
34783     end
34784    end
34785   end
34786   return true
34787  end
34788 end
34789end
34790otfregister {
34791 name="colr",
34792 description="color glyphs",
34793 manipulators={
34794  base=initializeoverlay,
34795  node=initializeoverlay,
34796 }
34797}
34798do
34799 local nofstreams=0
34800 local f_name=formatters[ [[pdf-glyph-%05i]] ]
34801 local f_used=context and formatters[ [[original:///%s]] ] or formatters[ [[%s]] ]
34802 local hashed={}
34803 local cache={}
34804 local openpdf=pdfe.new
34805 function otf.storepdfdata(pdf)
34806  local done=hashed[pdf]
34807  if not done then
34808   nofstreams=nofstreams+1
34809   local f=f_name(nofstreams)
34810   local n=openpdf(pdf,#pdf,f)
34811   done=f_used(n)
34812   hashed[pdf]=done
34813  end
34814  return done
34815 end
34816end
34817local function pdftovirtual(tfmdata,pdfshapes,kind) 
34818 if not tfmdata or not pdfshapes or not kind then
34819  return
34820 end
34821 local characters=tfmdata.characters
34822 local properties=tfmdata.properties
34823 local parameters=tfmdata.parameters
34824 local hfactor=parameters.hfactor
34825 properties.virtualized=true
34826 tfmdata.fonts={
34827  { id=0 } 
34828 }
34829 local getactualtext=otf.getactualtext
34830 local storepdfdata=otf.storepdfdata
34831 local b,e=getactualtext(tounicode(0xFFFD))
34832 local actualb={ "pdf","page",b } 
34833 local actuale={ "pdf","page",e }
34834 local vfimage=lpdf and lpdf.vfimage or function(wd,ht,dp,data,name)
34835  local name=storepdfdata(data)
34836  return { "image",{ filename=name,width=wd,height=ht,depth=dp } }
34837 end
34838 for unicode,character in sortedhash(characters) do  
34839  local index=character.index
34840  if index then
34841   local pdf=pdfshapes[index]
34842   local typ=type(pdf)
34843   local data=nil
34844   local dx=nil
34845   local dy=nil
34846   local scale=1
34847   if typ=="table" then
34848    data=pdf.data
34849    dx=pdf.x or pdf.dx or 0
34850    dy=pdf.y or pdf.dy or 0
34851    scale=pdf.scale or 1
34852   elseif typ=="string" then
34853    data=pdf
34854    dx=0
34855    dy=0
34856   elseif typ=="number" then
34857    data=pdf
34858    dx=0
34859    dy=0
34860   end
34861   if data then
34862    local bt=unicode and getactualtext(unicode)
34863    local wd=character.width  or 0
34864    local ht=character.height or 0
34865    local dp=character.depth  or 0
34866    character.commands={
34867     not unicode and actualb or { "pdf","page",(getactualtext(unicode)) },
34868     downcommand [dp+dy*hfactor],
34869     rightcommand[  dx*hfactor],
34870     vfimage(scale*wd,ht,dp,data,pdfshapes.filename or ""),
34871     actuale,
34872    }
34873    character[kind]=true
34874   end
34875  end
34876 end
34877end
34878local otfsvg=otf.svg or {}
34879otf.svg=otfsvg
34880otf.svgenabled=true
34881do
34882 local report_svg=logs.reporter("fonts","svg conversion")
34883 local loaddata=io.loaddata
34884 local savedata=io.savedata
34885 local remove=os.remove
34886if context then
34887
34888--removed
34889
34890else
34891  function otfsvg.filterglyph(entry,index) 
34892   return entry.data
34893  end
34894end
34895 local runner=sandbox and sandbox.registerrunner {
34896  name="otfsvg",
34897  program="inkscape",
34898  method="pipeto",
34899  template="--export-area-drawing --shell > temp-otf-svg-shape.log",
34900  reporter=report_svg,
34901 }
34902 if not runner then
34903  runner=function()
34904   return io.popen("inkscape --export-area-drawing --shell > temp-otf-svg-shape.log","w")
34905  end
34906 end
34907 local new=nil
34908 local function inkscapeformat(suffix)
34909  if new==nil then
34910   new=os.resultof("inkscape --version") or ""
34911   new=new=="" or not find(new,"Inkscape%s*0")
34912  end
34913  return new and "filename" or suffix
34914 end
34915 function otfsvg.topdf(svgshapes,tfmdata)
34916  local pdfshapes={}
34917  local inkscape=runner()
34918  if inkscape then
34919   local descriptions=tfmdata.descriptions
34920   local nofshapes=#svgshapes
34921   local s_format=inkscapeformat("pdf") 
34922   local f_svgfile=formatters["temp-otf-svg-shape-%i.svg"]
34923   local f_pdffile=formatters["temp-otf-svg-shape-%i.pdf"]
34924   local f_convert=formatters[new and "file-open:%s; export-%s:%s; export-do\n" or "%s --export-%s=%s\n"]
34925   local filterglyph=otfsvg.filterglyph
34926   local nofdone=0
34927   local processed={}
34928   report_svg("processing %i svg containers",nofshapes)
34929   statistics.starttiming()
34930   for i=1,nofshapes do
34931    local entry=svgshapes[i]
34932    for index=entry.first,entry.last do
34933     local data=filterglyph(entry,index)
34934     if data and data~="" then
34935      local svgfile=f_svgfile(index)
34936      local pdffile=f_pdffile(index)
34937      savedata(svgfile,data)
34938      inkscape:write(f_convert(svgfile,s_format,pdffile))
34939      processed[index]=true
34940      nofdone=nofdone+1
34941      if nofdone%25==0 then
34942       report_svg("%i shapes submitted",nofdone)
34943      end
34944     end
34945    end
34946   end
34947   if nofdone%25~=0 then
34948    report_svg("%i shapes submitted",nofdone)
34949   end
34950   report_svg("processing can be going on for a while")
34951   inkscape:write("quit\n")
34952   inkscape:close()
34953   report_svg("processing %i pdf results",nofshapes)
34954   for index in next,processed do
34955    local svgfile=f_svgfile(index)
34956    local pdffile=f_pdffile(index)
34957    local pdfdata=loaddata(pdffile)
34958    if pdfdata and pdfdata~="" then
34959     pdfshapes[index]={
34960      data=pdfdata,
34961     }
34962    end
34963    remove(svgfile)
34964    remove(pdffile)
34965   end
34966   local characters=tfmdata.characters
34967   for k,v in next,characters do
34968    local d=descriptions[k]
34969    local i=d.index
34970    if i then
34971     local p=pdfshapes[i]
34972     if p then
34973      local w=d.width
34974      local l=d.boundingbox[1]
34975      local r=d.boundingbox[3]
34976      p.scale=(r-l)/w
34977      p.x=l
34978     end
34979    end
34980   end
34981   if not next(pdfshapes) then
34982    report_svg("there are no converted shapes, fix your setup")
34983   end
34984   statistics.stoptiming()
34985   if statistics.elapsedseconds then
34986    report_svg("svg conversion time %s",statistics.elapsedseconds() or "-")
34987   end
34988  end
34989  return pdfshapes
34990 end
34991end
34992local function initializesvg(tfmdata,kind,value) 
34993 if value and otf.svgenabled then
34994  local svg=tfmdata.properties.svg
34995  local hash=svg and svg.hash
34996  local timestamp=svg and svg.timestamp
34997  if not hash then
34998   return
34999  end
35000  local pdffile=containers.read(otf.pdfcache,hash)
35001  local pdfshapes=pdffile and pdffile.pdfshapes
35002  if not pdfshapes or pdffile.timestamp~=timestamp or not next(pdfshapes) then
35003   local svgfile=containers.read(otf.svgcache,hash)
35004   local svgshapes=svgfile and svgfile.svgshapes
35005   pdfshapes=svgshapes and otfsvg.topdf(svgshapes,tfmdata,otf.pdfcache.writable,hash) or {}
35006   containers.write(otf.pdfcache,hash,{
35007    pdfshapes=pdfshapes,
35008    timestamp=timestamp,
35009   })
35010  end
35011  pdftovirtual(tfmdata,pdfshapes,"svg")
35012  return true
35013 end
35014end
35015otfregister {
35016 name="svg",
35017 description="svg glyphs",
35018 manipulators={
35019  base=initializesvg,
35020  node=initializesvg,
35021 }
35022}
35023local otfpng=otf.png or {}
35024otf.png=otfpng
35025otf.pngenabled=true
35026do
35027 local report_png=logs.reporter("fonts","png conversion")
35028 local loaddata=io.loaddata
35029 local savedata=io.savedata
35030 local remove=os.remove
35031 local runner=sandbox and sandbox.registerrunner {
35032  name="otfpng",
35033  program="gm",
35034  template="convert -quality 100 temp-otf-png-shape.png temp-otf-png-shape.pdf > temp-otf-svg-shape.log",
35035 }
35036 if not runner then
35037  runner=function()
35038   return os.execute("gm convert -quality 100 temp-otf-png-shape.png temp-otf-png-shape.pdf > temp-otf-svg-shape.log")
35039  end
35040 end
35041 function otfpng.topdf(pngshapes)
35042  local pdfshapes={}
35043  local pngfile="temp-otf-png-shape.png"
35044  local pdffile="temp-otf-png-shape.pdf"
35045  local nofdone=0
35046  local indices=sortedkeys(pngshapes) 
35047  local nofindices=#indices
35048  report_png("processing %i png containers",nofindices)
35049  statistics.starttiming()
35050  for i=1,nofindices do
35051   local index=indices[i]
35052   local entry=pngshapes[index]
35053   local data=entry.data 
35054   local x=entry.x
35055   local y=entry.y
35056   savedata(pngfile,data)
35057   runner()
35058   pdfshapes[index]={
35059    x=x~=0 and x or nil,
35060    y=y~=0 and y or nil,
35061    data=loaddata(pdffile),
35062   }
35063   nofdone=nofdone+1
35064   if nofdone%100==0 then
35065    report_png("%i shapes processed",nofdone)
35066   end
35067  end
35068  report_png("processing %i pdf results",nofindices)
35069  remove(pngfile)
35070  remove(pdffile)
35071  statistics.stoptiming()
35072  if statistics.elapsedseconds then
35073   report_png("png conversion time %s",statistics.elapsedseconds() or "-")
35074  end
35075  return pdfshapes
35076 end
35077end
35078local function initializepng(tfmdata,kind,value) 
35079 if value and otf.pngenabled then
35080  local png=tfmdata.properties.png
35081  local hash=png and png.hash
35082  local timestamp=png and png.timestamp
35083  if not hash then
35084   return
35085  end
35086  local pdffile=containers.read(otf.pdfcache,hash)
35087  local pdfshapes=pdffile and pdffile.pdfshapes
35088  if not pdfshapes or pdffile.timestamp~=timestamp then
35089   local pngfile=containers.read(otf.pngcache,hash)
35090   local pngshapes=pngfile and pngfile.pngshapes
35091   pdfshapes=pngshapes and otfpng.topdf(pngshapes) or {}
35092   containers.write(otf.pdfcache,hash,{
35093    pdfshapes=pdfshapes,
35094    timestamp=timestamp,
35095   })
35096  end
35097  pdftovirtual(tfmdata,pdfshapes,"png")
35098  return true
35099 end
35100end
35101otfregister {
35102 name="sbix",
35103 description="sbix glyphs",
35104 manipulators={
35105  base=initializepng,
35106  node=initializepng,
35107 }
35108}
35109otfregister {
35110 name="cblc",
35111 description="cblc glyphs",
35112 manipulators={
35113  base=initializepng,
35114  node=initializepng,
35115 }
35116}
35117if context then
35118
35119--removed
35120
35121end
35122
35123end -- closure
35124
35125do -- begin closure to overcome local limits and interference
35126
35127if not modules then modules={} end modules ['font-onr']={
35128 version=1.001,
35129 optimize=true,
35130 comment="companion to font-ini.mkiv",
35131 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
35132 copyright="PRAGMA ADE / ConTeXt Development Team",
35133 license="see context related readme files"
35134}
35135local fonts,logs,trackers,resolvers=fonts,logs,trackers,resolvers
35136local next,type,tonumber,rawset=next,type,tonumber,rawset
35137local match,lower,gsub,strip,find=string.match,string.lower,string.gsub,string.strip,string.find
35138local char,byte,sub=string.char,string.byte,string.sub
35139local abs=math.abs
35140local bxor,rshift=bit32.bxor,bit32.rshift
35141local P,S,R,V,Cmt,C,Ct,Cs,Carg,Cf,Cg,Cc=lpeg.P,lpeg.S,lpeg.R,lpeg.V,lpeg.Cmt,lpeg.C,lpeg.Ct,lpeg.Cs,lpeg.Carg,lpeg.Cf,lpeg.Cg,lpeg.Cc
35142local lpegmatch,patterns=lpeg.match,lpeg.patterns
35143local trace_indexing=false  trackers.register("afm.indexing",function(v) trace_indexing=v end)
35144local trace_loading=false  trackers.register("afm.loading",function(v) trace_loading=v end)
35145local report_afm=logs.reporter("fonts","afm loading")
35146local report_pfb=logs.reporter("fonts","pfb loading")
35147local handlers=fonts.handlers
35148local afm=handlers.afm or {}
35149handlers.afm=afm
35150local readers=afm.readers or {}
35151afm.readers=readers
35152afm.version=1.541
35153local get_indexes,get_shapes
35154do
35155 local decrypt
35156 do
35157  local r,c1,c2,n=0,0,0,0
35158  local function step(c)
35159   local cipher=byte(c)
35160   local plain=bxor(cipher,rshift(r,8))
35161   r=((cipher+r)*c1+c2)%65536
35162   return char(plain)
35163  end
35164  decrypt=function(binary,initial,seed)
35165   r,c1,c2,n=initial,52845,22719,seed
35166   binary=gsub(binary,".",step)
35167   return sub(binary,n+1)
35168  end
35169 end
35170 local charstrings=P("/CharStrings")
35171 local subroutines=P("/Subrs")
35172 local encoding=P("/Encoding")
35173 local dup=P("dup")
35174 local put=P("put")
35175 local array=P("array")
35176 local name=P("/")*C((R("az","AZ","09")+S("-_."))^1)
35177 local digits=R("09")^1
35178 local cardinal=digits/tonumber
35179 local spaces=P(" ")^1
35180 local spacing=patterns.whitespace^0
35181 local routines,vector,chars,n,m
35182 local initialize=function(str,position,size)
35183  n=0
35184  m=size
35185  return position+1
35186 end
35187 local setroutine=function(str,position,index,size,filename)
35188  if routines[index] then
35189   return false
35190  end
35191  local forward=position+size
35192  local stream=decrypt(sub(str,position+1,forward),4330,4)
35193  routines[index]={ byte(stream,1,#stream) }
35194  n=n+1
35195  if n>=m then
35196   return #str
35197  end
35198  return forward+1
35199 end
35200 local setvector=function(str,position,name,size,filename)
35201  local forward=position+tonumber(size)
35202  if n>=m then
35203   return #str
35204  elseif forward<#str then
35205   if n==0 and name~=".notdef" then
35206    report_pfb("reserving .notdef at index 0 in %a",filename) 
35207    n=n+1
35208   end
35209   vector[n]=name
35210   n=n+1
35211   return forward
35212  else
35213   return #str
35214  end
35215 end
35216 local setshapes=function(str,position,name,size,filename)
35217  local forward=position+tonumber(size)
35218  local stream=sub(str,position+1,forward)
35219  if n>m then
35220   return #str
35221  elseif forward<#str then
35222   if n==0 and name~=".notdef" then
35223    report_pfb("reserving .notdef at index 0 in %a",filename) 
35224    n=n+1
35225   end
35226   vector[n]=name
35227   n=n+1
35228   chars [n]=decrypt(stream,4330,4)
35229   return forward
35230  else
35231   return #str
35232  end
35233 end
35234 local p_rd=spacing*(P("RD")+P("-|"))
35235 local p_np=spacing*(P("NP")+P("|"))
35236 local p_nd=spacing*(P("ND")+P("|"))
35237 local p_filterroutines=
35238  (1-subroutines)^0*subroutines*spaces*Cmt(cardinal,initialize)*(Cmt(cardinal*spaces*cardinal*p_rd*Carg(1),setroutine)*p_np+(1-p_nd))^1
35239 local p_filtershapes=
35240  (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal*p_rd*Carg(1),setshapes)*p_nd+P(1))^1
35241 local p_filternames=Ct (
35242  (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal*Carg(1),setvector)+P(1))^1
35243 )
35244 local p_filterencoding=(1-encoding)^0*encoding*spaces*digits*spaces*array*(1-dup)^0*Cf(
35245   Ct("")*Cg(spacing*dup*spaces*cardinal*spaces*name*spaces*put)^1
35246,rawset)
35247 local key=spacing*P("/")*R("az","AZ")
35248 local str=spacing*Cs { (P("(")/"")*((1-P("\\(")-P("\\)")-S("()"))+V(1))^0*(P(")")/"") }
35249 local num=spacing*(R("09")+S("+-."))^1/tonumber
35250 local arr=spacing*Ct (S("[{")*(num)^0*spacing*S("]}"))
35251 local boo=spacing*(P("true")*Cc(true)+P("false")*Cc(false))
35252 local nam=spacing*P("/")*Cs(R("az","AZ")^1)
35253 local p_filtermetadata=(
35254  P("/")*Carg(1)*((
35255   C("version")*str+C("Copyright")*str+C("Notice")*str+C("FullName")*str+C("FamilyName")*str+C("Weight")*str+C("ItalicAngle")*num+C("isFixedPitch")*boo+C("UnderlinePosition")*num+C("UnderlineThickness")*num+C("FontName")*nam+C("FontMatrix")*arr+C("FontBBox")*arr
35256  ) )/function(t,k,v) t[lower(k)]=v end+P(1)
35257 )^0*Carg(1)
35258 local function loadpfbvector(filename,shapestoo,streams)
35259  local data=io.loaddata(resolvers.findfile(filename))
35260  if not data then
35261   report_pfb("no data in %a",filename)
35262   return
35263  end
35264  if not (find(data,"!PS-AdobeFont-",1,true) or find(data,"%!FontType1",1,true)) then
35265   report_pfb("no font in %a",filename)
35266   return
35267  end
35268  local ascii,binary=match(data,"(.*)eexec%s+......(.*)")
35269  if not binary then
35270   report_pfb("no binary data in %a",filename)
35271   return
35272  end
35273  binary=decrypt(binary,55665,4)
35274  local names={}
35275  local encoding=lpegmatch(p_filterencoding,ascii)
35276  local metadata=lpegmatch(p_filtermetadata,ascii,1,{})
35277  local glyphs={}
35278  routines,vector,chars={},{},{}
35279  if shapestoo or streams then
35280   lpegmatch(p_filterroutines,binary,1,filename)
35281   lpegmatch(p_filtershapes,binary,1,filename)
35282   local data={
35283    dictionaries={
35284     {
35285      charstrings=chars,
35286      charset=vector,
35287      subroutines=routines,
35288     }
35289    },
35290   }
35291   fonts.handlers.otf.readers.parsecharstrings(false,data,glyphs,true,"cff",streams,true)
35292  else
35293   lpegmatch(p_filternames,binary,1,filename)
35294  end
35295  names=vector
35296  routines,vector,chars=nil,nil,nil
35297  return names,encoding,glyphs,metadata
35298 end
35299 local pfb=handlers.pfb or {}
35300 handlers.pfb=pfb
35301 pfb.loadvector=loadpfbvector
35302 get_indexes=function(data,pfbname)
35303  local vector=loadpfbvector(pfbname)
35304  if vector then
35305   local characters=data.characters
35306   if trace_loading then
35307    report_afm("getting index data from %a",pfbname)
35308   end
35309   for index=0,#vector do 
35310    local name=vector[index]
35311    local char=characters[name]
35312    if char then
35313     if trace_indexing then
35314      report_afm("glyph %a has index %a",name,index)
35315     end
35316     char.index=index
35317    else
35318     if trace_indexing then
35319      report_afm("glyph %a has index %a but no data",name,index)
35320     end
35321    end
35322   end
35323  end
35324 end
35325 get_shapes=function(pfbname)
35326  local vector,encoding,glyphs=loadpfbvector(pfbname,true)
35327  return glyphs
35328 end
35329end
35330local spacer=patterns.spacer
35331local whitespace=patterns.whitespace
35332local lineend=patterns.newline
35333local spacing=spacer^0
35334local number=spacing*S("+-")^-1*(R("09")+S("."))^1/tonumber
35335local name=spacing*C((1-whitespace)^1)
35336local words=spacing*((1-lineend)^1/strip)
35337local rest=(1-lineend)^0
35338local fontdata=Carg(1)
35339local semicolon=spacing*P(";")
35340local plus=spacing*P("plus")*number
35341local minus=spacing*P("minus")*number
35342local function addkernpair(data,one,two,value)
35343 local chr=data.characters[one]
35344 if chr then
35345  local kerns=chr.kerns
35346  if kerns then
35347   kerns[two]=tonumber(value)
35348  else
35349   chr.kerns={ [two]=tonumber(value) }
35350  end
35351 end
35352end
35353local p_kernpair=(fontdata*P("KPX")*name*name*number)/addkernpair
35354local chr=false
35355local ind=0
35356local function start(data,version)
35357 data.metadata.afmversion=version
35358 ind=0
35359 chr={}
35360end
35361local function stop()
35362 ind=0
35363 chr=false
35364end
35365local function setindex(i)
35366 if i<0 then
35367  ind=ind+1 
35368 else
35369  ind=i
35370 end
35371 chr={
35372  index=ind
35373 }
35374end
35375local function setwidth(width)
35376 chr.width=width
35377end
35378local function setname(data,name)
35379 data.characters[name]=chr
35380end
35381local function setboundingbox(boundingbox)
35382 chr.boundingbox=boundingbox
35383end
35384local function setligature(plus,becomes)
35385 local ligatures=chr.ligatures
35386 if ligatures then
35387  ligatures[plus]=becomes
35388 else
35389  chr.ligatures={ [plus]=becomes }
35390 end
35391end
35392local p_charmetric=((
35393 P("C")*number/setindex+P("WX")*number/setwidth+P("N")*fontdata*name/setname+P("B")*Ct((number)^4)/setboundingbox+P("L")*(name)^2/setligature
35394  )*semicolon )^1
35395local p_charmetrics=P("StartCharMetrics")*number*(p_charmetric+(1-P("EndCharMetrics")))^0*P("EndCharMetrics")
35396local p_kernpairs=P("StartKernPairs")*number*(p_kernpair+(1-P("EndKernPairs"  )))^0*P("EndKernPairs"  )
35397local function set_1(data,key,a)  data.metadata[lower(key)]=a     end
35398local function set_2(data,key,a,b)   data.metadata[lower(key)]={ a,b } end
35399local function set_3(data,key,a,b,c) data.metadata[lower(key)]={ a,b,c } end
35400local p_parameters=P(false)+fontdata*((P("FontName")+P("FullName")+P("FamilyName"))/lower)*words/function(data,key,value)
35401  data.metadata[key]=value
35402 end+fontdata*((P("Weight")+P("Version"))/lower)*name/function(data,key,value)
35403  data.metadata[key]=value
35404 end+fontdata*P("IsFixedPitch")*name/function(data,pitch)
35405  data.metadata.monospaced=toboolean(pitch,true)
35406 end+fontdata*P("FontBBox")*Ct(number^4)/function(data,boundingbox)
35407  data.metadata.boundingbox=boundingbox
35408  end+fontdata*((P("CharWidth")+P("CapHeight")+P("XHeight")+P("Descender")+P("Ascender")+P("ItalicAngle"))/lower)*number/function(data,key,value)
35409  data.metadata[key]=value
35410 end+P("Comment")*spacing*(P(false)+(fontdata*C("DESIGNSIZE")*number*rest)/set_1 
35411+(fontdata*C("TFM designsize")*number*rest)/set_1+(fontdata*C("DesignSize")*number*rest)/set_1+(fontdata*C("CODINGSCHEME")*words*rest)/set_1 
35412+(fontdata*C("CHECKSUM")*number*words*rest)/set_1 
35413+(fontdata*C("SPACE")*number*plus*minus*rest)/set_3 
35414+(fontdata*C("QUAD")*number*rest)/set_1 
35415+(fontdata*C("EXTRASPACE")*number*rest)/set_1 
35416+(fontdata*C("NUM")*number*number*number*rest)/set_3 
35417+(fontdata*C("DENOM")*number*number*rest)/set_2 
35418+(fontdata*C("SUP")*number*number*number*rest)/set_3 
35419+(fontdata*C("SUB")*number*number*rest)/set_2 
35420+(fontdata*C("SUPDROP")*number*rest)/set_1 
35421+(fontdata*C("SUBDROP")*number*rest)/set_1 
35422+(fontdata*C("DELIM")*number*number*rest)/set_2 
35423+(fontdata*C("AXISHEIGHT")*number*rest)/set_1 
35424 )
35425local fullparser=(P("StartFontMetrics")*fontdata*name/start )*(p_charmetrics+p_kernpairs+p_parameters+(1-P("EndFontMetrics")) )^0*(P("EndFontMetrics")/stop )
35426local infoparser=(P("StartFontMetrics")*fontdata*name/start )*(p_parameters+(1-P("EndFontMetrics")) )^0*(P("EndFontMetrics")/stop )
35427local function read(filename,parser)
35428 local afmblob=io.loaddata(filename)
35429 if afmblob then
35430  local data={
35431   resources={
35432    filename=resolvers.unresolve(filename),
35433    version=afm.version,
35434    creator="context mkiv",
35435   },
35436   properties={
35437    hasitalics=false,
35438   },
35439   goodies={},
35440   metadata={
35441    filename=file.removesuffix(file.basename(filename))
35442   },
35443   characters={
35444   },
35445   descriptions={
35446   },
35447  }
35448  if trace_loading then
35449   report_afm("parsing afm file %a",filename)
35450  end
35451  lpegmatch(parser,afmblob,1,data)
35452  return data
35453 else
35454  if trace_loading then
35455   report_afm("no valid afm file %a",filename)
35456  end
35457  return nil
35458 end
35459end
35460function readers.loadfont(afmname,pfbname)
35461 local data=read(resolvers.findfile(afmname),fullparser)
35462 if data then
35463  if not pfbname or pfbname=="" then
35464   pfbname=resolvers.findfile(file.replacesuffix(file.nameonly(afmname),"pfb"))
35465  end
35466  if pfbname and pfbname~="" then
35467   data.resources.filename=resolvers.unresolve(pfbname)
35468   get_indexes(data,pfbname)
35469   return data
35470  else 
35471   report_afm("no pfb file for %a",afmname)
35472  end
35473 end
35474end
35475function readers.loadshapes(filename)
35476 local fullname=resolvers.findfile(filename) or ""
35477 if fullname=="" then
35478  return {
35479   filename="not found: "..filename,
35480   glyphs={}
35481  }
35482 else
35483  return {
35484   filename=fullname,
35485   format="opentype",
35486   glyphs=get_shapes(fullname) or {},
35487   units=1000,
35488  }
35489 end
35490end
35491function readers.getinfo(filename)
35492 local data=read(resolvers.findfile(filename),infoparser)
35493 if data then
35494  return data.metadata
35495 end
35496end
35497
35498end -- closure
35499
35500do -- begin closure to overcome local limits and interference
35501
35502if not modules then modules={} end modules ['font-one']={
35503 version=1.001,
35504 optimize=true,
35505 comment="companion to font-ini.mkiv",
35506 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
35507 copyright="PRAGMA ADE / ConTeXt Development Team",
35508 license="see context related readme files"
35509}
35510local fonts,logs,trackers,containers,resolvers=fonts,logs,trackers,containers,resolvers
35511local next,type,tonumber,rawget=next,type,tonumber,rawget
35512local match,gsub=string.match,string.gsub
35513local abs=math.abs
35514local P,S,R,Cmt,C,Ct,Cs,Carg=lpeg.P,lpeg.S,lpeg.R,lpeg.Cmt,lpeg.C,lpeg.Ct,lpeg.Cs,lpeg.Carg
35515local lpegmatch,patterns=lpeg.match,lpeg.patterns
35516local sortedhash=table.sortedhash
35517local trace_features=false  trackers.register("afm.features",function(v) trace_features=v end)
35518local trace_indexing=false  trackers.register("afm.indexing",function(v) trace_indexing=v end)
35519local trace_loading=false  trackers.register("afm.loading",function(v) trace_loading=v end)
35520local trace_defining=false  trackers.register("fonts.defining",function(v) trace_defining=v end)
35521local report_afm=logs.reporter("fonts","afm loading")
35522local setmetatableindex=table.setmetatableindex
35523local derivetable=table.derive
35524local findbinfile=resolvers.findbinfile
35525local privateoffset=fonts.constructors and fonts.constructors.privateoffset or 0xF0000 
35526local definers=fonts.definers
35527local readers=fonts.readers
35528local constructors=fonts.constructors
35529local afm=constructors.handlers.afm
35530local pfb=constructors.handlers.pfb
35531local otf=fonts.handlers.otf
35532local otfreaders=otf.readers
35533local otfenhancers=otf.enhancers
35534local afmfeatures=constructors.features.afm
35535local registerafmfeature=afmfeatures.register
35536local afmenhancers=constructors.enhancers.afm
35537local registerafmenhancer=afmenhancers.register
35538afm.version=1.541 
35539afm.cache=containers.define("fonts","one",afm.version,true)
35540afm.autoprefixed=true 
35541afm.helpdata={}  
35542afm.syncspace=true 
35543local overloads=fonts.mappings.overloads
35544local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes
35545function afm.load(filename)
35546 filename=resolvers.findfile(filename,'afm') or ""
35547 if filename~="" and not fonts.names.ignoredfile(filename) then
35548  local name=file.removesuffix(file.basename(filename))
35549  local data=containers.read(afm.cache,name)
35550  local attr=lfs.attributes(filename)
35551  local size=attr and attr.size or 0
35552  local time=attr and attr.modification or 0
35553  local pfbfile=file.replacesuffix(name,"pfb")
35554  local pfbname=resolvers.findfile(pfbfile,"pfb") or ""
35555  if pfbname=="" then
35556   pfbname=resolvers.findfile(file.basename(pfbfile),"pfb") or ""
35557  end
35558  local pfbsize=0
35559  local pfbtime=0
35560  if pfbname~="" then
35561   local attr=lfs.attributes(pfbname)
35562   pfbsize=attr.size or 0
35563   pfbtime=attr.modification or 0
35564  end
35565  if not data or data.size~=size or data.time~=time or data.pfbsize~=pfbsize or data.pfbtime~=pfbtime then
35566   report_afm("reading %a",filename)
35567   data=afm.readers.loadfont(filename,pfbname)
35568   if data then
35569    afmenhancers.apply(data,filename)
35570    fonts.mappings.addtounicode(data,filename)
35571    otfreaders.stripredundant(data)
35572    otfreaders.pack(data)
35573    data.size=size
35574    data.time=time
35575    data.pfbsize=pfbsize
35576    data.pfbtime=pfbtime
35577    report_afm("saving %a in cache",name)
35578    data=containers.write(afm.cache,name,data)
35579    data=containers.read(afm.cache,name)
35580   end
35581  end
35582  if data then
35583   otfreaders.unpack(data)
35584   otfreaders.expand(data) 
35585   otfreaders.addunicodetable(data) 
35586   otfenhancers.apply(data,filename,data)
35587   if applyruntimefixes then
35588    applyruntimefixes(filename,data)
35589   end
35590  end
35591  return data
35592 end
35593end
35594local uparser=fonts.mappings.makenameparser() 
35595local function enhance_unify_names(data,filename)
35596 local unicodevector=fonts.encodings.agl.unicodes 
35597 local unicodes={}
35598 local names={}
35599 local private=data.private or privateoffset
35600 local descriptions=data.descriptions
35601 for name,blob in sortedhash(data.characters) do 
35602  local code=unicodevector[name] 
35603  if not code then
35604   code=lpegmatch(uparser,name)
35605   if type(code)~="number" then
35606    code=private
35607    private=private+1
35608    report_afm("assigning private slot %U for unknown glyph name %a",code,name)
35609   end
35610  end
35611  local index=blob.index
35612  unicodes[name]=code
35613  names[name]=index
35614  blob.name=name
35615  descriptions[code]={
35616   boundingbox=blob.boundingbox,
35617   width=blob.width,
35618   kerns=blob.kerns,
35619   index=index,
35620   name=name,
35621  }
35622 end
35623 for unicode,description in next,descriptions do
35624  local kerns=description.kerns
35625  if kerns then
35626   local krn={}
35627   for name,kern in next,kerns do
35628    local unicode=unicodes[name]
35629    if unicode then
35630     krn[unicode]=kern
35631    else
35632    end
35633   end
35634   description.kerns=krn
35635  end
35636 end
35637 data.characters=nil
35638 data.private=private
35639 local resources=data.resources
35640 local filename=resources.filename or file.removesuffix(file.basename(filename))
35641 resources.filename=resolvers.unresolve(filename) 
35642 resources.unicodes=unicodes 
35643 resources.marks={}
35644end
35645local everywhere={ ["*"]={ ["*"]=true } } 
35646local noflags={ false,false,false,false }
35647local function enhance_normalize_features(data)
35648 local ligatures=setmetatableindex("table")
35649 local kerns=setmetatableindex("table")
35650 local extrakerns=setmetatableindex("table")
35651 for u,c in next,data.descriptions do
35652  local l=c.ligatures
35653  local k=c.kerns
35654  local e=c.extrakerns
35655  if l then
35656   ligatures[u]=l
35657   for u,v in next,l do
35658    l[u]={ ligature=v }
35659   end
35660   c.ligatures=nil
35661  end
35662  if k then
35663   kerns[u]=k
35664   for u,v in next,k do
35665    k[u]=v 
35666   end
35667   c.kerns=nil
35668  end
35669  if e then
35670   extrakerns[u]=e
35671   for u,v in next,e do
35672    e[u]=v 
35673   end
35674   c.extrakerns=nil
35675  end
35676 end
35677 local features={
35678  gpos={},
35679  gsub={},
35680 }
35681 local sequences={
35682 }
35683 if next(ligatures) then
35684  features.gsub.liga=everywhere
35685  data.properties.hasligatures=true
35686  sequences[#sequences+1]={
35687   features={
35688    liga=everywhere,
35689   },
35690   flags=noflags,
35691   name="s_s_0",
35692   nofsteps=1,
35693   order={ "liga" },
35694   type="gsub_ligature",
35695   steps={
35696    {
35697     coverage=ligatures,
35698    },
35699   },
35700  }
35701 end
35702 if next(kerns) then
35703  features.gpos.kern=everywhere
35704  data.properties.haskerns=true
35705  sequences[#sequences+1]={
35706   features={
35707    kern=everywhere,
35708   },
35709   flags=noflags,
35710   name="p_s_0",
35711   nofsteps=1,
35712   order={ "kern" },
35713   type="gpos_pair",
35714   steps={
35715    {
35716     format="kern",
35717     coverage=kerns,
35718    },
35719   },
35720  }
35721 end
35722 if next(extrakerns) then
35723  features.gpos.extrakerns=everywhere
35724  data.properties.haskerns=true
35725  sequences[#sequences+1]={
35726   features={
35727    extrakerns=everywhere,
35728   },
35729   flags=noflags,
35730   name="p_s_1",
35731   nofsteps=1,
35732   order={ "extrakerns" },
35733   type="gpos_pair",
35734   steps={
35735    {
35736     format="kern",
35737     coverage=extrakerns,
35738    },
35739   },
35740  }
35741 end
35742 data.resources.features=features
35743 data.resources.sequences=sequences
35744end
35745local function enhance_fix_names(data)
35746 for k,v in next,data.descriptions do
35747  local n=v.name
35748  local r=overloads[n]
35749  if r then
35750   local name=r.name
35751   if trace_indexing then
35752    report_afm("renaming characters %a to %a",n,name)
35753   end
35754   v.name=name
35755   v.unicode=r.unicode
35756  end
35757 end
35758end
35759local addthem=function(rawdata,ligatures)
35760 if ligatures then
35761  local descriptions=rawdata.descriptions
35762  local resources=rawdata.resources
35763  local unicodes=resources.unicodes
35764  for ligname,ligdata in next,ligatures do
35765   local one=descriptions[unicodes[ligname]]
35766   if one then
35767    for _,pair in next,ligdata do
35768     local two=unicodes[pair[1]]
35769     local three=unicodes[pair[2]]
35770     if two and three then
35771      local ol=one.ligatures
35772      if ol then
35773       if not ol[two] then
35774        ol[two]=three
35775       end
35776      else
35777       one.ligatures={ [two]=three }
35778      end
35779     end
35780    end
35781   end
35782  end
35783 end
35784end
35785local function enhance_add_ligatures(rawdata)
35786 addthem(rawdata,afm.helpdata.ligatures)
35787end
35788local function enhance_add_extra_kerns(rawdata) 
35789 local descriptions=rawdata.descriptions
35790 local resources=rawdata.resources
35791 local unicodes=resources.unicodes
35792 local function do_it_left(what)
35793  if what then
35794   for unicode,description in next,descriptions do
35795    local kerns=description.kerns
35796    if kerns then
35797     local extrakerns
35798     for complex,simple in next,what do
35799      complex=unicodes[complex]
35800      simple=unicodes[simple]
35801      if complex and simple then
35802       local ks=kerns[simple]
35803       if ks and not kerns[complex] then
35804        if extrakerns then
35805         extrakerns[complex]=ks
35806        else
35807         extrakerns={ [complex]=ks }
35808        end
35809       end
35810      end
35811     end
35812     if extrakerns then
35813      description.extrakerns=extrakerns
35814     end
35815    end
35816   end
35817  end
35818 end
35819 local function do_it_copy(what)
35820  if what then
35821   for complex,simple in next,what do
35822    complex=unicodes[complex]
35823    simple=unicodes[simple]
35824    if complex and simple then
35825     local complexdescription=descriptions[complex]
35826     if complexdescription then 
35827      local simpledescription=descriptions[complex]
35828      if simpledescription then
35829       local extrakerns
35830       local kerns=simpledescription.kerns
35831       if kerns then
35832        for unicode,kern in next,kerns do
35833         if extrakerns then
35834          extrakerns[unicode]=kern
35835         else
35836          extrakerns={ [unicode]=kern }
35837         end
35838        end
35839       end
35840       local extrakerns=simpledescription.extrakerns
35841       if extrakerns then
35842        for unicode,kern in next,extrakerns do
35843         if extrakerns then
35844          extrakerns[unicode]=kern
35845         else
35846          extrakerns={ [unicode]=kern }
35847         end
35848        end
35849       end
35850       if extrakerns then
35851        complexdescription.extrakerns=extrakerns
35852       end
35853      end
35854     end
35855    end
35856   end
35857  end
35858 end
35859 do_it_left(afm.helpdata.leftkerned)
35860 do_it_left(afm.helpdata.bothkerned)
35861 do_it_copy(afm.helpdata.bothkerned)
35862 do_it_copy(afm.helpdata.rightkerned)
35863end
35864local function adddimensions(data) 
35865 if data then
35866  for unicode,description in next,data.descriptions do
35867   local bb=description.boundingbox
35868   if bb then
35869    local ht=bb[4]
35870    local dp=-bb[2]
35871    if ht==0 or ht<0 then
35872    else
35873     description.height=ht
35874    end
35875    if dp==0 or dp<0 then
35876    else
35877     description.depth=dp
35878    end
35879   end
35880  end
35881 end
35882end
35883local function copytotfm(data)
35884 if data and data.descriptions then
35885  local metadata=data.metadata
35886  local resources=data.resources
35887  local properties=derivetable(data.properties)
35888  local descriptions=derivetable(data.descriptions)
35889  local goodies=derivetable(data.goodies)
35890  local characters={}
35891  local parameters={}
35892  local unicodes=resources.unicodes
35893  for unicode,description in next,data.descriptions do 
35894   characters[unicode]={}
35895  end
35896  local filename=constructors.checkedfilename(resources)
35897  local fontname=metadata.fontname or metadata.fullname
35898  local fullname=metadata.fullname or metadata.fontname
35899  local endash=0x2013
35900  local emdash=0x2014
35901  local space=0x0020 
35902  local spacer="space"
35903  local spaceunits=500
35904  local monospaced=metadata.monospaced
35905  local charwidth=metadata.charwidth
35906  local italicangle=metadata.italicangle
35907  local charxheight=metadata.xheight and metadata.xheight>0 and metadata.xheight
35908  properties.monospaced=monospaced
35909  parameters.italicangle=italicangle
35910  parameters.charwidth=charwidth
35911  parameters.charxheight=charxheight
35912  local d_endash=descriptions[endash]
35913  local d_emdash=descriptions[emdash]
35914  local d_space=descriptions[space]
35915  if not d_space or d_space==0 then
35916   d_space=d_endash
35917  end
35918  if d_space then
35919   spaceunits,spacer=d_space.width or 0,"space"
35920  end
35921  if properties.monospaced then
35922   if spaceunits==0 and d_emdash then
35923    spaceunits,spacer=d_emdash.width or 0,"emdash"
35924   end
35925  else
35926   if spaceunits==0 and d_endash then
35927    spaceunits,spacer=d_emdash.width or 0,"endash"
35928   end
35929  end
35930  if spaceunits==0 and charwidth then
35931   spaceunits,spacer=charwidth or 0,"charwidth"
35932  end
35933  if spaceunits==0 then
35934   spaceunits=tonumber(spaceunits) or 500
35935  end
35936  if spaceunits==0 then
35937   spaceunits=500
35938  end
35939  parameters.slant=0
35940  parameters.space=spaceunits
35941  parameters.space_stretch=500
35942  parameters.space_shrink=333
35943  parameters.x_height=400
35944  parameters.quad=1000
35945  if italicangle and italicangle~=0 then
35946   parameters.italicangle=italicangle
35947   parameters.italicfactor=math.cos(math.rad(90+italicangle))
35948   parameters.slant=- math.tan(italicangle*math.pi/180)
35949  end
35950  if monospaced then
35951   parameters.space_stretch=0
35952   parameters.space_shrink=0
35953  elseif afm.syncspace then
35954   parameters.space_stretch=spaceunits/2
35955   parameters.space_shrink=spaceunits/3
35956  end
35957  parameters.extra_space=parameters.space_shrink
35958  if charxheight then
35959   parameters.x_height=charxheight
35960  else
35961   local x=0x0078 
35962   if x then
35963    local x=descriptions[x]
35964    if x then
35965     parameters.x_height=x.height
35966    end
35967   end
35968  end
35969  if metadata.sup then
35970   local dummy={ 0,0,0 }
35971   parameters[ 1]=metadata.designsize  or 0
35972   parameters[ 2]=metadata.checksum    or 0
35973   parameters[ 3],
35974   parameters[ 4],
35975   parameters[ 5]=unpack(metadata.space   or dummy)
35976   parameters[ 6]=metadata.quad    or 0
35977   parameters[ 7]=metadata.extraspace or 0
35978   parameters[ 8],
35979   parameters[ 9],
35980   parameters[10]=unpack(metadata.num  or dummy)
35981   parameters[11],
35982   parameters[12]=unpack(metadata.denom   or dummy)
35983   parameters[13],
35984   parameters[14],
35985   parameters[15]=unpack(metadata.sup  or dummy)
35986   parameters[16],
35987   parameters[17]=unpack(metadata.sub  or dummy)
35988   parameters[18]=metadata.supdrop or 0
35989   parameters[19]=metadata.subdrop or 0
35990   parameters[20],
35991   parameters[21]=unpack(metadata.delim   or dummy)
35992   parameters[22]=metadata.axisheight or 0
35993  end
35994  parameters.designsize=(metadata.designsize or 10)*65536
35995  parameters.ascender=abs(metadata.ascender  or 0)
35996  parameters.descender=abs(metadata.descender or 0)
35997  parameters.units=1000
35998  properties.spacer=spacer
35999  properties.format=fonts.formats[filename] or "type1"
36000  properties.filename=filename
36001  properties.fontname=fontname
36002  properties.fullname=fullname
36003  properties.psname=fullname
36004  properties.name=filename or fullname or fontname
36005  properties.private=properties.private or data.private or privateoffset
36006if not CONTEXTLMTXMODE or CONTEXTLMTXMODE==0 then
36007  properties.encodingbytes=2
36008end
36009  if next(characters) then
36010   return {
36011    characters=characters,
36012    descriptions=descriptions,
36013    parameters=parameters,
36014    resources=resources,
36015    properties=properties,
36016    goodies=goodies,
36017   }
36018  end
36019 end
36020 return nil
36021end
36022function afm.setfeatures(tfmdata,features)
36023 local okay=constructors.initializefeatures("afm",tfmdata,features,trace_features,report_afm)
36024 if okay then
36025  return constructors.collectprocessors("afm",tfmdata,features,trace_features,report_afm)
36026 else
36027  return {} 
36028 end
36029end
36030local function addtables(data)
36031 local resources=data.resources
36032 local lookuptags=resources.lookuptags
36033 local unicodes=resources.unicodes
36034 if not lookuptags then
36035  lookuptags={}
36036  resources.lookuptags=lookuptags
36037 end
36038 setmetatableindex(lookuptags,function(t,k)
36039  local v=type(k)=="number" and ("lookup "..k) or k
36040  t[k]=v
36041  return v
36042 end)
36043 if not unicodes then
36044  unicodes={}
36045  resources.unicodes=unicodes
36046  setmetatableindex(unicodes,function(t,k)
36047   setmetatableindex(unicodes,nil)
36048   for u,d in next,data.descriptions do
36049    local n=d.name
36050    if n then
36051     t[n]=u
36052    end
36053   end
36054   return rawget(t,k)
36055  end)
36056 end
36057 constructors.addcoreunicodes(unicodes) 
36058end
36059local function afmtotfm(specification)
36060 local afmname=specification.filename or specification.name
36061 if specification.forced=="afm" or specification.format=="afm" then 
36062  if trace_loading then
36063   report_afm("forcing afm format for %a",afmname)
36064  end
36065 else
36066  local tfmname=findbinfile(afmname,"ofm") or ""
36067  if tfmname~="" then
36068   if trace_loading then
36069    report_afm("fallback from afm to tfm for %a",afmname)
36070   end
36071   return 
36072  end
36073 end
36074 if afmname~="" then
36075  local features=constructors.checkedfeatures("afm",specification.features.normal)
36076  specification.features.normal=features
36077  constructors.hashinstance(specification,true)
36078  specification=definers.resolve(specification) 
36079  local cache_id=specification.hash
36080  local tfmdata=containers.read(constructors.cache,cache_id) 
36081  if not tfmdata then
36082   local rawdata=afm.load(afmname)
36083   if rawdata and next(rawdata) then
36084    addtables(rawdata)
36085    adddimensions(rawdata)
36086    tfmdata=copytotfm(rawdata)
36087    if tfmdata and next(tfmdata) then
36088     local shared=tfmdata.shared
36089     if not shared then
36090      shared={}
36091      tfmdata.shared=shared
36092     end
36093     shared.rawdata=rawdata
36094     shared.dynamics={}
36095     tfmdata.changed={}
36096     shared.features=features
36097     shared.processes=afm.setfeatures(tfmdata,features)
36098    end
36099   elseif trace_loading then
36100    report_afm("no (valid) afm file found with name %a",afmname)
36101   end
36102   tfmdata=containers.write(constructors.cache,cache_id,tfmdata)
36103  end
36104  return tfmdata
36105 end
36106end
36107local function read_from_afm(specification)
36108 local tfmdata=afmtotfm(specification)
36109 if tfmdata then
36110  tfmdata.properties.name=specification.name
36111  tfmdata.properties.id=specification.id
36112  tfmdata=constructors.scale(tfmdata,specification)
36113  local allfeatures=tfmdata.shared.features or specification.features.normal
36114  constructors.applymanipulators("afm",tfmdata,allfeatures,trace_features,report_afm)
36115  fonts.loggers.register(tfmdata,'afm',specification)
36116 end
36117 return tfmdata
36118end
36119registerafmfeature {
36120 name="mode",
36121 description="mode",
36122 initializers={
36123  base=otf.modeinitializer,
36124  node=otf.modeinitializer,
36125 }
36126}
36127registerafmfeature {
36128 name="features",
36129 description="features",
36130 default=true,
36131 initializers={
36132  node=otf.nodemodeinitializer,
36133  base=otf.basemodeinitializer,
36134 },
36135 processors={
36136  node=otf.featuresprocessor,
36137 }
36138}
36139fonts.formats.afm="type1"
36140fonts.formats.pfb="type1"
36141local function check_afm(specification,fullname)
36142 local foundname=findbinfile(fullname,'afm') or "" 
36143 if foundname=="" then
36144  foundname=fonts.names.getfilename(fullname,"afm") or ""
36145 end
36146 if fullname and foundname=="" and afm.autoprefixed then
36147  local encoding,shortname=match(fullname,"^(.-)%-(.*)$") 
36148  if encoding and shortname and fonts.encodings.known[encoding] then
36149   shortname=findbinfile(shortname,'afm') or "" 
36150   if shortname~="" then
36151    foundname=shortname
36152    if trace_defining then
36153     report_afm("stripping encoding prefix from filename %a",afmname)
36154    end
36155   end
36156  end
36157 end
36158 if foundname~="" then
36159  specification.filename=foundname
36160  specification.format="afm"
36161  return read_from_afm(specification)
36162 end
36163end
36164function readers.afm(specification,method)
36165 local fullname=specification.filename or ""
36166 local tfmdata=nil
36167 if fullname=="" then
36168  local forced=specification.forced or ""
36169  if forced~="" then
36170   tfmdata=check_afm(specification,specification.name.."."..forced)
36171  end
36172  if not tfmdata then
36173   local check_tfm=readers.check_tfm
36174   method=(check_tfm and (method or definers.method or "afm or tfm")) or "afm"
36175   if method=="tfm" then
36176    tfmdata=check_tfm(specification,specification.name)
36177   elseif method=="afm" then
36178    tfmdata=check_afm(specification,specification.name)
36179   elseif method=="tfm or afm" then
36180    tfmdata=check_tfm(specification,specification.name) or check_afm(specification,specification.name)
36181   else 
36182    tfmdata=check_afm(specification,specification.name) or check_tfm(specification,specification.name)
36183   end
36184  end
36185 else
36186  tfmdata=check_afm(specification,fullname)
36187 end
36188 return tfmdata
36189end
36190function readers.pfb(specification,method) 
36191 local original=specification.specification
36192 if trace_defining then
36193  report_afm("using afm reader for %a",original)
36194 end
36195 specification.forced="afm"
36196 local function swap(name)
36197  local value=specification[swap]
36198  if value then
36199   specification[swap]=gsub("%.pfb",".afm",1)
36200  end
36201 end
36202 swap("filename")
36203 swap("fullname")
36204 swap("forcedname")
36205 swap("specification")
36206 return readers.afm(specification,method)
36207end
36208registerafmenhancer("unify names",enhance_unify_names)
36209registerafmenhancer("add ligatures",enhance_add_ligatures)
36210registerafmenhancer("add extra kerns",enhance_add_extra_kerns)
36211registerafmenhancer("normalize features",enhance_normalize_features)
36212registerafmenhancer("check extra features",otfenhancers.enhance)
36213registerafmenhancer("fix names",enhance_fix_names)
36214
36215end -- closure
36216
36217do -- begin closure to overcome local limits and interference
36218
36219if not modules then modules={} end modules ['font-afk']={
36220 version=1.001,
36221 comment="companion to font-lib.mkiv",
36222 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
36223 copyright="PRAGMA ADE / ConTeXt Development Team",
36224 license="see context related readme files",
36225 dataonly=true,
36226}
36227local allocate=utilities.storage.allocate
36228fonts.handlers.afm.helpdata={
36229 ligatures=allocate { 
36230  ['f']={ 
36231   { 'f','ff' },
36232   { 'i','fi' },
36233   { 'l','fl' },
36234  },
36235  ['ff']={
36236   { 'i','ffi' }
36237  },
36238  ['fi']={
36239   { 'i','fii' }
36240  },
36241  ['fl']={
36242   { 'i','fli' }
36243  },
36244  ['s']={
36245   { 't','st' }
36246  },
36247  ['i']={
36248   { 'j','ij' }
36249  },
36250 },
36251 texligatures=allocate {
36252  ['quoteleft']={
36253   { 'quoteleft','quotedblleft' }
36254  },
36255  ['quoteright']={
36256   { 'quoteright','quotedblright' }
36257  },
36258  ['hyphen']={
36259   { 'hyphen','endash' }
36260  },
36261  ['endash']={
36262   { 'hyphen','emdash' }
36263  }
36264 },
36265 leftkerned=allocate {
36266  AEligature="A",aeligature="a",
36267  OEligature="O",oeligature="o",
36268  IJligature="I",ijligature="i",
36269  AE="A",ae="a",
36270  OE="O",oe="o",
36271  IJ="I",ij="i",
36272  Ssharp="S",ssharp="s",
36273 },
36274 rightkerned=allocate {
36275  AEligature="E",aeligature="e",
36276  OEligature="E",oeligature="e",
36277  IJligature="J",ijligature="j",
36278  AE="E",ae="e",
36279  OE="E",oe="e",
36280  IJ="J",ij="j",
36281  Ssharp="S",ssharp="s",
36282 },
36283 bothkerned=allocate {
36284  Acircumflex="A",acircumflex="a",
36285  Ccircumflex="C",ccircumflex="c",
36286  Ecircumflex="E",ecircumflex="e",
36287  Gcircumflex="G",gcircumflex="g",
36288  Hcircumflex="H",hcircumflex="h",
36289  Icircumflex="I",icircumflex="i",
36290  Jcircumflex="J",jcircumflex="j",
36291  Ocircumflex="O",ocircumflex="o",
36292  Scircumflex="S",scircumflex="s",
36293  Ucircumflex="U",ucircumflex="u",
36294  Wcircumflex="W",wcircumflex="w",
36295  Ycircumflex="Y",ycircumflex="y",
36296  Agrave="A",agrave="a",
36297  Egrave="E",egrave="e",
36298  Igrave="I",igrave="i",
36299  Ograve="O",ograve="o",
36300  Ugrave="U",ugrave="u",
36301  Ygrave="Y",ygrave="y",
36302  Atilde="A",atilde="a",
36303  Itilde="I",itilde="i",
36304  Otilde="O",otilde="o",
36305  Utilde="U",utilde="u",
36306  Ntilde="N",ntilde="n",
36307  Adiaeresis="A",adiaeresis="a",Adieresis="A",adieresis="a",
36308  Ediaeresis="E",ediaeresis="e",Edieresis="E",edieresis="e",
36309  Idiaeresis="I",idiaeresis="i",Idieresis="I",idieresis="i",
36310  Odiaeresis="O",odiaeresis="o",Odieresis="O",odieresis="o",
36311  Udiaeresis="U",udiaeresis="u",Udieresis="U",udieresis="u",
36312  Ydiaeresis="Y",ydiaeresis="y",Ydieresis="Y",ydieresis="y",
36313  Aacute="A",aacute="a",
36314  Cacute="C",cacute="c",
36315  Eacute="E",eacute="e",
36316  Iacute="I",iacute="i",
36317  Lacute="L",lacute="l",
36318  Nacute="N",nacute="n",
36319  Oacute="O",oacute="o",
36320  Racute="R",racute="r",
36321  Sacute="S",sacute="s",
36322  Uacute="U",uacute="u",
36323  Yacute="Y",yacute="y",
36324  Zacute="Z",zacute="z",
36325  Dstroke="D",dstroke="d",
36326  Hstroke="H",hstroke="h",
36327  Tstroke="T",tstroke="t",
36328  Cdotaccent="C",cdotaccent="c",
36329  Edotaccent="E",edotaccent="e",
36330  Gdotaccent="G",gdotaccent="g",
36331  Idotaccent="I",idotaccent="i",
36332  Zdotaccent="Z",zdotaccent="z",
36333  Amacron="A",amacron="a",
36334  Emacron="E",emacron="e",
36335  Imacron="I",imacron="i",
36336  Omacron="O",omacron="o",
36337  Umacron="U",umacron="u",
36338  Ccedilla="C",ccedilla="c",
36339  Kcedilla="K",kcedilla="k",
36340  Lcedilla="L",lcedilla="l",
36341  Ncedilla="N",ncedilla="n",
36342  Rcedilla="R",rcedilla="r",
36343  Scedilla="S",scedilla="s",
36344  Tcedilla="T",tcedilla="t",
36345  Ohungarumlaut="O",ohungarumlaut="o",
36346  Uhungarumlaut="U",uhungarumlaut="u",
36347  Aogonek="A",aogonek="a",
36348  Eogonek="E",eogonek="e",
36349  Iogonek="I",iogonek="i",
36350  Uogonek="U",uogonek="u",
36351  Aring="A",aring="a",
36352  Uring="U",uring="u",
36353  Abreve="A",abreve="a",
36354  Ebreve="E",ebreve="e",
36355  Gbreve="G",gbreve="g",
36356  Ibreve="I",ibreve="i",
36357  Obreve="O",obreve="o",
36358  Ubreve="U",ubreve="u",
36359  Ccaron="C",ccaron="c",
36360  Dcaron="D",dcaron="d",
36361  Ecaron="E",ecaron="e",
36362  Lcaron="L",lcaron="l",
36363  Ncaron="N",ncaron="n",
36364  Rcaron="R",rcaron="r",
36365  Scaron="S",scaron="s",
36366  Tcaron="T",tcaron="t",
36367  Zcaron="Z",zcaron="z",
36368  dotlessI="I",dotlessi="i",
36369  dotlessJ="J",dotlessj="j",
36370  AEligature="AE",aeligature="ae",AE="AE",ae="ae",
36371  OEligature="OE",oeligature="oe",OE="OE",oe="oe",
36372  IJligature="IJ",ijligature="ij",IJ="IJ",ij="ij",
36373  Lstroke="L",lstroke="l",Lslash="L",lslash="l",
36374  Ostroke="O",ostroke="o",Oslash="O",oslash="o",
36375  Ssharp="SS",ssharp="ss",
36376  Aumlaut="A",aumlaut="a",
36377  Eumlaut="E",eumlaut="e",
36378  Iumlaut="I",iumlaut="i",
36379  Oumlaut="O",oumlaut="o",
36380  Uumlaut="U",uumlaut="u",
36381 }
36382}
36383
36384end -- closure
36385
36386do -- begin closure to overcome local limits and interference
36387
36388if not modules then modules={} end modules ['luatex-fonts-tfm']={
36389 version=1.001,
36390 comment="companion to font-ini.mkiv",
36391 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
36392 copyright="PRAGMA ADE / ConTeXt Development Team",
36393 license="see context related readme files"
36394}
36395local next,type=next,type
36396local match,format=string.match,string.format
36397local concat,sortedhash=table.concat,table.sortedhash
36398local idiv=number.idiv
36399local trace_defining=false  trackers.register("fonts.defining",function(v) trace_defining=v end)
36400local trace_features=false  trackers.register("tfm.features",function(v) trace_features=v end)
36401local report_defining=logs.reporter("fonts","defining")
36402local report_tfm=logs.reporter("fonts","tfm loading")
36403local findbinfile=resolvers.findbinfile
36404local setmetatableindex=table.setmetatableindex
36405local fonts=fonts
36406local handlers=fonts.handlers
36407local helpers=fonts.helpers
36408local readers=fonts.readers
36409local constructors=fonts.constructors
36410local encodings=fonts.encodings
36411local tfm=constructors.handlers.tfm
36412tfm.version=1.000
36413tfm.maxnestingdepth=5
36414tfm.maxnestingsize=65536*1024
36415local otf=fonts.handlers.otf
36416local otfenhancers=otf.enhancers
36417local tfmfeatures=constructors.features.tfm
36418local registertfmfeature=tfmfeatures.register
36419local tfmenhancers=constructors.enhancers.tfm
36420local registertfmenhancer=tfmenhancers.register
36421local charcommand=helpers.commands.char
36422constructors.resolvevirtualtoo=false 
36423fonts.formats.tfm="type1" 
36424fonts.formats.ofm="type1" 
36425function tfm.setfeatures(tfmdata,features)
36426 local okay=constructors.initializefeatures("tfm",tfmdata,features,trace_features,report_tfm)
36427 if okay then
36428  return constructors.collectprocessors("tfm",tfmdata,features,trace_features,report_tfm)
36429 else
36430  return {} 
36431 end
36432end
36433local depth={} 
36434local loadtfm=font.read_tfm
36435local loadvf=font.read_vf
36436local function read_from_tfm(specification)
36437 local filename=specification.filename
36438 local size=specification.size
36439 depth[filename]=(depth[filename] or 0)+1
36440 if trace_defining then
36441  report_defining("loading tfm file %a at size %s",filename,size)
36442 end
36443 local tfmdata=loadtfm(filename,size)
36444 if tfmdata then
36445  local features=specification.features and specification.features.normal or {}
36446  local features=constructors.checkedfeatures("tfm",features)
36447  specification.features.normal=features
36448  local newtfmdata=(depth[filename]==1) and tfm.reencode(tfmdata,specification)
36449  if newtfmdata then
36450    tfmdata=newtfmdata
36451  end
36452  local resources=tfmdata.resources  or {}
36453  local properties=tfmdata.properties or {}
36454  local parameters=tfmdata.parameters or {}
36455  local shared=tfmdata.shared  or {}
36456  shared.features=features
36457  shared.resources=resources
36458  properties.name=tfmdata.name     
36459  properties.fontname=tfmdata.fontname    
36460  properties.psname=tfmdata.psname   
36461  properties.fullname=tfmdata.fullname    
36462  properties.filename=specification.filename 
36463  properties.format=tfmdata.format or fonts.formats.tfm 
36464  properties.usedbitmap=tfmdata.usedbitmap
36465  tfmdata.properties=properties
36466  tfmdata.resources=resources
36467  tfmdata.parameters=parameters
36468  tfmdata.shared=shared
36469  shared.rawdata={ resources=resources }
36470  shared.features=features
36471  if newtfmdata then
36472   if not resources.marks then
36473    resources.marks={}
36474   end
36475   if not resources.sequences then
36476    resources.sequences={}
36477   end
36478   if not resources.features then
36479    resources.features={
36480     gsub={},
36481     gpos={},
36482    }
36483   end
36484   if not tfmdata.changed then
36485    tfmdata.changed={}
36486   end
36487   if not tfmdata.descriptions then
36488    tfmdata.descriptions=tfmdata.characters
36489   end
36490   otf.readers.addunicodetable(tfmdata)
36491   tfmenhancers.apply(tfmdata,filename)
36492   constructors.applymanipulators("tfm",tfmdata,features,trace_features,report_tfm)
36493   otf.readers.unifymissing(tfmdata)
36494   fonts.mappings.addtounicode(tfmdata,filename)
36495   tfmdata.tounicode=1
36496   local tounicode=fonts.mappings.tounicode
36497   for unicode,v in next,tfmdata.characters do
36498    local u=v.unicode
36499    if u then
36500     v.tounicode=tounicode(u)
36501    end
36502   end
36503   if tfmdata.usedbitmap then
36504    tfm.addtounicode(tfmdata)
36505   end
36506  end
36507  shared.processes=next(features) and tfm.setfeatures(tfmdata,features) or nil
36508  if size<0 then
36509   size=idiv(65536*-size,100)
36510  end
36511  parameters.factor=1  
36512  parameters.units=1000  
36513  parameters.size=size
36514  parameters.slant=parameters.slant    or parameters[1] or 0
36515  parameters.space=parameters.space    or parameters[2] or 0
36516  parameters.space_stretch=parameters.space_stretch  or parameters[3] or 0
36517  parameters.space_shrink=parameters.space_shrink   or parameters[4] or 0
36518  parameters.x_height=parameters.x_height    or parameters[5] or 0
36519  parameters.quad=parameters.quad     or parameters[6] or 0
36520  parameters.extra_space=parameters.extra_space or parameters[7] or 0
36521  constructors.enhanceparameters(parameters)
36522  properties.private=properties.private or tfmdata.private or privateoffset
36523  if newtfmdata then
36524  elseif constructors.resolvevirtualtoo then
36525   fonts.loggers.register(tfmdata,file.suffix(filename),specification) 
36526   local vfname=findbinfile(specification.name,'ovf')
36527   if vfname and vfname~="" then
36528    local vfdata=loadvf(vfname,size)
36529    if vfdata then
36530     local chars=tfmdata.characters
36531     for k,v in next,vfdata.characters do
36532      chars[k].commands=v.commands
36533     end
36534     properties.virtualized=true
36535     tfmdata.fonts=vfdata.fonts
36536     tfmdata.type="virtual" 
36537     local fontlist=vfdata.fonts
36538     local name=file.nameonly(filename)
36539     for i=1,#fontlist do
36540      local n=fontlist[i].name
36541      local s=fontlist[i].size
36542      local d=depth[filename]
36543      s=constructors.scaled(s,vfdata.designsize)
36544      if d>tfm.maxnestingdepth then
36545       report_defining("too deeply nested virtual font %a with size %a, max nesting depth %s",n,s,tfm.maxnestingdepth)
36546       fontlist[i]={ id=0 }
36547      elseif (d>1) and (s>tfm.maxnestingsize) then
36548       report_defining("virtual font %a exceeds size %s",n,s)
36549       fontlist[i]={ id=0 }
36550      else
36551       local t,id=constructors.readanddefine(n,s)
36552       fontlist[i]={ id=id }
36553      end
36554     end
36555    end
36556   end
36557  end
36558  properties.haskerns=true
36559  properties.hasligatures=true
36560  properties.hasitalics=true
36561  resources.unicodes={}
36562  resources.lookuptags={}
36563  depth[filename]=depth[filename]-1
36564  return tfmdata
36565 else
36566  depth[filename]=depth[filename]-1
36567 end
36568end
36569local function check_tfm(specification,fullname) 
36570 local foundname=findbinfile(fullname,'tfm') or ""
36571 if foundname=="" then
36572  foundname=findbinfile(fullname,'ofm') or "" 
36573 end
36574 if foundname=="" then
36575  foundname=fonts.names.getfilename(fullname,"tfm") or ""
36576 end
36577 if foundname~="" then
36578  specification.filename=foundname
36579  specification.format="ofm"
36580  return read_from_tfm(specification)
36581 elseif trace_defining then
36582  report_defining("loading tfm with name %a fails",specification.name)
36583 end
36584end
36585readers.check_tfm=check_tfm
36586function readers.tfm(specification)
36587 local fullname=specification.filename or ""
36588 if fullname=="" then
36589  local forced=specification.forced or ""
36590  if forced~="" then
36591   fullname=specification.name.."."..forced
36592  else
36593   fullname=specification.name
36594  end
36595 end
36596 return check_tfm(specification,fullname)
36597end
36598readers.ofm=readers.tfm
36599do
36600 local outfiles={}
36601 local tfmcache=table.setmetatableindex(function(t,tfmdata)
36602  local id=font.define(tfmdata)
36603  t[tfmdata]=id
36604  return id
36605 end)
36606 local encdone=table.setmetatableindex("table")
36607 function tfm.reencode(tfmdata,specification)
36608  local features=specification.features
36609  if not features then
36610   return
36611  end
36612  local features=features.normal
36613  if not features then
36614   return
36615  end
36616  local tfmfile=file.basename(tfmdata.name)
36617  local encfile=features.reencode 
36618  local pfbfile=features.pfbfile  
36619  local bitmap=features.bitmap   
36620  if not encfile then
36621   return
36622  end
36623  local pfbfile=outfiles[tfmfile]
36624  if pfbfile==nil then
36625   if bitmap then
36626    pfbfile=false
36627   elseif type(pfbfile)~="string" then
36628    pfbfile=tfmfile
36629   end
36630   if type(pfbfile)=="string" then
36631    pfbfile=file.addsuffix(pfbfile,"pfb")
36632    report_tfm("using type1 shapes from %a for %a",pfbfile,tfmfile)
36633   else
36634    report_tfm("using bitmap shapes for %a",tfmfile)
36635    pfbfile=false 
36636   end
36637   outfiles[tfmfile]=pfbfile
36638  end
36639  local encoding=false
36640  local vector=false
36641  if type(pfbfile)=="string" then
36642   local pfb=constructors.handlers.pfb
36643   if pfb and pfb.loadvector then
36644    local v,e=pfb.loadvector(pfbfile)
36645    if v then
36646     vector=v
36647    end
36648    if e then
36649     encoding=e
36650    end
36651   end
36652  end
36653  if type(encfile)=="string" and encfile~="auto" then
36654   encoding=fonts.encodings.load(file.addsuffix(encfile,"enc"))
36655   if encoding then
36656    encoding=encoding.vector
36657   end
36658  end
36659  if not encoding then
36660   report_tfm("bad encoding for %a, quitting",tfmfile)
36661   return
36662  end
36663  local unicoding=fonts.encodings.agl and fonts.encodings.agl.unicodes
36664  local virtualid=tfmcache[tfmdata]
36665  local tfmdata=table.copy(tfmdata) 
36666  local characters={}
36667  local originals=tfmdata.characters
36668  local indices={}
36669  local parentfont={ "font",1 }
36670  local private=tfmdata.privateoffset or constructors.privateoffset
36671  local reported=encdone[tfmfile][encfile]
36672  local backmap=vector and table.swapped(vector)
36673  local done={} 
36674  for index,name in sortedhash(encoding) do 
36675   local unicode=unicoding[name]
36676   local original=originals[index]
36677   if original then
36678    if unicode then
36679     original.unicode=unicode
36680    else
36681     unicode=private
36682     private=private+1
36683     if not reported then
36684      report_tfm("glyph %a in font %a with encoding %a gets unicode %U",name,tfmfile,encfile,unicode)
36685     end
36686    end
36687    characters[unicode]=original
36688    indices[index]=unicode
36689    original.name=name 
36690    if backmap then
36691     original.index=backmap[name]
36692    else 
36693     original.commands={ parentfont,charcommand[index] } 
36694     original.oindex=index
36695    end
36696    done[name]=true
36697   elseif not done[name] then
36698    report_tfm("bad index %a in font %a with name %a",index,tfmfile,name)
36699   end
36700  end
36701  encdone[tfmfile][encfile]=true
36702  for k,v in next,characters do
36703   local kerns=v.kerns
36704   if kerns then
36705    local t={}
36706    for k,v in next,kerns do
36707     local i=indices[k]
36708     if i then
36709      t[i]=v
36710     end
36711    end
36712    v.kerns=next(t) and t or nil
36713   end
36714   local ligatures=v.ligatures
36715   if ligatures then
36716    local t={}
36717    for k,v in next,ligatures do
36718     local i=indices[k]
36719     if i then
36720      t[i]=v
36721      v.char=indices[v.char]
36722     end
36723    end
36724    v.ligatures=next(t) and t or nil
36725   end
36726  end
36727  tfmdata.fonts={ { id=virtualid } }
36728  tfmdata.characters=characters
36729  tfmdata.fullname=tfmdata.fullname or tfmdata.name
36730  tfmdata.psname=file.nameonly(pfbfile or tfmdata.name)
36731  tfmdata.filename=pfbfile
36732  tfmdata.encodingbytes=2
36733  tfmdata.format="type1"
36734  tfmdata.tounicode=1
36735  tfmdata.embedding="subset"
36736  tfmdata.usedbitmap=bitmap and virtualid
36737  tfmdata.private=private
36738  return tfmdata
36739 end
36740end
36741do
36742 local template=[[
36743/CIDInit /ProcSet findresource begin
36744  12 dict begin
36745  begincmap
36746    /CIDSystemInfo << /Registry (TeX) /Ordering (bitmap-%s) /Supplement 0 >> def
36747    /CMapName /TeX-bitmap-%s def
36748    /CMapType 2 def
36749    1 begincodespacerange
36750      <00> <FF>
36751    endcodespacerange
36752    %s beginbfchar
36753%s
36754    endbfchar
36755  endcmap
36756CMapName currentdict /CMap defineresource pop end
36757end
36758end
36759]]
36760 local flushstreamobject=lpdf and lpdf.flushstreamobject 
36761 local setfontattributes=lpdf and lpdf.setfontattributes 
36762 if not flushstreamobject then
36763  flushstreamobject=function(data)
36764   return pdf.obj { immediate=true,type="stream",string=data } 
36765  end
36766 end
36767 if not setfontattributes then
36768  setfontattributes=function(id,data)
36769   return pdf.setfontattributes(id,data) 
36770  end
36771 end
36772 function tfm.addtounicode(tfmdata)
36773  local id=tfmdata.usedbitmap
36774  local map={}
36775  local char={} 
36776  for k,v in next,tfmdata.characters do
36777   local index=v.oindex
36778   local tounicode=v.tounicode
36779   if index and tounicode then
36780    map[index]=tounicode
36781   end
36782  end
36783  for k,v in sortedhash(map) do
36784   char[#char+1]=format("<%02X> <%s>",k,v)
36785  end
36786  char=concat(char,"\n")
36787  local stream=format(template,id,id,#char,char)
36788  local reference=flushstreamobject(stream,nil,true)
36789  setfontattributes(id,format("/ToUnicode %i 0 R",reference))
36790 end
36791end
36792do
36793 local everywhere={ ["*"]={ ["*"]=true } } 
36794 local noflags={ false,false,false,false }
36795 local function enhance_normalize_features(data)
36796  local ligatures=setmetatableindex("table")
36797  local kerns=setmetatableindex("table")
36798  local characters=data.characters
36799  for u,c in next,characters do
36800   local l=c.ligatures
36801   local k=c.kerns
36802   if l then
36803    ligatures[u]=l
36804    for u,v in next,l do
36805     l[u]={ ligature=v.char }
36806    end
36807    c.ligatures=nil
36808   end
36809   if k then
36810    kerns[u]=k
36811    for u,v in next,k do
36812     k[u]=v 
36813    end
36814    c.kerns=nil
36815   end
36816  end
36817  for u,l in next,ligatures do
36818   for k,v in next,l do
36819    local vl=v.ligature
36820    local dl=ligatures[vl]
36821    if dl then
36822     for kk,vv in next,dl do
36823      v[kk]=vv 
36824     end
36825    end
36826   end
36827  end
36828  local features={
36829   gpos={},
36830   gsub={},
36831  }
36832  local sequences={
36833  }
36834  if next(ligatures) then
36835   features.gsub.liga=everywhere
36836   data.properties.hasligatures=true
36837   sequences[#sequences+1]={
36838    features={
36839     liga=everywhere,
36840    },
36841    flags=noflags,
36842    name="s_s_0",
36843    nofsteps=1,
36844    order={ "liga" },
36845    type="gsub_ligature",
36846    steps={
36847     {
36848      coverage=ligatures,
36849     },
36850    },
36851   }
36852  end
36853  if next(kerns) then
36854   features.gpos.kern=everywhere
36855   data.properties.haskerns=true
36856   sequences[#sequences+1]={
36857    features={
36858     kern=everywhere,
36859    },
36860    flags=noflags,
36861    name="p_s_0",
36862    nofsteps=1,
36863    order={ "kern" },
36864    type="gpos_pair",
36865    steps={
36866     {
36867      format="kern",
36868      coverage=kerns,
36869     },
36870    },
36871   }
36872  end
36873  data.resources.features=features
36874  data.resources.sequences=sequences
36875  data.shared.resources=data.shared.resources or resources
36876 end
36877 registertfmenhancer("normalize features",enhance_normalize_features)
36878 registertfmenhancer("check extra features",otfenhancers.enhance)
36879end
36880registertfmfeature {
36881 name="mode",
36882 description="mode",
36883 initializers={
36884  base=otf.modeinitializer,
36885  node=otf.modeinitializer,
36886 }
36887}
36888registertfmfeature {
36889 name="features",
36890 description="features",
36891 default=true,
36892 initializers={
36893  base=otf.basemodeinitializer,
36894  node=otf.nodemodeinitializer,
36895 },
36896 processors={
36897  node=otf.featuresprocessor,
36898 }
36899}
36900
36901end -- closure
36902
36903do -- begin closure to overcome local limits and interference
36904
36905if not modules then modules={} end modules ['font-lua']={
36906 version=1.001,
36907 comment="companion to font-ini.mkiv",
36908 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
36909 copyright="PRAGMA ADE / ConTeXt Development Team",
36910 license="see context related readme files"
36911}
36912local trace_defining=false  trackers.register("fonts.defining",function(v) trace_defining=v end)
36913local report_lua=logs.reporter("fonts","lua loading")
36914local fonts=fonts
36915local readers=fonts.readers
36916fonts.formats.lua="lua"
36917local function check_lua(specification,fullname)
36918 local fullname=resolvers.findfile(fullname) or ""
36919 if fullname~="" then
36920  local loader=loadfile(fullname)
36921  loader=loader and loader()
36922  return loader and loader(specification)
36923 end
36924end
36925readers.check_lua=check_lua
36926function readers.lua(specification)
36927 local original=specification.specification
36928 if trace_defining then
36929  report_lua("using lua reader for %a",original)
36930 end
36931 local fullname=specification.filename or ""
36932 if fullname=="" then
36933  local forced=specification.forced or ""
36934  if forced~="" then
36935   fullname=specification.name.."."..forced
36936  else
36937   fullname=specification.name
36938  end
36939 end
36940 return check_lua(specification,fullname)
36941end
36942
36943end -- closure
36944
36945do -- begin closure to overcome local limits and interference
36946
36947if not modules then modules={} end modules ['font-def']={
36948 version=1.001,
36949 comment="companion to font-ini.mkiv",
36950 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
36951 copyright="PRAGMA ADE / ConTeXt Development Team",
36952 license="see context related readme files"
36953}
36954local lower,gsub=string.lower,string.gsub
36955local tostring,next=tostring,next
36956local lpegmatch=lpeg.match
36957local suffixonly,removesuffix,basename=file.suffix,file.removesuffix,file.basename
36958local formatters=string.formatters
36959local sortedhash,sortedkeys=table.sortedhash,table.sortedkeys
36960local allocate=utilities.storage.allocate
36961local trace_defining=false  trackers  .register("fonts.defining",function(v) trace_defining=v end)
36962local directive_embedall=false  directives.register("fonts.embedall",function(v) directive_embedall=v end)
36963trackers.register("fonts.loading","fonts.defining","otf.loading","afm.loading","tfm.loading")
36964local report_defining=logs.reporter("fonts","defining")
36965local fonts=fonts
36966local fontdata=fonts.hashes.identifiers
36967local readers=fonts.readers
36968local definers=fonts.definers
36969local specifiers=fonts.specifiers
36970local constructors=fonts.constructors
36971local fontgoodies=fonts.goodies
36972readers.sequence=allocate { 'otf','ttf','afm','tfm','lua' } 
36973local variants=allocate()
36974specifiers.variants=variants
36975definers.methods=definers.methods or {}
36976local internalized=allocate() 
36977local loadedfonts=constructors.loadedfonts
36978local designsizes=constructors.designsizes
36979local resolvefile=fontgoodies and fontgoodies.filenames and fontgoodies.filenames.resolve or function(s) return s end
36980local function makespecification(specification,lookup,name,sub,method,detail,size)
36981 size=size or 655360
36982 if not lookup or lookup=="" then
36983  lookup=definers.defaultlookup
36984 end
36985 if trace_defining then
36986  report_defining("specification %a, lookup %a, name %a, sub %a, method %a, detail %a",
36987   specification,lookup,name,sub,method,detail)
36988 end
36989 local t={
36990  lookup=lookup,
36991  specification=specification,
36992  size=size,
36993  name=name,
36994  sub=sub,
36995  method=method,
36996  detail=detail,
36997  resolved="",
36998  forced="",
36999  features={},
37000 }
37001 return t
37002end
37003definers.makespecification=makespecification
37004if context then
37005
37006--removed
37007
37008end
37009definers.resolvers=definers.resolvers or {}
37010local resolvers=definers.resolvers
37011function resolvers.file(specification)
37012 local name=resolvefile(specification.name) 
37013 local suffix=lower(suffixonly(name))
37014 if fonts.formats[suffix] then
37015  specification.forced=suffix
37016  specification.forcedname=name
37017  specification.name=removesuffix(name)
37018 else
37019  specification.name=name 
37020 end
37021end
37022function resolvers.name(specification)
37023 local resolve=fonts.names.resolve
37024 if resolve then
37025  local resolved,sub,subindex,instance=resolve(specification.name,specification.sub,specification) 
37026  if resolved then
37027   specification.resolved=resolved
37028   specification.sub=sub
37029   specification.subindex=subindex
37030   if instance then
37031    specification.instance=instance
37032    local features=specification.features
37033    if not features then
37034     features={}
37035     specification.features=features
37036    end
37037    local normal=features.normal
37038    if not normal then
37039     normal={}
37040     features.normal=normal
37041    end
37042    normal.instance=instance
37043   end
37044   local suffix=lower(suffixonly(resolved))
37045   if fonts.formats[suffix] then
37046    specification.forced=suffix
37047    specification.forcedname=resolved
37048    specification.name=removesuffix(resolved)
37049   else
37050    specification.name=resolved
37051   end
37052  end
37053 else
37054  resolvers.file(specification)
37055 end
37056end
37057function resolvers.spec(specification)
37058 local resolvespec=fonts.names.resolvespec
37059 if resolvespec then
37060  local resolved,sub,subindex=resolvespec(specification.name,specification.sub,specification) 
37061  if resolved then
37062   specification.resolved=resolved
37063   specification.sub=sub
37064   specification.subindex=subindex
37065   specification.forced=lower(suffixonly(resolved))
37066   specification.forcedname=resolved
37067   specification.name=removesuffix(resolved)
37068  end
37069 else
37070  resolvers.name(specification)
37071 end
37072end
37073function definers.resolve(specification)
37074 if not specification.resolved or specification.resolved=="" then 
37075  local r=resolvers[specification.lookup]
37076  if r then
37077   r(specification)
37078  end
37079 end
37080 if specification.forced=="" then
37081  specification.forced=nil
37082  specification.forcedname=nil
37083 end
37084 specification.hash=lower(specification.name..' @ '..constructors.hashfeatures(specification))
37085 if specification.sub and specification.sub~="" then
37086  specification.hash=specification.sub..' @ '..specification.hash
37087 end
37088 return specification
37089end
37090function definers.applypostprocessors(tfmdata)
37091 local postprocessors=tfmdata.postprocessors
37092 if postprocessors then
37093  local properties=tfmdata.properties
37094  for i=1,#postprocessors do
37095   local extrahash=postprocessors[i](tfmdata) 
37096   if type(extrahash)=="string" and extrahash~="" then
37097    extrahash=gsub(lower(extrahash),"[^a-z]","-")
37098    properties.fullname=formatters["%s-%s"](properties.fullname,extrahash)
37099   end
37100  end
37101 end
37102 return tfmdata
37103end
37104local function checkembedding(tfmdata)
37105 local properties=tfmdata.properties
37106 local embedding
37107 if directive_embedall then
37108  embedding="full"
37109 elseif properties and properties.filename and constructors.dontembed[properties.filename] then
37110  embedding="no"
37111 else
37112  embedding="subset"
37113 end
37114 if properties then
37115  properties.embedding=embedding
37116 else
37117  tfmdata.properties={ embedding=embedding }
37118 end
37119 tfmdata.embedding=embedding
37120end
37121local function checkfeatures(tfmdata)
37122 local resources=tfmdata.resources
37123 local shared=tfmdata.shared
37124 if resources and shared then
37125  local features=resources.features
37126  local usedfeatures=shared.features
37127  if features and usedfeatures then
37128   local usedlanguage=usedfeatures.language or "dflt"
37129   local usedscript=usedfeatures.script or "dflt"
37130   local function check(what)
37131    if what then
37132     local foundlanguages={}
37133     for feature,scripts in next,what do
37134      if usedscript=="auto" or scripts["*"] then
37135      elseif not scripts[usedscript] then
37136      else
37137       for script,languages in next,scripts do
37138        if languages["*"] then
37139        elseif context and not languages[usedlanguage] then
37140         report_defining("font %!font:name!, feature %a, script %a, no language %a",
37141          tfmdata,feature,script,usedlanguage)
37142        end
37143       end
37144      end
37145      for script,languages in next,scripts do
37146       for language in next,languages do
37147        foundlanguages[language]=true
37148       end
37149      end
37150     end
37151     if false then
37152      foundlanguages["*"]=nil
37153      foundlanguages=sortedkeys(foundlanguages)
37154      for feature,scripts in sortedhash(what) do
37155       for script,languages in next,scripts do
37156        if not languages["*"] then
37157         for i=1,#foundlanguages do
37158          local language=foundlanguages[i]
37159          if context and not languages[language] then
37160           report_defining("font %!font:name!, feature %a, script %a, no language %a",
37161            tfmdata,feature,script,language)
37162          end
37163         end
37164        end
37165       end
37166      end
37167     end
37168    end
37169   end
37170   check(features.gsub)
37171   check(features.gpos)
37172  end
37173 end
37174end
37175function definers.loadfont(specification)
37176 local hash=constructors.hashinstance(specification)
37177 local tfmdata=loadedfonts[hash] 
37178 if not tfmdata then
37179  local forced=specification.forced or ""
37180  if forced~="" then
37181   local reader=readers[lower(forced)] 
37182   tfmdata=reader and reader(specification)
37183   if not tfmdata then
37184    report_defining("forced type %a of %a not found",forced,specification.name)
37185   end
37186  else
37187   local sequence=readers.sequence 
37188   for s=1,#sequence do
37189    local reader=sequence[s]
37190    if readers[reader] then 
37191     if trace_defining then
37192      report_defining("trying (reader sequence driven) type %a for %a with file %a",reader,specification.name,specification.filename)
37193     end
37194     tfmdata=readers[reader](specification)
37195     if tfmdata then
37196      break
37197     else
37198      specification.filename=nil
37199     end
37200    end
37201   end
37202  end
37203  if tfmdata then
37204   tfmdata=definers.applypostprocessors(tfmdata)
37205   checkembedding(tfmdata) 
37206   loadedfonts[hash]=tfmdata
37207   designsizes[specification.hash]=tfmdata.parameters.designsize
37208   checkfeatures(tfmdata)
37209  end
37210 end
37211 if not tfmdata then
37212  report_defining("font with asked name %a is not found using lookup %a",specification.name,specification.lookup)
37213 end
37214 return tfmdata
37215end
37216function constructors.readanddefine(name,size) 
37217 local specification=definers.analyze(name,size)
37218 local method=specification.method
37219 if method and variants[method] then
37220  specification=variants[method](specification)
37221 end
37222 specification=definers.resolve(specification)
37223 local hash=constructors.hashinstance(specification)
37224 local id=definers.registered(hash)
37225 if not id then
37226  local tfmdata=definers.loadfont(specification)
37227  if tfmdata then
37228   tfmdata.properties.hash=hash
37229   id=font.define(tfmdata)
37230   definers.register(tfmdata,id)
37231  else
37232   id=0  
37233  end
37234 end
37235 return fontdata[id],id
37236end
37237function definers.registered(hash)
37238 local id=internalized[hash]
37239 return id,id and fontdata[id]
37240end
37241function definers.register(tfmdata,id)
37242 if tfmdata and id then
37243  local hash=tfmdata.properties.hash
37244  if not hash then
37245   report_defining("registering font, id %a, name %a, invalid hash",id,tfmdata.properties.filename or "?")
37246  elseif not internalized[hash] then
37247   internalized[hash]=id
37248   if trace_defining then
37249    report_defining("registering font, id %s, hash %a",id,hash)
37250   end
37251   fontdata[id]=tfmdata
37252  end
37253 end
37254end
37255function definers.read(specification,size,id) 
37256 statistics.starttiming(fonts)
37257 if type(specification)=="string" then
37258  specification=definers.analyze(specification,size)
37259 end
37260 local method=specification.method
37261 if method and variants[method] then
37262  specification=variants[method](specification)
37263 end
37264 specification=definers.resolve(specification)
37265 local hash=constructors.hashinstance(specification)
37266 local tfmdata=definers.registered(hash) 
37267 if tfmdata then
37268  if trace_defining then
37269   report_defining("already hashed: %s",hash)
37270  end
37271 else
37272  tfmdata=definers.loadfont(specification)
37273  if tfmdata then
37274   tfmdata.original=specification.specification
37275   if trace_defining then
37276    report_defining("loaded and hashed: %s",hash)
37277   end
37278   tfmdata.properties.hash=hash
37279   if id then
37280    definers.register(tfmdata,id)
37281   end
37282  else
37283   if trace_defining then
37284    report_defining("not loaded and hashed: %s",hash)
37285   end
37286  end
37287 end
37288 if not tfmdata then 
37289  report_defining("unknown font %a, loading aborted",specification.name)
37290 elseif trace_defining and type(tfmdata)=="table" then
37291  local properties=tfmdata.properties or {}
37292  local parameters=tfmdata.parameters or {}
37293  report_defining("using %a font with id %a, name %a, size %a, bytes %a, encoding %a, fullname %a, filename %a",
37294   properties.format or "unknown",id or "-",properties.name,parameters.size,properties.encodingbytes,
37295   properties.encodingname,properties.fullname,basename(properties.filename))
37296 end
37297 statistics.stoptiming(fonts)
37298 return tfmdata
37299end
37300function font.getfont(id)
37301 return fontdata[id] 
37302end
37303if not context then
37304 callbacks.register('define_font',definers.read,"definition of fonts (tfmdata preparation)")
37305end
37306
37307end -- closure
37308
37309do -- begin closure to overcome local limits and interference
37310
37311if not modules then modules={} end modules ['font-shp']={
37312 version=1.001,
37313 comment="companion to font-ini.mkiv",
37314 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
37315 copyright="PRAGMA ADE / ConTeXt Development Team",
37316 license="see context related readme files"
37317}
37318local tonumber,next=tonumber,next
37319local concat=table.concat
37320local formatters,lower=string.formatters,string.lower
37321local otf=fonts.handlers.otf
37322local afm=fonts.handlers.afm
37323local pfb=fonts.handlers.pfb
37324local hashes=fonts.hashes
37325local identifiers=hashes.identifiers
37326local version=otf.version or 0.015
37327local shapescache=containers.define("fonts","shapes",version,true)
37328local streamscache=containers.define("fonts","streams",version,true)
37329local compact_streams=false
37330directives.register("fonts.streams.compact",function(v) compact_streams=v end)
37331local function packoutlines(data,makesequence)
37332 local subfonts=data.subfonts
37333 if subfonts then
37334  for i=1,#subfonts do
37335   packoutlines(subfonts[i],makesequence)
37336  end
37337  return
37338 end
37339 local common=data.segments
37340 if common then
37341  return
37342 end
37343 local glyphs=data.glyphs
37344 if not glyphs then
37345  return
37346 end
37347 if makesequence then
37348  for index=0,#glyphs do
37349   local glyph=glyphs[index]
37350   if glyph then
37351    local segments=glyph.segments
37352    if segments then
37353     local sequence={}
37354     local nofsequence=0
37355     for i=1,#segments do
37356      local segment=segments[i]
37357      local nofsegment=#segment
37358      nofsequence=nofsequence+1
37359      sequence[nofsequence]=segment[nofsegment]
37360      for i=1,nofsegment-1 do
37361       nofsequence=nofsequence+1
37362       sequence[nofsequence]=segment[i]
37363      end
37364     end
37365     glyph.sequence=sequence
37366     glyph.segments=nil
37367    end
37368   end
37369  end
37370 else
37371  local hash={}
37372  local common={}
37373  local reverse={}
37374  local last=0
37375  for index=0,#glyphs do
37376   local glyph=glyphs[index]
37377   if glyph then
37378    local segments=glyph.segments
37379    if segments then
37380     for i=1,#segments do
37381      local h=concat(segments[i]," ")
37382      hash[h]=(hash[h] or 0)+1
37383     end
37384    end
37385   end
37386  end
37387  for index=0,#glyphs do
37388   local glyph=glyphs[index]
37389   if glyph then
37390    local segments=glyph.segments
37391    if segments then
37392     for i=1,#segments do
37393      local segment=segments[i]
37394      local h=concat(segment," ")
37395      if hash[h]>1 then 
37396       local idx=reverse[h]
37397       if not idx then
37398        last=last+1
37399        reverse[h]=last
37400        common[last]=segment
37401        idx=last
37402       end
37403       segments[i]=idx
37404      end
37405     end
37406    end
37407   end
37408  end
37409  if last>0 then
37410   data.segments=common
37411  end
37412 end
37413end
37414local function unpackoutlines(data)
37415 local subfonts=data.subfonts
37416 if subfonts then
37417  for i=1,#subfonts do
37418   unpackoutlines(subfonts[i])
37419  end
37420  return
37421 end
37422 local common=data.segments
37423 if not common then
37424  return
37425 end
37426 local glyphs=data.glyphs
37427 if not glyphs then
37428  return
37429 end
37430 for index=0,#glyphs do
37431  local glyph=glyphs[index]
37432  if glyph then
37433   local segments=glyph.segments
37434   if segments then
37435    for i=1,#segments do
37436     local c=common[segments[i]]
37437     if c then
37438      segments[i]=c
37439     end
37440    end
37441   end
37442  end
37443 end
37444 data.segments=nil
37445end
37446local readers=otf.readers
37447local cleanname=otf.readers.helpers.cleanname
37448local function makehash(filename,sub,instance)
37449 local name=cleanname(file.basename(filename))
37450 if instance then
37451  return formatters["%s-%s-%s"](name,sub or 0,cleanname(instance))
37452 else
37453  return formatters["%s-%s"]   (name,sub or 0)
37454 end
37455end
37456local function loadoutlines(cache,filename,sub,instance)
37457 local base=file.basename(filename)
37458 local name=file.removesuffix(base)
37459 local kind=file.suffix(filename)
37460 local attr=lfs.attributes(filename)
37461 local size=attr and attr.size or 0
37462 local time=attr and attr.modification or 0
37463 local sub=tonumber(sub)
37464 if size>0 and (kind=="otf" or kind=="ttf" or kind=="tcc") then
37465  local hash=makehash(filename,sub,instance)
37466  data=containers.read(cache,hash)
37467  if not data or data.time~=time or data.size~=size then
37468   data=otf.readers.loadshapes(filename,sub,instance)
37469   if data then
37470    data.size=size
37471    data.format=data.format or (kind=="otf" and "opentype") or "truetype"
37472    data.time=time
37473    packoutlines(data)
37474    containers.write(cache,hash,data)
37475    data=containers.read(cache,hash) 
37476   end
37477  end
37478  unpackoutlines(data)
37479 elseif size>0 and (kind=="pfb") then
37480  local hash=containers.cleanname(base) 
37481  data=containers.read(cache,hash)
37482  if not data or data.time~=time or data.size~=size then
37483   data=afm.readers.loadshapes(filename)
37484   if data then
37485    data.size=size
37486    data.format="type1"
37487    data.time=time
37488    packoutlines(data)
37489    containers.write(cache,hash,data)
37490    data=containers.read(cache,hash) 
37491   end
37492  end
37493  unpackoutlines(data)
37494 else
37495  data={
37496   filename=filename,
37497   size=0,
37498   time=time,
37499   format="unknown",
37500   units=1000,
37501   glyphs={}
37502  }
37503 end
37504 return data
37505end
37506local function cachethem(cache,hash,data)
37507 containers.write(cache,hash,data,compact_streams) 
37508 return containers.read(cache,hash) 
37509end
37510local function loadstreams(cache,filename,sub,instance)
37511 local base=file.basename(filename)
37512 local name=file.removesuffix(base)
37513 local kind=lower(file.suffix(filename))
37514 local attr=lfs.attributes(filename)
37515 local size=attr and attr.size or 0
37516 local time=attr and attr.modification or 0
37517 local sub=tonumber(sub)
37518 if size>0 and (kind=="otf" or kind=="ttf" or kind=="ttc") then
37519  local hash=makehash(filename,sub,instance)
37520  data=containers.read(cache,hash)
37521  if not data or data.time~=time or data.size~=size then
37522   data=otf.readers.loadshapes(filename,sub,instance,true)
37523   if data then
37524    local glyphs=data.glyphs
37525    local streams={}
37526    if glyphs then
37527     for i=0,#glyphs do
37528      local glyph=glyphs[i]
37529      if glyph then
37530       streams[i]=glyph.stream or ""
37531      else
37532       streams[i]=""
37533      end
37534     end
37535    end
37536    data.streams=streams
37537    data.glyphs=nil
37538    data.size=size
37539    data.format=data.format or (kind=="otf" and "opentype") or "truetype"
37540    data.time=time
37541    data=cachethem(cache,hash,data)
37542   end
37543  end
37544 elseif size>0 and (kind=="pfb") then
37545  local hash=makehash(filename,sub,instance)
37546  data=containers.read(cache,hash)
37547  if not data or data.time~=time or data.size~=size then
37548   local names,encoding,streams,metadata=pfb.loadvector(filename,false,true)
37549   if streams then
37550    local fontbbox=metadata.fontbbox or { 0,0,0,0 }
37551    for i=0,#streams do
37552     local s=streams[i]
37553     streams[i]=s.stream or "\14"
37554    end
37555    data={
37556     filename=filename,
37557     size=size,
37558     time=time,
37559     format="type1",
37560     streams=streams,
37561     fontheader={
37562      fontversion=metadata.version,
37563      units=1000,
37564      xmin=fontbbox[1],
37565      ymin=fontbbox[2],
37566      xmax=fontbbox[3],
37567      ymax=fontbbox[4],
37568     },
37569     horizontalheader={
37570      ascender=0,
37571      descender=0,
37572     },
37573     maximumprofile={
37574      nofglyphs=#streams+1,
37575     },
37576     names={
37577      copyright=metadata.copyright,
37578      family=metadata.familyname,
37579      fullname=metadata.fullname,
37580      fontname=metadata.fontname,
37581      subfamily=metadata.subfamilyname,
37582      trademark=metadata.trademark,
37583      notice=metadata.notice,
37584      version=metadata.version,
37585     },
37586     cffinfo={
37587      familyname=metadata.familyname,
37588      fullname=metadata.fullname,
37589      italicangle=metadata.italicangle,
37590      monospaced=metadata.isfixedpitch and true or false,
37591      underlineposition=metadata.underlineposition,
37592      underlinethickness=metadata.underlinethickness,
37593      weight=metadata.weight,
37594     },
37595    }
37596    data=cachethem(cache,hash,data)
37597   end
37598  end
37599 else
37600  data={
37601   filename=filename,
37602   size=0,
37603   time=time,
37604   format="unknown",
37605   streams={}
37606  }
37607 end
37608 return data
37609end
37610local loadedshapes={}
37611local loadedstreams={}
37612local function loadoutlinedata(fontdata,streams)
37613 local properties=fontdata.properties
37614 local filename=properties.filename
37615 local subindex=fontdata.subindex
37616 local instance=properties.instance
37617 local hash=makehash(filename,subindex,instance)
37618 local loaded=loadedshapes[hash]
37619 if not loaded then
37620  loaded=loadoutlines(shapescache,filename,subindex,instance)
37621  loadedshapes[hash]=loaded
37622 end
37623 return loaded
37624end
37625hashes.shapes=table.setmetatableindex(function(t,k)
37626 local f=identifiers[k]
37627 if f then
37628  return loadoutlinedata(f)
37629 end
37630end)
37631local function getstreamhash(fontid)
37632 local fontdata=identifiers[fontid]
37633 if fontdata then
37634  local properties=fontdata.properties
37635  local fonthash=makehash(properties.filename,properties.subfont,properties.instance)
37636  return fonthash,fontdata
37637 end
37638end
37639local function loadstreamdata(fontdata)
37640 local properties=fontdata.properties
37641 local shared=fontdata.shared
37642 local rawdata=shared and shared.rawdata
37643 local metadata=rawdata and rawdata.metadata
37644 local filename=properties.filename
37645 local subindex=metadata and metadata.subfontindex
37646 local instance=properties.instance
37647 local hash=makehash(filename,subindex,instance)
37648 local loaded=loadedstreams[hash]
37649 if not loaded then
37650  loaded=loadstreams(streamscache,filename,subindex,instance)
37651  loadedstreams[hash]=loaded
37652 end
37653 return loaded
37654end
37655hashes.streams=table.setmetatableindex(function(t,k)
37656 local f=identifiers[k]
37657 if f then
37658  return loadstreamdata(f)
37659 end
37660end)
37661otf.loadoutlinedata=loadoutlinedata 
37662otf.loadstreamdata=loadstreamdata  
37663otf.loadshapes=loadshapes
37664otf.getstreamhash=getstreamhash   
37665local streams=fonts.hashes.streams
37666callback.register("glyph_stream_provider",function(id,index,mode)
37667 if id>0 then
37668  local streams=streams[id].streams
37669  if streams then
37670   return streams[index] or ""
37671  end
37672 end
37673 return ""
37674end)
37675
37676end -- closure
37677
37678do -- begin closure to overcome local limits and interference
37679
37680if not modules then modules={} end modules ['luatex-fonts-def']={
37681 version=1.001,
37682 comment="companion to luatex-*.tex",
37683 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
37684 copyright="PRAGMA ADE / ConTeXt Development Team",
37685 license="see context related readme files"
37686}
37687if context then
37688--removed
37689
37690end
37691local fonts=fonts
37692fonts.constructors.namemode="specification"
37693function fonts.definers.getspecification(str)
37694 return "",str,"",":",str
37695end
37696local list={} 
37697local function issome () list.lookup='name'    end 
37698local function isfile () list.lookup='file'    end
37699local function isname () list.lookup='name'    end
37700local function thename(s)   list.name=s      end
37701local function issub  (v)   list.sub=v      end
37702local function iscrap (s)   list.crap=string.lower(s) end
37703local function iskey  (k,v) list[k]=v      end
37704local function istrue (s)   list[s]=true   end
37705local function isfalse(s)   list[s]=false     end
37706local P,S,R,C,Cs=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cs
37707local spaces=P(" ")^0
37708local namespec=Cs((P("{")/"")*(1-S("}"))^0*(P("}")/"")+(1-S("/:("))^0)
37709local crapspec=spaces*P("/")*(((1-P(":"))^0)/iscrap)*spaces
37710local filename_1=P("file:")/isfile*(namespec/thename)
37711local filename_2=P("[")*P(true)/isfile*(((1-P("]"))^0)/thename)*P("]")
37712local fontname_1=P("name:")/isname*(namespec/thename)
37713local fontname_2=P(true)/issome*(namespec/thename)
37714local sometext=R("az","AZ","09")^1
37715local somekey=R("az","AZ","09")^1
37716local somevalue=(P("{")/"")*(1-P("}"))^0*(P("}")/"")+(1-S(";"))^1
37717local truevalue=P("+")*spaces*(sometext/istrue)
37718local falsevalue=P("-")*spaces*(sometext/isfalse)
37719local keyvalue=(C(somekey)*spaces*P("=")*spaces*C(somevalue))/iskey
37720local somevalue=sometext/istrue
37721local subvalue=P("(")*(C(P(1-S("()"))^1)/issub)*P(")") 
37722local option=spaces*(keyvalue+falsevalue+truevalue+somevalue)*spaces
37723local options=P(":")*spaces*(P(";")^0*option)^0
37724local pattern=(filename_1+filename_2+fontname_1+fontname_2)*subvalue^0*crapspec^0*options^0
37725function fonts.definers.analyze(str,size)
37726 local specification=fonts.definers.makespecification(str,nil,nil,nil,":",nil,size)
37727 list={}
37728 lpeg.match(pattern,str)
37729 list.crap=nil
37730 if list.name then
37731  specification.name=list.name
37732  list.name=nil
37733 end
37734 if list.lookup then
37735  specification.lookup=list.lookup
37736  list.lookup=nil
37737 end
37738 if list.sub then
37739  specification.sub=list.sub
37740  list.sub=nil
37741 end
37742 specification.features.normal=fonts.handlers.otf.features.normalize(list)
37743 list=nil
37744 return specification
37745end
37746function fonts.definers.applypostprocessors(tfmdata)
37747 local postprocessors=tfmdata.postprocessors
37748 if postprocessors then
37749  for i=1,#postprocessors do
37750   local extrahash=postprocessors[i](tfmdata) 
37751   if type(extrahash)=="string" and extrahash~="" then
37752    extrahash=string.gsub(lower(extrahash),"[^a-z]","-")
37753    tfmdata.properties.fullname=format("%s-%s",tfmdata.properties.fullname,extrahash)
37754   end
37755  end
37756 end
37757 return tfmdata
37758end
37759
37760end -- closure
37761
37762do -- begin closure to overcome local limits and interference
37763
37764if not modules then modules={} end modules ['luatex-fonts-ext']={
37765 version=1.001,
37766 comment="companion to luatex-*.tex",
37767 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
37768 copyright="PRAGMA ADE / ConTeXt Development Team",
37769 license="see context related readme files"
37770}
37771if context then
37772--removed
37773
37774end
37775local byte=string.byte
37776local fonts=fonts
37777local handlers=fonts.handlers
37778local otf=handlers.otf
37779local afm=handlers.afm
37780local registerotffeature=otf.features.register
37781local registerafmfeature=afm.features.register
37782function fonts.loggers.onetimemessage() end
37783fonts.protrusions=fonts.protrusions  or {}
37784fonts.protrusions.setups=fonts.protrusions.setups or {}
37785local setups=fonts.protrusions.setups
37786setups['default']={ 
37787 factor=1,
37788 left=1,
37789 right=1,
37790 [0x002C]={ 0,1 },
37791 [0x002E]={ 0,1 },
37792 [0x003A]={ 0,1 },
37793 [0x003B]={ 0,1 },
37794 [0x002D]={ 0,1 },
37795 [0x2013]={ 0,0.50 },
37796 [0x2014]={ 0,0.33 },
37797 [0x3001]={ 0,1 },
37798 [0x3002]={ 0,1 },
37799 [0x060C]={ 0,1 },
37800 [0x061B]={ 0,1 },
37801 [0x06D4]={ 0,1 },
37802}
37803local function initializeprotrusion(tfmdata,value)
37804 if value then
37805  local setup=setups[value]
37806  if setup then
37807   local factor,left,right=setup.factor or 1,setup.left or 1,setup.right or 1
37808   local emwidth=tfmdata.parameters.quad
37809   tfmdata.parameters.protrusion={
37810    auto=true,
37811   }
37812   for i,chr in next,tfmdata.characters do
37813    local v,pl,pr=setup[i],nil,nil
37814    if v then
37815     pl,pr=v[1],v[2]
37816    end
37817    if pl and pl~=0 then chr.left_protruding=left*pl*factor end
37818    if pr and pr~=0 then chr.right_protruding=right*pr*factor end
37819   end
37820  end
37821 end
37822end
37823local specification={
37824 name="protrusion",
37825 description="shift characters into the left and or right margin",
37826 initializers={
37827  base=initializeprotrusion,
37828  node=initializeprotrusion,
37829 }
37830}
37831registerotffeature(specification)
37832registerafmfeature(specification)
37833fonts.expansions=fonts.expansions  or {}
37834fonts.expansions.setups=fonts.expansions.setups or {}
37835local setups=fonts.expansions.setups
37836setups['default']={ 
37837 stretch=2,
37838 shrink=2,
37839 step=.5,
37840 factor=1,
37841 [byte('A')]=0.5,[byte('B')]=0.7,[byte('C')]=0.7,[byte('D')]=0.5,[byte('E')]=0.7,
37842 [byte('F')]=0.7,[byte('G')]=0.5,[byte('H')]=0.7,[byte('K')]=0.7,[byte('M')]=0.7,
37843 [byte('N')]=0.7,[byte('O')]=0.5,[byte('P')]=0.7,[byte('Q')]=0.5,[byte('R')]=0.7,
37844 [byte('S')]=0.7,[byte('U')]=0.7,[byte('W')]=0.7,[byte('Z')]=0.7,
37845 [byte('a')]=0.7,[byte('b')]=0.7,[byte('c')]=0.7,[byte('d')]=0.7,[byte('e')]=0.7,
37846 [byte('g')]=0.7,[byte('h')]=0.7,[byte('k')]=0.7,[byte('m')]=0.7,[byte('n')]=0.7,
37847 [byte('o')]=0.7,[byte('p')]=0.7,[byte('q')]=0.7,[byte('s')]=0.7,[byte('u')]=0.7,
37848 [byte('w')]=0.7,[byte('z')]=0.7,
37849 [byte('2')]=0.7,[byte('3')]=0.7,[byte('6')]=0.7,[byte('8')]=0.7,[byte('9')]=0.7,
37850}
37851local function initializeexpansion(tfmdata,value)
37852 if value then
37853  local setup=setups[value]
37854  if setup then
37855   local factor=setup.factor or 1
37856   tfmdata.parameters.expansion={
37857    stretch=10*(setup.stretch or 0),
37858    shrink=10*(setup.shrink  or 0),
37859    step=10*(setup.step or 0),
37860    auto=true,
37861   }
37862   for i,chr in next,tfmdata.characters do
37863    local v=setup[i]
37864    if v and v~=0 then
37865     chr.expansion_factor=v*factor
37866    else 
37867     chr.expansion_factor=factor
37868    end
37869   end
37870  end
37871 end
37872end
37873local specification={
37874 name="expansion",
37875 description="apply hz optimization",
37876 initializers={
37877  base=initializeexpansion,
37878  node=initializeexpansion,
37879 }
37880}
37881registerotffeature(specification)
37882registerafmfeature(specification)
37883if not otf.features.normalize then
37884 otf.features.normalize=function(t)
37885  if t.rand then
37886   t.rand="random"
37887  end
37888  return t
37889 end
37890end
37891function fonts.helpers.nametoslot(name)
37892 local t=type(name)
37893 if t=="string" then
37894  local tfmdata=fonts.hashes.identifiers[currentfont()]
37895  local shared=tfmdata and tfmdata.shared
37896  local fntdata=shared and shared.rawdata
37897  return fntdata and fntdata.resources.unicodes[name]
37898 elseif t=="number" then
37899  return n
37900 end
37901end
37902fonts.encodings=fonts.encodings or {}
37903local reencodings={}
37904fonts.encodings.reencodings=reencodings
37905local function specialreencode(tfmdata,value)
37906 local encoding=value and reencodings[value]
37907 if encoding then
37908  local temp={}
37909  local char=tfmdata.characters
37910  for k,v in next,encoding do
37911   temp[k]=char[v]
37912  end
37913  for k,v in next,temp do
37914   char[k]=temp[k]
37915  end
37916  return string.format("reencoded:%s",value)
37917 end
37918end
37919local function initialize(tfmdata,value)
37920 tfmdata.postprocessors=tfmdata.postprocessors or {}
37921 table.insert(tfmdata.postprocessors,
37922  function(tfmdata)
37923   return specialreencode(tfmdata,value)
37924  end
37925 )
37926end
37927registerotffeature {
37928 name="reencode",
37929 description="reencode characters",
37930 manipulators={
37931  base=initialize,
37932  node=initialize,
37933 }
37934}
37935local function initialize(tfmdata,key,value)
37936 if value then
37937  tfmdata.mathparameters=nil
37938 end
37939end
37940registerotffeature {
37941 name="ignoremathconstants",
37942 description="ignore math constants table",
37943 initializers={
37944  base=initialize,
37945  node=initialize,
37946 }
37947}
37948
37949end -- closure
37950
37951do -- begin closure to overcome local limits and interference
37952
37953if not modules then modules={} end modules ['font-imp-tex']={
37954 version=1.001,
37955 comment="companion to font-ini.mkiv",
37956 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
37957 copyright="PRAGMA ADE / ConTeXt Development Team",
37958 license="see context related readme files"
37959}
37960local next=next
37961local fonts=fonts
37962local otf=fonts.handlers.otf
37963local registerotffeature=otf.features.register
37964local addotffeature=otf.addfeature
37965local tlig={
37966 type="ligature",
37967 order={ "tlig" },
37968 prepend=true,
37969 data={
37970  [0x2013]={ 0x002D,0x002D },
37971  [0x2014]={ 0x002D,0x002D,0x002D },
37972 },
37973}
37974local tquo={
37975 type="ligature",
37976 order={ "tquo" },
37977 prepend=true,
37978 data={
37979  [0x201C]={ 0x0060,0x0060 },
37980  [0x201D]={ 0x0027,0x0027 },
37981  [0x201E]={ 0x002C,0x002C },
37982 },
37983}
37984local trep={
37985 type="substitution",
37986 order={ "trep" },
37987 prepend=true,
37988 data={
37989  [0x0027]=0x2019,
37990 },
37991}
37992addotffeature("trep",trep) 
37993addotffeature("tlig",tlig)
37994addotffeature("tquo",tquo) 
37995registerotffeature { name="tlig",description="tex ligatures" }
37996registerotffeature { name="tquo",description="tex quotes" }
37997registerotffeature { name="trep",description="tex replacements" }
37998local anum_arabic={
37999 [0x0030]=0x0660,
38000 [0x0031]=0x0661,
38001 [0x0032]=0x0662,
38002 [0x0033]=0x0663,
38003 [0x0034]=0x0664,
38004 [0x0035]=0x0665,
38005 [0x0036]=0x0666,
38006 [0x0037]=0x0667,
38007 [0x0038]=0x0668,
38008 [0x0039]=0x0669,
38009}
38010local anum_persian={
38011 [0x0030]=0x06F0,
38012 [0x0031]=0x06F1,
38013 [0x0032]=0x06F2,
38014 [0x0033]=0x06F3,
38015 [0x0034]=0x06F4,
38016 [0x0035]=0x06F5,
38017 [0x0036]=0x06F6,
38018 [0x0037]=0x06F7,
38019 [0x0038]=0x06F8,
38020 [0x0039]=0x06F9,
38021}
38022local function valid(data)
38023 local features=data.resources.features
38024 if features then
38025  for k,v in next,features do
38026   for k,v in next,v do
38027    if v.arab then
38028     return true
38029    end
38030   end
38031  end
38032 end
38033end
38034local specification={
38035 {
38036  type="substitution",
38037  features={ arab={ urd=true,dflt=true } },
38038  order={ "anum" },
38039  data=anum_arabic,
38040  valid=valid,
38041 },
38042 {
38043  type="substitution",
38044  features={ arab={ urd=true } },
38045  order={ "anum" },
38046  data=anum_persian,
38047  valid=valid,
38048 },
38049}
38050addotffeature("anum",specification)
38051registerotffeature {
38052 name="anum",
38053 description="arabic digits",
38054}
38055
38056end -- closure
38057
38058do -- begin closure to overcome local limits and interference
38059
38060if not modules then modules={} end modules ['font-imp-ligatures']={
38061 version=1.001,
38062 comment="companion to font-ini.mkiv",
38063 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
38064 copyright="PRAGMA ADE / ConTeXt Development Team",
38065 license="see context related readme files"
38066}
38067local lpegmatch=lpeg.match
38068local utfsplit=utf.split
38069local settings_to_array=utilities.parsers.settings_to_array
38070local fonts=fonts
38071local otf=fonts.handlers.otf
38072local registerotffeature=otf.features.register
38073local addotffeature=otf.addfeature
38074local lookups={}
38075local protect={}
38076local revert={}
38077local zwjchar=0x200C
38078local zwj={ zwjchar }
38079addotffeature {
38080 name="blockligatures",
38081 type="chainsubstitution",
38082 nocheck=true,
38083 prepend=true,
38084 future=true,
38085 lookups={
38086  {
38087   type="multiple",
38088   data=lookups,
38089  },
38090 },
38091 data={
38092  rules=protect,
38093 }
38094}
38095addotffeature {
38096 name="blockligatures",
38097 type="chainsubstitution",
38098 nocheck=true,
38099 append=true,
38100 overload=false,
38101 lookups={
38102  {
38103   type="ligature",
38104   data=lookups,
38105  },
38106 },
38107 data={
38108  rules=revert,
38109 }
38110}
38111registerotffeature {
38112 name='blockligatures',
38113 description='block certain ligatures',
38114}
38115local splitter=lpeg.splitat(":")
38116local function blockligatures(str)
38117 local t=settings_to_array(str)
38118 for i=1,#t do
38119  local ti=t[i]
38120  local before,current,after=lpegmatch(splitter,ti)
38121  if current and after then
38122   if before then
38123    before=utfsplit(before)
38124    for i=1,#before do
38125     before[i]={ before[i] }
38126    end
38127   end
38128   if current then
38129    current=utfsplit(current)
38130   end
38131   if after then
38132    after=utfsplit(after)
38133    for i=1,#after do
38134     after[i]={ after[i] }
38135    end
38136   end
38137  else
38138   before=nil
38139   current=utfsplit(ti)
38140   after=nil
38141  end
38142  if #current>1 then
38143   local one=current[1]
38144   local two=current[2]
38145   lookups[one]={ one,zwjchar }
38146   local one={ one }
38147   local two={ two }
38148   local new=#protect+1
38149   protect[new]={
38150    before=before,
38151    current={ one,two },
38152    after=after,
38153    lookups={ 1,false },
38154   }
38155   revert[new]={
38156    current={ one,zwj },
38157    after={ two },
38158    lookups={ 1,false },
38159   }
38160  end
38161 end
38162end
38163otf.helpers.blockligatures=blockligatures
38164if context then
38165
38166--removed
38167
38168end
38169
38170end -- closure
38171
38172do -- begin closure to overcome local limits and interference
38173
38174if not modules then modules={} end modules ['font-imp-italics']={
38175 version=1.001,
38176 comment="companion to font-ini.mkiv and hand-ini.mkiv",
38177 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
38178 copyright="PRAGMA ADE / ConTeXt Development Team",
38179 license="see context related readme files"
38180}
38181local next,tonumber=next,tonumber
38182local fonts=fonts
38183local handlers=fonts.handlers
38184local registerotffeature=handlers.otf.features.register
38185local registerafmfeature=handlers.afm.features.register
38186local function initialize(tfmdata,value) 
38187 if value then
38188  local parameters=tfmdata.parameters
38189  local italicangle=parameters.italicangle
38190  if italicangle and italicangle~=0 then
38191   local properties=tfmdata.properties
38192   local factor=tonumber(value) or 1
38193   properties.hasitalics=true
38194   properties.autoitalicamount=factor*(parameters.uwidth or 40)/2
38195  end
38196 end
38197end
38198local specification={
38199 name="itlc",
38200 description="italic correction",
38201 initializers={
38202  base=initialize,
38203  node=initialize,
38204 }
38205}
38206registerotffeature(specification)
38207registerafmfeature(specification)
38208if context then
38209
38210--removed
38211
38212end
38213
38214end -- closure
38215
38216do -- begin closure to overcome local limits and interference
38217
38218if not modules then modules={} end modules ['font-imp-effects']={
38219 version=1.001,
38220 comment="companion to font-ini.mkiv",
38221 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
38222 copyright="PRAGMA ADE / ConTeXt Development Team",
38223 license="see context related readme files"
38224}
38225local next,type,tonumber=next,type,tonumber
38226local is_boolean=string.is_boolean
38227local fonts=fonts
38228local handlers=fonts.handlers
38229local registerotffeature=handlers.otf.features.register
38230local registerafmfeature=handlers.afm.features.register
38231local settings_to_hash=utilities.parsers.settings_to_hash_colon_too
38232local helpers=fonts.helpers
38233local prependcommands=helpers.prependcommands
38234local charcommand=helpers.commands.char
38235local leftcommand=helpers.commands.left
38236local rightcommand=helpers.commands.right
38237local upcommand=helpers.commands.up
38238local downcommand=helpers.commands.down
38239local dummycommand=helpers.commands.dummy
38240local report_effect=logs.reporter("fonts","effect")
38241local report_slant=logs.reporter("fonts","slant")
38242local report_extend=logs.reporter("fonts","extend")
38243local report_squeeze=logs.reporter("fonts","squeeze")
38244local trace=false
38245trackers.register("fonts.effect",function(v) trace=v end)
38246trackers.register("fonts.slant",function(v) trace=v end)
38247trackers.register("fonts.extend",function(v) trace=v end)
38248trackers.register("fonts.squeeze",function(v) trace=v end)
38249local function initializeslant(tfmdata,value)
38250 value=tonumber(value)
38251 if not value then
38252  value=0
38253 elseif value>1 then
38254  value=1
38255 elseif value<-1 then
38256  value=-1
38257 end
38258 if trace then
38259  report_slant("applying %0.3f",value)
38260 end
38261 tfmdata.parameters.slantfactor=value
38262end
38263local specification={
38264 name="slant",
38265 description="slant glyphs",
38266 initializers={
38267  base=initializeslant,
38268  node=initializeslant,
38269 }
38270}
38271registerotffeature(specification)
38272registerafmfeature(specification)
38273local function initializeextend(tfmdata,value)
38274 value=tonumber(value)
38275 if not value then
38276  value=0
38277 elseif value>10 then
38278  value=10
38279 elseif value<-10 then
38280  value=-10
38281 end
38282 if trace then
38283  report_extend("applying %0.3f",value)
38284 end
38285 tfmdata.parameters.extendfactor=value
38286end
38287local specification={
38288 name="extend",
38289 description="scale glyphs horizontally",
38290 initializers={
38291  base=initializeextend,
38292  node=initializeextend,
38293 }
38294}
38295registerotffeature(specification)
38296registerafmfeature(specification)
38297local function initializesqueeze(tfmdata,value)
38298 value=tonumber(value)
38299 if not value then
38300  value=0
38301 elseif value>10 then
38302  value=10
38303 elseif value<-10 then
38304  value=-10
38305 end
38306 if trace then
38307  report_squeeze("applying %0.3f",value)
38308 end
38309 tfmdata.parameters.squeezefactor=value
38310end
38311local specification={
38312 name="squeeze",
38313 description="scale glyphs vertically",
38314 initializers={
38315  base=initializesqueeze,
38316  node=initializesqueeze,
38317 }
38318}
38319registerotffeature(specification)
38320registerafmfeature(specification)
38321local effects={
38322 inner=0,
38323 normal=0,
38324 outer=1,
38325 outline=1,
38326 both=2,
38327 hidden=3,
38328}
38329local function initializeeffect(tfmdata,value)
38330 local spec
38331 if type(value)=="number" then
38332  spec={ width=value }
38333 else
38334  spec=settings_to_hash(value)
38335 end
38336 local effect=spec.effect or "both"
38337 local width=tonumber(spec.width) or 0
38338 local mode=effects[effect]
38339 if not mode then
38340  report_effect("invalid effect %a",effect)
38341 elseif width==0 and mode==0 then
38342  report_effect("invalid width %a for effect %a",width,effect)
38343 else
38344  local parameters=tfmdata.parameters
38345  local properties=tfmdata.properties
38346  parameters.mode=mode
38347  parameters.width=width*1000
38348  if is_boolean(spec.auto)==true then
38349   local squeeze=1-width/20
38350   local average=(1-squeeze)*width*100
38351   spec.squeeze=squeeze
38352   spec.extend=1+width/2
38353   spec.wdelta=average
38354   spec.hdelta=average/2
38355   spec.ddelta=average/2
38356   spec.vshift=average/2
38357  end
38358  local factor=tonumber(spec.factor)  or 0
38359  local hfactor=tonumber(spec.hfactor) or factor
38360  local vfactor=tonumber(spec.vfactor) or factor
38361  local delta=tonumber(spec.delta)   or 1
38362  local wdelta=tonumber(spec.wdelta)  or delta
38363  local hdelta=tonumber(spec.hdelta)  or delta
38364  local ddelta=tonumber(spec.ddelta)  or hdelta
38365  local vshift=tonumber(spec.vshift)  or 0
38366  local slant=spec.slant
38367  local extend=spec.extend
38368  local squeeze=spec.squeeze
38369  if slant then
38370   initializeslant(tfmdata,slant)
38371  end
38372  if extend then
38373   initializeextend(tfmdata,extend)
38374  end
38375  if squeeze then
38376   initializesqueeze(tfmdata,squeeze)
38377  end
38378  properties.effect={
38379   effect=effect,
38380   width=width,
38381   factor=factor,
38382   hfactor=hfactor,
38383   vfactor=vfactor,
38384   wdelta=wdelta,
38385   hdelta=hdelta,
38386   ddelta=ddelta,
38387   vshift=vshift,
38388   slant=tfmdata.parameters.slantfactor,
38389   extend=tfmdata.parameters.extendfactor,
38390   squeeze=tfmdata.parameters.squeezefactor,
38391  }
38392 end
38393end
38394local rules={
38395 "RadicalRuleThickness",
38396 "OverbarRuleThickness",
38397 "FractionRuleThickness",
38398 "UnderbarRuleThickness",
38399}
38400local function setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze,multiplier)
38401 if dy~=0 then
38402  for i=1,#rules do
38403   local name=rules[i]
38404   local value=mathparameters[name]
38405   if value then
38406      mathparameters[name]=(squeeze or 1)*(value+dy)
38407   end
38408  end
38409 end
38410end
38411local function setmathcharacters(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta)
38412 local function wdpatch(char)
38413  if wsnap~=0 then
38414   char.width=char.width+wdelta/2
38415  end
38416 end
38417 local function htpatch(char)
38418  if hsnap~=0 then
38419   local height=char.height
38420   if height then
38421    char.height=char.height+2*dy
38422   end
38423  end
38424 end
38425 local character=characters[0x221A]
38426 if character and character.next then
38427  local char=character
38428  local next=character.next
38429  wdpatch(char)
38430  htpatch(char)
38431  while next do
38432   char=characters[next]
38433   wdpatch(char)
38434   htpatch(char)
38435   next=char.next
38436  end
38437  if char then
38438   local v=char.vert_variants
38439   if v then
38440    local top=v[#v]
38441    if top then
38442     local char=characters[top.glyph]
38443     htpatch(char)
38444    end
38445   end
38446  end
38447 end
38448end
38449local function manipulateeffect(tfmdata)
38450 local effect=tfmdata.properties.effect
38451 if effect then
38452  local characters=tfmdata.characters
38453  local parameters=tfmdata.parameters
38454  local mathparameters=tfmdata.mathparameters
38455  local multiplier=effect.width*100
38456  local factor=parameters.factor
38457  local hfactor=parameters.hfactor
38458  local vfactor=parameters.vfactor
38459  local wdelta=effect.wdelta*hfactor*multiplier
38460  local hdelta=effect.hdelta*vfactor*multiplier
38461  local ddelta=effect.ddelta*vfactor*multiplier
38462  local vshift=effect.vshift*vfactor*multiplier
38463  local squeeze=effect.squeeze
38464  local hshift=wdelta/2
38465  local dx=multiplier*vfactor
38466  local dy=vshift
38467  local factor=(1+effect.factor)*factor
38468  local hfactor=(1+effect.hfactor)*hfactor
38469  local vfactor=(1+effect.vfactor)*vfactor
38470  vshift=vshift~=0 and upcommand[vshift] or false
38471  hshift=rightcommand[hshift]
38472  for unicode,character in next,characters do
38473   local oldwidth=character.width
38474   local oldheight=character.height
38475   local olddepth=character.depth
38476   if oldwidth and oldwidth>0 then
38477    character.width=oldwidth+wdelta
38478    local commands=character.commands
38479    if vshift then
38480     if commands then
38481      prependcommands (commands,
38482       hshift,
38483       vshift
38484      )
38485     else
38486      character.commands={
38487       hshift,
38488       vshift,
38489       charcommand[unicode]
38490      }
38491     end
38492    else
38493     if commands then
38494       prependcommands (commands,
38495        hshift
38496       )
38497     else
38498      character.commands={
38499       hshift,
38500       charcommand[unicode]
38501       }
38502     end
38503    end
38504   end
38505   if oldheight and oldheight>0 then
38506      character.height=oldheight+hdelta
38507   end
38508   if olddepth and olddepth>0 then
38509      character.depth=olddepth+ddelta
38510   end
38511  end
38512  if mathparameters then
38513   setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze,multiplier)
38514   setmathcharacters(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta)
38515  end
38516  parameters.factor=factor
38517  parameters.hfactor=hfactor
38518  parameters.vfactor=vfactor
38519  if trace then
38520   report_effect("applying")
38521   report_effect("  effect  : %s",effect.effect)
38522   report_effect("  width   : %s => %s",effect.width,multiplier)
38523   report_effect("  factor  : %s => %s",effect.factor,factor )
38524   report_effect("  hfactor : %s => %s",effect.hfactor,hfactor)
38525   report_effect("  vfactor : %s => %s",effect.vfactor,vfactor)
38526   report_effect("  wdelta  : %s => %s",effect.wdelta,wdelta)
38527   report_effect("  hdelta  : %s => %s",effect.hdelta,hdelta)
38528   report_effect("  ddelta  : %s => %s",effect.ddelta,ddelta)
38529  end
38530 end
38531end
38532local specification={
38533 name="effect",
38534 description="apply effects to glyphs",
38535 initializers={
38536  base=initializeeffect,
38537  node=initializeeffect,
38538 },
38539 manipulators={
38540  base=manipulateeffect,
38541  node=manipulateeffect,
38542 },
38543}
38544registerotffeature(specification)
38545registerafmfeature(specification)
38546local function initializeoutline(tfmdata,value)
38547 value=tonumber(value)
38548 if not value then
38549  value=0
38550 else
38551  value=tonumber(value) or 0
38552 end
38553 local parameters=tfmdata.parameters
38554 local properties=tfmdata.properties
38555 parameters.mode=effects.outline
38556 parameters.width=value*1000
38557 properties.effect={
38558  effect=effect,
38559  width=width,
38560 }
38561end
38562local specification={
38563 name="outline",
38564 description="outline glyphs",
38565 initializers={
38566  base=initializeoutline,
38567  node=initializeoutline,
38568 }
38569}
38570registerotffeature(specification)
38571registerafmfeature(specification)
38572
38573end -- closure
38574
38575do -- begin closure to overcome local limits and interference
38576
38577
38578fonts.handlers.otf.addfeature {
38579 ["dataset"]={
38580  {
38581   ["data"]={
38582 ["À"]={ "A","̀" },
38583 ["Á"]={ "A","́" },
38584 ["Â"]={ "A","̂" },
38585 ["Ã"]={ "A","̃" },
38586 ["Ä"]={ "A","̈" },
38587 ["Å"]={ "A","̊" },
38588 ["Ç"]={ "C","̧" },
38589 ["È"]={ "E","̀" },
38590 ["É"]={ "E","́" },
38591 ["Ê"]={ "E","̂" },
38592 ["Ë"]={ "E","̈" },
38593 ["Ì"]={ "I","̀" },
38594 ["Í"]={ "I","́" },
38595 ["Î"]={ "I","̂" },
38596 ["Ï"]={ "I","̈" },
38597 ["Ñ"]={ "N","̃" },
38598 ["Ò"]={ "O","̀" },
38599 ["Ó"]={ "O","́" },
38600 ["Ô"]={ "O","̂" },
38601 ["Õ"]={ "O","̃" },
38602 ["Ö"]={ "O","̈" },
38603 ["Ù"]={ "U","̀" },
38604 ["Ú"]={ "U","́" },
38605 ["Û"]={ "U","̂" },
38606 ["Ü"]={ "U","̈" },
38607 ["Ý"]={ "Y","́" },
38608 ["à"]={ "a","̀" },
38609 ["á"]={ "a","́" },
38610 ["â"]={ "a","̂" },
38611 ["ã"]={ "a","̃" },
38612 ["ä"]={ "a","̈" },
38613 ["å"]={ "a","̊" },
38614 ["ç"]={ "c","̧" },
38615 ["è"]={ "e","̀" },
38616 ["é"]={ "e","́" },
38617 ["ê"]={ "e","̂" },
38618 ["ë"]={ "e","̈" },
38619 ["ì"]={ "i","̀" },
38620 ["í"]={ "i","́" },
38621 ["î"]={ "i","̂" },
38622 ["ï"]={ "i","̈" },
38623 ["ñ"]={ "n","̃" },
38624 ["ò"]={ "o","̀" },
38625 ["ó"]={ "o","́" },
38626 ["ô"]={ "o","̂" },
38627 ["õ"]={ "o","̃" },
38628 ["ö"]={ "o","̈" },
38629 ["ù"]={ "u","̀" },
38630 ["ú"]={ "u","́" },
38631 ["û"]={ "u","̂" },
38632 ["ü"]={ "u","̈" },
38633 ["ý"]={ "y","́" },
38634 ["ÿ"]={ "y","̈" },
38635 ["Ā"]={ "A","̄" },
38636 ["ā"]={ "a","̄" },
38637 ["Ă"]={ "A","̆" },
38638 ["ă"]={ "a","̆" },
38639 ["Ą"]={ "A","̨" },
38640 ["ą"]={ "a","̨" },
38641 ["Ć"]={ "C","́" },
38642 ["ć"]={ "c","́" },
38643 ["Ĉ"]={ "C","̂" },
38644 ["ĉ"]={ "c","̂" },
38645 ["Ċ"]={ "C","̇" },
38646 ["ċ"]={ "c","̇" },
38647 ["Č"]={ "C","̌" },
38648 ["č"]={ "c","̌" },
38649 ["Ď"]={ "D","̌" },
38650 ["ď"]={ "d","̌" },
38651 ["Ē"]={ "E","̄" },
38652 ["ē"]={ "e","̄" },
38653 ["Ĕ"]={ "E","̆" },
38654 ["ĕ"]={ "e","̆" },
38655 ["Ė"]={ "E","̇" },
38656 ["ė"]={ "e","̇" },
38657 ["Ę"]={ "E","̨" },
38658 ["ę"]={ "e","̨" },
38659 ["Ě"]={ "E","̌" },
38660 ["ě"]={ "e","̌" },
38661 ["Ĝ"]={ "G","̂" },
38662 ["ĝ"]={ "g","̂" },
38663 ["Ğ"]={ "G","̆" },
38664 ["ğ"]={ "g","̆" },
38665 ["Ġ"]={ "G","̇" },
38666 ["ġ"]={ "g","̇" },
38667 ["Ģ"]={ "G","̧" },
38668 ["ģ"]={ "g","̧" },
38669 ["Ĥ"]={ "H","̂" },
38670 ["ĥ"]={ "h","̂" },
38671 ["Ĩ"]={ "I","̃" },
38672 ["ĩ"]={ "i","̃" },
38673 ["Ī"]={ "I","̄" },
38674 ["ī"]={ "i","̄" },
38675 ["Ĭ"]={ "I","̆" },
38676 ["ĭ"]={ "i","̆" },
38677 ["Į"]={ "I","̨" },
38678 ["į"]={ "i","̨" },
38679 ["İ"]={ "I","̇" },
38680 ["Ĵ"]={ "J","̂" },
38681 ["ĵ"]={ "j","̂" },
38682 ["Ķ"]={ "K","̧" },
38683 ["ķ"]={ "k","̧" },
38684 ["Ĺ"]={ "L","́" },
38685 ["ĺ"]={ "l","́" },
38686 ["Ļ"]={ "L","̧" },
38687 ["ļ"]={ "l","̧" },
38688 ["Ľ"]={ "L","̌" },
38689 ["ľ"]={ "l","̌" },
38690 ["Ń"]={ "N","́" },
38691 ["ń"]={ "n","́" },
38692 ["Ņ"]={ "N","̧" },
38693 ["ņ"]={ "n","̧" },
38694 ["Ň"]={ "N","̌" },
38695 ["ň"]={ "n","̌" },
38696 ["Ō"]={ "O","̄" },
38697 ["ō"]={ "o","̄" },
38698 ["Ŏ"]={ "O","̆" },
38699 ["ŏ"]={ "o","̆" },
38700 ["Ő"]={ "O","̋" },
38701 ["ő"]={ "o","̋" },
38702 ["Ŕ"]={ "R","́" },
38703 ["ŕ"]={ "r","́" },
38704 ["Ŗ"]={ "R","̧" },
38705 ["ŗ"]={ "r","̧" },
38706 ["Ř"]={ "R","̌" },
38707 ["ř"]={ "r","̌" },
38708 ["Ś"]={ "S","́" },
38709 ["ś"]={ "s","́" },
38710 ["Ŝ"]={ "S","̂" },
38711 ["ŝ"]={ "s","̂" },
38712 ["Ş"]={ "S","̧" },
38713 ["ş"]={ "s","̧" },
38714 ["Š"]={ "S","̌" },
38715 ["š"]={ "s","̌" },
38716 ["Ţ"]={ "T","̧" },
38717 ["ţ"]={ "t","̧" },
38718 ["Ť"]={ "T","̌" },
38719 ["ť"]={ "t","̌" },
38720 ["Ũ"]={ "U","̃" },
38721 ["ũ"]={ "u","̃" },
38722 ["Ū"]={ "U","̄" },
38723 ["ū"]={ "u","̄" },
38724 ["Ŭ"]={ "U","̆" },
38725 ["ŭ"]={ "u","̆" },
38726 ["Ů"]={ "U","̊" },
38727 ["ů"]={ "u","̊" },
38728 ["Ű"]={ "U","̋" },
38729 ["ű"]={ "u","̋" },
38730 ["Ų"]={ "U","̨" },
38731 ["ų"]={ "u","̨" },
38732 ["Ŵ"]={ "W","̂" },
38733 ["ŵ"]={ "w","̂" },
38734 ["Ŷ"]={ "Y","̂" },
38735 ["ŷ"]={ "y","̂" },
38736 ["Ÿ"]={ "Y","̈" },
38737 ["Ź"]={ "Z","́" },
38738 ["ź"]={ "z","́" },
38739 ["Ż"]={ "Z","̇" },
38740 ["ż"]={ "z","̇" },
38741 ["Ž"]={ "Z","̌" },
38742 ["ž"]={ "z","̌" },
38743 ["Ơ"]={ "O","̛" },
38744 ["ơ"]={ "o","̛" },
38745 ["Ư"]={ "U","̛" },
38746 ["ư"]={ "u","̛" },
38747 ["Ǎ"]={ "A","̌" },
38748 ["ǎ"]={ "a","̌" },
38749 ["Ǐ"]={ "I","̌" },
38750 ["ǐ"]={ "i","̌" },
38751 ["Ǒ"]={ "O","̌" },
38752 ["ǒ"]={ "o","̌" },
38753 ["Ǔ"]={ "U","̌" },
38754 ["ǔ"]={ "u","̌" },
38755 ["Ǖ"]={ "Ü","̄" },
38756 ["ǖ"]={ "ü","̄" },
38757 ["Ǘ"]={ "Ü","́" },
38758 ["ǘ"]={ "ü","́" },
38759 ["Ǚ"]={ "Ü","̌" },
38760 ["ǚ"]={ "ü","̌" },
38761 ["Ǜ"]={ "Ü","̀" },
38762 ["ǜ"]={ "ü","̀" },
38763 ["Ǟ"]={ "Ä","̄" },
38764 ["ǟ"]={ "ä","̄" },
38765 ["Ǡ"]={ "Ȧ","̄" },
38766 ["ǡ"]={ "ȧ","̄" },
38767 ["Ǣ"]={ "Æ","̄" },
38768 ["ǣ"]={ "æ","̄" },
38769 ["Ǧ"]={ "G","̌" },
38770 ["ǧ"]={ "g","̌" },
38771 ["Ǩ"]={ "K","̌" },
38772 ["ǩ"]={ "k","̌" },
38773 ["Ǫ"]={ "O","̨" },
38774 ["ǫ"]={ "o","̨" },
38775 ["Ǭ"]={ "Ǫ","̄" },
38776 ["ǭ"]={ "ǫ","̄" },
38777 ["Ǯ"]={ "Ʒ","̌" },
38778 ["ǯ"]={ "ʒ","̌" },
38779 ["ǰ"]={ "j","̌" },
38780 ["Ǵ"]={ "G","́" },
38781 ["ǵ"]={ "g","́" },
38782 ["Ǹ"]={ "N","̀" },
38783 ["ǹ"]={ "n","̀" },
38784 ["Ǻ"]={ "Å","́" },
38785 ["ǻ"]={ "å","́" },
38786 ["Ǽ"]={ "Æ","́" },
38787 ["ǽ"]={ "æ","́" },
38788 ["Ǿ"]={ "Ø","́" },
38789 ["ǿ"]={ "ø","́" },
38790 ["Ȁ"]={ "A","̏" },
38791 ["ȁ"]={ "a","̏" },
38792 ["Ȃ"]={ "A","̑" },
38793 ["ȃ"]={ "a","̑" },
38794 ["Ȅ"]={ "E","̏" },
38795 ["ȅ"]={ "e","̏" },
38796 ["Ȇ"]={ "E","̑" },
38797 ["ȇ"]={ "e","̑" },
38798 ["Ȉ"]={ "I","̏" },
38799 ["ȉ"]={ "i","̏" },
38800 ["Ȋ"]={ "I","̑" },
38801 ["ȋ"]={ "i","̑" },
38802 ["Ȍ"]={ "O","̏" },
38803 ["ȍ"]={ "o","̏" },
38804 ["Ȏ"]={ "O","̑" },
38805 ["ȏ"]={ "o","̑" },
38806 ["Ȑ"]={ "R","̏" },
38807 ["ȑ"]={ "r","̏" },
38808 ["Ȓ"]={ "R","̑" },
38809 ["ȓ"]={ "r","̑" },
38810 ["Ȕ"]={ "U","̏" },
38811 ["ȕ"]={ "u","̏" },
38812 ["Ȗ"]={ "U","̑" },
38813 ["ȗ"]={ "u","̑" },
38814 ["Ș"]={ "S","̦" },
38815 ["ș"]={ "s","̦" },
38816 ["Ț"]={ "T","̦" },
38817 ["ț"]={ "t","̦" },
38818 ["Ȟ"]={ "H","̌" },
38819 ["ȟ"]={ "h","̌" },
38820 ["Ȧ"]={ "A","̇" },
38821 ["ȧ"]={ "a","̇" },
38822 ["Ȩ"]={ "E","̧" },
38823 ["ȩ"]={ "e","̧" },
38824 ["Ȫ"]={ "Ö","̄" },
38825 ["ȫ"]={ "ö","̄" },
38826 ["Ȭ"]={ "Õ","̄" },
38827 ["ȭ"]={ "õ","̄" },
38828 ["Ȯ"]={ "O","̇" },
38829 ["ȯ"]={ "o","̇" },
38830 ["Ȱ"]={ "Ȯ","̄" },
38831 ["ȱ"]={ "ȯ","̄" },
38832 ["Ȳ"]={ "Y","̄" },
38833 ["ȳ"]={ "y","̄" },
38834 ["̈́"]={ "̈","́" },
38835 ["΅"]={ "¨","́" },
38836 ["Ά"]={ "Α","́" },
38837 ["Έ"]={ "Ε","́" },
38838 ["Ή"]={ "Η","́" },
38839 ["Ί"]={ "Ι","́" },
38840 ["Ό"]={ "Ο","́" },
38841 ["Ύ"]={ "Υ","́" },
38842 ["Ώ"]={ "Ω","́" },
38843 ["ΐ"]={ "ϊ","́" },
38844 ["Ϊ"]={ "Ι","̈" },
38845 ["Ϋ"]={ "Υ","̈" },
38846 ["ά"]={ "α","́" },
38847 ["έ"]={ "ε","́" },
38848 ["ή"]={ "η","́" },
38849 ["ί"]={ "ι","́" },
38850 ["ΰ"]={ "ϋ","́" },
38851 ["ϊ"]={ "ι","̈" },
38852 ["ϋ"]={ "υ","̈" },
38853 ["ό"]={ "ο","́" },
38854 ["ύ"]={ "υ","́" },
38855 ["ώ"]={ "ω","́" },
38856 ["ϓ"]={ "ϒ","́" },
38857 ["ϔ"]={ "ϒ","̈" },
38858 ["Ѐ"]={ "Е","̀" },
38859 ["Ё"]={ "Е","̈" },
38860 ["Ѓ"]={ "Г","́" },
38861 ["Ї"]={ "І","̈" },
38862 ["Ќ"]={ "К","́" },
38863 ["Ѝ"]={ "И","̀" },
38864 ["Ў"]={ "У","̆" },
38865 ["Й"]={ "И","̆" },
38866 ["й"]={ "и","̆" },
38867 ["ѐ"]={ "е","̀" },
38868 ["ё"]={ "е","̈" },
38869 ["ѓ"]={ "г","́" },
38870 ["ї"]={ "і","̈" },
38871 ["ќ"]={ "к","́" },
38872 ["ѝ"]={ "и","̀" },
38873 ["ў"]={ "у","̆" },
38874 ["Ѷ"]={ "Ѵ","̏" },
38875 ["ѷ"]={ "ѵ","̏" },
38876 ["Ӂ"]={ "Ж","̆" },
38877 ["ӂ"]={ "ж","̆" },
38878 ["Ӑ"]={ "А","̆" },
38879 ["ӑ"]={ "а","̆" },
38880 ["Ӓ"]={ "А","̈" },
38881 ["ӓ"]={ "а","̈" },
38882 ["Ӗ"]={ "Е","̆" },
38883 ["ӗ"]={ "е","̆" },
38884 ["Ӛ"]={ "Ә","̈" },
38885 ["ӛ"]={ "ә","̈" },
38886 ["Ӝ"]={ "Ж","̈" },
38887 ["ӝ"]={ "ж","̈" },
38888 ["Ӟ"]={ "З","̈" },
38889 ["ӟ"]={ "з","̈" },
38890 ["Ӣ"]={ "И","̄" },
38891 ["ӣ"]={ "и","̄" },
38892 ["Ӥ"]={ "И","̈" },
38893 ["ӥ"]={ "и","̈" },
38894 ["Ӧ"]={ "О","̈" },
38895 ["ӧ"]={ "о","̈" },
38896 ["Ӫ"]={ "Ө","̈" },
38897 ["ӫ"]={ "ө","̈" },
38898 ["Ӭ"]={ "Э","̈" },
38899 ["ӭ"]={ "э","̈" },
38900 ["Ӯ"]={ "У","̄" },
38901 ["ӯ"]={ "у","̄" },
38902 ["Ӱ"]={ "У","̈" },
38903 ["ӱ"]={ "у","̈" },
38904 ["Ӳ"]={ "У","̋" },
38905 ["ӳ"]={ "у","̋" },
38906 ["Ӵ"]={ "Ч","̈" },
38907 ["ӵ"]={ "ч","̈" },
38908 ["Ӹ"]={ "Ы","̈" },
38909 ["ӹ"]={ "ы","̈" },
38910 ["آ"]={ "ا","ٓ" },
38911 ["أ"]={ "ا","ٔ" },
38912 ["ؤ"]={ "و","ٔ" },
38913 ["إ"]={ "ا","ٕ" },
38914 ["ئ"]={ "ي","ٔ" },
38915 ["ۀ"]={ "ە","ٔ" },
38916 ["ۂ"]={ "ہ","ٔ" },
38917 ["ۓ"]={ "ے","ٔ" },
38918 [""]={ "","" },
38919 [""]={ "","" },
38920 [""]={ "","" },
38921 [""]={ "","" },
38922 [""]={ "","" },
38923 [""]={ "","" },
38924 [""]={ "","" },
38925 [""]={ "","" },
38926 [""]={ "","" },
38927 [""]={ "","" },
38928 [""]={ "","" },
38929 [""]={ "","" },
38930 [""]={ "","" },
38931 [""]={ "","" },
38932 [""]={ "","" },
38933 [""]={ "","" },
38934 [""]={ "","" },
38935 [""]={ "","" },
38936 [""]={ "","" },
38937 [""]={ "","" },
38938 [""]={ "","" },
38939 [""]={ "","" },
38940 [""]={ "","" },
38941 [""]={ "","" },
38942 [""]={ "","" },
38943 [""]={ "","" },
38944 [""]={ "","" },
38945 [""]={ "","" },
38946 [""]={ "","" },
38947 [""]={ "","" },
38948 [""]={ "","" },
38949 [""]={ "","" },
38950 [""]={ "ಿ","" },
38951 [""]={ "","" },
38952 [""]={ "","" },
38953 [""]={ "","" },
38954 [""]={ "","" },
38955 [""]={ "","" },
38956 [""]={ "","" },
38957 [""]={ "","" },
38958 [""]={ "","" },
38959 [""]={ "","" },
38960 [""]={ "","" },
38961 [""]={ "","" },
38962 [""]={ "","" },
38963 [""]={ "","" },
38964 [""]={ "","" },
38965 [""]={ "","" },
38966 [""]={ "","" },
38967 [""]={ "","" },
38968 [""]={ "","" },
38969 [""]={ "","" },
38970 [""]={ "","" },
38971 [""]={ "","" },
38972 [""]={ "","" },
38973 [""]={ "","" },
38974 [""]={ "","" },
38975 [""]={ "","" },
38976 [""]={ "","" },
38977 [""]={ "","" },
38978 [""]={ "","" },
38979 [""]={ "","" },
38980 [""]={ "","" },
38981 [""]={ "","" },
38982 [""]={ "","" },
38983 [""]={ "","" },
38984 [""]={ "","" },
38985 [""]={ "","" },
38986 [""]={ "","" },
38987 [""]={ "","" },
38988 [""]={ "","" },
38989 [""]={ "ᬿ","" },
38990 [""]={ "","" },
38991 [""]={ "A","̥" },
38992 [""]={ "a","̥" },
38993 [""]={ "B","̇" },
38994 [""]={ "b","̇" },
38995 [""]={ "B","̣" },
38996 [""]={ "b","̣" },
38997 [""]={ "B","̱" },
38998 [""]={ "b","̱" },
38999 [""]={ "Ç","́" },
39000 [""]={ "ç","́" },
39001 [""]={ "D","̇" },
39002 [""]={ "d","̇" },
39003 [""]={ "D","̣" },
39004 [""]={ "d","̣" },
39005 [""]={ "D","̱" },
39006 [""]={ "d","̱" },
39007 [""]={ "D","̧" },
39008 [""]={ "d","̧" },
39009 [""]={ "D","̭" },
39010 [""]={ "d","̭" },
39011 [""]={ "Ē","̀" },
39012 [""]={ "ē","̀" },
39013 [""]={ "Ē","́" },
39014 [""]={ "ē","́" },
39015 [""]={ "E","̭" },
39016 [""]={ "e","̭" },
39017 [""]={ "E","̰" },
39018 [""]={ "e","̰" },
39019 [""]={ "Ȩ","̆" },
39020 [""]={ "ȩ","̆" },
39021 [""]={ "F","̇" },
39022 [""]={ "f","̇" },
39023 [""]={ "G","̄" },
39024 [""]={ "g","̄" },
39025 [""]={ "H","̇" },
39026 [""]={ "h","̇" },
39027 [""]={ "H","̣" },
39028 [""]={ "h","̣" },
39029 [""]={ "H","̈" },
39030 [""]={ "h","̈" },
39031 [""]={ "H","̧" },
39032 [""]={ "h","̧" },
39033 [""]={ "H","̮" },
39034 [""]={ "h","̮" },
39035 [""]={ "I","̰" },
39036 [""]={ "i","̰" },
39037 [""]={ "Ï","́" },
39038 [""]={ "ï","́" },
39039 [""]={ "K","́" },
39040 [""]={ "k","́" },
39041 [""]={ "K","̣" },
39042 [""]={ "k","̣" },
39043 [""]={ "K","̱" },
39044 [""]={ "k","̱" },
39045 [""]={ "L","̣" },
39046 [""]={ "l","̣" },
39047 [""]={ "","̄" },
39048 [""]={ "","̄" },
39049 [""]={ "L","̱" },
39050 [""]={ "l","̱" },
39051 [""]={ "L","̭" },
39052 [""]={ "l","̭" },
39053 [""]={ "M","́" },
39054 ["ḿ"]={ "m","́" },
39055 [""]={ "M","̇" },
39056 [""]={ "m","̇" },
39057 [""]={ "M","̣" },
39058 [""]={ "m","̣" },
39059 [""]={ "N","̇" },
39060 [""]={ "n","̇" },
39061 [""]={ "N","̣" },
39062 [""]={ "n","̣" },
39063 [""]={ "N","̱" },
39064 [""]={ "n","̱" },
39065 [""]={ "N","̭" },
39066 [""]={ "n","̭" },
39067 [""]={ "Õ","́" },
39068 [""]={ "õ","́" },
39069 [""]={ "Õ","̈" },
39070 [""]={ "õ","̈" },
39071 [""]={ "Ō","̀" },
39072 [""]={ "ō","̀" },
39073 [""]={ "Ō","́" },
39074 [""]={ "ō","́" },
39075 [""]={ "P","́" },
39076 [""]={ "p","́" },
39077 [""]={ "P","̇" },
39078 [""]={ "p","̇" },
39079 [""]={ "R","̇" },
39080 [""]={ "r","̇" },
39081 [""]={ "R","̣" },
39082 [""]={ "r","̣" },
39083 [""]={ "","̄" },
39084 [""]={ "","̄" },
39085 [""]={ "R","̱" },
39086 [""]={ "r","̱" },
39087 [""]={ "S","̇" },
39088 [""]={ "s","̇" },
39089 [""]={ "S","̣" },
39090 [""]={ "s","̣" },
39091 [""]={ "Ś","̇" },
39092 [""]={ "ś","̇" },
39093 [""]={ "Š","̇" },
39094 [""]={ "š","̇" },
39095 [""]={ "","̇" },
39096 [""]={ "","̇" },
39097 [""]={ "T","̇" },
39098 [""]={ "t","̇" },
39099 [""]={ "T","̣" },
39100 [""]={ "t","̣" },
39101 [""]={ "T","̱" },
39102 [""]={ "t","̱" },
39103 [""]={ "T","̭" },
39104 [""]={ "t","̭" },
39105 [""]={ "U","̤" },
39106 [""]={ "u","̤" },
39107 [""]={ "U","̰" },
39108 [""]={ "u","̰" },
39109 [""]={ "U","̭" },
39110 [""]={ "u","̭" },
39111 [""]={ "Ũ","́" },
39112 [""]={ "ũ","́" },
39113 [""]={ "Ū","̈" },
39114 [""]={ "ū","̈" },
39115 [""]={ "V","̃" },
39116 [""]={ "v","̃" },
39117 [""]={ "V","̣" },
39118 ["ṿ"]={ "v","̣" },
39119 [""]={ "W","̀" },
39120 [""]={ "w","̀" },
39121 [""]={ "W","́" },
39122 [""]={ "w","́" },
39123 [""]={ "W","̈" },
39124 [""]={ "w","̈" },
39125 [""]={ "W","̇" },
39126 [""]={ "w","̇" },
39127 [""]={ "W","̣" },
39128 [""]={ "w","̣" },
39129 [""]={ "X","̇" },
39130 [""]={ "x","̇" },
39131 [""]={ "X","̈" },
39132 [""]={ "x","̈" },
39133 [""]={ "Y","̇" },
39134 [""]={ "y","̇" },
39135 [""]={ "Z","̂" },
39136 [""]={ "z","̂" },
39137 [""]={ "Z","̣" },
39138 [""]={ "z","̣" },
39139 [""]={ "Z","̱" },
39140 [""]={ "z","̱" },
39141 [""]={ "h","̱" },
39142 [""]={ "t","̈" },
39143 [""]={ "w","̊" },
39144 [""]={ "y","̊" },
39145 [""]={ "ſ","̇" },
39146 [""]={ "A","̣" },
39147 [""]={ "a","̣" },
39148 [""]={ "A","̉" },
39149 [""]={ "a","̉" },
39150 [""]={ "Â","́" },
39151 [""]={ "â","́" },
39152 [""]={ "Â","̀" },
39153 [""]={ "â","̀" },
39154 [""]={ "Â","̉" },
39155 [""]={ "â","̉" },
39156 [""]={ "Â","̃" },
39157 [""]={ "â","̃" },
39158 [""]={ "","̂" },
39159 [""]={ "","̂" },
39160 [""]={ "Ă","́" },
39161 [""]={ "ă","́" },
39162 [""]={ "Ă","̀" },
39163 [""]={ "ă","̀" },
39164 [""]={ "Ă","̉" },
39165 [""]={ "ă","̉" },
39166 [""]={ "Ă","̃" },
39167 [""]={ "ă","̃" },
39168 [""]={ "","̆" },
39169 [""]={ "","̆" },
39170 [""]={ "E","̣" },
39171 [""]={ "e","̣" },
39172 [""]={ "E","̉" },
39173 [""]={ "e","̉" },
39174 [""]={ "E","̃" },
39175 [""]={ "e","̃" },
39176 [""]={ "Ê","́" },
39177 ["ế"]={ "ê","́" },
39178 [""]={ "Ê","̀" },
39179 [""]={ "ê","̀" },
39180 [""]={ "Ê","̉" },
39181 [""]={ "ê","̉" },
39182 [""]={ "Ê","̃" },
39183 [""]={ "ê","̃" },
39184 [""]={ "","̂" },
39185 [""]={ "","̂" },
39186 [""]={ "I","̉" },
39187 [""]={ "i","̉" },
39188 [""]={ "I","̣" },
39189 [""]={ "i","̣" },
39190 [""]={ "O","̣" },
39191 [""]={ "o","̣" },
39192 [""]={ "O","̉" },
39193 [""]={ "o","̉" },
39194 [""]={ "Ô","́" },
39195 [""]={ "ô","́" },
39196 [""]={ "Ô","̀" },
39197 [""]={ "ô","̀" },
39198 [""]={ "Ô","̉" },
39199 [""]={ "ô","̉" },
39200 [""]={ "Ô","̃" },
39201 [""]={ "ô","̃" },
39202 [""]={ "","̂" },
39203 [""]={ "","̂" },
39204 [""]={ "Ơ","́" },
39205 [""]={ "ơ","́" },
39206 [""]={ "Ơ","̀" },
39207 [""]={ "ơ","̀" },
39208 [""]={ "Ơ","̉" },
39209 [""]={ "ơ","̉" },
39210 [""]={ "Ơ","̃" },
39211 [""]={ "ơ","̃" },
39212 [""]={ "Ơ","̣" },
39213 [""]={ "ơ","̣" },
39214 [""]={ "U","̣" },
39215 [""]={ "u","̣" },
39216 [""]={ "U","̉" },
39217 [""]={ "u","̉" },
39218 [""]={ "Ư","́" },
39219 [""]={ "ư","́" },
39220 [""]={ "Ư","̀" },
39221 [""]={ "ư","̀" },
39222 [""]={ "Ư","̉" },
39223 [""]={ "ư","̉" },
39224 [""]={ "Ư","̃" },
39225 [""]={ "ư","̃" },
39226 [""]={ "Ư","̣" },
39227 [""]={ "ư","̣" },
39228 [""]={ "Y","̀" },
39229 [""]={ "y","̀" },
39230 [""]={ "Y","̣" },
39231 [""]={ "y","̣" },
39232 [""]={ "Y","̉" },
39233 [""]={ "y","̉" },
39234 [""]={ "Y","̃" },
39235 [""]={ "y","̃" },
39236 [""]={ "α","̓" },
39237 [""]={ "α","̔" },
39238 [""]={ "","̀" },
39239 [""]={ "","̀" },
39240 [""]={ "","́" },
39241 [""]={ "","́" },
39242 [""]={ "","͂" },
39243 [""]={ "","͂" },
39244 [""]={ "Α","̓" },
39245 [""]={ "Α","̔" },
39246 [""]={ "","̀" },
39247 [""]={ "","̀" },
39248 [""]={ "","́" },
39249 [""]={ "","́" },
39250 [""]={ "","͂" },
39251 [""]={ "","͂" },
39252 [""]={ "ε","̓" },
39253 [""]={ "ε","̔" },
39254 [""]={ "","̀" },
39255 [""]={ "","̀" },
39256 [""]={ "","́" },
39257 [""]={ "","́" },
39258 [""]={ "Ε","̓" },
39259 [""]={ "Ε","̔" },
39260 [""]={ "","̀" },
39261 [""]={ "","̀" },
39262 [""]={ "","́" },
39263 [""]={ "","́" },
39264 [""]={ "η","̓" },
39265 [""]={ "η","̔" },
39266 [""]={ "","̀" },
39267 [""]={ "","̀" },
39268 [""]={ "","́" },
39269 [""]={ "","́" },
39270 [""]={ "","͂" },
39271 [""]={ "","͂" },
39272 [""]={ "Η","̓" },
39273 [""]={ "Η","̔" },
39274 [""]={ "","̀" },
39275 [""]={ "","̀" },
39276 [""]={ "","́" },
39277 [""]={ "","́" },
39278 [""]={ "","͂" },
39279 [""]={ "","͂" },
39280 [""]={ "ι","̓" },
39281 [""]={ "ι","̔" },
39282 [""]={ "","̀" },
39283 [""]={ "","̀" },
39284 [""]={ "","́" },
39285 [""]={ "","́" },
39286 [""]={ "","͂" },
39287 [""]={ "","͂" },
39288 [""]={ "Ι","̓" },
39289 [""]={ "Ι","̔" },
39290 [""]={ "","̀" },
39291 [""]={ "","̀" },
39292 [""]={ "","́" },
39293 [""]={ "","́" },
39294 [""]={ "","͂" },
39295 ["Ἷ"]={ "","͂" },
39296 [""]={ "ο","̓" },
39297 [""]={ "ο","̔" },
39298 [""]={ "","̀" },
39299 [""]={ "","̀" },
39300 [""]={ "","́" },
39301 [""]={ "","́" },
39302 [""]={ "Ο","̓" },
39303 [""]={ "Ο","̔" },
39304 [""]={ "","̀" },
39305 [""]={ "","̀" },
39306 [""]={ "","́" },
39307 [""]={ "","́" },
39308 [""]={ "υ","̓" },
39309 [""]={ "υ","̔" },
39310 [""]={ "","̀" },
39311 [""]={ "","̀" },
39312 [""]={ "","́" },
39313 [""]={ "","́" },
39314 [""]={ "","͂" },
39315 [""]={ "","͂" },
39316 [""]={ "Υ","̔" },
39317 [""]={ "","̀" },
39318 [""]={ "","́" },
39319 [""]={ "","͂" },
39320 [""]={ "ω","̓" },
39321 [""]={ "ω","̔" },
39322 [""]={ "","̀" },
39323 [""]={ "","̀" },
39324 [""]={ "","́" },
39325 [""]={ "","́" },
39326 [""]={ "","͂" },
39327 [""]={ "","͂" },
39328 [""]={ "Ω","̓" },
39329 [""]={ "Ω","̔" },
39330 [""]={ "","̀" },
39331 [""]={ "","̀" },
39332 [""]={ "","́" },
39333 [""]={ "","́" },
39334 [""]={ "","͂" },
39335 [""]={ "","͂" },
39336 [""]={ "α","̀" },
39337 [""]={ "ε","̀" },
39338 [""]={ "η","̀" },
39339 [""]={ "ι","̀" },
39340 [""]={ "ο","̀" },
39341 [""]={ "υ","̀" },
39342 [""]={ "ω","̀" },
39343 [""]={ "","ͅ" },
39344 [""]={ "","ͅ" },
39345 [""]={ "","ͅ" },
39346 [""]={ "","ͅ" },
39347 [""]={ "","ͅ" },
39348 [""]={ "","ͅ" },
39349 [""]={ "","ͅ" },
39350 [""]={ "","ͅ" },
39351 [""]={ "","ͅ" },
39352 [""]={ "","ͅ" },
39353 [""]={ "","ͅ" },
39354 [""]={ "","ͅ" },
39355 [""]={ "","ͅ" },
39356 [""]={ "","ͅ" },
39357 [""]={ "","ͅ" },
39358 [""]={ "","ͅ" },
39359 [""]={ "","ͅ" },
39360 [""]={ "","ͅ" },
39361 [""]={ "","ͅ" },
39362 [""]={ "","ͅ" },
39363 [""]={ "","ͅ" },
39364 [""]={ "","ͅ" },
39365 [""]={ "","ͅ" },
39366 [""]={ "","ͅ" },
39367 [""]={ "","ͅ" },
39368 [""]={ "","ͅ" },
39369 [""]={ "","ͅ" },
39370 [""]={ "","ͅ" },
39371 [""]={ "","ͅ" },
39372 [""]={ "","ͅ" },
39373 [""]={ "","ͅ" },
39374 [""]={ "","ͅ" },
39375 [""]={ "","ͅ" },
39376 [""]={ "","ͅ" },
39377 [""]={ "","ͅ" },
39378 [""]={ "","ͅ" },
39379 [""]={ "","ͅ" },
39380 [""]={ "","ͅ" },
39381 [""]={ "","ͅ" },
39382 [""]={ "","ͅ" },
39383 [""]={ "","ͅ" },
39384 [""]={ "","ͅ" },
39385 [""]={ "","ͅ" },
39386 [""]={ "","ͅ" },
39387 [""]={ "","ͅ" },
39388 [""]={ "","ͅ" },
39389 [""]={ "","ͅ" },
39390 [""]={ "","ͅ" },
39391 [""]={ "α","̆" },
39392 [""]={ "α","̄" },
39393 [""]={ "","ͅ" },
39394 [""]={ "α","ͅ" },
39395 [""]={ "ά","ͅ" },
39396 [""]={ "α","͂" },
39397 [""]={ "","ͅ" },
39398 [""]={ "Α","̆" },
39399 [""]={ "Α","̄" },
39400 [""]={ "Α","̀" },
39401 [""]={ "Α","ͅ" },
39402 [""]={ "¨","͂" },
39403 [""]={ "","ͅ" },
39404 [""]={ "η","ͅ" },
39405 [""]={ "ή","ͅ" },
39406 [""]={ "η","͂" },
39407 [""]={ "","ͅ" },
39408 [""]={ "Ε","̀" },
39409 [""]={ "Η","̀" },
39410 [""]={ "Η","ͅ" },
39411 [""]={ "᾿","̀" },
39412 [""]={ "᾿","́" },
39413 [""]={ "᾿","͂" },
39414 [""]={ "ι","̆" },
39415 [""]={ "ι","̄" },
39416 [""]={ "ϊ","̀" },
39417 [""]={ "ι","͂" },
39418 [""]={ "ϊ","͂" },
39419 [""]={ "Ι","̆" },
39420 [""]={ "Ι","̄" },
39421 [""]={ "Ι","̀" },
39422 [""]={ "","̀" },
39423 [""]={ "","́" },
39424 [""]={ "","͂" },
39425 [""]={ "υ","̆" },
39426 [""]={ "υ","̄" },
39427 [""]={ "ϋ","̀" },
39428 [""]={ "ρ","̓" },
39429 [""]={ "ρ","̔" },
39430 [""]={ "υ","͂" },
39431 [""]={ "ϋ","͂" },
39432 [""]={ "Υ","̆" },
39433 [""]={ "Υ","̄" },
39434 [""]={ "Υ","̀" },
39435 [""]={ "Ρ","̔" },
39436 [""]={ "¨","̀" },
39437 [""]={ "","ͅ" },
39438 [""]={ "ω","ͅ" },
39439 [""]={ "ώ","ͅ" },
39440 [""]={ "ω","͂" },
39441 [""]={ "","ͅ" },
39442 [""]={ "Ο","̀" },
39443 [""]={ "Ω","̀" },
39444 [""]={ "Ω","ͅ" },
39445 [""]={ "","̸" },
39446 [""]={ "","̸" },
39447 [""]={ "","̸" },
39448 [""]={ "","̸" },
39449 [""]={ "","̸" },
39450 [""]={ "","̸" },
39451 [""]={ "","̸" },
39452 [""]={ "","̸" },
39453 [""]={ "","̸" },
39454 [""]={ "","̸" },
39455 [""]={ "","̸" },
39456 [""]={ "","̸" },
39457 [""]={ "","̸" },
39458 [""]={ "","̸" },
39459 [""]={ "","̸" },
39460 [""]={ "=","̸" },
39461 [""]={ "","̸" },
39462 [""]={ "","̸" },
39463 [""]={ "<","̸" },
39464 [""]={ ">","̸" },
39465 [""]={ "","̸" },
39466 [""]={ "","̸" },
39467 [""]={ "","̸" },
39468 [""]={ "","̸" },
39469 [""]={ "","̸" },
39470 [""]={ "","̸" },
39471 [""]={ "","̸" },
39472 [""]={ "","̸" },
39473 [""]={ "","̸" },
39474 [""]={ "","̸" },
39475 [""]={ "","̸" },
39476 [""]={ "","̸" },
39477 [""]={ "","̸" },
39478 [""]={ "","̸" },
39479 [""]={ "","̸" },
39480 [""]={ "","̸" },
39481 [""]={ "","̸" },
39482 [""]={ "","̸" },
39483 [""]={ "","̸" },
39484 [""]={ "","̸" },
39485 [""]={ "","̸" },
39486 [""]={ "","̸" },
39487 [""]={ "","̸" },
39488 [""]={ "","̸" },
39489 [""]={ "","̸" },
39490 [""]={ "","" },
39491 [""]={ "","" },
39492 [""]={ "","" },
39493 [""]={ "","" },
39494 [""]={ "","" },
39495 [""]={ "","" },
39496 [""]={ "","" },
39497 [""]={ "","" },
39498 [""]={ "","" },
39499 [""]={ "","" },
39500 [""]={ "","" },
39501 [""]={ "","" },
39502 [""]={ "","" },
39503 [""]={ "","" },
39504 [""]={ "","" },
39505 [""]={ "","" },
39506 [""]={ "","" },
39507 [""]={ "","" },
39508 [""]={ "","" },
39509 [""]={ "","" },
39510 [""]={ "","" },
39511 [""]={ "","" },
39512 [""]={ "","" },
39513 [""]={ "","" },
39514 [""]={ "","" },
39515 [""]={ "","" },
39516 [""]={ "","" },
39517 [""]={ "","" },
39518 [""]={ "","" },
39519 [""]={ "","" },
39520 [""]={ "","" },
39521 [""]={ "","" },
39522 [""]={ "","" },
39523 [""]={ "","" },
39524 [""]={ "","" },
39525 [""]={ "","" },
39526 [""]={ "","" },
39527 [""]={ "","" },
39528 [""]={ "","" },
39529 [""]={ "","" },
39530 [""]={ "","" },
39531 [""]={ "","" },
39532 [""]={ "","" },
39533 [""]={ "","" },
39534 [""]={ "","" },
39535 [""]={ "","" },
39536 [""]={ "","" },
39537 [""]={ "","" },
39538 [""]={ "","" },
39539 [""]={ "","" },
39540 [""]={ "","" },
39541 [""]={ "","" },
39542 [""]={ "","" },
39543 [""]={ "","" },
39544 [""]={ "","" },
39545 [""]={ "","" },
39546 [""]={ "","" },
39547 [""]={ "","" },
39548 [""]={ "י","ִ" },
39549 [""]={ "ײ","ַ" },
39550 [""]={ "ש","ׁ" },
39551 [""]={ "ש","ׂ" },
39552 [""]={ "","ׁ" },
39553 [""]={ "","ׂ" },
39554 [""]={ "א","ַ" },
39555 [""]={ "א","ָ" },
39556 [""]={ "א","ּ" },
39557 [""]={ "ב","ּ" },
39558 [""]={ "ג","ּ" },
39559 [""]={ "ד","ּ" },
39560 [""]={ "ה","ּ" },
39561 [""]={ "ו","ּ" },
39562 [""]={ "ז","ּ" },
39563 [""]={ "ט","ּ" },
39564 [""]={ "י","ּ" },
39565 [""]={ "ך","ּ" },
39566 [""]={ "כ","ּ" },
39567 [""]={ "ל","ּ" },
39568 [""]={ "מ","ּ" },
39569 [""]={ "נ","ּ" },
39570 [""]={ "ס","ּ" },
39571 [""]={ "ף","ּ" },
39572 [""]={ "פ","ּ" },
39573 [""]={ "צ","ּ" },
39574 [""]={ "ק","ּ" },
39575 [""]={ "ר","ּ" },
39576 [""]={ "ש","ּ" },
39577 [""]={ "ת","ּ" },
39578 [""]={ "ו","ֹ" },
39579 [""]={ "ב","ֿ" },
39580 [""]={ "כ","ֿ" },
39581 [""]={ "פ","ֿ" },
39582 ["𑂚"]={ "𑂙","𑂺" },
39583 ["𑂜"]={ "𑂛","𑂺" },
39584 ["𑂫"]={ "𑂥","𑂺" },
39585 ["𑄮"]={ "𑄱","𑄧" },
39586 ["𑄯"]={ "𑄲","𑄧" },
39587 ["𑍋"]={ "𑍇","𑌾" },
39588 ["𑍌"]={ "𑍇","𑍗" },
39589 ["𑒻"]={ "𑒹","𑒺" },
39590 ["𑒼"]={ "𑒹","𑒰" },
39591 ["𑒾"]={ "𑒹","𑒽" },
39592 ["𑖺"]={ "𑖸","𑖯" },
39593 ["𑖻"]={ "𑖹","𑖯" },
39594 ["𝅗𝅥"]={ "𝅗","𝅥" },
39595 ["𝅘𝅥"]={ "𝅘","𝅥" },
39596 ["𝅘𝅥𝅮"]={ "𝅘𝅥","𝅮" },
39597 ["𝅘𝅥𝅯"]={ "𝅘𝅥","𝅯" },
39598 ["𝅘𝅥𝅰"]={ "𝅘𝅥","𝅰" },
39599 ["𝅘𝅥𝅱"]={ "𝅘𝅥","𝅱" },
39600 ["𝅘𝅥𝅲"]={ "𝅘𝅥","𝅲" },
39601 ["𝆹𝅥"]={ "𝆹","𝅥" },
39602 ["𝆺𝅥"]={ "𝆺","𝅥" },
39603 ["𝆹𝅥𝅮"]={ "𝆹𝅥","𝅮" },
39604 ["𝆺𝅥𝅮"]={ "𝆺𝅥","𝅮" },
39605 ["𝆹𝅥𝅯"]={ "𝆹𝅥","𝅯" },
39606 ["𝆺𝅥𝅯"]={ "𝆺𝅥","𝅯" },
39607   },
39608  },
39609  {
39610   ["data"]={
39611 ["À"]={ "A","̀" },
39612 ["Á"]={ "A","́" },
39613 ["Â"]={ "A","̂" },
39614 ["Ã"]={ "A","̃" },
39615 ["Ä"]={ "A","̈" },
39616 ["Å"]={ "A","̊" },
39617 ["Ç"]={ "C","̧" },
39618 ["È"]={ "E","̀" },
39619 ["É"]={ "E","́" },
39620 ["Ê"]={ "E","̂" },
39621 ["Ë"]={ "E","̈" },
39622 ["Ì"]={ "I","̀" },
39623 ["Í"]={ "I","́" },
39624 ["Î"]={ "I","̂" },
39625 ["Ï"]={ "I","̈" },
39626 ["Ñ"]={ "N","̃" },
39627 ["Ò"]={ "O","̀" },
39628 ["Ó"]={ "O","́" },
39629 ["Ô"]={ "O","̂" },
39630 ["Õ"]={ "O","̃" },
39631 ["Ö"]={ "O","̈" },
39632 ["Ù"]={ "U","̀" },
39633 ["Ú"]={ "U","́" },
39634 ["Û"]={ "U","̂" },
39635 ["Ü"]={ "U","̈" },
39636 ["Ý"]={ "Y","́" },
39637 ["à"]={ "a","̀" },
39638 ["á"]={ "a","́" },
39639 ["â"]={ "a","̂" },
39640 ["ã"]={ "a","̃" },
39641 ["ä"]={ "a","̈" },
39642 ["å"]={ "a","̊" },
39643 ["ç"]={ "c","̧" },
39644 ["è"]={ "e","̀" },
39645 ["é"]={ "e","́" },
39646 ["ê"]={ "e","̂" },
39647 ["ë"]={ "e","̈" },
39648 ["ì"]={ "i","̀" },
39649 ["í"]={ "i","́" },
39650 ["î"]={ "i","̂" },
39651 ["ï"]={ "i","̈" },
39652 ["ñ"]={ "n","̃" },
39653 ["ò"]={ "o","̀" },
39654 ["ó"]={ "o","́" },
39655 ["ô"]={ "o","̂" },
39656 ["õ"]={ "o","̃" },
39657 ["ö"]={ "o","̈" },
39658 ["ù"]={ "u","̀" },
39659 ["ú"]={ "u","́" },
39660 ["û"]={ "u","̂" },
39661 ["ü"]={ "u","̈" },
39662 ["ý"]={ "y","́" },
39663 ["ÿ"]={ "y","̈" },
39664 ["Ā"]={ "A","̄" },
39665 ["ā"]={ "a","̄" },
39666 ["Ă"]={ "A","̆" },
39667 ["ă"]={ "a","̆" },
39668 ["Ą"]={ "A","̨" },
39669 ["ą"]={ "a","̨" },
39670 ["Ć"]={ "C","́" },
39671 ["ć"]={ "c","́" },
39672 ["Ĉ"]={ "C","̂" },
39673 ["ĉ"]={ "c","̂" },
39674 ["Ċ"]={ "C","̇" },
39675 ["ċ"]={ "c","̇" },
39676 ["Č"]={ "C","̌" },
39677 ["č"]={ "c","̌" },
39678 ["Ď"]={ "D","̌" },
39679 ["ď"]={ "d","̌" },
39680 ["Ē"]={ "E","̄" },
39681 ["ē"]={ "e","̄" },
39682 ["Ĕ"]={ "E","̆" },
39683 ["ĕ"]={ "e","̆" },
39684 ["Ė"]={ "E","̇" },
39685 ["ė"]={ "e","̇" },
39686 ["Ę"]={ "E","̨" },
39687 ["ę"]={ "e","̨" },
39688 ["Ě"]={ "E","̌" },
39689 ["ě"]={ "e","̌" },
39690 ["Ĝ"]={ "G","̂" },
39691 ["ĝ"]={ "g","̂" },
39692 ["Ğ"]={ "G","̆" },
39693 ["ğ"]={ "g","̆" },
39694 ["Ġ"]={ "G","̇" },
39695 ["ġ"]={ "g","̇" },
39696 ["Ģ"]={ "G","̧" },
39697 ["ģ"]={ "g","̧" },
39698 ["Ĥ"]={ "H","̂" },
39699 ["ĥ"]={ "h","̂" },
39700 ["Ĩ"]={ "I","̃" },
39701 ["ĩ"]={ "i","̃" },
39702 ["Ī"]={ "I","̄" },
39703 ["ī"]={ "i","̄" },
39704 ["Ĭ"]={ "I","̆" },
39705 ["ĭ"]={ "i","̆" },
39706 ["Į"]={ "I","̨" },
39707 ["į"]={ "i","̨" },
39708 ["İ"]={ "I","̇" },
39709 ["Ĵ"]={ "J","̂" },
39710 ["ĵ"]={ "j","̂" },
39711 ["Ķ"]={ "K","̧" },
39712 ["ķ"]={ "k","̧" },
39713 ["Ĺ"]={ "L","́" },
39714 ["ĺ"]={ "l","́" },
39715 ["Ļ"]={ "L","̧" },
39716 ["ļ"]={ "l","̧" },
39717 ["Ľ"]={ "L","̌" },
39718 ["ľ"]={ "l","̌" },
39719 ["Ń"]={ "N","́" },
39720 ["ń"]={ "n","́" },
39721 ["Ņ"]={ "N","̧" },
39722 ["ņ"]={ "n","̧" },
39723 ["Ň"]={ "N","̌" },
39724 ["ň"]={ "n","̌" },
39725 ["Ō"]={ "O","̄" },
39726 ["ō"]={ "o","̄" },
39727 ["Ŏ"]={ "O","̆" },
39728 ["ŏ"]={ "o","̆" },
39729 ["Ő"]={ "O","̋" },
39730 ["ő"]={ "o","̋" },
39731 ["Ŕ"]={ "R","́" },
39732 ["ŕ"]={ "r","́" },
39733 ["Ŗ"]={ "R","̧" },
39734 ["ŗ"]={ "r","̧" },
39735 ["Ř"]={ "R","̌" },
39736 ["ř"]={ "r","̌" },
39737 ["Ś"]={ "S","́" },
39738 ["ś"]={ "s","́" },
39739 ["Ŝ"]={ "S","̂" },
39740 ["ŝ"]={ "s","̂" },
39741 ["Ş"]={ "S","̧" },
39742 ["ş"]={ "s","̧" },
39743 ["Š"]={ "S","̌" },
39744 ["š"]={ "s","̌" },
39745 ["Ţ"]={ "T","̧" },
39746 ["ţ"]={ "t","̧" },
39747 ["Ť"]={ "T","̌" },
39748 ["ť"]={ "t","̌" },
39749 ["Ũ"]={ "U","̃" },
39750 ["ũ"]={ "u","̃" },
39751 ["Ū"]={ "U","̄" },
39752 ["ū"]={ "u","̄" },
39753 ["Ŭ"]={ "U","̆" },
39754 ["ŭ"]={ "u","̆" },
39755 ["Ů"]={ "U","̊" },
39756 ["ů"]={ "u","̊" },
39757 ["Ű"]={ "U","̋" },
39758 ["ű"]={ "u","̋" },
39759 ["Ų"]={ "U","̨" },
39760 ["ų"]={ "u","̨" },
39761 ["Ŵ"]={ "W","̂" },
39762 ["ŵ"]={ "w","̂" },
39763 ["Ŷ"]={ "Y","̂" },
39764 ["ŷ"]={ "y","̂" },
39765 ["Ÿ"]={ "Y","̈" },
39766 ["Ź"]={ "Z","́" },
39767 ["ź"]={ "z","́" },
39768 ["Ż"]={ "Z","̇" },
39769 ["ż"]={ "z","̇" },
39770 ["Ž"]={ "Z","̌" },
39771 ["ž"]={ "z","̌" },
39772 ["Ơ"]={ "O","̛" },
39773 ["ơ"]={ "o","̛" },
39774 ["Ư"]={ "U","̛" },
39775 ["ư"]={ "u","̛" },
39776 ["Ǎ"]={ "A","̌" },
39777 ["ǎ"]={ "a","̌" },
39778 ["Ǐ"]={ "I","̌" },
39779 ["ǐ"]={ "i","̌" },
39780 ["Ǒ"]={ "O","̌" },
39781 ["ǒ"]={ "o","̌" },
39782 ["Ǔ"]={ "U","̌" },
39783 ["ǔ"]={ "u","̌" },
39784 ["Ǖ"]={ "Ü","̄" },
39785 ["ǖ"]={ "ü","̄" },
39786 ["Ǘ"]={ "Ü","́" },
39787 ["ǘ"]={ "ü","́" },
39788 ["Ǚ"]={ "Ü","̌" },
39789 ["ǚ"]={ "ü","̌" },
39790 ["Ǜ"]={ "Ü","̀" },
39791 ["ǜ"]={ "ü","̀" },
39792 ["Ǟ"]={ "Ä","̄" },
39793 ["ǟ"]={ "ä","̄" },
39794 ["Ǡ"]={ "Ȧ","̄" },
39795 ["ǡ"]={ "ȧ","̄" },
39796 ["Ǣ"]={ "Æ","̄" },
39797 ["ǣ"]={ "æ","̄" },
39798 ["Ǧ"]={ "G","̌" },
39799 ["ǧ"]={ "g","̌" },
39800 ["Ǩ"]={ "K","̌" },
39801 ["ǩ"]={ "k","̌" },
39802 ["Ǫ"]={ "O","̨" },
39803 ["ǫ"]={ "o","̨" },
39804 ["Ǭ"]={ "Ǫ","̄" },
39805 ["ǭ"]={ "ǫ","̄" },
39806 ["Ǯ"]={ "Ʒ","̌" },
39807 ["ǯ"]={ "ʒ","̌" },
39808 ["ǰ"]={ "j","̌" },
39809 ["Ǵ"]={ "G","́" },
39810 ["ǵ"]={ "g","́" },
39811 ["Ǹ"]={ "N","̀" },
39812 ["ǹ"]={ "n","̀" },
39813 ["Ǻ"]={ "Å","́" },
39814 ["ǻ"]={ "å","́" },
39815 ["Ǽ"]={ "Æ","́" },
39816 ["ǽ"]={ "æ","́" },
39817 ["Ǿ"]={ "Ø","́" },
39818 ["ǿ"]={ "ø","́" },
39819 ["Ȁ"]={ "A","̏" },
39820 ["ȁ"]={ "a","̏" },
39821 ["Ȃ"]={ "A","̑" },
39822 ["ȃ"]={ "a","̑" },
39823 ["Ȅ"]={ "E","̏" },
39824 ["ȅ"]={ "e","̏" },
39825 ["Ȇ"]={ "E","̑" },
39826 ["ȇ"]={ "e","̑" },
39827 ["Ȉ"]={ "I","̏" },
39828 ["ȉ"]={ "i","̏" },
39829 ["Ȋ"]={ "I","̑" },
39830 ["ȋ"]={ "i","̑" },
39831 ["Ȍ"]={ "O","̏" },
39832 ["ȍ"]={ "o","̏" },
39833 ["Ȏ"]={ "O","̑" },
39834 ["ȏ"]={ "o","̑" },
39835 ["Ȑ"]={ "R","̏" },
39836 ["ȑ"]={ "r","̏" },
39837 ["Ȓ"]={ "R","̑" },
39838 ["ȓ"]={ "r","̑" },
39839 ["Ȕ"]={ "U","̏" },
39840 ["ȕ"]={ "u","̏" },
39841 ["Ȗ"]={ "U","̑" },
39842 ["ȗ"]={ "u","̑" },
39843 ["Ș"]={ "S","̦" },
39844 ["ș"]={ "s","̦" },
39845 ["Ț"]={ "T","̦" },
39846 ["ț"]={ "t","̦" },
39847 ["Ȟ"]={ "H","̌" },
39848 ["ȟ"]={ "h","̌" },
39849 ["Ȧ"]={ "A","̇" },
39850 ["ȧ"]={ "a","̇" },
39851 ["Ȩ"]={ "E","̧" },
39852 ["ȩ"]={ "e","̧" },
39853 ["Ȫ"]={ "Ö","̄" },
39854 ["ȫ"]={ "ö","̄" },
39855 ["Ȭ"]={ "Õ","̄" },
39856 ["ȭ"]={ "õ","̄" },
39857 ["Ȯ"]={ "O","̇" },
39858 ["ȯ"]={ "o","̇" },
39859 ["Ȱ"]={ "Ȯ","̄" },
39860 ["ȱ"]={ "ȯ","̄" },
39861 ["Ȳ"]={ "Y","̄" },
39862 ["ȳ"]={ "y","̄" },
39863 ["̈́"]={ "̈","́" },
39864 ["΅"]={ "¨","́" },
39865 ["Ά"]={ "Α","́" },
39866 ["Έ"]={ "Ε","́" },
39867 ["Ή"]={ "Η","́" },
39868 ["Ί"]={ "Ι","́" },
39869 ["Ό"]={ "Ο","́" },
39870 ["Ύ"]={ "Υ","́" },
39871 ["Ώ"]={ "Ω","́" },
39872 ["ΐ"]={ "ϊ","́" },
39873 ["Ϊ"]={ "Ι","̈" },
39874 ["Ϋ"]={ "Υ","̈" },
39875 ["ά"]={ "α","́" },
39876 ["έ"]={ "ε","́" },
39877 ["ή"]={ "η","́" },
39878 ["ί"]={ "ι","́" },
39879 ["ΰ"]={ "ϋ","́" },
39880 ["ϊ"]={ "ι","̈" },
39881 ["ϋ"]={ "υ","̈" },
39882 ["ό"]={ "ο","́" },
39883 ["ύ"]={ "υ","́" },
39884 ["ώ"]={ "ω","́" },
39885 ["ϓ"]={ "ϒ","́" },
39886 ["ϔ"]={ "ϒ","̈" },
39887 ["Ѐ"]={ "Е","̀" },
39888 ["Ё"]={ "Е","̈" },
39889 ["Ѓ"]={ "Г","́" },
39890 ["Ї"]={ "І","̈" },
39891 ["Ќ"]={ "К","́" },
39892 ["Ѝ"]={ "И","̀" },
39893 ["Ў"]={ "У","̆" },
39894 ["Й"]={ "И","̆" },
39895 ["й"]={ "и","̆" },
39896 ["ѐ"]={ "е","̀" },
39897 ["ё"]={ "е","̈" },
39898 ["ѓ"]={ "г","́" },
39899 ["ї"]={ "і","̈" },
39900 ["ќ"]={ "к","́" },
39901 ["ѝ"]={ "и","̀" },
39902 ["ў"]={ "у","̆" },
39903 ["Ѷ"]={ "Ѵ","̏" },
39904 ["ѷ"]={ "ѵ","̏" },
39905 ["Ӂ"]={ "Ж","̆" },
39906 ["ӂ"]={ "ж","̆" },
39907 ["Ӑ"]={ "А","̆" },
39908 ["ӑ"]={ "а","̆" },
39909 ["Ӓ"]={ "А","̈" },
39910 ["ӓ"]={ "а","̈" },
39911 ["Ӗ"]={ "Е","̆" },
39912 ["ӗ"]={ "е","̆" },
39913 ["Ӛ"]={ "Ә","̈" },
39914 ["ӛ"]={ "ә","̈" },
39915 ["Ӝ"]={ "Ж","̈" },
39916 ["ӝ"]={ "ж","̈" },
39917 ["Ӟ"]={ "З","̈" },
39918 ["ӟ"]={ "з","̈" },
39919 ["Ӣ"]={ "И","̄" },
39920 ["ӣ"]={ "и","̄" },
39921 ["Ӥ"]={ "И","̈" },
39922 ["ӥ"]={ "и","̈" },
39923 ["Ӧ"]={ "О","̈" },
39924 ["ӧ"]={ "о","̈" },
39925 ["Ӫ"]={ "Ө","̈" },
39926 ["ӫ"]={ "ө","̈" },
39927 ["Ӭ"]={ "Э","̈" },
39928 ["ӭ"]={ "э","̈" },
39929 ["Ӯ"]={ "У","̄" },
39930 ["ӯ"]={ "у","̄" },
39931 ["Ӱ"]={ "У","̈" },
39932 ["ӱ"]={ "у","̈" },
39933 ["Ӳ"]={ "У","̋" },
39934 ["ӳ"]={ "у","̋" },
39935 ["Ӵ"]={ "Ч","̈" },
39936 ["ӵ"]={ "ч","̈" },
39937 ["Ӹ"]={ "Ы","̈" },
39938 ["ӹ"]={ "ы","̈" },
39939 ["آ"]={ "ا","ٓ" },
39940 ["أ"]={ "ا","ٔ" },
39941 ["ؤ"]={ "و","ٔ" },
39942 ["إ"]={ "ا","ٕ" },
39943 ["ئ"]={ "ي","ٔ" },
39944 ["ۀ"]={ "ە","ٔ" },
39945 ["ۂ"]={ "ہ","ٔ" },
39946 ["ۓ"]={ "ے","ٔ" },
39947 [""]={ "","" },
39948 [""]={ "","" },
39949 [""]={ "","" },
39950 [""]={ "","" },
39951 [""]={ "","" },
39952 [""]={ "","" },
39953 [""]={ "","" },
39954 [""]={ "","" },
39955 [""]={ "","" },
39956 [""]={ "","" },
39957 [""]={ "","" },
39958 [""]={ "","" },
39959 [""]={ "","" },
39960 [""]={ "","" },
39961 [""]={ "","" },
39962 [""]={ "","" },
39963 [""]={ "","" },
39964 [""]={ "","" },
39965 [""]={ "","" },
39966 [""]={ "","" },
39967 [""]={ "","" },
39968 [""]={ "","" },
39969 [""]={ "","" },
39970 [""]={ "","" },
39971 [""]={ "","" },
39972 [""]={ "","" },
39973 [""]={ "","" },
39974 [""]={ "","" },
39975 [""]={ "","" },
39976 [""]={ "","" },
39977 [""]={ "","" },
39978 [""]={ "","" },
39979 [""]={ "ಿ","" },
39980 [""]={ "","" },
39981 [""]={ "","" },
39982 [""]={ "","" },
39983 [""]={ "","" },
39984 [""]={ "","" },
39985 [""]={ "","" },
39986 [""]={ "","" },
39987 [""]={ "","" },
39988 [""]={ "","" },
39989 [""]={ "","" },
39990 [""]={ "","" },
39991 [""]={ "","" },
39992 [""]={ "","" },
39993 [""]={ "","" },
39994 [""]={ "","" },
39995 [""]={ "","" },
39996 [""]={ "","" },
39997 [""]={ "","" },
39998 [""]={ "","" },
39999 [""]={ "","" },
40000 [""]={ "","" },
40001 [""]={ "","" },
40002 [""]={ "","" },
40003 [""]={ "","" },
40004 [""]={ "","" },
40005 [""]={ "","" },
40006 [""]={ "","" },
40007 [""]={ "","" },
40008 [""]={ "","" },
40009 [""]={ "","" },
40010 [""]={ "","" },
40011 [""]={ "","" },
40012 [""]={ "","" },
40013 [""]={ "","" },
40014 [""]={ "","" },
40015 [""]={ "","" },
40016 [""]={ "","" },
40017 [""]={ "","" },
40018 [""]={ "ᬿ","" },
40019 [""]={ "","" },
40020 [""]={ "A","̥" },
40021 [""]={ "a","̥" },
40022 [""]={ "B","̇" },
40023 [""]={ "b","̇" },
40024 [""]={ "B","̣" },
40025 [""]={ "b","̣" },
40026 [""]={ "B","̱" },
40027 [""]={ "b","̱" },
40028 [""]={ "Ç","́" },
40029 [""]={ "ç","́" },
40030 [""]={ "D","̇" },
40031 [""]={ "d","̇" },
40032 [""]={ "D","̣" },
40033 [""]={ "d","̣" },
40034 [""]={ "D","̱" },
40035 [""]={ "d","̱" },
40036 [""]={ "D","̧" },
40037 [""]={ "d","̧" },
40038 [""]={ "D","̭" },
40039 [""]={ "d","̭" },
40040 [""]={ "Ē","̀" },
40041 [""]={ "ē","̀" },
40042 [""]={ "Ē","́" },
40043 [""]={ "ē","́" },
40044 [""]={ "E","̭" },
40045 [""]={ "e","̭" },
40046 [""]={ "E","̰" },
40047 [""]={ "e","̰" },
40048 [""]={ "Ȩ","̆" },
40049 [""]={ "ȩ","̆" },
40050 [""]={ "F","̇" },
40051 [""]={ "f","̇" },
40052 [""]={ "G","̄" },
40053 [""]={ "g","̄" },
40054 [""]={ "H","̇" },
40055 [""]={ "h","̇" },
40056 [""]={ "H","̣" },
40057 [""]={ "h","̣" },
40058 [""]={ "H","̈" },
40059 [""]={ "h","̈" },
40060 [""]={ "H","̧" },
40061 [""]={ "h","̧" },
40062 [""]={ "H","̮" },
40063 [""]={ "h","̮" },
40064 [""]={ "I","̰" },
40065 [""]={ "i","̰" },
40066 [""]={ "Ï","́" },
40067 [""]={ "ï","́" },
40068 [""]={ "K","́" },
40069 [""]={ "k","́" },
40070 [""]={ "K","̣" },
40071 [""]={ "k","̣" },
40072 [""]={ "K","̱" },
40073 [""]={ "k","̱" },
40074 [""]={ "L","̣" },
40075 [""]={ "l","̣" },
40076 [""]={ "","̄" },
40077 [""]={ "","̄" },
40078 [""]={ "L","̱" },
40079 [""]={ "l","̱" },
40080 [""]={ "L","̭" },
40081 [""]={ "l","̭" },
40082 [""]={ "M","́" },
40083 ["ḿ"]={ "m","́" },
40084 [""]={ "M","̇" },
40085 [""]={ "m","̇" },
40086 [""]={ "M","̣" },
40087 [""]={ "m","̣" },
40088 [""]={ "N","̇" },
40089 [""]={ "n","̇" },
40090 [""]={ "N","̣" },
40091 [""]={ "n","̣" },
40092 [""]={ "N","̱" },
40093 [""]={ "n","̱" },
40094 [""]={ "N","̭" },
40095 [""]={ "n","̭" },
40096 [""]={ "Õ","́" },
40097 [""]={ "õ","́" },
40098 [""]={ "Õ","̈" },
40099 [""]={ "õ","̈" },
40100 [""]={ "Ō","̀" },
40101 [""]={ "ō","̀" },
40102 [""]={ "Ō","́" },
40103 [""]={ "ō","́" },
40104 [""]={ "P","́" },
40105 [""]={ "p","́" },
40106 [""]={ "P","̇" },
40107 [""]={ "p","̇" },
40108 [""]={ "R","̇" },
40109 [""]={ "r","̇" },
40110 [""]={ "R","̣" },
40111 [""]={ "r","̣" },
40112 [""]={ "","̄" },
40113 [""]={ "","̄" },
40114 [""]={ "R","̱" },
40115 [""]={ "r","̱" },
40116 [""]={ "S","̇" },
40117 [""]={ "s","̇" },
40118 [""]={ "S","̣" },
40119 [""]={ "s","̣" },
40120 [""]={ "Ś","̇" },
40121 [""]={ "ś","̇" },
40122 [""]={ "Š","̇" },
40123 [""]={ "š","̇" },
40124 [""]={ "","̇" },
40125 [""]={ "","̇" },
40126 [""]={ "T","̇" },
40127 [""]={ "t","̇" },
40128 [""]={ "T","̣" },
40129 [""]={ "t","̣" },
40130 [""]={ "T","̱" },
40131 [""]={ "t","̱" },
40132 [""]={ "T","̭" },
40133 [""]={ "t","̭" },
40134 [""]={ "U","̤" },
40135 [""]={ "u","̤" },
40136 [""]={ "U","̰" },
40137 [""]={ "u","̰" },
40138 [""]={ "U","̭" },
40139 [""]={ "u","̭" },
40140 [""]={ "Ũ","́" },
40141 [""]={ "ũ","́" },
40142 [""]={ "Ū","̈" },
40143 [""]={ "ū","̈" },
40144 [""]={ "V","̃" },
40145 [""]={ "v","̃" },
40146 [""]={ "V","̣" },
40147 ["ṿ"]={ "v","̣" },
40148 [""]={ "W","̀" },
40149 [""]={ "w","̀" },
40150 [""]={ "W","́" },
40151 [""]={ "w","́" },
40152 [""]={ "W","̈" },
40153 [""]={ "w","̈" },
40154 [""]={ "W","̇" },
40155 [""]={ "w","̇" },
40156 [""]={ "W","̣" },
40157 [""]={ "w","̣" },
40158 [""]={ "X","̇" },
40159 [""]={ "x","̇" },
40160 [""]={ "X","̈" },
40161 [""]={ "x","̈" },
40162 [""]={ "Y","̇" },
40163 [""]={ "y","̇" },
40164 [""]={ "Z","̂" },
40165 [""]={ "z","̂" },
40166 [""]={ "Z","̣" },
40167 [""]={ "z","̣" },
40168 [""]={ "Z","̱" },
40169 [""]={ "z","̱" },
40170 [""]={ "h","̱" },
40171 [""]={ "t","̈" },
40172 [""]={ "w","̊" },
40173 [""]={ "y","̊" },
40174 [""]={ "ſ","̇" },
40175 [""]={ "A","̣" },
40176 [""]={ "a","̣" },
40177 [""]={ "A","̉" },
40178 [""]={ "a","̉" },
40179 [""]={ "Â","́" },
40180 [""]={ "â","́" },
40181 [""]={ "Â","̀" },
40182 [""]={ "â","̀" },
40183 [""]={ "Â","̉" },
40184 [""]={ "â","̉" },
40185 [""]={ "Â","̃" },
40186 [""]={ "â","̃" },
40187 [""]={ "","̂" },
40188 [""]={ "","̂" },
40189 [""]={ "Ă","́" },
40190 [""]={ "ă","́" },
40191 [""]={ "Ă","̀" },
40192 [""]={ "ă","̀" },
40193 [""]={ "Ă","̉" },
40194 [""]={ "ă","̉" },
40195 [""]={ "Ă","̃" },
40196 [""]={ "ă","̃" },
40197 [""]={ "","̆" },
40198 [""]={ "","̆" },
40199 [""]={ "E","̣" },
40200 [""]={ "e","̣" },
40201 [""]={ "E","̉" },
40202 [""]={ "e","̉" },
40203 [""]={ "E","̃" },
40204 [""]={ "e","̃" },
40205 [""]={ "Ê","́" },
40206 ["ế"]={ "ê","́" },
40207 [""]={ "Ê","̀" },
40208 [""]={ "ê","̀" },
40209 [""]={ "Ê","̉" },
40210 [""]={ "ê","̉" },
40211 [""]={ "Ê","̃" },
40212 [""]={ "ê","̃" },
40213 [""]={ "","̂" },
40214 [""]={ "","̂" },
40215 [""]={ "I","̉" },
40216 [""]={ "i","̉" },
40217 [""]={ "I","̣" },
40218 [""]={ "i","̣" },
40219 [""]={ "O","̣" },
40220 [""]={ "o","̣" },
40221 [""]={ "O","̉" },
40222 [""]={ "o","̉" },
40223 [""]={ "Ô","́" },
40224 [""]={ "ô","́" },
40225 [""]={ "Ô","̀" },
40226 [""]={ "ô","̀" },
40227 [""]={ "Ô","̉" },
40228 [""]={ "ô","̉" },
40229 [""]={ "Ô","̃" },
40230 [""]={ "ô","̃" },
40231 [""]={ "","̂" },
40232 [""]={ "","̂" },
40233 [""]={ "Ơ","́" },
40234 [""]={ "ơ","́" },
40235 [""]={ "Ơ","̀" },
40236 [""]={ "ơ","̀" },
40237 [""]={ "Ơ","̉" },
40238 [""]={ "ơ","̉" },
40239 [""]={ "Ơ","̃" },
40240 [""]={ "ơ","̃" },
40241 [""]={ "Ơ","̣" },
40242 [""]={ "ơ","̣" },
40243 [""]={ "U","̣" },
40244 [""]={ "u","̣" },
40245 [""]={ "U","̉" },
40246 [""]={ "u","̉" },
40247 [""]={ "Ư","́" },
40248 [""]={ "ư","́" },
40249 [""]={ "Ư","̀" },
40250 [""]={ "ư","̀" },
40251 [""]={ "Ư","̉" },
40252 [""]={ "ư","̉" },
40253 [""]={ "Ư","̃" },
40254 [""]={ "ư","̃" },
40255 [""]={ "Ư","̣" },
40256 [""]={ "ư","̣" },
40257 [""]={ "Y","̀" },
40258 [""]={ "y","̀" },
40259 [""]={ "Y","̣" },
40260 [""]={ "y","̣" },
40261 [""]={ "Y","̉" },
40262 [""]={ "y","̉" },
40263 [""]={ "Y","̃" },
40264 [""]={ "y","̃" },
40265 [""]={ "α","̓" },
40266 [""]={ "α","̔" },
40267 [""]={ "","̀" },
40268 [""]={ "","̀" },
40269 [""]={ "","́" },
40270 [""]={ "","́" },
40271 [""]={ "","͂" },
40272 [""]={ "","͂" },
40273 [""]={ "Α","̓" },
40274 [""]={ "Α","̔" },
40275 [""]={ "","̀" },
40276 [""]={ "","̀" },
40277 [""]={ "","́" },
40278 [""]={ "","́" },
40279 [""]={ "","͂" },
40280 [""]={ "","͂" },
40281 [""]={ "ε","̓" },
40282 [""]={ "ε","̔" },
40283 [""]={ "","̀" },
40284 [""]={ "","̀" },
40285 [""]={ "","́" },
40286 [""]={ "","́" },
40287 [""]={ "Ε","̓" },
40288 [""]={ "Ε","̔" },
40289 [""]={ "","̀" },
40290 [""]={ "","̀" },
40291 [""]={ "","́" },
40292 [""]={ "","́" },
40293 [""]={ "η","̓" },
40294 [""]={ "η","̔" },
40295 [""]={ "","̀" },
40296 [""]={ "","̀" },
40297 [""]={ "","́" },
40298 [""]={ "","́" },
40299 [""]={ "","͂" },
40300 [""]={ "","͂" },
40301 [""]={ "Η","̓" },
40302 [""]={ "Η","̔" },
40303 [""]={ "","̀" },
40304 [""]={ "","̀" },
40305 [""]={ "","́" },
40306 [""]={ "","́" },
40307 [""]={ "","͂" },
40308 [""]={ "","͂" },
40309 [""]={ "ι","̓" },
40310 [""]={ "ι","̔" },
40311 [""]={ "","̀" },
40312 [""]={ "","̀" },
40313 [""]={ "","́" },
40314 [""]={ "","́" },
40315 [""]={ "","͂" },
40316 [""]={ "","͂" },
40317 [""]={ "Ι","̓" },
40318 [""]={ "Ι","̔" },
40319 [""]={ "","̀" },
40320 [""]={ "","̀" },
40321 [""]={ "","́" },
40322 [""]={ "","́" },
40323 [""]={ "","͂" },
40324 ["Ἷ"]={ "","͂" },
40325 [""]={ "ο","̓" },
40326 [""]={ "ο","̔" },
40327 [""]={ "","̀" },
40328 [""]={ "","̀" },
40329 [""]={ "","́" },
40330 [""]={ "","́" },
40331 [""]={ "Ο","̓" },
40332 [""]={ "Ο","̔" },
40333 [""]={ "","̀" },
40334 [""]={ "","̀" },
40335 [""]={ "","́" },
40336 [""]={ "","́" },
40337 [""]={ "υ","̓" },
40338 [""]={ "υ","̔" },
40339 [""]={ "","̀" },
40340 [""]={ "","̀" },
40341 [""]={ "","́" },
40342 [""]={ "","́" },
40343 [""]={ "","͂" },
40344 [""]={ "","͂" },
40345 [""]={ "Υ","̔" },
40346 [""]={ "","̀" },
40347 [""]={ "","́" },
40348 [""]={ "","͂" },
40349 [""]={ "ω","̓" },
40350 [""]={ "ω","̔" },
40351 [""]={ "","̀" },
40352 [""]={ "","̀" },
40353 [""]={ "","́" },
40354 [""]={ "","́" },
40355 [""]={ "","͂" },
40356 [""]={ "","͂" },
40357 [""]={ "Ω","̓" },
40358 [""]={ "Ω","̔" },
40359 [""]={ "","̀" },
40360 [""]={ "","̀" },
40361 [""]={ "","́" },
40362 [""]={ "","́" },
40363 [""]={ "","͂" },
40364 [""]={ "","͂" },
40365 [""]={ "α","̀" },
40366 [""]={ "ε","̀" },
40367 [""]={ "η","̀" },
40368 [""]={ "ι","̀" },
40369 [""]={ "ο","̀" },
40370 [""]={ "υ","̀" },
40371 [""]={ "ω","̀" },
40372 [""]={ "","ͅ" },
40373 [""]={ "","ͅ" },
40374 [""]={ "","ͅ" },
40375 [""]={ "","ͅ" },
40376 [""]={ "","ͅ" },
40377 [""]={ "","ͅ" },
40378 [""]={ "","ͅ" },
40379 [""]={ "","ͅ" },
40380 [""]={ "","ͅ" },
40381 [""]={ "","ͅ" },
40382 [""]={ "","ͅ" },
40383 [""]={ "","ͅ" },
40384 [""]={ "","ͅ" },
40385 [""]={ "","ͅ" },
40386 [""]={ "","ͅ" },
40387 [""]={ "","ͅ" },
40388 [""]={ "","ͅ" },
40389 [""]={ "","ͅ" },
40390 [""]={ "","ͅ" },
40391 [""]={ "","ͅ" },
40392 [""]={ "","ͅ" },
40393 [""]={ "","ͅ" },
40394 [""]={ "","ͅ" },
40395 [""]={ "","ͅ" },
40396 [""]={ "","ͅ" },
40397 [""]={ "","ͅ" },
40398 [""]={ "","ͅ" },
40399 [""]={ "","ͅ" },
40400 [""]={ "","ͅ" },
40401 [""]={ "","ͅ" },
40402 [""]={ "","ͅ" },
40403 [""]={ "","ͅ" },
40404 [""]={ "","ͅ" },
40405 [""]={ "","ͅ" },
40406 [""]={ "","ͅ" },
40407 [""]={ "","ͅ" },
40408 [""]={ "","ͅ" },
40409 [""]={ "","ͅ" },
40410 [""]={ "","ͅ" },
40411 [""]={ "","ͅ" },
40412 [""]={ "","ͅ" },
40413 [""]={ "","ͅ" },
40414 [""]={ "","ͅ" },
40415 [""]={ "","ͅ" },
40416 [""]={ "","ͅ" },
40417 [""]={ "","ͅ" },
40418 [""]={ "","ͅ" },
40419 [""]={ "","ͅ" },
40420 [""]={ "α","̆" },
40421 [""]={ "α","̄" },
40422 [""]={ "","ͅ" },
40423 [""]={ "α","ͅ" },
40424 [""]={ "ά","ͅ" },
40425 [""]={ "α","͂" },
40426 [""]={ "","ͅ" },
40427 [""]={ "Α","̆" },
40428 [""]={ "Α","̄" },
40429 [""]={ "Α","̀" },
40430 [""]={ "Α","ͅ" },
40431 [""]={ "¨","͂" },
40432 [""]={ "","ͅ" },
40433 [""]={ "η","ͅ" },
40434 [""]={ "ή","ͅ" },
40435 [""]={ "η","͂" },
40436 [""]={ "","ͅ" },
40437 [""]={ "Ε","̀" },
40438 [""]={ "Η","̀" },
40439 [""]={ "Η","ͅ" },
40440 [""]={ "᾿","̀" },
40441 [""]={ "᾿","́" },
40442 [""]={ "᾿","͂" },
40443 [""]={ "ι","̆" },
40444 [""]={ "ι","̄" },
40445 [""]={ "ϊ","̀" },
40446 [""]={ "ι","͂" },
40447 [""]={ "ϊ","͂" },
40448 [""]={ "Ι","̆" },
40449 [""]={ "Ι","̄" },
40450 [""]={ "Ι","̀" },
40451 [""]={ "","̀" },
40452 [""]={ "","́" },
40453 [""]={ "","͂" },
40454 [""]={ "υ","̆" },
40455 [""]={ "υ","̄" },
40456 [""]={ "ϋ","̀" },
40457 [""]={ "ρ","̓" },
40458 [""]={ "ρ","̔" },
40459 [""]={ "υ","͂" },
40460 [""]={ "ϋ","͂" },
40461 [""]={ "Υ","̆" },
40462 [""]={ "Υ","̄" },
40463 [""]={ "Υ","̀" },
40464 [""]={ "Ρ","̔" },
40465 [""]={ "¨","̀" },
40466 [""]={ "","ͅ" },
40467 [""]={ "ω","ͅ" },
40468 [""]={ "ώ","ͅ" },
40469 [""]={ "ω","͂" },
40470 [""]={ "","ͅ" },
40471 [""]={ "Ο","̀" },
40472 [""]={ "Ω","̀" },
40473 [""]={ "Ω","ͅ" },
40474 [""]={ "","̸" },
40475 [""]={ "","̸" },
40476 [""]={ "","̸" },
40477 [""]={ "","̸" },
40478 [""]={ "","̸" },
40479 [""]={ "","̸" },
40480 [""]={ "","̸" },
40481 [""]={ "","̸" },
40482 [""]={ "","̸" },
40483 [""]={ "","̸" },
40484 [""]={ "","̸" },
40485 [""]={ "","̸" },
40486 [""]={ "","̸" },
40487 [""]={ "","̸" },
40488 [""]={ "","̸" },
40489 [""]={ "=","̸" },
40490 [""]={ "","̸" },
40491 [""]={ "","̸" },
40492 [""]={ "<","̸" },
40493 [""]={ ">","̸" },
40494 [""]={ "","̸" },
40495 [""]={ "","̸" },
40496 [""]={ "","̸" },
40497 [""]={ "","̸" },
40498 [""]={ "","̸" },
40499 [""]={ "","̸" },
40500 [""]={ "","̸" },
40501 [""]={ "","̸" },
40502 [""]={ "","̸" },
40503 [""]={ "","̸" },
40504 [""]={ "","̸" },
40505 [""]={ "","̸" },
40506 [""]={ "","̸" },
40507 [""]={ "","̸" },
40508 [""]={ "","̸" },
40509 [""]={ "","̸" },
40510 [""]={ "","̸" },
40511 [""]={ "","̸" },
40512 [""]={ "","̸" },
40513 [""]={ "","̸" },
40514 [""]={ "","̸" },
40515 [""]={ "","̸" },
40516 [""]={ "","̸" },
40517 [""]={ "","̸" },
40518 [""]={ "","̸" },
40519 [""]={ "","" },
40520 [""]={ "","" },
40521 [""]={ "","" },
40522 [""]={ "","" },
40523 [""]={ "","" },
40524 [""]={ "","" },
40525 [""]={ "","" },
40526 [""]={ "","" },
40527 [""]={ "","" },
40528 [""]={ "","" },
40529 [""]={ "","" },
40530 [""]={ "","" },
40531 [""]={ "","" },
40532 [""]={ "","" },
40533 [""]={ "","" },
40534 [""]={ "","" },
40535 [""]={ "","" },
40536 [""]={ "","" },
40537 [""]={ "","" },
40538 [""]={ "","" },
40539 [""]={ "","" },
40540 [""]={ "","" },
40541 [""]={ "","" },
40542 [""]={ "","" },
40543 [""]={ "","" },
40544 [""]={ "","" },
40545 [""]={ "","" },
40546 [""]={ "","" },
40547 [""]={ "","" },
40548 [""]={ "","" },
40549 [""]={ "","" },
40550 [""]={ "","" },
40551 [""]={ "","" },
40552 [""]={ "","" },
40553 [""]={ "","" },
40554 [""]={ "","" },
40555 [""]={ "","" },
40556 [""]={ "","" },
40557 [""]={ "","" },
40558 [""]={ "","" },
40559 [""]={ "","" },
40560 [""]={ "","" },
40561 [""]={ "","" },
40562 [""]={ "","" },
40563 [""]={ "","" },
40564 [""]={ "","" },
40565 [""]={ "","" },
40566 [""]={ "","" },
40567 [""]={ "","" },
40568 [""]={ "","" },
40569 [""]={ "","" },
40570 [""]={ "","" },
40571 [""]={ "","" },
40572 [""]={ "","" },
40573 [""]={ "","" },
40574 [""]={ "","" },
40575 [""]={ "","" },
40576 [""]={ "","" },
40577 [""]={ "י","ִ" },
40578 [""]={ "ײ","ַ" },
40579 [""]={ "ש","ׁ" },
40580 [""]={ "ש","ׂ" },
40581 [""]={ "","ׁ" },
40582 [""]={ "","ׂ" },
40583 [""]={ "א","ַ" },
40584 [""]={ "א","ָ" },
40585 [""]={ "א","ּ" },
40586 [""]={ "ב","ּ" },
40587 [""]={ "ג","ּ" },
40588 [""]={ "ד","ּ" },
40589 [""]={ "ה","ּ" },
40590 [""]={ "ו","ּ" },
40591 [""]={ "ז","ּ" },
40592 [""]={ "ט","ּ" },
40593 [""]={ "י","ּ" },
40594 [""]={ "ך","ּ" },
40595 [""]={ "כ","ּ" },
40596 [""]={ "ל","ּ" },
40597 [""]={ "מ","ּ" },
40598 [""]={ "נ","ּ" },
40599 [""]={ "ס","ּ" },
40600 [""]={ "ף","ּ" },
40601 [""]={ "פ","ּ" },
40602 [""]={ "צ","ּ" },
40603 [""]={ "ק","ּ" },
40604 [""]={ "ר","ּ" },
40605 [""]={ "ש","ּ" },
40606 [""]={ "ת","ּ" },
40607 [""]={ "ו","ֹ" },
40608 [""]={ "ב","ֿ" },
40609 [""]={ "כ","ֿ" },
40610 [""]={ "פ","ֿ" },
40611 ["𑂚"]={ "𑂙","𑂺" },
40612 ["𑂜"]={ "𑂛","𑂺" },
40613 ["𑂫"]={ "𑂥","𑂺" },
40614 ["𑄮"]={ "𑄱","𑄧" },
40615 ["𑄯"]={ "𑄲","𑄧" },
40616 ["𑍋"]={ "𑍇","𑌾" },
40617 ["𑍌"]={ "𑍇","𑍗" },
40618 ["𑒻"]={ "𑒹","𑒺" },
40619 ["𑒼"]={ "𑒹","𑒰" },
40620 ["𑒾"]={ "𑒹","𑒽" },
40621 ["𑖺"]={ "𑖸","𑖯" },
40622 ["𑖻"]={ "𑖹","𑖯" },
40623 ["𝅗𝅥"]={ "𝅗","𝅥" },
40624 ["𝅘𝅥"]={ "𝅘","𝅥" },
40625 ["𝅘𝅥𝅮"]={ "𝅘𝅥","𝅮" },
40626 ["𝅘𝅥𝅯"]={ "𝅘𝅥","𝅯" },
40627 ["𝅘𝅥𝅰"]={ "𝅘𝅥","𝅰" },
40628 ["𝅘𝅥𝅱"]={ "𝅘𝅥","𝅱" },
40629 ["𝅘𝅥𝅲"]={ "𝅘𝅥","𝅲" },
40630 ["𝆹𝅥"]={ "𝆹","𝅥" },
40631 ["𝆺𝅥"]={ "𝆺","𝅥" },
40632 ["𝆹𝅥𝅮"]={ "𝆹𝅥","𝅮" },
40633 ["𝆺𝅥𝅮"]={ "𝆺𝅥","𝅮" },
40634 ["𝆹𝅥𝅯"]={ "𝆹𝅥","𝅯" },
40635 ["𝆺𝅥𝅯"]={ "𝆺𝅥","𝅯" },
40636   },
40637  },
40638 },
40639 ["name"]="collapse",
40640 ["prepend"]=true,
40641 ["type"]="ligature",
40642}
40643
40644end -- closure
40645
40646do -- begin closure to overcome local limits and interference
40647
40648if not modules then modules={} end modules ['luatex-fonts-gbn']={
40649 version=1.001,
40650 comment="companion to luatex-*.tex",
40651 author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
40652 copyright="PRAGMA ADE / ConTeXt Development Team",
40653 license="see context related readme files"
40654}
40655if context then
40656--removed
40657
40658end
40659local next=next
40660local fonts=fonts
40661local nodes=nodes
40662local nuts=nodes.nuts 
40663local traverseid=nuts.traverseid
40664local flushnode=nuts.flushnode
40665local glyph_code=nodes.nodecodes.glyph
40666local disc_code=nodes.nodecodes.disc
40667local tonode=nuts.tonode
40668local tonut=nuts.tonut
40669local getfont=nuts.getfont
40670local getchar=nuts.getchar
40671local getid=nuts.getid
40672local getboth=nuts.getboth
40673local getprev=nuts.getprev
40674local getnext=nuts.getnext
40675local getdisc=nuts.getdisc
40676local setchar=nuts.setchar
40677local setlink=nuts.setlink
40678local setprev=nuts.setprev
40679local n_ligaturing=node.ligaturing
40680local n_kerning=node.kerning
40681local d_ligaturing=nuts.ligaturing
40682local d_kerning=nuts.kerning
40683local basemodepass=true
40684local function l_warning() logs.report("fonts","don't call 'node.ligaturing' directly") l_warning=nil end
40685local function k_warning() logs.report("fonts","don't call 'node.kerning' directly") k_warning=nil end
40686function node.ligaturing(...)
40687 if basemodepass and l_warning then
40688  l_warning()
40689 end
40690 return n_ligaturing(...)
40691end
40692function node.kerning(...)
40693 if basemodepass and k_warning then
40694  k_warning()
40695 end
40696 return n_kerning(...)
40697end
40698function nuts.ligaturing(...)
40699 if basemodepass and l_warning then
40700  l_warning()
40701 end
40702 return d_ligaturing(...)
40703end
40704function nuts.kerning(...)
40705 if basemodepass and k_warning then
40706  k_warning()
40707 end
40708 return d_kerning(...)
40709end
40710function nodes.handlers.setbasemodepass(v)
40711 basemodepass=v
40712end
40713local function nodepass(head,groupcode,size,packtype,direction)
40714 local fontdata=fonts.hashes.identifiers
40715 if fontdata then
40716  local usedfonts={}
40717  local basefonts={}
40718  local prevfont=nil
40719  local basefont=nil
40720  local variants=nil
40721  local redundant=nil
40722  local nofused=0
40723  for n in traverseid(glyph_code,head) do
40724   local font=getfont(n)
40725   if font~=prevfont then
40726    if basefont then
40727     basefont[2]=getprev(n)
40728    end
40729    prevfont=font
40730    local used=usedfonts[font]
40731    if not used then
40732     local tfmdata=fontdata[font] 
40733     if tfmdata then
40734      local shared=tfmdata.shared 
40735      if shared then
40736       local processors=shared.processes
40737       if processors and #processors>0 then
40738        usedfonts[font]=processors
40739        nofused=nofused+1
40740       elseif basemodepass then
40741        basefont={ n,nil }
40742        basefonts[#basefonts+1]=basefont
40743       end
40744      end
40745      local resources=tfmdata.resources
40746      variants=resources and resources.variants
40747      variants=variants and next(variants) and variants or false
40748     end
40749    else
40750     local tfmdata=fontdata[prevfont]
40751     if tfmdata then
40752      local resources=tfmdata.resources
40753      variants=resources and resources.variants
40754      variants=variants and next(variants) and variants or false
40755     end
40756    end
40757   end
40758   if variants then
40759    local char=getchar(n)
40760    if (char>=0xFE00 and char<=0xFE0F) or (char>=0xE0100 and char<=0xE01EF) then
40761     local hash=variants[char]
40762     if hash then
40763      local p=getprev(n)
40764      if p and getid(p)==glyph_code then
40765       local variant=hash[getchar(p)]
40766       if variant then
40767        setchar(p,variant)
40768       end
40769      end
40770     end
40771     if not redundant then
40772      redundant={ n }
40773     else
40774      redundant[#redundant+1]=n
40775     end
40776    end
40777   end
40778  end
40779  local nofbasefonts=#basefonts
40780  if redundant then
40781   for i=1,#redundant do
40782    local r=redundant[i]
40783    local p,n=getboth(r)
40784    if r==head then
40785     head=n
40786     setprev(n)
40787    else
40788     setlink(p,n)
40789    end
40790    if nofbasefonts>0 then
40791     for i=1,nofbasefonts do
40792      local bi=basefonts[i]
40793      if r==bi[1] then
40794       bi[1]=n
40795      end
40796      if r==bi[2] then
40797       bi[2]=n
40798      end
40799     end
40800    end
40801    flushnode(r)
40802   end
40803  end
40804  for d in traverseid(disc_code,head) do
40805   local _,_,r=getdisc(d)
40806   if r then
40807    for n in traverseid(glyph_code,r) do
40808     local font=getfont(n)
40809     if font~=prevfont then
40810      prevfont=font
40811      local used=usedfonts[font]
40812      if not used then
40813       local tfmdata=fontdata[font] 
40814       if tfmdata then
40815        local shared=tfmdata.shared 
40816        if shared then
40817         local processors=shared.processes
40818         if processors and #processors>0 then
40819          usedfonts[font]=processors
40820          nofused=nofused+1
40821         end
40822        end
40823       end
40824      end
40825     end
40826    end
40827   end
40828  end
40829  if next(usedfonts) then
40830   for font,processors in next,usedfonts do
40831    for i=1,#processors do
40832     head=processors[i](head,font,0,direction,nofused) or head
40833    end
40834   end
40835  end
40836  if basemodepass and nofbasefonts>0 then
40837   for i=1,nofbasefonts do
40838    local range=basefonts[i]
40839    local start=range[1]
40840    local stop=range[2]
40841    if start then
40842     local front=head==start
40843     local prev,next
40844     if stop then
40845      next=getnext(stop)
40846      start,stop=d_ligaturing(start,stop)
40847      start,stop=d_kerning(start,stop)
40848     else
40849      prev=getprev(start)
40850      start=d_ligaturing(start)
40851      start=d_kerning(start)
40852     end
40853     if prev then
40854      setlink(prev,start)
40855     end
40856     if next then
40857      setlink(stop,next)
40858     end
40859     if front and head~=start then
40860      head=start
40861     end
40862    end
40863   end
40864  end
40865 end
40866 return head
40867end
40868local function basepass(head)
40869 if basemodepass then
40870  head=d_ligaturing(head)
40871  head=d_kerning(head)
40872 end
40873 return head
40874end
40875local protectpass=node.direct.protectglyphs or node.direct.protect_glyphs
40876local injectpass=nodes.injections.handler
40877function nodes.handlers.nodepass(head,...)
40878 if head then
40879  return tonode(nodepass(tonut(head),...))
40880 end
40881end
40882function nodes.handlers.basepass(head)
40883 if head then
40884  return tonode(basepass(tonut(head)))
40885 end
40886end
40887function nodes.handlers.injectpass(head)
40888 if head then
40889  return tonode(injectpass(tonut(head)))
40890 end
40891end
40892function nodes.handlers.protectpass(head)
40893 if head then
40894  protectpass(tonut(head))
40895  return head
40896 end
40897end
40898function nodes.simple_font_handler(head,groupcode,size,packtype,direction)
40899 if head then
40900  head=tonut(head)
40901  head=nodepass(head,groupcode,size,packtype,direction)
40902  head=injectpass(head)
40903  if not basemodepass then
40904   head=basepass(head)
40905  end
40906  protectpass(head)
40907  head=tonode(head)
40908 end
40909 return head
40910end
40911
40912end -- closure
40913