fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
175 lines (163 loc) • 4.97 kB
text/typescript
import { svgNS } from './constants';
import {
parsePreserveAspectRatioAttribute,
parseUnit,
} from '../util/misc/svgParsing';
import { svgViewBoxElementsRegEx, reViewBoxAttrValue } from './constants';
import { NONE } from '../constants';
export type ParsedViewboxTransform = Partial<{
width: number;
height: number;
minX: number;
minY: number;
viewBoxWidth: number;
viewBoxHeight: number;
}>;
/**
* Add a <g> element that envelop all child elements and makes the viewbox transformMatrix descend on all elements
*/
export function applyViewboxTransform(
element: Element,
): ParsedViewboxTransform {
if (!svgViewBoxElementsRegEx.test(element.nodeName)) {
return {};
}
const viewBoxAttr: string | null = element.getAttribute('viewBox');
let scaleX = 1;
let scaleY = 1;
let minX = 0;
let minY = 0;
let matrix;
let el;
const widthAttr = element.getAttribute('width');
const heightAttr = element.getAttribute('height');
const x = element.getAttribute('x') || 0;
const y = element.getAttribute('y') || 0;
const goodViewbox = viewBoxAttr && reViewBoxAttrValue.test(viewBoxAttr);
const missingViewBox = !goodViewbox;
const missingDimAttr =
!widthAttr || !heightAttr || widthAttr === '100%' || heightAttr === '100%';
let translateMatrix = '';
let widthDiff = 0;
let heightDiff = 0;
if (missingViewBox) {
if (
(x || y) &&
element.parentNode &&
element.parentNode.nodeName !== '#document'
) {
translateMatrix =
' translate(' + parseUnit(x || '0') + ' ' + parseUnit(y || '0') + ') ';
matrix = (element.getAttribute('transform') || '') + translateMatrix;
element.setAttribute('transform', matrix);
element.removeAttribute('x');
element.removeAttribute('y');
}
}
if (missingViewBox && missingDimAttr) {
return {
width: 0,
height: 0,
};
}
const parsedDim: ParsedViewboxTransform = {
width: 0,
height: 0,
};
if (missingViewBox) {
parsedDim.width = parseUnit(widthAttr!);
parsedDim.height = parseUnit(heightAttr!);
// set a transform for elements that have x y and are inner(only) SVGs
return parsedDim;
}
const pasedViewBox = viewBoxAttr.match(reViewBoxAttrValue)!;
minX = -parseFloat(pasedViewBox[1]);
minY = -parseFloat(pasedViewBox[2]);
const viewBoxWidth = parseFloat(pasedViewBox[3]);
const viewBoxHeight = parseFloat(pasedViewBox[4]);
parsedDim.minX = minX;
parsedDim.minY = minY;
parsedDim.viewBoxWidth = viewBoxWidth;
parsedDim.viewBoxHeight = viewBoxHeight;
if (!missingDimAttr) {
parsedDim.width = parseUnit(widthAttr);
parsedDim.height = parseUnit(heightAttr);
scaleX = parsedDim.width / viewBoxWidth;
scaleY = parsedDim.height / viewBoxHeight;
} else {
parsedDim.width = viewBoxWidth;
parsedDim.height = viewBoxHeight;
}
// default is to preserve aspect ratio
const preserveAspectRatio = parsePreserveAspectRatioAttribute(
element.getAttribute('preserveAspectRatio') || '',
);
if (preserveAspectRatio.alignX !== NONE) {
//translate all container for the effect of Mid, Min, Max
if (preserveAspectRatio.meetOrSlice === 'meet') {
scaleY = scaleX = scaleX > scaleY ? scaleY : scaleX;
// calculate additional translation to move the viewbox
}
if (preserveAspectRatio.meetOrSlice === 'slice') {
scaleY = scaleX = scaleX > scaleY ? scaleX : scaleY;
// calculate additional translation to move the viewbox
}
widthDiff = parsedDim.width - viewBoxWidth * scaleX;
heightDiff = parsedDim.height - viewBoxHeight * scaleX;
if (preserveAspectRatio.alignX === 'Mid') {
widthDiff /= 2;
}
if (preserveAspectRatio.alignY === 'Mid') {
heightDiff /= 2;
}
if (preserveAspectRatio.alignX === 'Min') {
widthDiff = 0;
}
if (preserveAspectRatio.alignY === 'Min') {
heightDiff = 0;
}
}
if (
scaleX === 1 &&
scaleY === 1 &&
minX === 0 &&
minY === 0 &&
x === 0 &&
y === 0
) {
return parsedDim;
}
if ((x || y) && element.parentNode!.nodeName !== '#document') {
translateMatrix =
' translate(' + parseUnit(x || '0') + ' ' + parseUnit(y || '0') + ') ';
}
matrix =
translateMatrix +
' matrix(' +
scaleX +
' 0' +
' 0 ' +
scaleY +
' ' +
(minX * scaleX + widthDiff) +
' ' +
(minY * scaleY + heightDiff) +
') ';
// seems unused.
// parsedDim.viewboxTransform = parseTransformAttribute(matrix);
if (element.nodeName === 'svg') {
el = element.ownerDocument.createElementNS(svgNS, 'g');
// element.firstChild != null
while (element.firstChild) {
el.appendChild(element.firstChild);
}
element.appendChild(el);
} else {
el = element;
el.removeAttribute('x');
el.removeAttribute('y');
matrix = el.getAttribute('transform') + matrix;
}
el.setAttribute('transform', matrix);
return parsedDim;
}