@@ -3647,80 +3647,144 @@ var Font = (function FontClosure() {
3647
3647
var TTOpsStackDeltas = [
3648
3648
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , -2 , -2 , -2 , -2 , 0 , 0 , -2 , -5 ,
3649
3649
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , 0 , 0 , -1 , 0 , -1 , -1 , -1 , -1 ,
3650
- 1 , -1 , -999 , 0 , 1 , 0 , 0 , -2 , 0 , -1 , -2 , -1 , -999 , - 999 , -1 , -1 ,
3650
+ 1 , -1 , -999 , 0 , 1 , 0 , - 1 , -2 , 0 , -1 , -2 , -1 , -1 , 0 , -1 , -1 ,
3651
3651
0 , 0 , -999 , -999 , -1 , -1 , -1 , -1 , -2 , -999 , -2 , -2 , -2 , 0 , -2 , -2 ,
3652
3652
0 , 0 , -2 , 0 , -2 , 0 , 0 , 0 , -2 , -1 , -1 , 1 , 1 , 0 , 0 , -1 ,
3653
3653
-1 , -1 , -1 , -1 , -1 , -1 , 0 , 0 , -1 , 0 , -1 , -1 , 0 , -999 , -1 , -1 ,
3654
- -1 , -1 , -1 , -1 , 0 , 0 , 0 , 0 , - 1 , - 1 , 0 , 0 , 0 , 0 , 0 , 0 ,
3654
+ -1 , -1 , -1 , -1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
3655
3655
-2 , -999 , -999 , -999 , -999 , -999 , -1 , -1 , -2 , -2 , 0 , 0 , 0 , 0 , -1 , -1 ,
3656
- -999 , -2 , -2 , 0 , 0 , -1 , -2 , -2 , 0 , - 999 , 0 , 0 , 0 , -1 , -2 ];
3656
+ -999 , -2 , -2 , 0 , 0 , -1 , -2 , -2 , 0 , 0 , 0 , - 1 , - 1 , -1 , -2 ];
3657
3657
// 0xC0-DF == -1 and 0xE0-FF == -2
3658
3658
3659
3659
function sanitizeTTProgram (table , ttContext ) {
3660
3660
var data = table .data ;
3661
3661
var i = 0 , n , lastEndf = 0 , lastDeff = 0 ;
3662
3662
var stack = [];
3663
+ var callstack = [];
3664
+ var functionsCalled = [];
3663
3665
var tooComplexToFollowFunctions =
3664
3666
ttContext .tooComplexToFollowFunctions ;
3667
+ var inFDEF = false , ifLevel = 0 , inELSE = 0 ;
3665
3668
for (var ii = data .length ; i < ii ;) {
3666
3669
var op = data [i ++];
3667
3670
// The TrueType instruction set docs can be found at
3668
3671
// https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html
3669
3672
if (op === 0x40 ) { // NPUSHB - pushes n bytes
3670
3673
n = data [i ++];
3671
- for (var j = 0 ; j < n ; j ++) {
3672
- stack .push (data [i ++]);
3674
+ if (inFDEF || inELSE ) {
3675
+ i += n ;
3676
+ } else {
3677
+ for (var j = 0 ; j < n ; j ++) {
3678
+ stack .push (data [i ++]);
3679
+ }
3673
3680
}
3674
3681
} else if (op === 0x41 ) { // NPUSHW - pushes n words
3675
3682
n = data [i ++];
3676
- for (var j = 0 ; j < n ; j ++) {
3677
- var b = data [i ++];
3678
- stack .push ((b << 8 ) | data [i ++]);
3683
+ if (inFDEF || inELSE ) {
3684
+ i += n * 2 ;
3685
+ } else {
3686
+ for (var j = 0 ; j < n ; j ++) {
3687
+ var b = data [i ++];
3688
+ stack .push ((b << 8 ) | data [i ++]);
3689
+ }
3679
3690
}
3680
3691
} else if ((op & 0xF8 ) === 0xB0 ) { // PUSHB - pushes bytes
3681
3692
n = op - 0xB0 + 1 ;
3682
- for (var j = 0 ; j < n ; j ++) {
3683
- stack .push (data [i ++]);
3693
+ if (inFDEF || inELSE ) {
3694
+ i += n ;
3695
+ } else {
3696
+ for (var j = 0 ; j < n ; j ++) {
3697
+ stack .push (data [i ++]);
3698
+ }
3684
3699
}
3685
3700
} else if ((op & 0xF8 ) === 0xB8 ) { // PUSHW - pushes words
3686
3701
n = op - 0xB8 + 1 ;
3687
- for (var j = 0 ; j < n ; j ++) {
3688
- var b = data [i ++];
3689
- stack .push ((b << 8 ) | data [i ++]);
3702
+ if (inFDEF || inELSE ) {
3703
+ i += n * 2 ;
3704
+ } else {
3705
+ for (var j = 0 ; j < n ; j ++) {
3706
+ var b = data [i ++];
3707
+ stack .push ((b << 8 ) | data [i ++]);
3708
+ }
3690
3709
}
3691
3710
} else if (op === 0x2B && !tooComplexToFollowFunctions ) { // CALL
3692
- // collecting inforamtion about which functions are used
3693
- var funcId = stack [stack .length - 1 ];
3694
- ttContext .functionsUsed [funcId ] = true ;
3695
- if (i >= 2 && data [i - 2 ] === 0x2B ) {
3696
- // all data in stack, calls are performed in sequence
3697
- tooComplexToFollowFunctions = true ;
3711
+ if (!inFDEF && !inELSE ) {
3712
+ // collecting inforamtion about which functions are used
3713
+ var funcId = stack [stack .length - 1 ];
3714
+ ttContext .functionsUsed [funcId ] = true ;
3715
+ if (funcId in ttContext .functionsStackDeltas ) {
3716
+ stack .length += ttContext .functionsStackDeltas [funcId ];
3717
+ } else if (funcId in ttContext .functionsDefined &&
3718
+ functionsCalled .indexOf (funcId ) < 0 ) {
3719
+ callstack .push ({data : data , i : i , stackTop : stack .length - 1 });
3720
+ functionsCalled .push (funcId );
3721
+ var pc = ttContext .functionsDefined [funcId ];
3722
+ data = pc .data ;
3723
+ i = pc .i ;
3724
+ }
3698
3725
}
3699
3726
} else if (op === 0x2C && !tooComplexToFollowFunctions ) { // FDEF
3700
- // collecting inforamtion about which functions are defined
3701
- lastDeff = i ;
3702
- var funcId = stack [stack .length - 1 ];
3703
- ttContext .functionsDefined [funcId ] = true ;
3704
- if (i >= 2 && data [i - 2 ] === 0x2D ) {
3705
- // all function ids in stack, FDEF/ENDF perfomed in sequence
3727
+ if (inFDEF || inELSE ) {
3728
+ warn ('TT: nested FDEFs not allowed' );
3706
3729
tooComplexToFollowFunctions = true ;
3707
3730
}
3731
+ inFDEF = true ;
3732
+ // collecting inforamtion about which functions are defined
3733
+ lastDeff = i ;
3734
+ var funcId = stack .pop ();
3735
+ ttContext .functionsDefined [funcId ] = {data : data , i : i };
3708
3736
} else if (op === 0x2D ) { // ENDF - end of function
3709
- lastEndf = i ;
3737
+ if (inFDEF ) {
3738
+ inFDEF = false ;
3739
+ lastEndf = i ;
3740
+ } else {
3741
+ var pc = callstack .pop ();
3742
+ var funcId = functionsCalled .pop ();
3743
+ data = pc .data ;
3744
+ i = pc .i ;
3745
+ ttContext .functionsStackDeltas [funcId ] =
3746
+ stack .length - pc .stackTop ;
3747
+ }
3710
3748
} else if (op === 0x89 ) { // IDEF - instruction definition
3749
+ if (inFDEF || inELSE ) {
3750
+ warn ('TT: nested IDEFs not allowed' );
3751
+ tooComplexToFollowFunctions = true ;
3752
+ }
3753
+ inFDEF = true ;
3711
3754
// recording it as a function to track ENDF
3712
3755
lastDeff = i ;
3756
+ } else if (op === 0x58 ) { // IF
3757
+ ++ifLevel ;
3758
+ } else if (op === 0x1B ) { // ELSE
3759
+ inELSE = ifLevel ;
3760
+ } else if (op === 0x59 ) { // EIF
3761
+ if (inELSE === ifLevel ) {
3762
+ inELSE = 0 ;
3763
+ }
3764
+ --ifLevel ;
3765
+ } else if (op === 0x1C ) { // JMPR
3766
+ var offset = stack [stack .length - 1 ];
3767
+ // only jumping forward to prevent infinite loop
3768
+ if (offset > 0 ) { i += offset - 1 ; }
3713
3769
}
3714
3770
// Adjusting stack not extactly, but just enough to get function id
3715
- var stackDelta = op <= 0x8E ? TTOpsStackDeltas [op ] :
3716
- op >= 0xC0 && op <= 0xDF ? -1 : op >= 0xE0 ? -2 : 0 ;
3717
- while (stackDelta < 0 && stack .length > 0 ) {
3718
- stack .pop ();
3719
- stackDelta ++;
3720
- }
3721
- while (stackDelta > 0 ) {
3722
- stack .push (NaN ); // pushing any number into stack
3723
- stackDelta --;
3771
+ if (!inFDEF && !inELSE ) {
3772
+ var stackDelta = op <= 0x8E ? TTOpsStackDeltas [op ] :
3773
+ op >= 0xC0 && op <= 0xDF ? -1 : op >= 0xE0 ? -2 : 0 ;
3774
+ if (op >= 0x71 && op <= 0x75 ) {
3775
+ n = stack .pop ();
3776
+ if (n === n ) {
3777
+ stackDelta = -n * 2 ;
3778
+ }
3779
+ }
3780
+ while (stackDelta < 0 && stack .length > 0 ) {
3781
+ stack .pop ();
3782
+ stackDelta ++;
3783
+ }
3784
+ while (stackDelta > 0 ) {
3785
+ stack .push (NaN ); // pushing any number into stack
3786
+ stackDelta --;
3787
+ }
3724
3788
}
3725
3789
}
3726
3790
ttContext .tooComplexToFollowFunctions = tooComplexToFollowFunctions ;
@@ -3729,20 +3793,44 @@ var Font = (function FontClosure() {
3729
3793
content .push (new Uint8Array (i - data .length ));
3730
3794
}
3731
3795
if (lastDeff > lastEndf ) {
3796
+ warn ('TT: complementing a missing function tail' );
3732
3797
// new function definition started, but not finished
3733
3798
// complete function by [CLEAR, ENDF]
3734
3799
content .push (new Uint8Array ([0x22 , 0x2D ]));
3735
3800
}
3736
- if (ttContext .defineMissingFunctions && !tooComplexToFollowFunctions ) {
3801
+ foldTTTable (table , content );
3802
+ }
3803
+
3804
+ function addTTDummyFunctions (table , ttContext , maxFunctionDefs ) {
3805
+ var content = [table .data ];
3806
+ if (!ttContext .tooComplexToFollowFunctions ) {
3807
+ var undefinedFunctions = [];
3737
3808
for (var j = 0 , jj = ttContext .functionsUsed .length ; j < jj ; j ++) {
3738
3809
if (!ttContext .functionsUsed [j ] || ttContext .functionsDefined [j ]) {
3739
3810
continue ;
3740
3811
}
3812
+ undefinedFunctions .push (j );
3813
+ if (j >= maxFunctionDefs ) {
3814
+ continue ;
3815
+ }
3741
3816
// function is used, but not defined
3742
- // creating empty one [PUSHB, function-id, FDEF, ENDF]
3743
- content .push (new Uint8Array ([0xB0 , j , 0x2C , 0x2D ]));
3817
+ if (j < 256 ) {
3818
+ // creating empty one [PUSHB, function-id, FDEF, ENDF]
3819
+ content .push (new Uint8Array ([0xB0 , j , 0x2C , 0x2D ]));
3820
+ } else {
3821
+ // creating empty one [PUSHW, function-id, FDEF, ENDF]
3822
+ content .push (
3823
+ new Uint8Array ([0xB8 , j >> 8 , j & 255 , 0x2C , 0x2D ]));
3824
+ }
3825
+ }
3826
+ if (undefinedFunctions .length > 0 ) {
3827
+ warn ('TT: undefined functions: ' + undefinedFunctions );
3744
3828
}
3745
3829
}
3830
+ foldTTTable (table , content );
3831
+ }
3832
+
3833
+ function foldTTTable (table , content ) {
3746
3834
if (content .length > 1 ) {
3747
3835
// concatenating the content items
3748
3836
var newLength = 0 ;
@@ -3765,15 +3853,17 @@ var Font = (function FontClosure() {
3765
3853
var ttContext = {
3766
3854
functionsDefined : [],
3767
3855
functionsUsed : [],
3856
+ functionsStackDeltas : [],
3768
3857
tooComplexToFollowFunctions : false
3769
3858
};
3859
+ if (fpgm ) {
3860
+ sanitizeTTProgram (fpgm , ttContext );
3861
+ }
3770
3862
if (prep ) {
3771
- // collecting prep functions info first
3772
3863
sanitizeTTProgram (prep , ttContext );
3773
3864
}
3774
3865
if (fpgm ) {
3775
- ttContext .defineMissingFunctions = true ;
3776
- sanitizeTTProgram (fpgm , ttContext );
3866
+ addTTDummyFunctions (fpgm , ttContext , maxFunctionDefs );
3777
3867
}
3778
3868
}
3779
3869
@@ -3843,12 +3933,17 @@ var Font = (function FontClosure() {
3843
3933
// Ensure the hmtx table contains the advance width and
3844
3934
// sidebearings information for numGlyphs in the maxp table
3845
3935
font .pos = (font .start || 0 ) + maxp .offset ;
3846
- var version = int16 (font .getBytes (4 ));
3936
+ var version = int32 (font .getBytes (4 ));
3847
3937
var numGlyphs = int16 (font .getBytes (2 ));
3938
+ var maxFunctionDefs = 0 ;
3939
+ if (version >= 0x00010000 && maxp .length >= 22 ) {
3940
+ font .pos += 14 ;
3941
+ var maxFunctionDefs = int16 (font .getBytes (2 ));
3942
+ }
3848
3943
3849
3944
sanitizeMetrics (font , hhea , hmtx , numGlyphs );
3850
3945
3851
- sanitizeTTPrograms (fpgm , prep );
3946
+ sanitizeTTPrograms (fpgm , prep , maxFunctionDefs );
3852
3947
3853
3948
if (head ) {
3854
3949
sanitizeHead (head , numGlyphs , loca .length );
0 commit comments