@markdy/renderer-dom 0.8.28 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5 -0
- package/dist/index.js +68 -2
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -101,6 +101,11 @@ declare function exportDiagramAsGif(container: HTMLElement, timeline: TimelineCo
|
|
|
101
101
|
* packages/renderer-dom/src/export/png-exporter.ts
|
|
102
102
|
* High-DPI raster PNG export with 2x retina scaling.
|
|
103
103
|
* Zero external dependencies.
|
|
104
|
+
*
|
|
105
|
+
* Fix: Inline all external resources (images, fonts) as base64 data URIs
|
|
106
|
+
* before drawing the SVG to canvas. A foreignObject-wrapped SVG taints the
|
|
107
|
+
* canvas whenever it references any external URL, so every resource must be
|
|
108
|
+
* inlined first.
|
|
104
109
|
*/
|
|
105
110
|
|
|
106
111
|
interface PngExportOptions extends SvgExportOptions {
|
package/dist/index.js
CHANGED
|
@@ -2939,10 +2939,74 @@ ${serializer2.serializeToString(clonedSvg)}`;
|
|
|
2939
2939
|
` + serializer.serializeToString(svg);
|
|
2940
2940
|
}
|
|
2941
2941
|
|
|
2942
|
+
// src/export/inline-resources.ts
|
|
2943
|
+
async function toDataUri(url) {
|
|
2944
|
+
try {
|
|
2945
|
+
const resp = await fetch(url, { mode: "cors", credentials: "same-origin" });
|
|
2946
|
+
if (!resp.ok) return null;
|
|
2947
|
+
const arrayBuffer = await resp.arrayBuffer();
|
|
2948
|
+
const mimeType = resp.headers.get("Content-Type") || "application/octet-stream";
|
|
2949
|
+
const base64 = btoa(
|
|
2950
|
+
new Uint8Array(arrayBuffer).reduce((data, byte) => data + String.fromCharCode(byte), "")
|
|
2951
|
+
);
|
|
2952
|
+
return `data:${mimeType};base64,${base64}`;
|
|
2953
|
+
} catch {
|
|
2954
|
+
return null;
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
async function inlineCssUrls(cssValue) {
|
|
2958
|
+
const urlPattern = /url\(["']?([^"')]+)["']?\)/g;
|
|
2959
|
+
const matches = [];
|
|
2960
|
+
let m;
|
|
2961
|
+
while ((m = urlPattern.exec(cssValue)) !== null) {
|
|
2962
|
+
const src = m[1];
|
|
2963
|
+
if (!src.startsWith("data:")) {
|
|
2964
|
+
matches.push({ full: m[0], src });
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
let result = cssValue;
|
|
2968
|
+
await Promise.all(
|
|
2969
|
+
matches.map(async ({ full, src }) => {
|
|
2970
|
+
const absoluteSrc = new URL(src, document.baseURI).href;
|
|
2971
|
+
const dataUri = await toDataUri(absoluteSrc);
|
|
2972
|
+
if (dataUri) result = result.split(full).join(`url("${dataUri}")`);
|
|
2973
|
+
})
|
|
2974
|
+
);
|
|
2975
|
+
return result;
|
|
2976
|
+
}
|
|
2977
|
+
async function inlineExternalResources(root) {
|
|
2978
|
+
const tasks = [];
|
|
2979
|
+
root.querySelectorAll("img").forEach((img) => {
|
|
2980
|
+
const src = img.getAttribute("src");
|
|
2981
|
+
if (src && !src.startsWith("data:")) {
|
|
2982
|
+
const absoluteSrc = new URL(src, document.baseURI).href;
|
|
2983
|
+
tasks.push(
|
|
2984
|
+
toDataUri(absoluteSrc).then((dataUri) => {
|
|
2985
|
+
if (dataUri) img.setAttribute("src", dataUri);
|
|
2986
|
+
})
|
|
2987
|
+
);
|
|
2988
|
+
}
|
|
2989
|
+
});
|
|
2990
|
+
const allEls = [root, ...Array.from(root.querySelectorAll("*"))];
|
|
2991
|
+
allEls.forEach((el) => {
|
|
2992
|
+
const style = el.getAttribute("style");
|
|
2993
|
+
if (style && style.includes("url(")) {
|
|
2994
|
+
tasks.push(
|
|
2995
|
+
inlineCssUrls(style).then((inlined) => {
|
|
2996
|
+
if (inlined !== style) el.setAttribute("style", inlined);
|
|
2997
|
+
})
|
|
2998
|
+
);
|
|
2999
|
+
}
|
|
3000
|
+
});
|
|
3001
|
+
await Promise.all(tasks);
|
|
3002
|
+
}
|
|
3003
|
+
|
|
2942
3004
|
// src/export/gif-exporter.ts
|
|
2943
3005
|
var nextFrame = () => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));
|
|
2944
3006
|
async function rasterizeFrame(container, pixelRatio, options) {
|
|
2945
|
-
const
|
|
3007
|
+
const cloned = container.cloneNode(true);
|
|
3008
|
+
await inlineExternalResources(cloned);
|
|
3009
|
+
const svg = exportDiagramAsVectorSvg(cloned, options);
|
|
2946
3010
|
const url = URL.createObjectURL(new Blob([svg], { type: "image/svg+xml;charset=utf-8" }));
|
|
2947
3011
|
try {
|
|
2948
3012
|
const image = new Image();
|
|
@@ -2995,8 +3059,10 @@ async function exportDiagramAsGif(container, timeline, options = {}) {
|
|
|
2995
3059
|
|
|
2996
3060
|
// src/export/png-exporter.ts
|
|
2997
3061
|
async function exportDiagramAsPng(containerEl, options = {}) {
|
|
2998
|
-
const svgXml = exportDiagramAsVectorSvg(containerEl, options);
|
|
2999
3062
|
const pixelRatio = options.pixelRatio || 2;
|
|
3063
|
+
const clonedContainer = containerEl.cloneNode(true);
|
|
3064
|
+
await inlineExternalResources(clonedContainer);
|
|
3065
|
+
const svgXml = exportDiagramAsVectorSvg(clonedContainer, options);
|
|
3000
3066
|
const blob = new Blob([svgXml], { type: "image/svg+xml;charset=utf-8" });
|
|
3001
3067
|
const url = URL.createObjectURL(blob);
|
|
3002
3068
|
const img = new Image();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markdy/renderer-dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Browser renderer for diagram-native animated MarkdyScript architecture diagrams, built on the Web Animations API.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -44,14 +44,14 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@markdy/core": "0.
|
|
47
|
+
"@markdy/core": "1.0.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"jsdom": "^29.1.1",
|
|
51
51
|
"tsup": "^8.5.1",
|
|
52
52
|
"typescript": "^5.9.3",
|
|
53
53
|
"vitest": "^4.1.7",
|
|
54
|
-
"@markdy/stdlib-systems": "0.
|
|
54
|
+
"@markdy/stdlib-systems": "1.0.0"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "tsup",
|