Skip to content

Commit 6c7d2ac

Browse files
new output options (#292)
1 parent cb96266 commit 6c7d2ac

File tree

2 files changed

+67
-19
lines changed

2 files changed

+67
-19
lines changed

toolkit/jupyter/src/main/kotlin/org/jetbrains/letsPlot/toolkit/jupyter/Integration.kt

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ internal class Integration(private val notebook: Notebook, options: MutableMap<S
2727
private val lpJsVersion = VersionChecker.letsPlotJsVersion
2828
private val isolatedFrame = options["isolatedFrame"] ?: ""
2929

30-
private val webOnly: Boolean = options["webOnly"].toBoolean()
30+
// Output options
31+
private val addWebOutput = (options["addWebOutput"] ?: "true").toBoolean()
32+
private val addKTNBOutput = (options["addKTNBOutput"] ?: "true").toBoolean()
33+
private val addStaticSvg = (options["addStaticSvg"] ?: "true").toBoolean()
34+
private val addStaticPng = (options["addStaticPng"] ?: "false").toBoolean()
3135

3236
override fun Builder.onLoaded() {
3337
import("org.jetbrains.letsPlot.*")
@@ -59,29 +63,43 @@ internal class Integration(private val notebook: Notebook, options: MutableMap<S
5963
frontendContext = LetsPlot.setupNotebook(lpJsVersion, isolatedFrameParam) { display(HTML(it), null) }
6064
LetsPlot.apiVersion = lpkVersion
6165
// Load library JS
62-
display(HTML(frontendContext.getConfigureHtml()), null)
66+
if (addWebOutput) {
67+
display(HTML(frontendContext.getConfigureHtml()), null)
68+
}
6369
// add figure renders AFTER frontendContext initialization
64-
addRenders(lpJsVersion)
70+
addRenders()
6571
declare("letsPlotNotebookConfig" to config)
6672
}
6773
}
6874

6975

70-
private fun Builder.addRenders(jsVersion: String) {
76+
private fun Builder.addRenders() {
7177
var firstFigureRendered = false
72-
resources {
73-
js("letsPlotJs") {
74-
url(scriptUrl(jsVersion))
78+
79+
if (addWebOutput) {
80+
resources {
81+
js("letsPlotJs") {
82+
url(scriptUrl(lpJsVersion))
83+
}
7584
}
7685
}
86+
7787
renderWithHost<Figure> { host, value ->
7888
// For cases when Integration is added via Kotlin Notebook project dependency;
7989
// display configure HTML with the first `Figure` rendering
80-
if (!firstFigureRendered) {
90+
if (addWebOutput && !firstFigureRendered) {
8191
firstFigureRendered = true
8292
host.execute { display(HTML(frontendContext.getConfigureHtml()), null) }
8393
}
84-
NotebookRenderingContext(config, frontendContext, webOnly).figureToMimeResult(value)
94+
NotebookRenderingContext(
95+
config, frontendContext,
96+
NotebookRenderingContext.OutputOptions(
97+
addWebOutput = addWebOutput,
98+
addKTNBOutput = addKTNBOutput,
99+
addStaticSvg = addStaticSvg,
100+
addStaticPng = addStaticPng
101+
)
102+
).figureToMimeResult(value)
85103
}
86104
}
87105

toolkit/jupyter/src/main/kotlin/org/jetbrains/letsPlot/toolkit/jupyter/NotebookRenderingContext.kt

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,39 @@ import org.jetbrains.kotlinx.jupyter.api.MimeTypedResultEx
77
import org.jetbrains.kotlinx.jupyter.api.MimeTypes
88
import org.jetbrains.letsPlot.Figure
99
import org.jetbrains.letsPlot.awt.plot.PlotSvgExport
10+
import org.jetbrains.letsPlot.core.plot.export.PlotImageExport
1011
import org.jetbrains.letsPlot.frontend.NotebookFrontendContext
1112
import org.jetbrains.letsPlot.intern.toSpec
12-
import org.jetbrains.letsPlot.toolkit.jupyter.json.extendedByJson
1313
import org.jetbrains.letsPlot.toolkit.json.serializeJsonMap
14+
import org.jetbrains.letsPlot.toolkit.jupyter.json.extendedByJson
1415
import java.util.*
1516

1617
internal class NotebookRenderingContext(
1718
private val config: JupyterConfig,
1819
private val frontendContext: NotebookFrontendContext,
19-
private val webOnly: Boolean
20+
private val outputOptions: OutputOptions
2021
) {
22+
23+
data class OutputOptions(
24+
val addWebOutput: Boolean,
25+
val addKTNBOutput: Boolean,
26+
val addStaticSvg: Boolean,
27+
val addStaticPng: Boolean,
28+
)
29+
2130
/**
2231
* Creates Mime JSON with two output options - HTML and application/plot.
2332
* The HTML output is used in Jupyter Notebooks and Datalore (the other one is ignored).
2433
* The application/plot is used in Kotlin Notebook when native rendering via Swing is enabled.
2534
*/
2635
private fun figureToMimeJson(figure: Figure): JsonObject {
2736
val spec = figure.toSpec()
28-
val html = frontendContext.getDisplayHtml(figure.toSpec())
2937
return buildJsonObject {
30-
put(MimeTypes.HTML, JsonPrimitive(html))
31-
if (!webOnly) {
38+
if (outputOptions.addWebOutput) {
39+
val plotHtml = frontendContext.getDisplayHtml(spec)
40+
put(MimeTypes.HTML, JsonPrimitive(plotHtml))
41+
}
42+
if (outputOptions.addKTNBOutput) {
3243
put("application/plot+json", buildJsonObject {
3344
put("output_type", JsonPrimitive("lets_plot_spec"))
3445
put("output", serializeJsonMap(spec))
@@ -66,19 +77,38 @@ internal class NotebookRenderingContext(
6677
val svgSplit = split('\n')
6778
(listOf(updateSvg(svgSplit.first(), id)) + svgSplit.drop(1)).joinToString("\n")
6879
}
69-
val extraHTML = """
80+
val htmlWithSvg = """
7081
$svgWithID
7182
<script>document.getElementById("$id").style.display = "none";</script>
7283
""".trimIndent()
7384

74-
return mapOf(MimeTypes.HTML to JsonPrimitive(extraHTML))
85+
return mapOf(MimeTypes.HTML to JsonPrimitive(htmlWithSvg))
86+
}
87+
88+
private fun figureToHiddenPng(figure: Figure): Map<String, JsonPrimitive> {
89+
val base64 = Base64.getEncoder().encodeToString(
90+
PlotImageExport.buildImageFromRawSpecs(
91+
figure.toSpec(), PlotImageExport.Format.PNG
92+
).bytes
93+
)
94+
val id = UUID.randomUUID().toString()
95+
val htmlWithPng = """
96+
<img id="$id" src="data:image/png;base64,$base64" alt="image">
97+
<script>document.getElementById("$id").style.display = "none";</script>
98+
""".trimIndent()
99+
100+
return mapOf(MimeTypes.HTML to JsonPrimitive(htmlWithPng))
75101
}
76102

77103
fun figureToMimeResult(figure: Figure): MimeTypedResultEx {
78-
val basicResult = figureToMimeJson(figure)
79-
val extraSvg = figureToHiddenSvg(figure)
104+
val mimeJson = figureToMimeJson(figure)
105+
.let {
106+
if (outputOptions.addStaticSvg) it.extendedByJson(figureToHiddenSvg(figure)) else it
107+
}.let {
108+
if (outputOptions.addStaticPng) it.extendedByJson(figureToHiddenPng(figure)) else it
109+
}
80110
return MimeTypedResultEx(
81-
basicResult extendedByJson extraSvg,
111+
mimeJson,
82112
id = null,
83113
metadataModifiers = emptyList()
84114
)

0 commit comments

Comments
 (0)