jsdom
Version:
A JavaScript implementation of many web standards
172 lines (153 loc) • 4.83 kB
JavaScript
"use strict";
// TODO: Support math keyword.
const cssValues = require("./css-values");
const FONT_SIZE_REGEXP = /^((?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(%|[a-z]+)$/;
const LENGTH_REGEXP = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(%|[a-z]*)$/;
// Absolute font size mapping table.
const absoluteFontSize = new Map([
["xx-small", { px: 9, ratio: 9 / 16 }],
["x-small", { px: 10, ratio: 5 / 8 }],
["small", { px: 13, ratio: 13 / 16 }],
["medium", { px: 16, ratio: 1 }],
["large", { px: 18, ratio: 9 / 8 }],
["x-large", { px: 24, ratio: 1.5 }],
["xx-large", { px: 32, ratio: 2 }],
["xxx-large", { px: 48, ratio: 3 }]
]);
// Ratio of relative font size to pixels.
const relativeFontSize = new Map([
["smaller", 1 / 1.2],
["larger", 1.2]
]);
// Ratio of absolute length to pixels.
const absoluteLength = new Map([
["cm", 96 / 2.54],
["mm", 96 / 25.4],
["q", 96 / 101.6],
["in", 96],
["pc", 16],
["pt", 96 / 72],
["px", 1]
]);
// Ratio of root relative length to pixels.
const rootRelativeLength = new Map([
["rcap", 1],
["rch", 0.5],
["rem", 1],
["rex", 0.5],
["ric", 1],
["rlh", 1.2]
]);
// Ratio of relative length or percentage to pixels.
const relativeLength = new Map([
["%", 0.01],
["cap", 1],
["ch", 0.5],
["em", 1],
["ex", 0.5],
["ic", 1],
["lh", 1.2]
]);
function resolveFontSizeInPixels(elementImpl, size, root, parent) {
const isRelative = typeof parent === "number";
if (absoluteFontSize.has(size)) {
const pxSize = absoluteFontSize.get(size).px;
if (isRelative) {
return pxSize * parent;
}
return pxSize;
} else if (isRelative && relativeFontSize.has(size)) {
return relativeFontSize.get(size) * parent;
}
const match = FONT_SIZE_REGEXP.exec(size);
if (match) {
const [, value, unit] = match;
if (absoluteLength.has(unit)) {
return value * absoluteLength.get(unit);
} else if (rootRelativeLength.has(unit)) {
const pxSize = root ?? absoluteFontSize.get("medium").px;
return value * rootRelativeLength.get(unit) * pxSize;
} else if (relativeLength.has(unit)) {
if (isRelative) {
return value * relativeLength.get(unit) * parent;
}
const pxSize = root ?? absoluteFontSize.get("medium").px;
return value * relativeLength.get(unit) * pxSize;
}
}
return Number.NaN;
}
function resolveLengthInPixels(elementImpl, size, dimension, isFontSize) {
const { em, rem, vh, vw } = dimension;
if (absoluteFontSize.has(size)) {
return absoluteFontSize.get(size).px;
} else if (relativeFontSize.has(size)) {
return relativeFontSize.get(size) * em;
} else if (cssValues.hasCalcFunc(size)) {
let resolvedSize;
if (elementImpl === elementImpl._ownerDocument.documentElement) {
resolvedSize = cssValues.resolveCalc(size, {
dimension: {
em: absoluteFontSize.get("medium").px,
rem: absoluteFontSize.get("medium").px,
vh: elementImpl._globalObject.innerHeight / 100,
vw: elementImpl._globalObject.innerWidth / 100
},
format: "computedValue"
});
} else {
resolvedSize = cssValues.resolveCalc(size, {
dimension,
format: "computedValue"
});
}
const matchLength = LENGTH_REGEXP.exec(resolvedSize);
if (matchLength) {
const [, value] = matchLength;
return Number(value);
}
// Return as-is as a fallback.
return size;
}
const match = LENGTH_REGEXP.exec(size);
if (match) {
const [, value, unit] = match;
// Percentage value resolution varies depending on the property.
// Therefore, except for font-size, the values are returned as-is without attempting to resolve them.
if (unit === "%" && !isFontSize) {
return size;
} else if (absoluteLength.has(unit)) {
return value * absoluteLength.get(unit);
} else if (rootRelativeLength.has(unit)) {
const pxSize = rem ?? absoluteFontSize.get("medium").px;
return value * rootRelativeLength.get(unit) * pxSize;
} else if (relativeLength.has(unit)) {
const pxSize = em ?? absoluteFontSize.get("medium").px;
return value * relativeLength.get(unit) * pxSize;
}
switch (unit) {
case "vb": {
return value * vh;
}
case "vi": {
return value * vw;
}
case "vmax": {
return value * Math.max(vh, vw);
}
case "vmin": {
return value * Math.min(vh, vw);
}
default: {
if (Object.hasOwn(dimension, unit)) {
return value * dimension[unit];
}
}
}
}
// Return as-is as a fallback.
return size;
}
exports.absoluteFontSize = absoluteFontSize;
exports.resolveFontSizeInPixels = resolveFontSizeInPixels;
exports.resolveLengthInPixels = resolveLengthInPixels;