diff --git a/lib/injectSubsetDefinitions.js b/lib/injectSubsetDefinitions.js index ee531a96..4ddd0c75 100644 --- a/lib/injectSubsetDefinitions.js +++ b/lib/injectSubsetDefinitions.js @@ -5,30 +5,28 @@ function injectSubsetDefinitions(cssValue, webfontNameMap, replaceOriginal) { const subsetFontNames = new Set( Object.values(webfontNameMap).map(name => name.toLowerCase()) ); - const tokens = postcssValuesParser(cssValue).tokens; - let resultStr = ''; + const rootNode = postcssValuesParser.parse(cssValue); let isPreceededByWords = false; - for (let i = 0; i < tokens.length; i += 1) { - const token = tokens[i]; + for (const [i, node] of rootNode.nodes.entries()) { let possibleFontFamily; let lastFontFamilyTokenIndex = i; - if (token[0] === 'string') { - possibleFontFamily = unquote(token[1]); - } else if (token[0] === 'word') { + if (node.type === 'quoted') { + possibleFontFamily = unquote(node.value); + } else if (node.type === 'word') { if (!isPreceededByWords) { const wordSequence = []; - for (let j = i; j < tokens.length; j += 1) { - if (tokens[j][0] === 'word') { - wordSequence.push(tokens[j][1]); + for (let j = i; j < rootNode.nodes.length; j += 1) { + if (rootNode.nodes[j].type === 'word') { + wordSequence.push(rootNode.nodes[j].value); lastFontFamilyTokenIndex = j; - } else if (tokens[j][0] !== 'space') { + } else { break; } } possibleFontFamily = wordSequence.join(' '); } isPreceededByWords = true; - } else if (token[0] !== 'space') { + } else { isPreceededByWords = false; } if (possibleFontFamily) { @@ -37,10 +35,14 @@ function injectSubsetDefinitions(cssValue, webfontNameMap, replaceOriginal) { // Bail out, a subset font is already listed return cssValue; } else if (webfontNameMap[possibleFontFamilyLowerCase]) { - resultStr += `'${webfontNameMap[possibleFontFamilyLowerCase].replace( - /'/g, - "\\'" - )}'`; + rootNode.insertBefore( + node, + `'${webfontNameMap[possibleFontFamilyLowerCase].replace( + /'/g, + "\\'" + )}', ` + ); + return rootNode.toString(); if (replaceOriginal) { tokens.splice(i, lastFontFamilyTokenIndex - i + 1); i -= 1; @@ -50,9 +52,8 @@ function injectSubsetDefinitions(cssValue, webfontNameMap, replaceOriginal) { } } } - resultStr += token[1]; } - return resultStr; + return cssValue; } module.exports = injectSubsetDefinitions; diff --git a/lib/injectSubsetDefinitions.js.orig b/lib/injectSubsetDefinitions.js.orig new file mode 100644 index 00000000..ee531a96 --- /dev/null +++ b/lib/injectSubsetDefinitions.js.orig @@ -0,0 +1,58 @@ +const postcssValuesParser = require('postcss-values-parser'); +const unquote = require('./unquote'); + +function injectSubsetDefinitions(cssValue, webfontNameMap, replaceOriginal) { + const subsetFontNames = new Set( + Object.values(webfontNameMap).map(name => name.toLowerCase()) + ); + const tokens = postcssValuesParser(cssValue).tokens; + let resultStr = ''; + let isPreceededByWords = false; + for (let i = 0; i < tokens.length; i += 1) { + const token = tokens[i]; + let possibleFontFamily; + let lastFontFamilyTokenIndex = i; + if (token[0] === 'string') { + possibleFontFamily = unquote(token[1]); + } else if (token[0] === 'word') { + if (!isPreceededByWords) { + const wordSequence = []; + for (let j = i; j < tokens.length; j += 1) { + if (tokens[j][0] === 'word') { + wordSequence.push(tokens[j][1]); + lastFontFamilyTokenIndex = j; + } else if (tokens[j][0] !== 'space') { + break; + } + } + possibleFontFamily = wordSequence.join(' '); + } + isPreceededByWords = true; + } else if (token[0] !== 'space') { + isPreceededByWords = false; + } + if (possibleFontFamily) { + const possibleFontFamilyLowerCase = possibleFontFamily.toLowerCase(); + if (subsetFontNames.has(possibleFontFamilyLowerCase)) { + // Bail out, a subset font is already listed + return cssValue; + } else if (webfontNameMap[possibleFontFamilyLowerCase]) { + resultStr += `'${webfontNameMap[possibleFontFamilyLowerCase].replace( + /'/g, + "\\'" + )}'`; + if (replaceOriginal) { + tokens.splice(i, lastFontFamilyTokenIndex - i + 1); + i -= 1; + continue; + } else { + resultStr += ', '; + } + } + } + resultStr += token[1]; + } + return resultStr; +} + +module.exports = injectSubsetDefinitions; diff --git a/lib/injectSubsetDefinitions.js.rej b/lib/injectSubsetDefinitions.js.rej new file mode 100644 index 00000000..1bd73495 --- /dev/null +++ b/lib/injectSubsetDefinitions.js.rej @@ -0,0 +1,66 @@ +--- lib/util/fonts/injectSubsetDefinitions.js ++++ lib/util/fonts/injectSubsetDefinitions.js +@@ -5,28 +5,26 @@ function injectSubsetDefinitions(cssValue, webfontNameMap) { + const subsetFontNames = new Set( + Object.values(webfontNameMap).map(name => name.toLowerCase()) + ); +- const tokens = postcssValuesParser(cssValue).tokens; +- let resultStr = ''; ++ const rootNode = postcssValuesParser.parse(cssValue); + let isPreceededByWords = false; +- for (let i = 0; i < tokens.length; i += 1) { +- const token = tokens[i]; ++ for (const [i, node] of rootNode.nodes.entries()) { + let possibleFontFamily; +- if (token[0] === 'string') { +- possibleFontFamily = unquote(token[1]); +- } else if (token[0] === 'word') { ++ if (node.type === 'quoted') { ++ possibleFontFamily = unquote(node.value); ++ } else if (node.type === 'word') { + if (!isPreceededByWords) { + const wordSequence = []; +- for (let j = i; j < tokens.length; j += 1) { +- if (tokens[j][0] === 'word') { +- wordSequence.push(tokens[j][1]); +- } else if (tokens[j][0] !== 'space') { ++ for (let j = i; j < rootNode.nodes.length; j += 1) { ++ if (rootNode.nodes[j].type === 'word') { ++ wordSequence.push(rootNode.nodes[j].value); ++ } else { + break; + } + } + possibleFontFamily = wordSequence.join(' '); + } + isPreceededByWords = true; +- } else if (token[0] !== 'space') { ++ } else { + isPreceededByWords = false; + } + if (possibleFontFamily) { +@@ -35,15 +33,18 @@ function injectSubsetDefinitions(cssValue, webfontNameMap) { + if (subsetFontNames.has(possibleFontFamilyLowerCase)) { + return cssValue; + } else if (webfontNameMap[possibleFontFamilyLowerCase]) { +- resultStr += `'${webfontNameMap[possibleFontFamilyLowerCase].replace( +- /'/g, +- "\\'" +- )}', `; ++ rootNode.insertBefore( ++ node, ++ `'${webfontNameMap[possibleFontFamilyLowerCase].replace( ++ /'/g, ++ "\\'" ++ )}', ` ++ ); ++ return rootNode.toString(); + } + } +- resultStr += token[1]; + } +- return resultStr; ++ return cssValue; + } + + module.exports = injectSubsetDefinitions; diff --git a/package.json b/package.json index 287a86ce..0be538a2 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "font-tracer": "^1.1.0", "fontkit": "^1.8.0", "lodash.groupby": "^4.6.0", - "postcss-values-parser": "^2.0.1", + "postcss-values-parser": "^3.0.5", "pretty-bytes": "^5.1.0", "urltools": "^0.4.1", "yargs": "^12.0.2" diff --git a/test/injectSubsetDefinitions.js b/test/injectSubsetDefinitions.js index 4559690d..8773f971 100644 --- a/test/injectSubsetDefinitions.js +++ b/test/injectSubsetDefinitions.js @@ -42,7 +42,7 @@ describe('injectSubsetDefinitions', function() { expect( injectSubsetDefinitions('times new roman', webfontNameMap), 'to equal', - "'times new roman__subset', times new roman" + "'times new roman__subset', times new roman" ); });