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