svg export

pull/8613/head
Ryan Di 5 months ago
parent 7b012b1cad
commit 3f00762a77

@ -9531,12 +9531,7 @@ class App extends React.Component<AppProps, AppState> {
y,
width,
height,
crop: {
x: 0,
y: 0,
width: image.naturalWidth,
height: image.naturalHeight,
},
crop: null,
});
}
};

@ -32,19 +32,14 @@ import { isInitializedImageElement } from "./typeChecks";
const _cropElement = (
element: ExcalidrawImageElement,
image: HTMLImageElement,
transformHandle: TransformHandleType,
naturalWidth: number,
naturalHeight: number,
pointerX: number,
pointerY: number,
) => {
const uncroppedWidth =
element.width /
(element.crop ? element.crop.width / image.naturalWidth : 1);
const uncroppedHeight =
element.height /
(element.crop ? element.crop.height / image.naturalHeight : 1);
const { width: uncroppedWidth, height: uncroppedHeight } =
getUncroppedWidthAndHeight(element);
const naturalWidthToUncropped = naturalWidth / uncroppedWidth;
const naturalHeightToUncropped = naturalHeight / uncroppedHeight;
@ -79,6 +74,7 @@ const _cropElement = (
y: 0,
width: naturalWidth,
height: naturalHeight,
naturalDimension: [naturalWidth, naturalHeight],
};
const previousCropHeight = crop.height;
@ -168,25 +164,24 @@ export const cropElement = (
isInitializedImageElement(element) && imageCache.get(element.fileId)?.image;
if (image && !(image instanceof Promise)) {
const mutation = _cropElement(
mutateElement(
element,
_cropElement(
element,
image,
transformHandle,
image.naturalWidth,
image.naturalHeight,
pointerX,
pointerY,
),
);
mutateElement(element, mutation);
updateBoundElements(element, elementsMap, {
oldSize: { width: element.width, height: element.height },
});
}
};
// TODO: replace with the refactored resizeSingleElement
const recomputeOrigin = (
stateAtCropStart: NonDeleted<ExcalidrawElement>,
transformHandle: TransformHandleType,
@ -250,16 +245,9 @@ const recomputeOrigin = (
export const getUncroppedImageElement = (
element: ExcalidrawImageElement,
elementsMap: ElementsMap,
imageCache: AppClassProperties["imageCache"],
) => {
const image =
isInitializedImageElement(element) && imageCache.get(element.fileId)?.image;
if (image && !(image instanceof Promise)) {
if (element.crop) {
const width = element.width / (element.crop.width / image.naturalWidth);
const height =
element.height / (element.crop.height / image.naturalHeight);
const { width, height } = getUncroppedWidthAndHeight(element);
const [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords(
element,
@ -281,20 +269,19 @@ export const getUncroppedImageElement = (
const leftEdgeVector = vectorSubtract(bottomLeftVector, topLeftVector);
const leftEdgeNormalized = vectorNormalize(leftEdgeVector);
const { cropX, cropY } = adjustCropPosition(
element.crop,
element.scale,
image,
);
const { cropX, cropY } = adjustCropPosition(element.crop, element.scale);
const rotatedTopLeft = vectorAdd(
vectorAdd(
topLeftVector,
vectorScale(topEdgeNormalized, (-cropX * width) / image.naturalWidth),
vectorScale(
topEdgeNormalized,
(-cropX * width) / element.crop.naturalDimension[0],
),
),
vectorScale(
leftEdgeNormalized,
(-cropY * height) / image.naturalHeight,
(-cropY * height) / element.crop.naturalDimension[1],
),
);
@ -322,15 +309,32 @@ export const getUncroppedImageElement = (
return uncroppedElement;
}
}
return element;
};
export const getUncroppedWidthAndHeight = (element: ExcalidrawImageElement) => {
if (element.crop) {
const width =
element.width / (element.crop.width / element.crop.naturalDimension[0]);
const height =
element.height / (element.crop.height / element.crop.naturalDimension[1]);
return {
width,
height,
};
}
return {
width: element.width,
height: element.height,
};
};
const adjustCropPosition = (
crop: ImageCrop,
scale: ExcalidrawImageElement["scale"],
image: HTMLImageElement,
) => {
let cropX = crop.x;
let cropY = crop.y;
@ -339,11 +343,11 @@ const adjustCropPosition = (
const flipY = scale[1] === -1;
if (flipX) {
cropX = image.naturalWidth - Math.abs(cropX) - crop.width;
cropX = crop.naturalDimension[0] - Math.abs(cropX) - crop.width;
}
if (flipY) {
cropY = image.naturalHeight - Math.abs(cropY) - crop.height;
cropY = crop.naturalDimension[1] - Math.abs(cropY) - crop.height;
}
return {

@ -137,6 +137,7 @@ export type ImageCrop = {
y: number;
width: number;
height: number;
naturalDimension: [number, number];
};
export type ExcalidrawImageElement = _ExcalidrawElementBase &
@ -147,7 +148,7 @@ export type ExcalidrawImageElement = _ExcalidrawElementBase &
status: "pending" | "saved" | "error";
/** X and Y scale factors <-1, 1>, used for image axis flipping */
scale: [number, number];
/** whether an element is cropped */
crop: ImageCrop | null;
}>;

@ -950,11 +950,7 @@ export const renderElement = (
context.globalAlpha = 0.1;
const uncroppedElementCanvas = generateElementCanvas(
getUncroppedImageElement(
elementWithCanvas.element,
elementsMap,
renderConfig.imageCache,
),
getUncroppedImageElement(elementWithCanvas.element, elementsMap),
allElementsMap,
appState.zoom,
renderConfig,

@ -37,6 +37,7 @@ import { getFontFamilyString, isRTL, isTestEnv } from "../utils";
import { getFreeDrawSvgPath, IMAGE_INVERT_FILTER } from "./renderElement";
import { getVerticalOffset } from "../fonts";
import { getCornerRadius, isPathALoop } from "../shapes";
import { getUncroppedWidthAndHeight } from "../element/cropElement";
const roughSVGDrawWithPrecision = (
rsvg: RoughSVG,
@ -417,11 +418,29 @@ const renderElementToSvg = (
symbol.id = symbolId;
const image = svgRoot.ownerDocument!.createElementNS(SVG_NS, "image");
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.naturalDimension[0] / uncroppedWidth)
} ${
element.crop.y /
(element.crop.naturalDimension[1] / uncroppedHeight)
} ${width} ${height}`,
);
image.setAttribute("width", `${uncroppedWidth}`);
image.setAttribute("height", `${uncroppedHeight}`);
} else {
image.setAttribute("width", "100%");
image.setAttribute("height", "100%");
image.setAttribute("href", fileData.dataURL);
image.setAttribute("preserveAspectRatio", "none");
}
symbol.appendChild(image);

Loading…
Cancel
Save