Best JavaScript code snippet using wpt
fonts.js
Source:fonts.js
...282 return (283 readUint32(header, 0) === 0x00010000 || bytesToString(header) === "true"284 );285}286function isTrueTypeCollectionFile(file) {287 const header = file.peekBytes(4);288 return bytesToString(header) === "ttcf";289}290function isOpenTypeFile(file) {291 const header = file.peekBytes(4);292 return bytesToString(header) === "OTTO";293}294function isType1File(file) {295 const header = file.peekBytes(2);296 // All Type1 font programs must begin with the comment '%!' (0x25 + 0x21).297 if (header[0] === 0x25 && header[1] === 0x21) {298 return true;299 }300 // ... obviously some fonts violate that part of the specification,301 // please refer to the comment in |Type1Font| below (pfb file header).302 if (header[0] === 0x80 && header[1] === 0x01) {303 return true;304 }305 return false;306}307/**308 * Compared to other font formats, the header in CFF files is not constant309 * but contains version numbers. To reduce the possibility of misclassifying310 * font files as CFF, it's recommended to check for other font formats first.311 */312function isCFFFile(file) {313 const header = file.peekBytes(4);314 if (315 /* major version, [1, 255] */ header[0] >= 1 &&316 /* minor version, [0, 255]; header[1] */317 /* header size, [0, 255]; header[2] */318 /* offset(0) size, [1, 4] */ header[3] >= 1 &&319 header[3] <= 4320 ) {321 return true;322 }323 return false;324}325function getFontFileType(file, { type, subtype, composite }) {326 let fileType, fileSubtype;327 if (isTrueTypeFile(file) || isTrueTypeCollectionFile(file)) {328 if (composite) {329 fileType = "CIDFontType2";330 } else {331 fileType = "TrueType";332 }333 } else if (isOpenTypeFile(file)) {334 if (composite) {335 fileType = "CIDFontType2";336 } else {337 fileType = "OpenType";338 }339 } else if (isType1File(file)) {340 if (composite) {341 fileType = "CIDFontType0";342 } else {343 fileType = type === "MMType1" ? "MMType1" : "Type1";344 }345 } else if (isCFFFile(file)) {346 if (composite) {347 fileType = "CIDFontType0";348 fileSubtype = "CIDFontType0C";349 } else {350 fileType = type === "MMType1" ? "MMType1" : "Type1";351 fileSubtype = "Type1C";352 }353 } else {354 warn("getFontFileType: Unable to detect correct font file Type/Subtype.");355 fileType = type;356 fileSubtype = subtype;357 }358 return [fileType, fileSubtype];359}360function applyStandardFontGlyphMap(map, glyphMap) {361 for (const charCode in glyphMap) {362 map[+charCode] = glyphMap[charCode];363 }364}365function buildToFontChar(encoding, glyphsUnicodeMap, differences) {366 const toFontChar = [];367 let unicode;368 for (let i = 0, ii = encoding.length; i < ii; i++) {369 unicode = getUnicodeForGlyph(encoding[i], glyphsUnicodeMap);370 if (unicode !== -1) {371 toFontChar[i] = unicode;372 }373 }374 for (const charCode in differences) {375 unicode = getUnicodeForGlyph(differences[charCode], glyphsUnicodeMap);376 if (unicode !== -1) {377 toFontChar[+charCode] = unicode;378 }379 }380 return toFontChar;381}382function convertCidString(charCode, cid, shouldThrow = false) {383 switch (cid.length) {384 case 1:385 return cid.charCodeAt(0);386 case 2:387 return (cid.charCodeAt(0) << 8) | cid.charCodeAt(1);388 }389 const msg = `Unsupported CID string (charCode ${charCode}): "${cid}".`;390 if (shouldThrow) {391 throw new FormatError(msg);392 }393 warn(msg);394 return cid;395}396/**397 * Rebuilds the char code to glyph ID map by moving all char codes to the398 * private use area. This is done to avoid issues with various problematic399 * unicode areas where either a glyph won't be drawn or is deformed by a400 * shaper.401 * @returns {Object} Two properties:402 * 'toFontChar' - maps original char codes(the value that will be read403 * from commands such as show text) to the char codes that will be used in the404 * font that we build405 * 'charCodeToGlyphId' - maps the new font char codes to glyph ids406 */407function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId) {408 const newMap = Object.create(null);409 const toFontChar = [];410 let privateUseAreaIndex = 0;411 let nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];412 let privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];413 for (let originalCharCode in charCodeToGlyphId) {414 originalCharCode |= 0;415 let glyphId = charCodeToGlyphId[originalCharCode];416 // For missing glyphs don't create the mappings so the glyph isn't417 // drawn.418 if (!hasGlyph(glyphId)) {419 continue;420 }421 if (nextAvailableFontCharCode > privateUseOffetEnd) {422 privateUseAreaIndex++;423 if (privateUseAreaIndex >= PRIVATE_USE_AREAS.length) {424 warn("Ran out of space in font private use area.");425 break;426 }427 nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];428 privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];429 }430 const fontCharCode = nextAvailableFontCharCode++;431 if (glyphId === 0) {432 glyphId = newGlyphZeroId;433 }434 newMap[fontCharCode] = glyphId;435 toFontChar[originalCharCode] = fontCharCode;436 }437 return {438 toFontChar,439 charCodeToGlyphId: newMap,440 nextAvailableFontCharCode,441 };442}443function getRanges(glyphs, numGlyphs) {444 // Array.sort() sorts by characters, not numerically, so convert to an445 // array of characters.446 const codes = [];447 for (const charCode in glyphs) {448 // Remove an invalid glyph ID mappings to make OTS happy.449 if (glyphs[charCode] >= numGlyphs) {450 continue;451 }452 codes.push({ fontCharCode: charCode | 0, glyphId: glyphs[charCode] });453 }454 // Some fonts have zero glyphs and are used only for text selection, but455 // there needs to be at least one to build a valid cmap table.456 if (codes.length === 0) {457 codes.push({ fontCharCode: 0, glyphId: 0 });458 }459 codes.sort(function fontGetRangesSort(a, b) {460 return a.fontCharCode - b.fontCharCode;461 });462 // Split the sorted codes into ranges.463 const ranges = [];464 const length = codes.length;465 for (let n = 0; n < length; ) {466 const start = codes[n].fontCharCode;467 const codeIndices = [codes[n].glyphId];468 ++n;469 let end = start;470 while (n < length && end + 1 === codes[n].fontCharCode) {471 codeIndices.push(codes[n].glyphId);472 ++end;473 ++n;474 if (end === 0xffff) {475 break;476 }477 }478 ranges.push([start, end, codeIndices]);479 }480 return ranges;481}482function createCmapTable(glyphs, numGlyphs) {483 const ranges = getRanges(glyphs, numGlyphs);484 const numTables = ranges[ranges.length - 1][1] > 0xffff ? 2 : 1;485 let cmap =486 "\x00\x00" + // version487 string16(numTables) + // numTables488 "\x00\x03" + // platformID489 "\x00\x01" + // encodingID490 string32(4 + numTables * 8); // start of the table record491 let i, ii, j, jj;492 for (i = ranges.length - 1; i >= 0; --i) {493 if (ranges[i][0] <= 0xffff) {494 break;495 }496 }497 const bmpLength = i + 1;498 if (ranges[i][0] < 0xffff && ranges[i][1] === 0xffff) {499 ranges[i][1] = 0xfffe;500 }501 const trailingRangesCount = ranges[i][1] < 0xffff ? 1 : 0;502 const segCount = bmpLength + trailingRangesCount;503 const searchParams = OpenTypeFileBuilder.getSearchParams(segCount, 2);504 // Fill up the 4 parallel arrays describing the segments.505 let startCount = "";506 let endCount = "";507 let idDeltas = "";508 let idRangeOffsets = "";509 let glyphsIds = "";510 let bias = 0;511 let range, start, end, codes;512 for (i = 0, ii = bmpLength; i < ii; i++) {513 range = ranges[i];514 start = range[0];515 end = range[1];516 startCount += string16(start);517 endCount += string16(end);518 codes = range[2];519 let contiguous = true;520 for (j = 1, jj = codes.length; j < jj; ++j) {521 if (codes[j] !== codes[j - 1] + 1) {522 contiguous = false;523 break;524 }525 }526 if (!contiguous) {527 const offset = (segCount - i) * 2 + bias * 2;528 bias += end - start + 1;529 idDeltas += string16(0);530 idRangeOffsets += string16(offset);531 for (j = 0, jj = codes.length; j < jj; ++j) {532 glyphsIds += string16(codes[j]);533 }534 } else {535 const startCode = codes[0];536 idDeltas += string16((startCode - start) & 0xffff);537 idRangeOffsets += string16(0);538 }539 }540 if (trailingRangesCount > 0) {541 endCount += "\xFF\xFF";542 startCount += "\xFF\xFF";543 idDeltas += "\x00\x01";544 idRangeOffsets += "\x00\x00";545 }546 const format314 =547 "\x00\x00" + // language548 string16(2 * segCount) +549 string16(searchParams.range) +550 string16(searchParams.entry) +551 string16(searchParams.rangeShift) +552 endCount +553 "\x00\x00" +554 startCount +555 idDeltas +556 idRangeOffsets +557 glyphsIds;558 let format31012 = "";559 let header31012 = "";560 if (numTables > 1) {561 cmap +=562 "\x00\x03" + // platformID563 "\x00\x0A" + // encodingID564 string32(4 + numTables * 8 + 4 + format314.length); // start of the table record565 format31012 = "";566 for (i = 0, ii = ranges.length; i < ii; i++) {567 range = ranges[i];568 start = range[0];569 codes = range[2];570 let code = codes[0];571 for (j = 1, jj = codes.length; j < jj; ++j) {572 if (codes[j] !== codes[j - 1] + 1) {573 end = range[0] + j - 1;574 format31012 +=575 string32(start) + // startCharCode576 string32(end) + // endCharCode577 string32(code); // startGlyphID578 start = end + 1;579 code = codes[j];580 }581 }582 format31012 +=583 string32(start) + // startCharCode584 string32(range[1]) + // endCharCode585 string32(code); // startGlyphID586 }587 header31012 =588 "\x00\x0C" + // format589 "\x00\x00" + // reserved590 string32(format31012.length + 16) + // length591 "\x00\x00\x00\x00" + // language592 string32(format31012.length / 12); // nGroups593 }594 return (595 cmap +596 "\x00\x04" + // format597 string16(format314.length + 4) + // length598 format314 +599 header31012 +600 format31012601 );602}603function validateOS2Table(os2, file) {604 file.pos = (file.start || 0) + os2.offset;605 const version = file.getUint16();606 // TODO verify all OS/2 tables fields, but currently we validate only those607 // that give us issues608 file.skip(60); // skipping type, misc sizes, panose, unicode ranges609 const selection = file.getUint16();610 if (version < 4 && selection & 0x0300) {611 return false;612 }613 const firstChar = file.getUint16();614 const lastChar = file.getUint16();615 if (firstChar > lastChar) {616 return false;617 }618 file.skip(6); // skipping sTypoAscender/Descender/LineGap619 const usWinAscent = file.getUint16();620 if (usWinAscent === 0) {621 // makes font unreadable by windows622 return false;623 }624 // OS/2 appears to be valid, resetting some fields625 os2.data[8] = os2.data[9] = 0; // IE rejects fonts if fsType != 0626 return true;627}628function createOS2Table(properties, charstrings, override) {629 override = override || {630 unitsPerEm: 0,631 yMax: 0,632 yMin: 0,633 ascent: 0,634 descent: 0,635 };636 let ulUnicodeRange1 = 0;637 let ulUnicodeRange2 = 0;638 let ulUnicodeRange3 = 0;639 let ulUnicodeRange4 = 0;640 let firstCharIndex = null;641 let lastCharIndex = 0;642 if (charstrings) {643 for (let code in charstrings) {644 code |= 0;645 if (firstCharIndex > code || !firstCharIndex) {646 firstCharIndex = code;647 }648 if (lastCharIndex < code) {649 lastCharIndex = code;650 }651 const position = getUnicodeRangeFor(code);652 if (position < 32) {653 ulUnicodeRange1 |= 1 << position;654 } else if (position < 64) {655 ulUnicodeRange2 |= 1 << (position - 32);656 } else if (position < 96) {657 ulUnicodeRange3 |= 1 << (position - 64);658 } else if (position < 123) {659 ulUnicodeRange4 |= 1 << (position - 96);660 } else {661 throw new FormatError(662 "Unicode ranges Bits > 123 are reserved for internal usage"663 );664 }665 }666 if (lastCharIndex > 0xffff) {667 // OS2 only supports a 16 bit int. The spec says if supplementary668 // characters are used the field should just be set to 0xFFFF.669 lastCharIndex = 0xffff;670 }671 } else {672 // TODO673 firstCharIndex = 0;674 lastCharIndex = 255;675 }676 const bbox = properties.bbox || [0, 0, 0, 0];677 const unitsPerEm =678 override.unitsPerEm ||679 1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0];680 // if the font units differ to the PDF glyph space units681 // then scale up the values682 const scale = properties.ascentScaled683 ? 1.0684 : unitsPerEm / PDF_GLYPH_SPACE_UNITS;685 const typoAscent =686 override.ascent || Math.round(scale * (properties.ascent || bbox[3]));687 let typoDescent =688 override.descent || Math.round(scale * (properties.descent || bbox[1]));689 if (typoDescent > 0 && properties.descent > 0 && bbox[1] < 0) {690 typoDescent = -typoDescent; // fixing incorrect descent691 }692 const winAscent = override.yMax || typoAscent;693 const winDescent = -override.yMin || -typoDescent;694 return (695 "\x00\x03" + // version696 "\x02\x24" + // xAvgCharWidth697 "\x01\xF4" + // usWeightClass698 "\x00\x05" + // usWidthClass699 "\x00\x00" + // fstype (0 to let the font loads via font-face on IE)700 "\x02\x8A" + // ySubscriptXSize701 "\x02\xBB" + // ySubscriptYSize702 "\x00\x00" + // ySubscriptXOffset703 "\x00\x8C" + // ySubscriptYOffset704 "\x02\x8A" + // ySuperScriptXSize705 "\x02\xBB" + // ySuperScriptYSize706 "\x00\x00" + // ySuperScriptXOffset707 "\x01\xDF" + // ySuperScriptYOffset708 "\x00\x31" + // yStrikeOutSize709 "\x01\x02" + // yStrikeOutPosition710 "\x00\x00" + // sFamilyClass711 "\x00\x00\x06" +712 String.fromCharCode(properties.fixedPitch ? 0x09 : 0x00) +713 "\x00\x00\x00\x00\x00\x00" + // Panose714 string32(ulUnicodeRange1) + // ulUnicodeRange1 (Bits 0-31)715 string32(ulUnicodeRange2) + // ulUnicodeRange2 (Bits 32-63)716 string32(ulUnicodeRange3) + // ulUnicodeRange3 (Bits 64-95)717 string32(ulUnicodeRange4) + // ulUnicodeRange4 (Bits 96-127)718 "\x2A\x32\x31\x2A" + // achVendID719 string16(properties.italicAngle ? 1 : 0) + // fsSelection720 string16(firstCharIndex || properties.firstChar) + // usFirstCharIndex721 string16(lastCharIndex || properties.lastChar) + // usLastCharIndex722 string16(typoAscent) + // sTypoAscender723 string16(typoDescent) + // sTypoDescender724 "\x00\x64" + // sTypoLineGap (7%-10% of the unitsPerEM value)725 string16(winAscent) + // usWinAscent726 string16(winDescent) + // usWinDescent727 "\x00\x00\x00\x00" + // ulCodePageRange1 (Bits 0-31)728 "\x00\x00\x00\x00" + // ulCodePageRange2 (Bits 32-63)729 string16(properties.xHeight) + // sxHeight730 string16(properties.capHeight) + // sCapHeight731 string16(0) + // usDefaultChar732 string16(firstCharIndex || properties.firstChar) + // usBreakChar733 "\x00\x03"734 ); // usMaxContext735}736function createPostTable(properties) {737 const angle = Math.floor(properties.italicAngle * 2 ** 16);738 return (739 "\x00\x03\x00\x00" + // Version number740 string32(angle) + // italicAngle741 "\x00\x00" + // underlinePosition742 "\x00\x00" + // underlineThickness743 string32(properties.fixedPitch ? 1 : 0) + // isFixedPitch744 "\x00\x00\x00\x00" + // minMemType42745 "\x00\x00\x00\x00" + // maxMemType42746 "\x00\x00\x00\x00" + // minMemType1747 "\x00\x00\x00\x00"748 ); // maxMemType1749}750function createPostscriptName(name) {751 // See https://docs.microsoft.com/en-us/typography/opentype/spec/recom#name.752 return name.replace(/[^\x21-\x7E]|[[\](){}<>/%]/g, "").slice(0, 63);753}754function createNameTable(name, proto) {755 if (!proto) {756 proto = [[], []]; // no strings and unicode strings757 }758 const strings = [759 proto[0][0] || "Original licence", // 0.Copyright760 proto[0][1] || name, // 1.Font family761 proto[0][2] || "Unknown", // 2.Font subfamily (font weight)762 proto[0][3] || "uniqueID", // 3.Unique ID763 proto[0][4] || name, // 4.Full font name764 proto[0][5] || "Version 0.11", // 5.Version765 proto[0][6] || createPostscriptName(name), // 6.Postscript name766 proto[0][7] || "Unknown", // 7.Trademark767 proto[0][8] || "Unknown", // 8.Manufacturer768 proto[0][9] || "Unknown", // 9.Designer769 ];770 // Mac want 1-byte per character strings while Windows want771 // 2-bytes per character, so duplicate the names table772 const stringsUnicode = [];773 let i, ii, j, jj, str;774 for (i = 0, ii = strings.length; i < ii; i++) {775 str = proto[1][i] || strings[i];776 const strBufUnicode = [];777 for (j = 0, jj = str.length; j < jj; j++) {778 strBufUnicode.push(string16(str.charCodeAt(j)));779 }780 stringsUnicode.push(strBufUnicode.join(""));781 }782 const names = [strings, stringsUnicode];783 const platforms = ["\x00\x01", "\x00\x03"];784 const encodings = ["\x00\x00", "\x00\x01"];785 const languages = ["\x00\x00", "\x04\x09"];786 const namesRecordCount = strings.length * platforms.length;787 let nameTable =788 "\x00\x00" + // format789 string16(namesRecordCount) + // Number of names Record790 string16(namesRecordCount * 12 + 6); // Storage791 // Build the name records field792 let strOffset = 0;793 for (i = 0, ii = platforms.length; i < ii; i++) {794 const strs = names[i];795 for (j = 0, jj = strs.length; j < jj; j++) {796 str = strs[j];797 const nameRecord =798 platforms[i] + // platform ID799 encodings[i] + // encoding ID800 languages[i] + // language ID801 string16(j) + // name ID802 string16(str.length) +803 string16(strOffset);804 nameTable += nameRecord;805 strOffset += str.length;806 }807 }808 nameTable += strings.join("") + stringsUnicode.join("");809 return nameTable;810}811/**812 * 'Font' is the class the outside world should use, it encapsulate all the font813 * decoding logics whatever type it is (assuming the font type is supported).814 */815class Font {816 constructor(name, file, properties) {817 this.name = name;818 this.psName = null;819 this.mimetype = null;820 this.disableFontFace = false;821 this.loadedName = properties.loadedName;822 this.isType3Font = properties.isType3Font;823 this.missingFile = false;824 this.cssFontInfo = properties.cssFontInfo;825 this._charsCache = Object.create(null);826 this._glyphCache = Object.create(null);827 let isSerifFont = !!(properties.flags & FontFlags.Serif);828 // Fallback to checking the font name, in order to improve text-selection,829 // since the /Flags-entry is often wrong (fixes issue13845.pdf).830 if (!isSerifFont && !properties.isSimulatedFlags) {831 const baseName = name.replace(/[,_]/g, "-").split("-")[0],832 serifFonts = getSerifFonts();833 for (const namePart of baseName.split("+")) {834 if (serifFonts[namePart]) {835 isSerifFont = true;836 break;837 }838 }839 }840 this.isSerifFont = isSerifFont;841 this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);842 this.isMonospace = !!(properties.flags & FontFlags.FixedPitch);843 let type = properties.type;844 let subtype = properties.subtype;845 this.type = type;846 this.subtype = subtype;847 let fallbackName = "sans-serif";848 if (this.isMonospace) {849 fallbackName = "monospace";850 } else if (this.isSerifFont) {851 fallbackName = "serif";852 }853 this.fallbackName = fallbackName;854 this.differences = properties.differences;855 this.widths = properties.widths;856 this.defaultWidth = properties.defaultWidth;857 this.composite = properties.composite;858 this.cMap = properties.cMap;859 this.capHeight = properties.capHeight / PDF_GLYPH_SPACE_UNITS;860 this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;861 this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;862 this.lineHeight = this.ascent - this.descent;863 this.fontMatrix = properties.fontMatrix;864 this.bbox = properties.bbox;865 this.defaultEncoding = properties.defaultEncoding;866 this.toUnicode = properties.toUnicode;867 this.toFontChar = [];868 if (properties.type === "Type3") {869 for (let charCode = 0; charCode < 256; charCode++) {870 this.toFontChar[charCode] =871 this.differences[charCode] || properties.defaultEncoding[charCode];872 }873 this.fontType = FontType.TYPE3;874 return;875 }876 this.cidEncoding = properties.cidEncoding || "";877 this.vertical = !!properties.vertical;878 if (this.vertical) {879 this.vmetrics = properties.vmetrics;880 this.defaultVMetrics = properties.defaultVMetrics;881 }882 if (!file || file.isEmpty) {883 if (file) {884 // Some bad PDF generators will include empty font files,885 // attempting to recover by assuming that no file exists.886 warn('Font file is empty in "' + name + '" (' + this.loadedName + ")");887 }888 this.fallbackToSystemFont(properties);889 return;890 }891 // Parse the font file to determine the correct type/subtype, rather than892 // relying on the (often incorrect) data in the font dictionary; (see e.g.893 // issue6782.pdf, issue7598.pdf, and issue9949.pdf).894 [type, subtype] = getFontFileType(file, properties);895 if (type !== this.type || subtype !== this.subtype) {896 info(897 "Inconsistent font file Type/SubType, expected: " +898 `${this.type}/${this.subtype} but found: ${type}/${subtype}.`899 );900 }901 let data;902 try {903 switch (type) {904 case "MMType1":905 info("MMType1 font (" + name + "), falling back to Type1.");906 /* falls through */907 case "Type1":908 case "CIDFontType0":909 this.mimetype = "font/opentype";910 const cff =911 subtype === "Type1C" || subtype === "CIDFontType0C"912 ? new CFFFont(file, properties)913 : new Type1Font(name, file, properties);914 adjustWidths(properties);915 // Wrap the CFF data inside an OTF font file916 data = this.convert(name, cff, properties);917 break;918 case "OpenType":919 case "TrueType":920 case "CIDFontType2":921 this.mimetype = "font/opentype";922 // Repair the TrueType file. It is can be damaged in the point of923 // view of the sanitizer924 data = this.checkAndRepair(name, file, properties);925 if (this.isOpenType) {926 adjustWidths(properties);927 type = "OpenType";928 }929 break;930 default:931 throw new FormatError(`Font ${type} is not supported`);932 }933 } catch (e) {934 warn(e);935 this.fallbackToSystemFont(properties);936 return;937 }938 amendFallbackToUnicode(properties);939 this.data = data;940 this.fontType = getFontType(type, subtype, properties.isStandardFont);941 // Transfer some properties again that could change during font conversion942 this.fontMatrix = properties.fontMatrix;943 this.widths = properties.widths;944 this.defaultWidth = properties.defaultWidth;945 this.toUnicode = properties.toUnicode;946 this.seacMap = properties.seacMap;947 }948 get renderer() {949 const renderer = FontRendererFactory.create(this, SEAC_ANALYSIS_ENABLED);950 return shadow(this, "renderer", renderer);951 }952 exportData(extraProperties = false) {953 const exportDataProperties = extraProperties954 ? [...EXPORT_DATA_PROPERTIES, ...EXPORT_DATA_EXTRA_PROPERTIES]955 : EXPORT_DATA_PROPERTIES;956 const data = Object.create(null);957 let property, value;958 for (property of exportDataProperties) {959 value = this[property];960 // Ignore properties that haven't been explicitly set.961 if (value !== undefined) {962 data[property] = value;963 }964 }965 return data;966 }967 fallbackToSystemFont(properties) {968 this.missingFile = true;969 // The file data is not specified. Trying to fix the font name970 // to be used with the canvas.font.971 const name = this.name;972 const type = this.type;973 const subtype = this.subtype;974 let fontName = normalizeFontName(name);975 const stdFontMap = getStdFontMap(),976 nonStdFontMap = getNonStdFontMap();977 const isStandardFont = !!stdFontMap[fontName];978 const isMappedToStandardFont = !!(979 nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]980 );981 fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;982 const fontBasicMetricsMap = getFontBasicMetrics();983 const metrics = fontBasicMetricsMap[fontName];984 if (metrics) {985 if (isNaN(this.ascent)) {986 this.ascent = metrics.ascent / PDF_GLYPH_SPACE_UNITS;987 }988 if (isNaN(this.descent)) {989 this.descent = metrics.descent / PDF_GLYPH_SPACE_UNITS;990 }991 if (isNaN(this.capHeight)) {992 this.capHeight = metrics.capHeight / PDF_GLYPH_SPACE_UNITS;993 }994 }995 this.bold = fontName.search(/bold/gi) !== -1;996 this.italic =997 fontName.search(/oblique/gi) !== -1 || fontName.search(/italic/gi) !== -1;998 // Use 'name' instead of 'fontName' here because the original999 // name ArialBlack for example will be replaced by Helvetica.1000 this.black = name.search(/Black/g) !== -1;1001 // Use 'name' instead of 'fontName' here because the original1002 // name ArialNarrow for example will be replaced by Helvetica.1003 const isNarrow = name.search(/Narrow/g) !== -1;1004 // if at least one width is present, remeasure all chars when exists1005 this.remeasure =1006 (!isStandardFont || isNarrow) && Object.keys(this.widths).length > 0;1007 if (1008 (isStandardFont || isMappedToStandardFont) &&1009 type === "CIDFontType2" &&1010 this.cidEncoding.startsWith("Identity-")1011 ) {1012 const cidToGidMap = properties.cidToGidMap;1013 // Standard fonts might be embedded as CID font without glyph mapping.1014 // Building one based on GlyphMapForStandardFonts.1015 const map = [];1016 applyStandardFontGlyphMap(map, getGlyphMapForStandardFonts());1017 if (/Arial-?Black/i.test(name)) {1018 applyStandardFontGlyphMap(map, getSupplementalGlyphMapForArialBlack());1019 } else if (/Calibri/i.test(name)) {1020 applyStandardFontGlyphMap(map, getSupplementalGlyphMapForCalibri());1021 }1022 // Always update the glyph mapping with the `cidToGidMap` when it exists1023 // (fixes issue12418_reduced.pdf).1024 if (cidToGidMap) {1025 for (const charCode in map) {1026 const cid = map[charCode];1027 if (cidToGidMap[cid] !== undefined) {1028 map[+charCode] = cidToGidMap[cid];1029 }1030 }1031 // When the /CIDToGIDMap is "incomplete", fallback to the included1032 // /ToUnicode-map regardless of its encoding (fixes issue11915.pdf).1033 if (1034 cidToGidMap.length !== this.toUnicode.length &&1035 properties.hasIncludedToUnicodeMap &&1036 this.toUnicode instanceof IdentityToUnicodeMap1037 ) {1038 this.toUnicode.forEach(function (charCode, unicodeCharCode) {1039 const cid = map[charCode];1040 if (cidToGidMap[cid] === undefined) {1041 map[+charCode] = unicodeCharCode;1042 }1043 });1044 }1045 }1046 if (!(this.toUnicode instanceof IdentityToUnicodeMap)) {1047 this.toUnicode.forEach(function (charCode, unicodeCharCode) {1048 map[+charCode] = unicodeCharCode;1049 });1050 }1051 this.toFontChar = map;1052 this.toUnicode = new ToUnicodeMap(map);1053 } else if (/Symbol/i.test(fontName)) {1054 this.toFontChar = buildToFontChar(1055 SymbolSetEncoding,1056 getGlyphsUnicode(),1057 this.differences1058 );1059 } else if (/Dingbats/i.test(fontName)) {1060 if (/Wingdings/i.test(name)) {1061 warn("Non-embedded Wingdings font, falling back to ZapfDingbats.");1062 }1063 this.toFontChar = buildToFontChar(1064 ZapfDingbatsEncoding,1065 getDingbatsGlyphsUnicode(),1066 this.differences1067 );1068 } else if (isStandardFont) {1069 const map = buildToFontChar(1070 this.defaultEncoding,1071 getGlyphsUnicode(),1072 this.differences1073 );1074 if (1075 type === "CIDFontType2" &&1076 !this.cidEncoding.startsWith("Identity-") &&1077 !(this.toUnicode instanceof IdentityToUnicodeMap)1078 ) {1079 this.toUnicode.forEach(function (charCode, unicodeCharCode) {1080 map[+charCode] = unicodeCharCode;1081 });1082 }1083 this.toFontChar = map;1084 } else {1085 const glyphsUnicodeMap = getGlyphsUnicode();1086 const map = [];1087 this.toUnicode.forEach((charCode, unicodeCharCode) => {1088 if (!this.composite) {1089 const glyphName =1090 this.differences[charCode] || this.defaultEncoding[charCode];1091 const unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap);1092 if (unicode !== -1) {1093 unicodeCharCode = unicode;1094 }1095 }1096 map[+charCode] = unicodeCharCode;1097 });1098 // Attempt to improve the glyph mapping for (some) composite fonts that1099 // appear to lack meaningful ToUnicode data.1100 if (this.composite && this.toUnicode instanceof IdentityToUnicodeMap) {1101 if (/Verdana/i.test(name)) {1102 // Fixes issue11242_reduced.pdf1103 applyStandardFontGlyphMap(map, getGlyphMapForStandardFonts());1104 }1105 }1106 this.toFontChar = map;1107 }1108 amendFallbackToUnicode(properties);1109 this.loadedName = fontName.split("-")[0];1110 this.fontType = getFontType(type, subtype, properties.isStandardFont);1111 }1112 checkAndRepair(name, font, properties) {1113 const VALID_TABLES = [1114 "OS/2",1115 "cmap",1116 "head",1117 "hhea",1118 "hmtx",1119 "maxp",1120 "name",1121 "post",1122 "loca",1123 "glyf",1124 "fpgm",1125 "prep",1126 "cvt ",1127 "CFF ",1128 ];1129 function readTables(file, numTables) {1130 const tables = Object.create(null);1131 tables["OS/2"] = null;1132 tables.cmap = null;1133 tables.head = null;1134 tables.hhea = null;1135 tables.hmtx = null;1136 tables.maxp = null;1137 tables.name = null;1138 tables.post = null;1139 for (let i = 0; i < numTables; i++) {1140 const table = readTableEntry(file);1141 if (!VALID_TABLES.includes(table.tag)) {1142 continue; // skipping table if it's not a required or optional table1143 }1144 if (table.length === 0) {1145 continue; // skipping empty tables1146 }1147 tables[table.tag] = table;1148 }1149 return tables;1150 }1151 function readTableEntry(file) {1152 const tag = file.getString(4);1153 const checksum = file.getInt32() >>> 0;1154 const offset = file.getInt32() >>> 0;1155 const length = file.getInt32() >>> 0;1156 // Read the table associated data1157 const previousPosition = file.pos;1158 file.pos = file.start ? file.start : 0;1159 file.skip(offset);1160 const data = file.getBytes(length);1161 file.pos = previousPosition;1162 if (tag === "head") {1163 // clearing checksum adjustment1164 data[8] = data[9] = data[10] = data[11] = 0;1165 data[17] |= 0x20; // Set font optimized for cleartype flag.1166 }1167 return {1168 tag,1169 checksum,1170 length,1171 offset,1172 data,1173 };1174 }1175 function readOpenTypeHeader(ttf) {1176 return {1177 version: ttf.getString(4),1178 numTables: ttf.getUint16(),1179 searchRange: ttf.getUint16(),1180 entrySelector: ttf.getUint16(),1181 rangeShift: ttf.getUint16(),1182 };1183 }1184 function readTrueTypeCollectionHeader(ttc) {1185 const ttcTag = ttc.getString(4);1186 assert(ttcTag === "ttcf", "Must be a TrueType Collection font.");1187 const majorVersion = ttc.getUint16();1188 const minorVersion = ttc.getUint16();1189 const numFonts = ttc.getInt32() >>> 0;1190 const offsetTable = [];1191 for (let i = 0; i < numFonts; i++) {1192 offsetTable.push(ttc.getInt32() >>> 0);1193 }1194 const header = {1195 ttcTag,1196 majorVersion,1197 minorVersion,1198 numFonts,1199 offsetTable,1200 };1201 switch (majorVersion) {1202 case 1:1203 return header;1204 case 2:1205 header.dsigTag = ttc.getInt32() >>> 0;1206 header.dsigLength = ttc.getInt32() >>> 0;1207 header.dsigOffset = ttc.getInt32() >>> 0;1208 return header;1209 }1210 throw new FormatError(1211 `Invalid TrueType Collection majorVersion: ${majorVersion}.`1212 );1213 }1214 function readTrueTypeCollectionData(ttc, fontName) {1215 const { numFonts, offsetTable } = readTrueTypeCollectionHeader(ttc);1216 const fontNameParts = fontName.split("+");1217 let fallbackData;1218 for (let i = 0; i < numFonts; i++) {1219 ttc.pos = (ttc.start || 0) + offsetTable[i];1220 const potentialHeader = readOpenTypeHeader(ttc);1221 const potentialTables = readTables(ttc, potentialHeader.numTables);1222 if (!potentialTables.name) {1223 throw new FormatError(1224 'TrueType Collection font must contain a "name" table.'1225 );1226 }1227 const nameTable = readNameTable(potentialTables.name);1228 for (let j = 0, jj = nameTable.length; j < jj; j++) {1229 for (let k = 0, kk = nameTable[j].length; k < kk; k++) {1230 const nameEntry =1231 nameTable[j][k] && nameTable[j][k].replace(/\s/g, "");1232 if (!nameEntry) {1233 continue;1234 }1235 if (nameEntry === fontName) {1236 return {1237 header: potentialHeader,1238 tables: potentialTables,1239 };1240 }1241 if (fontNameParts.length < 2) {1242 continue;1243 }1244 for (const part of fontNameParts) {1245 if (nameEntry === part) {1246 fallbackData = {1247 name: part,1248 header: potentialHeader,1249 tables: potentialTables,1250 };1251 }1252 }1253 }1254 }1255 }1256 if (fallbackData) {1257 warn(1258 `TrueType Collection does not contain "${fontName}" font, ` +1259 `falling back to "${fallbackData.name}" font instead.`1260 );1261 return {1262 header: fallbackData.header,1263 tables: fallbackData.tables,1264 };1265 }1266 throw new FormatError(1267 `TrueType Collection does not contain "${fontName}" font.`1268 );1269 }1270 /**1271 * Read the appropriate subtable from the cmap according to 9.6.6.4 from1272 * PDF spec1273 */1274 function readCmapTable(cmap, file, isSymbolicFont, hasEncoding) {1275 if (!cmap) {1276 warn("No cmap table available.");1277 return {1278 platformId: -1,1279 encodingId: -1,1280 mappings: [],1281 hasShortCmap: false,1282 };1283 }1284 let segment;1285 let start = (file.start ? file.start : 0) + cmap.offset;1286 file.pos = start;1287 file.skip(2); // version1288 const numTables = file.getUint16();1289 let potentialTable;1290 let canBreak = false;1291 // There's an order of preference in terms of which cmap subtable to1292 // use:1293 // - non-symbolic fonts the preference is a 3,1 table then a 1,0 table1294 // - symbolic fonts the preference is a 3,0 table then a 1,0 table1295 // The following takes advantage of the fact that the tables are sorted1296 // to work.1297 for (let i = 0; i < numTables; i++) {1298 const platformId = file.getUint16();1299 const encodingId = file.getUint16();1300 const offset = file.getInt32() >>> 0;1301 let useTable = false;1302 // Sometimes there are multiple of the same type of table. Default1303 // to choosing the first table and skip the rest.1304 if (1305 potentialTable &&1306 potentialTable.platformId === platformId &&1307 potentialTable.encodingId === encodingId1308 ) {1309 continue;1310 }1311 if (1312 platformId === 0 &&1313 (encodingId === /* Unicode Default */ 0 ||1314 encodingId === /* Unicode 1.1 */ 1 ||1315 encodingId === /* Unicode BMP */ 3)1316 ) {1317 useTable = true;1318 // Continue the loop since there still may be a higher priority1319 // table.1320 } else if (platformId === 1 && encodingId === 0) {1321 useTable = true;1322 // Continue the loop since there still may be a higher priority1323 // table.1324 } else if (1325 platformId === 3 &&1326 encodingId === 1 &&1327 (hasEncoding || !potentialTable)1328 ) {1329 useTable = true;1330 if (!isSymbolicFont) {1331 canBreak = true;1332 }1333 } else if (isSymbolicFont && platformId === 3 && encodingId === 0) {1334 useTable = true;1335 let correctlySorted = true;1336 if (i < numTables - 1) {1337 const nextBytes = file.peekBytes(2),1338 nextPlatformId = int16(nextBytes[0], nextBytes[1]);1339 if (nextPlatformId < platformId) {1340 correctlySorted = false;1341 }1342 }1343 if (correctlySorted) {1344 canBreak = true;1345 }1346 }1347 if (useTable) {1348 potentialTable = {1349 platformId,1350 encodingId,1351 offset,1352 };1353 }1354 if (canBreak) {1355 break;1356 }1357 }1358 if (potentialTable) {1359 file.pos = start + potentialTable.offset;1360 }1361 if (!potentialTable || file.peekByte() === -1) {1362 warn("Could not find a preferred cmap table.");1363 return {1364 platformId: -1,1365 encodingId: -1,1366 mappings: [],1367 hasShortCmap: false,1368 };1369 }1370 const format = file.getUint16();1371 file.skip(2 + 2); // length + language1372 let hasShortCmap = false;1373 const mappings = [];1374 let j, glyphId;1375 // TODO(mack): refactor this cmap subtable reading logic out1376 if (format === 0) {1377 for (j = 0; j < 256; j++) {1378 const index = file.getByte();1379 if (!index) {1380 continue;1381 }1382 mappings.push({1383 charCode: j,1384 glyphId: index,1385 });1386 }1387 hasShortCmap = true;1388 } else if (format === 2) {1389 const subHeaderKeys = [];1390 let maxSubHeaderKey = 0;1391 // Read subHeaderKeys. If subHeaderKeys[i] === 0, then i is a1392 // single-byte character. Otherwise, i is the first byte of a1393 // multi-byte character, and the value is 8*index into1394 // subHeaders.1395 for (let i = 0; i < 256; i++) {1396 const subHeaderKey = file.getUint16() >> 3;1397 subHeaderKeys.push(subHeaderKey);1398 maxSubHeaderKey = Math.max(subHeaderKey, maxSubHeaderKey);1399 }1400 // Read subHeaders. The number of entries is determined1401 // dynamically based on the subHeaderKeys found above.1402 const subHeaders = [];1403 for (let i = 0; i <= maxSubHeaderKey; i++) {1404 subHeaders.push({1405 firstCode: file.getUint16(),1406 entryCount: file.getUint16(),1407 idDelta: signedInt16(file.getByte(), file.getByte()),1408 idRangePos: file.pos + file.getUint16(),1409 });1410 }1411 for (let i = 0; i < 256; i++) {1412 if (subHeaderKeys[i] === 0) {1413 // i is a single-byte code.1414 file.pos = subHeaders[0].idRangePos + 2 * i;1415 glyphId = file.getUint16();1416 mappings.push({1417 charCode: i,1418 glyphId,1419 });1420 } else {1421 // i is the first byte of a two-byte code.1422 const s = subHeaders[subHeaderKeys[i]];1423 for (j = 0; j < s.entryCount; j++) {1424 const charCode = (i << 8) + j + s.firstCode;1425 file.pos = s.idRangePos + 2 * j;1426 glyphId = file.getUint16();1427 if (glyphId !== 0) {1428 glyphId = (glyphId + s.idDelta) % 65536;1429 }1430 mappings.push({1431 charCode,1432 glyphId,1433 });1434 }1435 }1436 }1437 } else if (format === 4) {1438 // re-creating the table in format 4 since the encoding1439 // might be changed1440 const segCount = file.getUint16() >> 1;1441 file.skip(6); // skipping range fields1442 const segments = [];1443 let segIndex;1444 for (segIndex = 0; segIndex < segCount; segIndex++) {1445 segments.push({ end: file.getUint16() });1446 }1447 file.skip(2);1448 for (segIndex = 0; segIndex < segCount; segIndex++) {1449 segments[segIndex].start = file.getUint16();1450 }1451 for (segIndex = 0; segIndex < segCount; segIndex++) {1452 segments[segIndex].delta = file.getUint16();1453 }1454 let offsetsCount = 0,1455 offsetIndex;1456 for (segIndex = 0; segIndex < segCount; segIndex++) {1457 segment = segments[segIndex];1458 const rangeOffset = file.getUint16();1459 if (!rangeOffset) {1460 segment.offsetIndex = -1;1461 continue;1462 }1463 offsetIndex = (rangeOffset >> 1) - (segCount - segIndex);1464 segment.offsetIndex = offsetIndex;1465 offsetsCount = Math.max(1466 offsetsCount,1467 offsetIndex + segment.end - segment.start + 11468 );1469 }1470 const offsets = [];1471 for (j = 0; j < offsetsCount; j++) {1472 offsets.push(file.getUint16());1473 }1474 for (segIndex = 0; segIndex < segCount; segIndex++) {1475 segment = segments[segIndex];1476 start = segment.start;1477 const end = segment.end;1478 const delta = segment.delta;1479 offsetIndex = segment.offsetIndex;1480 for (j = start; j <= end; j++) {1481 if (j === 0xffff) {1482 continue;1483 }1484 glyphId = offsetIndex < 0 ? j : offsets[offsetIndex + j - start];1485 glyphId = (glyphId + delta) & 0xffff;1486 mappings.push({1487 charCode: j,1488 glyphId,1489 });1490 }1491 }1492 } else if (format === 6) {1493 // Format 6 is a 2-bytes dense mapping, which means the font data1494 // lives glue together even if they are pretty far in the unicode1495 // table. (This looks weird, so I can have missed something), this1496 // works on Linux but seems to fails on Mac so let's rewrite the1497 // cmap table to a 3-1-4 style1498 const firstCode = file.getUint16();1499 const entryCount = file.getUint16();1500 for (j = 0; j < entryCount; j++) {1501 glyphId = file.getUint16();1502 const charCode = firstCode + j;1503 mappings.push({1504 charCode,1505 glyphId,1506 });1507 }1508 } else {1509 warn("cmap table has unsupported format: " + format);1510 return {1511 platformId: -1,1512 encodingId: -1,1513 mappings: [],1514 hasShortCmap: false,1515 };1516 }1517 // removing duplicate entries1518 mappings.sort(function (a, b) {1519 return a.charCode - b.charCode;1520 });1521 for (let i = 1; i < mappings.length; i++) {1522 if (mappings[i - 1].charCode === mappings[i].charCode) {1523 mappings.splice(i, 1);1524 i--;1525 }1526 }1527 return {1528 platformId: potentialTable.platformId,1529 encodingId: potentialTable.encodingId,1530 mappings,1531 hasShortCmap,1532 };1533 }1534 function sanitizeMetrics(1535 file,1536 header,1537 metrics,1538 headTable,1539 numGlyphs,1540 dupFirstEntry1541 ) {1542 if (!header) {1543 if (metrics) {1544 metrics.data = null;1545 }1546 return;1547 }1548 file.pos = (file.start ? file.start : 0) + header.offset;1549 file.pos += 4; // version1550 file.pos += 2; // ascent1551 file.pos += 2; // descent1552 file.pos += 2; // linegap1553 file.pos += 2; // adv_width_max1554 file.pos += 2; // min_sb11555 file.pos += 2; // min_sb21556 file.pos += 2; // max_extent1557 file.pos += 2; // caret_slope_rise1558 file.pos += 2; // caret_slope_run1559 const caretOffset = file.getUint16();1560 file.pos += 8; // reserved1561 file.pos += 2; // format1562 let numOfMetrics = file.getUint16();1563 if (caretOffset !== 0) {1564 const macStyle = int16(headTable.data[44], headTable.data[45]);1565 if (!(macStyle & 2)) {1566 // Suppress OTS warnings about the `caretOffset` in the hhea-table.1567 header.data[22] = 0;1568 header.data[23] = 0;1569 }1570 }1571 if (numOfMetrics > numGlyphs) {1572 info(1573 `The numOfMetrics (${numOfMetrics}) should not be ` +1574 `greater than the numGlyphs (${numGlyphs}).`1575 );1576 // Reduce numOfMetrics if it is greater than numGlyphs1577 numOfMetrics = numGlyphs;1578 header.data[34] = (numOfMetrics & 0xff00) >> 8;1579 header.data[35] = numOfMetrics & 0x00ff;1580 }1581 const numOfSidebearings = numGlyphs - numOfMetrics;1582 const numMissing =1583 numOfSidebearings - ((metrics.length - numOfMetrics * 4) >> 1);1584 if (numMissing > 0) {1585 // For each missing glyph, we set both the width and lsb to 0 (zero).1586 // Since we need to add two properties for each glyph, this explains1587 // the use of |numMissing * 2| when initializing the typed array.1588 const entries = new Uint8Array(metrics.length + numMissing * 2);1589 entries.set(metrics.data);1590 if (dupFirstEntry) {1591 // Set the sidebearing value of the duplicated glyph.1592 entries[metrics.length] = metrics.data[2];1593 entries[metrics.length + 1] = metrics.data[3];1594 }1595 metrics.data = entries;1596 }1597 }1598 function sanitizeGlyph(1599 source,1600 sourceStart,1601 sourceEnd,1602 dest,1603 destStart,1604 hintsValid1605 ) {1606 const glyphProfile = {1607 length: 0,1608 sizeOfInstructions: 0,1609 };1610 if (sourceEnd - sourceStart <= 12) {1611 // glyph with data less than 12 is invalid one1612 return glyphProfile;1613 }1614 const glyf = source.subarray(sourceStart, sourceEnd);1615 let contoursCount = signedInt16(glyf[0], glyf[1]);1616 if (contoursCount < 0) {1617 // OTS doesn't like contour count to be less than -1.1618 contoursCount = -1;1619 writeSignedInt16(glyf, 0, contoursCount);1620 // complex glyph, writing as is1621 dest.set(glyf, destStart);1622 glyphProfile.length = glyf.length;1623 return glyphProfile;1624 }1625 let i,1626 j = 10,1627 flagsCount = 0;1628 for (i = 0; i < contoursCount; i++) {1629 const endPoint = (glyf[j] << 8) | glyf[j + 1];1630 flagsCount = endPoint + 1;1631 j += 2;1632 }1633 // skipping instructions1634 const instructionsStart = j;1635 const instructionsLength = (glyf[j] << 8) | glyf[j + 1];1636 glyphProfile.sizeOfInstructions = instructionsLength;1637 j += 2 + instructionsLength;1638 const instructionsEnd = j;1639 // validating flags1640 let coordinatesLength = 0;1641 for (i = 0; i < flagsCount; i++) {1642 const flag = glyf[j++];1643 if (flag & 0xc0) {1644 // reserved flags must be zero, cleaning up1645 glyf[j - 1] = flag & 0x3f;1646 }1647 let xLength = 2;1648 if (flag & 2) {1649 xLength = 1;1650 } else if (flag & 16) {1651 xLength = 0;1652 }1653 let yLength = 2;1654 if (flag & 4) {1655 yLength = 1;1656 } else if (flag & 32) {1657 yLength = 0;1658 }1659 const xyLength = xLength + yLength;1660 coordinatesLength += xyLength;1661 if (flag & 8) {1662 const repeat = glyf[j++];1663 i += repeat;1664 coordinatesLength += repeat * xyLength;1665 }1666 }1667 // glyph without coordinates will be rejected1668 if (coordinatesLength === 0) {1669 return glyphProfile;1670 }1671 let glyphDataLength = j + coordinatesLength;1672 if (glyphDataLength > glyf.length) {1673 // not enough data for coordinates1674 return glyphProfile;1675 }1676 if (!hintsValid && instructionsLength > 0) {1677 dest.set(glyf.subarray(0, instructionsStart), destStart);1678 dest.set([0, 0], destStart + instructionsStart);1679 dest.set(1680 glyf.subarray(instructionsEnd, glyphDataLength),1681 destStart + instructionsStart + 21682 );1683 glyphDataLength -= instructionsLength;1684 if (glyf.length - glyphDataLength > 3) {1685 glyphDataLength = (glyphDataLength + 3) & ~3;1686 }1687 glyphProfile.length = glyphDataLength;1688 return glyphProfile;1689 }1690 if (glyf.length - glyphDataLength > 3) {1691 // truncating and aligning to 4 bytes the long glyph data1692 glyphDataLength = (glyphDataLength + 3) & ~3;1693 dest.set(glyf.subarray(0, glyphDataLength), destStart);1694 glyphProfile.length = glyphDataLength;1695 return glyphProfile;1696 }1697 // glyph data is fine1698 dest.set(glyf, destStart);1699 glyphProfile.length = glyf.length;1700 return glyphProfile;1701 }1702 function sanitizeHead(head, numGlyphs, locaLength) {1703 const data = head.data;1704 // Validate version:1705 // Should always be 0x000100001706 const version = int32(data[0], data[1], data[2], data[3]);1707 if (version >> 16 !== 1) {1708 info("Attempting to fix invalid version in head table: " + version);1709 data[0] = 0;1710 data[1] = 1;1711 data[2] = 0;1712 data[3] = 0;1713 }1714 const indexToLocFormat = int16(data[50], data[51]);1715 if (indexToLocFormat < 0 || indexToLocFormat > 1) {1716 info(1717 "Attempting to fix invalid indexToLocFormat in head table: " +1718 indexToLocFormat1719 );1720 // The value of indexToLocFormat should be 0 if the loca table1721 // consists of short offsets, and should be 1 if the loca table1722 // consists of long offsets.1723 //1724 // The number of entries in the loca table should be numGlyphs + 1.1725 //1726 // Using this information, we can work backwards to deduce if the1727 // size of each offset in the loca table, and thus figure out the1728 // appropriate value for indexToLocFormat.1729 const numGlyphsPlusOne = numGlyphs + 1;1730 if (locaLength === numGlyphsPlusOne << 1) {1731 // 0x0000 indicates the loca table consists of short offsets1732 data[50] = 0;1733 data[51] = 0;1734 } else if (locaLength === numGlyphsPlusOne << 2) {1735 // 0x0001 indicates the loca table consists of long offsets1736 data[50] = 0;1737 data[51] = 1;1738 } else {1739 throw new FormatError(1740 "Could not fix indexToLocFormat: " + indexToLocFormat1741 );1742 }1743 }1744 }1745 function sanitizeGlyphLocations(1746 loca,1747 glyf,1748 numGlyphs,1749 isGlyphLocationsLong,1750 hintsValid,1751 dupFirstEntry,1752 maxSizeOfInstructions1753 ) {1754 let itemSize, itemDecode, itemEncode;1755 if (isGlyphLocationsLong) {1756 itemSize = 4;1757 itemDecode = function fontItemDecodeLong(data, offset) {1758 return (1759 (data[offset] << 24) |1760 (data[offset + 1] << 16) |1761 (data[offset + 2] << 8) |1762 data[offset + 3]1763 );1764 };1765 itemEncode = function fontItemEncodeLong(data, offset, value) {1766 data[offset] = (value >>> 24) & 0xff;1767 data[offset + 1] = (value >> 16) & 0xff;1768 data[offset + 2] = (value >> 8) & 0xff;1769 data[offset + 3] = value & 0xff;1770 };1771 } else {1772 itemSize = 2;1773 itemDecode = function fontItemDecode(data, offset) {1774 return (data[offset] << 9) | (data[offset + 1] << 1);1775 };1776 itemEncode = function fontItemEncode(data, offset, value) {1777 data[offset] = (value >> 9) & 0xff;1778 data[offset + 1] = (value >> 1) & 0xff;1779 };1780 }1781 // The first glyph is duplicated.1782 const numGlyphsOut = dupFirstEntry ? numGlyphs + 1 : numGlyphs;1783 const locaDataSize = itemSize * (1 + numGlyphsOut);1784 // Resize loca table to account for duplicated glyph.1785 const locaData = new Uint8Array(locaDataSize);1786 locaData.set(loca.data.subarray(0, locaDataSize));1787 loca.data = locaData;1788 // removing the invalid glyphs1789 const oldGlyfData = glyf.data;1790 const oldGlyfDataLength = oldGlyfData.length;1791 const newGlyfData = new Uint8Array(oldGlyfDataLength);1792 // The spec says the offsets should be in ascending order, however1793 // this is not true for some fonts or they use the offset of 0 to mark a1794 // glyph as missing. OTS requires the offsets to be in order and not to1795 // be zero, so we must sort and rebuild the loca table and potentially1796 // re-arrange the glyf data.1797 let i, j;1798 const locaEntries = [];1799 // There are numGlyphs + 1 loca table entries.1800 for (i = 0, j = 0; i < numGlyphs + 1; i++, j += itemSize) {1801 let offset = itemDecode(locaData, j);1802 if (offset > oldGlyfDataLength) {1803 offset = oldGlyfDataLength;1804 }1805 locaEntries.push({1806 index: i,1807 offset,1808 endOffset: 0,1809 });1810 }1811 locaEntries.sort((a, b) => {1812 return a.offset - b.offset;1813 });1814 // Now the offsets are sorted, calculate the end offset of each glyph.1815 // The last loca entry's endOffset is not calculated since it's the end1816 // of the data and will be stored on the previous entry's endOffset.1817 for (i = 0; i < numGlyphs; i++) {1818 locaEntries[i].endOffset = locaEntries[i + 1].offset;1819 }1820 // Re-sort so glyphs aren't out of order.1821 locaEntries.sort((a, b) => {1822 return a.index - b.index;1823 });1824 // Calculate the endOffset of the "first" glyph correctly when there are1825 // *multiple* empty ones at the start of the data (fixes issue14618.pdf).1826 for (i = 0; i < numGlyphs; i++) {1827 const { offset, endOffset } = locaEntries[i];1828 if (offset !== 0 || endOffset !== 0) {1829 break;1830 }1831 const nextOffset = locaEntries[i + 1].offset;1832 if (nextOffset === 0) {1833 continue;1834 }1835 locaEntries[i].endOffset = nextOffset;1836 break;1837 }1838 const missingGlyphs = Object.create(null);1839 let writeOffset = 0;1840 itemEncode(locaData, 0, writeOffset);1841 for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {1842 const glyphProfile = sanitizeGlyph(1843 oldGlyfData,1844 locaEntries[i].offset,1845 locaEntries[i].endOffset,1846 newGlyfData,1847 writeOffset,1848 hintsValid1849 );1850 const newLength = glyphProfile.length;1851 if (newLength === 0) {1852 missingGlyphs[i] = true;1853 }1854 if (glyphProfile.sizeOfInstructions > maxSizeOfInstructions) {1855 maxSizeOfInstructions = glyphProfile.sizeOfInstructions;1856 }1857 writeOffset += newLength;1858 itemEncode(locaData, j, writeOffset);1859 }1860 if (writeOffset === 0) {1861 // glyf table cannot be empty -- redoing the glyf and loca tables1862 // to have single glyph with one point1863 const simpleGlyph = new Uint8Array([1864 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0,1865 ]);1866 for (i = 0, j = itemSize; i < numGlyphsOut; i++, j += itemSize) {1867 itemEncode(locaData, j, simpleGlyph.length);1868 }1869 glyf.data = simpleGlyph;1870 } else if (dupFirstEntry) {1871 // Browsers will not display a glyph at position 0. Typically glyph 01872 // is notdef, but a number of fonts put a valid glyph there so it must1873 // be duplicated and appended.1874 const firstEntryLength = itemDecode(locaData, itemSize);1875 if (newGlyfData.length > firstEntryLength + writeOffset) {1876 glyf.data = newGlyfData.subarray(0, firstEntryLength + writeOffset);1877 } else {1878 glyf.data = new Uint8Array(firstEntryLength + writeOffset);1879 glyf.data.set(newGlyfData.subarray(0, writeOffset));1880 }1881 glyf.data.set(newGlyfData.subarray(0, firstEntryLength), writeOffset);1882 itemEncode(1883 loca.data,1884 locaData.length - itemSize,1885 writeOffset + firstEntryLength1886 );1887 } else {1888 glyf.data = newGlyfData.subarray(0, writeOffset);1889 }1890 return {1891 missingGlyphs,1892 maxSizeOfInstructions,1893 };1894 }1895 function readPostScriptTable(post, propertiesObj, maxpNumGlyphs) {1896 const start = (font.start ? font.start : 0) + post.offset;1897 font.pos = start;1898 const length = post.length,1899 end = start + length;1900 const version = font.getInt32();1901 // skip rest to the tables1902 font.skip(28);1903 let glyphNames;1904 let valid = true;1905 let i;1906 switch (version) {1907 case 0x00010000:1908 glyphNames = MacStandardGlyphOrdering;1909 break;1910 case 0x00020000:1911 const numGlyphs = font.getUint16();1912 if (numGlyphs !== maxpNumGlyphs) {1913 valid = false;1914 break;1915 }1916 const glyphNameIndexes = [];1917 for (i = 0; i < numGlyphs; ++i) {1918 const index = font.getUint16();1919 if (index >= 32768) {1920 valid = false;1921 break;1922 }1923 glyphNameIndexes.push(index);1924 }1925 if (!valid) {1926 break;1927 }1928 const customNames = [],1929 strBuf = [];1930 while (font.pos < end) {1931 const stringLength = font.getByte();1932 strBuf.length = stringLength;1933 for (i = 0; i < stringLength; ++i) {1934 strBuf[i] = String.fromCharCode(font.getByte());1935 }1936 customNames.push(strBuf.join(""));1937 }1938 glyphNames = [];1939 for (i = 0; i < numGlyphs; ++i) {1940 const j = glyphNameIndexes[i];1941 if (j < 258) {1942 glyphNames.push(MacStandardGlyphOrdering[j]);1943 continue;1944 }1945 glyphNames.push(customNames[j - 258]);1946 }1947 break;1948 case 0x00030000:1949 break;1950 default:1951 warn("Unknown/unsupported post table version " + version);1952 valid = false;1953 if (propertiesObj.defaultEncoding) {1954 glyphNames = propertiesObj.defaultEncoding;1955 }1956 break;1957 }1958 propertiesObj.glyphNames = glyphNames;1959 return valid;1960 }1961 function readNameTable(nameTable) {1962 const start = (font.start ? font.start : 0) + nameTable.offset;1963 font.pos = start;1964 const names = [[], []];1965 const length = nameTable.length,1966 end = start + length;1967 const format = font.getUint16();1968 const FORMAT_0_HEADER_LENGTH = 6;1969 if (format !== 0 || length < FORMAT_0_HEADER_LENGTH) {1970 // unsupported name table format or table "too" small1971 return names;1972 }1973 const numRecords = font.getUint16();1974 const stringsStart = font.getUint16();1975 const records = [];1976 const NAME_RECORD_LENGTH = 12;1977 let i, ii;1978 for (i = 0; i < numRecords && font.pos + NAME_RECORD_LENGTH <= end; i++) {1979 const r = {1980 platform: font.getUint16(),1981 encoding: font.getUint16(),1982 language: font.getUint16(),1983 name: font.getUint16(),1984 length: font.getUint16(),1985 offset: font.getUint16(),1986 };1987 // using only Macintosh and Windows platform/encoding names1988 if (1989 (r.platform === 1 && r.encoding === 0 && r.language === 0) ||1990 (r.platform === 3 && r.encoding === 1 && r.language === 0x409)1991 ) {1992 records.push(r);1993 }1994 }1995 for (i = 0, ii = records.length; i < ii; i++) {1996 const record = records[i];1997 if (record.length <= 0) {1998 continue; // Nothing to process, ignoring.1999 }2000 const pos = start + stringsStart + record.offset;2001 if (pos + record.length > end) {2002 continue; // outside of name table, ignoring2003 }2004 font.pos = pos;2005 const nameIndex = record.name;2006 if (record.encoding) {2007 // unicode2008 let str = "";2009 for (let j = 0, jj = record.length; j < jj; j += 2) {2010 str += String.fromCharCode(font.getUint16());2011 }2012 names[1][nameIndex] = str;2013 } else {2014 names[0][nameIndex] = font.getString(record.length);2015 }2016 }2017 return names;2018 }2019 // prettier-ignore2020 const TTOpsStackDeltas = [2021 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5,2022 -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, -1, -1,2023 1, -1, -999, 0, 1, 0, -1, -2, 0, -1, -2, -1, -1, 0, -1, -1,2024 0, 0, -999, -999, -1, -1, -1, -1, -2, -999, -2, -2, -999, 0, -2, -2,2025 0, 0, -2, 0, -2, 0, 0, 0, -2, -1, -1, 1, 1, 0, 0, -1,2026 -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, 0, -999, -1, -1,2027 -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,2028 -2, -999, -999, -999, -999, -999, -1, -1, -2, -2, 0, 0, 0, 0, -1, -1,2029 -999, -2, -2, 0, 0, -1, -2, -2, 0, 0, 0, -1, -1, -1, -2];2030 // 0xC0-DF == -1 and 0xE0-FF == -22031 function sanitizeTTProgram(table, ttContext) {2032 let data = table.data;2033 let i = 0,2034 j,2035 n,2036 b,2037 funcId,2038 pc,2039 lastEndf = 0,2040 lastDeff = 0;2041 const stack = [];2042 const callstack = [];2043 const functionsCalled = [];2044 let tooComplexToFollowFunctions = ttContext.tooComplexToFollowFunctions;2045 let inFDEF = false,2046 ifLevel = 0,2047 inELSE = 0;2048 for (let ii = data.length; i < ii; ) {2049 const op = data[i++];2050 // The TrueType instruction set docs can be found at2051 // https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html2052 if (op === 0x40) {2053 // NPUSHB - pushes n bytes2054 n = data[i++];2055 if (inFDEF || inELSE) {2056 i += n;2057 } else {2058 for (j = 0; j < n; j++) {2059 stack.push(data[i++]);2060 }2061 }2062 } else if (op === 0x41) {2063 // NPUSHW - pushes n words2064 n = data[i++];2065 if (inFDEF || inELSE) {2066 i += n * 2;2067 } else {2068 for (j = 0; j < n; j++) {2069 b = data[i++];2070 stack.push((b << 8) | data[i++]);2071 }2072 }2073 } else if ((op & 0xf8) === 0xb0) {2074 // PUSHB - pushes bytes2075 n = op - 0xb0 + 1;2076 if (inFDEF || inELSE) {2077 i += n;2078 } else {2079 for (j = 0; j < n; j++) {2080 stack.push(data[i++]);2081 }2082 }2083 } else if ((op & 0xf8) === 0xb8) {2084 // PUSHW - pushes words2085 n = op - 0xb8 + 1;2086 if (inFDEF || inELSE) {2087 i += n * 2;2088 } else {2089 for (j = 0; j < n; j++) {2090 b = data[i++];2091 stack.push((b << 8) | data[i++]);2092 }2093 }2094 } else if (op === 0x2b && !tooComplexToFollowFunctions) {2095 // CALL2096 if (!inFDEF && !inELSE) {2097 // collecting information about which functions are used2098 funcId = stack[stack.length - 1];2099 if (isNaN(funcId)) {2100 info("TT: CALL empty stack (or invalid entry).");2101 } else {2102 ttContext.functionsUsed[funcId] = true;2103 if (funcId in ttContext.functionsStackDeltas) {2104 const newStackLength =2105 stack.length + ttContext.functionsStackDeltas[funcId];2106 if (newStackLength < 0) {2107 warn("TT: CALL invalid functions stack delta.");2108 ttContext.hintsValid = false;2109 return;2110 }2111 stack.length = newStackLength;2112 } else if (2113 funcId in ttContext.functionsDefined &&2114 !functionsCalled.includes(funcId)2115 ) {2116 callstack.push({ data, i, stackTop: stack.length - 1 });2117 functionsCalled.push(funcId);2118 pc = ttContext.functionsDefined[funcId];2119 if (!pc) {2120 warn("TT: CALL non-existent function");2121 ttContext.hintsValid = false;2122 return;2123 }2124 data = pc.data;2125 i = pc.i;2126 }2127 }2128 }2129 } else if (op === 0x2c && !tooComplexToFollowFunctions) {2130 // FDEF2131 if (inFDEF || inELSE) {2132 warn("TT: nested FDEFs not allowed");2133 tooComplexToFollowFunctions = true;2134 }2135 inFDEF = true;2136 // collecting information about which functions are defined2137 lastDeff = i;2138 funcId = stack.pop();2139 ttContext.functionsDefined[funcId] = { data, i };2140 } else if (op === 0x2d) {2141 // ENDF - end of function2142 if (inFDEF) {2143 inFDEF = false;2144 lastEndf = i;2145 } else {2146 pc = callstack.pop();2147 if (!pc) {2148 warn("TT: ENDF bad stack");2149 ttContext.hintsValid = false;2150 return;2151 }2152 funcId = functionsCalled.pop();2153 data = pc.data;2154 i = pc.i;2155 ttContext.functionsStackDeltas[funcId] = stack.length - pc.stackTop;2156 }2157 } else if (op === 0x89) {2158 // IDEF - instruction definition2159 if (inFDEF || inELSE) {2160 warn("TT: nested IDEFs not allowed");2161 tooComplexToFollowFunctions = true;2162 }2163 inFDEF = true;2164 // recording it as a function to track ENDF2165 lastDeff = i;2166 } else if (op === 0x58) {2167 // IF2168 ++ifLevel;2169 } else if (op === 0x1b) {2170 // ELSE2171 inELSE = ifLevel;2172 } else if (op === 0x59) {2173 // EIF2174 if (inELSE === ifLevel) {2175 inELSE = 0;2176 }2177 --ifLevel;2178 } else if (op === 0x1c) {2179 // JMPR2180 if (!inFDEF && !inELSE) {2181 const offset = stack[stack.length - 1];2182 // only jumping forward to prevent infinite loop2183 if (offset > 0) {2184 i += offset - 1;2185 }2186 }2187 }2188 // Adjusting stack not extactly, but just enough to get function id2189 if (!inFDEF && !inELSE) {2190 let stackDelta = 0;2191 if (op <= 0x8e) {2192 stackDelta = TTOpsStackDeltas[op];2193 } else if (op >= 0xc0 && op <= 0xdf) {2194 stackDelta = -1;2195 } else if (op >= 0xe0) {2196 stackDelta = -2;2197 }2198 if (op >= 0x71 && op <= 0x75) {2199 n = stack.pop();2200 if (!isNaN(n)) {2201 stackDelta = -n * 2;2202 }2203 }2204 while (stackDelta < 0 && stack.length > 0) {2205 stack.pop();2206 stackDelta++;2207 }2208 while (stackDelta > 0) {2209 stack.push(NaN); // pushing any number into stack2210 stackDelta--;2211 }2212 }2213 }2214 ttContext.tooComplexToFollowFunctions = tooComplexToFollowFunctions;2215 const content = [data];2216 if (i > data.length) {2217 content.push(new Uint8Array(i - data.length));2218 }2219 if (lastDeff > lastEndf) {2220 warn("TT: complementing a missing function tail");2221 // new function definition started, but not finished2222 // complete function by [CLEAR, ENDF]2223 content.push(new Uint8Array([0x22, 0x2d]));2224 }2225 foldTTTable(table, content);2226 }2227 function checkInvalidFunctions(ttContext, maxFunctionDefs) {2228 if (ttContext.tooComplexToFollowFunctions) {2229 return;2230 }2231 if (ttContext.functionsDefined.length > maxFunctionDefs) {2232 warn("TT: more functions defined than expected");2233 ttContext.hintsValid = false;2234 return;2235 }2236 for (let j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) {2237 if (j > maxFunctionDefs) {2238 warn("TT: invalid function id: " + j);2239 ttContext.hintsValid = false;2240 return;2241 }2242 if (ttContext.functionsUsed[j] && !ttContext.functionsDefined[j]) {2243 warn("TT: undefined function: " + j);2244 ttContext.hintsValid = false;2245 return;2246 }2247 }2248 }2249 function foldTTTable(table, content) {2250 if (content.length > 1) {2251 // concatenating the content items2252 let newLength = 0;2253 let j, jj;2254 for (j = 0, jj = content.length; j < jj; j++) {2255 newLength += content[j].length;2256 }2257 newLength = (newLength + 3) & ~3;2258 const result = new Uint8Array(newLength);2259 let pos = 0;2260 for (j = 0, jj = content.length; j < jj; j++) {2261 result.set(content[j], pos);2262 pos += content[j].length;2263 }2264 table.data = result;2265 table.length = newLength;2266 }2267 }2268 function sanitizeTTPrograms(fpgm, prep, cvt, maxFunctionDefs) {2269 const ttContext = {2270 functionsDefined: [],2271 functionsUsed: [],2272 functionsStackDeltas: [],2273 tooComplexToFollowFunctions: false,2274 hintsValid: true,2275 };2276 if (fpgm) {2277 sanitizeTTProgram(fpgm, ttContext);2278 }2279 if (prep) {2280 sanitizeTTProgram(prep, ttContext);2281 }2282 if (fpgm) {2283 checkInvalidFunctions(ttContext, maxFunctionDefs);2284 }2285 if (cvt && cvt.length & 1) {2286 const cvtData = new Uint8Array(cvt.length + 1);2287 cvtData.set(cvt.data);2288 cvt.data = cvtData;2289 }2290 return ttContext.hintsValid;2291 }2292 // The following steps modify the original font data, making copy2293 font = new Stream(new Uint8Array(font.getBytes()));2294 let header, tables;2295 if (isTrueTypeCollectionFile(font)) {2296 const ttcData = readTrueTypeCollectionData(font, this.name);2297 header = ttcData.header;2298 tables = ttcData.tables;2299 } else {2300 header = readOpenTypeHeader(font);2301 tables = readTables(font, header.numTables);2302 }2303 let cff, cffFile;2304 const isTrueType = !tables["CFF "];2305 if (!isTrueType) {2306 const isComposite =2307 properties.composite &&2308 ((properties.cidToGidMap || []).length > 0 ||2309 !(properties.cMap instanceof IdentityCMap));...
Using AI Code Generation
1var wpt = require("wpt");2var path = require("path");3var isTrueTypeCollectionFile = wpt.isTrueTypeCollectionFile;4var filePath = path.join(__dirname, "test.ttf");5var result = isTrueTypeCollectionFile(filePath);6console.log(result);7var wpt = require("wpt");8var path = require("path");9var isTrueTypeCollectionFile = wpt.isTrueTypeCollectionFile;10var filePath = path.join(__dirname, "test.ttc");11var result = isTrueTypeCollectionFile(filePath);12console.log(result);13var wpt = require("wpt");14var path = require("path");15var isTrueTypeCollectionFile = wpt.isTrueTypeCollectionFile;16var filePath = path.join(__dirname, "test.pdf");17var result = isTrueTypeCollectionFile(filePath);18console.log(result);
Using AI Code Generation
1var wptools = require('wptools');2var fs = require('fs');3var path = require('path');4var filePath = path.join(__dirname, 'test.ttf');5var isTrueTypeCollection = wptools.isTrueTypeCollectionFile(filePath);6console.log(isTrueTypeCollection);
Using AI Code Generation
1var wptools = require('wptools');2var fs = require('fs');3var path = require('path');4var file = path.join(__dirname, 'test.ttc');5var buffer = fs.readFileSync(file);6var result = wptools.isTrueTypeCollectionFile(buffer);7console.log(result);
Using AI Code Generation
1var wptools = require('./wptools.js');2var fs = require('fs');3var path = require('path');4var filePath = path.join(__dirname, 'test.ttf');5var fileBuffer = fs.readFileSync(filePath);6var result = wptools.isTrueTypeCollectionFile(fileBuffer);7console.log(result);8var wptools = require('./wptools.js');9var fs = require('fs');10var path = require('path');11var filePath = path.join(__dirname, 'test.ttc');12var fileBuffer = fs.readFileSync(filePath);13var result = wptools.isTrueTypeCollectionFile(fileBuffer);14console.log(result);15var wptools = require('./wptools.js');16var fs = require('fs');17var path = require('path');18var filePath = path.join(__dirname, 'test.ttf');19var fileBuffer = fs.readFileSync(filePath);20var result = wptools.isWoffFile(fileBuffer);21console.log(result);22var wptools = require('./wptools.js');23var fs = require('fs');24var path = require('path');25var filePath = path.join(__dirname, 'test.woff');26var fileBuffer = fs.readFileSync(filePath);27var result = wptools.isWoffFile(fileBuffer);28console.log(result);29var wptools = require('./wptools.js');30var fs = require('fs');31var path = require('path');32var filePath = path.join(__dirname, 'test.ttf');33var fileBuffer = fs.readFileSync(filePath);34var result = wptools.isWoff2File(fileBuffer);35console.log(result);36var wptools = require('./wptools.js');37var fs = require('fs');38var path = require('path');39var filePath = path.join(__dirname, 'test.woff2');
Using AI Code Generation
1var wptools = require('wp-tools');2var path = require('path');3var assert = require('assert');4var filePath = path.resolve(__dirname, 'test.ttf');5var isTTC = wptools.isTrueTypeCollectionFile(filePath);6assert(isTTC === false);7var filePath = path.resolve(__dirname, 'test.ttc');8var isTTC = wptools.isTrueTypeCollectionFile(filePath);9assert(isTTC === true);10var filePath = path.resolve(__dirname, 'test.woff');
Using AI Code Generation
1var wptools = require('./wp-tools.js');2var fs = require('fs');3var path = require('path');4var file = path.join(__dirname, 'test.ttf');5var file2 = path.join(__dirname, 'test2.ttf');6var file3 = path.join(__dirname, 'test3.ttf');7var file4 = path.join(__dirname, 'test4.ttf');8var file5 = path.join(__dirname, 'test5.ttf');9var file6 = path.join(__dirname, 'test6.ttf');10var file7 = path.join(__dirname, 'test7.ttf');11var file8 = path.join(__dirname, 'test8.ttf');12var file9 = path.join(__dirname, 'test9.ttf');13var file10 = path.join(__dirname, 'test10.ttf');14var file11 = path.join(__dirname, 'test11.ttf');15var file12 = path.join(__dirname, 'test12.ttf');16var file13 = path.join(__dirname, 'test13.ttf');17var file14 = path.join(__dirname, 'test14.ttf');18var file15 = path.join(__dirname, 'test15.ttf');19var file16 = path.join(__dirname, 'test16.ttf');20var file17 = path.join(__dirname, 'test17.ttf');21var file18 = path.join(__dirname, 'test18.ttf');22var file19 = path.join(__dirname, 'test19.ttf');23var file20 = path.join(__dirname, 'test20.ttf');24var file21 = path.join(__dirname, 'test21.ttf');25var file22 = path.join(__dirname, 'test22.ttf');26var file23 = path.join(__dirname, 'test23.ttf');27var file24 = path.join(__dirname, 'test24.ttf');28var file25 = path.join(__dirname, 'test25.ttf');29var file26 = path.join(__dirname, 'test26.ttf');30var file27 = path.join(__dirname, 'test27.ttf');31var file28 = path.join(__dirname, 'test28.ttf');32var file29 = path.join(__dirname, 'test29.ttf');33var file30 = path.join(__dirname, 'test30.ttf');34var file31 = path.join(__dirname, 'test31.ttf');35var file32 = path.join(__dirname, 'test32.ttf');36var file33 = path.join(__dirname, 'test33.ttf');37var file34 = path.join(__dirname, 'test34
Using AI Code Generation
1var wptf = new ActiveXObject("WPTF.WPTF");2var isTrueTypeCollectionFile = wptf.isTrueTypeCollectionFile("c:\\windows\\fonts\\arial.ttf");3WScript.Echo(isTrueTypeCollectionFile);4var wptf = new ActiveXObject("WPTF.WPTF");5var isTrueTypeFontFile = wptf.isTrueTypeFontFile("c:\\windows\\fonts\\arial.ttf");6WScript.Echo(isTrueTypeFontFile);7var wptf = new ActiveXObject("WPTF.WPTF");8var isTrueTypeFontFile = wptf.isTrueTypeFontFile("c:\\windows\\fonts\\arial.ttc");9WScript.Echo(isTrueTypeFontFile);10var wptf = new ActiveXObject("WPTF.WPTF");11var isType1FontFile = wptf.isType1FontFile("c:\\windows\\fonts\\arial.ttf");12WScript.Echo(isType1FontFile);13var wptf = new ActiveXObject("WPTF.WPTF");14var isType1FontFile = wptf.isType1FontFile("c:\\windows\\fonts\\arial.pfb");15WScript.Echo(isType1FontFile);16var wptf = new ActiveXObject("WPTF.WPTF");17var isType1FontFile = wptf.isType1FontFile("c:\\windows\\fonts\\arial.pfa");18WScript.Echo(isType1FontFile);19var wptf = new ActiveXObject("WPTF.WPTF");
Using AI Code Generation
1var wpt = require('wpt');2var filePath = 'C:\\Users\\Desktop\\test.ttc';3var result = wpt.isTrueTypeCollectionFile(filePath);4console.log(result);5wpt.isTrueTypeCollectionFile(filePath)6var wpt = require('wpt');7var filePath = 'C:\\Users\\Desktop\\test.ttf';8var result = wpt.isTrueTypeCollectionFile(filePath);9console.log(result);10wpt.isTrueTypeFontFile(filePath)11var wpt = require('wpt');12var filePath = 'C:\\Users\\Desktop\\test.ttf';13var result = wpt.isTrueTypeFontFile(filePath);14console.log(result);15wpt.isType1FontFile(filePath)
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!