From 482f7dc926f27b59533b35f98dce95266704a7eb Mon Sep 17 00:00:00 2001 From: Zebreus Date: Fri, 14 Jul 2023 18:19:09 +0200 Subject: [PATCH] Add symbolator (#1566) Co-authored-by: Guillaume Grossetie --- DOCKERHUB.md | 2 +- README.adoc | 2 +- blockdiag/examples.py | 22 ++++ ci/tasks/update-versions.js | 20 +++- ci/tests/diagrams/component.sv | 16 +++ ci/tests/smoke.js | 111 +++++++++--------- docs/antora.yml | 1 + docs/modules/ROOT/pages/architecture.adoc | 4 + docs/modules/ROOT/pages/diagram-types.adoc | 1 + docs/modules/ROOT/pages/index.adoc | 4 +- docs/modules/setup/pages/configuration.adoc | 1 + docs/modules/setup/pages/diagram-options.adoc | 43 +++++++ docs/modules/setup/pages/install.adoc | 2 + docs/modules/setup/pages/kroki-cli.adoc | 2 +- server/README.adoc | 2 +- server/ops/docker/jdk11-jammy/Dockerfile | 32 +++++ .../src/main/java/io/kroki/server/Server.java | 2 + .../io/kroki/server/service/Symbolator.java | 61 ++++++++++ .../server/service/SymbolatorCommand.java | 80 +++++++++++++ server/src/main/resources/web/hello.html | 2 +- 20 files changed, 342 insertions(+), 68 deletions(-) create mode 100644 ci/tests/diagrams/component.sv create mode 100644 server/src/main/java/io/kroki/server/service/Symbolator.java create mode 100644 server/src/main/java/io/kroki/server/service/SymbolatorCommand.java diff --git a/DOCKERHUB.md b/DOCKERHUB.md index 9ba568fd5..e99240717 100644 --- a/DOCKERHUB.md +++ b/DOCKERHUB.md @@ -2,7 +2,7 @@ Convert plain text diagrams to images ! -Kroki provides a unified API with support for BlockDiag (BlockDiag, SeqDiag, ActDiag, NwDiag, PacketDiag, RackDiag), BPMN, Bytefield, C4 (with PlantUML), D2, DBML, Ditaa, Erd, Excalidraw, GraphViz, Mermaid, Nomnoml, Pikchr, PlantUML, SvgBob, UMLet, Vega, Vega-Lite, WaveDrom... and more to come! +Kroki provides a unified API with support for BlockDiag (BlockDiag, SeqDiag, ActDiag, NwDiag, PacketDiag, RackDiag), BPMN, Bytefield, C4 (with PlantUML), D2, DBML, Ditaa, Erd, Excalidraw, GraphViz, Mermaid, Nomnoml, Pikchr, PlantUML, SvgBob, Symbolator, UMLet, Vega, Vega-Lite, WaveDrom... and more to come! ## Getting Started diff --git a/README.adoc b/README.adoc index a250e8146..62804be3d 100644 --- a/README.adoc +++ b/README.adoc @@ -8,7 +8,7 @@ image:https://github.com/yuzutech/kroki/workflows/CI/badge.svg?branch=master[Git image:https://img.shields.io/badge/zulip-join_chat-brightgreen.svg[Zulip chat, link=https://kroki.zulipchat.com/] endif::[] -{uri-kroki}[Kroki] provides a unified API with support for BlockDiag (BlockDiag, SeqDiag, ActDiag, NwDiag, PacketDiag, RackDiag), BPMN, Bytefield, C4 (with PlantUML), D2, DBML, Diagrams.net (experimental), Ditaa, Erd, Excalidraw, GraphViz, Mermaid, Nomnoml, Pikchr, PlantUML, SvgBob, UMLet, Vega, Vega-Lite, WaveDrom and WireViz... and more to come! +{uri-kroki}[Kroki] provides a unified API with support for BlockDiag (BlockDiag, SeqDiag, ActDiag, NwDiag, PacketDiag, RackDiag), BPMN, Bytefield, C4 (with PlantUML), D2, DBML, Diagrams.net (experimental), Ditaa, Erd, Excalidraw, GraphViz, Mermaid, Nomnoml, Pikchr, PlantUML, SvgBob, Symbolator, UMLet, Vega, Vega-Lite, WaveDrom and WireViz... and more to come! == Quickstart diff --git a/blockdiag/examples.py b/blockdiag/examples.py index 91e7057e3..b637afdb0 100644 --- a/blockdiag/examples.py +++ b/blockdiag/examples.py @@ -393,6 +393,28 @@ def sample(service, source): sample('ditaa', ditaa) +# Symbolator +section('Symbolator') +symbolator = """ +module demo_device #( + //# {{}} + parameter SIZE = 8, + parameter RESET_ACTIVE_LEVEL = 1 +) ( + //# {{clocks|Clocking}} + input wire clock, + //# {{control|Control signals}} + input wire reset, + input wire enable, + //# {{data|Data ports}} + input wire [SIZE-1:0] data_in, + output wire [SIZE-1:0] data_out +); + // ... +endmodule +""" + +sample('symbolator', symbolator) section('C4 PlantUML') context_bigbank = """ diff --git a/ci/tasks/update-versions.js b/ci/tasks/update-versions.js index a84434e10..b96798da8 100755 --- a/ci/tasks/update-versions.js +++ b/ci/tasks/update-versions.js @@ -14,14 +14,14 @@ const krokiServicePath = ospath.join(rootDir, 'server', 'src', 'main', 'java', ' const diagramLibraryVersions = {} -function getDependencyVersion (pkg, name) { +function getDependencyVersion(pkg, name) { if (name in pkg.dependencies) { return pkg.dependencies[name] } return pkg.devDependencies[name] } -async function updateServiceGetVersion (javaServiceFileName, version) { +async function updateServiceGetVersion(javaServiceFileName, version) { const servicePath = ospath.join(krokiServicePath, javaServiceFileName) const javaContent = await fs.readFile(servicePath, 'utf8') const versionFound = javaContent.match(/(?<= +public String getVersion\(\) {\n\s+return ")(?[0-9.]+)(?=";\n\s+})/) @@ -34,7 +34,7 @@ async function updateServiceGetVersion (javaServiceFileName, version) { } } -async function updateVegaServiceGetVersion (vegaVersion, vegaLiteVersion) { +async function updateVegaServiceGetVersion(vegaVersion, vegaLiteVersion) { // update vega version const servicePath = ospath.join(krokiServicePath, 'Vega.java') let javaContent = await fs.readFile(servicePath, 'utf8') @@ -56,7 +56,7 @@ async function updateVegaServiceGetVersion (vegaVersion, vegaLiteVersion) { await fs.writeFile(servicePath, javaContent, 'utf8') } -async function mvnEvaluateExpression (expression, pomRootRelativePath) { +async function mvnEvaluateExpression(expression, pomRootRelativePath) { if (pomRootRelativePath === undefined) { pomRootRelativePath = ospath.join('server', 'pom.xml') } @@ -83,7 +83,7 @@ async function mvnEvaluateExpression (expression, pomRootRelativePath) { }) } -function addDiagramLibraryPackageVersion (diagramName, dependencyName, directoryName) { +function addDiagramLibraryPackageVersion(diagramName, dependencyName, directoryName) { if (directoryName === undefined) { directoryName = diagramName } @@ -118,6 +118,7 @@ const diagramLibraryNames = [ 'seqdiag', 'structurizr', 'svgbob', + 'symbolator', 'umlet', 'vega', 'vegalite', @@ -187,6 +188,12 @@ try { const { version } = pikchrVersionFound.groups diagramLibraryVersions.pikchr = version.slice(0, 10) } + + const symbolatorVersionFound = line.match(/^ARG SYMBOLATOR_VERSION=(?.+)$/) + if (symbolatorVersionFound) { + const { version } = symbolatorVersionFound.groups + diagramLibraryVersions.symbolator = version + } const umletVersionFound = line.match(/^ARG UMLET_VERSION="(?.+)"$/) if (umletVersionFound) { const { version } = umletVersionFound.groups @@ -234,7 +241,7 @@ try { for (let line of antoraComponentContent.split('\n')) { const found = line.match(/^\s+(?[a-z0-9]+)-version: '?(?[^']+)'?$/) if (found) { - const { name, version : versionFound } = found.groups + const { name, version: versionFound } = found.groups const version = diagramLibraryVersions[name] if (versionFound !== version) { line = line.replace(/(?<=^\s+(?[a-z0-9]+)-version: '?)(?[^']+)/, version) @@ -258,6 +265,7 @@ try { await updateServiceGetVersion('Plantuml.java', diagramLibraryVersions.plantuml) await updateServiceGetVersion('Structurizr.java', diagramLibraryVersions.structurizr) await updateServiceGetVersion('Svgbob.java', diagramLibraryVersions.svgbob) + await updateServiceGetVersion('Symbolator.java', diagramLibraryVersions.symbolator) await updateServiceGetVersion('Umlet.java', diagramLibraryVersions.umlet) await updateServiceGetVersion('Wavedrom.java', diagramLibraryVersions.wavedrom) await updateServiceGetVersion('Wireviz.java', diagramLibraryVersions.wireviz) diff --git a/ci/tests/diagrams/component.sv b/ci/tests/diagrams/component.sv new file mode 100644 index 000000000..ec6cec7d9 --- /dev/null +++ b/ci/tests/diagrams/component.sv @@ -0,0 +1,16 @@ +module demo_device #( + //# {{}} + parameter SIZE = 8, + parameter RESET_ACTIVE_LEVEL = 1 +) ( + //# {{clocks|Clocking}} + input wire clock, + //# {{control|Control signals}} + input wire reset, + input wire enable, + //# {{data|Data ports}} + input wire [SIZE-1:0] data_in, + output wire [SIZE-1:0] data_out +); + // ... +endmodule \ No newline at end of file diff --git a/ci/tests/smoke.js b/ci/tests/smoke.js index 503442b17..40d1edd81 100755 --- a/ci/tests/smoke.js +++ b/ci/tests/smoke.js @@ -5,41 +5,42 @@ import * as url from 'url' const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) const tests = [ - {engine: 'graphviz', file: 'hello.dot', options: {}, outputFormat: ['svg', 'jpeg']}, - {engine: 'dot', file: 'hello.dot', options: {}, outputFormat: ['svg', 'jpeg']}, - {engine: 'blockdiag', file: 'kroki.diag', options: {}, outputFormat: ['svg', 'png']}, - {engine: 'seqdiag', file: 'sequence.diag', options: {}, outputFormat: ['svg', 'png']}, - {engine: 'actdiag', file: 'actions.diag', options: {}, outputFormat: ['svg', 'png']}, - {engine: 'nwdiag', file: 'network.diag', options: {}, outputFormat: ['svg', 'png']}, - {engine: 'c4plantuml', file: 'banking-system.puml', options: {}, outputFormat: ['svg', 'pdf', 'png', 'txt']}, - {engine: 'dbml', file: 'dbml.dbml', options: {}, outputFormat: ['svg']}, - {engine: 'ditaa', file: 'components.ditaa', options: {}, outputFormat: ['svg']}, - {engine: 'erd', file: 'schema.erd', options: {}, outputFormat: ['svg']}, - {engine: 'mermaid', file: 'contribute.mmd', options: {}, outputFormat: ['svg']}, - {engine: 'bpmn', file: 'example.bpmn', options: {}, outputFormat: ['svg']}, - {engine: 'plantuml', file: 'architecture.puml', options: {}, outputFormat: ['svg', 'pdf', 'png', 'txt']}, - {engine: 'svgbob', file: 'cloud.bob', options: {}, outputFormat: ['svg']}, - {engine: 'nomnoml', file: 'pirate.nomnoml', options: {}, outputFormat: ['svg']}, - {engine: 'packetdiag', file: 'packet.diag', options: {}, outputFormat: ['svg', 'png']}, - {engine: 'rackdiag', file: 'rack.diag', options: {}, outputFormat: ['svg', 'png']}, - {engine: 'vega', file: 'bar-chart.vega', options: {}, outputFormat: ['svg', 'png', 'pdf']}, - {engine: 'vegalite', file: 'discretizing-scale.vlite', options: {}, outputFormat: ['svg', 'png', 'pdf']}, - {engine: 'wavedrom', file: 'wavedrom.json5', options: {}, outputFormat: ['svg']}, - {engine: 'wavedrom', file: 'bitfield.json5', options: {}, outputFormat: ['svg']}, - {engine: 'bytefield', file: 'bytefield.bf', options: {}, outputFormat: ['svg']}, - {engine: 'umlet', file: 'umlet.xml', options: {}, outputFormat: ['svg']}, - {engine: 'excalidraw', file: 'venn.excalidraw', options: {}, outputFormat: ['svg']}, - {engine: 'pikchr', file: 'sqlite-architecture.pikchr', options: {}, outputFormat: ['svg']}, - {engine: 'structurizr', file: 'gettingstarted.structurizr', options: {}, outputFormat: ['svg']}, - {engine: 'diagramsnet', file: 'diagramsnet-infography.xml', options: {}, outputFormat: ['svg', 'png']}, - {engine: 'diagramsnet', file: 'diagramsnet-mindmap.xml', options: {}, outputFormat: ['svg', 'png']}, - {engine: 'diagramsnet', file: 'diagramsnet-network.xml', options: {}, outputFormat: ['svg', 'png']}, - {engine: 'diagramsnet', file: 'diagramsnet-ui.xml', options: {}, outputFormat: ['svg', 'png']}, - {engine: 'diagramsnet', file: 'diagramsnet-venn.xml', options: {}, outputFormat: ['svg', 'png']}, - {engine: 'd2', file: 'connections.d2', options: {}, outputFormat: ['svg']}, - {engine: 'd2', file: 'connections.d2', options: {sketch: 'true'}, outputFormat: ['svg']}, - {engine: 'wireviz', file: 'wireviz.yaml', options: {}, outputFormat: ['svg', 'png']}, - {engine: 'tikz', file: 'periodic-table.tex', options: {}, outputFormat: ['jpeg', 'pdf', 'png', 'svg']} + { engine: 'graphviz', file: 'hello.dot', options: {}, outputFormat: ['svg', 'jpeg'] }, + { engine: 'dot', file: 'hello.dot', options: {}, outputFormat: ['svg', 'jpeg'] }, + { engine: 'blockdiag', file: 'kroki.diag', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'seqdiag', file: 'sequence.diag', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'actdiag', file: 'actions.diag', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'nwdiag', file: 'network.diag', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'c4plantuml', file: 'banking-system.puml', options: {}, outputFormat: ['svg', 'pdf', 'png', 'txt'] }, + { engine: 'dbml', file: 'dbml.dbml', options: {}, outputFormat: ['svg'] }, + { engine: 'ditaa', file: 'components.ditaa', options: {}, outputFormat: ['svg'] }, + { engine: 'symbolator', file: 'component.sv', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'erd', file: 'schema.erd', options: {}, outputFormat: ['svg'] }, + { engine: 'mermaid', file: 'contribute.mmd', options: {}, outputFormat: ['svg'] }, + { engine: 'bpmn', file: 'example.bpmn', options: {}, outputFormat: ['svg'] }, + { engine: 'plantuml', file: 'architecture.puml', options: {}, outputFormat: ['svg', 'pdf', 'png', 'txt'] }, + { engine: 'svgbob', file: 'cloud.bob', options: {}, outputFormat: ['svg'] }, + { engine: 'nomnoml', file: 'pirate.nomnoml', options: {}, outputFormat: ['svg'] }, + { engine: 'packetdiag', file: 'packet.diag', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'rackdiag', file: 'rack.diag', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'vega', file: 'bar-chart.vega', options: {}, outputFormat: ['svg', 'png', 'pdf'] }, + { engine: 'vegalite', file: 'discretizing-scale.vlite', options: {}, outputFormat: ['svg', 'png', 'pdf'] }, + { engine: 'wavedrom', file: 'wavedrom.json5', options: {}, outputFormat: ['svg'] }, + { engine: 'wavedrom', file: 'bitfield.json5', options: {}, outputFormat: ['svg'] }, + { engine: 'bytefield', file: 'bytefield.bf', options: {}, outputFormat: ['svg'] }, + { engine: 'umlet', file: 'umlet.xml', options: {}, outputFormat: ['svg'] }, + { engine: 'excalidraw', file: 'venn.excalidraw', options: {}, outputFormat: ['svg'] }, + { engine: 'pikchr', file: 'sqlite-architecture.pikchr', options: {}, outputFormat: ['svg'] }, + { engine: 'structurizr', file: 'gettingstarted.structurizr', options: {}, outputFormat: ['svg'] }, + { engine: 'diagramsnet', file: 'diagramsnet-infography.xml', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'diagramsnet', file: 'diagramsnet-mindmap.xml', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'diagramsnet', file: 'diagramsnet-network.xml', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'diagramsnet', file: 'diagramsnet-ui.xml', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'diagramsnet', file: 'diagramsnet-venn.xml', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'd2', file: 'connections.d2', options: {}, outputFormat: ['svg'] }, + { engine: 'd2', file: 'connections.d2', options: { sketch: 'true' }, outputFormat: ['svg'] }, + { engine: 'wireviz', file: 'wireviz.yaml', options: {}, outputFormat: ['svg', 'png'] }, + { engine: 'tikz', file: 'periodic-table.tex', options: {}, outputFormat: ['jpeg', 'pdf', 'png', 'svg'] } ] chai.use(chaiHttp) @@ -95,7 +96,7 @@ describe('Diagrams', function () { describe('PlantUML native image', function () { this.timeout(15000) it('plantuml (native image) should convert class diagram (issue#1546)', async () => { - const testCase = {engine: 'plantuml', file: 'class.puml'} + const testCase = { engine: 'plantuml', file: 'class.puml' } const response = await sendRequest(testCase, 'svg') try { expect(response.status).to.equal(200) @@ -109,7 +110,7 @@ describe('PlantUML native image', function () { describe('CJK font', function () { this.timeout(15000) it('plantuml should compute correct text length (issue#574)', async () => { - const testCase = {engine: 'plantuml', file: 'chinese.puml'} + const testCase = { engine: 'plantuml', file: 'chinese.puml' } const response = await sendRequest(testCase, 'svg') try { expect(response.body.toString('utf8')).to.include('textLength="56"') @@ -119,7 +120,7 @@ describe('CJK font', function () { } }) it('mermaid should compute correct text length (issue#1167)', async () => { - const testCase = {engine: 'mermaid', file: 'japanese.mermaid'} + const testCase = { engine: 'mermaid', file: 'japanese.mermaid' } const response = await sendRequest(testCase, 'svg') try { const data = response.body.toString('utf8') @@ -137,23 +138,23 @@ describe('CJK font', function () { describe('Health', function () { this.timeout(15000) - ;['/health', '/healthz', '/v1/health'].forEach((endpoint) => { - it(`should return health status from ${endpoint}`, async () => { - const response = await chai.request('localhost:8000') - .get(endpoint) - .set('Accept', 'application/health+json') - .send() + ;['/health', '/healthz', '/v1/health'].forEach((endpoint) => { + it(`should return health status from ${endpoint}`, async () => { + const response = await chai.request('localhost:8000') + .get(endpoint) + .set('Accept', 'application/health+json') + .send() - try { - expect(response.status).to.equal(200) - expect(response.body.status).to.equal('pass') - const engines = Array.from(new Set(tests.map((it) => it.engine))) - engines.push('kroki') - expect(response.body.version).to.have.keys(engines) - } catch (err) { - console.log('response:', response.text) - throw err - } + try { + expect(response.status).to.equal(200) + expect(response.body.status).to.equal('pass') + const engines = Array.from(new Set(tests.map((it) => it.engine))) + engines.push('kroki') + expect(response.body.version).to.have.keys(engines) + } catch (err) { + console.log('response:', response.text) + throw err + } + }) }) - }) }) diff --git a/docs/antora.yml b/docs/antora.yml index 45ea9a9c4..d2178c5f5 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -25,6 +25,7 @@ asciidoc: seqdiag-version: 3.0.0 structurizr-version: 1.30.1 svgbob-version: 0.7.0 + symbolator-version: 1.2.2 umlet-version: 15.1 vega-version: 5.25.0 vegalite-version: 5.9.3 diff --git a/docs/modules/ROOT/pages/architecture.adoc b/docs/modules/ROOT/pages/architecture.adoc index 530b2749d..cfedfb10f 100644 --- a/docs/modules/ROOT/pages/architecture.adoc +++ b/docs/modules/ROOT/pages/architecture.adoc @@ -69,6 +69,10 @@ The `yuzutech/kroki` Docker image contains the following diagrams libraries out- |{svgbob-version} //|Binary `/rust/bin/svgbob` +|https://hdl.github.io/symbolator/[Symbolator] +|{symbolator-version} +//|Binary `/usr/bin/symbolator` + |https://github.com/umlet/umlet[UMlet] |{umlet-version} //|Java library diff --git a/docs/modules/ROOT/pages/diagram-types.adoc b/docs/modules/ROOT/pages/diagram-types.adoc index 894660405..73e5dd970 100644 --- a/docs/modules/ROOT/pages/diagram-types.adoc +++ b/docs/modules/ROOT/pages/diagram-types.adoc @@ -38,6 +38,7 @@ Diagram:: * Digital Timing diagram (waveform) * BPMN diagram * Bytefield diagram +* HDL Component diagram * Excalidraw * https://www.diagrams.net/[diagrams.net] (experimental) * WireViz diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 73af09110..80edbe473 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -5,8 +5,8 @@ === Convert plain text diagrams to images ! Kroki provides a unified HTTP API with support for BlockDiag (BlockDiag, SeqDiag, ActDiag, NwDiag, PacketDiag, RackDiag), -BPMN, Bytefield, C4 (with PlantUML), Diagrams.net (experimental), Ditaa, Erd, Excalidraw, GraphViz, Mermaid, Nomnoml, PlantUML, Structurizr, SvgBob, UMLet, -Vega, Vega-Lite, WaveDrom and WireViz... and more to come! +BPMN, Bytefield, C4 (with PlantUML), Diagrams.net (experimental), Ditaa, Erd, Excalidraw, GraphViz, Mermaid, Nomnoml, PlantUML, Structurizr, SvgBob, +Symbolator, UMLet, Vega, Vega-Lite, WaveDrom and WireViz... and more to come! Kroki is available as a Self-Managed instance. + We are also _actively_ looking for sponsors to provide Kroki as a free service: diff --git a/docs/modules/setup/pages/configuration.adoc b/docs/modules/setup/pages/configuration.adoc index a23a643b3..ccb138eab 100644 --- a/docs/modules/setup/pages/configuration.adoc +++ b/docs/modules/setup/pages/configuration.adoc @@ -117,6 +117,7 @@ you can override its location manually using an environment variable or a Java s `KROKI_PIKCHR_BIN_PATH`:: Path to `pikchr` binary (defaults: `/usr/bin/pikchr`) `KROKI_PLANTUML_BIN_PATH`:: Path to `plantuml` binary (defaults: `/usr/bin/plantuml`) `KROKI_SVGBOB_BIN_PATH`:: Path to `svgbob` binary (defaults: `/usr/bin/svgbob`) +`KROKI_SYMBOLATOR_BIN_PATH`:: Path to `symbolator` binary (defaults: `/usr/bin/symbolator`) `KROKI_TIKZ2SVG_BIN_PATH`:: Path to `tikz2svg` binary (defaults: `/usr/bin/tikz2svg`) `KROKI_UMLET_BIN_PATH`:: Path to `umlet` binary (defaults: `/usr/bin/umlet`) `KROKI_VEGA_BIN_PATH`:: Path to `vega` binary which supports both Vega and Vega-Lite grammar (defaults: `/usr/bin/bytefield`) diff --git a/docs/modules/setup/pages/diagram-options.adoc b/docs/modules/setup/pages/diagram-options.adoc index 4f0bbe903..a808e3911 100644 --- a/docs/modules/setup/pages/diagram-options.adoc +++ b/docs/modules/setup/pages/diagram-options.adoc @@ -232,3 +232,46 @@ The complete list of options is available in Mermaid source code at: https://git |=== + +== Symbolator + +[cols="1m,1a,2a",opts="header"] +|=== +|Name +|Allowable Values +|Description + +|component +|_string_ + +empty string ("") +|Select the component that will be rendered + +If not set, the last component will be rendered + +|transparent +|_flag_ + +empty string ("") +|Background will be transparent instead of white + +With flag set, option takes effect. + +|title +|_string_ + +empty string ("") +|This title will be inserted into the diagram + +|scale +|_number_ + +*`1.0`* +|Diagram will be scaled by this factor + +|no-type +|_flag_ + +empty string ("") +|Omit type information for the ports + +|library-name +|_string_ + +empty string ("") +|Add name of a library to the diagram. + +Only works if title is also set + +|=== \ No newline at end of file diff --git a/docs/modules/setup/pages/install.adoc b/docs/modules/setup/pages/install.adoc index 2d5d82efe..77f712448 100644 --- a/docs/modules/setup/pages/install.adoc +++ b/docs/modules/setup/pages/install.adoc @@ -83,6 +83,7 @@ The {uri-docker-kroki-image}[yuzutech/kroki] image contains the gateway server, * https://github.com/plantuml/plantuml[PlantUML] including https://github.com/RicardoNiepel/C4-PlantUML[C4 model] * https://github.com/structurizr/dsl[Structurizr] * https://github.com/ivanceras/svgbob[Svgbob] +* https://github.com/hdl/symbolator[Symbolator] * https://github.com/umlet/umlet[UMlet] * https://github.com/vega/vega[Vega] * https://github.com/vega/vega-lite[Vega-Lite] @@ -104,6 +105,7 @@ In other words, the following endpoints will be available: /plantuml /structurizr /svgbob +/symbolator /umlet /vega /vegalite diff --git a/docs/modules/setup/pages/kroki-cli.adoc b/docs/modules/setup/pages/kroki-cli.adoc index 68b57d417..7af91ff50 100644 --- a/docs/modules/setup/pages/kroki-cli.adoc +++ b/docs/modules/setup/pages/kroki-cli.adoc @@ -80,7 +80,7 @@ kroki [options] |Infer from input file name. |-t, --type -|Diagram type: _actdiag_, _blockdiag_, _c4plantuml_, _ditaa_, _dot_, _erd_, _graphviz_, _nomnoml_, _nwdiag_, _plantuml_, _seqdiag_, _svgbob_, _umlet_, _vega_, _vegalite_. +|Diagram type: _actdiag_, _blockdiag_, _c4plantuml_, _ditaa_, _dot_, _erd_, _graphviz_, _nomnoml_, _nwdiag_, _plantuml_, _seqdiag_, _svgbob_, _symbolator_, _umlet_, _vega_, _vegalite_. |String |Infer from input file extension. diff --git a/server/README.adoc b/server/README.adoc index d12e3f84c..aa0814585 100644 --- a/server/README.adoc +++ b/server/README.adoc @@ -34,4 +34,4 @@ When a new version is available, we need to: . update the fork . run the following workflow: https://github.com/yuzutech/umlet/actions/workflows/release.yml -. update the argument variable `ARG UMLET_VERSION="x.y.z"` in `server/ops/docker/jdk11-jammy/Dockerfile` +. update the argument variable `ARG UMLET_VERSION="x.y.z"` in `server/ops/docker/jdk11-jammy/Dockerfile` \ No newline at end of file diff --git a/server/ops/docker/jdk11-jammy/Dockerfile b/server/ops/docker/jdk11-jammy/Dockerfile index 65fbe2ed4..55849fe96 100644 --- a/server/ops/docker/jdk11-jammy/Dockerfile +++ b/server/ops/docker/jdk11-jammy/Dockerfile @@ -153,6 +153,35 @@ FROM yuzutech/kroki-builder-erd:0.2.1.0 AS kroki-builder-static-erd # --ghc-options="-optc=-static" \ # --ghc-options="-w" +## Symbolator +FROM ubuntu:jammy AS kroki-builder-static-symbolator + +# Build from forked source because upstream is broken for the latest python3 versions +ARG SYMBOLATOR_VERSION=1.2.2 +ARG SYMBOLATOR_SOURCE=git+https://github.com/zebreus/symbolator.git@v$SYMBOLATOR_VERSION + +RUN apt-get update && apt-get install --no-install-recommends --yes \ + git \ + pip \ + python3-dev \ + patchelf \ + python3-gi-cairo \ + python3-gi \ + build-essential \ + libpango1.0-dev && \ + apt-get clean && apt-get autoremove + +WORKDIR /build + +# Install latest pip and setuptools +RUN python3 -m pip install --upgrade pip setuptools +RUN python3 -m pip install --upgrade nuitka +# Install symbolator +RUN python3 -m pip install --upgrade ${SYMBOLATOR_SOURCE} + +# Use nuitka to compile a static binary so we dont need python in the final image +RUN python3 -m nuitka --onefile `which symbolator` --include-module=gi.overrides.Pango --include-module=gi._gi_cairo + ## Pikchr FROM ubuntu:jammy AS kroki-builder-static-pikchr @@ -234,6 +263,7 @@ RUN apt-get update && apt-get install --no-install-recommends --yes \ libpangocairo-1.0 \ libgd3 \ libjpeg9 \ + libpango1.0-0 \ giflib-tools \ fonts-freefont-ttf \ fonts-noto-cjk \ @@ -271,6 +301,7 @@ COPY --from=kroki-builder-nomnoml /app/app.bin /usr/bin/nomnoml COPY --from=kroki-builder-vega /app/app.bin /usr/bin/vega COPY --from=kroki-builder-dbml /app/app.bin /usr/bin/dbml COPY --from=kroki-builder-wavedrom /app/app.bin /usr/bin/wavedrom +COPY --from=kroki-builder-static-symbolator /build/symbolator.bin /usr/bin/symbolator COPY --from=kroki-builder-bytefield /app/app.bin /usr/bin/bytefield COPY --from=kroki-builder-dvisvgm /usr/local/bin/dvisvgm /usr/bin/dvisvgm COPY --from=tikz tikz2svg /usr/bin/tikz2svg @@ -293,6 +324,7 @@ ENV KROKI_UMLET_BIN_PATH=/usr/bin/umlet ENV KROKI_PLANTUML_BIN_PATH=/usr/bin/plantuml ENV KROKI_TIKZ2SVG_BIN_PATH=/usr/bin/tikz2svg ENV KROKI_DITAA_BIN_PATH=/usr/bin/ditaa +ENV KROKI_SYMBOLATOR_BIN_PATH=/usr/bin/symbolator ENV JAVA_OPTS="-Dlogback.configurationFile=/etc/kroki/logback.xml -Dvertx.logger-delegate-factory-class-name=io.vertx.core.logging.SLF4JLogDelegateFactory" COPY --chown=kroki:kroki target/kroki-server.jar /usr/local/kroki/kroki-server.jar diff --git a/server/src/main/java/io/kroki/server/Server.java b/server/src/main/java/io/kroki/server/Server.java index d29daef36..51e234517 100644 --- a/server/src/main/java/io/kroki/server/Server.java +++ b/server/src/main/java/io/kroki/server/Server.java @@ -26,6 +26,7 @@ import io.kroki.server.service.ServiceVersion; import io.kroki.server.service.Structurizr; import io.kroki.server.service.Svgbob; +import io.kroki.server.service.Symbolator; import io.kroki.server.service.Umlet; import io.kroki.server.service.Vega; import io.kroki.server.service.Wavedrom; @@ -127,6 +128,7 @@ static void start(Vertx vertx, JsonObject config, Handler SUPPORTED_FORMATS = Arrays.asList(FileFormat.SVG, FileFormat.PNG); + private final SourceDecoder sourceDecoder; + private final Vertx vertx; + private final SymbolatorCommand command; + + public Symbolator(Vertx vertx, JsonObject config) { + this.vertx = vertx; + this.command = new SymbolatorCommand(config); + this.sourceDecoder = new SourceDecoder() { + @Override + public String decode(String encoded) throws DecodeException { + return DiagramSource.decode(encoded, false); + } + }; + } + + @Override + public List getSupportedFormats() { + return SUPPORTED_FORMATS; + } + + @Override + public SourceDecoder getSourceDecoder() { + return sourceDecoder; + } + + @Override + public String getVersion() { + return "1.2.2"; + } + + @Override + public void convert(String sourceDecoded, String serviceName, FileFormat fileFormat, JsonObject options, Handler> handler) { + vertx.executeBlocking(future -> { + try { + byte[] result = this.command.convert(sourceDecoded, fileFormat, options); + future.complete(result); + } catch (IllegalStateException | InterruptedException | IOException e) { + future.fail(e); + } + }, res -> handler.handle(res.map(o -> Buffer.buffer((byte[]) o)))); + } +} diff --git a/server/src/main/java/io/kroki/server/service/SymbolatorCommand.java b/server/src/main/java/io/kroki/server/service/SymbolatorCommand.java new file mode 100644 index 000000000..2f537eab6 --- /dev/null +++ b/server/src/main/java/io/kroki/server/service/SymbolatorCommand.java @@ -0,0 +1,80 @@ +package io.kroki.server.service; + +import io.kroki.server.action.Commander; +import io.kroki.server.format.FileFormat; +import io.vertx.core.json.JsonObject; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class SymbolatorCommand { + + private final String binPath; + private final Commander commander; + + public SymbolatorCommand(JsonObject config) { + this.binPath = config.getString("KROKI_SYMBOLATOR_BIN_PATH", "symbolator"); + this.commander = new Commander(config); + } + + public byte[] convert(String source, FileFormat format, JsonObject options) throws IOException, InterruptedException { + List commands = new ArrayList<>(); + commands.add(binPath); + // Set input to stdin + commands.add("--input"); + commands.add("-"); + // Set output to stdout + commands.add("--output"); + commands.add("/dev/stdout"); + // If this is not set --output is treated as a directory + commands.add("--output-as-filename"); + // ## Some notes on how symbolator selects a output format + // + // Format does not actually set the format but just the backend + // If format is svg the svg backend is used. In this case the output is always svg + // + // If format is unset or not svg the cairo backend is used. In this case the format depends on the output file extension. svg, pdf and eps use the cairo backends for their filetypes. Every other extension will be a png. + // Because of this `--output-as-filename --output file.svg --format svg` will create a different looking svg file from just `--output-as-filename --output file.svg` + // We cant change the filename because we want the file in stdout. Maybe we could create a symlink with the correct extension to stdout and use that as the filename? + // + // The current behaviour will produce a slightly different image depending on the format. This may even be considered a feature. + // It would be nice to also have a way to get a svg from cairo. + // We currently cannot output pdf and eps because we cant change the extension. + commands.add("--format"); + commands.add(format.getName()); + // Use a transparent background instead of white + String transparent = options.getString("transparent"); + if (transparent != null) { + commands.add("--transparent"); + } + // If set this title will be inserted into the diagram + String title = options.getString("title"); + if (title != null) { + commands.add("--title"); + commands.add(title); + } + // Select the scale of the diagram. The default is 1.0. + String scale = options.getString("scale"); + if (scale != null) { + commands.add("--scale"); + commands.add(scale); + } + // Select which component to render. The default is the last one I think. + String component = options.getString("component"); + if (component != null) { + commands.add("--component"); + commands.add(component); + } + String omitTypeInformation = options.getString("no-type"); + if (omitTypeInformation != null) { + commands.add("--no-type"); + } + String libname = options.getString("library-name"); + if (libname != null) { + commands.add("--libname "); + commands.add(libname); + } + return commander.execute(source.getBytes(), commands.toArray(new String[0])); + } +} diff --git a/server/src/main/resources/web/hello.html b/server/src/main/resources/web/hello.html index 4a010f2eb..a4975004e 100644 --- a/server/src/main/resources/web/hello.html +++ b/server/src/main/resources/web/hello.html @@ -13,7 +13,7 @@

Creates diagrams from textual descriptions!

- Kroki provides a unified API with support for BlockDiag (BlockDiag, SeqDiag, ActDiag, NwDiag, PacketDiag, RackDiag), BPMN, Bytefield, C4 (with PlantUML), D2, DBML, Diagrams.net (experimental), Ditaa, Erd, Excalidraw, GraphViz, Mermaid, Nomnoml, Pikchr, PlantUML, Structurizr, SvgBob, UMLet, Vega, Vega-Lite, WaveDrom and WireViz... and more to come! + Kroki provides a unified API with support for BlockDiag (BlockDiag, SeqDiag, ActDiag, NwDiag, PacketDiag, RackDiag), BPMN, Bytefield, C4 (with PlantUML), D2, DBML, Diagrams.net (experimental), Ditaa, Erd, Excalidraw, GraphViz, Mermaid, Nomnoml, Pikchr, PlantUML, Structurizr, SvgBob, Symbolator, UMLet, Vega, Vega-Lite, WaveDrom and WireViz... and more to come!