subfont 6.10.0 → 6.12.1
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/CHANGELOG.md +21 -0
- package/lib/normalizeFontPropertyValue.js +3 -0
- package/lib/parseFontVariationSettings.js +1 -1
- package/lib/subsetFonts.js +157 -28
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
### v6.12.1 (2022-09-11)
|
|
2
|
+
|
|
3
|
+
- [Map font-style: oblique to font-variation-settings: slnt -14](https://github.com/Munter/subfont/commit/ea96284729e0d100fcb87d09c63979641439b169) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
4
|
+
- [Fix typo in test case](https://github.com/Munter/subfont/commit/b07273ff0daff38a23ef9b0ce77d5a114c4e2154) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
5
|
+
- [Improve slnt axis test case with a copy of the Extendomatic font Should be OK according to the license: https:\/\/djr.com\/license\/\#server https:\/\/twitter.com\/djrrb\/status\/1568539078416539654?s=20&t=P3w5SKI\_UHV3zzrBMj47lQ](https://github.com/Munter/subfont/commit/f6692fc3ad17a745bda13d1eb78ccd5fb340b369) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
6
|
+
|
|
7
|
+
### v6.12.0 (2022-09-07)
|
|
8
|
+
|
|
9
|
+
- [Refactor repeated code into helper function](https://github.com/Munter/subfont/commit/5335d931957cadc150fed1c5d548ee6b6e245636) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
10
|
+
- [Clamp the used variation axis values to the available interval](https://github.com/Munter/subfont/commit/1429c0f35720ee8dadb691138eb4cfa3c1c86150) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
11
|
+
- [Detech unused slnt variation axis ranges](https://github.com/Munter/subfont/commit/dc4be1598ba1ba0e65d8269d0fa2d25aa528357d) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
12
|
+
- [Report variation axes that only use the default value as unused rather than underutilized](https://github.com/Munter/subfont/commit/12082590898407f558d9540347778272ddb9d197) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
13
|
+
- [Detect unused wdth variation axis ranges](https://github.com/Munter/subfont/commit/d7758eb3c9a45a49768aac12c36ed897a0c4b467) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
14
|
+
|
|
15
|
+
### v6.11.0 (2022-09-04)
|
|
16
|
+
|
|
17
|
+
- [Treat wdth as a standard axis \(but unsupported for now\)](https://github.com/Munter/subfont/commit/20d539750448134cca0a82f2cc98f85d9a6be068) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
18
|
+
- [Detect unused ital variation axis ranges](https://github.com/Munter/subfont/commit/b848b21b66cdbcd0ff313690ba49a4bc9b9f90a4) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
19
|
+
- [Don't assume that the display name of a variation axis equals its name](https://github.com/Munter/subfont/commit/4fcf7df801de01b2d3aef3d5f1ea3d697779ffda) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
20
|
+
- [Detect unused wght variation axis ranges](https://github.com/Munter/subfont/commit/6a964d609f21bf2c9dad96da1d16d9c32c28dbfd) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
21
|
+
|
|
1
22
|
### v6.10.0 (2022-08-29)
|
|
2
23
|
|
|
3
24
|
- [Update @hookun\/parse-animation-shorthand to ^0.1.4 to make sure we get the fix for hookhookun\/parse-animation-shorthand\#16](https://github.com/Munter/subfont/commit/4db17826245e289e987c6dc02d3f67ebf5891e6f) ([Andreas Lind](mailto:andreas.lind@workday.com))
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const cssFontWeightNames = require('css-font-weight-names');
|
|
2
2
|
const initialValueByProp = require('./initialValueByProp');
|
|
3
3
|
const unquote = require('./unquote');
|
|
4
|
+
const normalizeFontStretch = require('font-snapper/lib/normalizeFontStretch');
|
|
4
5
|
|
|
5
6
|
function normalizeFontPropertyValue(propName, value) {
|
|
6
7
|
const propNameLowerCase = propName.toLowerCase();
|
|
@@ -22,6 +23,8 @@ function normalizeFontPropertyValue(propName, value) {
|
|
|
22
23
|
} else {
|
|
23
24
|
return value;
|
|
24
25
|
}
|
|
26
|
+
} else if (propNameLowerCase === 'font-stretch') {
|
|
27
|
+
return normalizeFontStretch(value);
|
|
25
28
|
} else if (typeof value === 'string' && propNameLowerCase !== 'src') {
|
|
26
29
|
return value.toLowerCase();
|
|
27
30
|
}
|
package/lib/subsetFonts.js
CHANGED
|
@@ -194,10 +194,29 @@ function groupTextsByFontFamilyProps(
|
|
|
194
194
|
const { relations, ...props } = activeFontFaceDeclaration;
|
|
195
195
|
const fontUrl = getPreferredFontUrl(relations);
|
|
196
196
|
|
|
197
|
+
const fontStyle = normalizeFontPropertyValue(
|
|
198
|
+
'font-style',
|
|
199
|
+
textAndProps.props['font-style']
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
let fontWeight = normalizeFontPropertyValue(
|
|
203
|
+
'font-weight',
|
|
204
|
+
textAndProps.props['font-weight']
|
|
205
|
+
);
|
|
206
|
+
if (fontWeight === 'normal') {
|
|
207
|
+
fontWeight = 400;
|
|
208
|
+
}
|
|
209
|
+
|
|
197
210
|
return {
|
|
198
211
|
htmlOrSvgAsset: textAndProps.htmlOrSvgAsset,
|
|
199
212
|
text: textAndProps.text,
|
|
200
213
|
fontVariationSettings: textAndProps.props['font-variation-settings'],
|
|
214
|
+
fontStyle,
|
|
215
|
+
fontWeight,
|
|
216
|
+
fontStretch: normalizeFontPropertyValue(
|
|
217
|
+
'font-stretch',
|
|
218
|
+
textAndProps.props['font-stretch']
|
|
219
|
+
),
|
|
201
220
|
animationTimingFunction:
|
|
202
221
|
textAndProps.props['animation-timing-function'],
|
|
203
222
|
props,
|
|
@@ -217,6 +236,11 @@ function groupTextsByFontFamilyProps(
|
|
|
217
236
|
const fontFamilies = new Set(
|
|
218
237
|
textsPropsArray.map((obj) => obj.props['font-family'])
|
|
219
238
|
);
|
|
239
|
+
const fontStyles = new Set(textsPropsArray.map((obj) => obj.fontStyle));
|
|
240
|
+
const fontWeights = new Set(textsPropsArray.map((obj) => obj.fontWeight));
|
|
241
|
+
const fontStretches = new Set(
|
|
242
|
+
textsPropsArray.map((obj) => obj.fontStretch)
|
|
243
|
+
);
|
|
220
244
|
const fontVariationSettings = new Set(
|
|
221
245
|
textsPropsArray
|
|
222
246
|
.map((obj) => obj.fontVariationSettings)
|
|
@@ -256,6 +280,9 @@ function groupTextsByFontFamilyProps(
|
|
|
256
280
|
props: { ...textsPropsArray[0].props },
|
|
257
281
|
fontUrl,
|
|
258
282
|
fontFamilies,
|
|
283
|
+
fontStyles,
|
|
284
|
+
fontStretches,
|
|
285
|
+
fontWeights,
|
|
259
286
|
fontVariationSettings,
|
|
260
287
|
hasOutOfBoundsAnimationTimingFunction,
|
|
261
288
|
preload,
|
|
@@ -618,6 +645,46 @@ function cssAssetIsEmpty(cssAsset) {
|
|
|
618
645
|
);
|
|
619
646
|
}
|
|
620
647
|
|
|
648
|
+
function parseFontWeightRange(str) {
|
|
649
|
+
if (typeof str === 'undefined' || str === 'auto') {
|
|
650
|
+
return [-Infinity, Infinity];
|
|
651
|
+
}
|
|
652
|
+
let minFontWeight = 400;
|
|
653
|
+
let maxFontWeight = 400;
|
|
654
|
+
const fontWeightTokens = str.split(/\s+/).map((str) => parseFloat(str));
|
|
655
|
+
if (
|
|
656
|
+
[1, 2].includes(fontWeightTokens.length) &&
|
|
657
|
+
!fontWeightTokens.some(isNaN)
|
|
658
|
+
) {
|
|
659
|
+
minFontWeight = maxFontWeight = fontWeightTokens[0];
|
|
660
|
+
if (fontWeightTokens.length === 2) {
|
|
661
|
+
maxFontWeight = fontWeightTokens[1];
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return [minFontWeight, maxFontWeight];
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function parseFontStretchRange(str) {
|
|
668
|
+
if (typeof str === 'undefined' || str.toLowerCase() === 'auto') {
|
|
669
|
+
return [-Infinity, Infinity];
|
|
670
|
+
}
|
|
671
|
+
let minFontStretch = 100;
|
|
672
|
+
let maxFontStretch = 100;
|
|
673
|
+
const fontStretchTokens = str
|
|
674
|
+
.split(/\s+/)
|
|
675
|
+
.map((str) => normalizeFontPropertyValue('font-stretch', str));
|
|
676
|
+
if (
|
|
677
|
+
[1, 2].includes(fontStretchTokens.length) &&
|
|
678
|
+
!fontStretchTokens.some(isNaN)
|
|
679
|
+
) {
|
|
680
|
+
minFontStretch = maxFontStretch = fontStretchTokens[0];
|
|
681
|
+
if (fontStretchTokens.length === 2) {
|
|
682
|
+
maxFontStretch = fontStretchTokens[1];
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return [minFontStretch, maxFontStretch];
|
|
686
|
+
}
|
|
687
|
+
|
|
621
688
|
function warnAboutMissingGlyphs(htmlOrSvgAssetTextsWithProps, assetGraph) {
|
|
622
689
|
const missingGlyphsErrors = [];
|
|
623
690
|
|
|
@@ -708,37 +775,95 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
708
775
|
}
|
|
709
776
|
}
|
|
710
777
|
|
|
711
|
-
const standardVariationAxes = new Set(['
|
|
778
|
+
const standardVariationAxes = new Set(['wght', 'wdth', 'ital', 'slnt', 'opsz']);
|
|
779
|
+
// It would be very hard to trace statically which values of opsz (font-optical-sizing)
|
|
780
|
+
// are going to be used, so we ignore that one:
|
|
781
|
+
const ignoredVariationAxes = new Set(['opsz']);
|
|
712
782
|
|
|
713
|
-
function
|
|
783
|
+
function renderNumberRange(min, max) {
|
|
784
|
+
if (min === max) {
|
|
785
|
+
return String(min);
|
|
786
|
+
} else {
|
|
787
|
+
return `${min}-${max}`;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function warnAboutUnusedVariationAxes(
|
|
714
792
|
htmlOrSvgAssetTextsWithProps,
|
|
715
793
|
assetGraph
|
|
716
794
|
) {
|
|
717
795
|
const seenAxisValuesByFontUrlAndAxisName = new Map();
|
|
718
796
|
const outOfBoundsAxesByFontUrl = new Map();
|
|
719
797
|
|
|
798
|
+
function noteUsedValue(fontUrl, axisName, axisValue) {
|
|
799
|
+
let seenAxes = seenAxisValuesByFontUrlAndAxisName.get(fontUrl);
|
|
800
|
+
if (!seenAxes) {
|
|
801
|
+
seenAxes = new Map();
|
|
802
|
+
seenAxisValuesByFontUrlAndAxisName.set(fontUrl, seenAxes);
|
|
803
|
+
}
|
|
804
|
+
if (seenAxes.has(axisName)) {
|
|
805
|
+
seenAxes.get(axisName).push(axisValue);
|
|
806
|
+
} else {
|
|
807
|
+
seenAxes.set(axisName, [axisValue]);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
720
811
|
for (const { fontUsages } of htmlOrSvgAssetTextsWithProps) {
|
|
721
812
|
for (const {
|
|
722
813
|
fontUrl,
|
|
814
|
+
fontStyles,
|
|
815
|
+
fontWeights,
|
|
816
|
+
fontStretches,
|
|
723
817
|
fontVariationSettings,
|
|
724
818
|
hasOutOfBoundsAnimationTimingFunction,
|
|
819
|
+
props,
|
|
725
820
|
} of fontUsages) {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
821
|
+
if (fontStyles.has('italic')) {
|
|
822
|
+
noteUsedValue(fontUrl, 'ital', 1);
|
|
823
|
+
}
|
|
824
|
+
// If any font-style value except italic is seen (including normal or oblique)
|
|
825
|
+
// we're also utilizing value 0:
|
|
826
|
+
if (fontStyles.size > fontStyles.has('italic') ? 1 : 0) {
|
|
827
|
+
noteUsedValue(fontUrl, 'ital', 0);
|
|
828
|
+
}
|
|
829
|
+
if (fontStyles.has('oblique')) {
|
|
830
|
+
// https://www.w3.org/TR/css-fonts-4/#font-style-prop
|
|
831
|
+
// oblique <angle>?
|
|
832
|
+
// [...] The lack of an <angle> represents 14deg.
|
|
833
|
+
// And also:
|
|
834
|
+
// Note: the OpenType slnt axis is defined with a positive angle meaning a counter-clockwise slant, the opposite direction to CSS.
|
|
835
|
+
// sThe CSS implementation will take this into account when using variations to produce oblique faces.
|
|
836
|
+
noteUsedValue(fontUrl, 'slnt', -14);
|
|
837
|
+
}
|
|
838
|
+
// If any font-style value except oblique is seen (including normal or italic)
|
|
839
|
+
// we're also utilizing value 0:
|
|
840
|
+
if (fontStyles.size > fontStyles.has('oblique') ? 1 : 0) {
|
|
841
|
+
noteUsedValue(fontUrl, 'slnt', 0);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
const minMaxFontWeight = parseFontWeightRange(props['font-weight']);
|
|
845
|
+
for (const fontWeight of fontWeights) {
|
|
846
|
+
noteUsedValue(
|
|
847
|
+
fontUrl,
|
|
848
|
+
'wght',
|
|
849
|
+
_.clamp(fontWeight, ...minMaxFontWeight)
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
const minMaxFontStretch = parseFontStretchRange(props['font-stretch']);
|
|
854
|
+
for (const fontStrech of fontStretches) {
|
|
855
|
+
noteUsedValue(
|
|
856
|
+
fontUrl,
|
|
857
|
+
'wdth',
|
|
858
|
+
_.clamp(fontStrech, ...minMaxFontStretch)
|
|
859
|
+
);
|
|
730
860
|
}
|
|
731
861
|
|
|
732
862
|
for (const fontVariationSettingsValue of fontVariationSettings) {
|
|
733
863
|
for (const [axisName, axisValue] of parseFontVariationSettings(
|
|
734
864
|
fontVariationSettingsValue
|
|
735
865
|
)) {
|
|
736
|
-
|
|
737
|
-
if (seenAxisValues) {
|
|
738
|
-
seenAxisValues.push(axisValue);
|
|
739
|
-
} else {
|
|
740
|
-
seenAxes.set(axisName, [axisValue]);
|
|
741
|
-
}
|
|
866
|
+
noteUsedValue(fontUrl, axisName, axisValue);
|
|
742
867
|
if (hasOutOfBoundsAnimationTimingFunction) {
|
|
743
868
|
let outOfBoundsAxes = outOfBoundsAxesByFontUrl.get(fontUrl);
|
|
744
869
|
if (!outOfBoundsAxes) {
|
|
@@ -767,26 +892,27 @@ function warnAboutUnusedCustomVariationAxes(
|
|
|
767
892
|
}
|
|
768
893
|
const unusedAxes = [];
|
|
769
894
|
const underutilizedAxes = [];
|
|
770
|
-
for (const
|
|
895
|
+
for (const [name, { min, max, default: defaultValue }] of Object.entries(
|
|
771
896
|
font.variationAxes
|
|
772
897
|
)) {
|
|
773
|
-
|
|
774
|
-
if (standardVariationAxes.has(axisName)) {
|
|
898
|
+
if (ignoredVariationAxes.has(name)) {
|
|
775
899
|
continue;
|
|
776
900
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
901
|
+
let usedValues = [];
|
|
902
|
+
if (seenAxisValuesByAxisName.has(name) && !outOfBoundsAxes.has(name)) {
|
|
903
|
+
usedValues = [...seenAxisValuesByAxisName.get(name)].map((usedValue) =>
|
|
904
|
+
_.clamp(usedValue, min, max)
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
if (!usedValues.every((value) => value === defaultValue)) {
|
|
908
|
+
if (!standardVariationAxes.has(name)) {
|
|
909
|
+
usedValues.push(defaultValue);
|
|
910
|
+
}
|
|
785
911
|
const minUsed = Math.min(...usedValues);
|
|
786
912
|
const maxUsed = Math.max(...usedValues);
|
|
787
913
|
if (minUsed > min || maxUsed < max) {
|
|
788
914
|
underutilizedAxes.push({
|
|
789
|
-
name
|
|
915
|
+
name,
|
|
790
916
|
minUsed,
|
|
791
917
|
maxUsed,
|
|
792
918
|
min,
|
|
@@ -794,7 +920,7 @@ function warnAboutUnusedCustomVariationAxes(
|
|
|
794
920
|
});
|
|
795
921
|
}
|
|
796
922
|
} else {
|
|
797
|
-
unusedAxes.push(
|
|
923
|
+
unusedAxes.push(name);
|
|
798
924
|
}
|
|
799
925
|
}
|
|
800
926
|
|
|
@@ -807,7 +933,10 @@ function warnAboutUnusedCustomVariationAxes(
|
|
|
807
933
|
message += ` Underutilized axes:\n${underutilizedAxes
|
|
808
934
|
.map(
|
|
809
935
|
({ name, min, max, minUsed, maxUsed }) =>
|
|
810
|
-
` ${name}: ${
|
|
936
|
+
` ${name}: ${renderNumberRange(
|
|
937
|
+
minUsed,
|
|
938
|
+
maxUsed
|
|
939
|
+
)} used (${min}-${max} available)`
|
|
811
940
|
)
|
|
812
941
|
.join('\n')}\n`;
|
|
813
942
|
}
|
|
@@ -817,7 +946,7 @@ function warnAboutUnusedCustomVariationAxes(
|
|
|
817
946
|
|
|
818
947
|
if (warnings.length > 0) {
|
|
819
948
|
assetGraph.info(
|
|
820
|
-
new Error(`🪓 Unused
|
|
949
|
+
new Error(`🪓 Unused variation axes detected in your variable fonts.
|
|
821
950
|
The below variable fonts contain custom axes that do not appear to be fully used on any of your pages.
|
|
822
951
|
This bloats your fonts and also the subset fonts that subfont creates.
|
|
823
952
|
Consider removing the unused axis ranges using a tool like Slice <https://slice-gui.netlify.app/>
|
|
@@ -1112,7 +1241,7 @@ async function subsetFonts(
|
|
|
1112
1241
|
);
|
|
1113
1242
|
|
|
1114
1243
|
warnAboutMissingGlyphs(htmlOrSvgAssetTextsWithProps, assetGraph);
|
|
1115
|
-
|
|
1244
|
+
warnAboutUnusedVariationAxes(htmlOrSvgAssetTextsWithProps, assetGraph);
|
|
1116
1245
|
|
|
1117
1246
|
// Insert subsets:
|
|
1118
1247
|
|