1if not modules then modules = { } end modules ['font-dsp'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to font-ini.mkiv",
5 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6 copyright = "PRAGMA ADE / ConTeXt Development Team",
7 license = "see context related readme files"
8}
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58local next, type, tonumber = next, type, tonumber
59local band = bit32.band
60local extract = bit32.extract
61local bor = bit32.bor
62local lshift = bit32.lshift
63local rshift = bit32.rshift
64local gsub = string.gsub
65local lower = string.lower
66local sub = string.sub
67local strip = string.strip
68local tohash = table.tohash
69local concat = table.concat
70local copy = table.copy
71local reversed = table.reversed
72local sort = table.sort
73local insert = table.insert
74local round = math.round
75
76local settings_to_hash = utilities.parsers.settings_to_hash_colon_too
77local setmetatableindex = table.setmetatableindex
78local formatters = string.formatters
79local sortedkeys = table.sortedkeys
80local sortedhash = table.sortedhash
81local sequenced = table.sequenced
82
83local report = logs.reporter("otf reader")
84
85local readers = fonts.handlers.otf.readers
86local streamreader = readers.streamreader
87
88local setposition = streamreader.setposition
89local getposition = streamreader.getposition
90local readuinteger = streamreader.readcardinal1
91local readushort = streamreader.readcardinal2
92local readulong = streamreader.readcardinal4
93local readinteger = streamreader.readinteger1
94local readshort = streamreader.readinteger2
95local readstring = streamreader.readstring
96local readtag = streamreader.readtag
97local readbytes = streamreader.readbytes
98local readfixed = streamreader.readfixed4
99local read2dot14 = streamreader.read2dot14
100local skipshort = streamreader.skipshort
101local skipbytes = streamreader.skip
102local readbytetable = streamreader.readbytetable
103local readbyte = streamreader.readbyte
104local readcardinaltable = streamreader.readcardinaltable
105local readintegertable = streamreader.readintegertable
106local readfword = readshort
107
108local short = 2
109local ushort = 2
110local ulong = 4
111
112directives.register("fonts.streamreader",function()
113
114 streamreader = utilities.streams
115
116 setposition = streamreader.setposition
117 getposition = streamreader.getposition
118 readuinteger = streamreader.readcardinal1
119 readushort = streamreader.readcardinal2
120 readulong = streamreader.readcardinal4
121 readinteger = streamreader.readinteger1
122 readshort = streamreader.readinteger2
123 readstring = streamreader.readstring
124 readtag = streamreader.readtag
125 readbytes = streamreader.readbytes
126 readfixed = streamreader.readfixed4
127 read2dot14 = streamreader.read2dot14
128 skipshort = streamreader.skipshort
129 skipbytes = streamreader.skip
130 readbytetable = streamreader.readbytetable
131 readbyte = streamreader.readbyte
132 readcardinaltable = streamreader.readcardinaltable
133 readintegertable = streamreader.readintegertable
134 readfword = readshort
135
136end)
137
138local gsubhandlers = { }
139local gposhandlers = { }
140
141readers.gsubhandlers = gsubhandlers
142readers.gposhandlers = gposhandlers
143
144local helpers = readers.helpers
145local gotodatatable = helpers.gotodatatable
146local setvariabledata = helpers.setvariabledata
147
148local lookupidoffset = -1
149
150local classes = {
151 "base",
152 "ligature",
153 "mark",
154 "component",
155}
156
157local gsubtypes = {
158 "single",
159 "multiple",
160 "alternate",
161 "ligature",
162 "context",
163 "chainedcontext",
164 "extension",
165 "reversechainedcontextsingle",
166}
167
168local gpostypes = {
169 "single",
170 "pair",
171 "cursive",
172 "marktobase",
173 "marktoligature",
174 "marktomark",
175 "context",
176 "chainedcontext",
177 "extension",
178}
179
180local chaindirections = {
181 context = 0,
182 chainedcontext = 1,
183 reversechainedcontextsingle = -1,
184}
185
186local function setmetrics(data,where,tag,d)
187 local w = data[where]
188 if w then
189 local v = w[tag]
190 if v then
191
192
193 w[tag] = v + d
194 end
195 end
196end
197
198local variabletags = {
199 hasc = function(data,d) setmetrics(data,"windowsmetrics","typoascender",d) end,
200 hdsc = function(data,d) setmetrics(data,"windowsmetrics","typodescender",d) end,
201 hlgp = function(data,d) setmetrics(data,"windowsmetrics","typolinegap",d) end,
202 hcla = function(data,d) setmetrics(data,"windowsmetrics","winascent",d) end,
203 hcld = function(data,d) setmetrics(data,"windowsmetrics","windescent",d) end,
204 vasc = function(data,d) setmetrics(data,"vhea not done","ascent",d) end,
205 vdsc = function(data,d) setmetrics(data,"vhea not done","descent",d) end,
206 vlgp = function(data,d) setmetrics(data,"vhea not done","linegap",d) end,
207 xhgt = function(data,d) setmetrics(data,"windowsmetrics","xheight",d) end,
208 cpht = function(data,d) setmetrics(data,"windowsmetrics","capheight",d) end,
209 sbxs = function(data,d) setmetrics(data,"windowsmetrics","subscriptxsize",d) end,
210 sbys = function(data,d) setmetrics(data,"windowsmetrics","subscriptysize",d) end,
211 sbxo = function(data,d) setmetrics(data,"windowsmetrics","subscriptxoffset",d) end,
212 sbyo = function(data,d) setmetrics(data,"windowsmetrics","subscriptyoffset",d) end,
213 spxs = function(data,d) setmetrics(data,"windowsmetrics","superscriptxsize",d) end,
214 spys = function(data,d) setmetrics(data,"windowsmetrics","superscriptysize",d) end,
215 spxo = function(data,d) setmetrics(data,"windowsmetrics","superscriptxoffset",d) end,
216 spyo = function(data,d) setmetrics(data,"windowsmetrics","superscriptyoffset",d) end,
217 strs = function(data,d) setmetrics(data,"windowsmetrics","strikeoutsize",d) end,
218 stro = function(data,d) setmetrics(data,"windowsmetrics","strikeoutpos",d) end,
219 unds = function(data,d) setmetrics(data,"postscript","underlineposition",d) end,
220 undo = function(data,d) setmetrics(data,"postscript","underlinethickness",d) end,
221}
222
223local read_cardinal = {
224 streamreader.readcardinal1,
225 streamreader.readcardinal2,
226 streamreader.readcardinal3,
227 streamreader.readcardinal4,
228}
229
230local read_integer = {
231 streamreader.readinteger1,
232 streamreader.readinteger2,
233 streamreader.readinteger3,
234 streamreader.readinteger4,
235}
236
237directives.register("fonts.streamreader",function()
238
239 read_cardinal = {
240 streamreader.readcardinal1,
241 streamreader.readcardinal2,
242 streamreader.readcardinal3,
243 streamreader.readcardinal4,
244 }
245
246 read_integer = {
247 streamreader.readinteger1,
248 streamreader.readinteger2,
249 streamreader.readinteger3,
250 streamreader.readinteger4,
251 }
252
253end)
254
255
256
257
258
259
260
261local lookupnames = {
262 gsub = {
263 single = "gsub_single",
264 multiple = "gsub_multiple",
265 alternate = "gsub_alternate",
266 ligature = "gsub_ligature",
267 context = "gsub_context",
268 chainedcontext = "gsub_contextchain",
269 reversechainedcontextsingle = "gsub_reversecontextchain",
270 },
271 gpos = {
272 single = "gpos_single",
273 pair = "gpos_pair",
274 cursive = "gpos_cursive",
275 marktobase = "gpos_mark2base",
276 marktoligature = "gpos_mark2ligature",
277 marktomark = "gpos_mark2mark",
278 context = "gpos_context",
279 chainedcontext = "gpos_contextchain",
280 }
281}
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306local lookupflags = setmetatableindex(function(t,k)
307 local v = {
308 band(k,0x0008) ~= 0 and true or false,
309 band(k,0x0004) ~= 0 and true or false,
310 band(k,0x0002) ~= 0 and true or false,
311 band(k,0x0001) ~= 0 and true or false,
312 }
313 t[k] = v
314 return v
315end)
316
317
318
319
320
321
322
323
324
325
326local function axistofactors(str)
327 local t = settings_to_hash(str)
328 for k, v in next, t do
329 t[k] = tonumber(v) or v
330 end
331 return t
332end
333
334local hash = table.setmetatableindex(function(t,k)
335 local v = sequenced(axistofactors(k),",")
336 t[k] = v
337 return v
338end)
339
340helpers.normalizedaxishash = hash
341
342local cleanname = fonts.names and fonts.names.cleanname or function(name)
343 return name and (gsub(lower(name),"[^%a%d]","")) or nil
344end
345
346helpers.cleanname = cleanname
347
348function helpers.normalizedaxis(str)
349 return hash[str] or str
350end
351
352
353
354
355local function getaxisscale(segments,minimum,default,maximum,user)
356
357
358
359 if not minimum or not default or not maximum then
360 return false
361 end
362 if user < minimum then
363 user = minimum
364 elseif user > maximum then
365 user = maximum
366 end
367 if user < default then
368 default = - (default - user) / (default - minimum)
369 elseif user > default then
370 default = (user - default) / (maximum - default)
371 else
372 default = 0
373 end
374 if not segments then
375 return default
376 end
377 local e
378 for i=1,#segments do
379 local s = segments[i]
380 if type(s) ~= "number" then
381
382 return default
383 elseif s[1] >= default then
384 if s[2] == default then
385 return default
386 else
387 e = i
388 break
389 end
390 end
391 end
392 if e then
393 local b = segments[e-1]
394 local e = segments[e]
395 return b[2] + (e[2] - b[2]) * (default - b[1]) / (e[1] - b[1])
396 else
397 return false
398 end
399end
400
401local function getfactors(data,instancespec)
402 if instancespec == true then
403
404 elseif type(instancespec) ~= "string" or instancespec == "" then
405 return
406 end
407 local variabledata = data.variabledata
408 if not variabledata then
409 return
410 end
411 local instances = variabledata.instances
412 local axis = variabledata.axis
413 local segments = variabledata.segments
414 if instances and axis then
415 local values
416 if instancespec == true then
417
418
419
420 values = { }
421 for i=1,#axis do
422 values[i] = {
423
424 value = axis[i].default,
425 }
426 end
427
428 else
429 for i=1,#instances do
430 local instance = instances[i]
431 if cleanname(instance.subfamily) == instancespec then
432 values = instance.values
433 break
434 end
435 end
436 end
437 if values then
438 local factors = { }
439 for i=1,#axis do
440 local a = axis[i]
441 factors[i] = getaxisscale(segments,a.minimum,a.default,a.maximum,values[i].value)
442 end
443 return factors
444 end
445 local values = axistofactors(hash[instancespec] or instancespec)
446 if values then
447 local factors = { }
448 for i=1,#axis do
449 local a = axis[i]
450 local d = a.default
451 factors[i] = getaxisscale(segments,a.minimum,d,a.maximum,values[a.name or a.tag] or d)
452 end
453 return factors
454 end
455 end
456end
457
458local function getscales(regions,factors)
459 local scales = { }
460 for i=1,#regions do
461 local region = regions[i]
462 local s = 1
463 for j=1,#region do
464 local axis = region[j]
465 local f = factors[j]
466 local start = axis.start
467 local peak = axis.peak
468 local stop = axis.stop
469
470 if start > peak or peak > stop then
471
472 elseif start < 0 and stop > 0 and peak ~= 0 then
473
474 elseif peak == 0 then
475
476 elseif f < start or f > stop then
477
478 s = 0
479 break
480 elseif f < peak then
481
482 s = s * (f - start) / (peak - start)
483 elseif f > peak then
484 s = s * (stop - f) / (stop - peak)
485 else
486
487 end
488 end
489 scales[i] = s
490 end
491 return scales
492end
493
494helpers.getaxisscale = getaxisscale
495helpers.getfactors = getfactors
496helpers.getscales = getscales
497helpers.axistofactors = axistofactors
498
499local function readvariationdata(f,storeoffset,factors)
500 local position = getposition(f)
501 setposition(f,storeoffset)
502
503 local format = readushort(f)
504 local regionoffset = storeoffset + readulong(f)
505 local nofdeltadata = readushort(f)
506 local deltadata = readcardinaltable(f,nofdeltadata,ulong)
507
508 setposition(f,regionoffset)
509 local nofaxis = readushort(f)
510 local nofregions = readushort(f)
511 local regions = { }
512 for i=1,nofregions do
513 local t = { }
514 for i=1,nofaxis do
515 t[i] = {
516 start = read2dot14(f),
517 peak = read2dot14(f),
518 stop = read2dot14(f),
519 }
520 end
521 regions[i] = t
522 end
523
524
525 for i=1,nofdeltadata do
526 setposition(f,storeoffset+deltadata[i])
527 local nofdeltasets = readushort(f)
528 local nofshorts = readushort(f)
529 local nofregions = readushort(f)
530 local usedregions = { }
531 local deltas = { }
532 for i=1,nofregions do
533 usedregions[i] = regions[readushort(f)+1]
534 end
535
536 for i=1,nofdeltasets do
537 local t = readintegertable(f,nofshorts,short)
538 for i=nofshorts+1,nofregions do
539 t[i] = readinteger(f)
540 end
541 deltas[i] = t
542 end
543 deltadata[i] = {
544 regions = usedregions,
545 deltas = deltas,
546 scales = factors and getscales(usedregions,factors) or nil,
547 }
548 end
549
550 setposition(f,position)
551 return regions, deltadata
552end
553
554helpers.readvariationdata = readvariationdata
555
556
557
558
559local function readcoverage(f,offset,simple)
560 setposition(f,offset)
561 local coverageformat = readushort(f)
562 if coverageformat == 1 then
563 local nofcoverage = readushort(f)
564 if simple then
565
566 if nofcoverage == 1 then
567 return { readushort(f) }
568 elseif nofcoverage == 2 then
569 return { readushort(f), readushort(f) }
570 else
571 return readcardinaltable(f,nofcoverage,ushort)
572 end
573 elseif nofcoverage == 1 then
574 return { [readushort(f)] = 0 }
575 elseif nofcoverage == 2 then
576 return { [readushort(f)] = 0, [readushort(f)] = 1 }
577 else
578 local coverage = { }
579 for i=0,nofcoverage-1 do
580 coverage[readushort(f)] = i
581 end
582 return coverage
583 end
584 elseif coverageformat == 2 then
585 local nofranges = readushort(f)
586 local coverage = { }
587 local n = simple and 1 or 0
588 for i=1,nofranges do
589 local firstindex = readushort(f)
590 local lastindex = readushort(f)
591 local coverindex = readushort(f)
592 if simple then
593 for i=firstindex,lastindex do
594 coverage[n] = i
595 n = n + 1
596 end
597 else
598 for i=firstindex,lastindex do
599 coverage[i] = n
600 n = n + 1
601 end
602 end
603 end
604 return coverage
605 else
606 report("unknown coverage format %a ",coverageformat)
607 return { }
608 end
609end
610
611local function readclassdef(f,offset,preset)
612 setposition(f,offset)
613 local classdefformat = readushort(f)
614 local classdef = { }
615 if type(preset) == "number" then
616 for k=0,preset-1 do
617 classdef[k] = 1
618 end
619 end
620 if classdefformat == 1 then
621 local index = readushort(f)
622 local nofclassdef = readushort(f)
623 for i=1,nofclassdef do
624 classdef[index] = readushort(f) + 1
625 index = index + 1
626 end
627 elseif classdefformat == 2 then
628 local nofranges = readushort(f)
629 local n = 0
630 for i=1,nofranges do
631 local firstindex = readushort(f)
632 local lastindex = readushort(f)
633 local class = readushort(f) + 1
634 for i=firstindex,lastindex do
635 classdef[i] = class
636 end
637 end
638 else
639 report("unknown classdef format %a ",classdefformat)
640 end
641 if type(preset) == "table" then
642 for k in next, preset do
643 if not classdef[k] then
644 classdef[k] = 1
645 end
646 end
647 end
648 return classdef
649end
650
651local function classtocoverage(defs)
652 if defs then
653 local list = { }
654 for index, class in next, defs do
655 local c = list[class]
656 if c then
657 c[#c+1] = index
658 else
659 list[class] = { index }
660 end
661 end
662 return list
663 end
664end
665
666
667
668local skips = { [0] =
669 0,
670 1,
671 1,
672 2,
673 1,
674 2,
675 2,
676 3,
677 2,
678 2,
679 3,
680 2,
681 3,
682 3,
683 4,
684}
685
686
687
688
689local function readvariation(f,offset)
690 local p = getposition(f)
691 setposition(f,offset)
692 local outer = readushort(f)
693 local inner = readushort(f)
694 local format = readushort(f)
695 setposition(f,p)
696 if format == 0x8000 then
697 return outer, inner
698 end
699end
700
701local function readposition(f,format,mainoffset,getdelta)
702 if format == 0 then
703 return false
704 end
705
706 if format == 0x04 then
707 local h = readshort(f)
708 if h == 0 then
709 return true
710 else
711 return { 0, 0, h, 0 }
712 end
713 end
714 if format == 0x05 then
715 local x = readshort(f)
716 local h = readshort(f)
717 if x == 0 and h == 0 then
718 return true
719 else
720 return { x, 0, h, 0 }
721 end
722 end
723 if format == 0x44 then
724 local h = readshort(f)
725 if getdelta then
726 local d = readshort(f)
727 if d > 0 then
728 local outer, inner = readvariation(f,mainoffset+d)
729 if outer then
730 h = h + getdelta(outer,inner)
731 end
732 end
733 else
734 skipshort(f,1)
735 end
736 if h == 0 then
737 return true
738 else
739 return { 0, 0, h, 0 }
740 end
741 end
742
743
744
745
746
747
748
749
750
751 local x = band(format,0x1) ~= 0 and readshort(f) or 0
752 local y = band(format,0x2) ~= 0 and readshort(f) or 0
753 local h = band(format,0x4) ~= 0 and readshort(f) or 0
754 local v = band(format,0x8) ~= 0 and readshort(f) or 0
755 if format >= 0x10 then
756 local X = band(format,0x10) ~= 0 and skipshort(f) or 0
757 local Y = band(format,0x20) ~= 0 and skipshort(f) or 0
758 local H = band(format,0x40) ~= 0 and skipshort(f) or 0
759 local V = band(format,0x80) ~= 0 and skipshort(f) or 0
760 local s = skips[extract(format,4,4)]
761 if s > 0 then
762 skipshort(f,s)
763 end
764 if getdelta then
765 if X > 0 then
766 local outer, inner = readvariation(f,mainoffset+X)
767 if outer then
768 x = x + getdelta(outer,inner)
769 end
770 end
771 if Y > 0 then
772 local outer, inner = readvariation(f,mainoffset+Y)
773 if outer then
774 y = y + getdelta(outer,inner)
775 end
776 end
777 if H > 0 then
778 local outer, inner = readvariation(f,mainoffset+H)
779 if outer then
780 h = h + getdelta(outer,inner)
781 end
782 end
783 if V > 0 then
784 local outer, inner = readvariation(f,mainoffset+V)
785 if outer then
786 v = v + getdelta(outer,inner)
787 end
788 end
789 end
790 return { x, y, h, v }
791 elseif x == 0 and y == 0 and h == 0 and v == 0 then
792 return true
793 else
794 return { x, y, h, v }
795 end
796end
797
798local function readanchor(f,offset,getdelta)
799 if not offset or offset == 0 then
800 return nil
801 end
802 setposition(f,offset)
803
804 local format = readshort(f)
805 local x = readshort(f)
806 local y = readshort(f)
807 if format == 3 then
808 if getdelta then
809 local X = readshort(f)
810 local Y = readshort(f)
811 if X > 0 then
812 local outer, inner = readvariation(f,offset+X)
813 if outer then
814 x = x + getdelta(outer,inner)
815 end
816 end
817 if Y > 0 then
818 local outer, inner = readvariation(f,offset+Y)
819 if outer then
820 y = y + getdelta(outer,inner)
821 end
822 end
823 else
824 skipshort(f,2)
825 end
826 return { x, y }
827 else
828 return { x, y }
829 end
830end
831
832
833
834
835local function readfirst(f,offset)
836 if offset then
837 setposition(f,offset)
838 end
839 return { readushort(f) }
840end
841
842
843
844local function readarray(f,offset)
845 if offset then
846 setposition(f,offset)
847 end
848 local n = readushort(f)
849 if n == 1 then
850 return { readushort(f) }, 1
851 elseif n > 0 then
852 return readcardinaltable(f,n,ushort), n
853 end
854end
855
856local function readcoveragearray(f,offset,t,simple)
857 if not t then
858 return nil
859 end
860 local n = #t
861 if n == 0 then
862 return nil
863 end
864 for i=1,n do
865 t[i] = readcoverage(f,offset+t[i],simple)
866 end
867 return t
868end
869
870local function covered(subset,all)
871 local used, u
872 for i=1,#subset do
873 local s = subset[i]
874 if all[s] then
875 if used then
876 u = u + 1
877 used[u] = s
878 else
879 u = 1
880 used = { s }
881 end
882 end
883 end
884 return used
885end
886
887
888
889
890
891
892local function readlookuparray(f,noflookups,nofcurrent)
893 local lookups = { }
894 if noflookups > 0 then
895 local length = 0
896 for i=1,noflookups do
897 local index = readushort(f) + 1
898 if index > length then
899 length = index
900 end
901 local lookup = readushort(f) + 1
902 local list = lookups[index]
903 if list then
904 list[#list+1] = lookup
905 else
906 lookups[index] = { lookup }
907 end
908 end
909 for index=1,length do
910 if not lookups[index] then
911 lookups[index] = false
912 end
913 end
914
915
916
917 end
918 return lookups
919end
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942local function unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
943 local tableoffset = lookupoffset + offset
944 setposition(f,tableoffset)
945 local subtype = readushort(f)
946 if subtype == 1 then
947 local coverage = readushort(f)
948 local subclasssets = readarray(f)
949 local rules = { }
950 if subclasssets then
951 coverage = readcoverage(f,tableoffset+coverage,true)
952 for i=1,#subclasssets do
953 local offset = subclasssets[i]
954 if offset > 0 then
955 local firstcoverage = coverage[i]
956 local rulesoffset = tableoffset + offset
957 local subclassrules = readarray(f,rulesoffset)
958 for rule=1,#subclassrules do
959 setposition(f,rulesoffset + subclassrules[rule])
960 local nofcurrent = readushort(f)
961 local noflookups = readushort(f)
962 local current = { { firstcoverage } }
963 for i=2,nofcurrent do
964 current[i] = { readushort(f) }
965 end
966 local lookups = readlookuparray(f,noflookups,nofcurrent)
967 rules[#rules+1] = {
968 current = current,
969 lookups = lookups
970 }
971 end
972 end
973 end
974 else
975 report("empty subclassset in %a subtype %i","unchainedcontext",subtype)
976 end
977 return {
978 format = "glyphs",
979 rules = rules,
980 }
981 elseif subtype == 2 then
982
983
984 local coverage = readushort(f)
985 local currentclassdef = readushort(f)
986 local subclasssets = readarray(f)
987 local rules = { }
988 if subclasssets then
989 coverage = readcoverage(f,tableoffset + coverage)
990 currentclassdef = readclassdef(f,tableoffset + currentclassdef,coverage)
991 local currentclasses = classtocoverage(currentclassdef,fontdata.glyphs)
992 for class=1,#subclasssets do
993 local offset = subclasssets[class]
994 if offset > 0 then
995 local firstcoverage = currentclasses[class]
996 if firstcoverage then
997 firstcoverage = covered(firstcoverage,coverage)
998 if firstcoverage then
999 local rulesoffset = tableoffset + offset
1000 local subclassrules = readarray(f,rulesoffset)
1001 for rule=1,#subclassrules do
1002 setposition(f,rulesoffset + subclassrules[rule])
1003 local nofcurrent = readushort(f)
1004 local noflookups = readushort(f)
1005 local current = { firstcoverage }
1006 for i=2,nofcurrent do
1007 current[i] = currentclasses[readushort(f) + 1]
1008 end
1009 local lookups = readlookuparray(f,noflookups,nofcurrent)
1010 rules[#rules+1] = {
1011 current = current,
1012 lookups = lookups
1013 }
1014 end
1015 else
1016 report("no coverage")
1017 end
1018 else
1019 report("no coverage class")
1020 end
1021 end
1022 end
1023 else
1024 report("empty subclassset in %a subtype %i","unchainedcontext",subtype)
1025 end
1026 return {
1027 format = "class",
1028 rules = rules,
1029 }
1030 elseif subtype == 3 then
1031 local nofglyphs = readushort(f)
1032 local noflookups = readushort(f)
1033 local current = readcardinaltable(f,nofglyphs,ushort)
1034 local lookups = readlookuparray(f,noflookups,#current)
1035 current = readcoveragearray(f,tableoffset,current,true)
1036 return {
1037 format = "coverage",
1038 rules = {
1039 {
1040 current = current,
1041 lookups = lookups,
1042 }
1043 }
1044 }
1045 else
1046 report("unsupported subtype %a in %a %s",subtype,"unchainedcontext",what)
1047 end
1048end
1049
1050
1051
1052
1053
1054local function chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
1055 local tableoffset = lookupoffset + offset
1056 setposition(f,tableoffset)
1057 local subtype = readushort(f)
1058 if subtype == 1 then
1059 local coverage = readushort(f)
1060 local subclasssets = readarray(f)
1061 local rules = { }
1062 if subclasssets then
1063 coverage = readcoverage(f,tableoffset+coverage,true)
1064 for i=1,#subclasssets do
1065 local offset = subclasssets[i]
1066 if offset > 0 then
1067 local firstcoverage = coverage[i]
1068 local rulesoffset = tableoffset + offset
1069 local subclassrules = readarray(f,rulesoffset)
1070 for rule=1,#subclassrules do
1071 setposition(f,rulesoffset + subclassrules[rule])
1072 local nofbefore = readushort(f)
1073 local before
1074 if nofbefore > 0 then
1075 before = { }
1076 for i=1,nofbefore do
1077 before[i] = { readushort(f) }
1078 end
1079 end
1080 local nofcurrent = readushort(f)
1081 local current = { { firstcoverage } }
1082 for i=2,nofcurrent do
1083 current[i] = { readushort(f) }
1084 end
1085 local nofafter = readushort(f)
1086 local after
1087 if nofafter > 0 then
1088 after = { }
1089 for i=1,nofafter do
1090 after[i] = { readushort(f) }
1091 end
1092 end
1093 local noflookups = readushort(f)
1094 local lookups = readlookuparray(f,noflookups,nofcurrent)
1095 rules[#rules+1] = {
1096 before = before,
1097 current = current,
1098 after = after,
1099 lookups = lookups,
1100 }
1101 end
1102 end
1103 end
1104 else
1105 report("empty subclassset in %a subtype %i","chainedcontext",subtype)
1106 end
1107 return {
1108 format = "glyphs",
1109 rules = rules,
1110 }
1111 elseif subtype == 2 then
1112 local coverage = readushort(f)
1113 local beforeclassdef = readushort(f)
1114 local currentclassdef = readushort(f)
1115 local afterclassdef = readushort(f)
1116 local subclasssets = readarray(f)
1117 local rules = { }
1118 if subclasssets then
1119 local coverage = readcoverage(f,tableoffset + coverage)
1120 local beforeclassdef = readclassdef(f,tableoffset + beforeclassdef,nofglyphs)
1121 local currentclassdef = readclassdef(f,tableoffset + currentclassdef,coverage)
1122 local afterclassdef = readclassdef(f,tableoffset + afterclassdef,nofglyphs)
1123 local beforeclasses = classtocoverage(beforeclassdef,fontdata.glyphs)
1124 local currentclasses = classtocoverage(currentclassdef,fontdata.glyphs)
1125 local afterclasses = classtocoverage(afterclassdef,fontdata.glyphs)
1126 for class=1,#subclasssets do
1127 local offset = subclasssets[class]
1128 if offset > 0 then
1129 local firstcoverage = currentclasses[class]
1130 if firstcoverage then
1131 firstcoverage = covered(firstcoverage,coverage)
1132 if firstcoverage then
1133 local rulesoffset = tableoffset + offset
1134 local subclassrules = readarray(f,rulesoffset)
1135 for rule=1,#subclassrules do
1136
1137
1138 setposition(f,rulesoffset + subclassrules[rule])
1139 local nofbefore = readushort(f)
1140 local before
1141 if nofbefore > 0 then
1142 before = { }
1143 for i=1,nofbefore do
1144 before[i] = beforeclasses[readushort(f) + 1]
1145 end
1146 end
1147 local nofcurrent = readushort(f)
1148 local current = { firstcoverage }
1149 for i=2,nofcurrent do
1150 current[i] = currentclasses[readushort(f)+ 1]
1151 end
1152 local nofafter = readushort(f)
1153 local after
1154 if nofafter > 0 then
1155 after = { }
1156 for i=1,nofafter do
1157 after[i] = afterclasses[readushort(f) + 1]
1158 end
1159 end
1160
1161 local noflookups = readushort(f)
1162 local lookups = readlookuparray(f,noflookups,nofcurrent)
1163 rules[#rules+1] = {
1164 before = before,
1165 current = current,
1166 after = after,
1167 lookups = lookups,
1168 }
1169 end
1170 else
1171 report("no coverage")
1172 end
1173 else
1174 report("class is not covered")
1175 end
1176 end
1177 end
1178 else
1179 report("empty subclassset in %a subtype %i","chainedcontext",subtype)
1180 end
1181 return {
1182 format = "class",
1183 rules = rules,
1184 }
1185 elseif subtype == 3 then
1186 local before = readarray(f)
1187 local current = readarray(f)
1188 local after = readarray(f)
1189 local noflookups = readushort(f)
1190 local lookups = readlookuparray(f,noflookups,#current)
1191 before = readcoveragearray(f,tableoffset,before,true)
1192 current = readcoveragearray(f,tableoffset,current,true)
1193 after = readcoveragearray(f,tableoffset,after,true)
1194 return {
1195 format = "coverage",
1196 rules = {
1197 {
1198 before = before,
1199 current = current,
1200 after = after,
1201 lookups = lookups,
1202 }
1203 }
1204 }
1205 else
1206 report("unsupported subtype %a in %a %s",subtype,"chainedcontext",what)
1207 end
1208end
1209
1210local function extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,types,handlers,what)
1211 local tableoffset = lookupoffset + offset
1212 setposition(f,tableoffset)
1213 local subtype = readushort(f)
1214 if subtype == 1 then
1215 local lookuptype = types[readushort(f)]
1216 local faroffset = readulong(f)
1217 local handler = handlers[lookuptype]
1218 if handler then
1219
1220 return handler(f,fontdata,lookupid,tableoffset + faroffset,0,glyphs,nofglyphs), lookuptype
1221 else
1222 report("no handler for lookuptype %a subtype %a in %s %s",lookuptype,subtype,what,"extension")
1223 end
1224 else
1225 report("unsupported subtype %a in %s %s",subtype,what,"extension")
1226 end
1227end
1228
1229
1230
1231function gsubhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1232 local tableoffset = lookupoffset + offset
1233 setposition(f,tableoffset)
1234 local subtype = readushort(f)
1235 if subtype == 1 then
1236 local coverage = readushort(f)
1237 local delta = readshort(f)
1238 local coverage = readcoverage(f,tableoffset+coverage)
1239 for index in next, coverage do
1240 local newindex = (index + delta) % 65536
1241 if index > nofglyphs or newindex > nofglyphs then
1242 report("invalid index in %s format %i: %i -> %i (max %i)","single",subtype,index,newindex,nofglyphs)
1243 coverage[index] = nil
1244 else
1245 coverage[index] = newindex
1246 end
1247 end
1248 return {
1249 coverage = coverage
1250 }
1251 elseif subtype == 2 then
1252 local coverage = readushort(f)
1253 local nofreplacements = readushort(f)
1254 local replacements = readcardinaltable(f,nofreplacements,ushort)
1255 local coverage = readcoverage(f,tableoffset + coverage)
1256 for index, newindex in next, coverage do
1257 newindex = newindex + 1
1258 if index > nofglyphs or newindex > nofglyphs then
1259 report("invalid index in %s format %i: %i -> %i (max %i)","single",subtype,index,newindex,nofglyphs)
1260 coverage[index] = nil
1261 else
1262 coverage[index] = replacements[newindex]
1263 end
1264 end
1265 return {
1266 coverage = coverage
1267 }
1268 else
1269 report("unsupported subtype %a in %a substitution",subtype,"single")
1270 end
1271end
1272
1273
1274
1275local function sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
1276 local tableoffset = lookupoffset + offset
1277 setposition(f,tableoffset)
1278 local subtype = readushort(f)
1279 if subtype == 1 then
1280 local coverage = readushort(f)
1281 local nofsequence = readushort(f)
1282 local sequences = readcardinaltable(f,nofsequence,ushort)
1283 for i=1,nofsequence do
1284 setposition(f,tableoffset + sequences[i])
1285 sequences[i] = readcardinaltable(f,readushort(f),ushort)
1286 end
1287 local coverage = readcoverage(f,tableoffset + coverage)
1288 for index, newindex in next, coverage do
1289 newindex = newindex + 1
1290 if index > nofglyphs or newindex > nofglyphs then
1291 report("invalid index in %s format %i: %i -> %i (max %i)",what,subtype,index,newindex,nofglyphs)
1292 coverage[index] = nil
1293 else
1294 coverage[index] = sequences[newindex]
1295 end
1296 end
1297 return {
1298 coverage = coverage
1299 }
1300 else
1301 report("unsupported subtype %a in %a substitution",subtype,what)
1302 end
1303end
1304
1305function gsubhandlers.multiple(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1306 return sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"multiple")
1307end
1308
1309function gsubhandlers.alternate(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1310 return sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"alternate")
1311end
1312
1313function gsubhandlers.ligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1314 local tableoffset = lookupoffset + offset
1315 setposition(f,tableoffset)
1316 local subtype = readushort(f)
1317 if subtype == 1 then
1318 local coverage = readushort(f)
1319 local nofsets = readushort(f)
1320 local ligatures = readcardinaltable(f,nofsets,ushort)
1321 for i=1,nofsets do
1322 local offset = lookupoffset + offset + ligatures[i]
1323 setposition(f,offset)
1324 local n = readushort(f)
1325 if n == 1 then
1326 ligatures[i] = { offset + readushort(f) }
1327 else
1328 local l = { }
1329 for i=1,n do
1330 l[i] = offset + readushort(f)
1331 end
1332 ligatures[i] = l
1333 end
1334 end
1335 local coverage = readcoverage(f,tableoffset + coverage)
1336 for index, newindex in next, coverage do
1337 local hash = { }
1338 local ligatures = ligatures[newindex+1]
1339 for i=1,#ligatures do
1340 local offset = ligatures[i]
1341 setposition(f,offset)
1342 local lig = readushort(f)
1343 local cnt = readushort(f)
1344 local hsh = hash
1345 for i=2,cnt do
1346 local c = readushort(f)
1347 local h = hsh[c]
1348 if not h then
1349 h = { }
1350 hsh[c] = h
1351 end
1352 hsh = h
1353 end
1354 hsh.ligature = lig
1355 end
1356 coverage[index] = hash
1357 end
1358 return {
1359 coverage = coverage
1360 }
1361 else
1362 report("unsupported subtype %a in %a substitution",subtype,"ligature")
1363 end
1364end
1365
1366function gsubhandlers.context(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1367 return unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"substitution"), "context"
1368end
1369
1370function gsubhandlers.chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1371 return chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"substitution"), "chainedcontext"
1372end
1373
1374function gsubhandlers.extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1375 return extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,gsubtypes,gsubhandlers,"substitution")
1376end
1377
1378function gsubhandlers.reversechainedcontextsingle(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1379 local tableoffset = lookupoffset + offset
1380 setposition(f,tableoffset)
1381 local subtype = readushort(f)
1382 if subtype == 1 then
1383 local current = readfirst(f)
1384 local before = readarray(f)
1385 local after = readarray(f)
1386 local replacements = readarray(f)
1387 current = readcoveragearray(f,tableoffset,current,true)
1388 before = readcoveragearray(f,tableoffset,before,true)
1389 after = readcoveragearray(f,tableoffset,after,true)
1390 return {
1391 format = "reversecoverage",
1392 rules = {
1393 {
1394 before = before,
1395 current = current,
1396 after = after,
1397 replacements = replacements,
1398 }
1399 }
1400 }, "reversechainedcontextsingle"
1401 else
1402 report("unsupported subtype %a in %a substitution",subtype,"reversechainedcontextsingle")
1403 end
1404end
1405
1406
1407
1408local function readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta)
1409 local done = { }
1410 for i=1,#sets do
1411 local offset = sets[i]
1412 local reused = done[offset]
1413 if not reused then
1414 offset = tableoffset + offset
1415 setposition(f,offset)
1416 local n = readushort(f)
1417 reused = { }
1418 for i=1,n do
1419 reused[i] = {
1420 readushort(f),
1421 readposition(f,format1,offset,getdelta),
1422 readposition(f,format2,offset,getdelta),
1423 }
1424 end
1425 done[offset] = reused
1426 end
1427 sets[i] = reused
1428 end
1429 return sets
1430end
1431
1432local function readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,mainoffset,getdelta)
1433 local classlist1 = { }
1434 for i=1,nofclasses1 do
1435 local classlist2 = { }
1436 classlist1[i] = classlist2
1437 for j=1,nofclasses2 do
1438 local one = readposition(f,format1,mainoffset,getdelta)
1439 local two = readposition(f,format2,mainoffset,getdelta)
1440 if one or two then
1441 classlist2[j] = { one, two }
1442 else
1443 classlist2[j] = false
1444 end
1445 end
1446 end
1447 return classlist1
1448end
1449
1450
1451
1452function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1453 local tableoffset = lookupoffset + offset
1454 setposition(f,tableoffset)
1455 local subtype = readushort(f)
1456 local getdelta = fontdata.temporary.getdelta
1457 if subtype == 1 then
1458 local coverage = readushort(f)
1459 local format = readushort(f)
1460 local value = readposition(f,format,tableoffset,getdelta)
1461 local coverage = readcoverage(f,tableoffset+coverage)
1462 for index, newindex in next, coverage do
1463 coverage[index] = value
1464 end
1465 return {
1466 format = "single",
1467 coverage = coverage,
1468 }
1469 elseif subtype == 2 then
1470 local coverage = readushort(f)
1471 local format = readushort(f)
1472 local nofvalues = readushort(f)
1473 local values = { }
1474 for i=1,nofvalues do
1475 values[i] = readposition(f,format,tableoffset,getdelta)
1476 end
1477 local coverage = readcoverage(f,tableoffset+coverage)
1478 for index, newindex in next, coverage do
1479 coverage[index] = values[newindex+1]
1480 end
1481 return {
1482 format = "single",
1483 coverage = coverage,
1484 }
1485 else
1486 report("unsupported subtype %a in %a positioning",subtype,"single")
1487 end
1488end
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1613 local tableoffset = lookupoffset + offset
1614 setposition(f,tableoffset)
1615 local subtype = readushort(f)
1616 local getdelta = fontdata.temporary.getdelta
1617 if subtype == 1 then
1618 local coverage = readushort(f)
1619 local format1 = readushort(f)
1620 local format2 = readushort(f)
1621 local sets = readarray(f)
1622 sets = readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta)
1623 coverage = readcoverage(f,tableoffset + coverage)
1624 local shared = { }
1625 for index, newindex in next, coverage do
1626 local set = sets[newindex+1]
1627 local hash = { }
1628 for i=1,#set do
1629 local value = set[i]
1630 if value then
1631 local other = value[1]
1632 local share = shared[value]
1633 if share == nil then
1634 local first = value[2]
1635 local second = value[3]
1636 if first or second then
1637 share = { first, second or nil }
1638 else
1639 share = false
1640 end
1641 shared[value] = share
1642 end
1643 hash[other] = share or nil
1644 end
1645 end
1646 coverage[index] = hash
1647 end
1648 return {
1649 shared = shared and true or nil,
1650 format = "pair",
1651 coverage = coverage,
1652 }
1653 elseif subtype == 2 then
1654 local coverage = readushort(f)
1655 local format1 = readushort(f)
1656 local format2 = readushort(f)
1657 local classdef1 = readushort(f)
1658 local classdef2 = readushort(f)
1659 local nofclasses1 = readushort(f)
1660 local nofclasses2 = readushort(f)
1661 local classlist = readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,tableoffset,getdelta)
1662 coverage = readcoverage(f,tableoffset+coverage)
1663 classdef1 = readclassdef(f,tableoffset+classdef1,coverage)
1664 classdef2 = readclassdef(f,tableoffset+classdef2,nofglyphs)
1665 local usedcoverage = { }
1666 local shared = { }
1667 for g1, c1 in next, classdef1 do
1668 if coverage[g1] then
1669 local l1 = classlist[c1]
1670 if l1 then
1671 local hash = { }
1672 for paired, class in next, classdef2 do
1673 local offsets = l1[class]
1674 if offsets then
1675 local first = offsets[1]
1676 local second = offsets[2]
1677 if first or second then
1678 local s1 = shared[first]
1679 if s1 == nil then
1680 s1 = { }
1681 shared[first] = s1
1682 end
1683 local s2 = s1[second]
1684 if s2 == nil then
1685 s2 = { first, second or nil }
1686 s1[second] = s2
1687 end
1688 hash[paired] = s2
1689 end
1690 end
1691 end
1692 usedcoverage[g1] = hash
1693 end
1694 end
1695 end
1696 return {
1697 shared = shared and true or nil,
1698 format = "pair",
1699 coverage = usedcoverage,
1700 }
1701 elseif subtype == 3 then
1702 report("yet unsupported subtype %a in %a positioning",subtype,"pair")
1703 else
1704 report("unsupported subtype %a in %a positioning",subtype,"pair")
1705 end
1706end
1707
1708function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1709 local tableoffset = lookupoffset + offset
1710 setposition(f,tableoffset)
1711 local subtype = readushort(f)
1712 local getdelta = fontdata.temporary.getdelta
1713 if subtype == 1 then
1714 local coverage = tableoffset + readushort(f)
1715 local nofrecords = readushort(f)
1716 local records = { }
1717 for i=1,nofrecords do
1718 local entry = readushort(f)
1719 local exit = readushort(f)
1720 records[i] = {
1721
1722
1723 entry ~= 0 and (tableoffset + entry) or false,
1724 exit ~= 0 and (tableoffset + exit ) or nil,
1725 }
1726 end
1727
1728
1729 local cc = (fontdata.temporary.cursivecount or 0) + 1
1730 fontdata.temporary.cursivecount = cc
1731 cc = "cc-" .. cc
1732 coverage = readcoverage(f,coverage)
1733 for i=1,nofrecords do
1734 local r = records[i]
1735 records[i] = {
1736
1737 cc,
1738
1739
1740 readanchor(f,r[1],getdelta) or false,
1741 readanchor(f,r[2],getdelta) or nil,
1742 }
1743 end
1744 for index, newindex in next, coverage do
1745 coverage[index] = records[newindex+1]
1746 end
1747 return {
1748 coverage = coverage,
1749 }
1750 else
1751 report("unsupported subtype %a in %a positioning",subtype,"cursive")
1752 end
1753end
1754
1755local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,ligature)
1756 local tableoffset = lookupoffset + offset
1757 setposition(f,tableoffset)
1758 local subtype = readushort(f)
1759 local getdelta = fontdata.temporary.getdelta
1760 if subtype == 1 then
1761
1762 local markcoverage = tableoffset + readushort(f)
1763 local basecoverage = tableoffset + readushort(f)
1764 local nofclasses = readushort(f)
1765 local markoffset = tableoffset + readushort(f)
1766 local baseoffset = tableoffset + readushort(f)
1767
1768 local markcoverage = readcoverage(f,markcoverage)
1769 local basecoverage = readcoverage(f,basecoverage,true)
1770
1771 setposition(f,markoffset)
1772 local markclasses = { }
1773 local nofmarkclasses = readushort(f)
1774
1775 local lastanchor = fontdata.lastanchor or 0
1776 local usedanchors = { }
1777
1778 for i=1,nofmarkclasses do
1779 local class = readushort(f) + 1
1780 local offset = readushort(f)
1781 if offset == 0 then
1782 markclasses[i] = false
1783 else
1784 markclasses[i] = { class, markoffset + offset }
1785 end
1786 usedanchors[class] = true
1787 end
1788 for i=1,nofmarkclasses do
1789 local mc = markclasses[i]
1790 if mc then
1791 mc[2] = readanchor(f,mc[2],getdelta)
1792 end
1793 end
1794
1795 setposition(f,baseoffset)
1796 local nofbaserecords = readushort(f)
1797 local baserecords = { }
1798
1799 if ligature then
1800
1801
1802
1803
1804 for i=1,nofbaserecords do
1805 local offset = readushort(f)
1806 if offset == 0 then
1807 baserecords[i] = false
1808 else
1809 baserecords[i] = baseoffset + offset
1810 end
1811 end
1812 for i=1,nofbaserecords do
1813 local recordoffset = baserecords[i]
1814 if recordoffset then
1815 setposition(f,recordoffset)
1816 local nofcomponents = readushort(f)
1817 local components = { }
1818 for i=1,nofcomponents do
1819 local classes = { }
1820 for i=1,nofclasses do
1821 local offset = readushort(f)
1822 if offset ~= 0 then
1823 classes[i] = recordoffset + offset
1824 else
1825 classes[i] = false
1826 end
1827 end
1828 components[i] = classes
1829 end
1830 baserecords[i] = components
1831 end
1832 end
1833 local baseclasses = { }
1834 for i=1,nofclasses do
1835 baseclasses[i] = { }
1836 end
1837 for i=1,nofbaserecords do
1838 local components = baserecords[i]
1839 if components then
1840 local b = basecoverage[i]
1841 for c=1,#components do
1842 local classes = components[c]
1843 if classes then
1844 for i=1,nofclasses do
1845 local anchor = readanchor(f,classes[i],getdelta)
1846 local bclass = baseclasses[i]
1847 local bentry = bclass[b]
1848 if bentry then
1849 bentry[c] = anchor
1850 else
1851 bclass[b]= { [c] = anchor }
1852 end
1853 end
1854 end
1855 end
1856 end
1857 end
1858 for index, newindex in next, markcoverage do
1859 markcoverage[index] = markclasses[newindex+1] or nil
1860 end
1861 return {
1862 format = "ligature",
1863 baseclasses = baseclasses,
1864 coverage = markcoverage,
1865 }
1866 else
1867 for i=1,nofbaserecords do
1868 local r = { }
1869 for j=1,nofclasses do
1870 local offset = readushort(f)
1871 if offset == 0 then
1872 r[j] = false
1873 else
1874 r[j] = baseoffset + offset
1875 end
1876 end
1877 baserecords[i] = r
1878 end
1879 local baseclasses = { }
1880 for i=1,nofclasses do
1881 baseclasses[i] = { }
1882 end
1883 for i=1,nofbaserecords do
1884 local r = baserecords[i]
1885 local b = basecoverage[i]
1886 for j=1,nofclasses do
1887 baseclasses[j][b] = readanchor(f,r[j],getdelta)
1888 end
1889 end
1890 for index, newindex in next, markcoverage do
1891 markcoverage[index] = markclasses[newindex+1] or nil
1892 end
1893
1894 return {
1895 format = "base",
1896 baseclasses = baseclasses,
1897 coverage = markcoverage,
1898 }
1899 end
1900 else
1901 report("unsupported subtype %a in",subtype)
1902 end
1903
1904end
1905
1906function gposhandlers.marktobase(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1907 return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1908end
1909
1910function gposhandlers.marktoligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1911 return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,true)
1912end
1913
1914function gposhandlers.marktomark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1915 return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1916end
1917
1918function gposhandlers.context(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1919 return unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"positioning"), "context"
1920end
1921
1922function gposhandlers.chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1923 return chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"positioning"), "chainedcontext"
1924end
1925
1926function gposhandlers.extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1927 return extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,gpostypes,gposhandlers,"positioning")
1928end
1929
1930
1931
1932do
1933
1934 local plugins = { }
1935
1936 function plugins.size(f,fontdata,tableoffset,feature)
1937 if fontdata.designsize then
1938
1939
1940 else
1941 local function check(offset)
1942 setposition(f,offset)
1943 local designsize = readushort(f)
1944 if designsize > 0 then
1945 local fontstyleid = readushort(f)
1946 local guimenuid = readushort(f)
1947 local minsize = readushort(f)
1948 local maxsize = readushort(f)
1949 if minsize == 0 and maxsize == 0 and fontstyleid == 0 and guimenuid == 0 then
1950 minsize = designsize
1951 maxsize = designsize
1952 end
1953 if designsize >= minsize and designsize <= maxsize then
1954 return minsize, maxsize, designsize
1955 end
1956 end
1957 end
1958 local minsize, maxsize, designsize = check(tableoffset+feature.offset+feature.parameters)
1959 if not designsize then
1960
1961
1962
1963 minsize, maxsize, designsize = check(tableoffset+feature.parameters)
1964 if designsize then
1965 report("bad size feature in %a, falling back to wrong offset",fontdata.filename or "?")
1966 else
1967 report("bad size feature in %a,",fontdata.filename or "?")
1968 end
1969 end
1970 if designsize then
1971 fontdata.minsize = minsize
1972 fontdata.maxsize = maxsize
1973 fontdata.designsize = designsize
1974 end
1975 end
1976 end
1977
1978
1979
1980
1981
1982
1983
1984
1985 local function reorderfeatures(fontdata,scripts,features)
1986 local scriptlangs = { }
1987 local featurehash = { }
1988 local featureorder = { }
1989 for script, languages in next, scripts do
1990 for language, record in next, languages do
1991 local hash = { }
1992 local list = record.featureindices
1993 for k=1,#list do
1994 local index = list[k]
1995 local feature = features[index]
1996 local lookups = feature.lookups
1997 local tag = feature.tag
1998 if tag then
1999 hash[tag] = true
2000 end
2001 if lookups then
2002 for i=1,#lookups do
2003 local lookup = lookups[i]
2004 local o = featureorder[lookup]
2005 if o then
2006 local okay = true
2007 for i=1,#o do
2008 if o[i] == tag then
2009 okay = false
2010 break
2011 end
2012 end
2013 if okay then
2014 o[#o+1] = tag
2015 end
2016 else
2017 featureorder[lookup] = { tag }
2018 end
2019 local f = featurehash[lookup]
2020 if f then
2021 local h = f[tag]
2022 if h then
2023 local s = h[script]
2024 if s then
2025 s[language] = true
2026 else
2027 h[script] = { [language] = true }
2028 end
2029 else
2030 f[tag] = { [script] = { [language] = true } }
2031 end
2032 else
2033 featurehash[lookup] = { [tag] = { [script] = { [language] = true } } }
2034 end
2035
2036 local h = scriptlangs[tag]
2037 if h then
2038 local s = h[script]
2039 if s then
2040 s[language] = true
2041 else
2042 h[script] = { [language] = true }
2043 end
2044 else
2045 scriptlangs[tag] = { [script] = { [language] = true } }
2046 end
2047 end
2048 end
2049 end
2050 end
2051 end
2052 return scriptlangs, featurehash, featureorder
2053 end
2054
2055 local function readscriplan(f,fontdata,scriptoffset)
2056 setposition(f,scriptoffset)
2057 local nofscripts = readushort(f)
2058 local scripts = { }
2059 for i=1,nofscripts do
2060 scripts[readtag(f)] = scriptoffset + readushort(f)
2061 end
2062
2063 local languagesystems = setmetatableindex("table")
2064 for script, offset in next, scripts do
2065 setposition(f,offset)
2066 local defaultoffset = readushort(f)
2067 local noflanguages = readushort(f)
2068 local languages = { }
2069 if defaultoffset > 0 then
2070 languages.dflt = languagesystems[offset + defaultoffset]
2071 end
2072 for i=1,noflanguages do
2073 local language = readtag(f)
2074 local offset = offset + readushort(f)
2075 languages[language] = languagesystems[offset]
2076 end
2077 scripts[script] = languages
2078 end
2079
2080 for offset, usedfeatures in next, languagesystems do
2081 if offset > 0 then
2082 setposition(f,offset)
2083 local featureindices = { }
2084 usedfeatures.featureindices = featureindices
2085 usedfeatures.lookuporder = readushort(f)
2086 usedfeatures.requiredindex = readushort(f)
2087 local noffeatures = readushort(f)
2088 for i=1,noffeatures do
2089 featureindices[i] = readushort(f) + 1
2090 end
2091 end
2092 end
2093 return scripts
2094 end
2095
2096 local function readfeatures(f,fontdata,featureoffset)
2097 setposition(f,featureoffset)
2098 local features = { }
2099 local noffeatures = readushort(f)
2100 for i=1,noffeatures do
2101
2102 features[i] = {
2103 tag = readtag(f),
2104 offset = readushort(f)
2105 }
2106 end
2107
2108 for i=1,noffeatures do
2109 local feature = features[i]
2110 local offset = featureoffset+feature.offset
2111 setposition(f,offset)
2112 local parameters = readushort(f)
2113 local noflookups = readushort(f)
2114 if noflookups > 0 then
2115
2116
2117
2118
2119
2120 local lookups = readcardinaltable(f,noflookups,ushort)
2121 feature.lookups = lookups
2122 for j=1,noflookups do
2123 lookups[j] = lookups[j] + 1
2124 end
2125 end
2126 if parameters > 0 then
2127 feature.parameters = parameters
2128 local plugin = plugins[feature.tag]
2129 if plugin then
2130 plugin(f,fontdata,featureoffset,feature)
2131 end
2132 end
2133 end
2134 return features
2135 end
2136
2137 local function readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder,nofmarkclasses)
2138 setposition(f,lookupoffset)
2139 local noflookups = readushort(f)
2140 local lookups = readcardinaltable(f,noflookups,ushort)
2141 for lookupid=1,noflookups do
2142 local offset = lookups[lookupid]
2143 setposition(f,lookupoffset+offset)
2144 local subtables = { }
2145 local typebits = readushort(f)
2146 local flagbits = readushort(f)
2147 local lookuptype = lookuptypes[typebits]
2148 local lookupflags = lookupflags[flagbits]
2149 local nofsubtables = readushort(f)
2150 for j=1,nofsubtables do
2151 subtables[j] = offset + readushort(f)
2152 end
2153
2154 local markclass = band(flagbits,0x0010) ~= 0
2155 local markset = rshift(flagbits,8)
2156 if markclass then
2157 markclass = readushort(f)
2158 end
2159 if markset > 0 then
2160 markclass = nofmarkclasses + markset
2161 end
2162 lookups[lookupid] = {
2163 type = lookuptype,
2164
2165 flags = lookupflags,
2166 name = lookupid,
2167 subtables = subtables,
2168 markclass = markclass,
2169 features = featurehash[lookupid],
2170 order = featureorder[lookupid],
2171 }
2172 end
2173 return lookups
2174 end
2175
2176 local f_lookupname = formatters["%s_%s_%s"]
2177
2178 local function resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset)
2179
2180 local sequences = fontdata.sequences or { }
2181 local sublookuplist = fontdata.sublookups or { }
2182 fontdata.sequences = sequences
2183 fontdata.sublookups = sublookuplist
2184 local nofsublookups = #sublookuplist
2185 local nofsequences = #sequences
2186 local lastsublookup = nofsublookups
2187 local lastsequence = nofsequences
2188 local lookupnames = lookupnames[what]
2189 local sublookuphash = { }
2190 local sublookupcheck = { }
2191 local glyphs = fontdata.glyphs
2192 local nofglyphs = fontdata.nofglyphs or #glyphs
2193 local noflookups = #lookups
2194 local lookupprefix = sub(what,2,2)
2195
2196 local usedlookups = false
2197
2198 local allsteps = { }
2199
2200 for lookupid=1,noflookups do
2201 local lookup = lookups[lookupid]
2202 local lookuptype = lookup.type
2203 local subtables = lookup.subtables
2204 local features = lookup.features
2205 local handler = lookuphandlers[lookuptype]
2206 if handler then
2207 local nofsubtables = #subtables
2208 local order = lookup.order
2209 local flags = lookup.flags
2210
2211 if flags[1] then flags[1] = "mark" end
2212 if flags[2] then flags[2] = "ligature" end
2213 if flags[3] then flags[3] = "base" end
2214
2215 local markclass = lookup.markclass
2216
2217 if nofsubtables > 0 then
2218 local steps = { }
2219 local nofsteps = 0
2220 local oldtype = nil
2221 for s=1,nofsubtables do
2222 local step, lt = handler(f,fontdata,lookupid,lookupoffset,subtables[s],glyphs,nofglyphs)
2223 if lt then
2224 lookuptype = lt
2225 if oldtype and lt ~= oldtype then
2226 report("messy %s lookup type %a and %a",what,lookuptype,oldtype)
2227 end
2228 oldtype = lookuptype
2229 end
2230 if not step then
2231 report("unsupported %s lookup type %a",what,lookuptype)
2232 else
2233 nofsteps = nofsteps + 1
2234 steps[nofsteps] = step
2235 local rules = step.rules
2236 if rules then
2237 allsteps[#allsteps+1] = step
2238 for i=1,#rules do
2239 local rule = rules[i]
2240 local before = rule.before
2241 local current = rule.current
2242 local after = rule.after
2243 local replacements = rule.replacements
2244 if before then
2245 for i=1,#before do
2246 before[i] = tohash(before[i])
2247 end
2248
2249 rule.before = reversed(before)
2250 end
2251 if current then
2252 if replacements then
2253
2254
2255 local first = current[1]
2256 local hash = { }
2257 local repl = { }
2258 for i=1,#first do
2259 local c = first[i]
2260 hash[c] = true
2261 repl[c] = replacements[i]
2262 end
2263 rule.current = { hash }
2264 rule.replacements = repl
2265 else
2266 for i=1,#current do
2267 current[i] = tohash(current[i])
2268 end
2269 end
2270 else
2271
2272 end
2273 if after then
2274 for i=1,#after do
2275 after[i] = tohash(after[i])
2276 end
2277 end
2278 if usedlookups then
2279 local lookups = rule.lookups
2280 if lookups then
2281 for k, v in next, lookups do
2282 if v then
2283 for k, v in next, v do
2284 usedlookups[v] = usedlookups[v] + 1
2285 end
2286 end
2287 end
2288 end
2289 end
2290 end
2291 end
2292 end
2293 end
2294 if nofsteps ~= nofsubtables then
2295 report("bogus subtables removed in %s lookup type %a",what,lookuptype)
2296 end
2297 lookuptype = lookupnames[lookuptype] or lookuptype
2298 if features then
2299 nofsequences = nofsequences + 1
2300
2301 local l = {
2302 index = nofsequences,
2303 name = f_lookupname(lookupprefix,"s",lookupid+lookupidoffset),
2304 steps = steps,
2305 nofsteps = nofsteps,
2306 type = lookuptype,
2307 markclass = markclass or nil,
2308 flags = flags,
2309
2310 order = order,
2311 features = features,
2312 }
2313 sequences[nofsequences] = l
2314 lookup.done = l
2315 else
2316 nofsublookups = nofsublookups + 1
2317
2318 local l = {
2319 index = nofsublookups,
2320 name = f_lookupname(lookupprefix,"l",lookupid+lookupidoffset),
2321 steps = steps,
2322 nofsteps = nofsteps,
2323 type = lookuptype,
2324 markclass = markclass or nil,
2325 flags = flags,
2326
2327 }
2328 sublookuplist[nofsublookups] = l
2329 sublookuphash[lookupid] = nofsublookups
2330 sublookupcheck[lookupid] = 0
2331 lookup.done = l
2332 end
2333 else
2334 report("no subtables for lookup %a",lookupid)
2335 end
2336 else
2337 report("no handler for lookup %a with type %a",lookupid,lookuptype)
2338 end
2339 end
2340
2341 if usedlookups then
2342 report("used %s lookups: % t",what,sortedkeys(usedlookups))
2343 end
2344
2345
2346
2347
2348
2349
2350
2351 local reported = { }
2352
2353 local function report_issue(i,what,step,kind)
2354
2355 report("rule %i in step %i of %s has %s lookups",i,step,what,kind)
2356
2357
2358 end
2359
2360
2361
2362
2363
2364
2365
2366 for s=1,#allsteps do
2367 local step = allsteps[s]
2368 local rules = step.rules
2369 if rules then
2370 for i=1,#rules do
2371 local rule = rules[i]
2372 local rlookups = rule.lookups
2373 if not rlookups then
2374 report_issue(i,what,s,"no")
2375 elseif not next(rlookups) then
2376
2377
2378 rule.lookups = nil
2379 else
2380
2381
2382 local length = #rlookups
2383 for index=1,length do
2384 local lookuplist = rlookups[index]
2385 if lookuplist then
2386 local length = #lookuplist
2387 local found = { }
2388 local noffound = 0
2389 for index=1,length do
2390 local lookupid = lookuplist[index]
2391 if lookupid then
2392 local h = sublookuphash[lookupid]
2393 if not h then
2394
2395
2396 local lookup = lookups[lookupid]
2397 if lookup then
2398 local d = lookup.done
2399 if d then
2400 nofsublookups = nofsublookups + 1
2401
2402 local l = {
2403 index = nofsublookups,
2404 name = f_lookupname(lookupprefix,"d",lookupid+lookupidoffset),
2405 derived = true,
2406 steps = d.steps,
2407 nofsteps = d.nofsteps,
2408 type = d.lookuptype or "gsub_single",
2409 markclass = d.markclass or nil,
2410 flags = d.flags,
2411
2412 }
2413 sublookuplist[nofsublookups] = copy(l)
2414 sublookuphash[lookupid] = nofsublookups
2415 sublookupcheck[lookupid] = 1
2416 h = nofsublookups
2417 else
2418 report_issue(i,what,s,"missing")
2419 rule.lookups = nil
2420 break
2421 end
2422 else
2423 report_issue(i,what,s,"bad")
2424 rule.lookups = nil
2425 break
2426 end
2427 else
2428 sublookupcheck[lookupid] = sublookupcheck[lookupid] + 1
2429 end
2430 if h then
2431 noffound = noffound + 1
2432 found[noffound] = h
2433 end
2434 end
2435 end
2436 rlookups[index] = noffound > 0 and found or false
2437 else
2438 rlookups[index] = false
2439 end
2440 end
2441 end
2442 end
2443 end
2444 end
2445
2446
2447 for i, n in sortedhash(sublookupcheck) do
2448 local l = lookups[i]
2449 local t = l.type
2450 if n == 0 and t ~= "extension" then
2451 local d = l.done
2452 report("%s lookup %s of type %a is not used",what,d and d.name or l.name,t)
2453 end
2454 end
2455
2456 end
2457
2458 local function loadvariations(f,fontdata,variationsoffset,lookuptypes,featurehash,featureorder)
2459 setposition(f,variationsoffset)
2460 local version = readulong(f)
2461 local nofrecords = readulong(f)
2462 local records = { }
2463 for i=1,nofrecords do
2464 records[i] = {
2465 conditions = readulong(f),
2466 substitutions = readulong(f),
2467 }
2468 end
2469 for i=1,nofrecords do
2470 local record = records[i]
2471 local offset = record.conditions
2472 if offset == 0 then
2473 record.condition = nil
2474 record.matchtype = "always"
2475 else
2476 local offset = variationsoffset+offset
2477 setposition(f,offset)
2478 local nofconditions = readushort(f)
2479 local conditions = { }
2480 for i=1,nofconditions do
2481 conditions[i] = offset + readulong(f)
2482 end
2483 record.conditions = conditions
2484 record.matchtype = "condition"
2485 end
2486 end
2487 for i=1,nofrecords do
2488 local record = records[i]
2489 if record.matchtype == "condition" then
2490 local conditions = record.conditions
2491 for i=1,#conditions do
2492 setposition(f,conditions[i])
2493 conditions[i] = {
2494 format = readushort(f),
2495 axis = readushort(f),
2496 minvalue = read2dot14(f),
2497 maxvalue = read2dot14(f),
2498 }
2499 end
2500 end
2501 end
2502
2503 for i=1,nofrecords do
2504 local record = records[i]
2505 local offset = record.substitutions
2506 if offset == 0 then
2507 record.substitutions = { }
2508 else
2509 setposition(f,variationsoffset + offset)
2510 local version = readulong(f)
2511 local nofsubstitutions = readushort(f)
2512 local substitutions = { }
2513 for i=1,nofsubstitutions do
2514 substitutions[readushort(f)] = readulong(f)
2515 end
2516 for index, alternates in sortedhash(substitutions) do
2517 if index == 0 then
2518 record.substitutions = false
2519 else
2520 local tableoffset = variationsoffset + offset + alternates
2521 setposition(f,tableoffset)
2522 local parameters = readulong(f)
2523 local noflookups = readushort(f)
2524 local lookups = readcardinaltable(f,noflookups,ushort)
2525
2526 record.substitutions = lookups
2527 end
2528 end
2529 end
2530 end
2531 setvariabledata(fontdata,"features",records)
2532 end
2533
2534 local function readscripts(f,fontdata,what,lookuptypes,lookuphandlers,lookupstoo)
2535 local tableoffset = gotodatatable(f,fontdata,what,true)
2536 if tableoffset then
2537 local version = readulong(f)
2538 local scriptoffset = tableoffset + readushort(f)
2539 local featureoffset = tableoffset + readushort(f)
2540 local lookupoffset = tableoffset + readushort(f)
2541
2542
2543 local variationsoffset = version > 0x00010000 and readulong(f) or 0
2544 if not scriptoffset then
2545 return
2546 end
2547 local scripts = readscriplan(f,fontdata,scriptoffset)
2548 local features = readfeatures(f,fontdata,featureoffset)
2549
2550 local scriptlangs, featurehash, featureorder = reorderfeatures(fontdata,scripts,features)
2551
2552 if fontdata.features then
2553 fontdata.features[what] = scriptlangs
2554 else
2555 fontdata.features = { [what] = scriptlangs }
2556 end
2557
2558 if not lookupstoo then
2559 return
2560 end
2561
2562 local nofmarkclasses = (fontdata.markclasses and #fontdata.markclasses or 0) - (fontdata.marksets and #fontdata.marksets or 0)
2563 local lookups = readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder,nofmarkclasses)
2564
2565 if lookups then
2566 resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset)
2567 end
2568
2569 if variationsoffset > 0 then
2570
2571 loadvariations(f,fontdata,tableoffset + variationsoffset,lookuptypes,featurehash,featureorder)
2572 end
2573 end
2574 end
2575
2576 local function checkkerns(f,fontdata,specification)
2577 local datatable = fontdata.tables.kern
2578 if not datatable then
2579 return
2580 end
2581 local features = fontdata.features
2582 local gposfeatures = features and features.gpos
2583 local name
2584 if not gposfeatures or not gposfeatures.kern then
2585 name = "kern"
2586 elseif specification.globalkerns then
2587 name = "globalkern"
2588 else
2589 report("ignoring global kern table, using gpos kern feature")
2590 return
2591 end
2592 setposition(f,datatable.offset)
2593 local version = readushort(f)
2594 local noftables = readushort(f)
2595 if noftables > 1 then
2596 report("adding global kern table as gpos feature %a",name)
2597 local kerns = setmetatableindex("table")
2598 for i=1,noftables do
2599 local version = readushort(f)
2600 local length = readushort(f)
2601 local coverage = readushort(f)
2602
2603 local format = rshift(coverage,8)
2604 if format == 0 then
2605 local nofpairs = readushort(f)
2606 local searchrange = readushort(f)
2607 local entryselector = readushort(f)
2608 local rangeshift = readushort(f)
2609 for i=1,nofpairs do
2610 kerns[readushort(f)][readushort(f)] = readfword(f)
2611 end
2612 elseif format == 2 then
2613
2614 else
2615
2616 end
2617 end
2618 local feature = { dflt = { dflt = true } }
2619 if not features then
2620 fontdata.features = { gpos = { [name] = feature } }
2621 elseif not gposfeatures then
2622 fontdata.features.gpos = { [name] = feature }
2623 else
2624 gposfeatures[name] = feature
2625 end
2626 local sequences = fontdata.sequences
2627 if not sequences then
2628 sequences = { }
2629 fontdata.sequences = sequences
2630 end
2631 local nofsequences = #sequences + 1
2632 sequences[nofsequences] = {
2633 index = nofsequences,
2634 name = name,
2635 steps = {
2636 {
2637 coverage = kerns,
2638 format = "kern",
2639 },
2640 },
2641 nofsteps = 1,
2642 type = "gpos_pair",
2643 flags = { false, false, false, false },
2644 order = { name },
2645 features = { [name] = feature },
2646 }
2647 else
2648 report("ignoring empty kern table of feature %a",name)
2649 end
2650 end
2651
2652 function readers.gsub(f,fontdata,specification)
2653 if specification.details then
2654 readscripts(f,fontdata,"gsub",gsubtypes,gsubhandlers,specification.lookups)
2655 end
2656 end
2657
2658 function readers.gpos(f,fontdata,specification)
2659 if specification.details then
2660 readscripts(f,fontdata,"gpos",gpostypes,gposhandlers,specification.lookups)
2661 if specification.lookups then
2662 checkkerns(f,fontdata,specification)
2663 end
2664 end
2665 end
2666
2667end
2668
2669function readers.gdef(f,fontdata,specification)
2670 if not specification.glyphs then
2671 return
2672 end
2673 local datatable = fontdata.tables.gdef
2674 if datatable then
2675 local tableoffset = datatable.offset
2676 setposition(f,tableoffset)
2677 local version = readulong(f)
2678 local classoffset = readushort(f)
2679 local attachmentoffset = readushort(f)
2680 local ligaturecarets = readushort(f)
2681 local markclassoffset = readushort(f)
2682 local marksetsoffset = version >= 0x00010002 and readushort(f) or 0
2683 local varsetsoffset = version >= 0x00010003 and readulong(f) or 0
2684 local glyphs = fontdata.glyphs
2685 local marks = { }
2686 local markclasses = setmetatableindex("table")
2687 local marksets = setmetatableindex("table")
2688 fontdata.marks = marks
2689 fontdata.markclasses = markclasses
2690 fontdata.marksets = marksets
2691
2692 if classoffset ~= 0 then
2693 setposition(f,tableoffset + classoffset)
2694 local classformat = readushort(f)
2695 if classformat == 1 then
2696 local firstindex = readushort(f)
2697 local lastindex = firstindex + readushort(f) - 1
2698 for index=firstindex,lastindex do
2699 local class = classes[readushort(f)]
2700 if class == "mark" then
2701 marks[index] = true
2702 end
2703 glyphs[index].class = class
2704 end
2705 elseif classformat == 2 then
2706 local nofranges = readushort(f)
2707 for i=1,nofranges do
2708 local firstindex = readushort(f)
2709 local lastindex = readushort(f)
2710 local class = classes[readushort(f)]
2711 if class then
2712 for index=firstindex,lastindex do
2713 glyphs[index].class = class
2714 if class == "mark" then
2715 marks[index] = true
2716 end
2717 end
2718 end
2719 end
2720 end
2721 end
2722
2723 if markclassoffset ~= 0 then
2724 setposition(f,tableoffset + markclassoffset)
2725 local classformat = readushort(f)
2726 if classformat == 1 then
2727 local firstindex = readushort(f)
2728 local lastindex = firstindex + readushort(f) - 1
2729 for index=firstindex,lastindex do
2730 markclasses[readushort(f)][index] = true
2731 end
2732 elseif classformat == 2 then
2733 local nofranges = readushort(f)
2734 for i=1,nofranges do
2735 local firstindex = readushort(f)
2736 local lastindex = readushort(f)
2737 local class = markclasses[readushort(f)]
2738 for index=firstindex,lastindex do
2739 class[index] = true
2740 end
2741 end
2742 end
2743 end
2744
2745 if marksetsoffset ~= 0 then
2746 local nofmarkclasses = fontdata.markclasses and #fontdata.markclasses or 0
2747 marksetsoffset = tableoffset + marksetsoffset
2748 setposition(f,marksetsoffset)
2749 local format = readushort(f)
2750 if format == 1 then
2751 local nofsets = readushort(f)
2752 local sets = readcardinaltable(f,nofsets,ulong)
2753 for i=1,nofsets do
2754 local offset = sets[i]
2755 if offset ~= 0 then
2756 markclasses[nofmarkclasses + i] = readcoverage(f,marksetsoffset+offset)
2757 marksets[i] = { }
2758 end
2759 end
2760 end
2761 end
2762
2763 local factors = specification.factors
2764
2765 if (specification.variable or factors) and varsetsoffset ~= 0 then
2766
2767 local regions, deltas = readvariationdata(f,tableoffset+varsetsoffset,factors)
2768
2769
2770
2771 if factors then
2772 fontdata.temporary.getdelta = function(outer,inner)
2773 local delta = deltas[outer+1]
2774 if delta then
2775 local d = delta.deltas[inner+1]
2776 if d then
2777 local scales = delta.scales
2778 local dd = 0
2779 for i=1,#scales do
2780 local di = d[i]
2781 if di then
2782 dd = dd + scales[i] * di
2783 else
2784 break
2785 end
2786 end
2787 return round(dd)
2788 end
2789 end
2790 return 0
2791 end
2792 end
2793
2794 end
2795 end
2796end
2797
2798
2799
2800
2801local function readmathvalue(f)
2802 local v = readshort(f)
2803 skipshort(f,1)
2804 return v
2805end
2806
2807local function readmathconstants(f,fontdata,offset)
2808 setposition(f,offset)
2809 fontdata.mathconstants = {
2810 ScriptPercentScaleDown = readshort(f),
2811 ScriptScriptPercentScaleDown = readshort(f),
2812 DelimitedSubFormulaMinHeight = readushort(f),
2813 DisplayOperatorMinHeight = readushort(f),
2814 MathLeading = readmathvalue(f),
2815 AxisHeight = readmathvalue(f),
2816 AccentBaseHeight = readmathvalue(f),
2817 FlattenedAccentBaseHeight = readmathvalue(f),
2818 SubscriptShiftDown = readmathvalue(f),
2819 SubscriptTopMax = readmathvalue(f),
2820 SubscriptBaselineDropMin = readmathvalue(f),
2821 SuperscriptShiftUp = readmathvalue(f),
2822 SuperscriptShiftUpCramped = readmathvalue(f),
2823 SuperscriptBottomMin = readmathvalue(f),
2824 SuperscriptBaselineDropMax = readmathvalue(f),
2825 SubSuperscriptGapMin = readmathvalue(f),
2826 SuperscriptBottomMaxWithSubscript = readmathvalue(f),
2827 SpaceAfterScript = readmathvalue(f),
2828 UpperLimitGapMin = readmathvalue(f),
2829 UpperLimitBaselineRiseMin = readmathvalue(f),
2830 LowerLimitGapMin = readmathvalue(f),
2831 LowerLimitBaselineDropMin = readmathvalue(f),
2832 StackTopShiftUp = readmathvalue(f),
2833 StackTopDisplayStyleShiftUp = readmathvalue(f),
2834 StackBottomShiftDown = readmathvalue(f),
2835 StackBottomDisplayStyleShiftDown = readmathvalue(f),
2836 StackGapMin = readmathvalue(f),
2837 StackDisplayStyleGapMin = readmathvalue(f),
2838 StretchStackTopShiftUp = readmathvalue(f),
2839 StretchStackBottomShiftDown = readmathvalue(f),
2840 StretchStackGapAboveMin = readmathvalue(f),
2841 StretchStackGapBelowMin = readmathvalue(f),
2842 FractionNumeratorShiftUp = readmathvalue(f),
2843 FractionNumeratorDisplayStyleShiftUp = readmathvalue(f),
2844 FractionDenominatorShiftDown = readmathvalue(f),
2845 FractionDenominatorDisplayStyleShiftDown = readmathvalue(f),
2846 FractionNumeratorGapMin = readmathvalue(f),
2847 FractionNumeratorDisplayStyleGapMin = readmathvalue(f),
2848 FractionRuleThickness = readmathvalue(f),
2849 FractionDenominatorGapMin = readmathvalue(f),
2850 FractionDenominatorDisplayStyleGapMin = readmathvalue(f),
2851 SkewedFractionHorizontalGap = readmathvalue(f),
2852 SkewedFractionVerticalGap = readmathvalue(f),
2853 OverbarVerticalGap = readmathvalue(f),
2854 OverbarRuleThickness = readmathvalue(f),
2855 OverbarExtraAscender = readmathvalue(f),
2856 UnderbarVerticalGap = readmathvalue(f),
2857 UnderbarRuleThickness = readmathvalue(f),
2858 UnderbarExtraDescender = readmathvalue(f),
2859 RadicalVerticalGap = readmathvalue(f),
2860 RadicalDisplayStyleVerticalGap = readmathvalue(f),
2861 RadicalRuleThickness = readmathvalue(f),
2862 RadicalExtraAscender = readmathvalue(f),
2863 RadicalKernBeforeDegree = readmathvalue(f),
2864 RadicalKernAfterDegree = readmathvalue(f),
2865 RadicalDegreeBottomRaisePercent = readshort(f),
2866 }
2867end
2868
2869local function readmathglyphinfo(f,fontdata,offset)
2870 setposition(f,offset)
2871 local italics = readushort(f)
2872 local accents = readushort(f)
2873 local extensions = readushort(f)
2874 local kerns = readushort(f)
2875 local glyphs = fontdata.glyphs
2876 if italics ~= 0 then
2877 setposition(f,offset+italics)
2878 local coverage = readushort(f)
2879 local nofglyphs = readushort(f)
2880 coverage = readcoverage(f,offset+italics+coverage,true)
2881 setposition(f,offset+italics+4)
2882 for i=1,nofglyphs do
2883 local italic = readmathvalue(f)
2884 if italic ~= 0 then
2885 local glyph = glyphs[coverage[i]]
2886 local math = glyph.math
2887 if not math then
2888 glyph.math = { italic = italic }
2889 else
2890 math.italic = italic
2891 end
2892 end
2893 end
2894 fontdata.hasitalics = true
2895 end
2896 if accents ~= 0 then
2897 setposition(f,offset+accents)
2898 local coverage = readushort(f)
2899 local nofglyphs = readushort(f)
2900 coverage = readcoverage(f,offset+accents+coverage,true)
2901 setposition(f,offset+accents+4)
2902 for i=1,nofglyphs do
2903 local accent = readmathvalue(f)
2904 if accent ~= 0 then
2905 local glyph = glyphs[coverage[i]]
2906 local math = glyph.math
2907 if not math then
2908 glyph.math = { accent = accent }
2909 else
2910 math.accent = accent
2911 end
2912 end
2913 end
2914 end
2915 if extensions ~= 0 then
2916 setposition(f,offset+extensions)
2917 end
2918 if kerns ~= 0 then
2919 local kernoffset = offset + kerns
2920 setposition(f,kernoffset)
2921 local coverage = readushort(f)
2922 local nofglyphs = readushort(f)
2923 if nofglyphs > 0 then
2924 local function get(offset)
2925 setposition(f,kernoffset+offset)
2926 local n = readushort(f)
2927 if n == 0 then
2928 local k = readmathvalue(f)
2929 if k == 0 then
2930
2931 else
2932 return { { kern = k } }
2933 end
2934 else
2935 local l = { }
2936 for i=1,n do
2937 l[i] = { height = readmathvalue(f) }
2938 end
2939 for i=1,n do
2940 l[i].kern = readmathvalue(f)
2941 end
2942 l[n+1] = { kern = readmathvalue(f) }
2943 return l
2944 end
2945 end
2946 local kernsets = { }
2947 for i=1,nofglyphs do
2948 local topright = readushort(f)
2949 local topleft = readushort(f)
2950 local bottomright = readushort(f)
2951 local bottomleft = readushort(f)
2952 kernsets[i] = {
2953 topright = topright ~= 0 and topright or nil,
2954 topleft = topleft ~= 0 and topleft or nil,
2955 bottomright = bottomright ~= 0 and bottomright or nil,
2956 bottomleft = bottomleft ~= 0 and bottomleft or nil,
2957 }
2958 end
2959 coverage = readcoverage(f,kernoffset+coverage,true)
2960 for i=1,nofglyphs do
2961 local kernset = kernsets[i]
2962 if next(kernset) then
2963 local k = kernset.topright if k then kernset.topright = get(k) end
2964 local k = kernset.topleft if k then kernset.topleft = get(k) end
2965 local k = kernset.bottomright if k then kernset.bottomright = get(k) end
2966 local k = kernset.bottomleft if k then kernset.bottomleft = get(k) end
2967 if next(kernset) then
2968 local glyph = glyphs[coverage[i]]
2969 local math = glyph.math
2970 if math then
2971 math.kerns = kernset
2972 else
2973 glyph.math = { kerns = kernset }
2974 end
2975 end
2976 end
2977 end
2978 end
2979 end
2980end
2981
2982local function readmathvariants(f,fontdata,offset)
2983 setposition(f,offset)
2984 local glyphs = fontdata.glyphs
2985 local minoverlap = readushort(f)
2986 local vcoverage = readushort(f)
2987 local hcoverage = readushort(f)
2988 local vnofglyphs = readushort(f)
2989 local hnofglyphs = readushort(f)
2990 local vconstruction = readcardinaltable(f,vnofglyphs,ushort)
2991 local hconstruction = readcardinaltable(f,hnofglyphs,ushort)
2992
2993 fontdata.mathconstants.MinConnectorOverlap = minoverlap
2994
2995
2996
2997
2998
2999
3000 local function get(offset,coverage,nofglyphs,construction,kvariants,kparts,kitalic,korientation,orientation)
3001 if coverage ~= 0 and nofglyphs > 0 then
3002 local coverage = readcoverage(f,offset+coverage,true)
3003 for i=1,nofglyphs do
3004 local c = construction[i]
3005 if c ~= 0 then
3006 local index = coverage[i]
3007 local glyph = glyphs[index]
3008 local math = glyph.math
3009 setposition(f,offset+c)
3010 local assembly = readushort(f)
3011 local nofvariants = readushort(f)
3012 if nofvariants > 0 then
3013 local variants, v = nil, 0
3014 for i=1,nofvariants do
3015 local variant = readushort(f)
3016 if variant == index then
3017
3018 elseif variants then
3019 v = v + 1
3020 variants[v] = variant
3021 else
3022 v = 1
3023 variants = { variant }
3024 end
3025 skipshort(f)
3026 end
3027 if not variants then
3028
3029 elseif not math then
3030 math = { [kvariants] = variants }
3031 glyph.math = math
3032 else
3033 math[kvariants] = variants
3034 end
3035 end
3036 if assembly ~= 0 then
3037 setposition(f,offset + c + assembly)
3038 local italic = readmathvalue(f)
3039 local nofparts = readushort(f)
3040 local parts = { }
3041 for i=1,nofparts do
3042 local p = {
3043 glyph = readushort(f),
3044 start = readushort(f),
3045 ["end"] = readushort(f),
3046 advance = readushort(f),
3047 }
3048 local flags = readushort(f)
3049 if band(flags,0x0001) ~= 0 then
3050 p.extender = 1
3051 end
3052 parts[i] = p
3053 end
3054 if not math then
3055 math = {
3056 [kparts] = parts
3057 }
3058 glyph.math = math
3059 else
3060 math[kparts] = parts
3061 end
3062 if italic and italic ~= 0 then
3063 math[kitalic] = italic
3064 end
3065 if orientation then
3066 math[korientation] = orientation
3067 end
3068 end
3069 end
3070 end
3071 end
3072 end
3073
3074
3075 if CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 then
3076 get(offset,hcoverage,hnofglyphs,hconstruction,"variants","parts","partsitalic","partsorientation","horizontal")
3077 get(offset,vcoverage,vnofglyphs,vconstruction,"variants","parts","partsitalic","partsorientation","vertical")
3078 else
3079 get(offset,vcoverage,vnofglyphs,vconstruction,"vvariants","vparts","vitalic")
3080 get(offset,hcoverage,hnofglyphs,hconstruction,"hvariants","hparts","hitalic")
3081 end
3082end
3083
3084function readers.math(f,fontdata,specification)
3085 local tableoffset = gotodatatable(f,fontdata,"math",specification.glyphs)
3086 if tableoffset then
3087 local version = readulong(f)
3088
3089
3090
3091
3092 local constants = readushort(f)
3093 local glyphinfo = readushort(f)
3094 local variants = readushort(f)
3095 if constants == 0 then
3096 report("the math table of %a has no constants",fontdata.filename)
3097 else
3098 readmathconstants(f,fontdata,tableoffset+constants)
3099 end
3100 if glyphinfo ~= 0 then
3101 readmathglyphinfo(f,fontdata,tableoffset+glyphinfo)
3102 end
3103 if variants ~= 0 then
3104 readmathvariants(f,fontdata,tableoffset+variants)
3105 end
3106 end
3107end
3108
3109function readers.colr(f,fontdata,specification)
3110 local tableoffset = gotodatatable(f,fontdata,"colr",specification.glyphs)
3111 if tableoffset then
3112 local version = readushort(f)
3113 if version == 0 or version == 1 then
3114 report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"colr",fontdata.filename)
3115 return
3116 else
3117
3118 end
3119 if not fontdata.tables.cpal then
3120 report("color table %a in font %a has no mandate %a table","colr",fontdata.filename,"cpal")
3121 fontdata.colorpalettes = { }
3122 end
3123 local glyphs = fontdata.glyphs
3124 local nofglyphs = readushort(f)
3125 local baseoffset = readulong(f)
3126 local layeroffset = readulong(f)
3127 local noflayers = readushort(f)
3128 local layerrecords = { }
3129 local maxclass = 0
3130
3131
3132
3133 setposition(f,tableoffset + layeroffset)
3134 for i=1,noflayers do
3135 local slot = readushort(f)
3136 local class = readushort(f)
3137 if class < 0xFFFF then
3138 class = class + 1
3139 if class > maxclass then
3140 maxclass = class
3141 end
3142 end
3143 layerrecords[i] = {
3144 slot = slot,
3145 class = class,
3146 }
3147 end
3148 fontdata.maxcolorclass = maxclass
3149 setposition(f,tableoffset + baseoffset)
3150 for i=0,nofglyphs-1 do
3151 local glyphindex = readushort(f)
3152 local firstlayer = readushort(f)
3153 local noflayers = readushort(f)
3154 local t = { }
3155 for i=1,noflayers do
3156 t[i] = layerrecords[firstlayer+i]
3157 end
3158 glyphs[glyphindex].colors = t
3159 end
3160 end
3161 fontdata.hascolor = true
3162end
3163
3164function readers.cpal(f,fontdata,specification)
3165 local tableoffset = gotodatatable(f,fontdata,"cpal",specification.glyphs)
3166 if tableoffset then
3167 local version = readushort(f)
3168
3169
3170
3171
3172 local nofpaletteentries = readushort(f)
3173 local nofpalettes = readushort(f)
3174 local nofcolorrecords = readushort(f)
3175 local firstcoloroffset = readulong(f)
3176 local colorrecords = { }
3177 local palettes = readcardinaltable(f,nofpalettes,ushort)
3178 if version == 1 then
3179
3180 local palettettypesoffset = readulong(f)
3181 local palettelabelsoffset = readulong(f)
3182 local paletteentryoffset = readulong(f)
3183 end
3184 setposition(f,tableoffset+firstcoloroffset)
3185 for i=1,nofcolorrecords do
3186 local b, g, r, a = readbytes(f,4)
3187 colorrecords[i] = {
3188 r, g, b, a ~= 255 and a or nil,
3189 }
3190 end
3191 for i=1,nofpalettes do
3192 local p = { }
3193 local o = palettes[i]
3194 for j=1,nofpaletteentries do
3195 p[j] = colorrecords[o+j]
3196 end
3197 palettes[i] = p
3198 end
3199 fontdata.colorpalettes = palettes
3200 end
3201end
3202
3203local compress = gzip and gzip.compress
3204local compressed = compress and gzip.compressed
3205
3206
3207
3208
3209
3210
3211function readers.svg(f,fontdata,specification)
3212 local tableoffset = gotodatatable(f,fontdata,"svg",specification.glyphs)
3213 if tableoffset then
3214 local version = readushort(f)
3215
3216
3217
3218
3219 local glyphs = fontdata.glyphs
3220 local indexoffset = tableoffset + readulong(f)
3221 local reserved = readulong(f)
3222 setposition(f,indexoffset)
3223 local nofentries = readushort(f)
3224 local entries = { }
3225 for i=1,nofentries do
3226 entries[i] = {
3227 first = readushort(f),
3228 last = readushort(f),
3229 offset = indexoffset + readulong(f),
3230 length = readulong(f),
3231 }
3232 end
3233 for i=1,nofentries do
3234 local entry = entries[i]
3235 setposition(f,entry.offset)
3236 local data = readstring(f,entry.length)
3237 if compressed and not compressed(data) then
3238 data = compress(data)
3239 end
3240 entries[i] = {
3241 first = entry.first,
3242 last = entry.last,
3243 data = data
3244 }
3245 end
3246 fontdata.svgshapes = entries
3247 end
3248 fontdata.hascolor = true
3249end
3250
3251function readers.sbix(f,fontdata,specification)
3252 local tableoffset = gotodatatable(f,fontdata,"sbix",specification.glyphs)
3253 if tableoffset then
3254 local version = readushort(f)
3255 local flags = readushort(f)
3256 local nofstrikes = readulong(f)
3257 local strikes = { }
3258 local nofglyphs = fontdata.nofglyphs
3259 for i=1,nofstrikes do
3260 strikes[i] = readulong(f)
3261 end
3262 local shapes = { }
3263 local done = 0
3264 for i=1,nofstrikes do
3265 local strikeoffset = strikes[i] + tableoffset
3266 setposition(f,strikeoffset)
3267 strikes[i] = {
3268 ppem = readushort(f),
3269 ppi = readushort(f),
3270 offset = strikeoffset
3271 }
3272 end
3273
3274 sort(strikes,function(a,b)
3275 if b.ppem == a.ppem then
3276 return b.ppi < a.ppi
3277 else
3278 return b.ppem < a.ppem
3279 end
3280 end)
3281 local glyphs = { }
3282 local delayed = CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 or fonts.handlers.typethree
3283 for i=1,nofstrikes do
3284 local strike = strikes[i]
3285 local strikeppem = strike.ppem
3286 local strikeppi = strike.ppi
3287 local strikeoffset = strike.offset
3288 setposition(f,strikeoffset)
3289 for i=0,nofglyphs do
3290 glyphs[i] = readulong(f)
3291 end
3292 local glyphoffset = glyphs[0]
3293 for i=0,nofglyphs-1 do
3294 local nextoffset = glyphs[i+1]
3295 if not shapes[i] then
3296 local datasize = nextoffset - glyphoffset
3297 if datasize > 0 then
3298 setposition(f,strikeoffset + glyphoffset)
3299 local x = readshort(f)
3300 local y = readshort(f)
3301 local tag = readtag(f)
3302 local size = datasize - 8
3303 local data = nil
3304 local offset = nil
3305 if delayed then
3306 offset = getposition(f)
3307 data = nil
3308 else
3309 data = readstring(f,size)
3310 size = nil
3311 end
3312 shapes[i] = {
3313 x = x,
3314 y = y,
3315 o = offset,
3316 s = size,
3317 data = data,
3318
3319
3320
3321 }
3322 done = done + 1
3323 if done == nofglyphs then
3324 break
3325 end
3326 end
3327 end
3328 glyphoffset = nextoffset
3329 end
3330 end
3331 fontdata.pngshapes = shapes
3332 end
3333end
3334
3335
3336
3337
3338do
3339
3340 local function getmetrics(f)
3341 return {
3342 ascender = readinteger(f),
3343 descender = readinteger(f),
3344 widthmax = readuinteger(f),
3345 caretslopedumerator = readinteger(f),
3346 caretslopedenominator = readinteger(f),
3347 caretoffset = readinteger(f),
3348 minorigin = readinteger(f),
3349 minadvance = readinteger(f),
3350 maxbefore = readinteger(f),
3351 minafter = readinteger(f),
3352 pad1 = readinteger(f),
3353 pad2 = readinteger(f),
3354 }
3355 end
3356
3357
3358
3359 local function getbigmetrics(f)
3360
3361 return {
3362 height = readuinteger(f),
3363 width = readuinteger(f),
3364 horiBearingX = readinteger(f),
3365 horiBearingY = readinteger(f),
3366 horiAdvance = readuinteger(f),
3367 vertBearingX = readinteger(f),
3368 vertBearingY = readinteger(f),
3369 vertAdvance = readuinteger(f),
3370 }
3371 end
3372
3373 local function getsmallmetrics(f)
3374
3375 return {
3376 height = readuinteger(f),
3377 width = readuinteger(f),
3378 bearingX = readinteger(f),
3379 bearingY = readinteger(f),
3380 advance = readuinteger(f),
3381 }
3382 end
3383
3384 function readers.cblc(f,fontdata,specification)
3385
3386 local ctdttableoffset = gotodatatable(f,fontdata,"cbdt",specification.glyphs)
3387 if not ctdttableoffset then
3388 return
3389 end
3390 local cblctableoffset = gotodatatable(f,fontdata,"cblc",specification.glyphs)
3391 if cblctableoffset then
3392 local majorversion = readushort(f)
3393 local minorversion = readushort(f)
3394 local nofsizetables = readulong(f)
3395 local sizetables = { }
3396 local shapes = { }
3397 local subtables = { }
3398 for i=1,nofsizetables do
3399 sizetables[i] = {
3400 subtables = readulong(f),
3401 indexsize = readulong(f),
3402 nofsubtables = readulong(f),
3403 colorref = readulong(f),
3404 hormetrics = getmetrics(f),
3405 vermetrics = getmetrics(f),
3406 firstindex = readushort(f),
3407 lastindex = readushort(f),
3408 ppemx = readbyte(f),
3409 ppemy = readbyte(f),
3410 bitdepth = readbyte(f),
3411 flags = readbyte(f),
3412 }
3413 end
3414 sort(sizetables,function(a,b)
3415 if b.ppemx == a.ppemx then
3416 return b.bitdepth < a.bitdepth
3417 else
3418 return b.ppemx < a.ppemx
3419 end
3420 end)
3421 for i=1,nofsizetables do
3422 local s = sizetables[i]
3423 local d = false
3424 for j=s.firstindex,s.lastindex do
3425 if not shapes[j] then
3426 shapes[j] = i
3427 d = true
3428 end
3429 end
3430 if d then
3431 s.used = true
3432 end
3433 end
3434 for i=1,nofsizetables do
3435 local s = sizetables[i]
3436 if s.used then
3437 local offset = s.subtables
3438 setposition(f,cblctableoffset+offset)
3439 for j=1,s.nofsubtables do
3440 local firstindex = readushort(f)
3441 local lastindex = readushort(f)
3442 local tableoffset = readulong(f) + offset
3443 for k=firstindex,lastindex do
3444 if shapes[k] == i then
3445 local s = subtables[tableoffset]
3446 if not s then
3447 s = {
3448 firstindex = firstindex,
3449 lastindex = lastindex,
3450 }
3451 subtables[tableoffset] = s
3452 end
3453 shapes[k] = s
3454 end
3455 end
3456 end
3457 end
3458 end
3459
3460
3461
3462
3463 for offset, subtable in sortedhash(subtables) do
3464 local tabletype = readushort(f)
3465 subtable.format = readushort(f)
3466 local baseoffset = readulong(f) + ctdttableoffset
3467 local offsets = { }
3468 local metrics = nil
3469 if tabletype == 1 then
3470
3471 for i=subtable.firstindex,subtable.lastindex do
3472 offsets[i] = readulong(f) + baseoffset
3473 end
3474 skipbytes(f,4)
3475 elseif tabletype == 2 then
3476 local size = readulong(f)
3477 local done = baseoffset
3478 metrics = getbigmetrics(f)
3479 for i=subtable.firstindex,subtable.lastindex do
3480 offsets[i] = done
3481 done = done + size
3482 end
3483 elseif tabletype == 3 then
3484
3485 local n = subtable.lastindex - subtable.firstindex + 2
3486 for i=subtable.firstindex,subtable.lastindex do
3487 offsets[i] = readushort(f) + baseoffset
3488 end
3489 if math.odd(n) then
3490 skipbytes(f,4)
3491 else
3492 skipbytes(f,2)
3493 end
3494 elseif tabletype == 4 then
3495 for i=1,readulong(f) do
3496 offsets[readushort(f)] = readushort(f) + baseoffset
3497 end
3498 elseif tabletype == 5 then
3499 local size = readulong(f)
3500 local done = baseoffset
3501 metrics = getbigmetrics(f)
3502 local n = readulong(f)
3503 for i=1,n do
3504 offsets[readushort(f)] = done
3505 done = done + size
3506 end
3507 if math.odd(n) then
3508 skipbytes(f,2)
3509 end
3510 else
3511 return
3512 end
3513 subtable.offsets = offsets
3514 subtable.metrics = metrics
3515 end
3516
3517
3518
3519
3520 local default = { width = 0, height = 0 }
3521 local glyphs = fontdata.glyphs
3522 local delayed = CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 or fonts.handlers.typethree
3523
3524 for index, subtable in sortedhash(shapes) do
3525 if type(subtable) == "table" then
3526 local data = nil
3527 local size = nil
3528 local metrics = default
3529 local format = subtable.format
3530 local offset = subtable.offsets[index]
3531 setposition(f,offset)
3532 if format == 17 then
3533 metrics = getsmallmetrics(f)
3534 size = true
3535 elseif format == 18 then
3536 metrics = getbigmetrics(f)
3537 size = true
3538 elseif format == 19 then
3539 metrics = subtable.metrics
3540 size = true
3541 else
3542
3543 end
3544 if size then
3545 size = readulong(f)
3546 if delayed then
3547 offset = getposition(f)
3548 data = nil
3549 else
3550 offset = nil
3551 data = readstring(f,size)
3552 size = nil
3553 end
3554 else
3555 offset = nil
3556 end
3557 local x = metrics.width
3558 local y = metrics.height
3559 shapes[index] = {
3560 x = x,
3561 y = y,
3562 o = offset,
3563 s = size,
3564 data = data,
3565 }
3566
3567
3568 local glyph = glyphs[index]
3569 if not glyph.boundingbox then
3570 local width = glyph.width
3571 local height = width * y/x
3572 glyph.boundingbox = { 0, 0, width, height }
3573 end
3574 else
3575 shapes[index] = {
3576 x = 0,
3577 y = 0,
3578 data = "",
3579 }
3580 end
3581 end
3582
3583 fontdata.pngshapes = shapes
3584 end
3585 end
3586
3587 function readers.cbdt(f,fontdata,specification)
3588
3589
3590
3591
3592
3593 end
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610end
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629function readers.stat(f,fontdata,specification)
3630 local tableoffset = gotodatatable(f,fontdata,"stat",true)
3631 if tableoffset then
3632 local extras = fontdata.extras
3633 local version = readulong(f)
3634 local axissize = readushort(f)
3635 local nofaxis = readushort(f)
3636 local axisoffset = readulong(f)
3637 local nofvalues = readushort(f)
3638 local valuesoffset = readulong(f)
3639 local fallbackname = extras[readushort(f)]
3640 local axis = { }
3641 local values = { }
3642 setposition(f,tableoffset+axisoffset)
3643 for i=1,nofaxis do
3644 local tag = readtag(f)
3645 axis[i] = {
3646 tag = tag,
3647 name = lower(extras[readushort(f)] or tag),
3648 ordering = readushort(f),
3649 variants = { }
3650 }
3651 end
3652
3653
3654
3655
3656
3657
3658 setposition(f,tableoffset+valuesoffset)
3659 for i=1,nofvalues do
3660 values[i] = readushort(f)
3661 end
3662 for i=1,nofvalues do
3663 setposition(f,tableoffset + valuesoffset + values[i])
3664 local format = readushort(f)
3665 local index = readushort(f) + 1
3666 local flags = readushort(f)
3667 local name = lower(extras[readushort(f)] or "no name")
3668 local value = readfixed(f)
3669 local variant
3670 if format == 1 then
3671 variant = {
3672 flags = flags,
3673 name = name,
3674 value = value,
3675 }
3676 elseif format == 2 then
3677 variant = {
3678 flags = flags,
3679 name = name,
3680 value = value,
3681 minimum = readfixed(f),
3682 maximum = readfixed(f),
3683 }
3684 elseif format == 3 then
3685 variant = {
3686 flags = flags,
3687 name = name,
3688 value = value,
3689 link = readfixed(f),
3690 }
3691 end
3692 insert(axis[index].variants,variant)
3693 end
3694 sort(axis,function(a,b)
3695 return a.ordering < b.ordering
3696 end)
3697 for i=1,#axis do
3698 local a = axis[i]
3699 sort(a.variants,function(a,b)
3700 return a.name < b.name
3701 end)
3702 a.ordering = nil
3703 end
3704 setvariabledata(fontdata,"designaxis",axis)
3705 setvariabledata(fontdata,"fallbackname",fallbackname)
3706 end
3707end
3708
3709
3710
3711
3712
3713function readers.avar(f,fontdata,specification)
3714 local tableoffset = gotodatatable(f,fontdata,"avar",true)
3715 if tableoffset then
3716
3717 local function collect()
3718 local nofvalues = readushort(f)
3719 local values = { }
3720 local lastfrom = false
3721 local lastto = false
3722 for i=1,nofvalues do
3723 local from = read2dot14(f)
3724 local to = read2dot14(f)
3725 if lastfrom and from <= lastfrom then
3726
3727 elseif lastto and to >= lastto then
3728
3729 else
3730 values[#values+1] = { from, to }
3731 lastfrom, lastto = from, to
3732 end
3733 end
3734 nofvalues = #values
3735 if nofvalues > 2 then
3736 local some = values[1]
3737 if some[1] == -1 and some[2] == -1 then
3738 some = values[nofvalues]
3739 if some[1] == 1 and some[2] == 1 then
3740 for i=2,nofvalues-1 do
3741 some = values[i]
3742 if some[1] == 0 and some[2] == 0 then
3743 return values
3744 end
3745 end
3746 end
3747 end
3748 end
3749 return false
3750 end
3751
3752 local version = readulong(f)
3753 local reserved = readushort(f)
3754 local nofaxis = readushort(f)
3755 local segments = { }
3756 for i=1,nofaxis do
3757 segments[i] = collect()
3758 end
3759 setvariabledata(fontdata,"segments",segments)
3760 end
3761end
3762
3763function readers.fvar(f,fontdata,specification)
3764 local tableoffset = gotodatatable(f,fontdata,"fvar",true)
3765 if tableoffset then
3766 local version = readulong(f)
3767 local offsettoaxis = tableoffset + readushort(f)
3768 local reserved = skipshort(f)
3769
3770 local nofaxis = readushort(f)
3771 local sizeofaxis = readushort(f)
3772
3773 local nofinstances = readushort(f)
3774 local sizeofinstances = readushort(f)
3775
3776 local extras = fontdata.extras
3777 local axis = { }
3778 local instances = { }
3779
3780 setposition(f,offsettoaxis)
3781
3782 for i=1,nofaxis do
3783 axis[i] = {
3784 tag = readtag(f),
3785 minimum = readfixed(f),
3786 default = readfixed(f),
3787 maximum = readfixed(f),
3788 flags = readushort(f),
3789 name = lower(extras[readushort(f)] or "bad name"),
3790 }
3791 local n = sizeofaxis - 20
3792 if n > 0 then
3793 skipbytes(f,n)
3794 elseif n < 0 then
3795
3796 end
3797 end
3798
3799 local nofbytes = 2 + 2 + 2 + nofaxis * 4
3800 local readpsname = nofbytes <= sizeofinstances
3801 local skippable = sizeofinstances - nofbytes
3802 for i=1,nofinstances do
3803 local subfamid = readushort(f)
3804 local flags = readushort(f)
3805 local values = { }
3806 for i=1,nofaxis do
3807 values[i] = {
3808 axis = axis[i].tag,
3809 value = readfixed(f),
3810 }
3811 end
3812 local psnameid = readpsname and readushort(f) or 0xFFFF
3813 if subfamid == 2 or subfamid == 17 then
3814
3815 elseif subfamid == 0xFFFF then
3816 subfamid = nil
3817 elseif subfamid <= 256 or subfamid >= 32768 then
3818 subfamid = nil
3819 end
3820 if psnameid == 6 then
3821
3822 elseif psnameid == 0xFFFF then
3823 psnameid = nil
3824 elseif psnameid <= 256 or psnameid >= 32768 then
3825 psnameid = nil
3826 end
3827 instances[i] = {
3828
3829 subfamily = extras[subfamid],
3830 psname = psnameid and extras[psnameid] or nil,
3831 values = values,
3832 }
3833 if skippable > 0 then
3834 skipbytes(f,skippable)
3835 end
3836 end
3837 setvariabledata(fontdata,"axis",axis)
3838 setvariabledata(fontdata,"instances",instances)
3839 end
3840end
3841
3842function readers.hvar(f,fontdata,specification)
3843 local factors = specification.factors
3844 if not factors then
3845 return
3846 end
3847 local tableoffset = gotodatatable(f,fontdata,"hvar",specification.variable)
3848 if not tableoffset then
3849
3850 return
3851 end
3852
3853 local version = readulong(f)
3854 local variationoffset = tableoffset + readulong(f)
3855 local advanceoffset = tableoffset + readulong(f)
3856 local lsboffset = tableoffset + readulong(f)
3857 local rsboffset = tableoffset + readulong(f)
3858
3859 local regions = { }
3860 local variations = { }
3861 local innerindex = { }
3862 local outerindex = { }
3863 local deltas = { }
3864
3865 if variationoffset > 0 then
3866 regions, deltas = readvariationdata(f,variationoffset,factors)
3867 end
3868 if not regions then
3869
3870 return
3871 end
3872
3873 if advanceoffset > 0 then
3874
3875
3876
3877
3878
3879
3880
3881
3882 setposition(f,advanceoffset)
3883 local format = readushort(f)
3884 local mapcount = readushort(f)
3885 local entrysize = rshift(band(format,0x0030),4) + 1
3886 local nofinnerbits = band(format,0x000F) + 1
3887 local innermask = lshift(1,nofinnerbits) - 1
3888 local readcardinal = read_cardinal[entrysize]
3889 for i=0,mapcount-1 do
3890 local mapdata = readcardinal(f)
3891 outerindex[i] = rshift(mapdata,nofinnerbits)
3892 innerindex[i] = band(mapdata,innermask)
3893 end
3894
3895 setvariabledata(fontdata,"hvarwidths",true)
3896 local glyphs = fontdata.glyphs
3897 for i=0,fontdata.nofglyphs-1 do
3898 local glyph = glyphs[i]
3899 local width = glyph.width
3900 if width then
3901 local outer = outerindex[i] or 0
3902 local inner = innerindex[i] or i
3903 if outer and inner then
3904 local delta = deltas[outer+1]
3905 if delta then
3906 local d = delta.deltas[inner+1]
3907 if d then
3908 local scales = delta.scales
3909 local deltaw = 0
3910 for i=1,#scales do
3911 local di = d[i]
3912 if di then
3913 deltaw = deltaw + scales[i] * di
3914 else
3915 break
3916 end
3917 end
3918
3919
3920 glyph.width = width + round(deltaw)
3921 end
3922 end
3923 end
3924 end
3925 end
3926
3927 end
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939end
3940
3941function readers.vvar(f,fontdata,specification)
3942 if not specification.variable then
3943 return
3944 end
3945end
3946
3947function readers.mvar(f,fontdata,specification)
3948 local tableoffset = gotodatatable(f,fontdata,"mvar",specification.variable)
3949 if tableoffset then
3950 local version = readulong(f)
3951 local reserved = skipshort(f,1)
3952 local recordsize = readushort(f)
3953 local nofrecords = readushort(f)
3954 local offsettostore = tableoffset + readushort(f)
3955 local dimensions = { }
3956 local factors = specification.factors
3957 if factors then
3958 local regions, deltas = readvariationdata(f,offsettostore,factors)
3959 for i=1,nofrecords do
3960 local tag = readtag(f)
3961 local var = variabletags[tag]
3962 if var then
3963 local outer = readushort(f)
3964 local inner = readushort(f)
3965 local delta = deltas[outer+1]
3966 if delta then
3967 local d = delta.deltas[inner+1]
3968 if d then
3969 local scales = delta.scales
3970 local dd = 0
3971 for i=1,#scales do
3972 dd = dd + scales[i] * d[i]
3973 end
3974 var(fontdata,round(dd))
3975 end
3976 end
3977 else
3978 skipshort(f,2)
3979 end
3980 if recordsize > 8 then
3981 skipbytes(recordsize-8)
3982 end
3983 end
3984 end
3985
3986 end
3987end
3988 |