diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main/java/org/pacien/pandoc/filter/plantuml/Filter.java | 84 | ||||
-rw-r--r-- | src/main/kotlin/org/pacien/pandoc/filter/plantuml/Filter.kt | 57 | ||||
-rw-r--r-- | src/main/kotlin/org/pacien/pandoc/filter/plantuml/Latex.kt | 16 | ||||
-rw-r--r-- | src/main/kotlin/org/pacien/pandoc/filter/plantuml/Main.kt | 5 | ||||
-rw-r--r-- | src/main/kotlin/org/pacien/pandoc/filter/plantuml/PandocNode.kt | 30 | ||||
-rw-r--r-- | src/main/kotlin/org/pacien/pandoc/filter/plantuml/PlantUml.kt | 21 | ||||
-rw-r--r-- | src/test/java/org/pacien/pandoc/filter/plantuml/FilterTest.java | 27 | ||||
-rw-r--r-- | src/test/kotlin/org/pacien/pandoc/filter/plantuml/FilterTest.kt | 39 | ||||
-rw-r--r-- | src/test/resources/attributes.expected.json | 1 | ||||
-rw-r--r-- | src/test/resources/attributes.input.json | 1 | ||||
-rw-r--r-- | src/test/resources/identifyblock.expected.json (renamed from src/test/resources/expected.json) | 2 | ||||
-rw-r--r-- | src/test/resources/identifyblock.input.json (renamed from src/test/resources/input.json) | 0 | ||||
-rw-r--r-- | src/test/resources/resize.expected.json | 1 | ||||
-rw-r--r-- | src/test/resources/resize.input.json | 1 |
14 files changed, 173 insertions, 112 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 @@ | |||
1 | package org.pacien.pandoc.filter.plantuml; | ||
2 | |||
3 | import com.fasterxml.jackson.databind.JsonNode; | ||
4 | import com.fasterxml.jackson.databind.ObjectMapper; | ||
5 | import com.fasterxml.jackson.databind.node.ArrayNode; | ||
6 | import com.fasterxml.jackson.databind.node.ObjectNode; | ||
7 | import com.fasterxml.jackson.databind.node.TextNode; | ||
8 | import net.sourceforge.plantuml.FileFormat; | ||
9 | import net.sourceforge.plantuml.FileFormatOption; | ||
10 | import net.sourceforge.plantuml.SourceStringReader; | ||
11 | |||
12 | import java.io.*; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.stream.Collectors; | ||
15 | |||
16 | final 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 @@ | |||
1 | package org.pacien.pandoc.filter.plantuml | ||
2 | |||
3 | import com.fasterxml.jackson.databind.JsonNode | ||
4 | import com.fasterxml.jackson.databind.ObjectMapper | ||
5 | import com.fasterxml.jackson.databind.node.ArrayNode | ||
6 | import com.fasterxml.jackson.databind.node.ObjectNode | ||
7 | import com.fasterxml.jackson.databind.node.TextNode | ||
8 | import java.io.InputStream | ||
9 | import java.io.OutputStream | ||
10 | |||
11 | object 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 @@ | |||
1 | package org.pacien.pandoc.filter.plantuml | ||
2 | |||
3 | fun Sequence<String>.toLatex() = Latex(this) | ||
4 | |||
5 | class 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 @@ | |||
1 | package org.pacien.pandoc.filter.plantuml | ||
2 | |||
3 | fun 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 @@ | |||
1 | package org.pacien.pandoc.filter.plantuml | ||
2 | |||
3 | import com.fasterxml.jackson.databind.JsonNode | ||
4 | import com.fasterxml.jackson.databind.node.ObjectNode | ||
5 | import com.fasterxml.jackson.databind.node.TextNode | ||
6 | |||
7 | // Structure of a content node: | ||
8 | // { | ||