html2canvas-pro
Version:
Screenshots with JavaScript. Next generation!
190 lines • 7.35 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.clipPath = void 0;
const parser_1 = require("../syntax/parser");
const length_percentage_1 = require("../types/length-percentage");
const NONE = { type: 0 /* CLIP_PATH_TYPE.NONE */ };
/**
* Parse a shape-radius token: <length-percentage> | closest-side | farthest-side.
* Defaults to 'closest-side' when no tokens are provided.
*/
const parseShapeRadius = (tokens) => {
const [first] = tokens;
if (!first)
return 'closest-side';
if ((0, parser_1.isIdentToken)(first)) {
// Any unrecognised keyword (e.g. 'closest-corner') intentionally falls back to
// 'closest-side' as the CSS spec requires unknown values to be treated as invalid
// and the initial value of <shape-radius> is 'closest-side'.
return first.value === 'farthest-side' ? 'farthest-side' : 'closest-side';
}
return (0, length_percentage_1.isLengthPercentage)(first) ? first : 'closest-side';
};
/**
* Parse a CSS <position> as (cx, cy), each as a LengthPercentage.
*
* Supports the **1–2 value** subset of the CSS `<position>` syntax.
* The 4-value form (`at left 10px top 20px`) is not supported and will be
* parsed on a best-effort basis.
*
* Axis assignment rules:
* - `left` / `right` → x-axis (cx)
* - `top` / `bottom` → y-axis (cy)
* - `center` or a <length-percentage> → fills the first unset axis, in order
*
* Examples:
* "at left" → cx=0, cy=50% (left is x; y defaults to center)
* "at top" → cx=50%, cy=0 (top is y; x defaults to center)
* "at center 30%" → cx=50%, cy=30%
* "at 30% center" → cx=30%, cy=50%
* "at left top" → cx=0, cy=0
* "at top left" → cx=0, cy=0 (keyword order is irrelevant)
*
* Unset axes fall back to 50%.
*/
const parsePosition = (tokens) => {
let cx = null;
let cy = null;
for (const token of tokens) {
if ((0, parser_1.isIdentToken)(token)) {
switch (token.value) {
case 'left':
cx = length_percentage_1.ZERO_LENGTH;
break;
case 'right':
cx = length_percentage_1.HUNDRED_PERCENT;
break;
case 'top':
cy = length_percentage_1.ZERO_LENGTH;
break;
case 'bottom':
cy = length_percentage_1.HUNDRED_PERCENT;
break;
case 'center':
// `center` fills whichever axis has not yet been claimed.
if (cx === null)
cx = length_percentage_1.FIFTY_PERCENT;
else if (cy === null)
cy = length_percentage_1.FIFTY_PERCENT;
break;
}
}
else if ((0, length_percentage_1.isLengthPercentage)(token)) {
// Length-percentages are assigned in source order.
if (cx === null)
cx = token;
else if (cy === null)
cy = token;
}
}
return { cx: cx ?? length_percentage_1.FIFTY_PERCENT, cy: cy ?? length_percentage_1.FIFTY_PERCENT };
};
/**
* inset( <length-percentage>{1,4} [ round <'border-radius'> ]? )
* The 1-4 shorthand follows the same expansion as margin/padding:
* 1 value → all four sides
* 2 values → top/bottom | left/right
* 3 values → top | left/right | bottom
* 4 values → top | right | bottom | left
* The optional `round` clause (border-radius) is parsed but ignored.
*/
const parseInset = (values) => {
const lengths = [];
for (const token of values) {
if (token.type === 31 /* TokenType.WHITESPACE_TOKEN */)
continue;
if ((0, parser_1.isIdentToken)(token) && token.value === 'round')
break;
if ((0, length_percentage_1.isLengthPercentage)(token))
lengths.push(token);
}
const v0 = lengths[0] ?? length_percentage_1.ZERO_LENGTH;
const v1 = lengths[1] ?? v0;
const v2 = lengths[2] ?? v0;
const v3 = lengths[3] ?? v1;
return { type: 1 /* CLIP_PATH_TYPE.INSET */, top: v0, right: v1, bottom: v2, left: v3 };
};
/**
* circle( [ <shape-radius> ]? [ at <position> ]? )
*/
const parseCircle = (values) => {
const nonWs = values.filter(parser_1.nonWhiteSpace);
const atIdx = nonWs.findIndex((t) => (0, parser_1.isIdentWithValue)(t, 'at'));
const radiusTokens = atIdx === -1 ? nonWs : nonWs.slice(0, atIdx);
const posTokens = atIdx === -1 ? [] : nonWs.slice(atIdx + 1);
return {
type: 2 /* CLIP_PATH_TYPE.CIRCLE */,
radius: parseShapeRadius(radiusTokens),
...parsePosition(posTokens)
};
};
/**
* ellipse( [ <shape-radius>{2} ]? [ at <position> ]? )
*/
const parseEllipse = (values) => {
const nonWs = values.filter(parser_1.nonWhiteSpace);
const atIdx = nonWs.findIndex((t) => (0, parser_1.isIdentWithValue)(t, 'at'));
const radiusTokens = atIdx === -1 ? nonWs : nonWs.slice(0, atIdx);
const posTokens = atIdx === -1 ? [] : nonWs.slice(atIdx + 1);
return {
type: 3 /* CLIP_PATH_TYPE.ELLIPSE */,
rx: parseShapeRadius(radiusTokens.slice(0, 1)),
ry: parseShapeRadius(radiusTokens.slice(1, 2)),
...parsePosition(posTokens)
};
};
/**
* polygon( [ <fill-rule>, ]? [ <length-percentage> <length-percentage> ]# )
* Each comma-separated group defines one vertex (x y).
* A leading fill-rule keyword (nonzero/evenodd) is skipped.
*/
const parsePolygon = (values) => {
const args = (0, parser_1.parseFunctionArgs)(values);
const points = [];
for (const arg of args) {
if (arg.length === 1 && (0, parser_1.isIdentToken)(arg[0]))
continue; // skip fill-rule
const lengths = arg.filter(length_percentage_1.isLengthPercentage);
if (lengths.length >= 2) {
points.push([lengths[0], lengths[1]]);
}
}
return { type: 4 /* CLIP_PATH_TYPE.POLYGON */, points };
};
/**
* path( [ <fill-rule>, ]? <string> )
* The string value is the SVG path data (coordinates in the element's local space).
*/
const parsePath = (values) => {
const stringToken = values.find((t) => t.type === 0 /* TokenType.STRING_TOKEN */);
if (!stringToken)
return NONE;
return { type: 5 /* CLIP_PATH_TYPE.PATH */, d: stringToken.value };
};
exports.clipPath = {
name: 'clip-path',
initialValue: 'none',
prefix: false,
type: 0 /* PropertyDescriptorParsingType.VALUE */,
parse: (_context, token) => {
if ((0, parser_1.isIdentToken)(token) && token.value === 'none') {
return NONE;
}
if (token.type === 18 /* TokenType.FUNCTION */) {
switch (token.name) {
case 'inset':
return parseInset(token.values);
case 'circle':
return parseCircle(token.values);
case 'ellipse':
return parseEllipse(token.values);
case 'polygon':
return parsePolygon(token.values);
case 'path':
return parsePath(token.values);
}
}
return NONE;
}
};
//# sourceMappingURL=clip-path.js.map