single-file-core 1.6.19 → 1.6.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/helper.js +67 -2
- package/core/index.js +3 -2
- package/core/lib/processor-helper-common.js +17 -24
- package/core/lib/processor-helper-inline.js +7 -1
- package/core/lib/processor-helper.js +2 -1
- package/modules/css-fonts-minifier.js +257 -129
- package/package.json +1 -1
- package/vendor/css-font-property-parser.js +47 -6
package/core/helper.js
CHANGED
|
@@ -86,6 +86,17 @@ const FONT_WEIGHTS = {
|
|
|
86
86
|
bolder: "700",
|
|
87
87
|
lighter: "100"
|
|
88
88
|
};
|
|
89
|
+
const FONT_STRETCHES = {
|
|
90
|
+
"ultra-condensed": "50%",
|
|
91
|
+
"extra-condensed": "62.5%",
|
|
92
|
+
"condensed": "75%",
|
|
93
|
+
"semi-condensed": "87.5%",
|
|
94
|
+
"normal": "100%",
|
|
95
|
+
"semi-expanded": "112.5%",
|
|
96
|
+
"expanded": "125%",
|
|
97
|
+
"extra-expanded": "150%",
|
|
98
|
+
"ultra-expanded": "200%"
|
|
99
|
+
};
|
|
89
100
|
const COMMENT_HEADER_LEGACY = "Archive processed by SingleFile";
|
|
90
101
|
const ELEMENT_NODE_TYPE = 1;
|
|
91
102
|
const TEXT_NODE_TYPE = 3;
|
|
@@ -139,6 +150,8 @@ export {
|
|
|
139
150
|
removeQuotes,
|
|
140
151
|
flatten,
|
|
141
152
|
getFontWeight,
|
|
153
|
+
getFontStretch,
|
|
154
|
+
getStylesheetsInCascadeOrder,
|
|
142
155
|
normalizeFontFamily,
|
|
143
156
|
getShadowRoot,
|
|
144
157
|
appendInfobar,
|
|
@@ -926,8 +939,9 @@ function getUsedFont(computedStyle, usedFonts, usedFontsCharacters, drawnCharact
|
|
|
926
939
|
if (fontFamilyName) {
|
|
927
940
|
const fontWeight = getFontWeight(computedStyle.getPropertyValue("font-weight"));
|
|
928
941
|
const fontVariant = computedStyle.getPropertyValue("font-variant") || "normal";
|
|
929
|
-
const
|
|
930
|
-
|
|
942
|
+
const fontStretch = getFontStretch(computedStyle.getPropertyValue("font-stretch") || "normal");
|
|
943
|
+
const value = [fontFamilyName, fontWeight, fontStyle, fontVariant, fontStretch];
|
|
944
|
+
usedFonts.set(JSON.stringify(value), value);
|
|
931
945
|
if (usedFontsCharacters && drawnCharacters) {
|
|
932
946
|
addUsedFontCharacters(usedFontsCharacters, fontFamilyName, fontStyle, drawnCharacters);
|
|
933
947
|
}
|
|
@@ -1246,6 +1260,57 @@ function getFontWeight(weight) {
|
|
|
1246
1260
|
return FONT_WEIGHTS[weight.toLowerCase().trim()] || weight;
|
|
1247
1261
|
}
|
|
1248
1262
|
|
|
1263
|
+
function getFontStretch(stretch) {
|
|
1264
|
+
return FONT_STRETCHES[stretch] || stretch;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
function getStylesheetsInCascadeOrder(stylesheets) {
|
|
1268
|
+
const entries = [];
|
|
1269
|
+
const entriesByRules = new Map();
|
|
1270
|
+
stylesheets.forEach((stylesheetInfo, key) => {
|
|
1271
|
+
const entry = { stylesheetInfo, key, index: entries.length };
|
|
1272
|
+
entries.push(entry);
|
|
1273
|
+
if (stylesheetInfo.stylesheet && stylesheetInfo.stylesheet.children) {
|
|
1274
|
+
entriesByRules.set(stylesheetInfo.stylesheet.children, entry);
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
const importedEntries = new Set();
|
|
1278
|
+
entries.forEach(entry => getImportedStylesheetEntries(entry, entriesByRules).forEach(importedEntry => importedEntries.add(importedEntry)));
|
|
1279
|
+
const orderedEntries = [];
|
|
1280
|
+
const visitedEntries = new Set();
|
|
1281
|
+
entries
|
|
1282
|
+
.filter(entry => !importedEntries.has(entry))
|
|
1283
|
+
.forEach(entry => addStylesheetEntry(entry, entriesByRules, orderedEntries, visitedEntries));
|
|
1284
|
+
entries
|
|
1285
|
+
.filter(entry => !visitedEntries.has(entry))
|
|
1286
|
+
.forEach(entry => addStylesheetEntry(entry, entriesByRules, orderedEntries, visitedEntries));
|
|
1287
|
+
return orderedEntries;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
function addStylesheetEntry(entry, entriesByRules, orderedEntries, visitedEntries) {
|
|
1291
|
+
if (!visitedEntries.has(entry)) {
|
|
1292
|
+
visitedEntries.add(entry);
|
|
1293
|
+
getImportedStylesheetEntries(entry, entriesByRules).forEach(importedEntry => addStylesheetEntry(importedEntry, entriesByRules, orderedEntries, visitedEntries));
|
|
1294
|
+
orderedEntries.push(entry);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
function getImportedStylesheetEntries(entry, entriesByRules) {
|
|
1299
|
+
const importedEntries = [];
|
|
1300
|
+
const stylesheet = entry.stylesheetInfo.stylesheet;
|
|
1301
|
+
if (stylesheet && stylesheet.children) {
|
|
1302
|
+
stylesheet.children.forEach(ruleData => {
|
|
1303
|
+
if (ruleData.type == "Atrule" && ruleData.name == "import" && ruleData.prelude && ruleData.prelude.children && ruleData.prelude.children.head) {
|
|
1304
|
+
const importedEntry = entriesByRules.get(ruleData.prelude.children.head.data.importedChildren);
|
|
1305
|
+
if (importedEntry) {
|
|
1306
|
+
importedEntries.push(importedEntry);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
return importedEntries;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1249
1314
|
function getContentSize(content) {
|
|
1250
1315
|
return new Blob([content]).size;
|
|
1251
1316
|
}
|
package/core/index.js
CHANGED
|
@@ -1343,10 +1343,11 @@ class Processor {
|
|
|
1343
1343
|
if (element.media) {
|
|
1344
1344
|
mediaText = element.media.toLowerCase();
|
|
1345
1345
|
}
|
|
1346
|
-
const
|
|
1346
|
+
const scope = element.closest("[" + SHADOWROOT_ATTRIBUTE_NAME + "]");
|
|
1347
1347
|
const stylesheetInfo = {
|
|
1348
1348
|
mediaText,
|
|
1349
|
-
scoped
|
|
1349
|
+
scoped: Boolean(scope),
|
|
1350
|
+
scope
|
|
1350
1351
|
};
|
|
1351
1352
|
await this.processorHelper.resolveStylesheets(element, stylesheetInfo, this.stylesheets, this.baseURI, options, this.workStyleElement, this.resources);
|
|
1352
1353
|
}));
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
import {
|
|
25
25
|
normalizeFontFamily,
|
|
26
26
|
getFontWeight,
|
|
27
|
+
getFontStretch,
|
|
28
|
+
getStylesheetsInCascadeOrder,
|
|
27
29
|
getDataURI
|
|
28
30
|
} from "./../helper.js";
|
|
29
31
|
import { serialize as serializeSrcset } from "./../../vendor/html-srcset-parser.js";
|
|
@@ -51,17 +53,6 @@ const REGEXP_FONT_FORMAT = /\.([^.?#]+)((\?|#).*?)?$/;
|
|
|
51
53
|
const REGEXP_FONT_FORMAT_VALUE = /format\((.*?)\)\s*,?$/;
|
|
52
54
|
const REGEXP_FONT_SRC = /(.*?)\s*,?$/;
|
|
53
55
|
const MEDIA_ALL = "all";
|
|
54
|
-
const FONT_STRETCHES = {
|
|
55
|
-
"ultra-condensed": "50%",
|
|
56
|
-
"extra-condensed": "62.5%",
|
|
57
|
-
"condensed": "75%",
|
|
58
|
-
"semi-condensed": "87.5%",
|
|
59
|
-
"normal": "100%",
|
|
60
|
-
"semi-expanded": "112.5%",
|
|
61
|
-
"expanded": "125%",
|
|
62
|
-
"extra-expanded": "150%",
|
|
63
|
-
"ultra-expanded": "200%"
|
|
64
|
-
};
|
|
65
56
|
const Blob = globalThis.Blob;
|
|
66
57
|
const Image = globalThis.Image;
|
|
67
58
|
const OffscreenCanvas = globalThis.OffscreenCanvas;
|
|
@@ -423,9 +414,12 @@ class ProcessorHelperCommon {
|
|
|
423
414
|
async removeAlternativeFonts(doc, stylesheets, fonts, fontTests) {
|
|
424
415
|
const fontsDetails = this.createFontsDetailsInfo();
|
|
425
416
|
const stats = { rules: { processed: 0, discarded: 0 }, fonts: { processed: 0, discarded: 0 } };
|
|
426
|
-
|
|
427
|
-
stylesheets.forEach(stylesheetInfo => {
|
|
417
|
+
const ruleScopes = new Map();
|
|
418
|
+
getStylesheetsInCascadeOrder(stylesheets).forEach(({ stylesheetInfo, index: sheetIndex }) => {
|
|
428
419
|
if (stylesheetInfo.stylesheet) {
|
|
420
|
+
if (stylesheetInfo.scope) {
|
|
421
|
+
cssTree.walk(stylesheetInfo.stylesheet, { visit: "Atrule", enter: node => ruleScopes.set(node, stylesheetInfo.scope) });
|
|
422
|
+
}
|
|
429
423
|
const cssRules = stylesheetInfo.stylesheet.children;
|
|
430
424
|
if (cssRules) {
|
|
431
425
|
stats.rules.processed += cssRules.size;
|
|
@@ -439,10 +433,9 @@ class ProcessorHelperCommon {
|
|
|
439
433
|
}
|
|
440
434
|
}
|
|
441
435
|
}
|
|
442
|
-
sheetIndex++;
|
|
443
436
|
});
|
|
444
437
|
processFontDetails(fontsDetails, fonts);
|
|
445
|
-
this.markRepeatedFontFaceRules(fontsDetails);
|
|
438
|
+
this.markRepeatedFontFaceRules(fontsDetails, ruleScopes, new Map());
|
|
446
439
|
await Promise.all([...stylesheets].map(async ([, stylesheetInfo], sheetIndex) => {
|
|
447
440
|
if (stylesheetInfo.stylesheet) {
|
|
448
441
|
const cssRules = stylesheetInfo.stylesheet.children;
|
|
@@ -460,19 +453,23 @@ class ProcessorHelperCommon {
|
|
|
460
453
|
return stats;
|
|
461
454
|
}
|
|
462
455
|
|
|
463
|
-
markRepeatedFontFaceRules(fontsDetails) {
|
|
456
|
+
markRepeatedFontFaceRules(fontsDetails, ruleScopes, scopeIds) {
|
|
464
457
|
const lastRules = new Map();
|
|
465
458
|
fontsDetails.fonts.forEach((fontInfo, ruleData) => {
|
|
466
|
-
const
|
|
459
|
+
const scope = ruleScopes.get(ruleData);
|
|
460
|
+
if (scope && !scopeIds.has(scope)) {
|
|
461
|
+
scopeIds.set(scope, scopeIds.size + 1);
|
|
462
|
+
}
|
|
463
|
+
const ruleKey = (scope ? scopeIds.get(scope) : 0) + " " + this.getFontKey(ruleData) + " " + this.getPropertyValue(ruleData, "src");
|
|
467
464
|
const previousRuleData = lastRules.get(ruleKey);
|
|
468
465
|
if (previousRuleData) {
|
|
469
466
|
fontsDetails.repeatedFonts.add(previousRuleData);
|
|
470
467
|
}
|
|
471
468
|
lastRules.set(ruleKey, ruleData);
|
|
472
469
|
});
|
|
473
|
-
fontsDetails.medias.forEach(mediaFontsDetails => this.markRepeatedFontFaceRules(mediaFontsDetails));
|
|
474
|
-
fontsDetails.supports.forEach(supportsFontsDetails => this.markRepeatedFontFaceRules(supportsFontsDetails));
|
|
475
|
-
fontsDetails.layers.forEach(layerFontsDetails => this.markRepeatedFontFaceRules(layerFontsDetails));
|
|
470
|
+
fontsDetails.medias.forEach(mediaFontsDetails => this.markRepeatedFontFaceRules(mediaFontsDetails, ruleScopes, scopeIds));
|
|
471
|
+
fontsDetails.supports.forEach(supportsFontsDetails => this.markRepeatedFontFaceRules(supportsFontsDetails, ruleScopes, scopeIds));
|
|
472
|
+
fontsDetails.layers.forEach(layerFontsDetails => this.markRepeatedFontFaceRules(layerFontsDetails, ruleScopes, scopeIds));
|
|
476
473
|
}
|
|
477
474
|
|
|
478
475
|
async processFontFaceRules(cssRules, sheetIndex, fontsDetails, fonts, fontTests, stats) {
|
|
@@ -739,10 +736,6 @@ function getURL(urlFunction) {
|
|
|
739
736
|
return urlMatch && urlMatch[1];
|
|
740
737
|
}
|
|
741
738
|
|
|
742
|
-
function getFontStretch(stretch) {
|
|
743
|
-
return FONT_STRETCHES[stretch] || stretch;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
739
|
async function resizeImage(doc, dataURI, { imageReductionFactor, imageQuality }, element) {
|
|
747
740
|
if (dataURI) {
|
|
748
741
|
const contentType = dataURI.substring(5, dataURI.indexOf(";"));
|
|
@@ -582,9 +582,15 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
582
582
|
groupDuplicateFonts(stylesheets, fonts, options) {
|
|
583
583
|
if (options.usedFonts && options.usedFonts.length) {
|
|
584
584
|
const fontFaces = [];
|
|
585
|
+
const scopeIds = new Map();
|
|
585
586
|
stylesheets.forEach(stylesheetInfo => {
|
|
586
587
|
if (stylesheetInfo.stylesheet && stylesheetInfo.stylesheet.children) {
|
|
587
|
-
|
|
588
|
+
const scope = stylesheetInfo.scope || null;
|
|
589
|
+
if (!scopeIds.has(scope)) {
|
|
590
|
+
scopeIds.set(scope, scopeIds.size);
|
|
591
|
+
}
|
|
592
|
+
const mediaScope = stylesheetInfo.mediaText ? "|media " + stylesheetInfo.mediaText : "";
|
|
593
|
+
getFontFaces(stylesheetInfo.stylesheet.children, scopeIds.get(scope) + mediaScope, fontFaces);
|
|
588
594
|
}
|
|
589
595
|
});
|
|
590
596
|
const families = new Map();
|
|
@@ -216,7 +216,7 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
216
216
|
|
|
217
217
|
async resolveImportURLs(stylesheetInfo, baseURI, options, workStylesheet, resources, stylesheets, importedStyleSheets = new Set(), stylesheetReferrer) {
|
|
218
218
|
const stylesheet = stylesheetInfo.stylesheet;
|
|
219
|
-
const scoped = stylesheetInfo
|
|
219
|
+
const { scoped, scope } = stylesheetInfo;
|
|
220
220
|
this.resolveStylesheetURLs(stylesheet, baseURI, workStylesheet, stylesheetReferrer, options.stylesheetReferrers);
|
|
221
221
|
const imports = getImportFunctions(stylesheet);
|
|
222
222
|
await Promise.all(imports.map(async node => {
|
|
@@ -247,6 +247,7 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
247
247
|
}
|
|
248
248
|
const stylesheetInfo = {
|
|
249
249
|
scoped,
|
|
250
|
+
scope,
|
|
250
251
|
mediaText,
|
|
251
252
|
layerName,
|
|
252
253
|
supportsCondition
|
|
@@ -27,6 +27,8 @@ import {
|
|
|
27
27
|
normalizeFontFamily,
|
|
28
28
|
flatten,
|
|
29
29
|
getFontWeight,
|
|
30
|
+
getFontStretch,
|
|
31
|
+
getStylesheetsInCascadeOrder,
|
|
30
32
|
removeQuotes
|
|
31
33
|
} from "./../core/helper.js";
|
|
32
34
|
|
|
@@ -34,20 +36,39 @@ const helper = {
|
|
|
34
36
|
normalizeFontFamily,
|
|
35
37
|
flatten,
|
|
36
38
|
getFontWeight,
|
|
39
|
+
getFontStretch,
|
|
40
|
+
getStylesheetsInCascadeOrder,
|
|
37
41
|
removeQuotes
|
|
38
42
|
};
|
|
39
43
|
|
|
40
44
|
const REGEXP_COMMA = /\s*,\s*/;
|
|
41
45
|
const REGEXP_SPACES = /\s+/g;
|
|
42
|
-
const
|
|
46
|
+
const REGEXP_ANGLE = /^([-+]?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(deg|grad|rad|turn)?$/;
|
|
47
|
+
const ANGLE_UNITS = { deg: 1, grad: 0.9, rad: 180 / Math.PI, turn: 360 };
|
|
48
|
+
const NORMAL_FONT_STYLE = "normal";
|
|
49
|
+
const ITALIC_FONT_STYLE = "italic";
|
|
50
|
+
const OBLIQUE_FONT_STYLE = "oblique";
|
|
43
51
|
const DEFAULT_OBLIQUE_ANGLE = 14;
|
|
52
|
+
const NORMAL_FONT_STRETCH = 100;
|
|
53
|
+
const LOWER_WEIGHT_SEARCH_THRESHOLD = 400;
|
|
54
|
+
const UPPER_WEIGHT_SEARCH_THRESHOLD = 500;
|
|
55
|
+
const GECKO_REVERSE_STRETCH_DISTANCE = 1000;
|
|
56
|
+
const GECKO_REVERSE_STYLE_DISTANCE = 100;
|
|
57
|
+
const GECKO_NEGATE_STYLE_DISTANCE = 200;
|
|
58
|
+
const GECKO_BAD_STYLE_DISTANCE = 700;
|
|
59
|
+
const GECKO_REVERSE_WEIGHT_DISTANCE = 600;
|
|
60
|
+
const GECKO_LIGHTER_WEIGHT_DISTANCE = 100;
|
|
61
|
+
const FONT_MATCHING_ALGORITHMS = [
|
|
62
|
+
{ getStretchDistance: getWebKitStretchDistance, getStyleDistance: getWebKitStyleDistance, getWeightDistance: getWebKitWeightDistance },
|
|
63
|
+
{ getStretchDistance: getWebKitStretchDistance, getStyleDistance: getWebKitObliqueStyleDistance, getWeightDistance: getWebKitWeightDistance },
|
|
64
|
+
{ getStretchDistance: getGeckoStretchDistance, getStyleDistance: getGeckoStyleDistance, getWeightDistance: getGeckoWeightDistance }
|
|
65
|
+
];
|
|
44
66
|
const REGEXP_DASH = /-/;
|
|
45
67
|
const REGEXP_QUESTION_MARK = /\?/g;
|
|
46
68
|
const REGEXP_STARTS_U_PLUS = /^U\+/i;
|
|
47
69
|
const REGEXP_CUSTOM_PROPERTY = /var\(\s*(--[^\s,)]+)\s*(?:,[^)]*)?\)/g;
|
|
48
70
|
const REGEXP_CUSTOM_PROPERTY_FAMILY = /^var\(\s*(--[^\s,)]+)\s*(?:,(.*))?\)$/;
|
|
49
71
|
const REGEXP_CUSTOM_PROPERTY_NAME = /^--/;
|
|
50
|
-
const VALID_FONT_STYLES = [/^normal$/, /^italic$/, /^oblique$/, /^oblique\s+/];
|
|
51
72
|
const NON_GLYPH_CHAR_CODES = [9, 10, 12, 13];
|
|
52
73
|
const MAX_NON_GLYPH_CHAR_CODE = 13;
|
|
53
74
|
// a family name kept when the "font" shorthand cannot be read: it resolves to nothing, so it
|
|
@@ -74,7 +95,7 @@ function process(doc, stylesheets, styles, options) {
|
|
|
74
95
|
});
|
|
75
96
|
styles.forEach(declarations => getCustomProperties(declarations.children, customProperties));
|
|
76
97
|
options = Object.assign({}, options, { customProperties });
|
|
77
|
-
stylesheets.forEach(stylesheetInfo => {
|
|
98
|
+
helper.getStylesheetsInCascadeOrder(stylesheets).forEach(({ stylesheetInfo }) => {
|
|
78
99
|
if (stylesheetInfo.stylesheet) {
|
|
79
100
|
const cssRules = stylesheetInfo.stylesheet.children;
|
|
80
101
|
if (cssRules) {
|
|
@@ -103,7 +124,7 @@ function process(doc, stylesheets, styles, options) {
|
|
|
103
124
|
// DOMParser, and that document is never rendered, so it reports nothing and every face it
|
|
104
125
|
// declares would be dropped
|
|
105
126
|
const usedFontsUnknown = !options.usedFonts || !options.usedFonts.length;
|
|
106
|
-
let unusedFonts, filteredUsedFonts;
|
|
127
|
+
let unusedFonts, filteredUsedFonts, selectedFonts;
|
|
107
128
|
if (usedFontsUnknown) {
|
|
108
129
|
unusedFonts = [];
|
|
109
130
|
} else {
|
|
@@ -125,14 +146,15 @@ function process(doc, stylesheets, styles, options) {
|
|
|
125
146
|
keepDeclaredFontIfRendered(fontInfo.fontFamily, fontsInfo, filteredUsedFonts, options));
|
|
126
147
|
}
|
|
127
148
|
unusedFonts = fontsInfo.declared.filter(fontInfo => !filteredUsedFonts.has(fontInfo.fontFamily));
|
|
149
|
+
selectedFonts = getSelectedFonts(fontsInfo.declared, filteredUsedFonts);
|
|
128
150
|
}
|
|
129
|
-
const docChars = Array.from(new Set(docContent)).map(char => char.
|
|
151
|
+
const docChars = Array.from(new Set(docContent)).map(char => char.codePointAt(0)).sort((value1, value2) => value1 - value2);
|
|
130
152
|
const usedFontsCharacters = getUsedFontsCharacters(options);
|
|
131
153
|
stylesheets.forEach(stylesheetInfo => {
|
|
132
154
|
if (stylesheetInfo.stylesheet) {
|
|
133
155
|
const cssRules = stylesheetInfo.stylesheet.children;
|
|
134
156
|
if (cssRules) {
|
|
135
|
-
filterUnusedFonts(cssRules, fontsInfo.declared, unusedFonts,
|
|
157
|
+
filterUnusedFonts(cssRules, fontsInfo.declared, unusedFonts, selectedFonts, docChars, usedFontsCharacters);
|
|
136
158
|
stats.rules.discarded -= cssRules.size;
|
|
137
159
|
}
|
|
138
160
|
}
|
|
@@ -167,9 +189,10 @@ function getFontsInfo(cssRules, fontsInfo, options) {
|
|
|
167
189
|
const fontWeight = getDeclarationValue(ruleData.block.children, "font-weight") || "400";
|
|
168
190
|
const fontStyle = getDeclarationValue(ruleData.block.children, "font-style") || "normal";
|
|
169
191
|
const fontVariant = getDeclarationValue(ruleData.block.children, "font-variant") || "normal";
|
|
192
|
+
const fontStretch = getDeclarationValue(ruleData.block.children, "font-stretch") || "normal";
|
|
170
193
|
const unicodeRange = getDeclarationValue(ruleData.block.children, "unicode-range");
|
|
171
194
|
fontWeight.split(",").forEach(weightValue =>
|
|
172
|
-
fontsInfo.declared.push({ fontFamily, fontWeight: helper.getFontWeight(helper.removeQuotes(weightValue)), fontStyle, fontVariant, unicodeRange, ruleData }));
|
|
195
|
+
fontsInfo.declared.push({ fontFamily, fontWeight: helper.getFontWeight(helper.removeQuotes(weightValue)), fontStyle, fontVariant, fontStretch, unicodeRange, ruleData }));
|
|
173
196
|
}
|
|
174
197
|
}
|
|
175
198
|
}
|
|
@@ -296,14 +319,14 @@ function splitValues(value) {
|
|
|
296
319
|
return values;
|
|
297
320
|
}
|
|
298
321
|
|
|
299
|
-
function filterUnusedFonts(cssRules, declaredFonts, unusedFonts,
|
|
322
|
+
function filterUnusedFonts(cssRules, declaredFonts, unusedFonts, selectedFonts, docChars, usedFontsCharacters) {
|
|
300
323
|
const removedRules = [];
|
|
301
324
|
for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
|
|
302
325
|
const ruleData = cssRule.data;
|
|
303
326
|
if (ruleData.type == "Atrule" && ruleData.name == "import" && ruleData.prelude && ruleData.prelude.children && ruleData.prelude.children.head.data.importedChildren) {
|
|
304
|
-
filterUnusedFonts(ruleData.prelude.children.head.data.importedChildren, declaredFonts, unusedFonts,
|
|
327
|
+
filterUnusedFonts(ruleData.prelude.children.head.data.importedChildren, declaredFonts, unusedFonts, selectedFonts, docChars, usedFontsCharacters);
|
|
305
328
|
} else if (ruleData.type == "Atrule" && (ruleData.name == "media" || ruleData.name == "supports" || ruleData.name == "layer" || ruleData.name == "container") && ruleData.block && ruleData.block.children) {
|
|
306
|
-
filterUnusedFonts(ruleData.block.children, declaredFonts, unusedFonts,
|
|
329
|
+
filterUnusedFonts(ruleData.block.children, declaredFonts, unusedFonts, selectedFonts, docChars, usedFontsCharacters);
|
|
307
330
|
} else if (ruleData.type == "Atrule" && ruleData.name == "font-face") {
|
|
308
331
|
const fontFamily = helper.normalizeFontFamily(getDeclarationValue(ruleData.block.children, "font-family"));
|
|
309
332
|
if (fontFamily) {
|
|
@@ -312,8 +335,8 @@ function filterUnusedFonts(cssRules, declaredFonts, unusedFonts, filteredUsedFon
|
|
|
312
335
|
if (unusedFonts.find(fontInfo => fontInfo.fontFamily == fontFamily) ||
|
|
313
336
|
!testUnicodeRange(docChars, unicodeRange) ||
|
|
314
337
|
!testReachableUnicodeRange(docChars, unicodeRange, laterUnicodeRanges) ||
|
|
315
|
-
!testDrawnUnicodeRange(ruleData, fontFamily, unicodeRange, usedFontsCharacters) ||
|
|
316
|
-
!testUsedFont(ruleData, fontFamily,
|
|
338
|
+
!testDrawnUnicodeRange(ruleData, fontFamily, unicodeRange, usedFontsCharacters, selectedFonts) ||
|
|
339
|
+
!testUsedFont(ruleData, fontFamily, selectedFonts)) {
|
|
317
340
|
removedRules.push(cssRule);
|
|
318
341
|
}
|
|
319
342
|
}
|
|
@@ -346,7 +369,7 @@ function getUsedFontsCharacters(options) {
|
|
|
346
369
|
return usedFontsCharacters;
|
|
347
370
|
}
|
|
348
371
|
|
|
349
|
-
function testDrawnUnicodeRange(ruleData, familyName, unicodeRange, usedFontsCharacters) {
|
|
372
|
+
function testDrawnUnicodeRange(ruleData, familyName, unicodeRange, usedFontsCharacters, selectedFonts) {
|
|
350
373
|
if (!unicodeRange || !usedFontsCharacters || !usedFontsCharacters.size) {
|
|
351
374
|
return true;
|
|
352
375
|
}
|
|
@@ -354,11 +377,11 @@ function testDrawnUnicodeRange(ruleData, familyName, unicodeRange, usedFontsChar
|
|
|
354
377
|
if (!buckets || !buckets.length) {
|
|
355
378
|
return true;
|
|
356
379
|
}
|
|
357
|
-
const
|
|
358
|
-
if (!
|
|
380
|
+
const selection = selectedFonts && selectedFonts.get(familyName);
|
|
381
|
+
if (!selection || !selection.candidates.has(ruleData) || buckets.find(bucket => !selection.rulesByStyle.has(bucket.fontStyle))) {
|
|
359
382
|
return true;
|
|
360
383
|
}
|
|
361
|
-
const matchedBuckets = buckets.filter(bucket =>
|
|
384
|
+
const matchedBuckets = buckets.filter(bucket => selection.rulesByStyle.get(bucket.fontStyle).has(ruleData));
|
|
362
385
|
if (!matchedBuckets.length || matchedBuckets.find(bucket => bucket.unknown)) {
|
|
363
386
|
return true;
|
|
364
387
|
}
|
|
@@ -396,93 +419,242 @@ function testDrawnGlyph(drawnRange, range) {
|
|
|
396
419
|
return false;
|
|
397
420
|
}
|
|
398
421
|
|
|
399
|
-
function testUsedFont(ruleData, familyName,
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
.
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
test = testFontweight(fontWeight, usedFontWeights);
|
|
422
|
+
function testUsedFont(ruleData, familyName, selectedFonts) {
|
|
423
|
+
const selection = selectedFonts && selectedFonts.get(familyName);
|
|
424
|
+
return !selection || !selection.candidates.has(ruleData) || selection.rules.has(ruleData);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function getSelectedFonts(declaredFonts, filteredUsedFonts) {
|
|
428
|
+
const selectedFonts = new Map();
|
|
429
|
+
filteredUsedFonts.forEach((usedFonts, familyName) => {
|
|
430
|
+
const fonts = declaredFonts
|
|
431
|
+
.filter(fontInfo => fontInfo.fontFamily == familyName)
|
|
432
|
+
.map(fontInfo => ({
|
|
433
|
+
ruleData: fontInfo.ruleData,
|
|
434
|
+
weight: parseFontWeight(fontInfo.fontWeight),
|
|
435
|
+
style: parseFontStyle(fontInfo.fontStyle),
|
|
436
|
+
stretch: parseFontStretch(fontInfo.fontStretch)
|
|
437
|
+
}))
|
|
438
|
+
.filter(fontInfo => fontInfo.weight && fontInfo.style && fontInfo.stretch);
|
|
439
|
+
const selection = { candidates: new Set(fonts.map(fontInfo => fontInfo.ruleData)), rules: new Set(), rulesByStyle: new Map() };
|
|
440
|
+
usedFonts.forEach(([, fontWeight, fontStyle, , fontStretch]) => {
|
|
441
|
+
let styleRules = selection.rulesByStyle.get(fontStyle);
|
|
442
|
+
if (!styleRules) {
|
|
443
|
+
styleRules = new Set();
|
|
444
|
+
selection.rulesByStyle.set(fontStyle, styleRules);
|
|
423
445
|
}
|
|
446
|
+
FONT_MATCHING_ALGORITHMS.forEach(algorithm => selectFonts(fonts, fontWeight, fontStyle, fontStretch, algorithm).forEach(fontInfo => {
|
|
447
|
+
styleRules.add(fontInfo.ruleData);
|
|
448
|
+
selection.rules.add(fontInfo.ruleData);
|
|
449
|
+
}));
|
|
450
|
+
});
|
|
451
|
+
selectedFonts.set(familyName, selection);
|
|
452
|
+
});
|
|
453
|
+
return selectedFonts;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function selectFonts(fonts, fontWeight, fontStyle, fontStretch, algorithm) {
|
|
457
|
+
const stretchBounds = getBounds(fonts.map(fontInfo => fontInfo.stretch));
|
|
458
|
+
const styleBounds = getBounds(fonts.map(fontInfo => fontInfo.style.range));
|
|
459
|
+
const weightBounds = getBounds(fonts.map(fontInfo => fontInfo.weight));
|
|
460
|
+
const stretch = fontStretch && parseFontStretch(fontStretch);
|
|
461
|
+
if (fonts.length && stretch && stretch[0] == stretch[1]) {
|
|
462
|
+
fonts = filterFonts(fonts, fontInfo => fontInfo.stretch, fontInfo => algorithm.getStretchDistance(fontInfo.stretch, stretch[0], stretchBounds));
|
|
463
|
+
}
|
|
464
|
+
const style = fontStyle && parseFontStyle(fontStyle);
|
|
465
|
+
if (fonts.length && style && style.range[0] == style.range[1]) {
|
|
466
|
+
fonts = filterFonts(fonts, fontInfo => fontInfo.style.range, fontInfo => algorithm.getStyleDistance(fontInfo.style, style, styleBounds));
|
|
467
|
+
}
|
|
468
|
+
const weight = fontWeight && parseFontWeight(String(fontWeight));
|
|
469
|
+
if (fonts.length && weight && weight[0] == weight[1]) {
|
|
470
|
+
fonts = filterFonts(fonts, fontInfo => fontInfo.weight, fontInfo => algorithm.getWeightDistance(fontInfo.weight, weight[0], weightBounds));
|
|
471
|
+
}
|
|
472
|
+
return fonts;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function filterFonts(fonts, getRange, getDistance) {
|
|
476
|
+
const results = fonts.map(getDistance);
|
|
477
|
+
const distance = Math.min(...results.map(result => result.distance));
|
|
478
|
+
const values = results
|
|
479
|
+
.filter(result => result.distance == distance && result.value !== undefined)
|
|
480
|
+
.map(result => result.value);
|
|
481
|
+
return fonts.filter((fontInfo, index) => results[index].distance == distance || values.some(value => testRange(getRange(fontInfo), value)));
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function getBounds(ranges) {
|
|
485
|
+
return [Math.min(...ranges.map(([min]) => min)), Math.max(...ranges.map(([, max]) => max))];
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function getWebKitStretchDistance([min, max], stretch, [boundsMin, boundsMax]) {
|
|
489
|
+
if (testRange([min, max], stretch)) {
|
|
490
|
+
return { distance: 0, value: stretch };
|
|
491
|
+
} else if (stretch > NORMAL_FONT_STRETCH) {
|
|
492
|
+
return min > stretch ? { distance: min - stretch, value: min } : { distance: Math.max(stretch, boundsMax) - max, value: max };
|
|
493
|
+
} else {
|
|
494
|
+
return max < stretch ? { distance: stretch - max, value: max } : { distance: min - Math.min(stretch, boundsMin), value: min };
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function getWebKitStyleDistance({ range: [min, max] }, { range: [slope] }, [boundsMin, boundsMax]) {
|
|
499
|
+
if (testRange([min, max], slope)) {
|
|
500
|
+
return { distance: 0, value: slope };
|
|
501
|
+
} else if (slope >= DEFAULT_OBLIQUE_ANGLE) {
|
|
502
|
+
return min > slope ? { distance: min - slope, value: min } : { distance: Math.max(slope, boundsMax) - max, value: max };
|
|
503
|
+
} else if (slope >= 0) {
|
|
504
|
+
if (max >= 0 && max < slope) {
|
|
505
|
+
return { distance: slope - max, value: max };
|
|
424
506
|
} else {
|
|
425
|
-
|
|
507
|
+
return min > slope ? { distance: min, value: min } : { distance: Math.max(slope, boundsMax) - max, value: max };
|
|
508
|
+
}
|
|
509
|
+
} else if (slope > -DEFAULT_OBLIQUE_ANGLE) {
|
|
510
|
+
if (min > slope && min <= 0) {
|
|
511
|
+
return { distance: min - slope, value: min };
|
|
512
|
+
} else {
|
|
513
|
+
return max < slope ? { distance: -max, value: max } : { distance: min - Math.min(slope, boundsMin), value: min };
|
|
426
514
|
}
|
|
427
515
|
} else {
|
|
428
|
-
|
|
516
|
+
return max < slope ? { distance: slope - max, value: max } : { distance: min - Math.min(slope, boundsMin), value: min };
|
|
429
517
|
}
|
|
430
|
-
return test;
|
|
431
518
|
}
|
|
432
519
|
|
|
433
|
-
function
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (angles && otherAngles) {
|
|
437
|
-
return angles[0] <= otherAngles[1] && otherAngles[0] <= angles[1];
|
|
520
|
+
function getWebKitObliqueStyleDistance(fontStyle, style, bounds) {
|
|
521
|
+
if (fontStyle.italic && !style.italic) {
|
|
522
|
+
return { distance: Infinity };
|
|
438
523
|
} else {
|
|
439
|
-
return
|
|
524
|
+
return getWebKitStyleDistance(fontStyle, style, bounds);
|
|
440
525
|
}
|
|
441
526
|
}
|
|
442
527
|
|
|
443
|
-
function
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
return
|
|
528
|
+
function getWebKitWeightDistance([min, max], weight, [boundsMin, boundsMax]) {
|
|
529
|
+
if (testRange([min, max], weight)) {
|
|
530
|
+
return { distance: 0, value: weight };
|
|
531
|
+
} else if (weight >= LOWER_WEIGHT_SEARCH_THRESHOLD && weight <= UPPER_WEIGHT_SEARCH_THRESHOLD) {
|
|
532
|
+
if (min > weight && min <= UPPER_WEIGHT_SEARCH_THRESHOLD) {
|
|
533
|
+
return { distance: min - weight, value: min };
|
|
534
|
+
} else {
|
|
535
|
+
return max < weight ? { distance: UPPER_WEIGHT_SEARCH_THRESHOLD - max, value: max } : { distance: min - Math.min(weight, boundsMin), value: min };
|
|
536
|
+
}
|
|
537
|
+
} else if (weight < LOWER_WEIGHT_SEARCH_THRESHOLD) {
|
|
538
|
+
return max < weight ? { distance: weight - max, value: max } : { distance: min - Math.min(weight, boundsMin), value: min };
|
|
539
|
+
} else {
|
|
540
|
+
return min > weight ? { distance: min - weight, value: min } : { distance: Math.max(weight, boundsMax) - max, value: max };
|
|
454
541
|
}
|
|
455
542
|
}
|
|
456
543
|
|
|
457
|
-
function
|
|
458
|
-
|
|
544
|
+
function getGeckoStretchDistance([min, max], stretch) {
|
|
545
|
+
if (stretch < min) {
|
|
546
|
+
return { distance: min - stretch + (stretch > NORMAL_FONT_STRETCH ? 0 : GECKO_REVERSE_STRETCH_DISTANCE) };
|
|
547
|
+
} else if (stretch > max) {
|
|
548
|
+
return { distance: stretch - max + (stretch <= NORMAL_FONT_STRETCH ? 0 : GECKO_REVERSE_STRETCH_DISTANCE) };
|
|
549
|
+
} else {
|
|
550
|
+
return { distance: 0 };
|
|
551
|
+
}
|
|
459
552
|
}
|
|
460
553
|
|
|
461
|
-
function
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
554
|
+
function getGeckoStyleDistance({ italic, range: [min, max] }, style) {
|
|
555
|
+
const [angle] = style.range;
|
|
556
|
+
if (italic ? style.italic : !style.italic && (angle == min || angle == max)) {
|
|
557
|
+
return { distance: 0 };
|
|
558
|
+
} else if (!style.italic && angle == 0) {
|
|
559
|
+
if (italic) {
|
|
560
|
+
return { distance: GECKO_BAD_STYLE_DISTANCE };
|
|
561
|
+
} else if (min >= 0) {
|
|
562
|
+
return { distance: min };
|
|
563
|
+
} else {
|
|
564
|
+
return { distance: max >= 0 ? 0 : GECKO_NEGATE_STYLE_DISTANCE - max };
|
|
467
565
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
|
|
566
|
+
} else if (style.italic) {
|
|
567
|
+
if (min >= DEFAULT_OBLIQUE_ANGLE) {
|
|
568
|
+
return { distance: min - DEFAULT_OBLIQUE_ANGLE + 1 };
|
|
569
|
+
} else if (max >= DEFAULT_OBLIQUE_ANGLE) {
|
|
570
|
+
return { distance: 1 };
|
|
571
|
+
} else {
|
|
572
|
+
return { distance: (max > 0 ? GECKO_REVERSE_STYLE_DISTANCE : GECKO_REVERSE_STYLE_DISTANCE + GECKO_NEGATE_STYLE_DISTANCE) + DEFAULT_OBLIQUE_ANGLE - max };
|
|
573
|
+
}
|
|
574
|
+
} else if (italic) {
|
|
575
|
+
return { distance: GECKO_BAD_STYLE_DISTANCE };
|
|
576
|
+
} else if (angle >= DEFAULT_OBLIQUE_ANGLE || angle <= -DEFAULT_OBLIQUE_ANGLE) {
|
|
577
|
+
const sign = Math.sign(angle);
|
|
578
|
+
const [signedMin, signedMax] = sign > 0 ? [min, max] : [-max, -min];
|
|
579
|
+
const signedAngle = sign * angle;
|
|
580
|
+
if (signedMin >= signedAngle) {
|
|
581
|
+
return { distance: signedMin - signedAngle };
|
|
582
|
+
} else if (signedMax >= signedAngle) {
|
|
583
|
+
return { distance: 0 };
|
|
584
|
+
} else {
|
|
585
|
+
return { distance: (signedMax > 0 ? GECKO_REVERSE_STYLE_DISTANCE : GECKO_REVERSE_STYLE_DISTANCE + GECKO_NEGATE_STYLE_DISTANCE) + signedAngle - signedMax };
|
|
586
|
+
}
|
|
587
|
+
} else {
|
|
588
|
+
const sign = angle > 0 ? 1 : -1;
|
|
589
|
+
const [signedMin, signedMax] = sign > 0 ? [min, max] : [-max, -min];
|
|
590
|
+
const signedAngle = sign * angle;
|
|
591
|
+
if (signedMin > signedAngle) {
|
|
592
|
+
return { distance: GECKO_REVERSE_STYLE_DISTANCE + signedMin - signedAngle };
|
|
593
|
+
} else if (signedMax >= signedAngle) {
|
|
594
|
+
return { distance: 0 };
|
|
595
|
+
} else {
|
|
596
|
+
return { distance: (signedMax > 0 ? 0 : GECKO_REVERSE_STYLE_DISTANCE + GECKO_NEGATE_STYLE_DISTANCE) + signedAngle - signedMax };
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function getGeckoWeightDistance([min, max], weight) {
|
|
602
|
+
if (testRange([min, max], weight)) {
|
|
603
|
+
return { distance: 0 };
|
|
604
|
+
} else if (weight < LOWER_WEIGHT_SEARCH_THRESHOLD) {
|
|
605
|
+
return { distance: max < weight ? weight - max : min - weight + GECKO_REVERSE_WEIGHT_DISTANCE };
|
|
606
|
+
} else if (weight > UPPER_WEIGHT_SEARCH_THRESHOLD) {
|
|
607
|
+
return { distance: min > weight ? min - weight : weight - max + GECKO_REVERSE_WEIGHT_DISTANCE };
|
|
608
|
+
} else if (min > weight) {
|
|
609
|
+
return { distance: min - weight + (min <= UPPER_WEIGHT_SEARCH_THRESHOLD ? 0 : GECKO_REVERSE_WEIGHT_DISTANCE) };
|
|
610
|
+
} else {
|
|
611
|
+
return { distance: weight - max + GECKO_LIGHTER_WEIGHT_DISTANCE };
|
|
475
612
|
}
|
|
476
|
-
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function testRange([min, max], value) {
|
|
616
|
+
return min <= value && value <= max;
|
|
477
617
|
}
|
|
478
618
|
|
|
479
619
|
function parseFontWeight(fontWeight) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
620
|
+
return parseRange(fontWeight, value => Number(helper.getFontWeight(value)));
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function parseFontStretch(fontStretch) {
|
|
624
|
+
return parseRange(fontStretch, value => {
|
|
625
|
+
value = helper.getFontStretch(value.toLowerCase());
|
|
626
|
+
return value.endsWith("%") ? Number(value.slice(0, -1)) : NaN;
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function parseFontStyle(fontStyle) {
|
|
631
|
+
const values = String(fontStyle).trim().toLowerCase().split(REGEXP_SPACES);
|
|
632
|
+
if (values.length == 1 && values[0] == NORMAL_FONT_STYLE) {
|
|
633
|
+
return { italic: false, range: [0, 0] };
|
|
634
|
+
} else if (values.length == 1 && values[0] == ITALIC_FONT_STYLE) {
|
|
635
|
+
return { italic: true, range: [DEFAULT_OBLIQUE_ANGLE, DEFAULT_OBLIQUE_ANGLE] };
|
|
636
|
+
} else if (values[0] == OBLIQUE_FONT_STYLE) {
|
|
637
|
+
const range = values.length == 1 ? [DEFAULT_OBLIQUE_ANGLE, DEFAULT_OBLIQUE_ANGLE] : parseRange(values.slice(1).join(" "), parseAngle);
|
|
638
|
+
if (range) {
|
|
639
|
+
return { italic: false, range };
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function parseAngle(angle) {
|
|
645
|
+
const match = angle.match(REGEXP_ANGLE);
|
|
646
|
+
if (match && (match[2] || Number(match[1]) == 0)) {
|
|
647
|
+
return Number(match[1]) * ANGLE_UNITS[match[2] || "deg"];
|
|
648
|
+
} else {
|
|
649
|
+
return NaN;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function parseRange(value, parseValue) {
|
|
654
|
+
const values = String(value).trim().split(REGEXP_SPACES).map(parseValue);
|
|
655
|
+
if (values.length && values.length <= 2 && values.every(Number.isFinite)) {
|
|
656
|
+
return [Math.min(...values), Math.max(...values)];
|
|
657
|
+
}
|
|
486
658
|
}
|
|
487
659
|
|
|
488
660
|
function getDeclarationValue(declarations, propertyName) {
|
|
@@ -595,51 +767,6 @@ function parseFamilyNames(fontFamilyNameTokenData, fontFamilyNames) {
|
|
|
595
767
|
}
|
|
596
768
|
}
|
|
597
769
|
|
|
598
|
-
function getUsedFontWeight(fontInfo, fontStyle, fontWeights) {
|
|
599
|
-
let foundWeight;
|
|
600
|
-
fontWeights = fontWeights.map(weights => weights.map(value => String(Number.parseInt(value, 10))));
|
|
601
|
-
if (testFontStyle(fontInfo[2], fontStyle)) {
|
|
602
|
-
let fontWeight = Number(fontInfo[1]);
|
|
603
|
-
if (fontWeights.length > 1) {
|
|
604
|
-
if (fontWeight >= 400 && fontWeight <= 500) {
|
|
605
|
-
foundWeight = fontWeights.find(weights => weights[0] >= fontWeight && weights[0] <= 500);
|
|
606
|
-
if (!foundWeight) {
|
|
607
|
-
foundWeight = findDescendingFontWeight(fontWeight, fontWeights);
|
|
608
|
-
}
|
|
609
|
-
if (!foundWeight) {
|
|
610
|
-
foundWeight = findAscendingFontWeight(fontWeight, fontWeights);
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
if (fontWeight < 400) {
|
|
614
|
-
foundWeight = fontWeights.slice().reverse().find(weights => weights[weights.length - 1] <= fontWeight);
|
|
615
|
-
if (!foundWeight) {
|
|
616
|
-
foundWeight = findAscendingFontWeight(fontWeight, fontWeights);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
if (fontWeight > 500) {
|
|
620
|
-
foundWeight = fontWeights.find(weights => weights[0] >= fontWeight);
|
|
621
|
-
if (!foundWeight) {
|
|
622
|
-
foundWeight = findDescendingFontWeight(fontWeight, fontWeights);
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
if (!foundWeight) {
|
|
626
|
-
foundWeight = fontWeights.find(weights => weights[0] <= fontWeight && weights[weights.length - 1] >= fontWeight);
|
|
627
|
-
}
|
|
628
|
-
} else {
|
|
629
|
-
foundWeight = fontWeights[0];
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
return foundWeight ? foundWeight.join(" ") : undefined;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
function findDescendingFontWeight(fontWeight, fontWeights) {
|
|
636
|
-
return fontWeights.slice().reverse().find(weights => weights[weights.length - 1] < fontWeight);
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
function findAscendingFontWeight(fontWeight, fontWeights) {
|
|
640
|
-
return fontWeights.find(weights => weights[0] > fontWeight);
|
|
641
|
-
}
|
|
642
|
-
|
|
643
770
|
function getRulesTextContent(doc, cssRules, workStylesheet, content) {
|
|
644
771
|
cssRules.forEach(ruleData => {
|
|
645
772
|
if (ruleData.block && ruleData.block.children && ruleData.prelude && ruleData.prelude.children) {
|
|
@@ -710,13 +837,14 @@ function getLaterUnicodeRanges(declaredFonts, ruleData) {
|
|
|
710
837
|
if (index == -1) {
|
|
711
838
|
return [];
|
|
712
839
|
}
|
|
713
|
-
const { fontFamily, fontWeight, fontStyle } = declaredFonts[index];
|
|
840
|
+
const { fontFamily, fontWeight, fontStyle, fontStretch } = declaredFonts[index];
|
|
714
841
|
return declaredFonts
|
|
715
842
|
.slice(index + 1)
|
|
716
843
|
.filter(fontInfo => fontInfo.ruleData != ruleData &&
|
|
717
844
|
fontInfo.fontFamily == fontFamily &&
|
|
718
845
|
fontInfo.fontWeight == fontWeight &&
|
|
719
|
-
fontInfo.fontStyle == fontStyle
|
|
846
|
+
fontInfo.fontStyle == fontStyle &&
|
|
847
|
+
fontInfo.fontStretch == fontStretch)
|
|
720
848
|
.map(fontInfo => fontInfo.unicodeRange);
|
|
721
849
|
}
|
|
722
850
|
|
package/package.json
CHANGED
|
@@ -121,9 +121,22 @@ const FONT_DESCRIPTOR_KEYS = new Set([
|
|
|
121
121
|
"stretch"
|
|
122
122
|
]);
|
|
123
123
|
|
|
124
|
+
const ANGLE_UNITS = new Set([
|
|
125
|
+
"deg",
|
|
126
|
+
"grad",
|
|
127
|
+
"rad",
|
|
128
|
+
"turn"
|
|
129
|
+
]);
|
|
130
|
+
|
|
124
131
|
const OPERATOR_TYPE = "Operator";
|
|
125
132
|
const IDENTIFIER_TYPE = "Identifier";
|
|
133
|
+
const NUMBER_TYPE = "Number";
|
|
134
|
+
const DIMENSION_TYPE = "Dimension";
|
|
135
|
+
const FUNCTION_TYPE = "Function";
|
|
126
136
|
const NORMAL_KEYWORD = "normal";
|
|
137
|
+
const OBLIQUE_KEYWORD = "oblique";
|
|
138
|
+
const MIN_FONT_WEIGHT = 1;
|
|
139
|
+
const MAX_FONT_WEIGHT = 1000;
|
|
127
140
|
const LINE_HEIGHT_SEPARATOR = "/";
|
|
128
141
|
const FAMILY_SEPARATOR = ",";
|
|
129
142
|
|
|
@@ -142,7 +155,24 @@ function parse(value) {
|
|
|
142
155
|
if (GLOBAL_KEYWORDS.has(stringValueLower)) {
|
|
143
156
|
return { global: stringValue };
|
|
144
157
|
}
|
|
145
|
-
const
|
|
158
|
+
const fonts = [];
|
|
159
|
+
let parseError;
|
|
160
|
+
[true, false].forEach(descriptorsFirst => {
|
|
161
|
+
try {
|
|
162
|
+
fonts.push(parseFont(value.children, descriptorsFirst));
|
|
163
|
+
} catch (error) {
|
|
164
|
+
parseError = error;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
if (!fonts.length) {
|
|
168
|
+
throw parseError;
|
|
169
|
+
}
|
|
170
|
+
const [font] = fonts;
|
|
171
|
+
font.family = Array.from(new Set(fonts.flatMap(parsedFont => parsedFont.family)));
|
|
172
|
+
return font;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function parseFont(tokens, descriptorsFirst) {
|
|
146
176
|
const font = {
|
|
147
177
|
lineHeight: NORMAL_KEYWORD,
|
|
148
178
|
stretch: NORMAL_KEYWORD,
|
|
@@ -154,7 +184,8 @@ function parse(value) {
|
|
|
154
184
|
for (let tokenNode = tokens.head; tokenNode; tokenNode = tokenNode.next) {
|
|
155
185
|
const tokenRaw = tokenNode.data.name || tokenNode.data.value || cssTree.generate(tokenNode.data);
|
|
156
186
|
const token = tokenRaw.toLowerCase();
|
|
157
|
-
|
|
187
|
+
const keyword = tokenNode.data.type == IDENTIFIER_TYPE || tokenNode.data.type == NUMBER_TYPE;
|
|
188
|
+
if (keyword && token === NORMAL_KEYWORD) {
|
|
158
189
|
FONT_DESCRIPTOR_KEYS.forEach((prop) => {
|
|
159
190
|
if (!seen[prop]) {
|
|
160
191
|
font[prop] = tokenRaw;
|
|
@@ -162,28 +193,38 @@ function parse(value) {
|
|
|
162
193
|
});
|
|
163
194
|
continue;
|
|
164
195
|
}
|
|
165
|
-
if (FONT_WEIGHT_KEYWORDS.has(token)) {
|
|
196
|
+
if (keyword && FONT_WEIGHT_KEYWORDS.has(token)) {
|
|
166
197
|
if (!seen.weight) {
|
|
167
198
|
font.weight = tokenRaw;
|
|
168
199
|
seen.weight = true;
|
|
169
200
|
}
|
|
170
201
|
continue;
|
|
171
202
|
}
|
|
172
|
-
if (
|
|
203
|
+
if (descriptorsFirst && !seen.weight && ((tokenNode.data.type == NUMBER_TYPE && Number(token) >= MIN_FONT_WEIGHT && Number(token) <= MAX_FONT_WEIGHT) || tokenNode.data.type == FUNCTION_TYPE)) {
|
|
204
|
+
font.weight = tokenRaw;
|
|
205
|
+
seen.weight = true;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (keyword && FONT_STYLE_KEYWORDS.has(token)) {
|
|
173
209
|
if (!seen.style) {
|
|
174
210
|
font.style = tokenRaw;
|
|
175
211
|
seen.style = true;
|
|
212
|
+
const nextToken = tokenNode.next && tokenNode.next.data;
|
|
213
|
+
if (token == OBLIQUE_KEYWORD && nextToken && ((nextToken.type == DIMENSION_TYPE && ANGLE_UNITS.has(nextToken.unit.toLowerCase())) || (descriptorsFirst && nextToken.type == FUNCTION_TYPE))) {
|
|
214
|
+
font.style += " " + cssTree.generate(nextToken);
|
|
215
|
+
tokenNode = tokenNode.next;
|
|
216
|
+
}
|
|
176
217
|
}
|
|
177
218
|
continue;
|
|
178
219
|
}
|
|
179
|
-
if (FONT_VARIANT_KEYWORDS.has(token)) {
|
|
220
|
+
if (keyword && FONT_VARIANT_KEYWORDS.has(token)) {
|
|
180
221
|
if (!seen.variant) {
|
|
181
222
|
font.variant = tokenRaw;
|
|
182
223
|
seen.variant = true;
|
|
183
224
|
}
|
|
184
225
|
continue;
|
|
185
226
|
}
|
|
186
|
-
if (FONT_STRETCH_KEYWORDS.has(token)) {
|
|
227
|
+
if (keyword && FONT_STRETCH_KEYWORDS.has(token)) {
|
|
187
228
|
if (!seen.stretch) {
|
|
188
229
|
font.stretch = tokenRaw;
|
|
189
230
|
seen.stretch = true;
|