aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/org/pacien/pandoc/filter/plantuml/Filter.java84
-rw-r--r--src/main/kotlin/org/pacien/pandoc/filter/plantuml/Filter.kt57
-rw-r--r--src/main/kotlin/org/pacien/pandoc/filter/plantuml/Latex.kt16
-rw-r--r--src/main/kotlin/org/pacien/pandoc/filter/plantuml/Main.kt5
-rw-r--r--src/main/kotlin/org/pacien/pandoc/filter/plantuml/PandocNode.kt30
-rw-r--r--src/main/kotlin/org/pacien/pandoc/filter/plantuml/PlantUml.kt21
6 files changed, 129 insertions, 84 deletions
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 @@
1package org.pacien.pandoc.filter.plantuml;
2
3import com.fasterxml.jackson.databind.JsonNode;
4import com.fasterxml.jackson.databind.ObjectMapper;
5import com.fasterxml.jackson.databind.node.ArrayNode;
6import com.fasterxml.jackson.databind.node.ObjectNode;
7import com.fasterxml.jackson.databind.node.TextNode;
8import net.sourceforge.plantuml.FileFormat;
9import net.sourceforge.plantuml.FileFormatOption;
10import net.sourceforge.plantuml.SourceStringReader;
11
12import java.io.*;
13import java.util.Iterator;
14import java.util.stream.Collectors;
15
16final public class Filter {
17
18 private static final String BEGIN_TAG = "\\begin{tikzpicture}[yscale=-1]";
19 private static final String LINE_SEP = "\n";
20 private static final String TYPE_KEY = "t";
21 private static final String CONTENT_KEY = "c";
22 private static final String CODE_BLOCK_TYPE = "CodeBlock";
23 private static final String RAW_BLOCK_TYPE = "RawBlock";
24 private static final String PLANTUML_TYPE = "puml";
25 private static final String LATEX_TYPE = "latex";
26 private static final int META_INDEX = 0;
27 private static final int META_PROP_INDEX = 1;
28 private static final int META_PROP_TYPE_INDEX = 0;
29 private static final int CONTENT_INDEX = 1;
30
31 private static String plantumlToLatex(String puml) throws IOException {
32 try (ByteArrayOutputStream s = new ByteArrayOutputStream()) {
33 new SourceStringReader(puml).generateImage(s, new FileFormatOption(FileFormat.LATEX_NO_PREAMBLE));
34 try (BufferedReader r = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(s.toByteArray())))) {
35 return BEGIN_TAG + LINE_SEP + r.lines().filter(l -> !l.equals(BEGIN_TAG)).collect(Collectors.joining(LINE_SEP));
36 }
37 }
38 }
39
40 private static void renderPlantumlNode(ObjectNode n) throws IOException {
41 String puml = n.get(CONTENT_KEY).get(CONTENT_INDEX).asText();
42 String tikz = plantumlToLatex(puml);
43
44 n.set(TYPE_KEY, TextNode.valueOf(RAW_BLOCK_TYPE));
45 ((ArrayNode) n.get(CONTENT_KEY)).removeAll()
46 .add(TextNode.valueOf(LATEX_TYPE))
47 .add(TextNode.valueOf(tikz));
48 }
49
50 private static boolean isPlantumlNode(JsonNode n) {
51 return n.path(TYPE_KEY).asText().equals(CODE_BLOCK_TYPE) &&
52 n.path(CONTENT_KEY).path(META_INDEX).path(META_PROP_INDEX).path(META_PROP_TYPE_INDEX).asText().equals(PLANTUML_TYPE);
53 }
54
55 private static void walk(JsonNode n) throws IOException {
56 if (isPlantumlNode(n))
57 renderPlantumlNode((ObjectNode) n);
58 else if (n.isContainerNode())
59 for (Iterator<JsonNode> i = n.elements(); i.hasNext(); ) walk(i.next());
60 }
61
62 public static void filter(InputStream i, OutputStream o) throws IOException {
63 ObjectMapper m = new ObjectMapper();
64 JsonNode t = m.readTree(i);
65 if (t != null) {
66 walk(t);
67 m.writeValue(o, t);
68 }
69 }
70
71 public static void main(String args[]) {
72 try {
73 filter(System.in, System.out);
74 } catch (IOException e) {
75 e.printStackTrace();
76 System.exit(1);
77 }
78 }
79
80 private Filter() {
81 // static class
82 }
83
84}
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 @@
1package org.pacien.pandoc.filter.plantuml
2
3import com.fasterxml.jackson.databind.JsonNode
4import com.fasterxml.jackson.databind.ObjectMapper
5import com.fasterxml.jackson.databind.node.ArrayNode
6import com.fasterxml.jackson.databind.node.ObjectNode
7import com.fasterxml.jackson.databind.node.TextNode
8import java.io.InputStream
9import java.io.OutputStream
10
11object Filter {
12 private val mapper = ObjectMapper()
13
14 private inline fun <T> T.conditionally(condition: Boolean, block: (T) -> T): T =
15 if (condition) block(this) else this
16
17 private inline fun <T, A> T.withNonNull(value: A?, block: (T, A) -> T): T =
18 if (value != null) block(this, value) else this
19
20 private fun JsonNode.isCodeBlock() = type() == "CodeBlock"
21 private fun JsonNode.isPlantUmlBlock() = isCodeBlock() && "puml" in classNames()
22
23 private fun Latex.resizeBox(attrs: Map<String, String>) =
24 resizeBox(attrs["width"] ?: "!", attrs["height"] ?: "!")
25
26 private fun Latex.setOptions(classes: List<String>, attrs: Map<String, String>) =
27 this
28 .conditionally("width" in attrs || "height" in attrs) { it -> it.resizeBox(attrs) }
29 .conditionally("centered" in classes, Latex::centering)
30 .withNonNull(attrs["caption"], Latex::caption)
31 .withNonNull(attrs["label"], Latex::label)
32 .conditionally("caption" in attrs || "label" in attrs, Latex::figure)
33
34 private fun arrayNodeOf(type: String, content: String): ArrayNode =
35 mapper.createArrayNode()
36 .add(TextNode.valueOf(type))
37 .add(TextNode.valueOf(content))
38
39 private fun renderPlantumlNode(node: ObjectNode) {
40 val puml = node.content()
41 val tikz = PlantUml.renderTikz(puml)
42 val block = tikz.setOptions(node.classNames(), node.attributeMap())
43 node.setBlock("RawBlock", arrayNodeOf("latex", block.raw()))
44 }
45
46 private fun walk(node: JsonNode): Unit = when {
47 node.isPlantUmlBlock() -> renderPlantumlNode(node as ObjectNode)
48 else -> node.forEach(Filter::walk)
49 }
50
51 fun filter(input: InputStream, output: OutputStream) {
52 mapper.readTree(input)?.let { tree ->
53 walk(tree)
54 mapper.writeValue(output, tree)
55 }
56 }
57}
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 @@
1package org.pacien.pandoc.filter.plantuml
2
3fun Sequence<String>.toLatex() = Latex(this)
4
5class Latex(private val body: Sequence<String>) {
6 fun raw() = body.filterNot(String::isEmpty).joinToString("\n")
7
8 private fun surround(prefix: String, suffix: String) =
9 Latex(sequenceOf(prefix) + body + sequenceOf(suffix))
10
11 fun resizeBox(width: String, height: String) = surround("\\resizebox{$width}{$height}{", "}")
12 fun centering() = surround("\\centering", "")
13 fun label(label: String) = surround("", "\\label{$label}")
14 fun caption(caption: String) = surround("", "\\caption{$caption}")
15 fun figure() = surround("\\begin{figure}[h]", "\\end{figure}")
16}
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 @@
1package org.pacien.pandoc.filter.plantuml
2
3fun main(args: Array<String>) {
4 Filter.filter(System.`in`, System.out)
5}
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 @@
1package org.pacien.pandoc.filter.plantuml
2
3import com.fasterxml.jackson.databind.JsonNode
4import com.fasterxml.jackson.databind.node.ObjectNode
5import com.fasterxml.jackson.databind.node.TextNode
6
7// Structure of a content node:
8// {
9// "t": "CodeBlock",
10// "c": [
11// [
12// "",
13// [ "puml", "otherClass" ],
14// [ ["scale", "0.5"], ["key", "value"] ]
15// ],
16// "@startuml\n@enduml"
17// ]
18// }
19
20fun JsonNode.type(): String = path("t").asText()
21fun JsonNode.classes(): JsonNode = path("c").path(0).path(1)
22fun JsonNode.classNames(): List<String> = classes().map(JsonNode::asText)
23fun JsonNode.attributes(): JsonNode = path("c").path(0).path(2)
24fun JsonNode.attributePair(): Pair<String, String> = Pair(path(0).asText(), path(1).asText())
25fun JsonNode.attributeMap(): Map<String, String> = attributes().associate(JsonNode::attributePair)
26fun JsonNode.content(): String = path("c").path(1).asText()
27
28fun ObjectNode.setBlockType(type: String) = apply { set("t", TextNode.valueOf(type)) }
29fun ObjectNode.setBlockContent(content: JsonNode) = apply { set("c", content) }
30fun 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 @@
1package org.pacien.pandoc.filter.plantuml
2
3import net.sourceforge.plantuml.FileFormat
4import net.sourceforge.plantuml.FileFormatOption
5import net.sourceforge.plantuml.SourceStringReader
6import java.io.ByteArrayOutputStream
7
8object PlantUml {
9 private val OUTPUT_FORMAT = FileFormatOption(FileFormat.LATEX_NO_PREAMBLE)
10
11 private fun SourceStringReader.generateImage(outputFormat: FileFormatOption) =
12 ByteArrayOutputStream().use { buffer ->
13 generateImage(buffer, outputFormat)
14 buffer.toString().lineSequence()
15 }
16
17 fun renderTikz(plantuml: String) =
18 SourceStringReader(plantuml)
19 .generateImage(OUTPUT_FORMAT)
20 .toLatex()
21}