Evaluate type 1 charstrings for conversion to type 2.
This commit is contained in:
		
							parent
							
								
									2ccad4ca45
								
							
						
					
					
						commit
						b5278c5e27
					
				
							
								
								
									
										770
									
								
								src/fonts.js
									
									
									
									
									
								
							
							
						
						
									
										770
									
								
								src/fonts.js
									
									
									
									
									
								
							| @ -26,7 +26,8 @@ var SYMBOLIC_FONT_GLYPH_OFFSET = 0xF000; | |||||||
| // except for Type 3 fonts
 | // except for Type 3 fonts
 | ||||||
| var PDF_GLYPH_SPACE_UNITS = 1000; | var PDF_GLYPH_SPACE_UNITS = 1000; | ||||||
| 
 | 
 | ||||||
| // Until hinting is fully supported this constant can be used
 | // Hinting is currently disabled due to unknown problems on windows
 | ||||||
|  | // in tracemonkey and various other pdfs with type1 fonts.
 | ||||||
| var HINTING_ENABLED = false; | var HINTING_ENABLED = false; | ||||||
| 
 | 
 | ||||||
| var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; | var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; | ||||||
| @ -4351,12 +4352,324 @@ var ErrorFont = (function ErrorFontClosure() { | |||||||
|   return ErrorFont; |   return ErrorFont; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| var CallothersubrCmd = (function CallothersubrCmdClosure() { | /* | ||||||
|   function CallothersubrCmd(index) { |  * CharStrings are encoded following the the CharString Encoding sequence | ||||||
|     this.index = index; |  * describe in Chapter 6 of the "Adobe Type1 Font Format" specification. | ||||||
|  |  * The value in a byte indicates a command, a number, or subsequent bytes | ||||||
|  |  * that are to be interpreted in a special way. | ||||||
|  |  * | ||||||
|  |  * CharString Number Encoding: | ||||||
|  |  *  A CharString byte containing the values from 32 through 255 inclusive | ||||||
|  |  *  indicate an integer. These values are decoded in four ranges. | ||||||
|  |  * | ||||||
|  |  * 1. A CharString byte containing a value, v, between 32 and 246 inclusive, | ||||||
|  |  * indicate the integer v - 139. Thus, the integer values from -107 through | ||||||
|  |  * 107 inclusive may be encoded in single byte. | ||||||
|  |  * | ||||||
|  |  * 2. A CharString byte containing a value, v, between 247 and 250 inclusive, | ||||||
|  |  * indicates an integer involving the next byte, w, according to the formula: | ||||||
|  |  * [(v - 247) x 256] + w + 108 | ||||||
|  |  * | ||||||
|  |  * 3. A CharString byte containing a value, v, between 251 and 254 inclusive, | ||||||
|  |  * indicates an integer involving the next byte, w, according to the formula: | ||||||
|  |  * -[(v - 251) * 256] - w - 108 | ||||||
|  |  * | ||||||
|  |  * 4. A CharString containing the value 255 indicates that the next 4 bytes | ||||||
|  |  * are a two complement signed integer. The first of these bytes contains the | ||||||
|  |  * highest order bits, the second byte contains the next higher order bits | ||||||
|  |  * and the fourth byte contain the lowest order bits. | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * CharString Command Encoding: | ||||||
|  |  *  CharStrings commands are encoded in 1 or 2 bytes. | ||||||
|  |  * | ||||||
|  |  *  Single byte commands are encoded in 1 byte that contains a value between | ||||||
|  |  *  0 and 31 inclusive. | ||||||
|  |  *  If a command byte contains the value 12, then the value in the next byte | ||||||
|  |  *  indicates a command. This "escape" mechanism allows many extra commands | ||||||
|  |  * to be encoded and this encoding technique helps to minimize the length of | ||||||
|  |  * the charStrings. | ||||||
|  |  */ | ||||||
|  | var Type1CharString = (function Type1CharStringClosure() { | ||||||
|  |   var COMMAND_MAP = { | ||||||
|  |     'hstem': [1], | ||||||
|  |     'vstem': [3], | ||||||
|  |     'vmoveto': [4], | ||||||
|  |     'rlineto': [5], | ||||||
|  |     'hlineto': [6], | ||||||
|  |     'vlineto': [7], | ||||||
|  |     'rrcurveto': [8], | ||||||
|  |     'callsubr': [10], | ||||||
|  |     'flex': [12, 35], | ||||||
|  |     'drop' : [12, 18], | ||||||
|  |     'endchar': [14], | ||||||
|  |     'rmoveto': [21], | ||||||
|  |     'hmoveto': [22], | ||||||
|  |     'vhcurveto': [30], | ||||||
|  |     'hvcurveto': [31] | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   function Type1CharString() { | ||||||
|  |     this.width = 0; | ||||||
|  |     this.lsb = 0; | ||||||
|  |     this.flexing = false; | ||||||
|  |     this.output = []; | ||||||
|  |     this.stack = []; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return CallothersubrCmd; |   Type1CharString.prototype = { | ||||||
|  |     convert: function Type1CharString_convert(encoded, subrs) { | ||||||
|  |       var count = encoded.length; | ||||||
|  |       var error = false; | ||||||
|  |       for (var i = 0; i < count; i++) { | ||||||
|  |         var value = encoded[i]; | ||||||
|  |         if (value < 32) { | ||||||
|  |           if (value === 12) { | ||||||
|  |             value = (value << 8) + encoded[++i]; | ||||||
|  |           } | ||||||
|  |           switch (value) { | ||||||
|  |             case 1: // hstem
 | ||||||
|  |               if (!HINTING_ENABLED) { | ||||||
|  |                 this.stack = []; | ||||||
|  |                 break; | ||||||
|  |               } | ||||||
|  |               error = this.executeCommand(2, COMMAND_MAP.hstem); | ||||||
|  |               break; | ||||||
|  |             case 3: // vstem
 | ||||||
|  |               if (!HINTING_ENABLED) { | ||||||
|  |                 this.stack = []; | ||||||
|  |                 break; | ||||||
|  |               } | ||||||
|  |               error = this.executeCommand(2, COMMAND_MAP.vstem); | ||||||
|  |               break; | ||||||
|  |             case 4: // vmoveto
 | ||||||
|  |               if (this.flexing) { | ||||||
|  |                 if (this.stack.length < 1) { | ||||||
|  |                   error = true; | ||||||
|  |                   break; | ||||||
|  |                 } | ||||||
|  |                 // Add the dx for flex and but also swap the values so they are
 | ||||||
|  |                 // the right order.
 | ||||||
|  |                 var dy = this.stack.pop(); | ||||||
|  |                 this.stack.push(0, dy); | ||||||
|  |                 break; | ||||||
|  |               } | ||||||
|  |               error = this.executeCommand(1, COMMAND_MAP.vmoveto); | ||||||
|  |               break; | ||||||
|  |             case 5: // rlineto
 | ||||||
|  |               error = this.executeCommand(2, COMMAND_MAP.rlineto); | ||||||
|  |               break; | ||||||
|  |             case 6: // hlineto
 | ||||||
|  |               error = this.executeCommand(1, COMMAND_MAP.hlineto); | ||||||
|  |               break; | ||||||
|  |             case 7: // vlineto
 | ||||||
|  |               error = this.executeCommand(1, COMMAND_MAP.vlineto); | ||||||
|  |               break; | ||||||
|  |             case 8: // rrcurveto
 | ||||||
|  |               error = this.executeCommand(6, COMMAND_MAP.rrcurveto); | ||||||
|  |               break; | ||||||
|  |             case 9: // closepath
 | ||||||
|  |               // closepath is a Type1 command that does not take argument and is
 | ||||||
|  |               // useless in Type2 and it can simply be ignored.
 | ||||||
|  |               this.stack = []; | ||||||
|  |               break; | ||||||
|  |             case 10: // callsubr
 | ||||||
|  |               if (this.stack.length < 1) { | ||||||
|  |                 error = true; | ||||||
|  |                 break; | ||||||
|  |               } | ||||||
|  |               var subrNumber = this.stack.pop(); | ||||||
|  |               error = this.convert(subrs[subrNumber], subrs); | ||||||
|  |               break; | ||||||
|  |             case 11: // return
 | ||||||
|  |               return error; | ||||||
|  |               break; | ||||||
|  |             case 13: // hsbw
 | ||||||
|  |               if (this.stack.length < 2) { | ||||||
|  |                 error = true; | ||||||
|  |                 break; | ||||||
|  |               } | ||||||
|  |               // To convert to type2 we have to move the width value to the
 | ||||||
|  |               // first part of the charstring and then use hmoveto with lsb.
 | ||||||
|  |               var wx = this.stack.pop(); | ||||||
|  |               var sbx = this.stack.pop(); | ||||||
|  |               this.lsb = sbx; | ||||||
|  |               this.width = wx; | ||||||
|  |               this.stack.push(sbx); | ||||||
|  |               error = this.executeCommand(1, COMMAND_MAP.hmoveto); | ||||||
|  |               break; | ||||||
|  |             case 14: // endchar
 | ||||||
|  |               this.output.push(COMMAND_MAP.endchar[0]); | ||||||
|  |               break; | ||||||
|  |             case 21: // rmoveto
 | ||||||
|  |               if (this.flexing) { | ||||||
|  |                 break; | ||||||
|  |               } | ||||||
|  |               error = this.executeCommand(2, COMMAND_MAP.rmoveto); | ||||||
|  |               break; | ||||||
|  |             case 22: // hmoveto
 | ||||||
|  |               if (this.flexing) { | ||||||
|  |                 // Add the dy for flex.
 | ||||||
|  |                 this.stack.push(0); | ||||||
|  |                 break; | ||||||
|  |               } | ||||||
|  |               error = this.executeCommand(1, COMMAND_MAP.hmoveto); | ||||||
|  |               break; | ||||||
|  |             case 30: // vhcurveto
 | ||||||
|  |               error = this.executeCommand(4, COMMAND_MAP.vhcurveto); | ||||||
|  |               break; | ||||||
|  |             case 31: // hvcurveto
 | ||||||
|  |               error = this.executeCommand(4, COMMAND_MAP.hvcurveto); | ||||||
|  |               break; | ||||||
|  |             case (12 << 8) + 0: // dotsection
 | ||||||
|  |               // dotsection is a Type1 command to specify some hinting feature
 | ||||||
|  |               // for dots that do not take a parameter and it can safely be
 | ||||||
|  |               // ignored for Type2.
 | ||||||
|  |               this.stack = []; | ||||||
|  |               break; | ||||||
|  |             case (12 << 8) + 1: // vstem3
 | ||||||
|  |               if (!HINTING_ENABLED) { | ||||||
|  |                 this.stack = []; | ||||||
|  |                 break; | ||||||
|  |               } | ||||||
|  |               // [vh]stem3 are Type1 only and Type2 supports [vh]stem with
 | ||||||
|  |               // multiple parameters, so instead of returning [vh]stem3 take a
 | ||||||
|  |               // shortcut and return [vhstem] instead.
 | ||||||
|  |               error = this.executeCommand(2, COMMAND_MAP.vstem); | ||||||
|  |               break; | ||||||
|  |             case (12 << 8) + 2: // hstem3
 | ||||||
|  |               if (!HINTING_ENABLED) { | ||||||
|  |                  this.stack = []; | ||||||
|  |                 break; | ||||||
|  |               } | ||||||
|  |               // See vstem3.
 | ||||||
|  |               error = this.executeCommand(2, COMMAND_MAP.hstem); | ||||||
|  |               break; | ||||||
|  |             case (12 << 8) + 6: // seac
 | ||||||
|  |               // seac is like type 2's special endchar but it doesn't use the
 | ||||||
|  |               // first argument asb, so remove it.
 | ||||||
|  |               error = this.executeCommand(4, COMMAND_MAP.endchar); | ||||||
|  |               break; | ||||||
|  |             case (12 << 8) + 7: // sbw
 | ||||||
|  |               if (this.stack.length < 4) { | ||||||
|  |                 error = true; | ||||||
|  |                 break; | ||||||
|  |               } | ||||||
|  |               // To convert to type2 we have to move the width value to the
 | ||||||
|  |               // first part of the charstring and then use rmoveto with
 | ||||||
|  |               // (dx, dy). The height argument will not be used for vmtx and
 | ||||||
|  |               // vhea tables reconstruction -- ignoring it.
 | ||||||
|  |               var wy = this.stack.pop(); | ||||||
|  |               var wx = this.stack.pop(); | ||||||
|  |               var sby = this.stack.pop(); | ||||||
|  |               var sbx = this.stack.pop(); | ||||||
|  |               this.lsb = sbx; | ||||||
|  |               this.width = wx; | ||||||
|  |               this.stack.push(sbx, sby); | ||||||
|  |               error = this.executeCommand(2, COMMAND_MAP.rmoveto); | ||||||
|  |               break; | ||||||
|  |             case (12 << 8) + 12: // div
 | ||||||
|  |               if (this.stack.length < 2) { | ||||||
|  |                 error = true; | ||||||
|  |                 break; | ||||||
|  |               } | ||||||
|  |               var num2 = this.stack.pop(); | ||||||
|  |               var num1 = this.stack.pop(); | ||||||
|  |               this.stack.push(num1 / num2); | ||||||
|  |               break; | ||||||
|  |             case (12 << 8) + 16: // callothersubr
 | ||||||
|  |               if (this.stack.length < 2) { | ||||||
|  |                 error = true; | ||||||
|  |                 break; | ||||||
|  |               } | ||||||
|  |               var subrNumber = this.stack.pop(); | ||||||
|  |               var numArgs = this.stack.pop(); | ||||||
|  |               if (subrNumber === 0 && numArgs === 3) { | ||||||
|  |                 var flexArgs = this.stack.splice(this.stack.length - 17, 17); | ||||||
|  |                 this.stack.push( | ||||||
|  |                   flexArgs[2] + flexArgs[0], // bcp1x + rpx
 | ||||||
|  |                   flexArgs[3] + flexArgs[1], // bcp1y + rpy
 | ||||||
|  |                   flexArgs[4], // bcp2x
 | ||||||
|  |                   flexArgs[5], // bcp2y
 | ||||||
|  |                   flexArgs[6], // p2x
 | ||||||
|  |                   flexArgs[7], // p2y
 | ||||||
|  |                   flexArgs[8], // bcp3x
 | ||||||
|  |                   flexArgs[9], // bcp3y
 | ||||||
|  |                   flexArgs[10], // bcp4x
 | ||||||
|  |                   flexArgs[11], // bcp4y
 | ||||||
|  |                   flexArgs[12], // p3x
 | ||||||
|  |                   flexArgs[13], // p3y
 | ||||||
|  |                   flexArgs[14] // flexDepth
 | ||||||
|  |                   // 15 = finalx unused by flex
 | ||||||
|  |                   // 16 = finaly unused by flex
 | ||||||
|  |                 ); | ||||||
|  |                 error = this.executeCommand(13, COMMAND_MAP.flex, true); | ||||||
|  |                 this.flexing = false; | ||||||
|  |                 this.stack.push(flexArgs[15], flexArgs[16]); | ||||||
|  |               } else if (subrNumber === 1 && numArgs === 0) { | ||||||
|  |                 this.flexing = true; | ||||||
|  |               } | ||||||
|  |               break; | ||||||
|  |             case (12 << 8) + 17: // pop
 | ||||||
|  |               // Ignore this since it is only used with othersubr.
 | ||||||
|  |               break; | ||||||
|  |             case (12 << 8) + 33: // setcurrentpoint
 | ||||||
|  |               // Ignore for now.
 | ||||||
|  |               this.stack = []; | ||||||
|  |               break; | ||||||
|  |             default: | ||||||
|  |               warn('Unknown type 1 charstring command of "' + value + '"'); | ||||||
|  |               break; | ||||||
|  |           } | ||||||
|  |           if (error) { | ||||||
|  |             break; | ||||||
|  |           } | ||||||
|  |           continue; | ||||||
|  |         } else if (value <= 246) { | ||||||
|  |           value = value - 139; | ||||||
|  |         } else if (value <= 250) { | ||||||
|  |           value = ((value - 247) * 256) + encoded[++i] + 108; | ||||||
|  |         } else if (value <= 254) { | ||||||
|  |           value = -((value - 251) * 256) - encoded[++i] - 108; | ||||||
|  |         } else { | ||||||
|  |           value = (encoded[++i] & 0xff) << 24 | (encoded[++i] & 0xff) << 16 | | ||||||
|  |                   (encoded[++i] & 0xff) << 8 | (encoded[++i] & 0xff) << 0; | ||||||
|  |         } | ||||||
|  |         this.stack.push(value); | ||||||
|  |       } | ||||||
|  |       return error; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     executeCommand: function(howManyArgs, command, keepStack) { | ||||||
|  |       var stackLength = this.stack.length; | ||||||
|  |       if (howManyArgs > stackLength) { | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |       var start = stackLength - howManyArgs; | ||||||
|  |       for (var i = start; i < stackLength; i++) { | ||||||
|  |         var value = this.stack[i]; | ||||||
|  |         if (value === (value | 0)) { // int
 | ||||||
|  |           this.output.push(28, (value >> 8) & 0xff, value & 0xff); | ||||||
|  |         } else { // fixed point
 | ||||||
|  |           value = (65536 * value) | 0; | ||||||
|  |           this.output.push(255, | ||||||
|  |                            (value >> 24) & 0xFF, | ||||||
|  |                            (value >> 16) & 0xFF, | ||||||
|  |                            (value >> 8) & 0xFF, | ||||||
|  |                            value & 0xFF); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       this.output.push.apply(this.output, command); | ||||||
|  |       if (keepStack) { | ||||||
|  |         this.stack.splice(start, howManyArgs); | ||||||
|  |       } else { | ||||||
|  |         this.stack = []; | ||||||
|  |       } | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return Type1CharString; | ||||||
| })(); | })(); | ||||||
| 
 | 
 | ||||||
| /* | /* | ||||||
| @ -4387,338 +4700,6 @@ var Type1Parser = function type1Parser() { | |||||||
|     return decryptedString.slice(discardNumber); |     return decryptedString.slice(discardNumber); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /* |  | ||||||
|    * CharStrings are encoded following the the CharString Encoding sequence |  | ||||||
|    * describe in Chapter 6 of the "Adobe Type1 Font Format" specification. |  | ||||||
|    * The value in a byte indicates a command, a number, or subsequent bytes |  | ||||||
|    * that are to be interpreted in a special way. |  | ||||||
|    * |  | ||||||
|    * CharString Number Encoding: |  | ||||||
|    *  A CharString byte containing the values from 32 through 255 inclusive |  | ||||||
|    *  indicate an integer. These values are decoded in four ranges. |  | ||||||
|    * |  | ||||||
|    * 1. A CharString byte containing a value, v, between 32 and 246 inclusive, |  | ||||||
|    * indicate the integer v - 139. Thus, the integer values from -107 through |  | ||||||
|    * 107 inclusive may be encoded in single byte. |  | ||||||
|    * |  | ||||||
|    * 2. A CharString byte containing a value, v, between 247 and 250 inclusive, |  | ||||||
|    * indicates an integer involving the next byte, w, according to the formula: |  | ||||||
|    * [(v - 247) x 256] + w + 108 |  | ||||||
|    * |  | ||||||
|    * 3. A CharString byte containing a value, v, between 251 and 254 inclusive, |  | ||||||
|    * indicates an integer involving the next byte, w, according to the formula: |  | ||||||
|    * -[(v - 251) * 256] - w - 108 |  | ||||||
|    * |  | ||||||
|    * 4. A CharString containing the value 255 indicates that the next 4 bytes |  | ||||||
|    * are a two complement signed integer. The first of these bytes contains the |  | ||||||
|    * highest order bits, the second byte contains the next higher order bits |  | ||||||
|    * and the fourth byte contain the lowest order bits. |  | ||||||
|    * |  | ||||||
|    * |  | ||||||
|    * CharString Command Encoding: |  | ||||||
|    *  CharStrings commands are encoded in 1 or 2 bytes. |  | ||||||
|    * |  | ||||||
|    *  Single byte commands are encoded in 1 byte that contains a value between |  | ||||||
|    *  0 and 31 inclusive. |  | ||||||
|    *  If a command byte contains the value 12, then the value in the next byte |  | ||||||
|    *  indicates a command. This "escape" mechanism allows many extra commands |  | ||||||
|    * to be encoded and this encoding technique helps to minimize the length of |  | ||||||
|    * the charStrings. |  | ||||||
|    */ |  | ||||||
|   var charStringDictionary = { |  | ||||||
|     '1': 'hstem', |  | ||||||
|     '3': 'vstem', |  | ||||||
|     '4': 'vmoveto', |  | ||||||
|     '5': 'rlineto', |  | ||||||
|     '6': 'hlineto', |  | ||||||
|     '7': 'vlineto', |  | ||||||
|     '8': 'rrcurveto', |  | ||||||
| 
 |  | ||||||
|     // closepath is a Type1 command that do not take argument and is useless
 |  | ||||||
|     // in Type2 and it can simply be ignored.
 |  | ||||||
|     '9': null, // closepath
 |  | ||||||
| 
 |  | ||||||
|     '10': 'callsubr', |  | ||||||
| 
 |  | ||||||
|     // return is normally used inside sub-routines to tells to the execution
 |  | ||||||
|     // flow that it can be back to normal.
 |  | ||||||
|     // During the translation process Type1 charstrings will be flattened and
 |  | ||||||
|     // sub-routines will be embedded directly into the charstring directly, so
 |  | ||||||
|     // this can be ignored safely.
 |  | ||||||
|     '11': 'return', |  | ||||||
| 
 |  | ||||||
|     '12': { |  | ||||||
|       // dotsection is a Type1 command to specify some hinting feature for dots
 |  | ||||||
|       // that do not take a parameter and it can safely be ignored for Type2.
 |  | ||||||
|       '0': null, // dotsection
 |  | ||||||
| 
 |  | ||||||
|       // [vh]stem3 are Type1 only and Type2 supports [vh]stem with multiple
 |  | ||||||
|       // parameters, so instead of returning [vh]stem3 take a shortcut and
 |  | ||||||
|       // return [vhstem] instead.
 |  | ||||||
|       '1': 'vstem', |  | ||||||
|       '2': 'hstem', |  | ||||||
| 
 |  | ||||||
|       '6': 'endchar', // seac
 |  | ||||||
|       // Type1 only command with command not (yet) built-in ,throw an error
 |  | ||||||
|       '7': -1, // sbw
 |  | ||||||
| 
 |  | ||||||
|       '10': 'add', |  | ||||||
|       '11': 'sub', |  | ||||||
|       '12': 'div', |  | ||||||
| 
 |  | ||||||
|       // callothersubr is a mechanism to make calls on the postscript
 |  | ||||||
|       // interpreter, this is not supported by Type2 charstring but hopefully
 |  | ||||||
|       // most of the default commands can be ignored safely.
 |  | ||||||
|       '16': 'callothersubr', |  | ||||||
| 
 |  | ||||||
|       '17': 'pop', |  | ||||||
| 
 |  | ||||||
|       // setcurrentpoint sets the current point to x, y without performing a
 |  | ||||||
|       // moveto (this is a one shot positionning command). This is used only
 |  | ||||||
|       // with the return of an OtherSubrs call.
 |  | ||||||
|       // TODO Implement the OtherSubrs charstring embedding and replace this
 |  | ||||||
|       // call by a no-op, like 2 'pop' commands for example.
 |  | ||||||
|       '33': null // setcurrentpoint
 |  | ||||||
|     }, |  | ||||||
|     '13': 'hsbw', |  | ||||||
|     '14': 'endchar', |  | ||||||
|     '21': 'rmoveto', |  | ||||||
|     '22': 'hmoveto', |  | ||||||
|     '30': 'vhcurveto', |  | ||||||
|     '31': 'hvcurveto' |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   var ESCAPE_CMD = 12; |  | ||||||
| 
 |  | ||||||
|   // Breaks up the stack by arguments and also calculates the value.
 |  | ||||||
|   function breakUpArgs(stack, numArgs) { |  | ||||||
|     var args = []; |  | ||||||
|     var index = stack.length - 1; |  | ||||||
|     for (var i = 0; i < numArgs; i++) { |  | ||||||
|       if (index < 0) { |  | ||||||
|         args.unshift({ arg: [0], |  | ||||||
|                        value: 0, |  | ||||||
|                        offset: 0 }); |  | ||||||
|         warn('Malformed charstring stack: not enough values on stack.'); |  | ||||||
|         continue; |  | ||||||
|       } |  | ||||||
|       var token = stack[index]; |  | ||||||
|       if (token === 'div') { |  | ||||||
|         var a = stack[index - 2]; |  | ||||||
|         var b = stack[index - 1]; |  | ||||||
|         if (!isInt(a) || !isInt(b)) { |  | ||||||
|           warn('Malformed charsting stack: expected ints on stack for div.'); |  | ||||||
|           a = 0; |  | ||||||
|           b = 1; |  | ||||||
|         } |  | ||||||
|         args.unshift({ arg: [a, b, 'div'], |  | ||||||
|                        value: a / b, |  | ||||||
|                        offset: index - 2 }); |  | ||||||
|         index -= 3; |  | ||||||
|       } else if (isInt(token)) { |  | ||||||
|         args.unshift({ arg: stack.slice(index, index + 1), |  | ||||||
|                        value: token, |  | ||||||
|                        offset: index }); |  | ||||||
|         index--; |  | ||||||
|       } else { |  | ||||||
|         warn('Malformed charsting stack: found bad token ' + token + '.'); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return args; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   function decodeCharString(array) { |  | ||||||
|     var charstring = []; |  | ||||||
|     var lsb = 0; |  | ||||||
|     var width = 0; |  | ||||||
|     var flexing = false; |  | ||||||
| 
 |  | ||||||
|     var value = ''; |  | ||||||
|     var count = array.length; |  | ||||||
|     for (var i = 0; i < count; i++) { |  | ||||||
|       value = array[i]; |  | ||||||
| 
 |  | ||||||
|       if (value < 32) { |  | ||||||
|         var command = null; |  | ||||||
|         if (value == ESCAPE_CMD) { |  | ||||||
|           var escape = array[++i]; |  | ||||||
| 
 |  | ||||||
|           // TODO Clean this code
 |  | ||||||
|           if (escape == 16) { |  | ||||||
|             var index = charstring.pop(); |  | ||||||
|             var argc = charstring.pop(); |  | ||||||
|             for (var j = 0; j < argc; j++) |  | ||||||
|               charstring.push('drop'); |  | ||||||
| 
 |  | ||||||
|             // If the flex mechanism is not used in a font program, Adobe
 |  | ||||||
|             // states that entries 0, 1 and 2 can simply be replaced by
 |  | ||||||
|             // {}, which means that we can simply ignore them.
 |  | ||||||
|             if (index < 3) { |  | ||||||
|               continue; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // This is the same things about hint replacement, if it is not used
 |  | ||||||
|             // entry 3 can be replaced by {3}
 |  | ||||||
|             // TODO support hint replacment
 |  | ||||||
|             if (index == 3) { |  | ||||||
|               charstring.push(3); |  | ||||||
|               i++; |  | ||||||
|               continue; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             assert(argc == 0, 'callothersubr with arguments is not supported'); |  | ||||||
|             charstring.push(new CallothersubrCmd(index)); |  | ||||||
|             continue; |  | ||||||
|           } else if (escape == 7) { // sbw
 |  | ||||||
|             var args = breakUpArgs(charstring, 4); |  | ||||||
|             var arg0 = args[0]; |  | ||||||
|             var arg1 = args[1]; |  | ||||||
|             var arg2 = args[2]; |  | ||||||
|             lsb = arg0.value; |  | ||||||
|             width = arg2.value; |  | ||||||
|             // To convert to type2 we have to move the width value to the first
 |  | ||||||
|             // part of the charstring and then use rmoveto with (dx, dy).
 |  | ||||||
|             // The height argument will not be used for vmtx and vhea tables
 |  | ||||||
|             // reconstruction -- ignoring it.
 |  | ||||||
|             charstring = arg2.arg; |  | ||||||
|             charstring = charstring.concat(arg0.arg, arg1.arg); |  | ||||||
|             charstring.push('rmoveto'); |  | ||||||
|             continue; |  | ||||||
|           } else if (escape == 17 || escape == 33) { |  | ||||||
|             // pop or setcurrentpoint commands can be ignored
 |  | ||||||
|             // since we are not doing callothersubr
 |  | ||||||
|             continue; |  | ||||||
|           } else if (escape == 6) { |  | ||||||
|             // seac is like type 2's special endchar but it doesn't use the
 |  | ||||||
|             // first argument asb, so remove it.
 |  | ||||||
|             var args = breakUpArgs(charstring, 5); |  | ||||||
|             var arg0 = args[0]; |  | ||||||
|             charstring.splice(arg0.offset, arg0.arg.length); |  | ||||||
|           } else if (!HINTING_ENABLED && (escape == 1 || escape == 2)) { |  | ||||||
|             charstring.push('drop', 'drop', 'drop', 'drop', 'drop', 'drop'); |  | ||||||
|             continue; |  | ||||||
|           } |  | ||||||
| 
 |  | ||||||
|           command = charStringDictionary['12'][escape]; |  | ||||||
|         } else { |  | ||||||
|           if (value == 13) { // hsbw
 |  | ||||||
|             var args = breakUpArgs(charstring, 2); |  | ||||||
|             var arg0 = args[0]; |  | ||||||
|             var arg1 = args[1]; |  | ||||||
|             lsb = arg0.value; |  | ||||||
|             width = arg1.value; |  | ||||||
|             // To convert to type2 we have to move the width value to the first
 |  | ||||||
|             // part of the charstring and then use hmoveto with lsb.
 |  | ||||||
|             charstring = arg1.arg; |  | ||||||
|             charstring = charstring.concat(arg0.arg); |  | ||||||
|             charstring.push('hmoveto'); |  | ||||||
|             continue; |  | ||||||
|           } else if (value == 10) { // callsubr
 |  | ||||||
|             if (charstring[charstring.length - 1] < 3) { // subr #0..2
 |  | ||||||
|               // XXX: According to the spec if flex or hinting is not used then
 |  | ||||||
|               // subroutines 0-3 can actually be anything defined by the font,
 |  | ||||||
|               // so we really shouldn't be doing flex here but when
 |  | ||||||
|               // callothersubr 0-2 is used. There hasn't been a real world
 |  | ||||||
|               // example of this yet so we'll keep doing it here.
 |  | ||||||
|               var subrNumber = charstring.pop(); |  | ||||||
|               switch (subrNumber) { |  | ||||||
|                 case 1: |  | ||||||
|                   flexing = true; // prepare for flex coordinates
 |  | ||||||
|                   break; |  | ||||||
|                 case 0: |  | ||||||
|                   var flexArgs = breakUpArgs(charstring, 17); |  | ||||||
| 
 |  | ||||||
|                   // removing all flex arguments from the stack
 |  | ||||||
|                   charstring.splice(flexArgs[0].offset, |  | ||||||
|                                     charstring.length - flexArgs[0].offset); |  | ||||||
| 
 |  | ||||||
|                   charstring = charstring.concat( |  | ||||||
|                     flexArgs[0].arg, // bcp1x +
 |  | ||||||
|                     flexArgs[2].arg, // rpx
 |  | ||||||
|                     ['add'], |  | ||||||
|                     flexArgs[1].arg, // bcp1y +
 |  | ||||||
|                     flexArgs[3].arg, // rpy
 |  | ||||||
|                     ['add'], |  | ||||||
|                     flexArgs[4].arg, // bcp2x
 |  | ||||||
|                     flexArgs[5].arg, // bcp2y
 |  | ||||||
|                     flexArgs[6].arg, // p2x
 |  | ||||||
|                     flexArgs[7].arg, // p2y
 |  | ||||||
|                     flexArgs[8].arg, // bcp3x
 |  | ||||||
|                     flexArgs[9].arg, // bcp3y
 |  | ||||||
|                     flexArgs[10].arg, // bcp4x
 |  | ||||||
|                     flexArgs[11].arg, // bcp4y
 |  | ||||||
|                     flexArgs[12].arg, // p3x
 |  | ||||||
|                     flexArgs[13].arg, // p3y
 |  | ||||||
|                     flexArgs[14].arg, // flexDepth
 |  | ||||||
|                     // 15 = finalx unused by flex
 |  | ||||||
|                     // 16 = finaly unused by flex
 |  | ||||||
|                     ['flex'] |  | ||||||
|                   ); |  | ||||||
| 
 |  | ||||||
|                   flexing = false; |  | ||||||
|                   break; |  | ||||||
|               } |  | ||||||
|               continue; |  | ||||||
|             } |  | ||||||
|           } else if (value == 21 && flexing) { // rmoveto
 |  | ||||||
|             continue; // ignoring rmoveto
 |  | ||||||
|           } else if (value == 22 && flexing) { // hmoveto
 |  | ||||||
|             // Add the dy for flex.
 |  | ||||||
|             charstring.push(0); |  | ||||||
|             continue; // ignoring hmoveto
 |  | ||||||
|           } else if (value == 4 && flexing) { // vmoveto
 |  | ||||||
|             // Insert the dx for flex before dy.
 |  | ||||||
|             var flexArgs = breakUpArgs(charstring, 1); |  | ||||||
|             charstring.splice(flexArgs[0].offset, 0, 0); |  | ||||||
|             continue; // ignoring vmoveto
 |  | ||||||
|           } else if (!HINTING_ENABLED && (value == 1 || value == 3)) { |  | ||||||
|             charstring.push('drop', 'drop'); |  | ||||||
|             continue; |  | ||||||
|           } |  | ||||||
|           command = charStringDictionary[value]; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Some charstring commands are meaningless in Type2 and will return
 |  | ||||||
|         // a null, let's just ignored them
 |  | ||||||
|         if (!command && i < count) { |  | ||||||
|           continue; |  | ||||||
|         } else if (!command) { |  | ||||||
|           break; |  | ||||||
|         } else if (command == -1) { |  | ||||||
|           warn('Support for Type1 command ' + value + |  | ||||||
|                 ' (' + escape + ') is not implemented in charstring: ' + |  | ||||||
|                 charstring); |  | ||||||
|           if (value == 12) { |  | ||||||
|             // we know how to ignore only some the Type1 commands
 |  | ||||||
|             switch (escape) { |  | ||||||
|               case 7: |  | ||||||
|                 charstring.push('drop', 'drop', 'drop', 'drop'); |  | ||||||
|                 continue; |  | ||||||
|               case 8: |  | ||||||
|                 charstring.push('drop'); |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         value = command; |  | ||||||
|       } else if (value <= 246) { |  | ||||||
|         value = value - 139; |  | ||||||
|       } else if (value <= 250) { |  | ||||||
|         value = ((value - 247) * 256) + array[++i] + 108; |  | ||||||
|       } else if (value <= 254) { |  | ||||||
|         value = -((value - 251) * 256) - array[++i] - 108; |  | ||||||
|       } else { |  | ||||||
|         value = (array[++i] & 0xff) << 24 | (array[++i] & 0xff) << 16 | |  | ||||||
|                 (array[++i] & 0xff) << 8 | (array[++i] & 0xff) << 0; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       charstring.push(value); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return { charstring: charstring, width: width, lsb: lsb }; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* |   /* | ||||||
|    * Returns an object containing a Subrs array and a CharStrings |    * Returns an object containing a Subrs array and a CharStrings | ||||||
|    * array extracted from and eexec encrypted block of data |    * array extracted from and eexec encrypted block of data | ||||||
| @ -4786,6 +4767,7 @@ var Type1Parser = function type1Parser() { | |||||||
|       eexecStr += String.fromCharCode(eexec[i]); |       eexecStr += String.fromCharCode(eexec[i]); | ||||||
| 
 | 
 | ||||||
|     var glyphsSection = false, subrsSection = false; |     var glyphsSection = false, subrsSection = false; | ||||||
|  |     var subrs = [], charstrings = []; | ||||||
|     var program = { |     var program = { | ||||||
|       subrs: [], |       subrs: [], | ||||||
|       charstrings: [], |       charstrings: [], | ||||||
| @ -4821,17 +4803,14 @@ var Type1Parser = function type1Parser() { | |||||||
|         var data = eexec.slice(i, i + length); |         var data = eexec.slice(i, i + length); | ||||||
|         var lenIV = program.properties.privateData['lenIV']; |         var lenIV = program.properties.privateData['lenIV']; | ||||||
|         var encoded = decrypt(data, CHAR_STRS_ENCRYPT_KEY, lenIV); |         var encoded = decrypt(data, CHAR_STRS_ENCRYPT_KEY, lenIV); | ||||||
|         var str = decodeCharString(encoded); |  | ||||||
| 
 | 
 | ||||||
|         if (glyphsSection) { |         if (glyphsSection) { | ||||||
|           program.charstrings.push({ |           charstrings.push({ | ||||||
|             glyph: glyph, |             glyph: glyph, | ||||||
|             data: str.charstring, |             encoded: encoded | ||||||
|             lsb: str.lsb, |  | ||||||
|             width: str.width |  | ||||||
|           }); |           }); | ||||||
|         } else { |         } else { | ||||||
|           program.subrs.push(str.charstring); |           subrs.push(encoded); | ||||||
|         } |         } | ||||||
|         i += length; |         i += length; | ||||||
|         token = ''; |         token = ''; | ||||||
| @ -4863,12 +4842,11 @@ var Type1Parser = function type1Parser() { | |||||||
|                 var data = eexec.slice(i + 1, i + 1 + length); |                 var data = eexec.slice(i + 1, i + 1 + length); | ||||||
|                 var lenIV = program.properties.privateData['lenIV']; |                 var lenIV = program.properties.privateData['lenIV']; | ||||||
|                 var encoded = decrypt(data, CHAR_STRS_ENCRYPT_KEY, lenIV); |                 var encoded = decrypt(data, CHAR_STRS_ENCRYPT_KEY, lenIV); | ||||||
|                 var str = decodeCharString(encoded); |  | ||||||
|                 i = i + 1 + length; |                 i = i + 1 + length; | ||||||
|                 t = getToken(); // read in 'NP'
 |                 t = getToken(); // read in 'NP'
 | ||||||
|                 if (t == 'noaccess') |                 if (t == 'noaccess') | ||||||
|                   getToken(); // read in 'put'
 |                   getToken(); // read in 'put'
 | ||||||
|                 program.subrs[index] = str.charstring; |                 subrs[index] = encoded; | ||||||
|               } |               } | ||||||
|               break; |               break; | ||||||
|             case '/BlueValues': |             case '/BlueValues': | ||||||
| @ -4915,6 +4893,26 @@ var Type1Parser = function type1Parser() { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     for (var i = 0; i < charstrings.length; i++) { | ||||||
|  |       var glyph = charstrings[i].glyph; | ||||||
|  |       var encoded = charstrings[i].encoded; | ||||||
|  |       var charString = new Type1CharString(); | ||||||
|  |       var error = charString.convert(encoded, subrs); | ||||||
|  |       var output = charString.output; | ||||||
|  |       if (error) { | ||||||
|  |         // It seems when FreeType encounters an error while evaluating a glyph
 | ||||||
|  |         // that it completely ignores the glyph so we'll mimic that behaviour
 | ||||||
|  |         // here and put an endchar to make the validator happy.
 | ||||||
|  |         output = [14]; | ||||||
|  |       } | ||||||
|  |       program.charstrings.push({ | ||||||
|  |         glyph: glyph, | ||||||
|  |         data: output, | ||||||
|  |         lsb: charString.lsb, | ||||||
|  |         width: charString.width | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return program; |     return program; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| @ -5106,14 +5104,11 @@ Type1Font.prototype = { | |||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   getType2Charstrings: function Type1Font_getType2Charstrings( |   getType2Charstrings: function Type1Font_getType2Charstrings( | ||||||
|                                   type1Subrs) { |                                   type1Charstrings) { | ||||||
|     var type2Charstrings = []; |     var type2Charstrings = []; | ||||||
|     var count = type1Subrs.length; |     for (var i = 0, ii = type1Charstrings.length; i < ii; i++) { | ||||||
|     var type1Charstrings = []; |       type2Charstrings.push(type1Charstrings[i].charstring); | ||||||
|     for (var i = 0; i < count; i++) |     } | ||||||
|       type1Charstrings.push(type1Subrs[i].charstring.slice()); |  | ||||||
|     for (var i = 0; i < count; i++) |  | ||||||
|       type2Charstrings.push(this.flattenCharstring(type1Charstrings, i)); |  | ||||||
|     return type2Charstrings; |     return type2Charstrings; | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
| @ -5133,81 +5128,12 @@ Type1Font.prototype = { | |||||||
|       type2Subrs.push([0x0B]); |       type2Subrs.push([0x0B]); | ||||||
| 
 | 
 | ||||||
|     for (var i = 0; i < count; i++) { |     for (var i = 0; i < count; i++) { | ||||||
|       type2Subrs.push(this.flattenCharstring(type1Subrs, i)); |       type2Subrs.push(type1Subrs[i]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return type2Subrs; |     return type2Subrs; | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   /* |  | ||||||
|    * Flatten the commands by interpreting the postscript code and replacing |  | ||||||
|    * every 'callsubr', 'callothersubr' by the real commands. |  | ||||||
|    */ |  | ||||||
|   commandsMap: { |  | ||||||
|     'hstem': 1, |  | ||||||
|     'vstem': 3, |  | ||||||
|     'vmoveto': 4, |  | ||||||
|     'rlineto': 5, |  | ||||||
|     'hlineto': 6, |  | ||||||
|     'vlineto': 7, |  | ||||||
|     'rrcurveto': 8, |  | ||||||
|     'callsubr': 10, |  | ||||||
|     'return': 11, |  | ||||||
|     'add': [12, 10], |  | ||||||
|     'sub': [12, 11], |  | ||||||
|     'div': [12, 12], |  | ||||||
|     'exch': [12, 28], |  | ||||||
|     'flex': [12, 35], |  | ||||||
|     'drop' : [12, 18], |  | ||||||
|     'endchar': 14, |  | ||||||
|     'rmoveto': 21, |  | ||||||
|     'hmoveto': 22, |  | ||||||
|     'vhcurveto': 30, |  | ||||||
|     'hvcurveto': 31 |  | ||||||
|   }, |  | ||||||
| 
 |  | ||||||
|   flattenCharstring: function Type1Font_flattenCharstring(charstrings, index) { |  | ||||||
|     var charstring = charstrings[index]; |  | ||||||
|     if (!charstring) |  | ||||||
|       return [0x0B]; |  | ||||||
|     var map = this.commandsMap; |  | ||||||
|     // charstring changes size - can't cache .length in loop
 |  | ||||||
|     for (var i = 0; i < charstring.length; i++) { |  | ||||||
|       var command = charstring[i]; |  | ||||||
|       if (typeof command === 'string') { |  | ||||||
|         var cmd = map[command]; |  | ||||||
|         assert(cmd, 'Unknow command: ' + command); |  | ||||||
| 
 |  | ||||||
|         if (isArray(cmd)) |  | ||||||
|           charstring.splice(i++, 1, cmd[0], cmd[1]); |  | ||||||
|         else |  | ||||||
|           charstring[i] = cmd; |  | ||||||
|       } else if (command instanceof CallothersubrCmd) { |  | ||||||
|         var otherSubrCharstring = charstrings[command.index]; |  | ||||||
|         if (otherSubrCharstring) { |  | ||||||
|           var lastCommand = otherSubrCharstring.indexOf('return'); |  | ||||||
|           if (lastCommand >= 0) |  | ||||||
|             otherSubrCharstring = otherSubrCharstring.slice(0, lastCommand); |  | ||||||
|           charstring.splice.apply(charstring, |  | ||||||
|                                   [i, 1].concat(otherSubrCharstring)); |  | ||||||
|         } else |  | ||||||
|           charstring.splice(i, 1); // ignoring empty subr call
 |  | ||||||
|         i--; |  | ||||||
|       } else { |  | ||||||
|         // Type1 charstring use a division for number above 32000
 |  | ||||||
|         if (command > 32000) { |  | ||||||
|           var divisor = charstring[i + 1]; |  | ||||||
|           command /= divisor; |  | ||||||
|           charstring.splice(i, 3, 28, (command >> 8) & 0xff, command & 0xff); |  | ||||||
|         } else { |  | ||||||
|           charstring.splice(i, 1, 28, (command >> 8) & 0xff, command & 0xff); |  | ||||||
|         } |  | ||||||
|         i += 2; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return charstring; |  | ||||||
|   }, |  | ||||||
| 
 |  | ||||||
|   wrap: function Type1Font_wrap(name, glyphs, charstrings, subrs, properties) { |   wrap: function Type1Font_wrap(name, glyphs, charstrings, subrs, properties) { | ||||||
|     var cff = new CFF(); |     var cff = new CFF(); | ||||||
|     cff.header = new CFFHeader(1, 0, 4, 4); |     cff.header = new CFFHeader(1, 0, 4, 4); | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								test/pdfs/issue1936.pdf.link
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								test/pdfs/issue1936.pdf.link
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | http://sci2s.ugr.es/keel/pdf/specific/articulo/alp99.pdf | ||||||
|  | 
 | ||||||
| @ -776,6 +776,14 @@ | |||||||
|       "link": true, |       "link": true, | ||||||
|       "type": "eq" |       "type": "eq" | ||||||
|     }, |     }, | ||||||
|  |     {  "id": "issue1936", | ||||||
|  |       "file": "pdfs/issue1936.pdf", | ||||||
|  |       "md5": "7302eb9b6a626308e2a933aaed9e1756", | ||||||
|  |       "rounds": 1, | ||||||
|  |       "pageLimit": 1, | ||||||
|  |       "link": true, | ||||||
|  |       "type": "eq" | ||||||
|  |     }, | ||||||
|     {  "id": "issue2337", |     {  "id": "issue2337", | ||||||
|       "file": "pdfs/issue2337.pdf", |       "file": "pdfs/issue2337.pdf", | ||||||
|       "md5": "ea10f4131202b9b8f2a6cb7770d3f185", |       "md5": "ea10f4131202b9b8f2a6cb7770d3f185", | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user