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