temp
This commit is contained in:
Vendored
+2
@@ -9,6 +9,7 @@
|
||||
"corg",
|
||||
"cssnano",
|
||||
"esserializer",
|
||||
"fontawesome",
|
||||
"fsegurai",
|
||||
"gantt",
|
||||
"gitgraph",
|
||||
@@ -30,6 +31,7 @@
|
||||
"Stackable",
|
||||
"tailwindcss",
|
||||
"uparrow",
|
||||
"webfonts",
|
||||
"zenuml"
|
||||
],
|
||||
"vitest.commandLine": "pnpm test:unit",
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.1"
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39",
|
||||
"pnpm": {
|
||||
|
||||
@@ -19,38 +19,56 @@
|
||||
import ExternalLinkIcon from '~icons/material-symbols/open-in-new-rounded';
|
||||
import WidthIcon from '~icons/material-symbols/width-rounded';
|
||||
|
||||
const FONT_AWESOME_URL = `https://cdnjs.cloudflare.com/ajax/libs/font-awesome/${FAVersion}`;
|
||||
const fontAwesomeURLs = (() => {
|
||||
const baseUrl = `https://cdnjs.cloudflare.com/ajax/libs/font-awesome/${FAVersion}`;
|
||||
return {
|
||||
css: `${baseUrl}/css/all.min.css`,
|
||||
woff2: `${baseUrl}/webfonts/fa-solid-900.woff2`
|
||||
};
|
||||
})();
|
||||
|
||||
type Exporter = (context: CanvasRenderingContext2D, image: HTMLImageElement) => () => void;
|
||||
|
||||
const getFileName = (extension: string) =>
|
||||
`mermaid-diagram-${dayjs().format('YYYY-MM-DD-HHmmss')}.${extension}`;
|
||||
|
||||
const getSvgElement = () => {
|
||||
const svgElement = document.querySelector('#container svg')?.cloneNode(true) as HTMLElement;
|
||||
svgElement.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
|
||||
return svgElement;
|
||||
const getSvgElement = async () => {
|
||||
try {
|
||||
$inputStateStore.panZoom = false;
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
await waitForRender();
|
||||
const svgElement = document.querySelector('#container svg');
|
||||
if (!svgElement) {
|
||||
throw new Error('svg not found');
|
||||
}
|
||||
svgElement.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
|
||||
return {
|
||||
svg: svgElement.cloneNode(true) as HTMLElement,
|
||||
box: svgElement.querySelector('g')!.getBoundingClientRect()
|
||||
};
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
$inputStateStore.panZoom = true;
|
||||
}, 10000);
|
||||
}
|
||||
};
|
||||
|
||||
const getBase64SVG = async (
|
||||
svg?: HTMLElement,
|
||||
width?: number,
|
||||
height?: number
|
||||
): Promise<string> => {
|
||||
if (svg) {
|
||||
// Prevents the SVG size of the interface from being changed
|
||||
svg = svg.cloneNode(true) as HTMLElement;
|
||||
}
|
||||
height && svg?.setAttribute('height', `${height}px`);
|
||||
width && svg?.setAttribute('width', `${width}px`); // Workaround https://stackoverflow.com/questions/28690643/firefox-error-rendering-an-svg-image-to-html5-canvas-with-drawimage
|
||||
|
||||
if (!svg) {
|
||||
svg = getSvgElement();
|
||||
const injectFontAwesome = async ({
|
||||
svgString,
|
||||
fontAwesomeEmbedMode
|
||||
}: {
|
||||
svgString: string;
|
||||
fontAwesomeEmbedMode: 'raw' | 'url';
|
||||
}) => {
|
||||
if (fontAwesomeEmbedMode === 'url') {
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet href="${fontAwesomeURLs.css}" type="text/css"?>
|
||||
${svgString}`;
|
||||
}
|
||||
|
||||
const [fontAwesomeCSS, fontBlob] = await Promise.all([
|
||||
fetch(`${FONT_AWESOME_URL}/css/all.min.css`).then((response) => response.text()),
|
||||
fetch(`${FONT_AWESOME_URL}/webfonts/fa-solid-900.woff2`).then((res) => res.blob())
|
||||
fetch(fontAwesomeURLs.css).then((response) => response.text()),
|
||||
fetch(fontAwesomeURLs.woff2).then((res) => res.blob())
|
||||
]);
|
||||
const reader = new FileReader();
|
||||
const fontBase64 = await new Promise<string>((resolve) => {
|
||||
@@ -61,7 +79,7 @@
|
||||
};
|
||||
reader.readAsDataURL(fontBlob);
|
||||
});
|
||||
const fontFaceCSS = `
|
||||
const styleString = `
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-style: normal;
|
||||
@@ -77,14 +95,47 @@
|
||||
width: 16px;
|
||||
fill: black;
|
||||
}
|
||||
`;
|
||||
|
||||
${fontAwesomeCSS}
|
||||
`;
|
||||
return svgString.replace('<style>', `<style>${styleString}`);
|
||||
};
|
||||
|
||||
const getBase64SVG = async ({
|
||||
svg,
|
||||
box,
|
||||
width,
|
||||
height,
|
||||
embedFontAwesome
|
||||
}: {
|
||||
svg?: HTMLElement;
|
||||
box?: DOMRect;
|
||||
width?: number;
|
||||
height?: number;
|
||||
embedFontAwesome?: boolean;
|
||||
} = {}): Promise<string> => {
|
||||
if (!svg || !box) {
|
||||
({ svg, box } = await getSvgElement());
|
||||
}
|
||||
|
||||
console.log(box);
|
||||
// Prevents the SVG size of the interface from being changed
|
||||
// svg = svg.cloneNode(true) as HTMLElement;
|
||||
|
||||
height && svg.setAttribute('height', `${height}px`);
|
||||
width && svg.setAttribute('width', `${width}px`); // Workaround https://stackoverflow.com/questions/28690643/firefox-error-rendering-an-svg-image-to-html5-canvas-with-drawimage
|
||||
svg.setAttribute('viewBox', `0 0 ${box.width} ${box.height}`);
|
||||
const svgString = svg.outerHTML
|
||||
.replaceAll('<br>', '<br/>')
|
||||
.replaceAll(/<img([^>]*)>/g, (m, g: string) => `<img ${g} />`)
|
||||
.replace('<style>', `<style>${fontFaceCSS} ${fontAwesomeCSS}`);
|
||||
.replaceAll(/<img([^>]*)>/g, (m, g: string) => `<img ${g} />`);
|
||||
|
||||
return toBase64(svgString);
|
||||
const svgStringWithFontAwesome = await injectFontAwesome({
|
||||
svgString,
|
||||
fontAwesomeEmbedMode: embedFontAwesome ? 'raw' : 'url'
|
||||
});
|
||||
|
||||
console.log(svgStringWithFontAwesome);
|
||||
return toBase64(svgStringWithFontAwesome);
|
||||
};
|
||||
|
||||
const simulateDownload = (download: string, href: string): void => {
|
||||
@@ -96,16 +147,10 @@
|
||||
};
|
||||
|
||||
const exportImage = async (event: Event, exporter: Exporter) => {
|
||||
$inputStateStore.panZoom = false;
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
await waitForRender();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
const canvas = document.createElement('canvas');
|
||||
const svg = document.querySelector<HTMLElement>('#container svg');
|
||||
if (!svg) {
|
||||
throw new Error('svg not found');
|
||||
}
|
||||
|
||||
const box = svg.getBoundingClientRect();
|
||||
const { svg, box } = await getSvgElement();
|
||||
|
||||
if (imageSizeMode === 'width') {
|
||||
const ratio = box.height / box.width;
|
||||
@@ -132,18 +177,14 @@
|
||||
const image = new Image();
|
||||
image.addEventListener('load', () => {
|
||||
exporter(context, image)();
|
||||
$inputStateStore.panZoom = true;
|
||||
});
|
||||
image.src = `data:image/svg+xml;base64,${await getBase64SVG(svg, canvas.width, canvas.height)}`;
|
||||
image.src = `data:image/svg+xml;base64,${await getBase64SVG({ svg, box, height: canvas.height, width: canvas.width, embedFontAwesome: true })}`;
|
||||
// Fallback to set panZoom to true after 2 seconds
|
||||
// This is a workaround for the case when the image is not loaded
|
||||
setTimeout(() => {
|
||||
if (!$inputStateStore.panZoom) {
|
||||
$inputStateStore.panZoom = true;
|
||||
}
|
||||
}, 2000);
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
// setTimeout(() => {
|
||||
// if (!$inputStateStore.panZoom) {
|
||||
// }
|
||||
// }, 2000);
|
||||
};
|
||||
|
||||
const downloadImage: Exporter = (context, image) => {
|
||||
|
||||
Reference in New Issue
Block a user