Convert src/core/type1_font.js to use standard classes
				
					
				
			This commit is contained in:
		
							parent
							
								
									4bd69556ab
								
							
						
					
					
						commit
						f64b7922b3
					
				| @ -30,129 +30,130 @@ import { Stream } from "./stream.js"; | |||||||
| import { Type1Parser } from "./type1_parser.js"; | import { Type1Parser } from "./type1_parser.js"; | ||||||
| import { warn } from "../shared/util.js"; | import { warn } from "../shared/util.js"; | ||||||
| 
 | 
 | ||||||
| // Type1Font is also a CIDFontType0.
 | function findBlock(streamBytes, signature, startIndex) { | ||||||
| const Type1Font = (function Type1FontClosure() { |   const streamBytesLength = streamBytes.length; | ||||||
|   function findBlock(streamBytes, signature, startIndex) { |   const signatureLength = signature.length; | ||||||
|     const streamBytesLength = streamBytes.length; |   const scanLength = streamBytesLength - signatureLength; | ||||||
|     const signatureLength = signature.length; |  | ||||||
|     const scanLength = streamBytesLength - signatureLength; |  | ||||||
| 
 | 
 | ||||||
|     let i = startIndex, |   let i = startIndex, | ||||||
|       found = false; |     found = false; | ||||||
|     while (i < scanLength) { |   while (i < scanLength) { | ||||||
|       let j = 0; |     let j = 0; | ||||||
|       while (j < signatureLength && streamBytes[i + j] === signature[j]) { |     while (j < signatureLength && streamBytes[i + j] === signature[j]) { | ||||||
|         j++; |       j++; | ||||||
|       } |  | ||||||
|       if (j >= signatureLength) { |  | ||||||
|         // `signature` found, skip over whitespace.
 |  | ||||||
|         i += j; |  | ||||||
|         while (i < streamBytesLength && isWhiteSpace(streamBytes[i])) { |  | ||||||
|           i++; |  | ||||||
|         } |  | ||||||
|         found = true; |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|       i++; |  | ||||||
|     } |     } | ||||||
|     return { |     if (j >= signatureLength) { | ||||||
|       found, |       // `signature` found, skip over whitespace.
 | ||||||
|       length: i, |       i += j; | ||||||
|     }; |       while (i < streamBytesLength && isWhiteSpace(streamBytes[i])) { | ||||||
|  |         i++; | ||||||
|  |       } | ||||||
|  |       found = true; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     i++; | ||||||
|  |   } | ||||||
|  |   return { | ||||||
|  |     found, | ||||||
|  |     length: i, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function getHeaderBlock(stream, suggestedLength) { | ||||||
|  |   const EEXEC_SIGNATURE = [0x65, 0x65, 0x78, 0x65, 0x63]; | ||||||
|  | 
 | ||||||
|  |   const streamStartPos = stream.pos; // Save the initial stream position.
 | ||||||
|  |   let headerBytes, headerBytesLength, block; | ||||||
|  |   try { | ||||||
|  |     headerBytes = stream.getBytes(suggestedLength); | ||||||
|  |     headerBytesLength = headerBytes.length; | ||||||
|  |   } catch (ex) { | ||||||
|  |     if (ex instanceof MissingDataException) { | ||||||
|  |       throw ex; | ||||||
|  |     } | ||||||
|  |     // Ignore errors if the `suggestedLength` is huge enough that a Uint8Array
 | ||||||
|  |     // cannot hold the result of `getBytes`, and fallback to simply checking
 | ||||||
|  |     // the entire stream (fixes issue3928.pdf).
 | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function getHeaderBlock(stream, suggestedLength) { |   if (headerBytesLength === suggestedLength) { | ||||||
|     const EEXEC_SIGNATURE = [0x65, 0x65, 0x78, 0x65, 0x63]; |     // Most of the time `suggestedLength` is correct, so to speed things up we
 | ||||||
|  |     // initially only check the last few bytes to see if the header was found.
 | ||||||
|  |     // Otherwise we (potentially) check the entire stream to prevent errors in
 | ||||||
|  |     // `Type1Parser` (fixes issue5686.pdf).
 | ||||||
|  |     block = findBlock( | ||||||
|  |       headerBytes, | ||||||
|  |       EEXEC_SIGNATURE, | ||||||
|  |       suggestedLength - 2 * EEXEC_SIGNATURE.length | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     const streamStartPos = stream.pos; // Save the initial stream position.
 |     if (block.found && block.length === suggestedLength) { | ||||||
|     let headerBytes, headerBytesLength, block; |  | ||||||
|     try { |  | ||||||
|       headerBytes = stream.getBytes(suggestedLength); |  | ||||||
|       headerBytesLength = headerBytes.length; |  | ||||||
|     } catch (ex) { |  | ||||||
|       if (ex instanceof MissingDataException) { |  | ||||||
|         throw ex; |  | ||||||
|       } |  | ||||||
|       // Ignore errors if the `suggestedLength` is huge enough that a Uint8Array
 |  | ||||||
|       // cannot hold the result of `getBytes`, and fallback to simply checking
 |  | ||||||
|       // the entire stream (fixes issue3928.pdf).
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (headerBytesLength === suggestedLength) { |  | ||||||
|       // Most of the time `suggestedLength` is correct, so to speed things up we
 |  | ||||||
|       // initially only check the last few bytes to see if the header was found.
 |  | ||||||
|       // Otherwise we (potentially) check the entire stream to prevent errors in
 |  | ||||||
|       // `Type1Parser` (fixes issue5686.pdf).
 |  | ||||||
|       block = findBlock( |  | ||||||
|         headerBytes, |  | ||||||
|         EEXEC_SIGNATURE, |  | ||||||
|         suggestedLength - 2 * EEXEC_SIGNATURE.length |  | ||||||
|       ); |  | ||||||
| 
 |  | ||||||
|       if (block.found && block.length === suggestedLength) { |  | ||||||
|         return { |  | ||||||
|           stream: new Stream(headerBytes), |  | ||||||
|           length: suggestedLength, |  | ||||||
|         }; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     warn('Invalid "Length1" property in Type1 font -- trying to recover.'); |  | ||||||
|     stream.pos = streamStartPos; // Reset the stream position.
 |  | ||||||
| 
 |  | ||||||
|     const SCAN_BLOCK_LENGTH = 2048; |  | ||||||
|     let actualLength; |  | ||||||
|     while (true) { |  | ||||||
|       const scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH); |  | ||||||
|       block = findBlock(scanBytes, EEXEC_SIGNATURE, 0); |  | ||||||
| 
 |  | ||||||
|       if (block.length === 0) { |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|       stream.pos += block.length; // Update the stream position.
 |  | ||||||
| 
 |  | ||||||
|       if (block.found) { |  | ||||||
|         actualLength = stream.pos - streamStartPos; |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     stream.pos = streamStartPos; // Reset the stream position.
 |  | ||||||
| 
 |  | ||||||
|     if (actualLength) { |  | ||||||
|       return { |       return { | ||||||
|         stream: new Stream(stream.getBytes(actualLength)), |         stream: new Stream(headerBytes), | ||||||
|         length: actualLength, |         length: suggestedLength, | ||||||
|       }; |       }; | ||||||
|     } |     } | ||||||
|     warn('Unable to recover "Length1" property in Type1 font -- using as is.'); |   } | ||||||
|  |   warn('Invalid "Length1" property in Type1 font -- trying to recover.'); | ||||||
|  |   stream.pos = streamStartPos; // Reset the stream position.
 | ||||||
|  | 
 | ||||||
|  |   const SCAN_BLOCK_LENGTH = 2048; | ||||||
|  |   let actualLength; | ||||||
|  |   while (true) { | ||||||
|  |     const scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH); | ||||||
|  |     block = findBlock(scanBytes, EEXEC_SIGNATURE, 0); | ||||||
|  | 
 | ||||||
|  |     if (block.length === 0) { | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     stream.pos += block.length; // Update the stream position.
 | ||||||
|  | 
 | ||||||
|  |     if (block.found) { | ||||||
|  |       actualLength = stream.pos - streamStartPos; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   stream.pos = streamStartPos; // Reset the stream position.
 | ||||||
|  | 
 | ||||||
|  |   if (actualLength) { | ||||||
|     return { |     return { | ||||||
|       stream: new Stream(stream.getBytes(suggestedLength)), |       stream: new Stream(stream.getBytes(actualLength)), | ||||||
|       length: suggestedLength, |       length: actualLength, | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
|  |   warn('Unable to recover "Length1" property in Type1 font -- using as is.'); | ||||||
|  |   return { | ||||||
|  |     stream: new Stream(stream.getBytes(suggestedLength)), | ||||||
|  |     length: suggestedLength, | ||||||
|  |   }; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|   function getEexecBlock(stream, suggestedLength) { | function getEexecBlock(stream, suggestedLength) { | ||||||
|     // We should ideally parse the eexec block to ensure that `suggestedLength`
 |   // We should ideally parse the eexec block to ensure that `suggestedLength`
 | ||||||
|     // is correct, so we don't truncate the block data if it's too small.
 |   // is correct, so we don't truncate the block data if it's too small.
 | ||||||
|     // However, this would also require checking if the fixed-content portion
 |   // However, this would also require checking if the fixed-content portion
 | ||||||
|     // exists (using the 'Length3' property), and ensuring that it's valid.
 |   // exists (using the 'Length3' property), and ensuring that it's valid.
 | ||||||
|     //
 |   //
 | ||||||
|     // Given that `suggestedLength` almost always is correct, all the validation
 |   // Given that `suggestedLength` almost always is correct, all the validation
 | ||||||
|     // would require a great deal of unnecessary parsing for most fonts.
 |   // would require a great deal of unnecessary parsing for most fonts.
 | ||||||
|     // To save time, we always fetch the entire stream instead, which also avoid
 |   // To save time, we always fetch the entire stream instead, which also avoid
 | ||||||
|     // issues if `suggestedLength` is huge (see comment in `getHeaderBlock`).
 |   // issues if `suggestedLength` is huge (see comment in `getHeaderBlock`).
 | ||||||
|     //
 |   //
 | ||||||
|     // NOTE: This means that the function can include the fixed-content portion
 |   // NOTE: This means that the function can include the fixed-content portion
 | ||||||
|     // in the returned eexec block. In practice this does *not* seem to matter,
 |   // in the returned eexec block. In practice this does *not* seem to matter,
 | ||||||
|     // since `Type1Parser_extractFontProgram` will skip over any non-commands.
 |   // since `Type1Parser_extractFontProgram` will skip over any non-commands.
 | ||||||
|     const eexecBytes = stream.getBytes(); |   const eexecBytes = stream.getBytes(); | ||||||
|     return { |   return { | ||||||
|       stream: new Stream(eexecBytes), |     stream: new Stream(eexecBytes), | ||||||
|       length: eexecBytes.length, |     length: eexecBytes.length, | ||||||
|     }; |   }; | ||||||
|   } | } | ||||||
| 
 | 
 | ||||||
|   // eslint-disable-next-line no-shadow
 | /** | ||||||
|   function Type1Font(name, file, properties) { |  * Type1Font is also a CIDFontType0. | ||||||
|  |  */ | ||||||
|  | class Type1Font { | ||||||
|  |   constructor(name, file, properties) { | ||||||
|     // Some bad generators embed pfb file as is, we have to strip 6-byte header.
 |     // Some bad generators embed pfb file as is, we have to strip 6-byte header.
 | ||||||
|     // Also, length1 and length2 might be off by 6 bytes as well.
 |     // Also, length1 and length2 might be off by 6 bytes as well.
 | ||||||
|     // http://www.math.ubc.ca/~cass/piscript/type1.pdf
 |     // http://www.math.ubc.ca/~cass/piscript/type1.pdf
 | ||||||
| @ -215,222 +216,209 @@ const Type1Font = (function Type1FontClosure() { | |||||||
|     this.seacs = this.getSeacs(data.charstrings); |     this.seacs = this.getSeacs(data.charstrings); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Type1Font.prototype = { |   get numGlyphs() { | ||||||
|     get numGlyphs() { |     return this.charstrings.length + 1; | ||||||
|       return this.charstrings.length + 1; |   } | ||||||
|     }, |  | ||||||
| 
 | 
 | ||||||
|     getCharset: function Type1Font_getCharset() { |   getCharset() { | ||||||
|       const charset = [".notdef"]; |     const charset = [".notdef"]; | ||||||
|       const charstrings = this.charstrings; |     const charstrings = this.charstrings; | ||||||
|       for (let glyphId = 0; glyphId < charstrings.length; glyphId++) { |     for (let glyphId = 0; glyphId < charstrings.length; glyphId++) { | ||||||
|         charset.push(charstrings[glyphId].glyphName); |       charset.push(charstrings[glyphId].glyphName); | ||||||
|  |     } | ||||||
|  |     return charset; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   getGlyphMapping(properties) { | ||||||
|  |     const charstrings = this.charstrings; | ||||||
|  | 
 | ||||||
|  |     if (properties.composite) { | ||||||
|  |       const charCodeToGlyphId = Object.create(null); | ||||||
|  |       // Map CIDs directly to GIDs.
 | ||||||
|  |       for ( | ||||||
|  |         let glyphId = 0, charstringsLen = charstrings.length; | ||||||
|  |         glyphId < charstringsLen; | ||||||
|  |         glyphId++ | ||||||
|  |       ) { | ||||||
|  |         const charCode = properties.cMap.charCodeOf(glyphId); | ||||||
|  |         // Add 1 because glyph 0 is duplicated.
 | ||||||
|  |         charCodeToGlyphId[charCode] = glyphId + 1; | ||||||
|       } |       } | ||||||
|       return charset; |       return charCodeToGlyphId; | ||||||
|     }, |     } | ||||||
| 
 | 
 | ||||||
|     getGlyphMapping: function Type1Font_getGlyphMapping(properties) { |     const glyphNames = [".notdef"]; | ||||||
|       const charstrings = this.charstrings; |     let builtInEncoding, glyphId; | ||||||
| 
 |     for (glyphId = 0; glyphId < charstrings.length; glyphId++) { | ||||||
|       if (properties.composite) { |       glyphNames.push(charstrings[glyphId].glyphName); | ||||||
|         const charCodeToGlyphId = Object.create(null); |     } | ||||||
|         // Map CIDs directly to GIDs.
 |     const encoding = properties.builtInEncoding; | ||||||
|         for ( |     if (encoding) { | ||||||
|           let glyphId = 0, charstringsLen = charstrings.length; |       builtInEncoding = Object.create(null); | ||||||
|           glyphId < charstringsLen; |       for (const charCode in encoding) { | ||||||
|           glyphId++ |         glyphId = glyphNames.indexOf(encoding[charCode]); | ||||||
|         ) { |         if (glyphId >= 0) { | ||||||
|           const charCode = properties.cMap.charCodeOf(glyphId); |           builtInEncoding[charCode] = glyphId; | ||||||
|           // Add 1 because glyph 0 is duplicated.
 |  | ||||||
|           charCodeToGlyphId[charCode] = glyphId + 1; |  | ||||||
|         } |  | ||||||
|         return charCodeToGlyphId; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       const glyphNames = [".notdef"]; |  | ||||||
|       let builtInEncoding, glyphId; |  | ||||||
|       for (glyphId = 0; glyphId < charstrings.length; glyphId++) { |  | ||||||
|         glyphNames.push(charstrings[glyphId].glyphName); |  | ||||||
|       } |  | ||||||
|       const encoding = properties.builtInEncoding; |  | ||||||
|       if (encoding) { |  | ||||||
|         builtInEncoding = Object.create(null); |  | ||||||
|         for (const charCode in encoding) { |  | ||||||
|           glyphId = glyphNames.indexOf(encoding[charCode]); |  | ||||||
|           if (glyphId >= 0) { |  | ||||||
|             builtInEncoding[charCode] = glyphId; |  | ||||||
|           } |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|       return type1FontGlyphMapping(properties, builtInEncoding, glyphNames); |     return type1FontGlyphMapping(properties, builtInEncoding, glyphNames); | ||||||
|     }, |   } | ||||||
| 
 | 
 | ||||||
|     hasGlyphId: function Type1Font_hasGlyphID(id) { |   hasGlyphId(id) { | ||||||
|       if (id < 0 || id >= this.numGlyphs) { |     if (id < 0 || id >= this.numGlyphs) { | ||||||
|         return false; |       return false; | ||||||
|  |     } | ||||||
|  |     if (id === 0) { | ||||||
|  |       // notdef is always defined.
 | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     const glyph = this.charstrings[id - 1]; | ||||||
|  |     return glyph.charstring.length > 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   getSeacs(charstrings) { | ||||||
|  |     const seacMap = []; | ||||||
|  |     for (let i = 0, ii = charstrings.length; i < ii; i++) { | ||||||
|  |       const charstring = charstrings[i]; | ||||||
|  |       if (charstring.seac) { | ||||||
|  |         // Offset by 1 for .notdef
 | ||||||
|  |         seacMap[i + 1] = charstring.seac; | ||||||
|       } |       } | ||||||
|       if (id === 0) { |     } | ||||||
|         // notdef is always defined.
 |     return seacMap; | ||||||
|         return true; |   } | ||||||
|       } |  | ||||||
|       const glyph = this.charstrings[id - 1]; |  | ||||||
|       return glyph.charstring.length > 0; |  | ||||||
|     }, |  | ||||||
| 
 | 
 | ||||||
|     getSeacs: function Type1Font_getSeacs(charstrings) { |   getType2Charstrings(type1Charstrings) { | ||||||
|       let i, ii; |     const type2Charstrings = []; | ||||||
|       const seacMap = []; |     for (let i = 0, ii = type1Charstrings.length; i < ii; i++) { | ||||||
|       for (i = 0, ii = charstrings.length; i < ii; i++) { |       type2Charstrings.push(type1Charstrings[i].charstring); | ||||||
|         const charstring = charstrings[i]; |     } | ||||||
|         if (charstring.seac) { |     return type2Charstrings; | ||||||
|           // Offset by 1 for .notdef
 |   } | ||||||
|           seacMap[i + 1] = charstring.seac; | 
 | ||||||
|  |   getType2Subrs(type1Subrs) { | ||||||
|  |     let bias = 0; | ||||||
|  |     const count = type1Subrs.length; | ||||||
|  |     if (count < 1133) { | ||||||
|  |       bias = 107; | ||||||
|  |     } else if (count < 33769) { | ||||||
|  |       bias = 1131; | ||||||
|  |     } else { | ||||||
|  |       bias = 32768; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Add a bunch of empty subrs to deal with the Type2 bias
 | ||||||
|  |     const type2Subrs = []; | ||||||
|  |     let i; | ||||||
|  |     for (i = 0; i < bias; i++) { | ||||||
|  |       type2Subrs.push([0x0b]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (i = 0; i < count; i++) { | ||||||
|  |       type2Subrs.push(type1Subrs[i]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return type2Subrs; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   wrap(name, glyphs, charstrings, subrs, properties) { | ||||||
|  |     const cff = new CFF(); | ||||||
|  |     cff.header = new CFFHeader(1, 0, 4, 4); | ||||||
|  | 
 | ||||||
|  |     cff.names = [name]; | ||||||
|  | 
 | ||||||
|  |     const topDict = new CFFTopDict(); | ||||||
|  |     // CFF strings IDs 0...390 are predefined names, so refering
 | ||||||
|  |     // to entries in our own String INDEX starts at SID 391.
 | ||||||
|  |     topDict.setByName("version", 391); | ||||||
|  |     topDict.setByName("Notice", 392); | ||||||
|  |     topDict.setByName("FullName", 393); | ||||||
|  |     topDict.setByName("FamilyName", 394); | ||||||
|  |     topDict.setByName("Weight", 395); | ||||||
|  |     topDict.setByName("Encoding", null); // placeholder
 | ||||||
|  |     topDict.setByName("FontMatrix", properties.fontMatrix); | ||||||
|  |     topDict.setByName("FontBBox", properties.bbox); | ||||||
|  |     topDict.setByName("charset", null); // placeholder
 | ||||||
|  |     topDict.setByName("CharStrings", null); // placeholder
 | ||||||
|  |     topDict.setByName("Private", null); // placeholder
 | ||||||
|  |     cff.topDict = topDict; | ||||||
|  | 
 | ||||||
|  |     const strings = new CFFStrings(); | ||||||
|  |     strings.add("Version 0.11"); // Version
 | ||||||
|  |     strings.add("See original notice"); // Notice
 | ||||||
|  |     strings.add(name); // FullName
 | ||||||
|  |     strings.add(name); // FamilyName
 | ||||||
|  |     strings.add("Medium"); // Weight
 | ||||||
|  |     cff.strings = strings; | ||||||
|  | 
 | ||||||
|  |     cff.globalSubrIndex = new CFFIndex(); | ||||||
|  | 
 | ||||||
|  |     const count = glyphs.length; | ||||||
|  |     const charsetArray = [".notdef"]; | ||||||
|  |     let i, ii; | ||||||
|  |     for (i = 0; i < count; i++) { | ||||||
|  |       const glyphName = charstrings[i].glyphName; | ||||||
|  |       const index = CFFStandardStrings.indexOf(glyphName); | ||||||
|  |       if (index === -1) { | ||||||
|  |         strings.add(glyphName); | ||||||
|  |       } | ||||||
|  |       charsetArray.push(glyphName); | ||||||
|  |     } | ||||||
|  |     cff.charset = new CFFCharset(false, 0, charsetArray); | ||||||
|  | 
 | ||||||
|  |     const charStringsIndex = new CFFIndex(); | ||||||
|  |     charStringsIndex.add([0x8b, 0x0e]); // .notdef
 | ||||||
|  |     for (i = 0; i < count; i++) { | ||||||
|  |       charStringsIndex.add(glyphs[i]); | ||||||
|  |     } | ||||||
|  |     cff.charStrings = charStringsIndex; | ||||||
|  | 
 | ||||||
|  |     const privateDict = new CFFPrivateDict(); | ||||||
|  |     privateDict.setByName("Subrs", null); // placeholder
 | ||||||
|  |     const fields = [ | ||||||
|  |       "BlueValues", | ||||||
|  |       "OtherBlues", | ||||||
|  |       "FamilyBlues", | ||||||
|  |       "FamilyOtherBlues", | ||||||
|  |       "StemSnapH", | ||||||
|  |       "StemSnapV", | ||||||
|  |       "BlueShift", | ||||||
|  |       "BlueFuzz", | ||||||
|  |       "BlueScale", | ||||||
|  |       "LanguageGroup", | ||||||
|  |       "ExpansionFactor", | ||||||
|  |       "ForceBold", | ||||||
|  |       "StdHW", | ||||||
|  |       "StdVW", | ||||||
|  |     ]; | ||||||
|  |     for (i = 0, ii = fields.length; i < ii; i++) { | ||||||
|  |       const field = fields[i]; | ||||||
|  |       if (!(field in properties.privateData)) { | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       const value = properties.privateData[field]; | ||||||
|  |       if (Array.isArray(value)) { | ||||||
|  |         // All of the private dictionary array data in CFF must be stored as
 | ||||||
|  |         // "delta-encoded" numbers.
 | ||||||
|  |         for (let j = value.length - 1; j > 0; j--) { | ||||||
|  |           value[j] -= value[j - 1]; // ... difference from previous value
 | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       return seacMap; |       privateDict.setByName(field, value); | ||||||
|     }, |     } | ||||||
|  |     cff.topDict.privateDict = privateDict; | ||||||
| 
 | 
 | ||||||
|     getType2Charstrings: function Type1Font_getType2Charstrings( |     const subrIndex = new CFFIndex(); | ||||||
|       type1Charstrings |     for (i = 0, ii = subrs.length; i < ii; i++) { | ||||||
|     ) { |       subrIndex.add(subrs[i]); | ||||||
|       const type2Charstrings = []; |     } | ||||||
|       for (let i = 0, ii = type1Charstrings.length; i < ii; i++) { |     privateDict.subrsIndex = subrIndex; | ||||||
|         type2Charstrings.push(type1Charstrings[i].charstring); |  | ||||||
|       } |  | ||||||
|       return type2Charstrings; |  | ||||||
|     }, |  | ||||||
| 
 | 
 | ||||||
|     getType2Subrs: function Type1Font_getType2Subrs(type1Subrs) { |     const compiler = new CFFCompiler(cff); | ||||||
|       let bias = 0; |     return compiler.compile(); | ||||||
|       const count = type1Subrs.length; |   } | ||||||
|       if (count < 1133) { | } | ||||||
|         bias = 107; |  | ||||||
|       } else if (count < 33769) { |  | ||||||
|         bias = 1131; |  | ||||||
|       } else { |  | ||||||
|         bias = 32768; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       // Add a bunch of empty subrs to deal with the Type2 bias
 |  | ||||||
|       const type2Subrs = []; |  | ||||||
|       let i; |  | ||||||
|       for (i = 0; i < bias; i++) { |  | ||||||
|         type2Subrs.push([0x0b]); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       for (i = 0; i < count; i++) { |  | ||||||
|         type2Subrs.push(type1Subrs[i]); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       return type2Subrs; |  | ||||||
|     }, |  | ||||||
| 
 |  | ||||||
|     wrap: function Type1Font_wrap( |  | ||||||
|       name, |  | ||||||
|       glyphs, |  | ||||||
|       charstrings, |  | ||||||
|       subrs, |  | ||||||
|       properties |  | ||||||
|     ) { |  | ||||||
|       const cff = new CFF(); |  | ||||||
|       cff.header = new CFFHeader(1, 0, 4, 4); |  | ||||||
| 
 |  | ||||||
|       cff.names = [name]; |  | ||||||
| 
 |  | ||||||
|       const topDict = new CFFTopDict(); |  | ||||||
|       // CFF strings IDs 0...390 are predefined names, so refering
 |  | ||||||
|       // to entries in our own String INDEX starts at SID 391.
 |  | ||||||
|       topDict.setByName("version", 391); |  | ||||||
|       topDict.setByName("Notice", 392); |  | ||||||
|       topDict.setByName("FullName", 393); |  | ||||||
|       topDict.setByName("FamilyName", 394); |  | ||||||
|       topDict.setByName("Weight", 395); |  | ||||||
|       topDict.setByName("Encoding", null); // placeholder
 |  | ||||||
|       topDict.setByName("FontMatrix", properties.fontMatrix); |  | ||||||
|       topDict.setByName("FontBBox", properties.bbox); |  | ||||||
|       topDict.setByName("charset", null); // placeholder
 |  | ||||||
|       topDict.setByName("CharStrings", null); // placeholder
 |  | ||||||
|       topDict.setByName("Private", null); // placeholder
 |  | ||||||
|       cff.topDict = topDict; |  | ||||||
| 
 |  | ||||||
|       const strings = new CFFStrings(); |  | ||||||
|       strings.add("Version 0.11"); // Version
 |  | ||||||
|       strings.add("See original notice"); // Notice
 |  | ||||||
|       strings.add(name); // FullName
 |  | ||||||
|       strings.add(name); // FamilyName
 |  | ||||||
|       strings.add("Medium"); // Weight
 |  | ||||||
|       cff.strings = strings; |  | ||||||
| 
 |  | ||||||
|       cff.globalSubrIndex = new CFFIndex(); |  | ||||||
| 
 |  | ||||||
|       const count = glyphs.length; |  | ||||||
|       const charsetArray = [".notdef"]; |  | ||||||
|       let i, ii; |  | ||||||
|       for (i = 0; i < count; i++) { |  | ||||||
|         const glyphName = charstrings[i].glyphName; |  | ||||||
|         const index = CFFStandardStrings.indexOf(glyphName); |  | ||||||
|         if (index === -1) { |  | ||||||
|           strings.add(glyphName); |  | ||||||
|         } |  | ||||||
|         charsetArray.push(glyphName); |  | ||||||
|       } |  | ||||||
|       cff.charset = new CFFCharset(false, 0, charsetArray); |  | ||||||
| 
 |  | ||||||
|       const charStringsIndex = new CFFIndex(); |  | ||||||
|       charStringsIndex.add([0x8b, 0x0e]); // .notdef
 |  | ||||||
|       for (i = 0; i < count; i++) { |  | ||||||
|         charStringsIndex.add(glyphs[i]); |  | ||||||
|       } |  | ||||||
|       cff.charStrings = charStringsIndex; |  | ||||||
| 
 |  | ||||||
|       const privateDict = new CFFPrivateDict(); |  | ||||||
|       privateDict.setByName("Subrs", null); // placeholder
 |  | ||||||
|       const fields = [ |  | ||||||
|         "BlueValues", |  | ||||||
|         "OtherBlues", |  | ||||||
|         "FamilyBlues", |  | ||||||
|         "FamilyOtherBlues", |  | ||||||
|         "StemSnapH", |  | ||||||
|         "StemSnapV", |  | ||||||
|         "BlueShift", |  | ||||||
|         "BlueFuzz", |  | ||||||
|         "BlueScale", |  | ||||||
|         "LanguageGroup", |  | ||||||
|         "ExpansionFactor", |  | ||||||
|         "ForceBold", |  | ||||||
|         "StdHW", |  | ||||||
|         "StdVW", |  | ||||||
|       ]; |  | ||||||
|       for (i = 0, ii = fields.length; i < ii; i++) { |  | ||||||
|         const field = fields[i]; |  | ||||||
|         if (!(field in properties.privateData)) { |  | ||||||
|           continue; |  | ||||||
|         } |  | ||||||
|         const value = properties.privateData[field]; |  | ||||||
|         if (Array.isArray(value)) { |  | ||||||
|           // All of the private dictionary array data in CFF must be stored as
 |  | ||||||
|           // "delta-encoded" numbers.
 |  | ||||||
|           for (let j = value.length - 1; j > 0; j--) { |  | ||||||
|             value[j] -= value[j - 1]; // ... difference from previous value
 |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|         privateDict.setByName(field, value); |  | ||||||
|       } |  | ||||||
|       cff.topDict.privateDict = privateDict; |  | ||||||
| 
 |  | ||||||
|       const subrIndex = new CFFIndex(); |  | ||||||
|       for (i = 0, ii = subrs.length; i < ii; i++) { |  | ||||||
|         subrIndex.add(subrs[i]); |  | ||||||
|       } |  | ||||||
|       privateDict.subrsIndex = subrIndex; |  | ||||||
| 
 |  | ||||||
|       const compiler = new CFFCompiler(cff); |  | ||||||
|       return compiler.compile(); |  | ||||||
|     }, |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   return Type1Font; |  | ||||||
| })(); |  | ||||||
| 
 | 
 | ||||||
| export { Type1Font }; | export { Type1Font }; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user