How to use readOpenTypeHeader method in wpt

Best JavaScript code snippet using wpt

fonts.js

Source:fonts.js Github

copy

Full Screen

...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));2310 // OpenType font (skip composite fonts with non-default glyph mapping).2311 if (2312 (header.version === "OTTO" && !isComposite) ||2313 !tables.head ||2314 !tables.hhea ||...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require('webpagetest');2var wpt = new WebPageTest('www.webpagetest.org');3 if (err) {4 console.log(err);5 } else {6 console.log(data);7 }8});9var wpt = require('webpagetest');10var wpt = new WebPageTest('www.webpagetest.org');11wpt.getLocations(function(err, data) {12 if (err) {13 console.log(err);14 } else {15 console.log(data);16 }17});18var wpt = require('webpagetest');19var wpt = new WebPageTest('www.webpagetest.org');20wpt.getTesters(function(err, data) {21 if (err) {22 console.log(err);23 } else {24 console.log(data);25 }26});27var wpt = require('webpagetest');28var wpt = new WebPageTest('www.webpagetest.org');29wpt.getTestStatus('123456789', function(err, data) {30 if (err) {31 console.log(err);32 } else {33 console.log(data);34 }35});36var wpt = require('webpagetest');37var wpt = new WebPageTest('www.webpagetest.org');38wpt.getTestResults('123456789', function(err, data) {39 if (err) {40 console.log(err);41 } else {42 console.log(data);43 }44});45var wpt = require('webpagetest');46var wpt = new WebPageTest('www.webpagetest.org');47wpt.getTestResults('123456789', function(err, data) {48 if (err) {49 console.log(err);50 } else {51 console.log(data);52 }53});54var wpt = require('webpagetest');

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require('wpt');2var wptObj = new wpt();3wptObj.readOpenTypeHeader('test.otf', function(err, header) {4 if (err) {5 console.log('Error: ' + err);6 } else {7 console.log('header: ' + header);8 }9});10var wpt = require('wpt');11var wptObj = new wpt();12wptObj.readOpenTypeTable('test.otf', 'head', function(err, table) {13 if (err) {14 console.log('Error: ' + err);15 } else {16 console.log('table: ' + table);17 }18});19var wpt = require('wpt');20var wptObj = new wpt();21wptObj.readOpenTypeTable('test.otf', 'head', function(err, table) {22 if (err) {23 console.log('Error: ' + err);24 } else {25 console.log('table: ' + table);26 }27});

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require('wpt.js');2var fs = require('fs');3var file = fs.readFileSync('test.ttf');4var result = wpt.readOpenTypeHeader(file);5console.log(result);6{ version: 1,7 [ { tag: 'OS/2',8 length: 96 },9 { tag: 'cmap',10 length: 74 },11 { tag: 'cvt ',12 length: 0 },13 { tag: 'fpgm',14 length: 0 },15 { tag: 'glyf',16 length: 0 },17 { tag: 'head',18 length: 0 },19 { tag: 'hhea',20 length: 0 },21 { tag: 'hmtx',22 length: 0 },23 { tag: 'loca',24 length: 0 },25 { tag: 'maxp',26 length: 0 },27 { tag: 'name',28 length: 0 },29 { tag: 'post',30 length: 0 } ] }

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptools = require('wp-tools');2var wp = new wptools();3var fs = require('fs');4var buffer = fs.readFileSync('test.ttf');5var header = wp.readOpenTypeHeader(buffer);6console.log(header);7console.log(header.version);8console.log(header.version);9console.log(header.numTables);10console.log(header.searchRange);11console.log(header.entrySelector);12console.log(header.rangeShift);13console.log(header.tableRecords);14console.log(header.tableRecords);15console.log(header.tableRecords[0]);16console.log(header.tableRecords[0].tag);17console.log(header.tableRecords[0].checkSum);18console.log(header.tableRecords[0].offset);19console.log(header.tableRecords[0].length);20console.log(header.tableRecords[0].length);21console.log(header.tableRecords[0].length);22console.log(header.tableRecords[0].length);23console.log(header.tableRecords[0].length);24console.log(header.tableRecords[0].length);25console.log(header.tableRecords[0].length);26console.log(header.tableRecords[0].length);27console.log(header.tableRecords[0].length);28console.log(header.tableRecords[0].length);29console.log(header.tableRecords[0].length);

Full Screen

Automation Testing Tutorials

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.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run wpt automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful