Skip to content

Commit

Permalink
Support specifying image sizes
Browse files Browse the repository at this point in the history
This adds support for an images option, similar to the previous version.

Sizes of images referenced by the image attribute can be specified using an "images" render option:

  viz.render("graph { a[image="test.png"] }", {
    images: [
      { name: "test.png", width: 300, height: 200 }
    ]
  });

The property "name" is used instead of "path" to match the gvusershape functions in Graphviz.
  • Loading branch information
mdaines committed Feb 14, 2024
1 parent f8198db commit 8c155c5
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 2 deletions.
2 changes: 1 addition & 1 deletion packages/viz/src/module/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 31 additions & 1 deletion packages/viz/src/viz.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="${image.width}" height="${image.height}"></svg>
`;

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)) {
Expand Down Expand Up @@ -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") {
Expand Down Expand Up @@ -211,6 +237,10 @@ function renderInput(module, input, options) {
if (resultPointer) {
module.ccall("free", "number", ["number"], [resultPointer]);
}

if (imageFilePaths) {
removeImageFiles(module, imageFilePaths);
}
}
}

Expand Down
44 changes: 44 additions & 0 deletions packages/viz/test/render.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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: []
});
Expand Down
10 changes: 10 additions & 0 deletions packages/viz/test/types/render-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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" }];
7 changes: 7 additions & 0 deletions packages/viz/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface RenderOptions {
graphAttributes?: Attributes
nodeAttributes?: Attributes
edgeAttributes?: Attributes
images?: ImageSize[]
}

export type RenderResult = SuccessResult | FailureResult
Expand Down Expand Up @@ -89,3 +90,9 @@ interface Subgraph {
edges?: Edge[]
subgraphs?: Subgraph[]
}

interface ImageSize {
name: string,
width: string | number,
height: string | number
}
46 changes: 46 additions & 0 deletions packages/website/src/api/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,20 @@ <h4>UMD Bundle</h4>
<dd>
<p>Sets the default edge attributes. This corresponds the <a href="https://www.graphviz.org/doc/info/command.html#-E"><code>-E</code> Graphviz command-line option</a>.</p>
</dd>

<dt id="viz.RenderOptions.images"><code><b><a href="#viz.RenderOptions.images">images</a></b>?: <a href="#viz.ImageSize">ImageSize</a>[]</code></dt>
<dd>
<p>Image sizes to use when rendering nodes with <code>image</code> attributes.</p>

<p>For example, to indicate to Graphviz that the image <code>test.png</code> has size 300x200:</p>

<pre><code>viz.render("graph { a[image=\"test.png\"] }", {
images: [
{ name: "test.png", width: 300, height: 200 }
]
});
</pre></code>
</dd>
</dl>
</dd>

Expand Down Expand Up @@ -489,6 +503,38 @@ <h4>Attributes, Subgraphs, HTML Labels</h4>
</dd>
</dl>
</dd>

<dt id="viz.ImageSize"><code><b><a href="#viz.ImageSize">ImageSize</a></b></code></dt>
<dd>
<p>Specifies the size of an image used as a node's <code>image</code> attribute.</p>

<p><a href="#viz.ImageSize.width">width</a> and <a href="#viz.ImageSize.height">height</a> 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.</p>

<dl>
<dt id="viz.ImageSize.name"><code><b><a href="#viz.ImageSize.name">name</a></b>: string</code></dt>
<dd>
<p>The name of the image. In addition to filenames, names that look like absolute filesystem paths or URLs can be used.</p>

<ul>
<li><code>"example.png"</code></li>
<li><code>"/images/example.png"</code></li>
<li><code>"http://example.com/image.png"</code></li>
</ul>

<p>Names that look like relative filesystem paths, such as <code>"../example.png"</code>, are not supported.</p>
</dd>

<dt id="viz.ImageSize.width"><code><b><a href="#viz.ImageSize.width">width</a></b>: string | number</code></dt>
<dd>
<p>The width of the image.</p>
</dd>

<dt id="viz.ImageSize.height"><code><b><a href="#viz.ImageSize.height">height</a></b>: string | number</code></dt>
<dd>
<p>The height of the image.</p>
</dd>
</dl>
</dd>
</dl>
</section>
</main>
Expand Down

0 comments on commit 8c155c5

Please sign in to comment.