font-utils.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.getFontDefinitionFromNetwork = getFontDefinitionFromNetwork;
  6. exports.getFontDefinitionFromManifest = getFontDefinitionFromManifest;
  7. exports.getFontOverrideCss = getFontOverrideCss;
  8. var Log = _interopRequireWildcard(require("../build/output/log"));
  9. var _constants = require("../shared/lib/constants");
  10. function _getRequireWildcardCache() {
  11. if (typeof WeakMap !== "function") return null;
  12. var cache = new WeakMap();
  13. _getRequireWildcardCache = function() {
  14. return cache;
  15. };
  16. return cache;
  17. }
  18. function _interopRequireWildcard(obj) {
  19. if (obj && obj.__esModule) {
  20. return obj;
  21. }
  22. if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
  23. return {
  24. default: obj
  25. };
  26. }
  27. var cache = _getRequireWildcardCache();
  28. if (cache && cache.has(obj)) {
  29. return cache.get(obj);
  30. }
  31. var newObj = {};
  32. var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
  33. for(var key in obj){
  34. if (Object.prototype.hasOwnProperty.call(obj, key)) {
  35. var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
  36. if (desc && (desc.get || desc.set)) {
  37. Object.defineProperty(newObj, key, desc);
  38. } else {
  39. newObj[key] = obj[key];
  40. }
  41. }
  42. }
  43. newObj.default = obj;
  44. if (cache) {
  45. cache.set(obj, newObj);
  46. }
  47. return newObj;
  48. }
  49. const googleFontsMetrics = require("./google-font-metrics.json");
  50. const https = require("https");
  51. const CHROME_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36";
  52. const IE_UA = "Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko";
  53. function isGoogleFont(url) {
  54. return url.startsWith(_constants.GOOGLE_FONT_PROVIDER);
  55. }
  56. function getFontForUA(url, UA) {
  57. return new Promise((resolve, reject)=>{
  58. let rawData = "";
  59. https.get(url, {
  60. headers: {
  61. "user-agent": UA
  62. }
  63. }, (res)=>{
  64. res.on("data", (chunk)=>{
  65. rawData += chunk;
  66. });
  67. res.on("end", ()=>{
  68. resolve(rawData.toString("utf8"));
  69. });
  70. }).on("error", (e)=>{
  71. reject(e);
  72. });
  73. });
  74. }
  75. async function getFontDefinitionFromNetwork(url) {
  76. let result = "";
  77. /**
  78. * The order of IE -> Chrome is important, other wise chrome starts loading woff1.
  79. * CSS cascading 🤷‍♂️.
  80. */ try {
  81. if (isGoogleFont(url)) {
  82. result += await getFontForUA(url, IE_UA);
  83. }
  84. result += await getFontForUA(url, CHROME_UA);
  85. } catch (e) {
  86. Log.warn(`Failed to download the stylesheet for ${url}. Skipped optimizing this font.`);
  87. return "";
  88. }
  89. return result;
  90. }
  91. function getFontDefinitionFromManifest(url, manifest) {
  92. var ref;
  93. return ((ref = manifest.find((font)=>{
  94. if (font && font.url === url) {
  95. return true;
  96. }
  97. return false;
  98. })) == null ? void 0 : ref.content) || "";
  99. }
  100. function parseGoogleFontName(css) {
  101. const regex = /font-family: ([^;]*)/g;
  102. const matches = css.matchAll(regex);
  103. const fontNames = new Set();
  104. for (let font of matches){
  105. const fontFamily = font[1].replace(/^['"]|['"]$/g, "");
  106. fontNames.add(fontFamily);
  107. }
  108. return [
  109. ...fontNames
  110. ];
  111. }
  112. function calculateOverrideCSS(font, fontMetrics) {
  113. const fontName = font.toLowerCase().trim().replace(/ /g, "-");
  114. const fontKey = font.toLowerCase().trim().replace(/ /g, "");
  115. const { category , ascentOverride , descentOverride , lineGapOverride } = fontMetrics[fontKey];
  116. const fallbackFont = category === "serif" ? _constants.DEFAULT_SERIF_FONT : _constants.DEFAULT_SANS_SERIF_FONT;
  117. const ascent = (ascentOverride * 100).toFixed(2);
  118. const descent = (descentOverride * 100).toFixed(2);
  119. const lineGap = (lineGapOverride * 100).toFixed(2);
  120. return `
  121. @font-face {
  122. font-family: "${fontName}-fallback";
  123. ascent-override: ${ascent}%;
  124. descent-override: ${descent}%;
  125. line-gap-override: ${lineGap}%;
  126. src: local("${fallbackFont}");
  127. }
  128. `;
  129. }
  130. function getFontOverrideCss(url, css) {
  131. if (!isGoogleFont(url)) {
  132. return "";
  133. }
  134. try {
  135. const fontNames = parseGoogleFontName(css);
  136. const fontMetrics = googleFontsMetrics;
  137. const fontCss = fontNames.reduce((cssStr, fontName)=>{
  138. cssStr += calculateOverrideCSS(fontName, fontMetrics);
  139. return cssStr;
  140. }, "");
  141. return fontCss;
  142. } catch (e) {
  143. console.log("Error getting font override values - ", e);
  144. return "";
  145. }
  146. }
  147. //# sourceMappingURL=font-utils.js.map