Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(render): adds postscript output formats (cli only) #131

Merged
merged 3 commits into from
Jan 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,21 @@ This is what `smcat --help` would get you:
```
Usage: smcat [options] [infile]

Write beautiful state charts - https://github.com/sverweij/state-machine-cat

Options:
-V, --version output the version number
-T --output-type <type> svg|dot|smcat|json|ast|scxml|oldsvg|scjson (default: "svg")
-I --input-type <type> smcat|scxml|json (default: "smcat")
-T --output-type <type> svg|ps|ps2|dot|smcat|json|ast|scxml|oldsvg|scjson (default: "svg")
-I --input-type <type> smcat|json|scxml (default: "smcat")
-E --engine <type> dot|circo|fdp|neato|osage|twopi (default: "dot")
-d --direction <dir> top-down|bottom-top|left-right|right-left (default: "top-down")
-o --output-to <file> File to write to. use - for stdout.
--dot-graph-attrs <string> graph attributes to pass to the dot render engine
--dot-node-attrs <string> node attributes to pass to the dot render engine
--dot-edge-attrs <string> edge attributes to pass to the dot render engine
--desugar transform forks and joins into transitions (!experimental!)
--desugar transform pseudo states into transitions (!experimental!)
-l --license Display license and exit
-h, --help output usage information
-h, --help display help for command
```

... so to convert the above chart to `sample.svg`
Expand Down
10 changes: 9 additions & 1 deletion src/cli/normalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,16 @@ function classifyExtension(pString, pExtensionMap, pDefault) {
return pExtensionMap[path.extname(pString)] || pDefault;
}

function outputType2Extension(pOutputType) {
const lExceptions = {
oldsvg: "svg",
oldps2: "ps",
ps2: "ps",
};
return lExceptions[pOutputType] || pOutputType;
}
function deriveOutputFromInput(pInputFrom, pOutputType) {
const lExtension = pOutputType === "oldsvg" ? "svg" : pOutputType;
const lExtension = outputType2Extension(pOutputType);

if (!pInputFrom || "-" === pInputFrom) {
return "-";
Expand Down
3 changes: 3 additions & 0 deletions src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ const ALLOWED_VALUES = Object.freeze({
default: "svg",
values: [
{ name: "svg" },
{ name: "ps" },
{ name: "ps2" },
{ name: "dot" },
{ name: "smcat" },
{ name: "json" },
{ name: "ast" },
{ name: "scxml" },
{ name: "oldsvg" },
{ name: "oldps2" },
{ name: "scjson" },
],
},
Expand Down
12 changes: 12 additions & 0 deletions src/render/dot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Why does this renderer use `#FFFFFF01` instead of `transparent` for transparency?

Typically you'll use the `dot` output to translate it to scalable vector graphics
(svg) with GraphViz. In svg `transparent` isn't a color, though (see issue [#129](https://github.com/sverweij/state-machine-cat/issues/129)).
In web browsers this isn't a problem, but less lenient svg interpreters will
render it as black instead. This is suboptimal.

The obvious alternative is to use `#FFFFFF00` (as svg does allow RGBA), however
GraphViz translates this into `transparent` nonetheless. So to hack around this
the dot renderer uses `01` in the alpha channel in stead (`#FFFFFF01`). GrapViz
nicely translates this to `fill="#ffffff" fill-opacity="0.004"` which is sufficiently
transparent for all practical purposes.
11 changes: 7 additions & 4 deletions src/render/index-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@
const has = require("lodash.has");
const smcat = require("./smcat");
const dot = require("./dot");
const svg = require("./svg/svg-native-dot-with-fallback");
const oldsvg = require("./svg/svg-with-viz-js");
const vector = require("./vector/vector-native-dot-with-fallback");
const oldVector = require("./vector/vector-with-viz-js");
const scjson = require("./scjson");
const scxml = require("./scxml");

module.exports = function getRenderFunction(pOutputType) {
const lOutputtype2Renderfunction = {
smcat,
dot,
svg,
oldsvg,
svg: vector,
ps: vector,
ps2: vector,
oldsvg: oldVector,
oldps2: oldVector,
scjson,
scxml,
};
Expand Down
2 changes: 1 addition & 1 deletion src/render/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
const has = require("lodash.has");
const smcat = require("./smcat");
const dot = require("./dot");
const svg = require("./svg/svg-with-viz-js");
const svg = require("./vector/vector-with-viz-js");
const scjson = require("./scjson");
const scxml = require("./scxml");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { spawnSync } = require("child_process");

const DEFAULT_OPTIONS = {
exec: "dot",
format: "svg",
};

/**
Expand All @@ -21,10 +22,14 @@ function convert(pDot, pOptions) {
...DEFAULT_OPTIONS,
...pOptions,
};
const { stdout, status, error } = spawnSync(lOptions.exec, [`-Tsvg`], {
// cwd: lOptions.workingDirectory,
input: pDot,
});
const { stdout, status, error } = spawnSync(
lOptions.exec,
[`-T${lOptions.format}`],
{
// cwd: lOptions.workingDirectory,
input: pDot,
}
);

// 0: okeleedokelee
// 1: error in the program
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ const wrapAnsi = require("wrap-ansi");
const chalk = require("chalk");
const options = require("../../options");
const ast2dot = require("../dot");
const dotToSvgNative = require("./dot-to-svg-native");
const dotToSvgNative = require("./dot-to-vector-native");

const DEFAULT_INDENT = 2;
const DOGMATIC_CONSOLE_WIDTH = 78;
module.exports = (pAST, pOptions) => {
const lDotProgram = ast2dot(pAST, pOptions);
const lDotOptions = {
engine: options.getOptionValue(pOptions, "engine"),
format: options.getOptionValue(pOptions, "outputType"),
};

if (dotToSvgNative.isAvailable(pOptions)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ const viz = require("viz.js");
const options = require("../../options");
const ast2dot = require("../dot");

const OUTPUT_TYPE2FORMAT = {
oldsvg: "svg",
oldps2: "ps2",
};

module.exports = (pAST, pOptions) =>
viz(ast2dot(pAST, pOptions), {
engine: options.getOptionValue(pOptions, "engine"),
format:
OUTPUT_TYPE2FORMAT[options.getOptionValue(pOptions, "outputType")] ||
"svg",
});
Binary file added test/render/fixtures/000empty.ps
Binary file not shown.
Binary file added test/render/fixtures/100one-state.ps
Binary file not shown.
Binary file not shown.
Binary file added test/render/fixtures/102one-state-with-onentry.ps
Binary file not shown.
Binary file not shown.
Binary file added test/render/fixtures/104one-state-with-onexit.ps
Binary file not shown.
Binary file added test/render/fixtures/105one-state-with-onexits.ps
Binary file not shown.
Binary file not shown.
Binary file added test/render/fixtures/110one-initial-state.ps
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added test/render/fixtures/120one-final-state.ps
Binary file not shown.
Binary file added test/render/fixtures/130one-history-state.ps
Binary file not shown.
Binary file added test/render/fixtures/131one-deep-history-state.ps
Binary file not shown.
Binary file added test/render/fixtures/200more-states.ps
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added test/render/fixtures/400pseudo-state-join.ps
Binary file not shown.
Binary file not shown.
Binary file added test/render/fixtures/401pseudo-state-fork.ps
Binary file not shown.
Binary file not shown.
Binary file added test/render/fixtures/402pseudo-state-junction.ps
Binary file not shown.
Binary file added test/render/fixtures/403pseudo-state-choice.ps
Binary file not shown.
Binary file added test/render/fixtures/403pseudo-state-terminate.ps
Binary file not shown.
Binary file added test/render/fixtures/500hierarchic-states.ps
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added test/render/fixtures/516hierarchical-issue-15.ps
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added test/render/fixtures/600kitchensink.ps
Binary file not shown.
Binary file added test/render/fixtures/601kitchensink-with-notes.ps
Binary file not shown.
Binary file added test/render/fixtures/700-parallel-no-states.ps
Binary file not shown.
Binary file added test/render/fixtures/701-parallel-states-only.ps
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added test/render/fixtures/801-labels-one-state.ps
Binary file not shown.
Binary file added test/render/fixtures/802-colors-toverbal.ps
Binary file not shown.
Binary file added test/render/fixtures/803active-and-inactive.ps
Binary file not shown.
Binary file not shown.
45 changes: 0 additions & 45 deletions test/render/svg/dot-to-svg-native.spec.js

This file was deleted.

72 changes: 72 additions & 0 deletions test/render/vector/dot-to-vector-native.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const chai = require("chai");
const dotToVector = require("../../../src/render/vector/dot-to-vector-native");

const expect = chai.expect;

if (dotToVector.isAvailable()) {
describe("dot-to-svg-native - isAvailable", () => {
it("returns false when the executable isn't available", () => {
expect(
dotToVector.isAvailable({
exec: "this_executable_does_not_exist_in_the_path",
})
).to.equal(false);
});
it("returns true when the executable is available", () => {
expect(
dotToVector.isAvailable({
exec: "dot",
})
).to.equal(true);
});
});

describe("dot-to-vector-native - convert", () => {
it("renders an svg when presented with valid dot when no extra options passed", () => {
expect(dotToVector.convert("digraph { a }")).to.contain("<svg");
expect(dotToVector.convert("digraph { a }")).to.contain("</svg>");
});

it("renders an svg when presented with valid dot when svg is passed as an explicit option", () => {
expect(
dotToVector.convert("digraph { a }", { format: "svg" })
).to.contain("<svg");
expect(
dotToVector.convert("digraph { a }", { format: "svg" })
).to.contain("</svg>");
});

it("renders postscript when presented with valid dot when ps is passed as an option", () => {
expect(dotToVector.convert("digraph { a }", { format: "ps" })).to.contain(
"%!PS-Adobe-3.0"
);
expect(dotToVector.convert("digraph { a }", { format: "ps" })).to.contain(
"%%EOF"
);
});

it("renders postscript when presented with valid dot when pss2 is passed as an option", () => {
expect(
dotToVector.convert("digraph { a }", { format: "ps2" })
).to.contain("%!PS-Adobe-3.0");
expect(
dotToVector.convert("digraph { a }", { format: "ps2" })
).to.contain("%%EOF");
});

it("throws an error when presented with an invalid dot", () => {
expect(() => {
dotToVector.convert("this ain't no dot program");
}).to.throw("Unexpected error occurred. Exit code 1");
});
it("throws an error when it can't find the executable", () => {
expect(() => {
dotToVector.convert("digraph { a }", {
exec: "this_executable_does_not_exist_in_the_path",
});
}).to.throw(
"Error: spawnSync this_executable_does_not_exist_in_the_path ENOENT"
);
});
});
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const chai = require("chai");
const dotToSVG = require("../../../src/render/svg/svg-native-dot-with-fallback");
const dotToSVG = require("../../../src/render/vector/vector-native-dot-with-fallback");

const expect = chai.expect;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const fs = require("fs");
const path = require("path");
const chai = require("chai");
const convert = require("../../../src/render/svg/svg-with-viz-js");
const convert = require("../../../src/render/vector/vector-with-viz-js");

const expect = chai.expect;

Expand All @@ -25,3 +25,20 @@ describe("#ast2svg-with-viz-js - integration - ", () => {
});
});
});

describe("#ast2ps2-with-viz-js - integration - ", () => {
FIXTURE_INPUTS.forEach((pInputFixture) => {
it(`correctly converts ${path.basename(
pInputFixture
)} to postscript`, () => {
const lResult = convert(
JSON.parse(fs.readFileSync(pInputFixture, "utf8")),
{ outputType: "oldps2" }
);

expect(lResult).to.deep.equal(
fs.readFileSync(pInputFixture.replace(/\.json$/g, ".ps"), "utf8")
);
});
});
});
1 change: 1 addition & 0 deletions tools/regenerate_render_fixtures.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ find -X test/parse/fixtures/color-*.smcat -exec bin/smcat -T json {} ";" & \
find -X test/parse/fixtures/no-color-*.smcat -exec bin/smcat -T json {} ";" & \
find -X test/parse/fixtures/no-color-*.smcat -exec bin/smcat -T dot --dot-node-attrs "color=pink" {} ";" & \
find -X test/render/fixtures/*.smcat -exec bin/smcat -T oldsvg {} ";"
find -X test/render/fixtures/*.smcat -exec bin/smcat -T oldps2 {} ";"
mkdir -p test/render/fixtures/scxml
find -X test/render/fixtures/*.scxml -exec bin/smcat -I scxml -T json {} -o {}.re-json ";"
bin/smcat -T json test/parse/fixtures/composite.smcat
Expand Down