feat: fancyBackgrounds in svg

This commit is contained in:
Arnošt Pleskot 2023-08-11 12:00:14 +02:00
parent 94f20566d1
commit e8cc787edc
No known key found for this signature in database
4 changed files with 106 additions and 16 deletions

View File

@ -47,6 +47,7 @@ export const exportCanvas = async (
exportPadding,
exportScale: appState.exportScale,
exportEmbedScene: appState.exportEmbedScene && type === "svg",
fancyBackgroundImageUrl: appState.fancyBackgroundImageUrl,
},
files,
);

View File

@ -123,3 +123,24 @@ export const normalizeSVG = async (SVGString: string) => {
return svg.outerHTML;
}
};
export const loadSVGElement = (filePath: string) => {
return new Promise<SVGSVGElement>((resolve, reject) => {
fetch(filePath)
.then((response) => response.text())
.then((svgString) => {
const parser = new DOMParser();
const svgDoc = parser.parseFromString(svgString, "image/svg+xml");
const svgElement = svgDoc.documentElement;
if (svgElement instanceof SVGSVGElement) {
resolve(svgElement);
} else {
reject(new Error("Parsed element is not an SVGSVGElement"));
}
})
.catch((error) => {
reject(error);
});
});
};

View File

@ -3,7 +3,7 @@ import { NonDeletedExcalidrawElement } from "../element/types";
import { getCommonBounds, getElementAbsoluteCoords } from "../element/bounds";
import { renderSceneToSvg, renderStaticScene } from "../renderer/renderScene";
import { distance, isOnlyExportingSingleFrame } from "../utils";
import { AppState, BinaryFiles } from "../types";
import { AppState, BinaryFiles, DataURL } from "../types";
import {
DEFAULT_EXPORT_PADDING,
FANCY_BG_BORDER_RADIUS,
@ -19,7 +19,10 @@ import {
updateImageCache,
} from "../element/image";
import Scene from "./Scene";
import { applyFancyBackground } from "./fancyBackground";
import {
applyFancyBackgroundOnCanvas,
applyFancyBackgroundOnSvg,
} from "./fancyBackground";
export const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
@ -93,7 +96,7 @@ export const exportToCanvas = async (
};
if (exportWithFancyBackground) {
await applyFancyBackground({
await applyFancyBackgroundOnCanvas({
canvas,
fancyBackgroundImageUrl: appState.fancyBackgroundImageUrl!,
backgroundColor: viewBackgroundColor,
@ -136,6 +139,7 @@ export const exportToSvg = async (
exportWithDarkMode?: boolean;
exportEmbedScene?: boolean;
renderFrame?: boolean;
fancyBackgroundImageUrl: DataURL | null;
},
files: BinaryFiles | null,
opts?: {
@ -148,7 +152,18 @@ export const exportToSvg = async (
viewBackgroundColor,
exportScale = 1,
exportEmbedScene,
exportBackground,
} = appState;
const exportWithFancyBackground =
exportBackground &&
!!appState.fancyBackgroundImageUrl &&
elements.length > 0;
const padding = !exportWithFancyBackground
? exportPadding
: (exportPadding + FANCY_BG_PADDING + FANCY_BG_BORDER_RADIUS) * exportScale;
let metadata = "";
if (exportEmbedScene) {
try {
@ -163,7 +178,7 @@ export const exportToSvg = async (
console.error(error);
}
}
const [minX, minY, width, height] = getCanvasSize(elements, exportPadding);
const [minX, minY, width, height] = getCanvasSize(elements, padding);
// initialize SVG root
const svgRoot = document.createElementNS(SVG_NS, "svg");
@ -198,8 +213,8 @@ export const exportToSvg = async (
const onlyExportingSingleFrame = isOnlyExportingSingleFrame(elements);
const offsetX = -minX + (onlyExportingSingleFrame ? 0 : exportPadding);
const offsetY = -minY + (onlyExportingSingleFrame ? 0 : exportPadding);
const offsetX = -minX + (onlyExportingSingleFrame ? 0 : padding);
const offsetY = -minY + (onlyExportingSingleFrame ? 0 : padding);
const exportingFrame =
isExportingWholeCanvas || !onlyExportingSingleFrame
@ -243,6 +258,16 @@ export const exportToSvg = async (
// render background rect
if (appState.exportBackground && viewBackgroundColor) {
if (appState.fancyBackgroundImageUrl) {
await applyFancyBackgroundOnSvg({
svgRoot,
fancyBackgroundImageUrl:
`${appState.fancyBackgroundImageUrl}` as DataURL,
backgroundColor: viewBackgroundColor,
dimensions: { w: width, h: height },
exportScale,
});
} else {
const rect = svgRoot.ownerDocument!.createElementNS(SVG_NS, "rect");
rect.setAttribute("x", "0");
rect.setAttribute("y", "0");
@ -251,6 +276,7 @@ export const exportToSvg = async (
rect.setAttribute("fill", viewBackgroundColor);
svgRoot.appendChild(rect);
}
}
const rsvg = rough.svg(svgRoot);
renderSceneToSvg(elements, rsvg, svgRoot, files || {}, {

View File

@ -1,5 +1,5 @@
import { FANCY_BG_BORDER_RADIUS, FANCY_BG_PADDING } from "../constants";
import { loadHTMLImageElement } from "../element/image";
import { FANCY_BG_BORDER_RADIUS, FANCY_BG_PADDING, SVG_NS } from "../constants";
import { loadHTMLImageElement, loadSVGElement } from "../element/image";
import { roundRect } from "../renderer/roundRect";
import { AppState, DataURL } from "../types";
@ -121,7 +121,7 @@ const addContentBackground = (
});
};
export const applyFancyBackground = async ({
export const applyFancyBackgroundOnCanvas = async ({
canvas,
fancyBackgroundImageUrl,
backgroundColor,
@ -144,3 +144,45 @@ export const applyFancyBackground = async ({
addContentBackground(context, canvasDimensions, backgroundColor, exportScale);
};
export const applyFancyBackgroundOnSvg = async ({
svgRoot,
fancyBackgroundImageUrl,
backgroundColor,
dimensions,
exportScale,
}: {
svgRoot: SVGSVGElement;
fancyBackgroundImageUrl: DataURL;
backgroundColor: string;
dimensions: Dimensions;
exportScale: AppState["exportScale"];
}) => {
const fancyBackgroundImage = await loadSVGElement(
`${fancyBackgroundImageUrl}`,
);
fancyBackgroundImage.setAttribute("x", "0");
fancyBackgroundImage.setAttribute("y", "0");
fancyBackgroundImage.setAttribute("width", `${dimensions.w}`);
fancyBackgroundImage.setAttribute("height", `${dimensions.h}`);
fancyBackgroundImage.setAttribute("preserveAspectRatio", "none");
svgRoot.appendChild(fancyBackgroundImage);
const rect = svgRoot.ownerDocument!.createElementNS(SVG_NS, "rect");
rect.setAttribute("x", (FANCY_BG_PADDING * exportScale).toString());
rect.setAttribute("y", (FANCY_BG_PADDING * exportScale).toString());
rect.setAttribute(
"width",
`${dimensions.w - FANCY_BG_PADDING * 2 * exportScale}`,
);
rect.setAttribute(
"height",
`${dimensions.h - FANCY_BG_PADDING * 2 * exportScale}`,
);
rect.setAttribute("rx", (FANCY_BG_PADDING * exportScale).toString());
rect.setAttribute("ry", (FANCY_BG_PADDING * exportScale).toString());
rect.setAttribute("fill", backgroundColor);
svgRoot.appendChild(rect);
};