From 69bc6c2ae35883286d9c2b8cc77d5fa0a8c37351 Mon Sep 17 00:00:00 2001 From: pacien Date: Thu, 26 Jul 2018 11:34:06 +0200 Subject: Kotlin rewrite, adding diagram options --- .../org/pacien/pandoc/filter/plantuml/Filter.java | 84 ---------------------- .../org/pacien/pandoc/filter/plantuml/Filter.kt | 57 +++++++++++++++ .../org/pacien/pandoc/filter/plantuml/Latex.kt | 16 +++++ .../org/pacien/pandoc/filter/plantuml/Main.kt | 5 ++ .../pacien/pandoc/filter/plantuml/PandocNode.kt | 30 ++++++++ .../org/pacien/pandoc/filter/plantuml/PlantUml.kt | 21 ++++++ 6 files changed, 129 insertions(+), 84 deletions(-) delete mode 100644 src/main/java/org/pacien/pandoc/filter/plantuml/Filter.java create mode 100644 src/main/kotlin/org/pacien/pandoc/filter/plantuml/Filter.kt create mode 100644 src/main/kotlin/org/pacien/pandoc/filter/plantuml/Latex.kt create mode 100644 src/main/kotlin/org/pacien/pandoc/filter/plantuml/Main.kt create mode 100644 src/main/kotlin/org/pacien/pandoc/filter/plantuml/PandocNode.kt create mode 100644 src/main/kotlin/org/pacien/pandoc/filter/plantuml/PlantUml.kt (limited to 'src/main') diff --git a/src/main/java/org/pacien/pandoc/filter/plantuml/Filter.java b/src/main/java/org/pacien/pandoc/filter/plantuml/Filter.java deleted file mode 100644 index 66abc2d..0000000 --- a/src/main/java/org/pacien/pandoc/filter/plantuml/Filter.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.pacien.pandoc.filter.plantuml; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.node.TextNode; -import net.sourceforge.plantuml.FileFormat; -import net.sourceforge.plantuml.FileFormatOption; -import net.sourceforge.plantuml.SourceStringReader; - -import java.io.*; -import java.util.Iterator; -import java.util.stream.Collectors; - -final public class Filter { - - private static final String BEGIN_TAG = "\\begin{tikzpicture}[yscale=-1]"; - private static final String LINE_SEP = "\n"; - private static final String TYPE_KEY = "t"; - private static final String CONTENT_KEY = "c"; - private static final String CODE_BLOCK_TYPE = "CodeBlock"; - private static final String RAW_BLOCK_TYPE = "RawBlock"; - private static final String PLANTUML_TYPE = "puml"; - private static final String LATEX_TYPE = "latex"; - private static final int META_INDEX = 0; - private static final int META_PROP_INDEX = 1; - private static final int META_PROP_TYPE_INDEX = 0; - private static final int CONTENT_INDEX = 1; - - private static String plantumlToLatex(String puml) throws IOException { - try (ByteArrayOutputStream s = new ByteArrayOutputStream()) { - new SourceStringReader(puml).generateImage(s, new FileFormatOption(FileFormat.LATEX_NO_PREAMBLE)); - try (BufferedReader r = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(s.toByteArray())))) { - return BEGIN_TAG + LINE_SEP + r.lines().filter(l -> !l.equals(BEGIN_TAG)).collect(Collectors.joining(LINE_SEP)); - } - } - } - - private static void renderPlantumlNode(ObjectNode n) throws IOException { - String puml = n.get(CONTENT_KEY).get(CONTENT_INDEX).asText(); - String tikz = plantumlToLatex(puml); - - n.set(TYPE_KEY, TextNode.valueOf(RAW_BLOCK_TYPE)); - ((ArrayNode) n.get(CONTENT_KEY)).removeAll() - .add(TextNode.valueOf(LATEX_TYPE)) - .add(TextNode.valueOf(tikz)); - } - - private static boolean isPlantumlNode(JsonNode n) { - return n.path(TYPE_KEY).asText().equals(CODE_BLOCK_TYPE) && - n.path(CONTENT_KEY).path(META_INDEX).path(META_PROP_INDEX).path(META_PROP_TYPE_INDEX).asText().equals(PLANTUML_TYPE); - } - - private static void walk(JsonNode n) throws IOException { - if (isPlantumlNode(n)) - renderPlantumlNode((ObjectNode) n); - else if (n.isContainerNode()) - for (Iterator i = n.elements(); i.hasNext(); ) walk(i.next()); - } - - public static void filter(InputStream i, OutputStream o) throws IOException { - ObjectMapper m = new ObjectMapper(); - JsonNode t = m.readTree(i); - if (t != null) { - walk(t); - m.writeValue(o, t); - } - } - - public static void main(String args[]) { - try { - filter(System.in, System.out); - } catch (IOException e) { - e.printStackTrace(); - System.exit(1); - } - } - - private Filter() { - // static class - } - -} diff --git a/src/main/kotlin/org/pacien/pandoc/filter/plantuml/Filter.kt b/src/main/kotlin/org/pacien/pandoc/filter/plantuml/Filter.kt new file mode 100644 index 0000000..82a78b5 --- /dev/null +++ b/src/main/kotlin/org/pacien/pandoc/filter/plantuml/Filter.kt @@ -0,0 +1,57 @@ +package org.pacien.pandoc.filter.plantuml + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.node.ArrayNode +import com.fasterxml.jackson.databind.node.ObjectNode +import com.fasterxml.jackson.databind.node.TextNode +import java.io.InputStream +import java.io.OutputStream + +object Filter { + private val mapper = ObjectMapper() + + private inline fun T.conditionally(condition: Boolean, block: (T) -> T): T = + if (condition) block(this) else this + + private inline fun T.withNonNull(value: A?, block: (T, A) -> T): T = + if (value != null) block(this, value) else this + + private fun JsonNode.isCodeBlock() = type() == "CodeBlock" + private fun JsonNode.isPlantUmlBlock() = isCodeBlock() && "puml" in classNames() + + private fun Latex.resizeBox(attrs: Map) = + resizeBox(attrs["width"] ?: "!", attrs["height"] ?: "!") + + private fun Latex.setOptions(classes: List, attrs: Map) = + this + .conditionally("width" in attrs || "height" in attrs) { it -> it.resizeBox(attrs) } + .conditionally("centered" in classes, Latex::centering) + .withNonNull(attrs["caption"], Latex::caption) + .withNonNull(attrs["label"], Latex::label) + .conditionally("caption" in attrs || "label" in attrs, Latex::figure) + + private fun arrayNodeOf(type: String, content: String): ArrayNode = + mapper.createArrayNode() + .add(TextNode.valueOf(type)) + .add(TextNode.valueOf(content)) + + private fun renderPlantumlNode(node: ObjectNode) { + val puml = node.content() + val tikz = PlantUml.renderTikz(puml) + val block = tikz.setOptions(node.classNames(), node.attributeMap()) + node.setBlock("RawBlock", arrayNodeOf("latex", block.raw())) + } + + private fun walk(node: JsonNode): Unit = when { + node.isPlantUmlBlock() -> renderPlantumlNode(node as ObjectNode) + else -> node.forEach(Filter::walk) + } + + fun filter(input: InputStream, output: OutputStream) { + mapper.readTree(input)?.let { tree -> + walk(tree) + mapper.writeValue(output, tree) + } + } +} diff --git a/src/main/kotlin/org/pacien/pandoc/filter/plantuml/Latex.kt b/src/main/kotlin/org/pacien/pandoc/filter/plantuml/Latex.kt new file mode 100644 index 0000000..48ce087 --- /dev/null +++ b/src/main/kotlin/org/pacien/pandoc/filter/plantuml/Latex.kt @@ -0,0 +1,16 @@ +package org.pacien.pandoc.filter.plantuml + +fun Sequence.toLatex() = Latex(this) + +class Latex(private val body: Sequence) { + fun raw() = body.filterNot(String::isEmpty).joinToString("\n") + + private fun surround(prefix: String, suffix: String) = + Latex(sequenceOf(prefix) + body + sequenceOf(suffix)) + + fun resizeBox(width: String, height: String) = surround("\\resizebox{$width}{$height}{", "}") + fun centering() = surround("\\centering", "") + fun label(label: String) = surround("", "\\label{$label}") + fun caption(caption: String) = surround("", "\\caption{$caption}") + fun figure() = surround("\\begin{figure}[h]", "\\end{figure}") +} diff --git a/src/main/kotlin/org/pacien/pandoc/filter/plantuml/Main.kt b/src/main/kotlin/org/pacien/pandoc/filter/plantuml/Main.kt new file mode 100644 index 0000000..1ebe8f9 --- /dev/null +++ b/src/main/kotlin/org/pacien/pandoc/filter/plantuml/Main.kt @@ -0,0 +1,5 @@ +package org.pacien.pandoc.filter.plantuml + +fun main(args: Array) { + Filter.filter(System.`in`, System.out) +} diff --git a/src/main/kotlin/org/pacien/pandoc/filter/plantuml/PandocNode.kt b/src/main/kotlin/org/pacien/pandoc/filter/plantuml/PandocNode.kt new file mode 100644 index 0000000..8b3ddde --- /dev/null +++ b/src/main/kotlin/org/pacien/pandoc/filter/plantuml/PandocNode.kt @@ -0,0 +1,30 @@ +package org.pacien.pandoc.filter.plantuml + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.node.ObjectNode +import com.fasterxml.jackson.databind.node.TextNode + +// Structure of a content node: +// { +// "t": "CodeBlock", +// "c": [ +// [ +// "", +// [ "puml", "otherClass" ], +// [ ["scale", "0.5"], ["key", "value"] ] +// ], +// "@startuml\n@enduml" +// ] +// } + +fun JsonNode.type(): String = path("t").asText() +fun JsonNode.classes(): JsonNode = path("c").path(0).path(1) +fun JsonNode.classNames(): List = classes().map(JsonNode::asText) +fun JsonNode.attributes(): JsonNode = path("c").path(0).path(2) +fun JsonNode.attributePair(): Pair = Pair(path(0).asText(), path(1).asText()) +fun JsonNode.attributeMap(): Map = attributes().associate(JsonNode::attributePair) +fun JsonNode.content(): String = path("c").path(1).asText() + +fun ObjectNode.setBlockType(type: String) = apply { set("t", TextNode.valueOf(type)) } +fun ObjectNode.setBlockContent(content: JsonNode) = apply { set("c", content) } +fun ObjectNode.setBlock(type: String, content: JsonNode) = setBlockType(type).setBlockContent(content) diff --git a/src/main/kotlin/org/pacien/pandoc/filter/plantuml/PlantUml.kt b/src/main/kotlin/org/pacien/pandoc/filter/plantuml/PlantUml.kt new file mode 100644 index 0000000..2a8da1b --- /dev/null +++ b/src/main/kotlin/org/pacien/pandoc/filter/plantuml/PlantUml.kt @@ -0,0 +1,21 @@ +package org.pacien.pandoc.filter.plantuml + +import net.sourceforge.plantuml.FileFormat +import net.sourceforge.plantuml.FileFormatOption +import net.sourceforge.plantuml.SourceStringReader +import java.io.ByteArrayOutputStream + +object PlantUml { + private val OUTPUT_FORMAT = FileFormatOption(FileFormat.LATEX_NO_PREAMBLE) + + private fun SourceStringReader.generateImage(outputFormat: FileFormatOption) = + ByteArrayOutputStream().use { buffer -> + generateImage(buffer, outputFormat) + buffer.toString().lineSequence() + } + + fun renderTikz(plantuml: String) = + SourceStringReader(plantuml) + .generateImage(OUTPUT_FORMAT) + .toLatex() +} -- cgit v1.2.3