Skip to content

Commit 8db6f95

Browse files
committed
Register default renderer for BufferedImage
Fixes #292
1 parent ea39823 commit 8db6f95

File tree

7 files changed

+51
-13
lines changed

7 files changed

+51
-13
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.jetbrains.kotlinx.jupyter.api
2+
3+
import java.awt.image.BufferedImage
4+
import java.io.ByteArrayOutputStream
5+
import java.util.Base64
6+
import javax.imageio.ImageIO
7+
8+
val bufferedImageRenderer = createRenderer<BufferedImage> {
9+
val format = "png"
10+
val stream = ByteArrayOutputStream()
11+
ImageIO.write(it, format, stream)
12+
val data = stream.toByteArray()
13+
val encoder = Base64.getEncoder()
14+
val src = buildString {
15+
append("""data:image/$format;base64,""")
16+
append(encoder.encodeToString(data))
17+
}
18+
HTML("""<img src="$src"/>""")
19+
}

jupyter-lib/api/src/main/kotlin/org/jetbrains/kotlinx/jupyter/api/renderersHandling.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,9 @@ class SubtypeRendererTypeHandler(private val superType: KClass<*>, override val
142142
return SubtypeRendererTypeHandler(superType, execution.replaceVariables(mapping))
143143
}
144144
}
145+
146+
inline fun <reified T : Any> createRenderer(crossinline renderAction: (T) -> Any?): RendererTypeHandler {
147+
return SubtypeRendererTypeHandler(T::class) { _, result ->
148+
FieldValue(renderAction(result.value as T), result.name)
149+
}
150+
}

src/main/kotlin/org/jetbrains/kotlinx/jupyter/protocol.kt

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import org.jetbrains.kotlinx.jupyter.LoggingManagement.disableLogging
1313
import org.jetbrains.kotlinx.jupyter.LoggingManagement.mainLoggerLevel
1414
import org.jetbrains.kotlinx.jupyter.api.DisplayResult
1515
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelVersion.Companion.toMaybeUnspecifiedString
16-
import org.jetbrains.kotlinx.jupyter.api.MutableJsonObject
1716
import org.jetbrains.kotlinx.jupyter.api.Notebook
1817
import org.jetbrains.kotlinx.jupyter.api.Renderable
18+
import org.jetbrains.kotlinx.jupyter.api.libraries.ExecutionHost
1919
import org.jetbrains.kotlinx.jupyter.api.setDisplayId
2020
import org.jetbrains.kotlinx.jupyter.api.textResult
2121
import org.jetbrains.kotlinx.jupyter.common.looksLikeReplCommand
@@ -104,17 +104,22 @@ class OkResponseWithMessage(
104104
}
105105

106106
interface DisplayHandler {
107-
fun handleDisplay(value: Any)
108-
fun handleUpdate(value: Any, id: String? = null)
107+
fun handleDisplay(value: Any, host: ExecutionHost)
108+
fun handleUpdate(value: Any, host: ExecutionHost, id: String? = null)
109109
}
110110

111111
class SocketDisplayHandler(
112112
private val socket: JupyterConnection.Socket,
113113
private val notebook: NotebookImpl,
114114
private val message: Message,
115115
) : DisplayHandler {
116-
override fun handleDisplay(value: Any) {
117-
val display = value.toDisplayResult(notebook) ?: return
116+
private fun render(host: ExecutionHost, value: Any): DisplayResult? {
117+
val renderedValue = notebook.renderersProcessor.renderValue(host, value)
118+
return renderedValue.toDisplayResult(notebook)
119+
}
120+
121+
override fun handleDisplay(value: Any, host: ExecutionHost) {
122+
val display = render(host, value) ?: return
118123
val json = display.toJson()
119124

120125
notebook.currentCell?.addDisplay(display)
@@ -132,9 +137,9 @@ class SocketDisplayHandler(
132137
)
133138
}
134139

135-
override fun handleUpdate(value: Any, id: String?) {
136-
val display = value.toDisplayResult(notebook) ?: return
137-
val json: MutableJsonObject = display.toJson().toMutableMap()
140+
override fun handleUpdate(value: Any, host: ExecutionHost, id: String?) {
141+
val display = render(host, value) ?: return
142+
val json = display.toJson().toMutableMap()
138143

139144
notebook.currentCell?.displays?.update(id, display)
140145

src/main/kotlin/org/jetbrains/kotlinx/jupyter/repl.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ class ReplForJupyterImpl(
313313

314314
private val renderersProcessor: ResultsRenderersProcessor = run {
315315
val processor = RenderersProcessorImpl(contextUpdater)
316+
processor.registerDefaultRenderers()
316317
notebook.typeRenderersProcessor = processor
317318
processor
318319
}

src/main/kotlin/org/jetbrains/kotlinx/jupyter/repl/impl/CellExecutorImpl.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ internal class CellExecutorImpl(private val replContext: SharedReplContext) : Ce
134134
rethrowAsLibraryException(LibraryProblemPart.RESOURCES) {
135135
library.resources.forEach {
136136
val htmlText = sharedContext.resourcesProcessor.wrapLibrary(it, classLoader)
137-
displayHandler?.handleDisplay(HTML(htmlText))
137+
displayHandler?.handleDisplay(HTML(htmlText), this)
138138
}
139139
}
140140

@@ -145,11 +145,11 @@ internal class CellExecutorImpl(private val replContext: SharedReplContext) : Ce
145145
override fun execute(code: Code) = executor.execute(code, displayHandler, processVariables = false, invokeAfterCallbacks = false).result
146146

147147
override fun display(value: Any) {
148-
displayHandler?.handleDisplay(value)
148+
displayHandler?.handleDisplay(value, this)
149149
}
150150

151151
override fun updateDisplay(value: Any, id: String?) {
152-
displayHandler?.handleUpdate(value, id)
152+
displayHandler?.handleUpdate(value, this, id)
153153
}
154154

155155
override fun scheduleExecution(execution: ExecutionCallback<*>) {

src/main/kotlin/org/jetbrains/kotlinx/jupyter/util.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.jetbrains.kotlinx.jupyter
22

3+
import org.jetbrains.kotlinx.jupyter.api.bufferedImageRenderer
4+
import org.jetbrains.kotlinx.jupyter.codegen.ResultsRenderersProcessor
35
import org.jetbrains.kotlinx.jupyter.compiler.util.SourceCodeImpl
46
import kotlin.script.experimental.api.ScriptDiagnostic
57
import kotlin.script.experimental.api.SourceCode
@@ -62,3 +64,7 @@ fun Int.toSourceCodePositionWithNewAbsolute(code: SourceCode, newCode: SourceCod
6264

6365
return SourceCode.Position(pos.line, abs - absLineStart + 1, abs)
6466
}
67+
68+
fun ResultsRenderersProcessor.registerDefaultRenderers() {
69+
register(bufferedImageRenderer)
70+
}

src/test/kotlin/org/jetbrains/kotlinx/jupyter/test/testUtil.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import org.jetbrains.kotlinx.jupyter.api.JREInfoProvider
1313
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelVersion
1414
import org.jetbrains.kotlinx.jupyter.api.Notebook
1515
import org.jetbrains.kotlinx.jupyter.api.RenderersProcessor
16+
import org.jetbrains.kotlinx.jupyter.api.libraries.ExecutionHost
1617
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
1718
import org.jetbrains.kotlinx.jupyter.api.libraries.LibraryDefinition
1819
import org.jetbrains.kotlinx.jupyter.config.defaultRepositories
@@ -132,11 +133,11 @@ class InMemoryLibraryResolver(
132133
}
133134

134135
class TestDisplayHandler(val list: MutableList<Any> = mutableListOf()) : DisplayHandler {
135-
override fun handleDisplay(value: Any) {
136+
override fun handleDisplay(value: Any, host: ExecutionHost) {
136137
list.add(value)
137138
}
138139

139-
override fun handleUpdate(value: Any, id: String?) {
140+
override fun handleUpdate(value: Any, host: ExecutionHost, id: String?) {
140141
// TODO: Implement correct updating
141142
}
142143
}

0 commit comments

Comments
 (0)