aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/org/pacien/pandoc
diff options
context:
space:
mode:
authorpacien2018-07-26 11:34:06 +0200
committerpacien2018-07-26 11:34:06 +0200
commit69bc6c2ae35883286d9c2b8cc77d5fa0a8c37351 (patch)
tree26d8635694b9d7f61b77772b29a9c980b632cc2c /src/main/kotlin/org/pacien/pandoc
parent4d80dd03b75421f2fb40cb89f9634be47b09a779 (diff)
downloadpandoc-filter-plantuml-69bc6c2ae35883286d9c2b8cc77d5fa0a8c37351.tar.gz
Kotlin rewrite, adding diagram options
Diffstat (limited to 'src/main/kotlin/org/pacien/pandoc')
-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
5 files changed, 129 insertions, 0 deletions
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}