font.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. "use strict";
  2. const parsers = require("../parsers");
  3. const fontStyle = require("./fontStyle");
  4. const fontVariant = require("./fontVariant");
  5. const fontWeight = require("./fontWeight");
  6. const fontSize = require("./fontSize");
  7. const lineHeight = require("./lineHeight");
  8. const fontFamily = require("./fontFamily");
  9. const shorthandFor = new Map([
  10. ["font-style", fontStyle],
  11. ["font-variant", fontVariant],
  12. ["font-weight", fontWeight],
  13. ["font-size", fontSize],
  14. ["line-height", lineHeight],
  15. ["font-family", fontFamily]
  16. ]);
  17. module.exports.parse = function parse(v) {
  18. const keywords = ["caption", "icon", "menu", "message-box", "small-caption", "status-bar"];
  19. const key = parsers.parseKeyword(v, keywords);
  20. if (key) {
  21. return key;
  22. }
  23. const [fontBlock, ...families] = parsers.splitValue(v, {
  24. delimiter: ","
  25. });
  26. const [fontBlockA, fontBlockB] = parsers.splitValue(fontBlock, {
  27. delimiter: "/"
  28. });
  29. const font = {
  30. "font-style": "normal",
  31. "font-variant": "normal",
  32. "font-weight": "normal"
  33. };
  34. const fontFamilies = new Set();
  35. if (fontBlockB) {
  36. const [lineB, ...familiesB] = fontBlockB.trim().split(" ");
  37. if (!lineB || !lineHeight.isValid(lineB) || !familiesB.length) {
  38. return;
  39. }
  40. const lineHeightB = lineHeight.parse(lineB);
  41. const familyB = familiesB.join(" ");
  42. if (fontFamily.isValid(familyB)) {
  43. fontFamilies.add(fontFamily.parse(familyB));
  44. } else {
  45. return;
  46. }
  47. const parts = parsers.splitValue(fontBlockA.trim());
  48. const properties = ["font-style", "font-variant", "font-weight", "font-size"];
  49. for (const part of parts) {
  50. if (part === "normal") {
  51. continue;
  52. } else {
  53. for (const property of properties) {
  54. switch (property) {
  55. case "font-style":
  56. case "font-variant":
  57. case "font-weight":
  58. case "font-size": {
  59. const value = shorthandFor.get(property);
  60. if (value.isValid(part)) {
  61. font[property] = value.parse(part);
  62. }
  63. break;
  64. }
  65. default:
  66. }
  67. }
  68. }
  69. }
  70. if (Object.hasOwn(font, "font-size")) {
  71. font["line-height"] = lineHeightB;
  72. } else {
  73. return;
  74. }
  75. } else {
  76. // FIXME: Switch to toReversed() when we can drop Node.js 18 support.
  77. const revParts = [...parsers.splitValue(fontBlockA.trim())].reverse();
  78. const revFontFamily = [];
  79. const properties = ["font-style", "font-variant", "font-weight", "line-height"];
  80. font["font-style"] = "normal";
  81. font["font-variant"] = "normal";
  82. font["font-weight"] = "normal";
  83. font["line-height"] = "normal";
  84. let fontSizeA;
  85. for (const part of revParts) {
  86. if (fontSizeA) {
  87. if (part === "normal") {
  88. continue;
  89. } else {
  90. for (const property of properties) {
  91. switch (property) {
  92. case "font-style":
  93. case "font-variant":
  94. case "font-weight":
  95. case "line-height": {
  96. const value = shorthandFor.get(property);
  97. if (value.isValid(part)) {
  98. font[property] = value.parse(part);
  99. }
  100. break;
  101. }
  102. default:
  103. }
  104. }
  105. }
  106. } else if (fontSize.isValid(part)) {
  107. fontSizeA = fontSize.parse(part);
  108. } else if (fontFamily.isValid(part)) {
  109. revFontFamily.push(part);
  110. } else {
  111. return;
  112. }
  113. }
  114. const family = revFontFamily.reverse().join(" ");
  115. if (fontSizeA && fontFamily.isValid(family)) {
  116. font["font-size"] = fontSizeA;
  117. fontFamilies.add(fontFamily.parse(family));
  118. } else {
  119. return;
  120. }
  121. }
  122. for (const family of families) {
  123. if (fontFamily.isValid(family)) {
  124. fontFamilies.add(fontFamily.parse(family));
  125. } else {
  126. return;
  127. }
  128. }
  129. font["font-family"] = [...fontFamilies].join(", ");
  130. return font;
  131. };
  132. module.exports.definition = {
  133. set(v) {
  134. v = parsers.prepareValue(v, this._global);
  135. if (v === "" || parsers.hasVarFunc(v)) {
  136. for (const [key] of shorthandFor) {
  137. this._setProperty(key, "");
  138. }
  139. this._setProperty("font", v);
  140. } else {
  141. const obj = module.exports.parse(v);
  142. if (!obj) {
  143. return;
  144. }
  145. const str = new Set();
  146. for (const [key] of shorthandFor) {
  147. const val = obj[key];
  148. if (typeof val === "string") {
  149. this._setProperty(key, val);
  150. if (val && val !== "normal" && !str.has(val)) {
  151. if (key === "line-height") {
  152. str.add(`/ ${val}`);
  153. } else {
  154. str.add(val);
  155. }
  156. }
  157. }
  158. }
  159. this._setProperty("font", [...str].join(" "));
  160. }
  161. },
  162. get() {
  163. const val = this.getPropertyValue("font");
  164. if (parsers.hasVarFunc(val)) {
  165. return val;
  166. }
  167. const str = new Set();
  168. for (const [key] of shorthandFor) {
  169. const v = this.getPropertyValue(key);
  170. if (parsers.hasVarFunc(v)) {
  171. return "";
  172. }
  173. if (v && v !== "normal" && !str.has(v)) {
  174. if (key === "line-height") {
  175. str.add(`/ ${v}`);
  176. } else {
  177. str.add(`${v}`);
  178. }
  179. }
  180. }
  181. return [...str].join(" ");
  182. },
  183. enumerable: true,
  184. configurable: true
  185. };