fix: image cropping svg + compat mode

remotes/origin/dwelle/fix-svg-crop
dwelle 3 months ago
parent 7d52176fea
commit f444383b46

@ -7,7 +7,7 @@ import {
SVG_NS, SVG_NS,
} from "../constants"; } from "../constants";
import { normalizeLink, toValidURL } from "../data/url"; import { normalizeLink, toValidURL } from "../data/url";
import { getElementAbsoluteCoords } from "../element"; import { getElementAbsoluteCoords, hashString } from "../element";
import { import {
createPlaceholderEmbeddableLabel, createPlaceholderEmbeddableLabel,
getEmbedLink, getEmbedLink,
@ -411,7 +411,26 @@ const renderElementToSvg = (
const fileData = const fileData =
isInitializedImageElement(element) && files[element.fileId]; isInitializedImageElement(element) && files[element.fileId];
if (fileData) { if (fileData) {
const symbolId = `image-${fileData.id}`; // TODO set to `false` before merging
const { reuseImages = true } = renderConfig;
let symbolId = `image-${fileData.id}`;
let uncroppedWidth = element.width;
let uncroppedHeight = element.height;
if (element.crop) {
({ width: uncroppedWidth, height: uncroppedHeight } =
getUncroppedWidthAndHeight(element));
symbolId = `image-crop-${fileData.id}-${hashString(
`${uncroppedWidth}x${uncroppedHeight}`,
)}`;
}
if (!reuseImages) {
symbolId = `image-${element.id}`;
}
let symbol = svgRoot.querySelector(`#${symbolId}`); let symbol = svgRoot.querySelector(`#${symbolId}`);
if (!symbol) { if (!symbol) {
symbol = svgRoot.ownerDocument!.createElementNS(SVG_NS, "symbol"); symbol = svgRoot.ownerDocument!.createElementNS(SVG_NS, "symbol");
@ -421,18 +440,7 @@ const renderElementToSvg = (
image.setAttribute("href", fileData.dataURL); image.setAttribute("href", fileData.dataURL);
image.setAttribute("preserveAspectRatio", "none"); image.setAttribute("preserveAspectRatio", "none");
if (element.crop) { if (element.crop || !reuseImages) {
const { width: uncroppedWidth, height: uncroppedHeight } =
getUncroppedWidthAndHeight(element);
symbol.setAttribute(
"viewBox",
`${
element.crop.x / (element.crop.naturalWidth / uncroppedWidth)
} ${
element.crop.y / (element.crop.naturalHeight / uncroppedHeight)
} ${width} ${height}`,
);
image.setAttribute("width", `${uncroppedWidth}`); image.setAttribute("width", `${uncroppedWidth}`);
image.setAttribute("height", `${uncroppedHeight}`); image.setAttribute("height", `${uncroppedHeight}`);
} else { } else {
@ -476,12 +484,46 @@ const renderElementToSvg = (
} }
const g = svgRoot.ownerDocument!.createElementNS(SVG_NS, "g"); const g = svgRoot.ownerDocument!.createElementNS(SVG_NS, "g");
let normalizedCropX = 0;
let normalizedCropY = 0;
if (element.crop) {
const { width: uncroppedWidth, height: uncroppedHeight } =
getUncroppedWidthAndHeight(element);
normalizedCropX =
element.crop.x / (element.crop.naturalWidth / uncroppedWidth);
normalizedCropY =
element.crop.y / (element.crop.naturalHeight / uncroppedHeight);
}
if (element.crop) {
use.setAttribute("width", `100%`);
use.setAttribute("height", `100%`);
const mask = svgRoot.ownerDocument!.createElementNS(SVG_NS, "mask");
mask.setAttribute("id", `mask-image-crop-${element.id}`);
mask.setAttribute("fill", "#fff");
const maskRect = svgRoot.ownerDocument!.createElementNS(
SVG_NS,
"rect",
);
maskRect.setAttribute("x", `${normalizedCropX}`);
maskRect.setAttribute("y", `${normalizedCropY}`);
maskRect.setAttribute("width", `${width}`);
maskRect.setAttribute("height", `${height}`);
mask.appendChild(maskRect);
root.appendChild(mask);
g.setAttribute("mask", `url(#${mask.id})`);
}
g.appendChild(use); g.appendChild(use);
g.setAttribute( g.setAttribute(
"transform", "transform",
`translate(${offsetX || 0} ${ `translate(${offsetX - normalizedCropX} ${
offsetY || 0 offsetY - normalizedCropY
}) rotate(${degree} ${cx} ${cy})`, }) rotate(${degree} ${cx + normalizedCropX} ${cy + normalizedCropY})`,
); );
if (element.roundness) { if (element.roundness) {

@ -46,6 +46,13 @@ export type SVGRenderConfig = {
frameRendering: AppState["frameRendering"]; frameRendering: AppState["frameRendering"];
canvasBackgroundColor: AppState["viewBackgroundColor"]; canvasBackgroundColor: AppState["viewBackgroundColor"];
embedsValidationStatus: EmbedsValidationStatus; embedsValidationStatus: EmbedsValidationStatus;
/**
* whether to attempt to reuse images as much as possible through symbols
* (reduces SVG size, but may be incompoatible with some SVG renderers)
*
* @default true
*/
reuseImages?: boolean;
}; };
export type InteractiveCanvasRenderConfig = { export type InteractiveCanvasRenderConfig = {

Loading…
Cancel
Save