diff --git a/packages/viz/src/module/Dockerfile b/packages/viz/src/module/Dockerfile index 813532c5..8a0caa42 100644 --- a/packages/viz/src/module/Dockerfile +++ b/packages/viz/src/module/Dockerfile @@ -27,7 +27,7 @@ COPY --from=graphviz "${PREFIX}" "${PREFIX}" COPY viz.c pre.js . RUN mkdir -p "${OUTPUT}" -RUN emcc -Oz ${DEBUG:+-g2} --closure=0 --no-entry -sMODULARIZE=1 -sMINIMAL_RUNTIME=1 -sFILESYSTEM=0 -sASSERTIONS=0 -sALLOW_MEMORY_GROWTH=1 -sENVIRONMENT=web -sEXPORT_KEEPALIVE=1 -sEXPORTED_FUNCTIONS="['_malloc', '_free']" -s EXPORTED_RUNTIME_METHODS="['ccall', 'UTF8ToString', 'lengthBytesUTF8', 'stringToUTF8', 'getValue']" -sINCOMING_MODULE_JS_API="['wasm']" --pre-js pre.js -o "${OUTPUT}/module.mjs" viz.c -I"${PREFIX}/include" -I"${PREFIX}/include/graphviz" -L"${PREFIX}/lib" -L"${PREFIX}/lib/graphviz" -lgvplugin_dot_layout -lgvplugin_neato_layout -lgvplugin_core -lgvc -lpathplan -lcgraph -lxdot -lcdt -lexpat +RUN emcc -Oz ${DEBUG:+-g2} --closure=0 --no-entry -sMODULARIZE=1 -sMINIMAL_RUNTIME=1 -sASSERTIONS=0 -sALLOW_MEMORY_GROWTH=1 -sENVIRONMENT=web -sEXPORT_KEEPALIVE=1 -sEXPORTED_FUNCTIONS="['_malloc', '_free']" -s EXPORTED_RUNTIME_METHODS="['ccall', 'UTF8ToString', 'lengthBytesUTF8', 'stringToUTF8', 'getValue', 'FS', 'PATH']" -sINCOMING_MODULE_JS_API="['wasm']" -sWASM_BIGINT=1 --pre-js pre.js -o "${OUTPUT}/module.mjs" viz.c -I"${PREFIX}/include" -I"${PREFIX}/include/graphviz" -L"${PREFIX}/lib" -L"${PREFIX}/lib/graphviz" -lgvplugin_dot_layout -lgvplugin_neato_layout -lgvplugin_core -lgvc -lpathplan -lcgraph -lxdot -lcdt -lexpat FROM scratch AS export diff --git a/packages/viz/src/viz.mjs b/packages/viz/src/viz.mjs index 7656a11f..78e171a7 100644 --- a/packages/viz/src/viz.mjs +++ b/packages/viz/src/viz.mjs @@ -60,6 +60,30 @@ function withStringPointer(module, graphPointer, value, callbackFn) { module.ccall("viz_string_free", "number", ["number", "number"], [graphPointer, stringPointer]); } +function createImageFiles(module, images) { + if (!images) { + return []; + } + + return images.map(image => { + const path = module.PATH.join("/", image.name); + const data = ` + +`; + + module.FS.createPath("/", module.PATH.dirname(path)); + module.FS.writeFile(path, data); + + return path; + }); +} + +function removeImageFiles(module, imageFilePaths) { + for (const path of imageFilePaths) { + module.FS.unlink(path); + } +} + function setDefaultAttributes(module, graphPointer, data) { if (data.graphAttributes) { for (const [name, value] of Object.entries(data.graphAttributes)) { @@ -151,12 +175,14 @@ function readObjectInput(module, object, options) { } function renderInput(module, input, options) { - let graphPointer, resultPointer; + let graphPointer, resultPointer, imageFilePaths; try { module["agerrMessages"] = []; module["stderrMessages"] = []; + imageFilePaths = createImageFiles(module, options.images); + if (typeof input === "string") { graphPointer = readStringInput(module, input, options); } else if (typeof input === "object") { @@ -211,6 +237,10 @@ function renderInput(module, input, options) { if (resultPointer) { module.ccall("free", "number", ["number"], [resultPointer]); } + + if (imageFilePaths) { + removeImageFiles(module, imageFilePaths); + } } } diff --git a/packages/viz/test/render.test.mjs b/packages/viz/test/render.test.mjs index 43858acf..d8c495fd 100644 --- a/packages/viz/test/render.test.mjs +++ b/packages/viz/test/render.test.mjs @@ -325,6 +325,50 @@ stop pos="99,18", width=0.75]; } +`, + errors: [] + }); + }); + + it("accepts an images option", function() { + const result = viz.render("graph { a[image=\"test.png\"] }", { + images: [ + { name: "test.png", width: 300, height: 200 } + ] + }); + + assert.deepStrictEqual(result, { + status: "success", + output: `graph { + graph [bb="0,0,321.03,214.96"]; + node [label="\\N"]; + a [height=2.9856, + image="test.png", + pos="160.51,107.48", + width=4.4587]; +} +`, + errors: [] + }); + }); + + it("accepts URLs for image names", function() { + const result = viz.render("graph { a[image=\"http://example.com/test.png\"] }", { + images: [ + { name: "http://example.com/test.png", width: 300, height: 200 } + ] + }); + + assert.deepStrictEqual(result, { + status: "success", + output: `graph { + graph [bb="0,0,321.03,214.96"]; + node [label="\\N"]; + a [height=2.9856, + image="http://example.com/test.png", + pos="160.51,107.48", + width=4.4587]; +} `, errors: [] }); diff --git a/packages/viz/test/types/render-options.ts b/packages/viz/test/types/render-options.ts index 5260e281..904c0f35 100644 --- a/packages/viz/test/types/render-options.ts +++ b/packages/viz/test/types/render-options.ts @@ -31,6 +31,10 @@ options.edgeAttributes = { test: true }; +options.images = [{ name: "test.png", width: 300, height: 200 }]; + +options.images = [{ name: "test.png", width: "1cm", height: "1cm" }]; + // @ts-expect-error options.format = false; @@ -45,3 +49,9 @@ options.whatever = 123; // @ts-expect-error options.graphAttributes = { something: { whatever: 123 } }; + +// @ts-expect-error +options.images = [{ path: "test.png" }]; + +// @ts-expect-error +options.images = [{ url: "test.png" }]; diff --git a/packages/viz/types/index.d.ts b/packages/viz/types/index.d.ts index 62a28ffa..ce9a8acf 100644 --- a/packages/viz/types/index.d.ts +++ b/packages/viz/types/index.d.ts @@ -28,6 +28,7 @@ export interface RenderOptions { graphAttributes?: Attributes nodeAttributes?: Attributes edgeAttributes?: Attributes + images?: ImageSize[] } export type RenderResult = SuccessResult | FailureResult @@ -89,3 +90,9 @@ interface Subgraph { edges?: Edge[] subgraphs?: Subgraph[] } + +interface ImageSize { + name: string, + width: string | number, + height: string | number +} diff --git a/packages/website/src/api/index.html b/packages/website/src/api/index.html index 185b2a7b..9dc27797 100644 --- a/packages/website/src/api/index.html +++ b/packages/website/src/api/index.html @@ -186,6 +186,20 @@
Sets the default edge attributes. This corresponds the -E
Graphviz command-line option.
images?: ImageSize[]
Image sizes to use when rendering nodes with image
attributes.
For example, to indicate to Graphviz that the image test.png
has size 300x200:
viz.render("graph { a[image=\"test.png\"] }", {
+ images: [
+ { name: "test.png", width: 300, height: 200 }
+ ]
+});
+
+ ImageSize
Specifies the size of an image used as a node's image
attribute.
width and height may be specified as numbers or strings with units: in, px, pc, pt, cm, or mm. If no units are given or measurements are given as numbers, points (pt) are used.
+ +name: string
The name of the image. In addition to filenames, names that look like absolute filesystem paths or URLs can be used.
+ +"example.png"
"/images/example.png"
"http://example.com/image.png"
Names that look like relative filesystem paths, such as "../example.png"
, are not supported.
width: string | number
The width of the image.
+height: string | number
The height of the image.
+