@@ -44,6 +44,7 @@ import {
4444 IMPORT_FROM ,
4545 noteWidth ,
4646 pngExportPixelRatio ,
47+ darkBgTheme ,
4748} from "../../data/constants" ;
4849import jsPDF from "jspdf" ;
4950import { 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 && / d i s p l a y \s * : \s * n o n e / . 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