mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-08-21 01:07:02 +02:00
Compare commits
2 Commits
dependabot
...
fix-svg-cr
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d308451584 | ||
![]() |
f444383b46 |
@@ -4557,9 +4557,9 @@ http-parser-js@>=0.5.1:
|
||||
integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==
|
||||
|
||||
http-proxy-middleware@^2.0.3:
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz#915f236d92ae98ef48278a95dedf17e991936ec6"
|
||||
integrity sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f"
|
||||
integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==
|
||||
dependencies:
|
||||
"@types/http-proxy" "^1.17.8"
|
||||
http-proxy "^1.18.1"
|
||||
|
@@ -7,7 +7,7 @@ import {
|
||||
SVG_NS,
|
||||
} from "../constants";
|
||||
import { normalizeLink, toValidURL } from "../data/url";
|
||||
import { getElementAbsoluteCoords } from "../element";
|
||||
import { getElementAbsoluteCoords, hashString } from "../element";
|
||||
import {
|
||||
createPlaceholderEmbeddableLabel,
|
||||
getEmbedLink,
|
||||
@@ -411,7 +411,26 @@ const renderElementToSvg = (
|
||||
const fileData =
|
||||
isInitializedImageElement(element) && files[element.fileId];
|
||||
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}`);
|
||||
if (!symbol) {
|
||||
symbol = svgRoot.ownerDocument!.createElementNS(SVG_NS, "symbol");
|
||||
@@ -421,18 +440,7 @@ const renderElementToSvg = (
|
||||
image.setAttribute("href", fileData.dataURL);
|
||||
image.setAttribute("preserveAspectRatio", "none");
|
||||
|
||||
if (element.crop) {
|
||||
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}`,
|
||||
);
|
||||
if (element.crop || !reuseImages) {
|
||||
image.setAttribute("width", `${uncroppedWidth}`);
|
||||
image.setAttribute("height", `${uncroppedHeight}`);
|
||||
} else {
|
||||
@@ -456,8 +464,23 @@ const renderElementToSvg = (
|
||||
use.setAttribute("filter", IMAGE_INVERT_FILTER);
|
||||
}
|
||||
|
||||
use.setAttribute("width", `${width}`);
|
||||
use.setAttribute("height", `${height}`);
|
||||
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);
|
||||
}
|
||||
|
||||
const adjustedCenterX = cx + normalizedCropX;
|
||||
const adjustedCenterY = cy + normalizedCropY;
|
||||
|
||||
use.setAttribute("width", `${width + normalizedCropX}`);
|
||||
use.setAttribute("height", `${height + normalizedCropY}`);
|
||||
use.setAttribute("opacity", `${opacity}`);
|
||||
|
||||
// We first apply `scale` transforms (horizontal/vertical mirroring)
|
||||
@@ -467,21 +490,43 @@ const renderElementToSvg = (
|
||||
// the transformations correctly (the transform-origin was not being
|
||||
// applied correctly).
|
||||
if (element.scale[0] !== 1 || element.scale[1] !== 1) {
|
||||
const translateX = element.scale[0] !== 1 ? -width : 0;
|
||||
const translateY = element.scale[1] !== 1 ? -height : 0;
|
||||
use.setAttribute(
|
||||
"transform",
|
||||
`scale(${element.scale[0]}, ${element.scale[1]}) translate(${translateX} ${translateY})`,
|
||||
`translate(${adjustedCenterX} ${adjustedCenterY}) scale(${
|
||||
element.scale[0]
|
||||
} ${
|
||||
element.scale[1]
|
||||
}) translate(${-adjustedCenterX} ${-adjustedCenterY})`,
|
||||
);
|
||||
}
|
||||
|
||||
const g = svgRoot.ownerDocument!.createElementNS(SVG_NS, "g");
|
||||
|
||||
if (element.crop) {
|
||||
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.setAttribute(
|
||||
"transform",
|
||||
`translate(${offsetX || 0} ${
|
||||
offsetY || 0
|
||||
}) rotate(${degree} ${cx} ${cy})`,
|
||||
`translate(${offsetX - normalizedCropX} ${
|
||||
offsetY - normalizedCropY
|
||||
}) rotate(${degree} ${adjustedCenterX} ${adjustedCenterY})`,
|
||||
);
|
||||
|
||||
if (element.roundness) {
|
||||
|
@@ -46,6 +46,13 @@ export type SVGRenderConfig = {
|
||||
frameRendering: AppState["frameRendering"];
|
||||
canvasBackgroundColor: AppState["viewBackgroundColor"];
|
||||
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 = {
|
||||
|
@@ -10,5 +10,5 @@ exports[`export > exporting svg containing transformed images > svg export outpu
|
||||
</style>
|
||||
|
||||
</defs>
|
||||
<clipPath id="image-clipPath-id1" data-id="id1"><rect width="100" height="100" rx="25" ry="25"></rect></clipPath><g transform="translate(30.710678118654755 30.710678118654755) rotate(315 50 50)" clip-path="url(#image-clipPath-id1)" data-id="id1"><use href="#image-file_A" width="100" height="100" opacity="1"></use></g><clipPath id="image-clipPath-id2" data-id="id2"><rect width="50" height="50" rx="12.5" ry="12.5"></rect></clipPath><g transform="translate(130.71067811865476 30.710678118654755) rotate(45 25 25)" clip-path="url(#image-clipPath-id2)" data-id="id2"><use href="#image-file_A" width="50" height="50" opacity="1" transform="scale(-1, 1) translate(-50 0)"></use></g><clipPath id="image-clipPath-id3" data-id="id3"><rect width="100" height="100" rx="25" ry="25"></rect></clipPath><g transform="translate(30.710678118654755 130.71067811865476) rotate(45 50 50)" clip-path="url(#image-clipPath-id3)" data-id="id3"><use href="#image-file_A" width="100" height="100" opacity="1" transform="scale(1, -1) translate(0 -100)"></use></g><clipPath id="image-clipPath-id4" data-id="id4"><rect width="50" height="50" rx="12.5" ry="12.5"></rect></clipPath><g transform="translate(130.71067811865476 130.71067811865476) rotate(315 25 25)" clip-path="url(#image-clipPath-id4)" data-id="id4"><use href="#image-file_A" width="50" height="50" opacity="1" transform="scale(-1, -1) translate(-50 -50)"></use></g></svg>"
|
||||
<clipPath id="image-clipPath-id1" data-id="id1"><rect width="100" height="100" rx="25" ry="25"></rect></clipPath><g transform="translate(30.710678118654755 30.710678118654755) rotate(315 50 50)" clip-path="url(#image-clipPath-id1)" data-id="id1"><use href="#image-file_A" width="100" height="100" opacity="1"></use></g><clipPath id="image-clipPath-id2" data-id="id2"><rect width="50" height="50" rx="12.5" ry="12.5"></rect></clipPath><g transform="translate(130.71067811865476 30.710678118654755) rotate(45 25 25)" clip-path="url(#image-clipPath-id2)" data-id="id2"><use href="#image-file_A" width="50" height="50" opacity="1" transform="translate(25 25) scale(-1 1) translate(-25 -25)"></use></g><clipPath id="image-clipPath-id3" data-id="id3"><rect width="100" height="100" rx="25" ry="25"></rect></clipPath><g transform="translate(30.710678118654755 130.71067811865476) rotate(45 50 50)" clip-path="url(#image-clipPath-id3)" data-id="id3"><use href="#image-file_A" width="100" height="100" opacity="1" transform="translate(50 50) scale(1 -1) translate(-50 -50)"></use></g><clipPath id="image-clipPath-id4" data-id="id4"><rect width="50" height="50" rx="12.5" ry="12.5"></rect></clipPath><g transform="translate(130.71067811865476 130.71067811865476) rotate(315 25 25)" clip-path="url(#image-clipPath-id4)" data-id="id4"><use href="#image-file_A" width="50" height="50" opacity="1" transform="translate(25 25) scale(-1 -1) translate(-25 -25)"></use></g></svg>"
|
||||
`;
|
||||
|
Reference in New Issue
Block a user