1if not modules then modules = { } end modules [ ' font-oup ' ] = {
2 version = 1 . 001 ,
3 comment = " companion to font-ini.mkiv " ,
4 author = " Hans Hagen, PRAGMA-ADE, Hasselt NL " ,
5 copyright = " PRAGMA ADE / ConTeXt Development Team " ,
6 license = " see context related readme files "
7}
8
9local next , type = next , type
10local P , R , S = lpeg . P , lpeg . R , lpeg . S
11local lpegmatch = lpeg . match
12local insert , remove , copy , unpack = table . insert , table . remove , table . copy , table . unpack
13
14local formatters = string . formatters
15local sortedkeys = table . sortedkeys
16local sortedhash = table . sortedhash
17local tohash = table . tohash
18local setmetatableindex = table . setmetatableindex
19
20local report_error = logs . reporter ( " otf reader " , " error " )
21local report_markwidth = logs . reporter ( " otf reader " , " markwidth " )
22local report_cleanup = logs . reporter ( " otf reader " , " cleanup " )
23local report_optimizations = logs . reporter ( " otf reader " , " merges " )
24local report_unicodes = logs . reporter ( " otf reader " , " unicodes " )
25
26local trace_markwidth = false trackers . register ( " otf.markwidth " , function ( v ) trace_markwidth = v end )
27local trace_cleanup = false trackers . register ( " otf.cleanups " , function ( v ) trace_cleanups = v end )
28local trace_optimizations = false trackers . register ( " otf.optimizations " , function ( v ) trace_optimizations = v end )
29local trace_unicodes = false trackers . register ( " otf.unicodes " , function ( v ) trace_unicodes = v end )
30
31local readers = fonts . handlers . otf . readers
32local privateoffset = fonts . constructors and fonts . constructors . privateoffset or 0xF0000
33
34local f_private = formatters [ " P%05X " ]
35local f_unicode = formatters [ " U%05X " ]
36local f_index = formatters [ " I%05X " ]
37local f_character_y = formatters [ " %C " ]
38local f_character_n = formatters [ " [ %C ] " ]
39
40local check_duplicates = true
41local check_soft_hyphen = true
42
43directives . register ( " otf.checksofthyphen " , function ( v )
44 check_soft_hyphen = v
45end )
46
47local function replaced ( list , index , replacement )
48 if type ( list ) = = " number " then
49 return replacement
50 elseif type ( replacement ) = = " table " then
51 local t = { }
52 local n = index -1
53 for i = 1 , n do
54 t [ i ] = list [ i ]
55 end
56 for i = 1 , # replacement do
57 n = n + 1
58 t [ n ] = replacement [ i ]
59 end
60 for i = index + 1 , # list do
61 n = n + 1
62 t [ n ] = list [ i ]
63 end
64 else
65 list [ index ] = replacement
66 return list
67 end
68end
69
70local function unifyresources ( fontdata , indices )
71 local descriptions = fontdata . descriptions
72 local resources = fontdata . resources
73 if not descriptions or not resources then
74 return
75 end
76
77 local nofindices = # indices
78
79 local variants = fontdata . resources . variants
80 if variants then
81 for selector , unicodes in next , variants do
82 for unicode , index in next , unicodes do
83 unicodes [ unicode ] = indices [ index ]
84 end
85 end
86 end
87
88 local function remark ( marks )
89 if marks then
90 local newmarks = { }
91 for k , v in next , marks do
92 local u = indices [ k ]
93 if u then
94 newmarks [ u ] = v
95 elseif trace_optimizations then
96 report_optimizations ( " discarding mark %i " , k )
97 end
98 end
99 return newmarks
100 end
101 end
102
103 local marks = resources . marks
104 if marks then
105 resources . marks = remark ( marks )
106 end
107
108 local markclasses = resources . markclasses
109 if markclasses then
110 for class , marks in next , markclasses do
111 markclasses [ class ] = remark ( marks )
112 end
113 end
114
115 local marksets = resources . marksets
116 if marksets then
117 for class , marks in next , marksets do
118 marksets [ class ] = remark ( marks )
119 end
120 end
121
122 local done = { }
123
124 local duplicates = check_duplicates and resources . duplicates
125 if duplicates and not next ( duplicates ) then
126 duplicates = false
127 end
128
129 local function recover ( cover )
130 for i = 1 , # cover do
131 local c = cover [ i ]
132 if not done [ c ] then
133 local t = { }
134 for k , v in next , c do
135 local ug = indices [ k ]
136 if ug then
137 t [ ug ] = v
138 else
139 report_error ( " case %i, bad index in unifying %s: %s of %s " , 1 , " coverage " , k , nofindices )
140 end
141 end
142 cover [ i ] = t
143 done [ c ] = d
144 end
145 end
146 end
147
148 local function recursed ( c , kind )
149 local t = { }
150 for g , d in next , c do
151 if type ( d ) = = " table " then
152 local ug = indices [ g ]
153 if ug then
154 t [ ug ] = recursed ( d , kind )
155 else
156 report_error ( " case %i, bad index in unifying %s: %s of %s " , 1 , kind , g , nofindices )
157 end
158 else
159 t [ g ] = indices [ d ]
160 end
161 end
162 return t
163 end
164
165
166
167
168 local function unifythem ( sequences )
169 if not sequences then
170 return
171 end
172 for i = 1 , # sequences do
173 local sequence = sequences [ i ]
174 local kind = sequence . type
175 local steps = sequence . steps
176 local features = sequence . features
177 if steps then
178 for i = 1 , # steps do
179 local step = steps [ i ]
180 if kind = = " gsub_single " then
181 local c = step . coverage
182 if c then
183 local t1 = done [ c ]
184 if not t1 then
185 t1 = { }
186 if duplicates then
187 for g1 , d1 in next , c do
188 local ug1 = indices [ g1 ]
189 if ug1 then
190 local ud1 = indices [ d1 ]
191 if ud1 then
192 t1 [ ug1 ] = ud1
193 local dg1 = duplicates [ ug1 ]
194 if dg1 then
195 for u in next , dg1 do
196 t1 [ u ] = ud1
197 end
198 end
199 else
200 report_error ( " case %i, bad index in unifying %s: %s of %s " , 3 , kind , d1 , nofindices )
201 end
202 else
203 report_error ( " case %i, bad index in unifying %s: %s of %s " , 1 , kind , g1 , nofindices )
204 end
205 end
206 else
207 for g1 , d1 in next , c do
208 local ug1 = indices [ g1 ]
209 if ug1 then
210 t1 [ ug1 ] = indices [ d1 ]
211 else
212 report_error ( " fuzzy case %i in unifying %s: %i " , 2 , kind , g1 )
213 end
214 end
215 end
216 done [ c ] = t1
217 end
218 step . coverage = t1
219 end
220 elseif kind = = " gpos_pair " then
221 local c = step . coverage
222 if c then
223 local t1 = done [ c ]
224 if not t1 then
225 t1 = { }
226 for g1 , d1 in next , c do
227 local ug1 = indices [ g1 ]
228 if ug1 then
229 local t2 = done [ d1 ]
230 if not t2 then
231 t2 = { }
232 for g2 , d2 in next , d1 do
233 local ug2 = indices [ g2 ]
234 if ug2 then
235 t2 [ ug2 ] = d2
236 else
237 report_error ( " case %i, bad index in unifying %s: %s of %s " , 1 , kind , g2 , nofindices , nofindices )
238 end
239 end
240 done [ d1 ] = t2
241 end
242 t1 [ ug1 ] = t2
243 else
244 report_error ( " case %i, bad index in unifying %s: %s of %s " , 2 , kind , g1 , nofindices )
245 end
246 end
247 done [ c ] = t1
248 end
249 step . coverage = t1
250 end
251 elseif kind = = " gsub_ligature " then
252 local c = step . coverage
253 if c then
254 step . coverage = recursed ( c , kind )
255 end
256 elseif kind = = " gsub_alternate " or kind = = " gsub_multiple " then
257 local c = step . coverage
258 if c then
259 local t1 = done [ c ]
260 if not t1 then
261 t1 = { }
262 if duplicates then
263 for g1 , d1 in next , c do
264 for i = 1 , # d1 do
265 local d1i = d1 [ i ]
266 local d1u = indices [ d1i ]
267 if d1u then
268 d1 [ i ] = d1u
269 else
270 report_error ( " case %i, bad index in unifying %s: %s of %s " , 1 , kind , i , d1i , nofindices )
271 end
272 end
273 local ug1 = indices [ g1 ]
274 if ug1 then
275 t1 [ ug1 ] = d1
276 local dg1 = duplicates [ ug1 ]
277 if dg1 then
278 for u in next , dg1 do
279 t1 [ u ] = copy ( d1 )
280 end
281 end
282 else
283 report_error ( " case %i, bad index in unifying %s: %s of %s " , 2 , kind , g1 , nofindices )
284 end
285 end
286 else
287 for g1 , d1 in next , c do
288 for i = 1 , # d1 do
289 local d1i = d1 [ i ]
290 local d1u = indices [ d1i ]
291 if d1u then
292 d1 [ i ] = d1u
293 else
294 report_error ( " case %i, bad index in unifying %s: %s of %s " , 2 , kind , d1i , nofindices )
295 end
296 end
297 t1 [ indices [ g1 ] ] = d1
298 end
299 end
300 done [ c ] = t1
301 end
302 step . coverage = t1
303 end
304 elseif kind = = " gpos_single " then
305 local c = step . coverage
306 if c then
307 local t1 = done [ c ]
308 if not t1 then
309 t1 = { }
310 if duplicates then
311 for g1 , d1 in next , c do
312 local ug1 = indices [ g1 ]
313 if ug1 then
314 t1 [ ug1 ] = d1
315 local dg1 = duplicates [ ug1 ]
316 if dg1 then
317 for u in next , dg1 do
318 t1 [ u ] = d1
319 end
320 end
321 else
322 report_error ( " case %i, bad index in unifying %s: %s of %s " , 1 , kind , g1 , nofindices )
323 end
324 end
325 else
326 for g1 , d1 in next , c do
327 local ug1 = indices [ g1 ]
328 if ug1 then
329 t1 [ ug1 ] = d1
330 else
331 report_error ( " case %i, bad index in unifying %s: %s of %s " , 2 , kind , g1 , nofindices )
332 end
333 end
334 end
335 done [ c ] = t1
336 end
337 step . coverage = t1
338 end
339 elseif kind = = " gpos_mark2base " or kind = = " gpos_mark2mark " or kind = = " gpos_mark2ligature " then
340 local c = step . coverage
341 if c then
342 local t1 = done [ c ]
343 if not t1 then
344 t1 = { }
345 for g1 , d1 in next , c do
346 local ug1 = indices [ g1 ]
347 if ug1 then
348 t1 [ ug1 ] = d1
349 else
350 report_error ( " case %i, bad index in unifying %s: %s of %s " , 1 , kind , g1 , nofindices )
351 end
352 end
353 done [ c ] = t1
354 end
355 step . coverage = t1
356 end
357 local c = step . baseclasses
358 if c then
359 local t1 = done [ c ]
360 if not t1 then
361 for g1 , d1 in next , c do
362 local t2 = done [ d1 ]
363 if not t2 then
364 t2 = { }
365 for g2 , d2 in next , d1 do
366 local ug2 = indices [ g2 ]
367 if ug2 then
368 t2 [ ug2 ] = d2
369 else
370 report_error ( " case %i, bad index in unifying %s: %s of %s " , 2 , kind , g2 , nofindices )
371 end
372 end
373 done [ d1 ] = t2
374 end
375 c [ g1 ] = t2
376 end
377 done [ c ] = c
378 end
379 end
380 elseif kind = = " gpos_cursive " then
381 local c = step . coverage
382 if c then
383 local t1 = done [ c ]
384 if not t1 then
385 t1 = { }
386 if duplicates then
387 for g1 , d1 in next , c do
388 local ug1 = indices [ g1 ]
389 if ug1 then
390 t1 [ ug1 ] = d1
391
392 local dg1 = duplicates [ ug1 ]
393 if dg1 then
394
395 for u in next , dg1 do
396 t1 [ u ] = copy ( d1 )
397 end
398 end
399 else
400 report_error ( " case %i, bad index in unifying %s: %s of %s " , 1 , kind , g1 , nofindices )
401 end
402 end
403 else
404 for g1 , d1 in next , c do
405 local ug1 = indices [ g1 ]
406 if ug1 then
407 t1 [ ug1 ] = d1
408 else
409 report_error ( " case %i, bad index in unifying %s: %s of %s " , 2 , kind , g1 , nofindices )
410 end
411 end
412 end
413 done [ c ] = t1
414 end
415 step . coverage = t1
416 end
417 end
418
419 local rules = step . rules
420 if rules then
421 for i = 1 , # rules do
422 local rule = rules [ i ]
423
424 local before = rule . before if before then recover ( before ) end
425 local after = rule . after if after then recover ( after ) end
426 local current = rule . current if current then recover ( current ) end
427
428 local replacements = rule . replacements
429 if replacements then
430 if not done [ replacements ] then
431 local r = { }
432 for k , v in next , replacements do
433 r [ indices [ k ] ] = indices [ v ]
434 end
435 rule . replacements = r
436 done [ replacements ] = r
437 end
438 end
439 end
440 end
441 end
442 end
443 end
444 end
445
446 unifythem ( resources . sequences )
447 unifythem ( resources . sublookups )
448end
449
450local function copyduplicates ( fontdata )
451 if check_duplicates then
452 local descriptions = fontdata . descriptions
453 local resources = fontdata . resources
454 local duplicates = resources . duplicates
455 if check_soft_hyphen then
456
457
458 local ds = descriptions [ 0xAD ]
459 if not ds or ds . width = = 0 then
460 if ds then
461 descriptions [ 0xAD ] = nil
462 if trace_unicodes then
463 report_unicodes ( " patching soft hyphen " )
464 end
465 else
466 if trace_unicodes then
467 report_unicodes ( " adding soft hyphen " )
468 end
469 end
470 if not duplicates then
471 duplicates = { }
472 resources . duplicates = duplicates
473 end
474 local dh = duplicates [ 0x2D ]
475 if dh then
476 dh [ # dh + 1 ] = { [ 0xAD ] = true }
477 else
478 duplicates [ 0x2D ] = { [ 0xAD ] = true }
479 end
480 end
481 end
482 if duplicates then
483 for u , d in next , duplicates do
484 local du = descriptions [ u ]
485 if du then
486 local t = { f_character_y ( u ) , " @ " , f_index ( du . index ) , " -> " }
487 local n = 0
488 local m = 25
489 for u in next , d do
490 if descriptions [ u ] then
491 if n < m then
492 t [ n + 4 ] = f_character_n ( u )
493 end
494 else
495 local c = copy ( du )
496 c . unicode = u
497 descriptions [ u ] = c
498 if n < m then
499 t [ n + 4 ] = f_character_y ( u )
500 end
501 end
502 n = n + 1
503 end
504 if trace_unicodes then
505 if n < = m then
506 report_unicodes ( " %i : % t " , n , t )
507 else
508 report_unicodes ( " %i : % t ... " , n , t )
509 end
510 end
511 else
512
513 end
514 end
515 end
516 end
517end
518
519local ignore = {
520 [ " notdef " ] = true ,
521 [ " .notdef " ] = true ,
522 [ " null " ] = true ,
523 [ " .null " ] = true ,
524 [ " nonmarkingreturn " ] = true ,
525}
526
527
528local function checklookups ( fontdata , missing , nofmissing )
529 local descriptions = fontdata . descriptions
530 local resources = fontdata . resources
531 if missing and nofmissing and nofmissing < = 0 then
532 return
533 end
534
535 local singles = { }
536 local alternates = { }
537 local ligatures = { }
538
539 if not missing then
540 missing = { }
541 nofmissing = 0
542 for u , d in next , descriptions do
543 if not d . unicode then
544 nofmissing = nofmissing + 1
545 missing [ u ] = true
546 end
547 end
548 end
549 local function collectthem ( sequences )
550 if not sequences then
551 return
552 end
553 for i = 1 , # sequences do
554 local sequence = sequences [ i ]
555 local kind = sequence . type
556 local steps = sequence . steps
557 if steps then
558 for i = 1 , # steps do
559 local step = steps [ i ]
560 if kind = = " gsub_single " then
561 local c = step . coverage
562 if c then
563 singles [ # singles + 1 ] = c
564 end
565 elseif kind = = " gsub_alternate " then
566 local c = step . coverage
567 if c then
568 alternates [ # alternates + 1 ] = c
569 end
570 elseif kind = = " gsub_ligature " then
571 local c = step . coverage
572 if c then
573 ligatures [ # ligatures + 1 ] = c
574 end
575 end
576 end
577 end
578 end
579 end
580
581 collectthem ( resources . sequences )
582 collectthem ( resources . sublookups )
583
584 local loops = 0
585 while true do
586 loops = loops + 1
587 local old = nofmissing
588 for i = 1 , # singles do
589 local c = singles [ i ]
590 for g1 , g2 in next , c do
591 if missing [ g1 ] then
592 local u2 = descriptions [ g2 ] . unicode
593 if u2 then
594 missing [ g1 ] = false
595 descriptions [ g1 ] . unicode = u2
596 nofmissing = nofmissing - 1
597 end
598 end
599 if missing [ g2 ] then
600 local u1 = descriptions [ g1 ] . unicode
601 if u1 then
602 missing [ g2 ] = false
603 descriptions [ g2 ] . unicode = u1
604 nofmissing = nofmissing - 1
605 end
606 end
607 end
608 end
609 for i = 1 , # alternates do
610 local c = alternates [ i ]
611
612 for g1 , d1 in next , c do
613 if missing [ g1 ] then
614 for i = 1 , # d1 do
615 local g2 = d1 [ i ]
616 local u2 = descriptions [ g2 ] . unicode
617 if u2 then
618 missing [ g1 ] = false
619 descriptions [ g1 ] . unicode = u2
620 nofmissing = nofmissing - 1
621 end
622 end
623 end
624 if not missing [ g1 ] then
625 for i = 1 , # d1 do
626 local g2 = d1 [ i ]
627 if missing [ g2 ] then
628 local u1 = descriptions [ g1 ] . unicode
629 if u1 then
630 missing [ g2 ] = false
631 descriptions [ g2 ] . unicode = u1
632 nofmissing = nofmissing - 1
633 end
634 end
635 end
636 end
637 end
638 end
639 if nofmissing < = 0 then
640 if trace_unicodes then
641 report_unicodes ( " all missings done in %s loops " , loops )
642 end
643 return
644 elseif old = = nofmissing then
645 break
646 end
647 end
648
649 local t , n
650
651 local function recursed ( c )
652 for g , d in next , c do
653 if g ~ = " ligature " then
654 local u = descriptions [ g ] . unicode
655 if u then
656 n = n + 1
657 t [ n ] = u
658 recursed ( d )
659 n = n - 1
660 end
661 elseif missing [ d ] then
662 local l = { }
663 local m = 0
664 for i = 1 , n do
665 local u = t [ i ]
666 if type ( u ) = = " table " then
667 for i = 1 , # u do
668 m = m + 1
669 l [ m ] = u [ i ]
670 end
671 else
672 m = m + 1
673 l [ m ] = u
674 end
675 end
676 missing [ d ] = false
677 descriptions [ d ] . unicode = l
678 nofmissing = nofmissing - 1
679 end
680 end
681 end
682
683 if nofmissing > 0 then
684 t = { }
685 n = 0
686 local loops = 0
687 while true do
688 loops = loops + 1
689 local old = nofmissing
690 for i = 1 , # ligatures do
691 recursed ( ligatures [ i ] )
692 end
693 if nofmissing < = 0 then
694 if trace_unicodes then
695 report_unicodes ( " all missings done in %s loops " , loops )
696 end
697 return
698 elseif old = = nofmissing then
699 break
700 end
701 end
702 t = nil
703 n = 0
704 end
705
706 if trace_unicodes and nofmissing > 0 then
707 local done = { }
708 for i , r in next , missing do
709 if r then
710 local data = descriptions [ i ]
711 local name = data and data . name or f_index ( i )
712 if not ignore [ name ] then
713 done [ name ] = true
714 end
715 end
716 end
717 if next ( done ) then
718 report_unicodes ( " not unicoded: % t " , sortedkeys ( done ) )
719 end
720 end
721end
722
723local firstprivate = fonts . privateoffsets and fonts . privateoffsets . textbase or 0xF0000
724local puafirst = 0xE000
725local pualast = 0xF8FF
726
727local function unifymissing ( fontdata )
728 if not fonts . mappings then
729 require ( " font-map " )
730 require ( " font-agl " )
731 end
732 local unicodes = { }
733 local resources = fontdata . resources
734 resources . unicodes = unicodes
735 for unicode , d in next , fontdata . descriptions do
736 if unicode < privateoffset then
737 if unicode > = puafirst and unicode < = pualast then
738
739 else
740 local name = d . name
741 if name then
742 unicodes [ name ] = unicode
743 end
744 end
745 else
746
747 end
748 end
749 fonts . mappings . addtounicode ( fontdata , fontdata . filename , checklookups )
750 resources . unicodes = nil
751end
752
753local function unifyglyphs ( fontdata , usenames )
754 local private = fontdata . private or privateoffset
755 local glyphs = fontdata . glyphs
756 local indices = { }
757 local descriptions = { }
758 local names = usenames and { }
759 local resources = fontdata . resources
760 local zero = glyphs [ 0 ]
761 local zerocode = zero . unicode
762 if not zerocode then
763 zerocode = private
764 zero . unicode = zerocode
765 private = private + 1
766 end
767 descriptions [ zerocode ] = zero
768 if names then
769 local name = glyphs [ 0 ] . name or f_private ( zerocode )
770 indices [ 0 ] = name
771 names [ name ] = zerocode
772 else
773 indices [ 0 ] = zerocode
774 end
775
776 if names then
777
778 for index = 1 , # glyphs do
779 local glyph = glyphs [ index ]
780 local unicode = glyph . unicode
781 if not unicode then
782 unicode = private
783 local name = glyph . name or f_private ( unicode )
784 indices [ index ] = name
785 names [ name ] = unicode
786 private = private + 1
787 elseif unicode > = firstprivate then
788 unicode = private
789 local name = glyph . name or f_private ( unicode )
790 indices [ index ] = name
791 names [ name ] = unicode
792 private = private + 1
793 elseif unicode > = puafirst and unicode < = pualast then
794 local name = glyph . name or f_private ( unicode )
795 indices [ index ] = name
796 names [ name ] = unicode
797 elseif descriptions [ unicode ] then
798 unicode = private
799 local name = glyph . name or f_private ( unicode )
800 indices [ index ] = name
801 names [ name ] = unicode
802 private = private + 1
803 else
804 local name = glyph . name or f_unicode ( unicode )
805 indices [ index ] = name
806 names [ name ] = unicode
807 end
808 descriptions [ unicode ] = glyph
809 end
810 elseif trace_unicodes then
811 for index = 1 , # glyphs do
812 local glyph = glyphs [ index ]
813 local unicode = glyph . unicode
814 if not unicode then
815 unicode = private
816 indices [ index ] = unicode
817 private = private + 1
818 elseif unicode > = firstprivate then
819 local name = glyph . name
820 if name then
821 report_unicodes ( " moving glyph %a indexed %05X from private %U to %U " , name , index , unicode , private )
822 else
823 report_unicodes ( " moving glyph indexed %05X from private %U to %U " , index , unicode , private )
824 end
825 unicode = private
826 indices [ index ] = unicode
827 private = private + 1
828 elseif unicode > = puafirst and unicode < = pualast then
829 local name = glyph . name
830 if name then
831 report_unicodes ( " keeping private unicode %U for glyph %a indexed %05X " , unicode , name , index )
832 else
833 report_unicodes ( " keeping private unicode %U for glyph indexed %05X " , unicode , index )
834 end
835 indices [ index ] = unicode
836 elseif descriptions [ unicode ] then
837 local name = glyph . name
838 if name then
839 report_unicodes ( " assigning duplicate unicode %U to %U for glyph %a indexed %05X " , unicode , private , name , index )
840 else
841 report_unicodes ( " assigning duplicate unicode %U to %U for glyph indexed %05X " , unicode , private , index )
842 end
843 unicode = private
844 indices [ index ] = unicode
845 private = private + 1
846 else
847 indices [ index ] = unicode
848 end
849 descriptions [ unicode ] = glyph
850 end
851 else
852 for index = 1 , # glyphs do
853 local glyph = glyphs [ index ]
854 local unicode = glyph . unicode
855 if not unicode then
856 unicode = private
857 indices [ index ] = unicode
858 private = private + 1
859 elseif unicode > = firstprivate then
860 local name = glyph . name
861 unicode = private
862 indices [ index ] = unicode
863 private = private + 1
864 elseif unicode > = puafirst and unicode < = pualast then
865 local name = glyph . name
866 indices [ index ] = unicode
867 elseif descriptions [ unicode ] then
868 local name = glyph . name
869 unicode = private
870 indices [ index ] = unicode
871 private = private + 1
872 else
873 indices [ index ] = unicode
874 end
875 descriptions [ unicode ] = glyph
876 end
877 end
878
879 for index = 1 , # glyphs do
880 local math = glyphs [ index ] . math
881 if math then
882 local list = math . vparts
883 if list then
884 for i = 1 , # list do local l = list [ i ] l . glyph = indices [ l . glyph ] end
885 end
886 local list = math . hparts
887 if list then
888 for i = 1 , # list do local l = list [ i ] l . glyph = indices [ l . glyph ] end
889 end
890 local list = math . vvariants
891 if list then
892
893 for i = 1 , # list do list [ i ] = indices [ list [ i ] ] end
894 end
895 local list = math . hvariants
896 if list then
897
898 for i = 1 , # list do list [ i ] = indices [ list [ i ] ] end
899 end
900 end
901 end
902
903 local colorpalettes = resources . colorpalettes
904 if colorpalettes then
905 for index = 1 , # glyphs do
906 local colors = glyphs [ index ] . colors
907 if colors then
908 for i = 1 , # colors do
909 local c = colors [ i ]
910 c . slot = indices [ c . slot ]
911 end
912 end
913 end
914 end
915
916 fontdata . private = private
917 fontdata . glyphs = nil
918 fontdata . names = names
919 fontdata . descriptions = descriptions
920 fontdata . hashmethod = hashmethod
921
922 return indices , names
923end
924
925local p_crappyname do
926
927 local p_hex = R ( " af " , " AF " , " 09 " )
928 local p_digit = R ( " 09 " )
929 local p_done = S ( " ._- " ) ^ 0 + P ( -1 )
930 local p_alpha = R ( " az " , " AZ " )
931 local p_ALPHA = R ( " AZ " )
932
933 p_crappyname = (
934
935 lpeg . utfchartabletopattern ( { " uni " , " u " } , true )
936 * S ( " Xx_ " ) ^ 0
937 * p_hex ^ 1
938
939 + lpeg . utfchartabletopattern ( { " identity " , " glyph " , " jamo " } , true )
940 * p_hex ^ 1
941
942 + lpeg . utfchartabletopattern ( { " index " , " afii " } , true )
943 * p_digit ^ 1
944
945 + p_digit
946 * p_hex ^ 3
947 + p_alpha
948 * p_digit ^ 1
949
950 + P ( " aj " )
951 * p_digit ^ 1
952 + P ( " eh_ " )
953 * ( p_digit ^ 1 + p_ALPHA * p_digit ^ 1 )
954 + ( 1 - P ( " _ " ) ) ^ 1
955 * P ( " _uni " )
956 * p_hex ^ 1
957 + P ( " _ " )
958 * P ( 1 ) ^ 1
959 ) * p_done
960
961end
962
963
964
965
966local forcekeep = false
967
968directives . register ( " otf.keepnames " , function ( v )
969 report_cleanup ( " keeping weird glyph names, expect larger files and more memory usage " )
970 forcekeep = v
971end )
972
973local function stripredundant ( fontdata )
974 local descriptions = fontdata . descriptions
975 if descriptions then
976 local n = 0
977 local c = 0
978
979 if ( not context and fonts . privateoffsets . keepnames ) or forcekeep then
980 for unicode , d in next , descriptions do
981 if d . class = = " base " then
982 d . class = nil
983 c = c + 1
984 end
985 end
986 else
987 for unicode , d in next , descriptions do
988 local name = d . name
989 if name and lpegmatch ( p_crappyname , name ) then
990 d . name = nil
991 n = n + 1
992 end
993 if d . class = = " base " then
994 d . class = nil
995 c = c + 1
996 end
997 end
998 end
999 if trace_cleanup then
1000 if n > 0 then
1001 report_cleanup ( " %s bogus names removed (verbose unicode) " , n )
1002 end
1003 if c > 0 then
1004 report_cleanup ( " %s base class tags removed (default is base) " , c )
1005 end
1006 end
1007 end
1008end
1009
1010readers . stripredundant = stripredundant
1011
1012function readers . getcomponents ( fontdata )
1013 local resources = fontdata . resources
1014 if resources then
1015 local sequences = resources . sequences
1016 if sequences then
1017 local collected = { }
1018 for i = 1 , # sequences do
1019 local sequence = sequences [ i ]
1020 if sequence . type = = " gsub_ligature " then
1021 local steps = sequence . steps
1022 if steps then
1023 local l = { }
1024 local function traverse ( p , k , v )
1025 if k = = " ligature " then
1026 collected [ v ] = { unpack ( l ) }
1027 else
1028 insert ( l , k )
1029 for k , vv in next , v do
1030 traverse ( p , k , vv )
1031 end
1032 remove ( l )
1033 end
1034 end
1035 for i = 1 , # steps do
1036
1037 local c = steps [ i ] . coverage
1038 if c then
1039 for k , v in next , c do
1040 traverse ( k , k , v )
1041 end
1042 end
1043 end
1044 end
1045 end
1046 end
1047 if next ( collected ) then
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058 while true do
1059 local done = false
1060 for k , v in next , collected do
1061 for i = 1 , # v do
1062 local vi = v [ i ]
1063 if vi = = k then
1064
1065 collected [ k ] = nil
1066 break
1067 else
1068 local c = collected [ vi ]
1069 if c then
1070 done = true
1071 local t = { }
1072 local n = i - 1
1073 for j = 1 , n do
1074 t [ j ] = v [ j ]
1075 end
1076 for j = 1 , # c do
1077 n = n + 1
1078 t [ n ] = c [ j ]
1079 end
1080 for j = i + 1 , # v do
1081 n = n + 1
1082 t [ n ] = v [ j ]
1083 end
1084 collected [ k ] = t
1085 break
1086 end
1087 end
1088 end
1089 end
1090 if not done then
1091 break
1092 end
1093 end
1094 return collected
1095 end
1096 end
1097 end
1098end
1099
1100readers . unifymissing = unifymissing
1101
1102function readers . rehash ( fontdata , hashmethod )
1103 if not ( fontdata and fontdata . glyphs ) then
1104 return
1105 end
1106 if hashmethod = = " indices " then
1107 fontdata . hashmethod = " indices "
1108 elseif hashmethod = = " names " then
1109 fontdata . hashmethod = " names "
1110 local indices = unifyglyphs ( fontdata , true )
1111 unifyresources ( fontdata , indices )
1112 copyduplicates ( fontdata )
1113 unifymissing ( fontdata )
1114
1115 else
1116 fontdata . hashmethod = " unicodes "
1117 local indices = unifyglyphs ( fontdata )
1118 unifyresources ( fontdata , indices )
1119 copyduplicates ( fontdata )
1120 unifymissing ( fontdata )
1121 stripredundant ( fontdata )
1122 end
1123
1124end
1125
1126function readers . checkhash ( fontdata )
1127 local hashmethod = fontdata . hashmethod
1128 if hashmethod = = " unicodes " then
1129 fontdata . names = nil
1130 elseif hashmethod = = " names " and fontdata . names then
1131 unifyresources ( fontdata , fontdata . names )
1132 copyduplicates ( fontdata )
1133 fontdata . hashmethod = " unicodes "
1134 fontdata . names = nil
1135 else
1136 readers . rehash ( fontdata , " unicodes " )
1137 end
1138end
1139
1140function readers . addunicodetable ( fontdata )
1141 local resources = fontdata . resources
1142 local unicodes = resources . unicodes
1143 if not unicodes then
1144 local descriptions = fontdata . descriptions
1145 if descriptions then
1146 unicodes = { }
1147 resources . unicodes = unicodes
1148 for u , d in next , descriptions do
1149 local n = d . name
1150 if n then
1151 unicodes [ n ] = u
1152 end
1153 end
1154 end
1155 end
1156end
1157
1158
1159
1160local concat , sort = table . concat , table . sort
1161local next , type , tostring = next , type , tostring
1162
1163local criterium = 1
1164local threshold = 0
1165
1166local trace_packing = false trackers . register ( " otf.packing " , function ( v ) trace_packing = v end )
1167local trace_loading = false trackers . register ( " otf.loading " , function ( v ) trace_loading = v end )
1168
1169local report_otf = logs . reporter ( " fonts " , " otf loading " )
1170
1171local function tabstr_normal ( t )
1172 local s = { }
1173 local n = 0
1174 for k , v in next , t do
1175 n = n + 1
1176 if type ( v ) = = " table " then
1177 s [ n ] = k . . " > " . . tabstr_normal ( v )
1178 elseif v = = true then
1179 s [ n ] = k . . " + "
1180 elseif v then
1181 s [ n ] = k . . " = " . . v
1182 else
1183 s [ n ] = k . . " - "
1184 end
1185 end
1186 if n = = 0 then
1187 return " "
1188 elseif n = = 1 then
1189 return s [ 1 ]
1190 else
1191 sort ( s )
1192 return concat ( s , " , " )
1193 end
1194end
1195
1196local function tabstr_flat ( t )
1197 local s = { }
1198 local n = 0
1199 for k , v in next , t do
1200 n = n + 1
1201 s [ n ] = k . . " = " . . v
1202 end
1203 if n = = 0 then
1204 return " "
1205 elseif n = = 1 then
1206 return s [ 1 ]
1207 else
1208 sort ( s )
1209 return concat ( s , " , " )
1210 end
1211end
1212
1213local function tabstr_mixed ( t )
1214 local s = { }
1215 local n = # t
1216 if n = = 0 then
1217 return " "
1218 elseif n = = 1 then
1219 local k = t [ 1 ]
1220 if k = = true then
1221 return " ++ "
1222 elseif k = = false then
1223 return " -- "
1224 else
1225 return tostring ( k )
1226 end
1227 else
1228 for i = 1 , n do
1229 local k = t [ i ]
1230 if k = = true then
1231 s [ i ] = " ++ "
1232 elseif k = = false then
1233 s [ i ] = " -- "
1234 else
1235 s [ i ] = k
1236 end
1237 end
1238 return concat ( s , " , " )
1239 end
1240end
1241
1242local function tabstr_boolean ( t )
1243 local s = { }
1244 local n = 0
1245 for k , v in next , t do
1246 n = n + 1
1247 if v then
1248 s [ n ] = k . . " + "
1249 else
1250 s [ n ] = k . . " - "
1251 end
1252 end
1253 if n = = 0 then
1254 return " "
1255 elseif n = = 1 then
1256 return s [ 1 ]
1257 else
1258 sort ( s )
1259 return concat ( s , " , " )
1260 end
1261end
1262
1263
1264
1265
1266
1267
1268function readers . pack ( data )
1269
1270 if data then
1271
1272 local h , t , c = { } , { } , { }
1273 local hh , tt , cc = { } , { } , { }
1274 local nt , ntt = 0 , 0
1275
1276 local function pack_normal ( v )
1277 local tag = tabstr_normal ( v )
1278 local ht = h [ tag ]
1279 if ht then
1280 c [ ht ] = c [ ht ] + 1
1281 return ht
1282 else
1283 nt = nt + 1
1284 t [ nt ] = v
1285 h [ tag ] = nt
1286 c [ nt ] = 1
1287 return nt
1288 end
1289 end
1290
1291 local function pack_normal_cc ( v )
1292 local tag = tabstr_normal ( v )
1293 local ht = h [ tag ]
1294 if ht then
1295 c [ ht ] = c [ ht ] + 1
1296 return ht
1297 else
1298 v [ 1 ] = 0
1299 nt = nt + 1
1300 t [ nt ] = v
1301 h [ tag ] = nt
1302 c [ nt ] = 1
1303 return nt
1304 end
1305 end
1306
1307 local function pack_flat ( v )
1308 local tag = tabstr_flat ( v )
1309 local ht = h [ tag ]
1310 if ht then
1311 c [ ht ] = c [ ht ] + 1
1312 return ht
1313 else
1314 nt = nt + 1
1315 t [ nt ] = v
1316 h [ tag ] = nt
1317 c [ nt ] = 1
1318 return nt
1319 end
1320 end
1321
1322 local function pack_indexed ( v )
1323 local tag = concat ( v , " " )
1324 local ht = h [ tag ]
1325 if ht then
1326 c [ ht ] = c [ ht ] + 1
1327 return ht
1328 else
1329 nt = nt + 1
1330 t [ nt ] = v
1331 h [ tag ] = nt
1332 c [ nt ] = 1
1333 return nt
1334 end
1335 end
1336
1337 local function pack_mixed ( v )
1338 local tag = tabstr_mixed ( v )
1339 local ht = h [ tag ]
1340 if ht then
1341 c [ ht ] = c [ ht ] + 1
1342 return ht
1343 else
1344 nt = nt + 1
1345 t [ nt ] = v
1346 h [ tag ] = nt
1347 c [ nt ] = 1
1348 return nt
1349 end
1350 end
1351
1352
1353
1354
1355
1356 local function pack_boolean ( v )
1357 local tag = tabstr_boolean ( v )
1358 local ht = h [ tag ]
1359 if ht then
1360 c [ ht ] = c [ ht ] + 1
1361 return ht
1362 else
1363 nt = nt + 1
1364 t [ nt ] = v
1365 h [ tag ] = nt
1366 c [ nt ] = 1
1367 return nt
1368 end
1369 end
1370
1371 local function pack_final ( v )
1372
1373 if c [ v ] < = criterium then
1374 return t [ v ]
1375 else
1376
1377 local hv = hh [ v ]
1378 if hv then
1379 return hv
1380 else
1381 ntt = ntt + 1
1382 tt [ ntt ] = t [ v ]
1383 hh [ v ] = ntt
1384 cc [ ntt ] = c [ v ]
1385 return ntt
1386 end
1387 end
1388 end
1389
1390 local function pack_final_cc ( v )
1391
1392 if c [ v ] < = criterium then
1393 return t [ v ]
1394 else
1395
1396 local hv = hh [ v ]
1397 if hv then
1398 return hv
1399 else
1400 ntt = ntt + 1
1401 tt [ ntt ] = t [ v ]
1402 hh [ v ] = ntt
1403 cc [ ntt ] = c [ v ]
1404 return ntt
1405 end
1406 end
1407 end
1408
1409 local function success ( stage , pass )
1410 if nt = = 0 then
1411 if trace_loading or trace_packing then
1412 report_otf ( " pack quality: nothing to pack " )
1413 end
1414 return false
1415 elseif nt > = threshold then
1416 local one = 0
1417 local two = 0
1418 local rest = 0
1419 if pass = = 1 then
1420 for k , v in next , c do
1421 if v = = 1 then
1422 one = one + 1
1423 elseif v = = 2 then
1424 two = two + 1
1425 else
1426 rest = rest + 1
1427 end
1428 end
1429 else
1430 for k , v in next , cc do
1431 if v > 20 then
1432 rest = rest + 1
1433 elseif v > 10 then
1434 two = two + 1
1435 else
1436 one = one + 1
1437 end
1438 end
1439 data . tables = tt
1440 end
1441 if trace_loading or trace_packing then
1442 report_otf ( " pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s) " ,
1443 stage , pass , one + two + rest , one , two , rest , criterium )
1444 end
1445 return true
1446 else
1447 if trace_loading or trace_packing then
1448 report_otf ( " pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s) " ,
1449 stage , pass , nt , threshold )
1450 end
1451 return false
1452 end
1453 end
1454
1455 local function packers ( pass )
1456 if pass = = 1 then
1457 return pack_normal , pack_indexed , pack_flat , pack_boolean , pack_mixed , pack_normal_cc
1458 else
1459 return pack_final , pack_final , pack_final , pack_final , pack_final , pack_final_cc
1460 end
1461 end
1462
1463 local resources = data . resources
1464 local sequences = resources . sequences
1465 local sublookups = resources . sublookups
1466 local features = resources . features
1467 local palettes = resources . colorpalettes
1468 local variable = resources . variabledata
1469
1470 local chardata = characters and characters . data
1471 local descriptions = data . descriptions or data . glyphs
1472
1473 if not descriptions then
1474 return
1475 end
1476
1477 for pass = 1 , 2 do
1478
1479 if trace_packing then
1480 report_otf ( " start packing: stage 1, pass %s " , pass )
1481 end
1482
1483 local pack_normal , pack_indexed , pack_flat , pack_boolean , pack_mixed , pack_normal_cc = packers ( pass )
1484
1485 for unicode , description in next , descriptions do
1486 local boundingbox = description . boundingbox
1487 if boundingbox then
1488 description . boundingbox = pack_indexed ( boundingbox )
1489 end
1490 local math = description . math
1491 if math then
1492 local kerns = math . kerns
1493 if kerns then
1494 for tag , kern in next , kerns do
1495 kerns [ tag ] = pack_normal ( kern )
1496 end
1497 end
1498 end
1499
1500
1501
1502
1503
1504
1505
1506
1507 end
1508
1509 local function packthem ( sequences )
1510 for i = 1 , # sequences do
1511 local sequence = sequences [ i ]
1512 local kind = sequence . type
1513 local steps = sequence . steps
1514 local order = sequence . order
1515 local features = sequence . features
1516 local flags = sequence . flags
1517 if steps then
1518 for i = 1 , # steps do
1519 local step = steps [ i ]
1520 if kind = = " gpos_pair " then
1521 local c = step . coverage
1522 if c then
1523 if step . format ~ = " pair " then
1524 for g1 , d1 in next , c do
1525 c [ g1 ] = pack_normal ( d1 )
1526 end
1527 elseif step . shared then
1528
1529
1530
1531
1532 local shared = { }
1533 for g1 , d1 in next , c do
1534 for g2 , d2 in next , d1 do
1535 if not shared [ d2 ] then
1536 local f = d2 [ 1 ] if f and f ~ = true then d2 [ 1 ] = pack_indexed ( f ) end
1537 local s = d2 [ 2 ] if s and s ~ = true then d2 [ 2 ] = pack_indexed ( s ) end
1538 shared [ d2 ] = true
1539 end
1540 end
1541 end
1542 if pass = = 2 then
1543 step . shared = nil
1544 end
1545 else
1546 for g1 , d1 in next , c do
1547 for g2 , d2 in next , d1 do
1548 local f = d2 [ 1 ] if f and f ~ = true then d2 [ 1 ] = pack_indexed ( f ) end
1549 local s = d2 [ 2 ] if s and s ~ = true then d2 [ 2 ] = pack_indexed ( s ) end
1550 end
1551 end
1552 end
1553 end
1554 elseif kind = = " gpos_single " then
1555 local c = step . coverage
1556 if c then
1557 if step . format = = " single " then
1558 for g1 , d1 in next , c do
1559 if d1 and d1 ~ = true then
1560 c [ g1 ] = pack_indexed ( d1 )
1561 end
1562 end
1563 else
1564 step . coverage = pack_normal ( c )
1565 end
1566 end
1567 elseif kind = = " gpos_cursive " then
1568 local c = step . coverage
1569 if c then
1570 for g1 , d1 in next , c do
1571 local f = d1 [ 2 ] if f then d1 [ 2 ] = pack_indexed ( f ) end
1572 local s = d1 [ 3 ] if s then d1 [ 3 ] = pack_indexed ( s ) end
1573 end
1574 end
1575 elseif kind = = " gpos_mark2base " or kind = = " gpos_mark2mark " then
1576 local c = step . baseclasses
1577 if c then
1578 for g1 , d1 in next , c do
1579 for g2 , d2 in next , d1 do
1580 d1 [ g2 ] = pack_indexed ( d2 )
1581 end
1582 end
1583 end
1584 local c = step . coverage
1585 if c then
1586 for g1 , d1 in next , c do
1587 d1 [ 2 ] = pack_indexed ( d1 [ 2 ] )
1588 end
1589 end
1590 elseif kind = = " gpos_mark2ligature " then
1591 local c = step . baseclasses
1592 if c then
1593 for g1 , d1 in next , c do
1594 for g2 , d2 in next , d1 do
1595 for g3 , d3 in next , d2 do
1596 d2 [ g3 ] = pack_indexed ( d3 )
1597 end
1598 end
1599 end
1600 end
1601 local c = step . coverage
1602 if c then
1603 for g1 , d1 in next , c do
1604 d1 [ 2 ] = pack_indexed ( d1 [ 2 ] )
1605 end
1606 end
1607 end
1608
1609 local rules = step . rules
1610 if rules then
1611 for i = 1 , # rules do
1612 local rule = rules [ i ]
1613 local r = rule . before if r then for i = 1 , # r do r [ i ] = pack_boolean ( r [ i ] ) end end
1614 local r = rule . after if r then for i = 1 , # r do r [ i ] = pack_boolean ( r [ i ] ) end end
1615 local r = rule . current if r then for i = 1 , # r do r [ i ] = pack_boolean ( r [ i ] ) end end
1616
1617 local r = rule . replacements if r then rule . replacements = pack_flat ( r ) end
1618 end
1619 end
1620 end
1621 end
1622 if order then
1623 sequence . order = pack_indexed ( order )
1624 end
1625 if features then
1626 for script , feature in next , features do
1627 features [ script ] = pack_normal ( feature )
1628 end
1629 end
1630 if flags then
1631 sequence . flags = pack_normal ( flags )
1632 end
1633 end
1634 end
1635
1636 if sequences then
1637 packthem ( sequences )
1638 end
1639
1640 if sublookups then
1641 packthem ( sublookups )
1642 end
1643
1644 if features then
1645 for k , list in next , features do
1646 for feature , spec in next , list do
1647 list [ feature ] = pack_normal ( spec )
1648 end
1649 end
1650 end
1651
1652 if palettes then
1653 for i = 1 , # palettes do
1654 local p = palettes [ i ]
1655 for j = 1 , # p do
1656 p [ j ] = pack_indexed ( p [ j ] )
1657 end
1658 end
1659
1660 end
1661
1662 if variable then
1663
1664
1665
1666 local instances = variable . instances
1667 if instances then
1668 for i = 1 , # instances do
1669 local v = instances [ i ] . values
1670 for j = 1 , # v do
1671 v [ j ] = pack_normal ( v [ j ] )
1672 end
1673 end
1674 end
1675
1676 local function packdeltas ( main )
1677 if main then
1678 local deltas = main . deltas
1679 if deltas then
1680 for i = 1 , # deltas do
1681 local di = deltas [ i ]
1682 local d = di . deltas
1683
1684 for j = 1 , # d do
1685 d [ j ] = pack_indexed ( d [ j ] )
1686 end
1687 di . regions = pack_indexed ( di . regions )
1688 end
1689 end
1690 local regions = main . regions
1691 if regions then
1692 for i = 1 , # regions do
1693 local r = regions [ i ]
1694 for j = 1 , # r do
1695 r [ j ] = pack_normal ( r [ j ] )
1696 end
1697 end
1698 end
1699 end
1700 end
1701
1702 packdeltas ( variable . global )
1703 packdeltas ( variable . horizontal )
1704 packdeltas ( variable . vertical )
1705 packdeltas ( variable . metrics )
1706
1707 end
1708
1709 if not success ( 1 , pass ) then
1710 return
1711 end
1712
1713 end
1714
1715 if nt > 0 then
1716
1717 for pass = 1 , 2 do
1718
1719 if trace_packing then
1720 report_otf ( " start packing: stage 2, pass %s " , pass )
1721 end
1722
1723 local pack_normal , pack_indexed , pack_flat , pack_boolean , pack_mixed , pack_normal_cc = packers ( pass )
1724
1725 for unicode , description in next , descriptions do
1726 local math = description . math
1727 if math then
1728 local kerns = math . kerns
1729 if kerns then
1730 math . kerns = pack_normal ( kerns )
1731 end
1732 end
1733 end
1734
1735 local function packthem ( sequences )
1736 for i = 1 , # sequences do
1737 local sequence = sequences [ i ]
1738 local kind = sequence . type
1739 local steps = sequence . steps
1740 local features = sequence . features
1741 if steps then
1742 for i = 1 , # steps do
1743 local step = steps [ i ]
1744 if kind = = " gpos_pair " then
1745 local c = step . coverage
1746 if c then
1747 if step . format = = " pair " then
1748 for g1 , d1 in next , c do
1749 for g2 , d2 in next , d1 do
1750 d1 [ g2 ] = pack_normal ( d2 )
1751 end
1752 end
1753 end
1754 end
1755
1756
1757
1758
1759
1760
1761
1762 elseif kind = = " gpos_mark2ligature " then
1763 local c = step . baseclasses
1764 if c then
1765 for g1 , d1 in next , c do
1766 for g2 , d2 in next , d1 do
1767 d1 [ g2 ] = pack_normal ( d2 )
1768 end
1769 end
1770 end
1771 end
1772 local rules = step . rules
1773 if rules then
1774 for i = 1 , # rules do
1775 local rule = rules [ i ]
1776 local r = rule . before if r then rule . before = pack_normal ( r ) end
1777 local r = rule . after if r then rule . after = pack_normal ( r ) end
1778 local r = rule . current if r then rule . current = pack_normal ( r ) end
1779 end
1780 end
1781 end
1782 end
1783 if features then
1784 sequence . features = pack_normal ( features )
1785 end
1786 end
1787 end
1788 if sequences then
1789 packthem ( sequences )
1790 end
1791 if sublookups then
1792 packthem ( sublookups )
1793 end
1794 if variable then
1795 local function unpackdeltas ( main )
1796 if main then
1797 local regions = main . regions
1798 if regions then
1799 main . regions = pack_normal ( regions )
1800 end
1801 end
1802 end
1803 unpackdeltas ( variable . global )
1804 unpackdeltas ( variable . horizontal )
1805 unpackdeltas ( variable . vertical )
1806 unpackdeltas ( variable . metrics )
1807 end
1808
1809
1810
1811 end
1812
1813 for pass = 1 , 2 do
1814 if trace_packing then
1815 report_otf ( " start packing: stage 3, pass %s " , pass )
1816 end
1817
1818 local pack_normal , pack_indexed , pack_flat , pack_boolean , pack_mixed , pack_normal_cc = packers ( pass )
1819
1820 local function packthem ( sequences )
1821 for i = 1 , # sequences do
1822 local sequence = sequences [ i ]
1823 local kind = sequence . type
1824 local steps = sequence . steps
1825 local features = sequence . features
1826 if steps then
1827 for i = 1 , # steps do
1828 local step = steps [ i ]
1829 if kind = = " gpos_pair " then
1830 local c = step . coverage
1831 if c then
1832 if step . format = = " pair " then
1833 for g1 , d1 in next , c do
1834 c [ g1 ] = pack_normal ( d1 )
1835 end
1836 end
1837 end
1838 elseif kind = = " gpos_cursive " then
1839 local c = step . coverage
1840 if c then
1841 for g1 , d1 in next , c do
1842 c [ g1 ] = pack_normal_cc ( d1 )
1843 end
1844 end
1845 end
1846 end
1847 end
1848 end
1849 end
1850
1851 if sequences then
1852 packthem ( sequences )
1853 end
1854 if sublookups then
1855 packthem ( sublookups )
1856 end
1857
1858 end
1859
1860 end
1861
1862 end
1863end
1864
1865local unpacked_mt = {
1866 __index =
1867 function ( t , k )
1868 t [ k ] = false
1869 return k
1870 end
1871}
1872
1873function readers . unpack ( data )
1874
1875 if data then
1876 local tables = data . tables
1877 if tables then
1878 local resources = data . resources
1879 local descriptions = data . descriptions or data . glyphs
1880 local sequences = resources . sequences
1881 local sublookups = resources . sublookups
1882 local features = resources . features
1883 local palettes = resources . colorpalettes
1884 local variable = resources . variabledata
1885 local unpacked = { }
1886 setmetatable ( unpacked , unpacked_mt )
1887 for unicode , description in next , descriptions do
1888 local tv = tables [ description . boundingbox ]
1889 if tv then
1890 description . boundingbox = tv
1891 end
1892 local math = description . math
1893 if math then
1894 local kerns = math . kerns
1895 if kerns then
1896 local tm = tables [ kerns ]
1897 if tm then
1898 math . kerns = tm
1899 kerns = unpacked [ tm ]
1900 end
1901 if kerns then
1902 for k , kern in next , kerns do
1903 local tv = tables [ kern ]
1904 if tv then
1905 kerns [ k ] = tv
1906 end
1907 end
1908 end
1909 end
1910 end
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922 end
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933 local function unpackthem ( sequences )
1934 for i = 1 , # sequences do
1935 local sequence = sequences [ i ]
1936 local kind = sequence . type
1937 local steps = sequence . steps
1938 local order = sequence . order
1939 local features = sequence . features
1940 local flags = sequence . flags
1941 local markclass = sequence . markclass
1942 if features then
1943 local tv = tables [ features ]
1944 if tv then
1945 sequence . features = tv
1946 features = tv
1947 end
1948 for script , feature in next , features do
1949 local tv = tables [ feature ]
1950 if tv then
1951 features [ script ] = tv
1952 end
1953 end
1954 end
1955 if steps then
1956 for i = 1 , # steps do
1957 local step = steps [ i ]
1958 if kind = = " gpos_pair " then
1959 local c = step . coverage
1960 if c then
1961 if step . format = = " pair " then
1962 for g1 , d1 in next , c do
1963 local tv = tables [ d1 ]
1964 if tv then
1965 c [ g1 ] = tv
1966 d1 = tv
1967 end
1968 for g2 , d2 in next , d1 do
1969 local tv = tables [ d2 ]
1970 if tv then
1971 d1 [ g2 ] = tv
1972 d2 = tv
1973 end
1974 local f = tables [ d2 [ 1 ] ] if f then d2 [ 1 ] = f end
1975 local s = tables [ d2 [ 2 ] ] if s then d2 [ 2 ] = s end
1976 end
1977 end
1978 else
1979 for g1 , d1 in next , c do
1980 local tv = tables [ d1 ]
1981 if tv then
1982 c [ g1 ] = tv
1983 end
1984 end
1985 end
1986 end
1987 elseif kind = = " gpos_single " then
1988 local c = step . coverage
1989 if c then
1990 if step . format = = " single " then
1991 for g1 , d1 in next , c do
1992 local tv = tables [ d1 ]
1993 if tv then
1994 c [ g1 ] = tv
1995 end
1996 end
1997 else
1998 local tv = tables [ c ]
1999 if tv then
2000 step . coverage = tv
2001 end
2002 end
2003 end
2004 elseif kind = = " gpos_cursive " then
2005 local c = step . coverage
2006 if c then
2007 for g1 , d1 in next , c do
2008 local tv = tables [ d1 ]
2009 if tv then
2010 d1 = tv
2011 c [ g1 ] = d1
2012 end
2013 local f = tables [ d1 [ 2 ] ] if f then d1 [ 2 ] = f end
2014 local s = tables [ d1 [ 3 ] ] if s then d1 [ 3 ] = s end
2015 end
2016 end
2017 elseif kind = = " gpos_mark2base " or kind = = " gpos_mark2mark " then
2018 local c = step . baseclasses
2019 if c then
2020 for g1 , d1 in next , c do
2021 for g2 , d2 in next , d1 do
2022 local tv = tables [ d2 ]
2023 if tv then
2024 d1 [ g2 ] = tv
2025 end
2026 end
2027 end
2028 end
2029 local c = step . coverage
2030 if c then
2031 for g1 , d1 in next , c do
2032 local tv = tables [ d1 [ 2 ] ]
2033 if tv then
2034 d1 [ 2 ] = tv
2035 end
2036 end
2037 end
2038 elseif kind = = " gpos_mark2ligature " then
2039 local c = step . baseclasses
2040 if c then
2041 for g1 , d1 in next , c do
2042 for g2 , d2 in next , d1 do
2043 local tv = tables [ d2 ]
2044 if tv then
2045 d2 = tv
2046 d1 [ g2 ] = d2
2047 end
2048 for g3 , d3 in next , d2 do
2049 local tv = tables [ d2 [ g3 ] ]
2050 if tv then
2051 d2 [ g3 ] = tv
2052 end
2053 end
2054 end
2055 end
2056 end
2057 local c = step . coverage
2058 if c then
2059 for g1 , d1 in next , c do
2060 local tv = tables [ d1 [ 2 ] ]
2061 if tv then
2062 d1 [ 2 ] = tv
2063 end
2064 end
2065 end
2066 end
2067 local rules = step . rules
2068 if rules then
2069 for i = 1 , # rules do
2070 local rule = rules [ i ]
2071 local before = rule . before
2072 if before then
2073 local tv = tables [ before ]
2074 if tv then
2075 rule . before = tv
2076 before = tv
2077 end
2078 for i = 1 , # before do
2079 local tv = tables [ before [ i ] ]
2080 if tv then
2081 before [ i ] = tv
2082 end
2083 end
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096 end
2097 local after = rule . after
2098 if after then
2099 local tv = tables [ after ]
2100 if tv then
2101 rule . after = tv
2102 after = tv
2103 end
2104 for i = 1 , # after do
2105 local tv = tables [ after [ i ] ]
2106 if tv then
2107 after [ i ] = tv
2108 end
2109 end
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122 end
2123 local current = rule . current
2124 if current then
2125 local tv = tables [ current ]
2126 if tv then
2127 rule . current = tv
2128 current = tv
2129 end
2130 for i = 1 , # current do
2131 local tv = tables [ current [ i ] ]
2132 if tv then
2133 current [ i ] = tv
2134 end
2135 end
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148 end
2149
2150
2151
2152
2153
2154
2155
2156 local replacements = rule . replacements
2157 if replacements then
2158 local tv = tables [ replacements ]
2159 if tv then
2160 rule . replacements = tv
2161 end
2162 end
2163 end
2164 end
2165 end
2166 end
2167 if order then
2168 local tv = tables [ order ]
2169 if tv then
2170 sequence . order = tv
2171 end
2172 end
2173 if flags then
2174 local tv = tables [ flags ]
2175 if tv then
2176 sequence . flags = tv
2177 end
2178 end
2179 end
2180 end
2181
2182 if sequences then
2183 unpackthem ( sequences )
2184 end
2185
2186 if sublookups then
2187 unpackthem ( sublookups )
2188 end
2189
2190 if features then
2191 for k , list in next , features do
2192 for feature , spec in next , list do
2193 local tv = tables [ spec ]
2194 if tv then
2195 list [ feature ] = tv
2196 end
2197 end
2198 end
2199 end
2200
2201 if palettes then
2202 for i = 1 , # palettes do
2203 local p = palettes [ i ]
2204 for j = 1 , # p do
2205 local tv = tables [ p [ j ] ]
2206 if tv then
2207 p [ j ] = tv
2208 end
2209 end
2210 end
2211 end
2212
2213 if variable then
2214
2215
2216
2217 local instances = variable . instances
2218 if instances then
2219 for i = 1 , # instances do
2220 local v = instances [ i ] . values
2221 for j = 1 , # v do
2222 local tv = tables [ v [ j ] ]
2223 if tv then
2224 v [ j ] = tv
2225 end
2226 end
2227 end
2228 end
2229
2230 local function unpackdeltas ( main )
2231 if main then
2232 local deltas = main . deltas
2233 if deltas then
2234 for i = 1 , # deltas do
2235 local di = deltas [ i ]
2236 local d = di . deltas
2237 local r = di . regions
2238 for j = 1 , # d do
2239 local tv = tables [ d [ j ] ]
2240 if tv then
2241 d [ j ] = tv
2242 end
2243 end
2244 local tv = di . regions
2245 if tv then
2246 di . regions = tv
2247 end
2248 end
2249 end
2250 local regions = main . regions
2251 if regions then
2252 local tv = tables [ regions ]
2253 if tv then
2254 main . regions = tv
2255 regions = tv
2256 end
2257 for i = 1 , # regions do
2258 local r = regions [ i ]
2259 for j = 1 , # r do
2260 local tv = tables [ r [ j ] ]
2261 if tv then
2262 r [ j ] = tv
2263 end
2264 end
2265 end
2266 end
2267 end
2268 end
2269
2270 unpackdeltas ( variable . global )
2271 unpackdeltas ( variable . horizontal )
2272 unpackdeltas ( variable . vertical )
2273 unpackdeltas ( variable . metrics )
2274
2275 end
2276
2277 data . tables = nil
2278 end
2279 end
2280end
2281
2282local mt = {
2283 __index = function ( t , k )
2284 if k = = " height " then
2285 local ht = t . boundingbox [ 4 ]
2286 return ht < 0 and 0 or ht
2287 elseif k = = " depth " then
2288 local dp = - t . boundingbox [ 2 ]
2289 return dp < 0 and 0 or dp
2290 elseif k = = " width " then
2291 return 0
2292 elseif k = = " name " then
2293 return forcenotdef and " .notdef "
2294 end
2295 end
2296}
2297
2298local function sameformat ( sequence , steps , first , nofsteps , kind )
2299 return true
2300end
2301
2302local function mergesteps_1 ( lookup , strict )
2303 local steps = lookup . steps
2304 local nofsteps = lookup . nofsteps
2305 local first = steps [ 1 ]
2306 if strict then
2307 local f = first . format
2308 for i = 2 , nofsteps do
2309 if steps [ i ] . format ~ = f then
2310 if trace_optimizations then
2311 report_optimizations ( " not merging %a steps of %a lookup %a, different formats " , nofsteps , lookup . type , lookup . name )
2312 end
2313 return 0
2314 end
2315 end
2316 end
2317 if trace_optimizations then
2318 report_optimizations ( " merging %a steps of %a lookup %a " , nofsteps , lookup . type , lookup . name )
2319 end
2320 local target = first . coverage
2321 for i = 2 , nofsteps do
2322 local c = steps [ i ] . coverage
2323 if c then
2324 for k , v in next , c do
2325 if not target [ k ] then
2326 target [ k ] = v
2327 end
2328 end
2329 end
2330 end
2331 lookup . nofsteps = 1
2332 lookup . merged = true
2333 lookup . steps = { first }
2334 return nofsteps - 1
2335end
2336
2337local function mergesteps_2 ( lookup )
2338
2339
2340
2341 local steps = lookup . steps
2342 local nofsteps = lookup . nofsteps
2343 local first = steps [ 1 ]
2344 if strict then
2345 local f = first . format
2346 for i = 2 , nofsteps do
2347 if steps [ i ] . format ~ = f then
2348 if trace_optimizations then
2349 report_optimizations ( " not merging %a steps of %a lookup %a, different formats " , nofsteps , lookup . type , lookup . name )
2350 end
2351 return 0
2352 end
2353 end
2354 end
2355 if trace_optimizations then
2356 report_optimizations ( " merging %a steps of %a lookup %a " , nofsteps , lookup . type , lookup . name )
2357 end
2358 local target = first . coverage
2359 for i = 2 , nofsteps do
2360 local c = steps [ i ] . coverage
2361 if c then
2362 for k , v in next , c do
2363 local tk = target [ k ]
2364 if tk then
2365 for kk , vv in next , v do
2366 if tk [ kk ] = = nil then
2367 tk [ kk ] = vv
2368 end
2369 end
2370 else
2371 target [ k ] = v
2372 end
2373 end
2374 end
2375 end
2376 lookup . nofsteps = 1
2377 lookup . merged = true
2378 lookup . steps = { first }
2379 return nofsteps - 1
2380end
2381
2382
2383
2384
2385local function mergesteps_3 ( lookup , strict )
2386 local steps = lookup . steps
2387 local nofsteps = lookup . nofsteps
2388 if trace_optimizations then
2389 report_optimizations ( " merging %a steps of %a lookup %a " , nofsteps , lookup . type , lookup . name )
2390 end
2391
2392 local coverage = { }
2393 for i = 1 , nofsteps do
2394 local c = steps [ i ] . coverage
2395 if c then
2396 for k , v in next , c do
2397 local tk = coverage [ k ]
2398 if tk then
2399 if trace_optimizations then
2400 report_optimizations ( " quitting merge due to multiple checks " )
2401 end
2402 return nofsteps
2403 else
2404 coverage [ k ] = v
2405 end
2406 end
2407 end
2408 end
2409
2410 local first = steps [ 1 ]
2411 local baseclasses = { }
2412 for i = 1 , nofsteps do
2413 local offset = i * 10
2414 local step = steps [ i ]
2415 for k , v in sortedhash ( step . baseclasses ) do
2416 baseclasses [ offset + k ] = v
2417 end
2418 for k , v in next , step . coverage do
2419 v [ 1 ] = offset + v [ 1 ]
2420 end
2421 end
2422 first . baseclasses = baseclasses
2423 first . coverage = coverage
2424 lookup . nofsteps = 1
2425 lookup . merged = true
2426 lookup . steps = { first }
2427 return nofsteps - 1
2428end
2429
2430local function nested ( old , new )
2431 for k , v in next , old do
2432 if k = = " ligature " then
2433 if not new . ligature then
2434 new . ligature = v
2435 end
2436 else
2437 local n = new [ k ]
2438 if n then
2439 nested ( v , n )
2440 else
2441 new [ k ] = v
2442 end
2443 end
2444 end
2445end
2446
2447local function mergesteps_4 ( lookup )
2448 local steps = lookup . steps
2449 local nofsteps = lookup . nofsteps
2450 local first = steps [ 1 ]
2451 if trace_optimizations then
2452 report_optimizations ( " merging %a steps of %a lookup %a " , nofsteps , lookup . type , lookup . name )
2453 end
2454 local target = first . coverage
2455 for i = 2 , nofsteps do
2456 local c = steps [ i ] . coverage
2457 if c then
2458 for k , v in next , c do
2459 local tk = target [ k ]
2460 if tk then
2461 nested ( v , tk )
2462 else
2463 target [ k ] = v
2464 end
2465 end
2466 end
2467 end
2468 lookup . nofsteps = 1
2469 lookup . steps = { first }
2470 return nofsteps - 1
2471end
2472
2473
2474
2475
2476
2477local function mergesteps_5 ( lookup )
2478 local steps = lookup . steps
2479 local nofsteps = lookup . nofsteps
2480 local first = steps [ 1 ]
2481 if trace_optimizations then
2482 report_optimizations ( " merging %a steps of %a lookup %a " , nofsteps , lookup . type , lookup . name )
2483 end
2484 local target = first . coverage
2485 local hash = nil
2486 for k , v in next , target do
2487 hash = v [ 1 ]
2488 break
2489 end
2490 for i = 2 , nofsteps do
2491 local c = steps [ i ] . coverage
2492 if c then
2493 for k , v in next , c do
2494 local tk = target [ k ]
2495 if tk then
2496 if not tk [ 2 ] then
2497 tk [ 2 ] = v [ 2 ]
2498 end
2499 if not tk [ 3 ] then
2500 tk [ 3 ] = v [ 3 ]
2501 end
2502 else
2503 target [ k ] = v
2504 v [ 1 ] = hash
2505 end
2506 end
2507 end
2508 end
2509 lookup . nofsteps = 1
2510 lookup . merged = true
2511 lookup . steps = { first }
2512 return nofsteps - 1
2513end
2514
2515local function checkkerns ( lookup )
2516 local steps = lookup . steps
2517 local nofsteps = lookup . nofsteps
2518 local kerned = 0
2519 for i = 1 , nofsteps do
2520 local step = steps [ i ]
2521 if step . format = = " pair " then
2522 local coverage = step . coverage
2523 local kerns = true
2524 for g1 , d1 in next , coverage do
2525 if d1 = = true then
2526
2527 elseif not d1 then
2528
2529 elseif d1 [ 1 ] ~ = 0 or d1 [ 2 ] ~ = 0 or d1 [ 4 ] ~ = 0 then
2530 kerns = false
2531 break
2532 end
2533 end
2534 if kerns then
2535 if trace_optimizations then
2536 report_optimizations ( " turning pairs of step %a of %a lookup %a into kerns " , i , lookup . type , lookup . name )
2537 end
2538 local c = { }
2539 for g1 , d1 in next , coverage do
2540 if d1 and d1 ~ = true then
2541 c [ g1 ] = d1 [ 3 ]
2542 end
2543 end
2544 step . coverage = c
2545 step . format = " move "
2546 kerned = kerned + 1
2547 end
2548 end
2549 end
2550 return kerned
2551end
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567local function checkpairs ( lookup )
2568 local steps = lookup . steps
2569 local nofsteps = lookup . nofsteps
2570 local kerned = 0
2571
2572 local function onlykerns ( step )
2573 local coverage = step . coverage
2574 for g1 , d1 in next , coverage do
2575 for g2 , d2 in next , d1 do
2576 if d2 [ 2 ] then
2577
2578 return false
2579 else
2580 local v = d2 [ 1 ]
2581 if v = = true then
2582
2583 elseif v and ( v [ 1 ] ~ = 0 or v [ 2 ] ~ = 0 or v [ 4 ] ~ = 0 ) then
2584 return false
2585 end
2586 end
2587 end
2588 end
2589 return coverage
2590 end
2591
2592 for i = 1 , nofsteps do
2593 local step = steps [ i ]
2594 if step . format = = " pair " then
2595 local coverage = onlykerns ( step )
2596 if coverage then
2597 if trace_optimizations then
2598 report_optimizations ( " turning pairs of step %a of %a lookup %a into kerns " , i , lookup . type , lookup . name )
2599 end
2600 for g1 , d1 in next , coverage do
2601 local d = { }
2602 for g2 , d2 in next , d1 do
2603 local v = d2 [ 1 ]
2604 if v = = true then
2605
2606 elseif v then
2607 d [ g2 ] = v [ 3 ]
2608 end
2609 end
2610 coverage [ g1 ] = d
2611 end
2612 step . format = " move "
2613 kerned = kerned + 1
2614 end
2615 end
2616 end
2617 return kerned
2618end
2619
2620local compact_pairs = true
2621local compact_singles = true
2622
2623local merge_pairs = true
2624local merge_singles = true
2625local merge_substitutions = true
2626local merge_alternates = true
2627local merge_multiples = true
2628local merge_ligatures = true
2629local merge_cursives = true
2630local merge_marks = true
2631
2632directives . register ( " otf.compact.pairs " , function ( v ) compact_pairs = v end )
2633directives . register ( " otf.compact.singles " , function ( v ) compact_singles = v end )
2634
2635directives . register ( " otf.merge.pairs " , function ( v ) merge_pairs = v end )
2636directives . register ( " otf.merge.singles " , function ( v ) merge_singles = v end )
2637directives . register ( " otf.merge.substitutions " , function ( v ) merge_substitutions = v end )
2638directives . register ( " otf.merge.alternates " , function ( v ) merge_alternates = v end )
2639directives . register ( " otf.merge.multiples " , function ( v ) merge_multiples = v end )
2640directives . register ( " otf.merge.ligatures " , function ( v ) merge_ligatures = v end )
2641directives . register ( " otf.merge.cursives " , function ( v ) merge_cursives = v end )
2642directives . register ( " otf.merge.marks " , function ( v ) merge_marks = v end )
2643
2644function readers . compact ( data )
2645 if not data or data . compacted then
2646 return
2647 else
2648 data . compacted = true
2649 end
2650 local resources = data . resources
2651 local merged = 0
2652 local kerned = 0
2653 local allsteps = 0
2654 local function compact ( what )
2655 local lookups = resources [ what ]
2656 if lookups then
2657 for i = 1 , # lookups do
2658 local lookup = lookups [ i ]
2659 local nofsteps = lookup . nofsteps
2660 local kind = lookup . type
2661 allsteps = allsteps + nofsteps
2662 if nofsteps > 1 then
2663 local merg = merged
2664 if kind = = " gsub_single " then
2665 if merge_substitutions then
2666 merged = merged + mergesteps_1 ( lookup )
2667 end
2668 elseif kind = = " gsub_alternate " then
2669 if merge_alternates then
2670 merged = merged + mergesteps_1 ( lookup )
2671 end
2672 elseif kind = = " gsub_multiple " then
2673 if merge_multiples then
2674 merged = merged + mergesteps_1 ( lookup )
2675 end
2676 elseif kind = = " gsub_ligature " then
2677 if merge_ligatures then
2678 merged = merged + mergesteps_4 ( lookup )
2679 end
2680 elseif kind = = " gpos_single " then
2681 if merge_singles then
2682 merged = merged + mergesteps_1 ( lookup , true )
2683 end
2684 if compact_singles then
2685 kerned = kerned + checkkerns ( lookup )
2686 end
2687 elseif kind = = " gpos_pair " then
2688 if merge_pairs then
2689 merged = merged + mergesteps_2 ( lookup )
2690 end
2691 if compact_pairs then
2692 kerned = kerned + checkpairs ( lookup )
2693 end
2694 elseif kind = = " gpos_cursive " then
2695 if merge_cursives then
2696 merged = merged + mergesteps_5 ( lookup )
2697 end
2698 elseif kind = = " gpos_mark2mark " or kind = = " gpos_mark2base " or kind = = " gpos_mark2ligature " then
2699 if merge_marks then
2700 merged = merged + mergesteps_3 ( lookup )
2701 end
2702 end
2703 if merg ~ = merged then
2704 lookup . merged = true
2705 end
2706 elseif nofsteps = = 1 then
2707 local kern = kerned
2708 if kind = = " gpos_single " then
2709 if compact_singles then
2710 kerned = kerned + checkkerns ( lookup )
2711 end
2712 elseif kind = = " gpos_pair " then
2713 if compact_pairs then
2714 kerned = kerned + checkpairs ( lookup )
2715 end
2716 end
2717 if kern ~ = kerned then
2718
2719 end
2720 end
2721 end
2722 elseif trace_optimizations then
2723 report_optimizations ( " no lookups in %a " , what )
2724 end
2725 end
2726 compact ( " sequences " )
2727 compact ( " sublookups " )
2728 if trace_optimizations then
2729 if merged > 0 then
2730 report_optimizations ( " %i steps of %i removed due to merging " , merged , allsteps )
2731 end
2732 if kerned > 0 then
2733 report_optimizations ( " %i steps of %i steps turned from pairs into kerns " , kerned , allsteps )
2734 end
2735 end
2736end
2737
2738local function mergesteps ( t , k )
2739 if k = = " merged " then
2740 local merged = { }
2741 for i = 1 , # t do
2742 local step = t [ i ]
2743 local coverage = step . coverage
2744 for k in next , coverage do
2745 local m = merged [ k ]
2746 if m then
2747 m [ 2 ] = i
2748
2749 else
2750 merged [ k ] = { i , i }
2751
2752 end
2753 end
2754 end
2755 t . merged = merged
2756 return merged
2757 end
2758end
2759
2760local function checkmerge ( sequence )
2761 local steps = sequence . steps
2762 if steps then
2763 setmetatableindex ( steps , mergesteps )
2764 end
2765end
2766
2767local function checkflags ( sequence , resources )
2768 if not sequence . skiphash then
2769 local flags = sequence . flags
2770 if flags then
2771 local skipmark = flags [ 1 ]
2772 local skipligature = flags [ 2 ]
2773 local skipbase = flags [ 3 ]
2774 local markclass = sequence . markclass
2775 local skipsome = skipmark or skipligature or skipbase or markclass or false
2776 if skipsome then
2777 sequence . skiphash = setmetatableindex ( function ( t , k )
2778 local c = resources . classes [ k ]
2779 local v = c = = skipmark
2780 or ( markclass and c = = " mark " and not markclass [ k ] )
2781 or c = = skipligature
2782 or c = = skipbase
2783 or false
2784 t [ k ] = v
2785 return v
2786 end )
2787 else
2788 sequence . skiphash = false
2789 end
2790 else
2791 sequence . skiphash = false
2792 end
2793 end
2794end
2795
2796local function checksteps ( sequence )
2797 local steps = sequence . steps
2798 if steps then
2799 for i = 1 , # steps do
2800 steps [ i ] . index = i
2801 end
2802 end
2803end
2804
2805if fonts . helpers then
2806 fonts . helpers . checkmerge = checkmerge
2807 fonts . helpers . checkflags = checkflags
2808 fonts . helpers . checksteps = checksteps
2809end
2810
2811function readers . expand ( data )
2812 if not data or data . expanded then
2813 return
2814 else
2815 data . expanded = true
2816 end
2817 local resources = data . resources
2818 local sublookups = resources . sublookups
2819 local sequences = resources . sequences
2820 local markclasses = resources . markclasses
2821 local descriptions = data . descriptions
2822 if descriptions then
2823 local defaultwidth = resources . defaultwidth or 0
2824 local defaultheight = resources . defaultheight or 0
2825 local defaultdepth = resources . defaultdepth or 0
2826 local basename = trace_markwidth and file . basename ( resources . filename )
2827 for u , d in next , descriptions do
2828 local bb = d . boundingbox
2829 local wd = d . width
2830 if not wd then
2831
2832 d . width = defaultwidth
2833 elseif trace_markwidth and wd ~ = 0 and d . class = = " mark " then
2834 report_markwidth ( " mark %a with width %b found in %a " , d . name or " <noname> " , wd , basename )
2835 end
2836 if bb then
2837 local ht = bb [ 4 ]
2838 local dp = - bb [ 2 ]
2839 if ht = = 0 or ht < 0 then
2840
2841 else
2842 d . height = ht
2843 end
2844 if dp = = 0 or dp < 0 then
2845
2846 else
2847 d . depth = dp
2848 end
2849 end
2850 end
2851 end
2852
2853
2854
2855
2856
2857 local function expandlookups ( sequences )
2858 if sequences then
2859
2860 for i = 1 , # sequences do
2861 local sequence = sequences [ i ]
2862 local steps = sequence . steps
2863 if steps then
2864 local nofsteps = sequence . nofsteps
2865
2866 local kind = sequence . type
2867 local markclass = sequence . markclass
2868 if markclass then
2869 if not markclasses then
2870 report_warning ( " missing markclasses " )
2871 sequence . markclass = false
2872 else
2873 sequence . markclass = markclasses [ markclass ]
2874 end
2875 end
2876
2877 for i = 1 , nofsteps do
2878 local step = steps [ i ]
2879 local baseclasses = step . baseclasses
2880 if baseclasses then
2881 local coverage = step . coverage
2882 for k , v in next , coverage do
2883 v [ 1 ] = baseclasses [ v [ 1 ] ]
2884 end
2885 elseif kind = = " gpos_cursive " then
2886 local coverage = step . coverage
2887 for k , v in next , coverage do
2888 v [ 1 ] = coverage
2889 end
2890 end
2891 local rules = step . rules
2892 if rules then
2893 local rulehash = { n = 0 }
2894 local rulesize = 0
2895 local coverage = { }
2896 local lookuptype = sequence . type
2897 local nofrules = # rules
2898 step . coverage = coverage
2899 for currentrule = 1 , nofrules do
2900 local rule = rules [ currentrule ]
2901 local current = rule . current
2902 local before = rule . before
2903 local after = rule . after
2904 local replacements = rule . replacements or false
2905 local sequence = { }
2906 local nofsequences = 0
2907 if before then
2908 for n = 1 , # before do
2909 nofsequences = nofsequences + 1
2910 sequence [ nofsequences ] = before [ n ]
2911 end
2912 end
2913 local start = nofsequences + 1
2914 for n = 1 , # current do
2915 nofsequences = nofsequences + 1
2916 sequence [ nofsequences ] = current [ n ]
2917 end
2918 local stop = nofsequences
2919 if after then
2920 for n = 1 , # after do
2921 nofsequences = nofsequences + 1
2922 sequence [ nofsequences ] = after [ n ]
2923 end
2924 end
2925 local lookups = rule . lookups or false
2926 local subtype = nil
2927 if lookups then
2928 for i = 1 , # lookups do
2929 local lookups = lookups [ i ]
2930 if lookups then
2931 for k , v in next , lookups do
2932 local lookup = sublookups [ v ]
2933 if lookup then
2934 lookups [ k ] = lookup
2935 if not subtype then
2936 subtype = lookup . type
2937 end
2938 else
2939
2940 end
2941 end
2942 end
2943 end
2944 end
2945 if sequence [ 1 ] then
2946 sequence . n = # sequence
2947 local ruledata = {
2948 currentrule ,
2949 lookuptype ,
2950 sequence ,
2951 start ,
2952 stop ,
2953 lookups ,
2954 replacements ,
2955 subtype ,
2956 }
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968 rulesize = rulesize + 1
2969 rulehash [ rulesize ] = ruledata
2970 rulehash . n = rulesize
2971
2972 if true then
2973
2974 for unic in next , sequence [ start ] do
2975 local cu = coverage [ unic ]
2976 if cu then
2977 local n = # cu + 1
2978 cu [ n ] = ruledata
2979 cu . n = n
2980 else
2981 coverage [ unic ] = { ruledata , n = 1 }
2982 end
2983 end
2984
2985 else
2986
2987 for unic in next , sequence [ start ] do
2988 local cu = coverage [ unic ]
2989 if cu then
2990
2991
2992 else
2993 coverage [ unic ] = rulehash
2994 end
2995 end
2996
2997 end
2998 end
2999 end
3000 end
3001 end
3002
3003 checkmerge ( sequence )
3004 checkflags ( sequence , resources )
3005 checksteps ( sequence )
3006
3007 end
3008 end
3009 end
3010 end
3011
3012 expandlookups ( sequences )
3013 expandlookups ( sublookups )
3014end
3015 |