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