diff --git a/packages/viz/src/module/Dockerfile b/packages/viz/src/module/Dockerfile index fa1b18f1..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 -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']" --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/module/pre.js b/packages/viz/src/module/pre.js index f8ebdba2..ef1b8b72 100644 --- a/packages/viz/src/module/pre.js +++ b/packages/viz/src/module/pre.js @@ -1,3 +1,4 @@ Module["agerrMessages"] = []; Module["stderrMessages"] = []; err = text => Module["stderrMessages"].push(text); +Module["preRun"] = () => { ENV.SERVER_NAME = "viz-js" }; diff --git a/packages/viz/src/module/viz.c b/packages/viz/src/module/viz.c index fc335dbd..f0b958db 100644 --- a/packages/viz/src/module/viz.c +++ b/packages/viz/src/module/viz.c @@ -3,6 +3,7 @@ extern int Y_invert; extern unsigned char Reduce; +extern char *Gvimagepath; extern gvplugin_library_t gvplugin_core_LTX_library; extern gvplugin_library_t gvplugin_dot_layout_LTX_library; @@ -25,6 +26,11 @@ void viz_set_reduce(int value) { Reduce = value; } +EMSCRIPTEN_KEEPALIVE +void viz_set_imagepath(char *path) { + Gvimagepath = path; +} + EMSCRIPTEN_KEEPALIVE char *viz_get_graphviz_version() { GVC_t *context = NULL; diff --git a/packages/viz/src/viz.mjs b/packages/viz/src/viz.mjs index 502b5355..d70353b4 100644 --- a/packages/viz/src/viz.mjs +++ b/packages/viz/src/viz.mjs @@ -60,17 +60,30 @@ function withStringPointer(module, graphPointer, value, callbackFn) { module.ccall("viz_string_free", "number", ["number", "number"], [graphPointer, stringPointer]); } -function createImageFiles(module, options) { - if (options.images) { - for (const image of options.images) { - const path = module.PATH.join("/", image.path); - const data = ` +function createImageFiles(module, images) { + if (!images) { + return []; + } + + const base = "/" + performance.now(); + module.ccall("viz_set_imagepath", "number", ["string"], [base]); + + return images.map(image => { + const path = module.PATH.join(base, image.path); + const data = ` `; - module.FS.createPath("/", module.PATH.dirname(path)); - module.FS.writeFile(path, 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); } } @@ -165,13 +178,13 @@ function readObjectInput(module, object, options) { } function renderInput(module, input, options) { - let graphPointer, resultPointer; + let graphPointer, resultPointer, imageFilePaths; try { module["agerrMessages"] = []; module["stderrMessages"] = []; - createImageFiles(module, options); + imageFilePaths = createImageFiles(module, options.images); if (typeof input === "string") { graphPointer = readStringInput(module, input, options); @@ -227,6 +240,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 6b3c3165..7e94acad 100644 --- a/packages/viz/test/render.test.mjs +++ b/packages/viz/test/render.test.mjs @@ -351,5 +351,36 @@ stop errors: [] }); }); + + it("image files are not accessible on subsequent calls to render", function() { + viz.render("graph { a[image=\"test.png\"] }", { + images: [ + { path: "test.png", width: 300, height: 200 } + ] + }); + + const result = viz.render("graph { a[image=\"test.png\"] }", { + images: [ + { path: "another.png", width: 123, height: 456 } + ] + }); + + assert.deepStrictEqual(result, { + "status": "success", + "output": `graph { + graph [bb="0,0,54,36"]; + node [label="\\N"]; + a [height=0.5, + image="test.png", + pos="27,18", + width=0.75]; +} +`, + "errors": [ + { level: "warning", message: "No such file or directory while opening test.png" }, + { level: "warning", message: "No or improper image=\"test.png\" for node \"a\"" } + ] + }); + }); }); });