diff --git a/server/ops/docker/jdk17-noble/Dockerfile b/server/ops/docker/jdk17-noble/Dockerfile index 85dd2c5e3..66778ef09 100644 --- a/server/ops/docker/jdk17-noble/Dockerfile +++ b/server/ops/docker/jdk17-noble/Dockerfile @@ -235,6 +235,8 @@ RUN wget "https://github.com/yuzutech/blockdiag/releases/download/v${BLOCKDIAG_V RUN wget "https://github.com/yuzutech/WireViz/releases/download/v${WIREVIZ_VERSION}/wireviz-linux-${TARGETARCH}.bin" -O /usr/bin/wireviz && \ chmod +x /usr/bin/wireviz +RUN apt-get install --no-install-recommends --yes msc-generator-nox + COPY --from=erd /root/.cabal/bin/erd /usr/bin/erd COPY --from=kroki-builder-bytefield /app/app.bin /usr/bin/bytefield COPY --from=kroki-builder-dbml /app/app.bin /usr/bin/dbml diff --git a/server/src/main/java/io/kroki/server/Server.java b/server/src/main/java/io/kroki/server/Server.java index 95761d6a7..5bfdf3f9c 100644 --- a/server/src/main/java/io/kroki/server/Server.java +++ b/server/src/main/java/io/kroki/server/Server.java @@ -122,6 +122,7 @@ static void start(Vertx vertx, VertxOptions vertxOptions, JsonObject config, Han registry.register(new TikZ(vertx, config, commander), "tikz"); registry.register(new Dbml(vertx, config, commander), "dbml"); registry.register(new Wireviz(vertx, config, commander), "wireviz"); + registry.register(new MscGenerator(vertx, config, commander), "mscgen"); router.post("/") .handler(bodyHandler) diff --git a/server/src/main/java/io/kroki/server/service/MscGenerator.java b/server/src/main/java/io/kroki/server/service/MscGenerator.java new file mode 100644 index 000000000..56b4b4eb6 --- /dev/null +++ b/server/src/main/java/io/kroki/server/service/MscGenerator.java @@ -0,0 +1,85 @@ +package io.kroki.server.service; + +import io.kroki.server.action.Commander; +import io.kroki.server.decode.DiagramSource; +import io.kroki.server.decode.SourceDecoder; +import io.kroki.server.error.DecodeException; +import io.kroki.server.format.FileFormat; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.json.JsonObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class MscGenerator implements DiagramService { + + private static final List SUPPORTED_FORMATS = Arrays.asList(FileFormat.PNG, FileFormat.SVG, FileFormat.PDF); + + private final Vertx vertx; + private final String binPath; + private final SourceDecoder sourceDecoder; + private final Commander commander; + + public MscGenerator(Vertx vertx, JsonObject config, Commander commander) { + this.vertx = vertx; + this.binPath = config.getString("KROKI_MSCGEN_BIN_PATH", "msc-gen"); + this.sourceDecoder = new SourceDecoder() { + @Override + public String decode(String encoded) throws DecodeException { + return DiagramSource.decode(encoded); + } + }; + this.commander = commander; + } + + @Override + public List getSupportedFormats() { + return SUPPORTED_FORMATS; + } + + @Override + public SourceDecoder getSourceDecoder() { + return sourceDecoder; + } + + @Override + public String getVersion() { + try { + Process process = new ProcessBuilder(binPath, "--version").start(); + String line = new BufferedReader(new InputStreamReader(process.getInputStream())).readLine(); + return line.split("\\s+")[1].substring(1); + } catch (IOException e) { + return "unknown"; + } + } + + @Override + public void convert(String sourceDecoded, String serviceName, FileFormat fileFormat, JsonObject options, Handler> handler) { + vertx.executeBlocking(future -> { + try { + byte[] result = mscgen(sourceDecoded.getBytes(), fileFormat.getName(), options); + future.complete(result); + } catch (IOException | InterruptedException | IllegalStateException e) { + future.fail(e); + } + }, res -> handler.handle(res.map(o -> Buffer.buffer((byte[]) o)))); + } + + private byte[] mscgen(byte[] source, String format, JsonObject options) throws IOException, InterruptedException, IllegalStateException { + return commander.execute( + source, + binPath, + "-S", options.getString("lang", "signalling"), // Supported languages: signalling | msc, graph, block + "-T", format, + "-s=" + options.getFloat("scale", 1.f), + "-" + ); + } +}