Skip to content

Commit 3d2a2c4

Browse files
committed
Fix font shorthand
1 parent 55241d6 commit 3d2a2c4

File tree

2 files changed

+263
-32
lines changed

2 files changed

+263
-32
lines changed

lib/properties/font.js

Lines changed: 100 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,38 +26,109 @@ module.exports.parse = function parse(v) {
2626
const [fontBlock, ...families] = parsers.splitValue(v, {
2727
delimiter: ","
2828
});
29-
let blockA, blockB;
30-
if (fontBlock.includes("/")) {
31-
[blockA, blockB] = parsers.splitValue(fontBlock, {
32-
delimiter: "/"
33-
});
34-
} else {
35-
blockA = fontBlock.trim();
36-
}
37-
const obj = parsers.parseShorthand(blockA, shorthandFor, true);
38-
if (!obj) {
39-
return;
40-
}
41-
const font = {};
29+
const [fontBlockA, fontBlockB] = parsers.splitValue(fontBlock, {
30+
delimiter: "/"
31+
});
32+
const font = {
33+
"font-style": "normal",
34+
"font-variant": "normal",
35+
"font-weight": "normal"
36+
};
4237
const fontFamilies = new Set();
43-
for (const [property, value] of Object.entries(obj)) {
44-
if (property === "font-family") {
45-
if (!blockB) {
46-
fontFamilies.add(value);
47-
}
38+
if (fontBlockB) {
39+
const [lineB, ...familiesB] = fontBlockB.trim().split(" ");
40+
if (!lineB || !lineHeight.isValid(lineB) || !familiesB.length) {
41+
return;
42+
}
43+
const lineHeightB = lineHeight.parse(lineB);
44+
const familyB = familiesB.join(" ");
45+
if (fontFamily.isValid(familyB)) {
46+
fontFamilies.add(fontFamily.parse(familyB));
4847
} else {
49-
font[property] = value;
48+
return;
5049
}
51-
}
52-
// blockB, if matched, includes line-height and first font-family
53-
if (blockB) {
54-
const [lineheight, family] = parsers.splitValue(blockB);
55-
if (lineHeight.isValid(lineheight)) {
56-
font["line-height"] = lineHeight.parse(lineheight);
50+
const parts = parsers.splitValue(fontBlockA.trim());
51+
const properties = ["font-style", "font-variant", "font-weight", "font-size"];
52+
for (const part of parts) {
53+
if (part === "normal") {
54+
continue;
55+
} else {
56+
for (const property of properties) {
57+
switch (property) {
58+
case "font-style":
59+
case "font-variant":
60+
case "font-weight": {
61+
const value = shorthandFor.get(property);
62+
if (value.isValid(part)) {
63+
font[property] = value.parse(part);
64+
}
65+
break;
66+
}
67+
case "font-size": {
68+
const value = shorthandFor.get(property);
69+
if (value.isValid(part)) {
70+
font[property] = value.parse(part);
71+
}
72+
break;
73+
}
74+
default:
75+
}
76+
}
77+
}
78+
}
79+
if (Object.hasOwn(font, "font-size")) {
80+
font["line-height"] = lineHeightB;
5781
} else {
5882
return;
5983
}
60-
if (fontFamily.isValid(family)) {
84+
} else {
85+
// FIXME: Switch to toReversed() when we can drop Node.js 18 support.
86+
const revParts = [...parsers.splitValue(fontBlockA.trim())].reverse();
87+
const revFontFamily = [];
88+
const properties = ["font-style", "font-variant", "font-weight", "line-height"];
89+
font["font-style"] = "normal";
90+
font["font-variant"] = "normal";
91+
font["font-weight"] = "normal";
92+
font["line-height"] = "normal";
93+
let fontSizeA;
94+
for (const part of revParts) {
95+
if (fontSizeA) {
96+
if (part === "normal") {
97+
continue;
98+
} else {
99+
for (const property of properties) {
100+
switch (property) {
101+
case "font-style":
102+
case "font-variant":
103+
case "font-weight": {
104+
const value = shorthandFor.get(property);
105+
if (value.isValid(part)) {
106+
font[property] = value.parse(part);
107+
}
108+
break;
109+
}
110+
case "line-height": {
111+
const value = shorthandFor.get(property);
112+
if (value.isValid(part)) {
113+
font[property] = value.parse(part);
114+
}
115+
break;
116+
}
117+
default:
118+
}
119+
}
120+
}
121+
} else if (fontSize.isValid(part)) {
122+
fontSizeA = fontSize.parse(part);
123+
} else if (fontFamily.isValid(part)) {
124+
revFontFamily.push(part);
125+
} else {
126+
return;
127+
}
128+
}
129+
const family = revFontFamily.reverse().join(" ");
130+
if (fontSizeA && fontFamily.isValid(family)) {
131+
font["font-size"] = fontSizeA;
61132
fontFamilies.add(fontFamily.parse(family));
62133
} else {
63134
return;
@@ -77,7 +148,7 @@ module.exports.parse = function parse(v) {
77148
module.exports.definition = {
78149
set(v) {
79150
v = parsers.prepareValue(v, this._global);
80-
if (parsers.hasVarFunc(v)) {
151+
if (v === "" || parsers.hasVarFunc(v)) {
81152
for (const [key] of shorthandFor) {
82153
this._setProperty(key, "");
83154
}
@@ -92,7 +163,7 @@ module.exports.definition = {
92163
const val = obj[key];
93164
if (typeof val === "string") {
94165
this._setProperty(key, val);
95-
if (val && !str.has(val)) {
166+
if (val && val !== "normal" && !str.has(val)) {
96167
if (key === "line-height") {
97168
str.add(`/ ${val}`);
98169
} else {
@@ -115,7 +186,7 @@ module.exports.definition = {
115186
if (parsers.hasVarFunc(v)) {
116187
return "";
117188
}
118-
if (v && !str.has(v)) {
189+
if (v && v !== "normal" && !str.has(v)) {
119190
if (key === "line-height") {
120191
str.add(`/ ${v}`);
121192
} else {

test/properties.test.js

Lines changed: 163 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,7 +1399,7 @@ describe("font", () => {
13991399
testImplicitPropertyValue(
14001400
"font",
14011401
'normal medium Times, "Times New Roman", Georgia, serif',
1402-
'normal medium Times, "Times New Roman", Georgia, serif',
1402+
'medium Times, "Times New Roman", Georgia, serif',
14031403
new Map([
14041404
["font-style", "normal"],
14051405
["font-variant", "normal"],
@@ -1411,14 +1411,174 @@ describe("font", () => {
14111411
);
14121412
});
14131413

1414+
it("font shorthand should set / get values", () => {
1415+
testImplicitPropertyValue(
1416+
"font",
1417+
"normal medium Gill Sans Extrabold, sans-serif",
1418+
"medium Gill Sans Extrabold, sans-serif",
1419+
new Map([
1420+
["font-style", "normal"],
1421+
["font-variant", "normal"],
1422+
["font-weight", "normal"],
1423+
["font-size", "medium"],
1424+
["line-height", "normal"],
1425+
["font-family", "Gill Sans Extrabold, sans-serif"]
1426+
])
1427+
);
1428+
});
1429+
1430+
it("font shorthand should set / get values", () => {
1431+
testImplicitPropertyValue(
1432+
"font",
1433+
'normal medium "Goudy Bookletter 1911", sans-serif',
1434+
'medium "Goudy Bookletter 1911", sans-serif',
1435+
new Map([
1436+
["font-style", "normal"],
1437+
["font-variant", "normal"],
1438+
["font-weight", "normal"],
1439+
["font-size", "medium"],
1440+
["line-height", "normal"],
1441+
["font-family", '"Goudy Bookletter 1911", sans-serif']
1442+
])
1443+
);
1444+
});
1445+
1446+
it("font shorthand should not set / get invalid values", () => {
1447+
testImplicitPropertyValue(
1448+
"font",
1449+
"normal medium Goudy Bookletter 1911, sans-serif",
1450+
"",
1451+
new Map([
1452+
["font-style", ""],
1453+
["font-variant", ""],
1454+
["font-weight", ""],
1455+
["font-size", ""],
1456+
["line-height", ""],
1457+
["font-family", ""]
1458+
])
1459+
);
1460+
});
1461+
1462+
it("font shorthand should not set / get invalid values", () => {
1463+
testImplicitPropertyValue(
1464+
"font",
1465+
"normal medium Red/Black, sans-serif",
1466+
"",
1467+
new Map([
1468+
["font-style", ""],
1469+
["font-variant", ""],
1470+
["font-weight", ""],
1471+
["font-size", ""],
1472+
["line-height", ""],
1473+
["font-family", ""]
1474+
])
1475+
);
1476+
});
1477+
1478+
it("font shorthand should not set / get invalid values", () => {
1479+
testImplicitPropertyValue(
1480+
"font",
1481+
'normal medium "Lucida" Grande, sans-serif',
1482+
"",
1483+
new Map([
1484+
["font-style", ""],
1485+
["font-variant", ""],
1486+
["font-weight", ""],
1487+
["font-size", ""],
1488+
["line-height", ""],
1489+
["font-family", ""]
1490+
])
1491+
);
1492+
});
1493+
1494+
it("font shorthand should not set / get invalid values", () => {
1495+
testImplicitPropertyValue(
1496+
"font",
1497+
'normal medium Lucida "Grande", sans-serif',
1498+
"",
1499+
new Map([
1500+
["font-style", ""],
1501+
["font-variant", ""],
1502+
["font-weight", ""],
1503+
["font-size", ""],
1504+
["line-height", ""],
1505+
["font-family", ""]
1506+
])
1507+
);
1508+
});
1509+
1510+
it("font shorthand should not set / get invalid values", () => {
1511+
testImplicitPropertyValue(
1512+
"font",
1513+
"normal medium Ahem!, sans-serif",
1514+
"",
1515+
new Map([
1516+
["font-style", ""],
1517+
["font-variant", ""],
1518+
["font-weight", ""],
1519+
["font-size", ""],
1520+
["line-height", ""],
1521+
["font-family", ""]
1522+
])
1523+
);
1524+
});
1525+
1526+
it("font shorthand should not set / get invalid values", () => {
1527+
testImplicitPropertyValue(
1528+
"font",
1529+
"normal medium test@foo, sans-serif",
1530+
"",
1531+
new Map([
1532+
["font-style", ""],
1533+
["font-variant", ""],
1534+
["font-weight", ""],
1535+
["font-size", ""],
1536+
["line-height", ""],
1537+
["font-family", ""]
1538+
])
1539+
);
1540+
});
1541+
1542+
it("font shorthand should not set / get invalid values", () => {
1543+
testImplicitPropertyValue(
1544+
"font",
1545+
"normal medium #POUND, sans-serif",
1546+
"",
1547+
new Map([
1548+
["font-style", ""],
1549+
["font-variant", ""],
1550+
["font-weight", ""],
1551+
["font-size", ""],
1552+
["line-height", ""],
1553+
["font-family", ""]
1554+
])
1555+
);
1556+
});
1557+
1558+
it("font shorthand should not set / get invalid values", () => {
1559+
testImplicitPropertyValue(
1560+
"font",
1561+
"normal medium Hawaii 5-0, sans-serif",
1562+
"",
1563+
new Map([
1564+
["font-style", ""],
1565+
["font-variant", ""],
1566+
["font-weight", ""],
1567+
["font-size", ""],
1568+
["line-height", ""],
1569+
["font-family", ""]
1570+
])
1571+
);
1572+
});
1573+
14141574
it("font shorthand should set / get values", () => {
14151575
testImplicitPropertyValue(
14161576
"font",
14171577
'italic bold medium/1.2 Times, "Times New Roman", Georgia, serif',
14181578
'italic bold medium / 1.2 Times, "Times New Roman", Georgia, serif',
14191579
new Map([
14201580
["font-style", "italic"],
1421-
["font-variant", ""],
1581+
["font-variant", "normal"],
14221582
["font-weight", "bold"],
14231583
["font-size", "medium"],
14241584
["line-height", "1.2"],
@@ -1434,7 +1594,7 @@ describe("font", () => {
14341594
'italic bold calc(1.5em) / 1.2 Times, "Times New Roman", Georgia, serif',
14351595
new Map([
14361596
["font-style", "italic"],
1437-
["font-variant", ""],
1597+
["font-variant", "normal"],
14381598
["font-weight", "bold"],
14391599
["font-size", "calc(1.5em)"],
14401600
["line-height", "1.2"],

0 commit comments

Comments
 (0)