Skip to content

Commit 94e3f68

Browse files
committed
fix: resolve exported SVG appearing blank in Inkscape (#612)
1 parent d428a2c commit 94e3f68

File tree

1 file changed

+62
-11
lines changed

1 file changed

+62
-11
lines changed

src/components/EditorHeader/ControlPanel.jsx

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import {
4444
IMPORT_FROM,
4545
noteWidth,
4646
pngExportPixelRatio,
47+
darkBgTheme,
4748
} from "../../data/constants";
4849
import jsPDF from "jspdf";
4950
import { useHotkeys } from "react-hotkeys-hook";
@@ -1140,17 +1141,67 @@ export default function ControlPanel({
11401141
{
11411142
name: "SVG",
11421143
function: () => {
1143-
const filter = (node) => node.tagName !== "i";
1144-
toSvg(document.getElementById("canvas"), { filter: filter }).then(
1145-
function (dataUrl) {
1146-
setExportData((prev) => ({
1147-
...prev,
1148-
data: dataUrl,
1149-
extension: "svg",
1150-
}));
1151-
},
1152-
);
1153-
setModal(MODAL.IMG);
1144+
try {
1145+
const svg = document.getElementById("diagram");
1146+
if (!svg) {
1147+
Toast.error(t("oops_smth_went_wrong"));
1148+
return;
1149+
}
1150+
1151+
// Clone the SVG so we don't mutate the in-page one
1152+
const clone = svg.cloneNode(true);
1153+
const xmlns = "http://www.w3.org/2000/svg";
1154+
if (!clone.getAttribute("xmlns"))
1155+
clone.setAttribute("xmlns", xmlns);
1156+
if (!clone.getAttribute("xmlns:xlink"))
1157+
clone.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
1158+
1159+
// Ensure width/height are explicit so Inkscape positions content correctly
1160+
const viewBoxAttr = clone.getAttribute("viewBox") || "0 0 800 600";
1161+
const vb = viewBoxAttr.split(/\s+/).map(Number);
1162+
const vbLeft = vb[0] || 0;
1163+
const vbTop = vb[1] || 0;
1164+
const vbWidth = vb[2] || 800;
1165+
const vbHeight = vb[3] || 600;
1166+
clone.setAttribute("width", Math.round(vbWidth));
1167+
clone.setAttribute("height", Math.round(vbHeight));
1168+
clone.setAttribute("preserveAspectRatio", "xMidYMid meet");
1169+
1170+
// Insert a background rect as the first child (behind everything).
1171+
// Do not place it above content.
1172+
const bgRect = document.createElementNS(xmlns, "rect");
1173+
bgRect.setAttribute("x", vbLeft);
1174+
bgRect.setAttribute("y", vbTop);
1175+
bgRect.setAttribute("width", vbWidth);
1176+
bgRect.setAttribute("height", vbHeight);
1177+
// Use canvas background color: dark mode uses theme, otherwise white
1178+
const bgColor = settings.mode === "dark" ? darkBgTheme : "white";
1179+
bgRect.setAttribute("fill", bgColor);
1180+
clone.insertBefore(bgRect, clone.firstChild);
1181+
1182+
// Remove any hidden elements (display:none) which inflate file and may confuse parsers
1183+
const hidden = clone.querySelectorAll("[style]");
1184+
hidden.forEach((el) => {
1185+
const st = el.getAttribute("style");
1186+
if (st && /display\s*:\s*none/.test(st)) el.remove();
1187+
});
1188+
1189+
// Serialize
1190+
const serializer = new XMLSerializer();
1191+
let svgString = serializer.serializeToString(clone);
1192+
svgString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + svgString;
1193+
const dataUrl = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svgString);
1194+
1195+
setExportData((prev) => ({
1196+
...prev,
1197+
data: dataUrl,
1198+
extension: "svg",
1199+
}));
1200+
setModal(MODAL.IMG);
1201+
} catch (e) {
1202+
console.error(e);
1203+
Toast.error(t("oops_smth_went_wrong"));
1204+
}
11541205
},
11551206
},
11561207
{

0 commit comments

Comments
 (0)