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
1187 local before = readarray(f)
1188 local current = readarray(f)
1189 local after = readarray(f)
1190 local noflookups = readushort(f)
1191 local lookups = current and readlookuparray(f,noflookups,#current)
1192 if lookups then
1193 before = readcoveragearray(f,tableoffset,before,true)
1194 current = readcoveragearray(f,tableoffset,current,true)
1195 after = readcoveragearray(f,tableoffset,after,true)
1196 return {
1197 format = "coverage",
1198 rules = {
1199 {
1200 before = before,
1201 current = current,
1202 after = after,
1203 lookups = lookups,
1204 }
1205 }
1206 }
1207 else
1208 report("confusing subtype %a in %a %s",subtype,"chainedcontext",what)
1209 end
1210 else
1211 report("unsupported subtype %a in %a %s",subtype,"chainedcontext",what)
1212 end
1213end
1214
1215local function extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,types,handlers,what)
1216 local tableoffset = lookupoffset + offset
1217 setposition(f,tableoffset)
1218 local subtype = readushort(f)
1219 if subtype == 1 then
1220 local lookuptype = types[readushort(f)]
1221 local faroffset = readulong(f)
1222 local handler = handlers[lookuptype]
1223 if handler then
1224
1225 return handler(f,fontdata,lookupid,tableoffset + faroffset,0,glyphs,nofglyphs), lookuptype
1226 else
1227 report("no handler for lookuptype %a subtype %a in %s %s",lookuptype,subtype,what,"extension")
1228 end
1229 else
1230 report("unsupported subtype %a in %s %s",subtype,what,"extension")
1231 end
1232end
1233
1234
1235
1236function gsubhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1237 local tableoffset = lookupoffset + offset
1238 setposition(f,tableoffset)
1239 local subtype = readushort(f)
1240 if subtype == 1 then
1241 local coverage = readushort(f)
1242 local delta = readshort(f)
1243 local coverage = readcoverage(f,tableoffset+coverage)
1244 for index in next, coverage do
1245 local newindex = (index + delta) % 65536
1246 if index > nofglyphs or newindex > nofglyphs then
1247 report("invalid index in %s format %i: %i -> %i (max %i)","single",subtype,index,newindex,nofglyphs)
1248 coverage[index] = nil
1249 else
1250 coverage[index] = newindex
1251 end
1252 end
1253 return {
1254 coverage = coverage
1255 }
1256 elseif subtype == 2 then
1257 local coverage = readushort(f)
1258 local nofreplacements = readushort(f)
1259 local replacements = readcardinaltable(f,nofreplacements,ushort)
1260 local coverage = readcoverage(f,tableoffset + coverage)
1261 for index, newindex in next, coverage do
1262 newindex = newindex + 1
1263 if index > nofglyphs or newindex > nofglyphs then
1264 report("invalid index in %s format %i: %i -> %i (max %i)","single",subtype,index,newindex,nofglyphs)
1265 coverage[index] = nil
1266 else
1267 coverage[index] = replacements[newindex]
1268 end
1269 end
1270 return {
1271 coverage = coverage
1272 }
1273 else
1274 report("unsupported subtype %a in %a substitution",subtype,"single")
1275 end
1276end
1277
1278
1279
1280local function sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,what)
1281 local tableoffset = lookupoffset + offset
1282 setposition(f,tableoffset)
1283 local subtype = readushort(f)
1284 if subtype == 1 then
1285 local coverage = readushort(f)
1286 local nofsequence = readushort(f)
1287 local sequences = readcardinaltable(f,nofsequence,ushort)
1288 for i=1,nofsequence do
1289 setposition(f,tableoffset + sequences[i])
1290 sequences[i] = readcardinaltable(f,readushort(f),ushort)
1291 end
1292 local coverage = readcoverage(f,tableoffset + coverage)
1293 for index, newindex in next, coverage do
1294 newindex = newindex + 1
1295 if index > nofglyphs or newindex > nofglyphs then
1296 report("invalid index in %s format %i: %i -> %i (max %i)",what,subtype,index,newindex,nofglyphs)
1297 coverage[index] = nil
1298 else
1299 coverage[index] = sequences[newindex]
1300 end
1301 end
1302 return {
1303 coverage = coverage
1304 }
1305 else
1306 report("unsupported subtype %a in %a substitution",subtype,what)
1307 end
1308end
1309
1310function gsubhandlers.multiple(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1311 return sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"multiple")
1312end
1313
1314function gsubhandlers.alternate(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1315 return sethandler(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"alternate")
1316end
1317
1318function gsubhandlers.ligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1319 local tableoffset = lookupoffset + offset
1320 setposition(f,tableoffset)
1321 local subtype = readushort(f)
1322 if subtype == 1 then
1323 local coverage = readushort(f)
1324 local nofsets = readushort(f)
1325 local ligatures = readcardinaltable(f,nofsets,ushort)
1326 for i=1,nofsets do
1327 local offset = lookupoffset + offset + ligatures[i]
1328 setposition(f,offset)
1329 local n = readushort(f)
1330 if n == 1 then
1331 ligatures[i] = { offset + readushort(f) }
1332 else
1333 local l = { }
1334 for i=1,n do
1335 l[i] = offset + readushort(f)
1336 end
1337 ligatures[i] = l
1338 end
1339 end
1340 local coverage = readcoverage(f,tableoffset + coverage)
1341 for index, newindex in next, coverage do
1342 local hash = { }
1343 local ligatures = ligatures[newindex+1]
1344 for i=1,#ligatures do
1345 local offset = ligatures[i]
1346 setposition(f,offset)
1347 local lig = readushort(f)
1348 local cnt = readushort(f)
1349 local hsh = hash
1350 for i=2,cnt do
1351 local c = readushort(f)
1352 local h = hsh[c]
1353 if not h then
1354 h = { }
1355 hsh[c] = h
1356 end
1357 hsh = h
1358 end
1359 hsh.ligature = lig
1360 end
1361 coverage[index] = hash
1362 end
1363 return {
1364 coverage = coverage
1365 }
1366 else
1367 report("unsupported subtype %a in %a substitution",subtype,"ligature")
1368 end
1369end
1370
1371function gsubhandlers.context(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1372 return unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"substitution"), "context"
1373end
1374
1375function gsubhandlers.chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1376 return chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"substitution"), "chainedcontext"
1377end
1378
1379function gsubhandlers.extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1380 return extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,gsubtypes,gsubhandlers,"substitution")
1381end
1382
1383function gsubhandlers.reversechainedcontextsingle(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1384 local tableoffset = lookupoffset + offset
1385 setposition(f,tableoffset)
1386 local subtype = readushort(f)
1387 if subtype == 1 then
1388 local current = readfirst(f)
1389 local before = readarray(f)
1390 local after = readarray(f)
1391 local replacements = readarray(f)
1392 current = readcoveragearray(f,tableoffset,current,true)
1393 before = readcoveragearray(f,tableoffset,before,true)
1394 after = readcoveragearray(f,tableoffset,after,true)
1395 return {
1396 format = "reversecoverage",
1397 rules = {
1398 {
1399 before = before,
1400 current = current,
1401 after = after,
1402 replacements = replacements,
1403 }
1404 }
1405 }, "reversechainedcontextsingle"
1406 else
1407 report("unsupported subtype %a in %a substitution",subtype,"reversechainedcontextsingle")
1408 end
1409end
1410
1411
1412
1413local function readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta)
1414 local done = { }
1415 for i=1,#sets do
1416 local offset = sets[i]
1417 local reused = done[offset]
1418 if not reused then
1419 offset = tableoffset + offset
1420 setposition(f,offset)
1421 local n = readushort(f)
1422 reused = { }
1423 for i=1,n do
1424 reused[i] = {
1425 readushort(f),
1426 readposition(f,format1,offset,getdelta),
1427 readposition(f,format2,offset,getdelta),
1428 }
1429 end
1430 done[offset] = reused
1431 end
1432 sets[i] = reused
1433 end
1434 return sets
1435end
1436
1437local function readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,mainoffset,getdelta)
1438 local classlist1 = { }
1439 for i=1,nofclasses1 do
1440 local classlist2 = { }
1441 classlist1[i] = classlist2
1442 for j=1,nofclasses2 do
1443 local one = readposition(f,format1,mainoffset,getdelta)
1444 local two = readposition(f,format2,mainoffset,getdelta)
1445 if one or two then
1446 classlist2[j] = { one, two }
1447 else
1448 classlist2[j] = false
1449 end
1450 end
1451 end
1452 return classlist1
1453end
1454
1455
1456
1457function gposhandlers.single(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1458 local tableoffset = lookupoffset + offset
1459 setposition(f,tableoffset)
1460 local subtype = readushort(f)
1461 local getdelta = fontdata.temporary.getdelta
1462 if subtype == 1 then
1463 local coverage = readushort(f)
1464 local format = readushort(f)
1465 local value = readposition(f,format,tableoffset,getdelta)
1466 local coverage = readcoverage(f,tableoffset+coverage)
1467 for index, newindex in next, coverage do
1468 coverage[index] = value
1469 end
1470 return {
1471 format = "single",
1472 coverage = coverage,
1473 }
1474 elseif subtype == 2 then
1475 local coverage = readushort(f)
1476 local format = readushort(f)
1477 local nofvalues = readushort(f)
1478 local values = { }
1479 for i=1,nofvalues do
1480 values[i] = readposition(f,format,tableoffset,getdelta)
1481 end
1482 local coverage = readcoverage(f,tableoffset+coverage)
1483 for index, newindex in next, coverage do
1484 coverage[index] = values[newindex+1]
1485 end
1486 return {
1487 format = "single",
1488 coverage = coverage,
1489 }
1490 else
1491 report("unsupported subtype %a in %a positioning",subtype,"single")
1492 end
1493end
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
1612
1613
1614
1615
1616
1617function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1618 local tableoffset = lookupoffset + offset
1619 setposition(f,tableoffset)
1620 local subtype = readushort(f)
1621 local getdelta = fontdata.temporary.getdelta
1622 if subtype == 1 then
1623 local coverage = readushort(f)
1624 local format1 = readushort(f)
1625 local format2 = readushort(f)
1626 local sets = readarray(f)
1627 sets = readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta)
1628 coverage = readcoverage(f,tableoffset + coverage)
1629 local shared = { }
1630 for index, newindex in next, coverage do
1631 local set = sets[newindex+1]
1632 local hash = { }
1633 for i=1,#set do
1634 local value = set[i]
1635 if value then
1636 local other = value[1]
1637 local share = shared[value]
1638 if share == nil then
1639 local first = value[2]
1640 local second = value[3]
1641 if first or second then
1642 share = { first, second or nil }
1643 else
1644 share = false
1645 end
1646 shared[value] = share
1647 end
1648 hash[other] = share or nil
1649 end
1650 end
1651 coverage[index] = hash
1652 end
1653 return {
1654 shared = shared and true or nil,
1655 format = "pair",
1656 coverage = coverage,
1657 }
1658 elseif subtype == 2 then
1659 local coverage = readushort(f)
1660 local format1 = readushort(f)
1661 local format2 = readushort(f)
1662 local classdef1 = readushort(f)
1663 local classdef2 = readushort(f)
1664 local nofclasses1 = readushort(f)
1665 local nofclasses2 = readushort(f)
1666 local classlist = readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,tableoffset,getdelta)
1667 coverage = readcoverage(f,tableoffset+coverage)
1668 classdef1 = readclassdef(f,tableoffset+classdef1,coverage)
1669 classdef2 = readclassdef(f,tableoffset+classdef2,nofglyphs)
1670 local usedcoverage = { }
1671 local shared = { }
1672 for g1, c1 in next, classdef1 do
1673 if coverage[g1] then
1674 local l1 = classlist[c1]
1675 if l1 then
1676 local hash = { }
1677 for paired, class in next, classdef2 do
1678 local offsets = l1[class]
1679 if offsets then
1680 local first = offsets[1]
1681 local second = offsets[2]
1682 if first or second then
1683 local s1 = shared[first]
1684 if s1 == nil then
1685 s1 = { }
1686 shared[first] = s1
1687 end
1688 local s2 = s1[second]
1689 if s2 == nil then
1690 s2 = { first, second or nil }
1691 s1[second] = s2
1692 end
1693 hash[paired] = s2
1694 end
1695 end
1696 end
1697 usedcoverage[g1] = hash
1698 end
1699 end
1700 end
1701 return {
1702 shared = shared and true or nil,
1703 format = "pair",
1704 coverage = usedcoverage,
1705 }
1706 elseif subtype == 3 then
1707 report("yet unsupported subtype %a in %a positioning",subtype,"pair")
1708 else
1709 report("unsupported subtype %a in %a positioning",subtype,"pair")
1710 end
1711end
1712
1713function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1714 local tableoffset = lookupoffset + offset
1715 setposition(f,tableoffset)
1716 local subtype = readushort(f)
1717 local getdelta = fontdata.temporary.getdelta
1718 if subtype == 1 then
1719 local coverage = tableoffset + readushort(f)
1720 local nofrecords = readushort(f)
1721 local records = { }
1722 for i=1,nofrecords do
1723 local entry = readushort(f)
1724 local exit = readushort(f)
1725 records[i] = {
1726
1727
1728 entry ~= 0 and (tableoffset + entry) or false,
1729 exit ~= 0 and (tableoffset + exit ) or nil,
1730 }
1731 end
1732
1733
1734 local cc = (fontdata.temporary.cursivecount or 0) + 1
1735 fontdata.temporary.cursivecount = cc
1736 cc = "cc-" .. cc
1737 coverage = readcoverage(f,coverage)
1738 for i=1,nofrecords do
1739 local r = records[i]
1740 records[i] = {
1741
1742 cc,
1743
1744
1745 readanchor(f,r[1],getdelta) or false,
1746 readanchor(f,r[2],getdelta) or nil,
1747 }
1748 end
1749 for index, newindex in next, coverage do
1750 coverage[index] = records[newindex+1]
1751 end
1752 return {
1753 coverage = coverage,
1754 }
1755 else
1756 report("unsupported subtype %a in %a positioning",subtype,"cursive")
1757 end
1758end
1759
1760local function handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,ligature)
1761 local tableoffset = lookupoffset + offset
1762 setposition(f,tableoffset)
1763 local subtype = readushort(f)
1764 local getdelta = fontdata.temporary.getdelta
1765 if subtype == 1 then
1766
1767 local markcoverage = tableoffset + readushort(f)
1768 local basecoverage = tableoffset + readushort(f)
1769 local nofclasses = readushort(f)
1770 local markoffset = tableoffset + readushort(f)
1771 local baseoffset = tableoffset + readushort(f)
1772
1773 local markcoverage = readcoverage(f,markcoverage)
1774 local basecoverage = readcoverage(f,basecoverage,true)
1775
1776 setposition(f,markoffset)
1777 local markclasses = { }
1778 local nofmarkclasses = readushort(f)
1779
1780 local lastanchor = fontdata.lastanchor or 0
1781 local usedanchors = { }
1782
1783 for i=1,nofmarkclasses do
1784 local class = readushort(f) + 1
1785 local offset = readushort(f)
1786 if offset == 0 then
1787 markclasses[i] = false
1788 else
1789 markclasses[i] = { class, markoffset + offset }
1790 end
1791 usedanchors[class] = true
1792 end
1793 for i=1,nofmarkclasses do
1794 local mc = markclasses[i]
1795 if mc then
1796 mc[2] = readanchor(f,mc[2],getdelta)
1797 end
1798 end
1799
1800 setposition(f,baseoffset)
1801 local nofbaserecords = readushort(f)
1802 local baserecords = { }
1803
1804 if ligature then
1805
1806
1807
1808
1809 for i=1,nofbaserecords do
1810 local offset = readushort(f)
1811 if offset == 0 then
1812 baserecords[i] = false
1813 else
1814 baserecords[i] = baseoffset + offset
1815 end
1816 end
1817 for i=1,nofbaserecords do
1818 local recordoffset = baserecords[i]
1819 if recordoffset then
1820 setposition(f,recordoffset)
1821 local nofcomponents = readushort(f)
1822 local components = { }
1823 for i=1,nofcomponents do
1824 local classes = { }
1825 for i=1,nofclasses do
1826 local offset = readushort(f)
1827 if offset ~= 0 then
1828 classes[i] = recordoffset + offset
1829 else
1830 classes[i] = false
1831 end
1832 end
1833 components[i] = classes
1834 end
1835 baserecords[i] = components
1836 end
1837 end
1838 local baseclasses = { }
1839 for i=1,nofclasses do
1840 baseclasses[i] = { }
1841 end
1842 for i=1,nofbaserecords do
1843 local components = baserecords[i]
1844 if components then
1845 local b = basecoverage[i]
1846 for c=1,#components do
1847 local classes = components[c]
1848 if classes then
1849 for i=1,nofclasses do
1850 local anchor = readanchor(f,classes[i],getdelta)
1851 local bclass = baseclasses[i]
1852 local bentry = bclass[b]
1853 if bentry then
1854 bentry[c] = anchor
1855 else
1856 bclass[b]= { [c] = anchor }
1857 end
1858 end
1859 end
1860 end
1861 end
1862 end
1863 for index, newindex in next, markcoverage do
1864 markcoverage[index] = markclasses[newindex+1] or nil
1865 end
1866 return {
1867 format = "ligature",
1868 baseclasses = baseclasses,
1869 coverage = markcoverage,
1870 }
1871 else
1872 for i=1,nofbaserecords do
1873 local r = { }
1874 for j=1,nofclasses do
1875 local offset = readushort(f)
1876 if offset == 0 then
1877 r[j] = false
1878 else
1879 r[j] = baseoffset + offset
1880 end
1881 end
1882 baserecords[i] = r
1883 end
1884 local baseclasses = { }
1885 for i=1,nofclasses do
1886 baseclasses[i] = { }
1887 end
1888 for i=1,nofbaserecords do
1889 local r = baserecords[i]
1890 local b = basecoverage[i]
1891 for j=1,nofclasses do
1892 baseclasses[j][b] = readanchor(f,r[j],getdelta)
1893 end
1894 end
1895 for index, newindex in next, markcoverage do
1896 markcoverage[index] = markclasses[newindex+1] or nil
1897 end
1898
1899 return {
1900 format = "base",
1901 baseclasses = baseclasses,
1902 coverage = markcoverage,
1903 }
1904 end
1905 else
1906 report("unsupported subtype %a in",subtype)
1907 end
1908
1909end
1910
1911function gposhandlers.marktobase(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1912 return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1913end
1914
1915function gposhandlers.marktoligature(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1916 return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,true)
1917end
1918
1919function gposhandlers.marktomark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1920 return handlemark(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1921end
1922
1923function gposhandlers.context(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1924 return unchainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"positioning"), "context"
1925end
1926
1927function gposhandlers.chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1928 return chainedcontext(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,"positioning"), "chainedcontext"
1929end
1930
1931function gposhandlers.extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1932 return extension(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs,gpostypes,gposhandlers,"positioning")
1933end
1934
1935
1936
1937do
1938
1939 local plugins = { }
1940
1941 function plugins.size(f,fontdata,tableoffset,feature)
1942 if fontdata.designsize then
1943
1944
1945 else
1946 local function check(offset)
1947 setposition(f,offset)
1948 local designsize = readushort(f)
1949 if designsize > 0 then
1950 local fontstyleid = readushort(f)
1951 local guimenuid = readushort(f)
1952 local minsize = readushort(f)
1953 local maxsize = readushort(f)
1954 if minsize == 0 and maxsize == 0 and fontstyleid == 0 and guimenuid == 0 then
1955 minsize = designsize
1956 maxsize = designsize
1957 end
1958 if designsize >= minsize and designsize <= maxsize then
1959 return minsize, maxsize, designsize
1960 end
1961 end
1962 end
1963 local minsize, maxsize, designsize = check(tableoffset+feature.offset+feature.parameters)
1964 if not designsize then
1965
1966
1967
1968 minsize, maxsize, designsize = check(tableoffset+feature.parameters)
1969 if designsize then
1970 report("bad size feature in %a, falling back to wrong offset",fontdata.filename or "?")
1971 else
1972 report("bad size feature in %a,",fontdata.filename or "?")
1973 end
1974 end
1975 if designsize then
1976 fontdata.minsize = minsize
1977 fontdata.maxsize = maxsize
1978 fontdata.designsize = designsize
1979 end
1980 end
1981 end
1982
1983
1984
1985
1986
1987
1988
1989
1990 local function reorderfeatures(fontdata,scripts,features)
1991 local scriptlangs = { }
1992 local featurehash = { }
1993 local featureorder = { }
1994 for script, languages in next, scripts do
1995 for language, record in next, languages do
1996 local hash = { }
1997 local list = record.featureindices
1998 for k=1,#list do
1999 local index = list[k]
2000 local feature = features[index]
2001 local lookups = feature.lookups
2002 local tag = feature.tag
2003 if tag then
2004 hash[tag] = true
2005 end
2006 if lookups then
2007 for i=1,#lookups do
2008 local lookup = lookups[i]
2009 local o = featureorder[lookup]
2010 if o then
2011 local okay = true
2012 for i=1,#o do
2013 if o[i] == tag then
2014 okay = false
2015 break
2016 end
2017 end
2018 if okay then
2019 o[#o+1] = tag
2020 end
2021 else
2022 featureorder[lookup] = { tag }
2023 end
2024 local f = featurehash[lookup]
2025 if f then
2026 local h = f[tag]
2027 if h then
2028 local s = h[script]
2029 if s then
2030 s[language] = true
2031 else
2032 h[script] = { [language] = true }
2033 end
2034 else
2035 f[tag] = { [script] = { [language] = true } }
2036 end
2037 else
2038 featurehash[lookup] = { [tag] = { [script] = { [language] = true } } }
2039 end
2040
2041 local h = scriptlangs[tag]
2042 if h then
2043 local s = h[script]
2044 if s then
2045 s[language] = true
2046 else
2047 h[script] = { [language] = true }
2048 end
2049 else
2050 scriptlangs[tag] = { [script] = { [language] = true } }
2051 end
2052 end
2053 end
2054 end
2055 end
2056 end
2057 return scriptlangs, featurehash, featureorder
2058 end
2059
2060 local function readscriplan(f,fontdata,scriptoffset)
2061 setposition(f,scriptoffset)
2062 local nofscripts = readushort(f)
2063 local scripts = { }
2064 for i=1,nofscripts do
2065 scripts[readtag(f)] = scriptoffset + readushort(f)
2066 end
2067
2068 local languagesystems = setmetatableindex("table")
2069 for script, offset in next, scripts do
2070 setposition(f,offset)
2071 local defaultoffset = readushort(f)
2072 local noflanguages = readushort(f)
2073 local languages = { }
2074 if defaultoffset > 0 then
2075 languages.dflt = languagesystems[offset + defaultoffset]
2076 end
2077 for i=1,noflanguages do
2078 local language = readtag(f)
2079 local offset = offset + readushort(f)
2080 languages[language] = languagesystems[offset]
2081 end
2082 scripts[script] = languages
2083 end
2084
2085 for offset, usedfeatures in next, languagesystems do
2086 if offset > 0 then
2087 setposition(f,offset)
2088 local featureindices = { }
2089 usedfeatures.featureindices = featureindices
2090 usedfeatures.lookuporder = readushort(f)
2091 usedfeatures.requiredindex = readushort(f)
2092 local noffeatures = readushort(f)
2093 for i=1,noffeatures do
2094 featureindices[i] = readushort(f) + 1
2095 end
2096 end
2097 end
2098 return scripts
2099 end
2100
2101 local function readfeatures(f,fontdata,featureoffset)
2102 setposition(f,featureoffset)
2103 local features = { }
2104 local noffeatures = readushort(f)
2105 for i=1,noffeatures do
2106
2107 features[i] = {
2108 tag = readtag(f),
2109 offset = readushort(f)
2110 }
2111 end
2112
2113 for i=1,noffeatures do
2114 local feature = features[i]
2115 local offset = featureoffset+feature.offset
2116 setposition(f,offset)
2117 local parameters = readushort(f)
2118 local noflookups = readushort(f)
2119 if noflookups > 0 then
2120
2121
2122
2123
2124
2125 local lookups = readcardinaltable(f,noflookups,ushort)
2126 feature.lookups = lookups
2127 for j=1,noflookups do
2128 lookups[j] = lookups[j] + 1
2129 end
2130 end
2131 if parameters > 0 then
2132 feature.parameters = parameters
2133 local plugin = plugins[feature.tag]
2134 if plugin then
2135 plugin(f,fontdata,featureoffset,feature)
2136 end
2137 end
2138 end
2139 return features
2140 end
2141
2142 local function readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder,nofmarkclasses)
2143 setposition(f,lookupoffset)
2144 local noflookups = readushort(f)
2145 local lookups = readcardinaltable(f,noflookups,ushort)
2146 for lookupid=1,noflookups do
2147 local offset = lookups[lookupid]
2148 setposition(f,lookupoffset+offset)
2149 local subtables = { }
2150 local typebits = readushort(f)
2151 local flagbits = readushort(f)
2152 local lookuptype = lookuptypes[typebits]
2153 local lookupflags = lookupflags[flagbits]
2154 local nofsubtables = readushort(f)
2155 for j=1,nofsubtables do
2156 subtables[j] = offset + readushort(f)
2157 end
2158
2159 local markclass = band(flagbits,0x0010) ~= 0
2160 local markset = rshift(flagbits,8)
2161 if markclass then
2162 markclass = readushort(f)
2163 end
2164 if markset > 0 then
2165 markclass = nofmarkclasses + markset
2166 end
2167 lookups[lookupid] = {
2168 type = lookuptype,
2169
2170 flags = lookupflags,
2171 name = lookupid,
2172 subtables = subtables,
2173 markclass = markclass,
2174 features = featurehash[lookupid],
2175 order = featureorder[lookupid],
2176 }
2177 end
2178 return lookups
2179 end
2180
2181 local f_lookupname = formatters["%s_%s_%s"]
2182
2183 local function resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset)
2184
2185 local sequences = fontdata.sequences or { }
2186 local sublookuplist = fontdata.sublookups or { }
2187 fontdata.sequences = sequences
2188 fontdata.sublookups = sublookuplist
2189 local nofsublookups = #sublookuplist
2190 local nofsequences = #sequences
2191 local lastsublookup = nofsublookups
2192 local lastsequence = nofsequences
2193 local lookupnames = lookupnames[what]
2194 local sublookuphash = { }
2195 local sublookupcheck = { }
2196 local glyphs = fontdata.glyphs
2197 local nofglyphs = fontdata.nofglyphs or #glyphs
2198 local noflookups = #lookups
2199 local lookupprefix = sub(what,2,2)
2200
2201 local usedlookups = false
2202
2203 local allsteps = { }
2204
2205 for lookupid=1,noflookups do
2206 local lookup = lookups[lookupid]
2207 local lookuptype = lookup.type
2208 local subtables = lookup.subtables
2209 local features = lookup.features
2210 local handler = lookuphandlers[lookuptype]
2211 if handler then
2212 local nofsubtables = #subtables
2213 local order = lookup.order
2214 local flags = lookup.flags
2215
2216 if flags[1] then flags[1] = "mark" end
2217 if flags[2] then flags[2] = "ligature" end
2218 if flags[3] then flags[3] = "base" end
2219
2220 local markclass = lookup.markclass
2221
2222 if nofsubtables > 0 then
2223 local steps = { }
2224 local nofsteps = 0
2225 local oldtype = nil
2226 for s=1,nofsubtables do
2227 local step, lt = handler(f,fontdata,lookupid,lookupoffset,subtables[s],glyphs,nofglyphs)
2228 if lt then
2229 lookuptype = lt
2230 if oldtype and lt ~= oldtype then
2231 report("messy %s lookup type %a and %a",what,lookuptype,oldtype)
2232 end
2233 oldtype = lookuptype
2234 end
2235 if not step then
2236 report("unsupported %s lookup type %a",what,lookuptype)
2237 else
2238 nofsteps = nofsteps + 1
2239 steps[nofsteps] = step
2240 local rules = step.rules
2241 if rules then
2242 allsteps[#allsteps+1] = step
2243 for i=1,#rules do
2244 local rule = rules[i]
2245 local before = rule.before
2246 local current = rule.current
2247 local after = rule.after
2248 local replacements = rule.replacements
2249 if before then
2250 for i=1,#before do
2251 before[i] = tohash(before[i])
2252 end
2253
2254 rule.before = reversed(before)
2255 end
2256 if current then
2257 if replacements then
2258
2259
2260 local first = current[1]
2261 local hash = { }
2262 local repl = { }
2263 for i=1,#first do
2264 local c = first[i]
2265 hash[c] = true
2266 repl[c] = replacements[i]
2267 end
2268 rule.current = { hash }
2269 rule.replacements = repl
2270 else
2271 for i=1,#current do
2272 current[i] = tohash(current[i])
2273 end
2274 end
2275 else
2276
2277 end
2278 if after then
2279 for i=1,#after do
2280 after[i] = tohash(after[i])
2281 end
2282 end
2283 if usedlookups then
2284 local lookups = rule.lookups
2285 if lookups then
2286 for k, v in next, lookups do
2287 if v then
2288 for k, v in next, v do
2289 usedlookups[v] = usedlookups[v] + 1
2290 end
2291 end
2292 end
2293 end
2294 end
2295 end
2296 end
2297 end
2298 end
2299 if nofsteps ~= nofsubtables then
2300 report("bogus subtables removed in %s lookup type %a",what,lookuptype)
2301 end
2302 lookuptype = lookupnames[lookuptype] or lookuptype
2303 if features then
2304 nofsequences = nofsequences + 1
2305
2306 local l = {
2307 index = nofsequences,
2308 name = f_lookupname(lookupprefix,"s",lookupid+lookupidoffset),
2309 steps = steps,
2310 nofsteps = nofsteps,
2311 type = lookuptype,
2312 markclass = markclass or nil,
2313 flags = flags,
2314
2315 order = order,
2316 features = features,
2317 }
2318 sequences[nofsequences] = l
2319 lookup.done = l
2320 else
2321 nofsublookups = nofsublookups + 1
2322
2323 local l = {
2324 index = nofsublookups,
2325 name = f_lookupname(lookupprefix,"l",lookupid+lookupidoffset),
2326 steps = steps,
2327 nofsteps = nofsteps,
2328 type = lookuptype,
2329 markclass = markclass or nil,
2330 flags = flags,
2331
2332 }
2333 sublookuplist[nofsublookups] = l
2334 sublookuphash[lookupid] = nofsublookups
2335 sublookupcheck[lookupid] = 0
2336 lookup.done = l
2337 end
2338 else
2339 report("no subtables for lookup %a",lookupid)
2340 end
2341 else
2342 report("no handler for lookup %a with type %a",lookupid,lookuptype)
2343 end
2344 end
2345
2346 if usedlookups then
2347 report("used %s lookups: % t",what,sortedkeys(usedlookups))
2348 end
2349
2350
2351
2352
2353
2354
2355
2356 local reported = { }
2357
2358 local function report_issue(i,what,step,kind)
2359
2360 report("rule %i in step %i of %s has %s lookups",i,step,what,kind)
2361
2362
2363 end
2364
2365
2366
2367
2368
2369
2370
2371 for s=1,#allsteps do
2372 local step = allsteps[s]
2373 local rules = step.rules
2374 if rules then
2375 for i=1,#rules do
2376 local rule = rules[i]
2377 local rlookups = rule.lookups
2378 if not rlookups then
2379 report_issue(i,what,s,"no")
2380 elseif not next(rlookups) then
2381
2382
2383 rule.lookups = nil
2384 else
2385
2386
2387 local length = #rlookups
2388 for index=1,length do
2389 local lookuplist = rlookups[index]
2390 if lookuplist then
2391 local length = #lookuplist
2392 local found = { }
2393 local noffound = 0
2394 for index=1,length do
2395 local lookupid = lookuplist[index]
2396 if lookupid then
2397 local h = sublookuphash[lookupid]
2398 if not h then
2399
2400
2401 local lookup = lookups[lookupid]
2402 if lookup then
2403 local d = lookup.done
2404 if d then
2405 nofsublookups = nofsublookups + 1
2406
2407 local l = {
2408 index = nofsublookups,
2409 name = f_lookupname(lookupprefix,"d",lookupid+lookupidoffset),
2410 derived = true,
2411 steps = d.steps,
2412 nofsteps = d.nofsteps,
2413 type = d.lookuptype or "gsub_single",
2414 markclass = d.markclass or nil,
2415 flags = d.flags,
2416
2417 }
2418 sublookuplist[nofsublookups] = copy(l)
2419 sublookuphash[lookupid] = nofsublookups
2420 sublookupcheck[lookupid] = 1
2421 h = nofsublookups
2422 else
2423 report_issue(i,what,s,"missing")
2424 rule.lookups = nil
2425 break
2426 end
2427 else
2428 report_issue(i,what,s,"bad")
2429 rule.lookups = nil
2430 break
2431 end
2432 else
2433 sublookupcheck[lookupid] = sublookupcheck[lookupid] + 1
2434 end
2435 if h then
2436 noffound = noffound + 1
2437 found[noffound] = h
2438 end
2439 end
2440 end
2441 rlookups[index] = noffound > 0 and found or false
2442 else
2443 rlookups[index] = false
2444 end
2445 end
2446 end
2447 end
2448 end
2449 end
2450
2451
2452 for i, n in sortedhash(sublookupcheck) do
2453 local l = lookups[i]
2454 local t = l.type
2455 if n == 0 and t ~= "extension" then
2456 local d = l.done
2457 report("%s lookup %s of type %a is not used",what,d and d.name or l.name,t)
2458 end
2459 end
2460
2461 end
2462
2463 local function loadvariations(f,fontdata,variationsoffset,lookuptypes,featurehash,featureorder)
2464 setposition(f,variationsoffset)
2465 local version = readulong(f)
2466 local nofrecords = readulong(f)
2467 local records = { }
2468 for i=1,nofrecords do
2469 records[i] = {
2470 conditions = readulong(f),
2471 substitutions = readulong(f),
2472 }
2473 end
2474 for i=1,nofrecords do
2475 local record = records[i]
2476 local offset = record.conditions
2477 if offset == 0 then
2478 record.condition = nil
2479 record.matchtype = "always"
2480 else
2481 local offset = variationsoffset+offset
2482 setposition(f,offset)
2483 local nofconditions = readushort(f)
2484 local conditions = { }
2485 for i=1,nofconditions do
2486 conditions[i] = offset + readulong(f)
2487 end
2488 record.conditions = conditions
2489 record.matchtype = "condition"
2490 end
2491 end
2492 for i=1,nofrecords do
2493 local record = records[i]
2494 if record.matchtype == "condition" then
2495 local conditions = record.conditions
2496 for i=1,#conditions do
2497 setposition(f,conditions[i])
2498 conditions[i] = {
2499 format = readushort(f),
2500 axis = readushort(f),
2501 minvalue = read2dot14(f),
2502 maxvalue = read2dot14(f),
2503 }
2504 end
2505 end
2506 end
2507
2508 for i=1,nofrecords do
2509 local record = records[i]
2510 local offset = record.substitutions
2511 if offset == 0 then
2512 record.substitutions = { }
2513 else
2514 setposition(f,variationsoffset + offset)
2515 local version = readulong(f)
2516 local nofsubstitutions = readushort(f)
2517 local substitutions = { }
2518 for i=1,nofsubstitutions do
2519 substitutions[readushort(f)] = readulong(f)
2520 end
2521 for index, alternates in sortedhash(substitutions) do
2522 if index == 0 then
2523 record.substitutions = false
2524 else
2525 local tableoffset = variationsoffset + offset + alternates
2526 setposition(f,tableoffset)
2527 local parameters = readulong(f)
2528 local noflookups = readushort(f)
2529 local lookups = readcardinaltable(f,noflookups,ushort)
2530
2531 record.substitutions = lookups
2532 end
2533 end
2534 end
2535 end
2536 setvariabledata(fontdata,"features",records)
2537 end
2538
2539 local function readscripts(f,fontdata,what,lookuptypes,lookuphandlers,lookupstoo)
2540 local tableoffset = gotodatatable(f,fontdata,what,true)
2541 if tableoffset then
2542 local version = readulong(f)
2543 local scriptoffset = tableoffset + readushort(f)
2544 local featureoffset = tableoffset + readushort(f)
2545 local lookupoffset = tableoffset + readushort(f)
2546
2547
2548 local variationsoffset = version > 0x00010000 and readulong(f) or 0
2549 if not scriptoffset then
2550 return
2551 end
2552 local scripts = readscriplan(f,fontdata,scriptoffset)
2553 local features = readfeatures(f,fontdata,featureoffset)
2554
2555 local scriptlangs, featurehash, featureorder = reorderfeatures(fontdata,scripts,features)
2556
2557 if fontdata.features then
2558 fontdata.features[what] = scriptlangs
2559 else
2560 fontdata.features = { [what] = scriptlangs }
2561 end
2562
2563 if not lookupstoo then
2564 return
2565 end
2566
2567 local markclasses = fontdata.markclasses
2568 local marksets = fontdata.marksets
2569 local nofmarkclasses = (markclasses and #markclasses or 0) - (marksets and #marksets or 0)
2570 local lookups = readlookups(f,lookupoffset,lookuptypes,featurehash,featureorder,nofmarkclasses)
2571
2572 if lookups then
2573 resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset)
2574 end
2575
2576 if variationsoffset > 0 then
2577
2578 loadvariations(f,fontdata,tableoffset + variationsoffset,lookuptypes,featurehash,featureorder)
2579 end
2580 end
2581 end
2582
2583 local function checkkerns(f,fontdata,specification)
2584 local datatable = fontdata.tables.kern
2585 if not datatable then
2586 return
2587 end
2588 local features = fontdata.features
2589 local gposfeatures = features and features.gpos
2590 local name
2591 if not gposfeatures or not gposfeatures.kern then
2592 name = "kern"
2593 elseif specification.globalkerns then
2594 name = "globalkern"
2595 else
2596 report("ignoring global kern table, using gpos kern feature")
2597 return
2598 end
2599 setposition(f,datatable.offset)
2600 local version = readushort(f)
2601 local noftables = readushort(f)
2602 if noftables > 1 then
2603 report("adding global kern table as gpos feature %a",name)
2604 local kerns = setmetatableindex("table")
2605 for i=1,noftables do
2606 local version = readushort(f)
2607 local length = readushort(f)
2608 local coverage = readushort(f)
2609
2610 local format = rshift(coverage,8)
2611 if format == 0 then
2612 local nofpairs = readushort(f)
2613 local searchrange = readushort(f)
2614 local entryselector = readushort(f)
2615 local rangeshift = readushort(f)
2616 for i=1,nofpairs do
2617 kerns[readushort(f)][readushort(f)] = readfword(f)
2618 end
2619 elseif format == 2 then
2620
2621 else
2622
2623 end
2624 end
2625 local feature = { dflt = { dflt = true } }
2626 if not features then
2627 fontdata.features = { gpos = { [name] = feature } }
2628 elseif not gposfeatures then
2629 fontdata.features.gpos = { [name] = feature }
2630 else
2631 gposfeatures[name] = feature
2632 end
2633 local sequences = fontdata.sequences
2634 if not sequences then
2635 sequences = { }
2636 fontdata.sequences = sequences
2637 end
2638 local nofsequences = #sequences + 1
2639 sequences[nofsequences] = {
2640 index = nofsequences,
2641 name = name,
2642 steps = {
2643 {
2644 coverage = kerns,
2645 format = "kern",
2646 },
2647 },
2648 nofsteps = 1,
2649 type = "gpos_pair",
2650 flags = { false, false, false, false },
2651 order = { name },
2652 features = { [name] = feature },
2653 }
2654 else
2655 report("ignoring empty kern table of feature %a",name)
2656 end
2657 end
2658
2659 function readers.gsub(f,fontdata,specification)
2660 if specification.details then
2661 readscripts(f,fontdata,"gsub",gsubtypes,gsubhandlers,specification.lookups)
2662 end
2663 end
2664
2665 function readers.gpos(f,fontdata,specification)
2666 if specification.details then
2667 readscripts(f,fontdata,"gpos",gpostypes,gposhandlers,specification.lookups)
2668 if specification.lookups then
2669 checkkerns(f,fontdata,specification)
2670 end
2671 end
2672 end
2673
2674end
2675
2676function readers.gdef(f,fontdata,specification)
2677 if not specification.glyphs then
2678 return
2679 end
2680 local datatable = fontdata.tables.gdef
2681 if datatable then
2682 local tableoffset = datatable.offset
2683 setposition(f,tableoffset)
2684 local version = readulong(f)
2685 local classoffset = readushort(f)
2686 local attachmentoffset = readushort(f)
2687 local ligaturecarets = readushort(f)
2688 local markclassoffset = readushort(f)
2689 local marksetsoffset = version >= 0x00010002 and readushort(f) or 0
2690 local varsetsoffset = version >= 0x00010003 and readulong(f) or 0
2691 local glyphs = fontdata.glyphs
2692 local marks = { }
2693 local markclasses = setmetatableindex("table")
2694 local marksets = setmetatableindex("table")
2695 fontdata.marks = marks
2696 fontdata.markclasses = markclasses
2697 fontdata.marksets = marksets
2698
2699 if classoffset ~= 0 then
2700 setposition(f,tableoffset + classoffset)
2701 local classformat = readushort(f)
2702 if classformat == 1 then
2703 local firstindex = readushort(f)
2704 local lastindex = firstindex + readushort(f) - 1
2705 for index=firstindex,lastindex do
2706 local class = classes[readushort(f)]
2707 if class == "mark" then
2708 marks[index] = true
2709 end
2710 glyphs[index].class = class
2711 end
2712 elseif classformat == 2 then
2713 local nofranges = readushort(f)
2714 for i=1,nofranges do
2715 local firstindex = readushort(f)
2716 local lastindex = readushort(f)
2717 local class = classes[readushort(f)]
2718 if class then
2719 for index=firstindex,lastindex do
2720 glyphs[index].class = class
2721 if class == "mark" then
2722 marks[index] = true
2723 end
2724 end
2725 end
2726 end
2727 end
2728 end
2729
2730 if markclassoffset ~= 0 then
2731 setposition(f,tableoffset + markclassoffset)
2732 local classformat = readushort(f)
2733 if classformat == 1 then
2734 local firstindex = readushort(f)
2735 local lastindex = firstindex + readushort(f) - 1
2736 for index=firstindex,lastindex do
2737 markclasses[readushort(f)][index] = true
2738 end
2739 elseif classformat == 2 then
2740 local nofranges = readushort(f)
2741 for i=1,nofranges do
2742 local firstindex = readushort(f)
2743 local lastindex = readushort(f)
2744 local class = markclasses[readushort(f)]
2745 for index=firstindex,lastindex do
2746 class[index] = true
2747 end
2748 end
2749 end
2750 end
2751
2752 if marksetsoffset ~= 0 then
2753 local nofmarkclasses = fontdata.markclasses and #fontdata.markclasses or 0
2754 marksetsoffset = tableoffset + marksetsoffset
2755 setposition(f,marksetsoffset)
2756 local format = readushort(f)
2757 if format == 1 then
2758 local nofsets = readushort(f)
2759 local sets = readcardinaltable(f,nofsets,ulong)
2760 for i=1,nofsets do
2761 local offset = sets[i]
2762 if offset ~= 0 then
2763 markclasses[nofmarkclasses + i] = readcoverage(f,marksetsoffset+offset)
2764 marksets[i] = { }
2765 end
2766 end
2767 end
2768 end
2769
2770 local factors = specification.factors
2771
2772 if (specification.variable or factors) and varsetsoffset ~= 0 then
2773
2774 local regions, deltas = readvariationdata(f,tableoffset+varsetsoffset,factors)
2775
2776
2777
2778 if factors then
2779 fontdata.temporary.getdelta = function(outer,inner)
2780 local delta = deltas[outer+1]
2781 if delta then
2782 local d = delta.deltas[inner+1]
2783 if d then
2784 local scales = delta.scales
2785 local dd = 0
2786 for i=1,#scales do
2787 local di = d[i]
2788 if di then
2789 dd = dd + scales[i] * di
2790 else
2791 break
2792 end
2793 end
2794 return round(dd)
2795 end
2796 end
2797 return 0
2798 end
2799 end
2800
2801 end
2802 end
2803end
2804
2805
2806
2807
2808local function readmathvalue(f)
2809 local v = readshort(f)
2810 skipshort(f,1)
2811 return v
2812end
2813
2814local function readmathconstants(f,fontdata,offset)
2815 setposition(f,offset)
2816 fontdata.mathconstants = {
2817 ScriptPercentScaleDown = readshort(f),
2818 ScriptScriptPercentScaleDown = readshort(f),
2819 DelimitedSubFormulaMinHeight = readushort(f),
2820 DisplayOperatorMinHeight = readushort(f),
2821 MathLeading = readmathvalue(f),
2822 AxisHeight = readmathvalue(f),
2823 AccentBaseHeight = readmathvalue(f),
2824 FlattenedAccentBaseHeight = readmathvalue(f),
2825 SubscriptShiftDown = readmathvalue(f),
2826 SubscriptTopMax = readmathvalue(f),
2827 SubscriptBaselineDropMin = readmathvalue(f),
2828 SuperscriptShiftUp = readmathvalue(f),
2829 SuperscriptShiftUpCramped = readmathvalue(f),
2830 SuperscriptBottomMin = readmathvalue(f),
2831 SuperscriptBaselineDropMax = readmathvalue(f),
2832 SubSuperscriptGapMin = readmathvalue(f),
2833 SuperscriptBottomMaxWithSubscript = readmathvalue(f),
2834 SpaceAfterScript = readmathvalue(f),
2835 UpperLimitGapMin = readmathvalue(f),
2836 UpperLimitBaselineRiseMin = readmathvalue(f),
2837 LowerLimitGapMin = readmathvalue(f),
2838 LowerLimitBaselineDropMin = readmathvalue(f),
2839 StackTopShiftUp = readmathvalue(f),
2840 StackTopDisplayStyleShiftUp = readmathvalue(f),
2841 StackBottomShiftDown = readmathvalue(f),
2842 StackBottomDisplayStyleShiftDown = readmathvalue(f),
2843 StackGapMin = readmathvalue(f),
2844 StackDisplayStyleGapMin = readmathvalue(f),
2845 StretchStackTopShiftUp = readmathvalue(f),
2846 StretchStackBottomShiftDown = readmathvalue(f),
2847 StretchStackGapAboveMin = readmathvalue(f),
2848 StretchStackGapBelowMin = readmathvalue(f),
2849 FractionNumeratorShiftUp = readmathvalue(f),
2850 FractionNumeratorDisplayStyleShiftUp = readmathvalue(f),
2851 FractionDenominatorShiftDown = readmathvalue(f),
2852 FractionDenominatorDisplayStyleShiftDown = readmathvalue(f),
2853 FractionNumeratorGapMin = readmathvalue(f),
2854 FractionNumeratorDisplayStyleGapMin = readmathvalue(f),
2855 FractionRuleThickness = readmathvalue(f),
2856 FractionDenominatorGapMin = readmathvalue(f),
2857 FractionDenominatorDisplayStyleGapMin = readmathvalue(f),
2858 SkewedFractionHorizontalGap = readmathvalue(f),
2859 SkewedFractionVerticalGap = readmathvalue(f),
2860 OverbarVerticalGap = readmathvalue(f),
2861 OverbarRuleThickness = readmathvalue(f),
2862 OverbarExtraAscender = readmathvalue(f),
2863 UnderbarVerticalGap = readmathvalue(f),
2864 UnderbarRuleThickness = readmathvalue(f),
2865 UnderbarExtraDescender = readmathvalue(f),
2866 RadicalVerticalGap = readmathvalue(f),
2867 RadicalDisplayStyleVerticalGap = readmathvalue(f),
2868 RadicalRuleThickness = readmathvalue(f),
2869 RadicalExtraAscender = readmathvalue(f),
2870 RadicalKernBeforeDegree = readmathvalue(f),
2871 RadicalKernAfterDegree = readmathvalue(f),
2872 RadicalDegreeBottomRaisePercent = readshort(f),
2873 }
2874end
2875
2876local function readmathglyphinfo(f,fontdata,offset)
2877 setposition(f,offset)
2878 local italics = readushort(f)
2879 local accents = readushort(f)
2880 local extensions = readushort(f)
2881 local kerns = readushort(f)
2882 local glyphs = fontdata.glyphs
2883 if italics ~= 0 then
2884 setposition(f,offset+italics)
2885 local coverage = readushort(f)
2886 local nofglyphs = readushort(f)
2887 coverage = readcoverage(f,offset+italics+coverage,true)
2888 setposition(f,offset+italics+4)
2889 for i=1,nofglyphs do
2890 local italic = readmathvalue(f)
2891 if italic ~= 0 then
2892 local glyph = glyphs[coverage[i]]
2893 local math = glyph.math
2894 if not math then
2895 glyph.math = { italic = italic }
2896 else
2897 math.italic = italic
2898 end
2899 end
2900 end
2901 fontdata.hasitalics = true
2902 end
2903 if accents ~= 0 then
2904 setposition(f,offset+accents)
2905 local coverage = readushort(f)
2906 local nofglyphs = readushort(f)
2907 coverage = readcoverage(f,offset+accents+coverage,true)
2908 setposition(f,offset+accents+4)
2909 for i=1,nofglyphs do
2910 local accent = readmathvalue(f)
2911 if accent ~= 0 then
2912 local glyph = glyphs[coverage[i]]
2913 local math = glyph.math
2914 if not math then
2915 glyph.math = { accent = accent }
2916 else
2917 math.accent = accent
2918 end
2919 end
2920 end
2921 end
2922 if extensions ~= 0 then
2923 setposition(f,offset+extensions)
2924 end
2925 if kerns ~= 0 then
2926 local kernoffset = offset + kerns
2927 setposition(f,kernoffset)
2928 local coverage = readushort(f)
2929 local nofglyphs = readushort(f)
2930 if nofglyphs > 0 then
2931 local function get(offset)
2932 setposition(f,kernoffset+offset)
2933 local n = readushort(f)
2934 if n == 0 then
2935 local k = readmathvalue(f)
2936 if k == 0 then
2937
2938 else
2939 return { { kern = k } }
2940 end
2941 else
2942 local l = { }
2943 for i=1,n do
2944 l[i] = { height = readmathvalue(f) }
2945 end
2946 for i=1,n do
2947 l[i].kern = readmathvalue(f)
2948 end
2949 l[n+1] = { kern = readmathvalue(f) }
2950 return l
2951 end
2952 end
2953 local kernsets = { }
2954 for i=1,nofglyphs do
2955 local topright = readushort(f)
2956 local topleft = readushort(f)
2957 local bottomright = readushort(f)
2958 local bottomleft = readushort(f)
2959 kernsets[i] = {
2960 topright = topright ~= 0 and topright or nil,
2961 topleft = topleft ~= 0 and topleft or nil,
2962 bottomright = bottomright ~= 0 and bottomright or nil,
2963 bottomleft = bottomleft ~= 0 and bottomleft or nil,
2964 }
2965 end
2966 coverage = readcoverage(f,kernoffset+coverage,true)
2967 for i=1,nofglyphs do
2968 local kernset = kernsets[i]
2969 if next(kernset) then
2970 local k = kernset.topright if k then kernset.topright = get(k) end
2971 local k = kernset.topleft if k then kernset.topleft = get(k) end
2972 local k = kernset.bottomright if k then kernset.bottomright = get(k) end
2973 local k = kernset.bottomleft if k then kernset.bottomleft = get(k) end
2974 if next(kernset) then
2975 local glyph = glyphs[coverage[i]]
2976 local math = glyph.math
2977 if math then
2978 math.kerns = kernset
2979 else
2980 glyph.math = { kerns = kernset }
2981 end
2982 end
2983 end
2984 end
2985 end
2986 end
2987end
2988
2989local function readmathvariants(f,fontdata,offset)
2990 setposition(f,offset)
2991 local glyphs = fontdata.glyphs
2992 local minoverlap = readushort(f)
2993 local vcoverage = readushort(f)
2994 local hcoverage = readushort(f)
2995 local vnofglyphs = readushort(f)
2996 local hnofglyphs = readushort(f)
2997 local vconstruction = readcardinaltable(f,vnofglyphs,ushort)
2998 local hconstruction = readcardinaltable(f,hnofglyphs,ushort)
2999
3000 fontdata.mathconstants.MinConnectorOverlap = minoverlap
3001
3002
3003
3004
3005
3006
3007 local function get(offset,coverage,nofglyphs,construction,kvariants,kparts,kitalic,korientation,orientation)
3008 if coverage ~= 0 and nofglyphs > 0 then
3009 local coverage = readcoverage(f,offset+coverage,true)
3010 local n = 0
3011 for i=1,nofglyphs do
3012 local c = construction[i]
3013 if c ~= 0 then
3014 local index = coverage[i]
3015 local glyph = glyphs[index]
3016 local math = glyph.math
3017 setposition(f,offset+c)
3018 local assembly = readushort(f)
3019 local nofvariants = readushort(f)
3020 if nofvariants > 0 then
3021 local variants, v = nil, 0
3022 for i=1,nofvariants do
3023 local variant = readushort(f)
3024 if variant == index then
3025
3026 n = n + 1
3027 elseif variants then
3028 v = v + 1
3029 variants[v] = variant
3030 else
3031 v = 1
3032 variants = { variant }
3033 end
3034 skipshort(f)
3035 end
3036 if not variants or not next(variants) then
3037
3038 elseif not math then
3039 math = { [kvariants] = variants }
3040 glyph.math = math
3041 else
3042 math[kvariants] = variants
3043 end
3044 end
3045 if assembly ~= 0 then
3046 setposition(f,offset + c + assembly)
3047 local italic = readmathvalue(f)
3048 local nofparts = readushort(f)
3049 local parts = { }
3050 for i=1,nofparts do
3051 local p = {
3052 glyph = readushort(f),
3053 start = readushort(f),
3054 ["end"] = readushort(f),
3055 advance = readushort(f),
3056 }
3057 local flags = readushort(f)
3058 if band(flags,0x0001) ~= 0 then
3059 p.extender = 1
3060 end
3061 parts[i] = p
3062 end
3063 if not math then
3064 math = {
3065 [kparts] = parts
3066 }
3067 glyph.math = math
3068 else
3069 math[kparts] = parts
3070 end
3071 if italic and italic ~= 0 then
3072 math[kitalic] = italic
3073 end
3074 if korientation and orientation then
3075 math[korientation] = orientation
3076 end
3077 end
3078 end
3079 end
3080
3081
3082 if n > 0 then
3083 report("discarding %i self referencing %s variant entries",n,orientation)
3084 end
3085
3086
3087 for index=1,#glyphs do
3088 local g = glyphs[index]
3089 local m = g.math
3090 if m then
3091 local v = m[kvariants]
3092 if v then
3093 local done = { [index] = true }
3094 local size = #v
3095 local i = 1
3096 while i <= size do
3097 local vi = v[i]
3098 if done[vi] then
3099 report("discarding %sdirect circular %s variant index 0x%04X for index 0x%04X, %C","",orientation,vi,index,tonumber(g.unicode) or 0xFFFD)
3100 table.remove(v,i)
3101 size = size - 1
3102 goto NEXT
3103 else
3104 local gg = glyphs[vi]
3105 if gg then
3106 local mm = gg.math
3107 if mm then
3108 local vv = mm[kvariants]
3109 if vv then
3110 report("discarding %sdirect circular %s variant index 0x%04X for index 0x%04X, %C","in",orientation,vi,index,tonumber(g.unicode) or 0xFFFD)
3111 table.remove(v,i)
3112 size = size - 1
3113 goto NEXT
3114 end
3115 end
3116 end
3117 done[vi] = true
3118 end
3119 i = i + 1
3120 ::NEXT::
3121 end
3122 end
3123 end
3124 end
3125 end
3126 end
3127 get(offset,hcoverage,hnofglyphs,hconstruction,"hvariants","hparts","hitalic",nil,"horizontal")
3128 get(offset,vcoverage,vnofglyphs,vconstruction,"vvariants","vparts","vitalic",nil,"vertical")
3129end
3130
3131function readers.math(f,fontdata,specification)
3132 local tableoffset = gotodatatable(f,fontdata,"math",specification.glyphs)
3133 if tableoffset then
3134 local version = readulong(f)
3135
3136
3137
3138
3139 local constants = readushort(f)
3140 local glyphinfo = readushort(f)
3141 local variants = readushort(f)
3142 if constants == 0 then
3143 report("the math table of %a has no constants",fontdata.filename)
3144 else
3145 readmathconstants(f,fontdata,tableoffset+constants)
3146 end
3147 if glyphinfo ~= 0 then
3148 readmathglyphinfo(f,fontdata,tableoffset+glyphinfo)
3149 end
3150 if variants ~= 0 then
3151 readmathvariants(f,fontdata,tableoffset+variants)
3152 end
3153 end
3154end
3155
3156function readers.colr(f,fontdata,specification)
3157 local tableoffset = gotodatatable(f,fontdata,"colr",specification.glyphs)
3158 if tableoffset then
3159 local version = readushort(f)
3160 if version == 0 then
3161
3162 elseif version == 1 then
3163 report("table version %a of %a is %s supported for font %s",version,"colr","partially",fontdata.filename)
3164 else
3165 report("table version %a of %a is %s supported for font %s",version,"colr","not",fontdata.filename)
3166 return
3167 end
3168 if not fontdata.tables.cpal then
3169 report("color table %a in font %a has no mandate %a table","colr",fontdata.filename,"cpal")
3170 fontdata.colorpalettes = { }
3171 end
3172 local glyphs = fontdata.glyphs
3173 local nofglyphs = readushort(f)
3174 local baseoffset = readulong(f)
3175 local layeroffset = readulong(f)
3176 local noflayers = readushort(f)
3177 local layerrecords = { }
3178 local maxclass = 0
3179
3180
3181
3182 setposition(f,tableoffset + layeroffset)
3183 for i=1,noflayers do
3184 local slot = readushort(f)
3185 local class = readushort(f)
3186 if class < 0xFFFF then
3187 class = class + 1
3188 if class > maxclass then
3189 maxclass = class
3190 end
3191 end
3192 layerrecords[i] = {
3193 slot = slot,
3194 class = class,
3195 }
3196 end
3197 fontdata.maxcolorclass = maxclass
3198 setposition(f,tableoffset + baseoffset)
3199 for i=0,nofglyphs-1 do
3200 local glyphindex = readushort(f)
3201 local firstlayer = readushort(f)
3202 local noflayers = readushort(f)
3203 local t = { }
3204 for i=1,noflayers do
3205 t[i] = layerrecords[firstlayer+i]
3206 end
3207 glyphs[glyphindex].colors = t
3208 end
3209 end
3210 fontdata.hascolor = true
3211end
3212
3213function readers.cpal(f,fontdata,specification)
3214 local tableoffset = gotodatatable(f,fontdata,"cpal",specification.glyphs)
3215 if tableoffset then
3216 local version = readushort(f)
3217
3218
3219
3220
3221 local nofpaletteentries = readushort(f)
3222 local nofpalettes = readushort(f)
3223 local nofcolorrecords = readushort(f)
3224 local firstcoloroffset = readulong(f)
3225 local colorrecords = { }
3226 local palettes = readcardinaltable(f,nofpalettes,ushort)
3227 if version == 1 then
3228
3229 local palettettypesoffset = readulong(f)
3230 local palettelabelsoffset = readulong(f)
3231 local paletteentryoffset = readulong(f)
3232 end
3233 setposition(f,tableoffset+firstcoloroffset)
3234 for i=1,nofcolorrecords do
3235 local b, g, r, a = readbytes(f,4)
3236 colorrecords[i] = {
3237 r, g, b, a ~= 255 and a or nil,
3238 }
3239 end
3240 for i=1,nofpalettes do
3241 local p = { }
3242 local o = palettes[i]
3243 for j=1,nofpaletteentries do
3244 p[j] = colorrecords[o+j]
3245 end
3246 palettes[i] = p
3247 end
3248 fontdata.colorpalettes = palettes
3249 end
3250end
3251
3252local compress = gzip and gzip.compress
3253local compressed = compress and gzip.compressed
3254
3255
3256
3257function readers.svg(f,fontdata,specification)
3258 local tableoffset = gotodatatable(f,fontdata,"svg",specification.glyphs)
3259 if tableoffset then
3260 local version = readushort(f)
3261
3262
3263
3264
3265 local glyphs = fontdata.glyphs
3266 local indexoffset = tableoffset + readulong(f)
3267 local reserved = readulong(f)
3268 setposition(f,indexoffset)
3269 local nofentries = readushort(f)
3270 local entries = { }
3271 for i=1,nofentries do
3272 entries[i] = {
3273 first = readushort(f),
3274 last = readushort(f),
3275 offset = indexoffset + readulong(f),
3276 length = readulong(f),
3277 }
3278 end
3279 for i=1,nofentries do
3280 local entry = entries[i]
3281 setposition(f,entry.offset)
3282 local data = readstring(f,entry.length)
3283 if compressed and not compressed(data) then
3284 data = compress(data)
3285 end
3286 entries[i] = {
3287 first = entry.first,
3288 last = entry.last,
3289 data = data
3290 }
3291 end
3292 fontdata.svgshapes = entries
3293 end
3294 fontdata.hascolor = true
3295end
3296
3297function readers.sbix(f,fontdata,specification)
3298 local tableoffset = gotodatatable(f,fontdata,"sbix",specification.glyphs)
3299 if tableoffset then
3300 local version = readushort(f)
3301 local flags = readushort(f)
3302 local nofstrikes = readulong(f)
3303 local strikes = { }
3304 local nofglyphs = fontdata.nofglyphs
3305 for i=1,nofstrikes do
3306 strikes[i] = readulong(f)
3307 end
3308 local shapes = { }
3309 local done = 0
3310 for i=1,nofstrikes do
3311 local strikeoffset = strikes[i] + tableoffset
3312 setposition(f,strikeoffset)
3313 strikes[i] = {
3314 ppem = readushort(f),
3315 ppi = readushort(f),
3316 offset = strikeoffset
3317 }
3318 end
3319
3320 sort(strikes,function(a,b)
3321 if b.ppem == a.ppem then
3322 return b.ppi < a.ppi
3323 else
3324 return b.ppem < a.ppem
3325 end
3326 end)
3327 local glyphs = { }
3328 local delayed = CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 or fonts.handlers.typethree
3329 for i=1,nofstrikes do
3330 local strike = strikes[i]
3331 local strikeppem = strike.ppem
3332 local strikeppi = strike.ppi
3333 local strikeoffset = strike.offset
3334 setposition(f,strikeoffset)
3335 for i=0,nofglyphs do
3336 glyphs[i] = readulong(f)
3337 end
3338 local glyphoffset = glyphs[0]
3339 for i=0,nofglyphs-1 do
3340 local nextoffset = glyphs[i+1]
3341 if not shapes[i] then
3342 local datasize = nextoffset - glyphoffset
3343 if datasize > 0 then
3344 setposition(f,strikeoffset + glyphoffset)
3345 local x = readshort(f)
3346 local y = readshort(f)
3347 local tag = readtag(f)
3348 local size = datasize - 8
3349 local data = nil
3350 local offset = nil
3351 if delayed then
3352 offset = getposition(f)
3353 data = nil
3354 else
3355 data = readstring(f,size)
3356 size = nil
3357 end
3358 shapes[i] = {
3359 x = x,
3360 y = y,
3361 o = offset,
3362 s = size,
3363 data = data,
3364
3365
3366
3367 }
3368 done = done + 1
3369 if done == nofglyphs then
3370 break
3371 end
3372 end
3373 end
3374 glyphoffset = nextoffset
3375 end
3376 end
3377 fontdata.pngshapes = shapes
3378 end
3379end
3380
3381
3382
3383
3384do
3385
3386 local function getmetrics(f)
3387 return {
3388 ascender = readinteger(f),
3389 descender = readinteger(f),
3390 widthmax = readuinteger(f),
3391 caretslopedumerator = readinteger(f),
3392 caretslopedenominator = readinteger(f),
3393 caretoffset = readinteger(f),
3394 minorigin = readinteger(f),
3395 minadvance = readinteger(f),
3396 maxbefore = readinteger(f),
3397 minafter = readinteger(f),
3398 pad1 = readinteger(f),
3399 pad2 = readinteger(f),
3400 }
3401 end
3402
3403
3404
3405 local function getbigmetrics(f)
3406
3407 return {
3408 height = readuinteger(f),
3409 width = readuinteger(f),
3410 horiBearingX = readinteger(f),
3411 horiBearingY = readinteger(f),
3412 horiAdvance = readuinteger(f),
3413 vertBearingX = readinteger(f),
3414 vertBearingY = readinteger(f),
3415 vertAdvance = readuinteger(f),
3416 }
3417 end
3418
3419 local function getsmallmetrics(f)
3420
3421 return {
3422 height = readuinteger(f),
3423 width = readuinteger(f),
3424 bearingX = readinteger(f),
3425 bearingY = readinteger(f),
3426 advance = readuinteger(f),
3427 }
3428 end
3429
3430 function readers.cblc(f,fontdata,specification)
3431
3432 local ctdttableoffset = gotodatatable(f,fontdata,"cbdt",specification.glyphs)
3433 if not ctdttableoffset then
3434 return
3435 end
3436 local cblctableoffset = gotodatatable(f,fontdata,"cblc",specification.glyphs)
3437 if cblctableoffset then
3438 local majorversion = readushort(f)
3439 local minorversion = readushort(f)
3440 local nofsizetables = readulong(f)
3441 local sizetables = { }
3442 local shapes = { }
3443 local subtables = { }
3444 for i=1,nofsizetables do
3445 sizetables[i] = {
3446 subtables = readulong(f),
3447 indexsize = readulong(f),
3448 nofsubtables = readulong(f),
3449 colorref = readulong(f),
3450 hormetrics = getmetrics(f),
3451 vermetrics = getmetrics(f),
3452 firstindex = readushort(f),
3453 lastindex = readushort(f),
3454 ppemx = readbyte(f),
3455 ppemy = readbyte(f),
3456 bitdepth = readbyte(f),
3457 flags = readbyte(f),
3458 }
3459 end
3460 sort(sizetables,function(a,b)
3461 if b.ppemx == a.ppemx then
3462 return b.bitdepth < a.bitdepth
3463 else
3464 return b.ppemx < a.ppemx
3465 end
3466 end)
3467 for i=1,nofsizetables do
3468 local s = sizetables[i]
3469 local d = false
3470 for j=s.firstindex,s.lastindex do
3471 if not shapes[j] then
3472 shapes[j] = i
3473 d = true
3474 end
3475 end
3476 if d then
3477 s.used = true
3478 end
3479 end
3480 for i=1,nofsizetables do
3481 local s = sizetables[i]
3482 if s.used then
3483 local offset = s.subtables
3484 setposition(f,cblctableoffset+offset)
3485 for j=1,s.nofsubtables do
3486 local firstindex = readushort(f)
3487 local lastindex = readushort(f)
3488 local tableoffset = readulong(f) + offset
3489 for k=firstindex,lastindex do
3490 if shapes[k] == i then
3491 local s = subtables[tableoffset]
3492 if not s then
3493 s = {
3494 firstindex = firstindex,
3495 lastindex = lastindex,
3496 }
3497 subtables[tableoffset] = s
3498 end
3499 shapes[k] = s
3500 end
3501 end
3502 end
3503 end
3504 end
3505
3506
3507
3508
3509 for offset, subtable in sortedhash(subtables) do
3510 local tabletype = readushort(f)
3511 subtable.format = readushort(f)
3512 local baseoffset = readulong(f) + ctdttableoffset
3513 local offsets = { }
3514 local metrics = nil
3515 if tabletype == 1 then
3516
3517 for i=subtable.firstindex,subtable.lastindex do
3518 offsets[i] = readulong(f) + baseoffset
3519 end
3520 skipbytes(f,4)
3521 elseif tabletype == 2 then
3522 local size = readulong(f)
3523 local done = baseoffset
3524 metrics = getbigmetrics(f)
3525 for i=subtable.firstindex,subtable.lastindex do
3526 offsets[i] = done
3527 done = done + size
3528 end
3529 elseif tabletype == 3 then
3530
3531 local n = subtable.lastindex - subtable.firstindex + 2
3532 for i=subtable.firstindex,subtable.lastindex do
3533 offsets[i] = readushort(f) + baseoffset
3534 end
3535 if math.odd(n) then
3536 skipbytes(f,4)
3537 else
3538 skipbytes(f,2)
3539 end
3540 elseif tabletype == 4 then
3541 for i=1,readulong(f) do
3542 offsets[readushort(f)] = readushort(f) + baseoffset
3543 end
3544 elseif tabletype == 5 then
3545 local size = readulong(f)
3546 local done = baseoffset
3547 metrics = getbigmetrics(f)
3548 local n = readulong(f)
3549 for i=1,n do
3550 offsets[readushort(f)] = done
3551 done = done + size
3552 end
3553 if math.odd(n) then
3554 skipbytes(f,2)
3555 end
3556 else
3557 return
3558 end
3559 subtable.offsets = offsets
3560 subtable.metrics = metrics
3561 end
3562
3563
3564
3565
3566 local default = { width = 0, height = 0 }
3567 local glyphs = fontdata.glyphs
3568 local delayed = CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 or fonts.handlers.typethree
3569
3570 for index, subtable in sortedhash(shapes) do
3571 if type(subtable) == "table" then
3572 local data = nil
3573 local size = nil
3574 local metrics = default
3575 local format = subtable.format
3576 local offset = subtable.offsets[index]
3577 setposition(f,offset)
3578 if format == 17 then
3579 metrics = getsmallmetrics(f)
3580 size = true
3581 elseif format == 18 then
3582 metrics = getbigmetrics(f)
3583 size = true
3584 elseif format == 19 then
3585 metrics = subtable.metrics
3586 size = true
3587 else
3588
3589 end
3590 if size then
3591 size = readulong(f)
3592 if delayed then
3593 offset = getposition(f)
3594 data = nil
3595 else
3596 offset = nil
3597 data = readstring(f,size)
3598 size = nil
3599 end
3600 else
3601 offset = nil
3602 end
3603 local x = metrics.width
3604 local y = metrics.height
3605 shapes[index] = {
3606 x = x,
3607 y = y,
3608 o = offset,
3609 s = size,
3610 data = data,
3611 }
3612
3613
3614 local glyph = glyphs[index]
3615 if not glyph.boundingbox then
3616 local width = glyph.width
3617 local height = width * y/x
3618 glyph.boundingbox = { 0, 0, width, height }
3619 end
3620 else
3621 shapes[index] = {
3622 x = 0,
3623 y = 0,
3624 data = "",
3625 }
3626 end
3627 end
3628
3629 fontdata.pngshapes = shapes
3630 end
3631 end
3632
3633 function readers.cbdt(f,fontdata,specification)
3634
3635
3636
3637
3638
3639 end
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656end
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675function readers.stat(f,fontdata,specification)
3676 local tableoffset = gotodatatable(f,fontdata,"stat",true)
3677 if tableoffset then
3678 local extras = fontdata.extras
3679 local version = readulong(f)
3680 local axissize = readushort(f)
3681 local nofaxis = readushort(f)
3682 local axisoffset = readulong(f)
3683 local nofvalues = readushort(f)
3684 local valuesoffset = readulong(f)
3685 local fallbackname = extras[readushort(f)]
3686 local axis = { }
3687 local values = { }
3688 setposition(f,tableoffset+axisoffset)
3689 for i=1,nofaxis do
3690 local tag = readtag(f)
3691 axis[i] = {
3692 tag = tag,
3693 name = lower(extras[readushort(f)] or tag),
3694 ordering = readushort(f),
3695 variants = { }
3696 }
3697 end
3698
3699
3700
3701
3702
3703
3704 setposition(f,tableoffset+valuesoffset)
3705 for i=1,nofvalues do
3706 values[i] = readushort(f)
3707 end
3708 for i=1,nofvalues do
3709 setposition(f,tableoffset + valuesoffset + values[i])
3710 local format = readushort(f)
3711 local index = readushort(f) + 1
3712 local flags = readushort(f)
3713 local name = lower(extras[readushort(f)] or "no name")
3714 local value = readfixed(f)
3715 local variant
3716 if format == 1 then
3717 variant = {
3718 flags = flags,
3719 name = name,
3720 value = value,
3721 }
3722 elseif format == 2 then
3723 variant = {
3724 flags = flags,
3725 name = name,
3726 value = value,
3727 minimum = readfixed(f),
3728 maximum = readfixed(f),
3729 }
3730 elseif format == 3 then
3731 variant = {
3732 flags = flags,
3733 name = name,
3734 value = value,
3735 link = readfixed(f),
3736 }
3737 end
3738 insert(axis[index].variants,variant)
3739 end
3740 sort(axis,function(a,b)
3741 return a.ordering < b.ordering
3742 end)
3743 for i=1,#axis do
3744 local a = axis[i]
3745 sort(a.variants,function(a,b)
3746 return a.name < b.name
3747 end)
3748 a.ordering = nil
3749 end
3750 setvariabledata(fontdata,"designaxis",axis)
3751 setvariabledata(fontdata,"fallbackname",fallbackname)
3752 end
3753end
3754
3755
3756
3757
3758
3759function readers.avar(f,fontdata,specification)
3760 local tableoffset = gotodatatable(f,fontdata,"avar",true)
3761 if tableoffset then
3762
3763 local function collect()
3764 local nofvalues = readushort(f)
3765 local values = { }
3766 local lastfrom = false
3767 local lastto = false
3768 for i=1,nofvalues do
3769 local from = read2dot14(f)
3770 local to = read2dot14(f)
3771 if lastfrom and from <= lastfrom then
3772
3773 elseif lastto and to >= lastto then
3774
3775 else
3776 values[#values+1] = { from, to }
3777 lastfrom, lastto = from, to
3778 end
3779 end
3780 nofvalues = #values
3781 if nofvalues > 2 then
3782 local some = values[1]
3783 if some[1] == -1 and some[2] == -1 then
3784 some = values[nofvalues]
3785 if some[1] == 1 and some[2] == 1 then
3786 for i=2,nofvalues-1 do
3787 some = values[i]
3788 if some[1] == 0 and some[2] == 0 then
3789 return values
3790 end
3791 end
3792 end
3793 end
3794 end
3795 return false
3796 end
3797
3798 local version = readulong(f)
3799 local reserved = readushort(f)
3800 local nofaxis = readushort(f)
3801 local segments = { }
3802 for i=1,nofaxis do
3803 segments[i] = collect()
3804 end
3805 setvariabledata(fontdata,"segments",segments)
3806 end
3807end
3808
3809function readers.fvar(f,fontdata,specification)
3810 local tableoffset = gotodatatable(f,fontdata,"fvar",true)
3811 if tableoffset then
3812 local version = readulong(f)
3813 local offsettoaxis = tableoffset + readushort(f)
3814 local reserved = skipshort(f)
3815
3816 local nofaxis = readushort(f)
3817 local sizeofaxis = readushort(f)
3818
3819 local nofinstances = readushort(f)
3820 local sizeofinstances = readushort(f)
3821
3822 local extras = fontdata.extras
3823 local axis = { }
3824 local instances = { }
3825
3826 setposition(f,offsettoaxis)
3827
3828 for i=1,nofaxis do
3829 axis[i] = {
3830 tag = readtag(f),
3831 minimum = readfixed(f),
3832 default = readfixed(f),
3833 maximum = readfixed(f),
3834 flags = readushort(f),
3835 name = lower(extras[readushort(f)] or "bad name"),
3836 }
3837 local n = sizeofaxis - 20
3838 if n > 0 then
3839 skipbytes(f,n)
3840 elseif n < 0 then
3841
3842 end
3843 end
3844
3845 local nofbytes = 2 + 2 + 2 + nofaxis * 4
3846 local readpsname = nofbytes <= sizeofinstances
3847 local skippable = sizeofinstances - nofbytes
3848 for i=1,nofinstances do
3849 local subfamid = readushort(f)
3850 local flags = readushort(f)
3851 local values = { }
3852 for i=1,nofaxis do
3853 values[i] = {
3854 axis = axis[i].tag,
3855 value = readfixed(f),
3856 }
3857 end
3858 local psnameid = readpsname and readushort(f) or 0xFFFF
3859 if subfamid == 2 or subfamid == 17 then
3860
3861 elseif subfamid == 0xFFFF then
3862 subfamid = nil
3863 elseif subfamid <= 256 or subfamid >= 32768 then
3864 subfamid = nil
3865 end
3866 if psnameid == 6 then
3867
3868 elseif psnameid == 0xFFFF then
3869 psnameid = nil
3870 elseif psnameid <= 256 or psnameid >= 32768 then
3871 psnameid = nil
3872 end
3873 instances[i] = {
3874
3875 subfamily = extras[subfamid],
3876 psname = psnameid and extras[psnameid] or nil,
3877 values = values,
3878 }
3879 if skippable > 0 then
3880 skipbytes(f,skippable)
3881 end
3882 end
3883 setvariabledata(fontdata,"axis",axis)
3884 setvariabledata(fontdata,"instances",instances)
3885 end
3886end
3887
3888function readers.hvar(f,fontdata,specification)
3889 local factors = specification.factors
3890 if not factors then
3891 return
3892 end
3893 local tableoffset = gotodatatable(f,fontdata,"hvar",specification.variable)
3894 if not tableoffset then
3895
3896 return
3897 end
3898
3899 local version = readulong(f)
3900 local variationoffset = tableoffset + readulong(f)
3901 local advanceoffset = tableoffset + readulong(f)
3902 local lsboffset = tableoffset + readulong(f)
3903 local rsboffset = tableoffset + readulong(f)
3904
3905 local regions = { }
3906 local variations = { }
3907 local innerindex = { }
3908 local outerindex = { }
3909 local deltas = { }
3910
3911 if variationoffset > 0 then
3912 regions, deltas = readvariationdata(f,variationoffset,factors)
3913 end
3914 if not regions then
3915
3916 return
3917 end
3918
3919 if advanceoffset > 0 then
3920
3921
3922
3923
3924
3925
3926
3927
3928 setposition(f,advanceoffset)
3929 local format = readushort(f)
3930 local mapcount = readushort(f)
3931 local entrysize = rshift(band(format,0x0030),4) + 1
3932 local nofinnerbits = band(format,0x000F) + 1
3933 local innermask = lshift(1,nofinnerbits) - 1
3934 local readcardinal = read_cardinal[entrysize]
3935 for i=0,mapcount-1 do
3936 local mapdata = readcardinal(f)
3937 outerindex[i] = rshift(mapdata,nofinnerbits)
3938 innerindex[i] = band(mapdata,innermask)
3939 end
3940
3941 setvariabledata(fontdata,"hvarwidths",true)
3942 local glyphs = fontdata.glyphs
3943 for i=0,fontdata.nofglyphs-1 do
3944 local glyph = glyphs[i]
3945 local width = glyph.width
3946 if width then
3947 local outer = outerindex[i] or 0
3948 local inner = innerindex[i] or i
3949 if outer and inner then
3950 local delta = deltas[outer+1]
3951 if delta then
3952 local d = delta.deltas[inner+1]
3953 if d then
3954 local scales = delta.scales
3955 local deltaw = 0
3956 for i=1,#scales do
3957 local di = d[i]
3958 if di then
3959 deltaw = deltaw + scales[i] * di
3960 else
3961 break
3962 end
3963 end
3964
3965
3966 glyph.width = width + round(deltaw)
3967 end
3968 end
3969 end
3970 end
3971 end
3972
3973 end
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985end
3986
3987function readers.vvar(f,fontdata,specification)
3988 if not specification.variable then
3989 return
3990 end
3991end
3992
3993function readers.mvar(f,fontdata,specification)
3994 local tableoffset = gotodatatable(f,fontdata,"mvar",specification.variable)
3995 if tableoffset then
3996 local version = readulong(f)
3997 local reserved = skipshort(f,1)
3998 local recordsize = readushort(f)
3999 local nofrecords = readushort(f)
4000 local offsettostore = tableoffset + readushort(f)
4001 local dimensions = { }
4002 local factors = specification.factors
4003 if factors then
4004 local regions, deltas = readvariationdata(f,offsettostore,factors)
4005 for i=1,nofrecords do
4006 local tag = readtag(f)
4007 local var = variabletags[tag]
4008 if var then
4009 local outer = readushort(f)
4010 local inner = readushort(f)
4011 local delta = deltas[outer+1]
4012 if delta then
4013 local d = delta.deltas[inner+1]
4014 if d then
4015 local scales = delta.scales
4016 local dd = 0
4017 for i=1,#scales do
4018 dd = dd + scales[i] * d[i]
4019 end
4020 var(fontdata,round(dd))
4021 end
4022 end
4023 else
4024 skipshort(f,2)
4025 end
4026 if recordsize > 8 then
4027 skipbytes(recordsize-8)
4028 end
4029 end
4030 end
4031
4032 end
4033end
4034
4035function readers.dsig(f,fontdata,specification)
4036
4037end
4038 |