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, y,
width, width,
height, height,
crop: { crop: null,
x: 0,
y: 0,
width: image.naturalWidth,
height: image.naturalHeight,
},
}); });
} }
}; };

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

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

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

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

Loading…
Cancel
Save