Skip to content

Instantly share code, notes, and snippets.

@jongalloway
Last active December 10, 2024 06:17
Show Gist options
  • Save jongalloway/ce88bdde32d01bd94ccb13716a66d271 to your computer and use it in GitHub Desktop.
Save jongalloway/ce88bdde32d01bd94ccb13716a66d271 to your computer and use it in GitHub Desktop.
Mermaid diagrams use non-standard foreignObject and HTML instead of standard SVG. This snippet can be run in a browser console to get most of the way to SVG that can be edited in Inkscape and other SVG editors. More info about the issue this fixes here: https://github.com/mermaid-js/mermaid/issues/2688
(function() {
// Manually set x and y values for text elements since calculation isn't reliable. Set to what works for your theme.
const textX = 0;
const textY = 17;
// Optional feature to insert a white rectangle below the diagram for dark mode visibility
const addBackgroundRect = false;
// Get the SVG element
const svg = document.querySelector('svg');
// Remove the external stylesheet reference
const xmlStylesheet = document.querySelector('xml-stylesheet');
if (xmlStylesheet) {
xmlStylesheet.remove();
}
// Insert a white rectangle below the diagram if the feature is enabled
if (addBackgroundRect) {
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('x', 0);
rect.setAttribute('y', 0);
rect.setAttribute('width', '100%');
rect.setAttribute('height', '100%');
rect.setAttribute('fill', 'white');
svg.insertBefore(rect, svg.firstChild);
}
// Convert foreignObject elements to SVG text elements
const foreignObjects = svg.querySelectorAll('foreignObject');
foreignObjects.forEach(foreignObject => {
const div = foreignObject.querySelector('div');
if (div) {
const span = div.querySelector('span');
const p = div.querySelector('p');
const textContent = span ? span.textContent : (p ? p.textContent : '');
if (textContent) {
// Create a new text element
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
text.setAttribute('x', textX);
text.setAttribute('y', textY);
// Extract and apply font styles
const computedStyle = window.getComputedStyle(div);
const fontFamily = computedStyle.fontFamily;
const fontSize = computedStyle.fontSize;
const fill = computedStyle.color;
text.setAttribute('style', `font-family: ${fontFamily}; font-size: ${fontSize}; fill: ${fill};`);
text.textContent = textContent;
// Replace the foreignObject with the new text element
foreignObject.parentNode.replaceChild(text, foreignObject);
}
}
});
// Inline CSS styles
const style = svg.querySelector('style');
if (style) {
const cssRules = style.sheet.cssRules;
for (let rule of cssRules) {
const selector = rule.selectorText;
const elements = svg.querySelectorAll(selector);
elements.forEach(element => {
const styleText = rule.style.cssText;
const existingStyle = element.getAttribute('style') || '';
element.setAttribute('style', existingStyle + styleText);
});
}
// Remove the style element
style.remove();
}
console.log('SVG conversion complete');
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment