From 21a85b4501789bd57179595e6df7bc50c85644fb Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 1 May 2024 13:26:19 +0300 Subject: [PATCH] Add image rendering --- .../document/data/loremIpsum/SPC-logo.png | Bin 0 -> 5166 bytes examples/document/data/loremIpsum/chapter2.md | 3 +- .../document/data/loremIpsum/document.yaml | 20 ++-- examples/document/src/jvmMain/kotlin/main.kt | 1 - .../space/kscience/snark/html/SnarkHtml.kt | 5 + .../snark/html/document/DocumentBuilder.kt | 87 ++++++++---------- .../snark/html/document/DocumentFragment.kt | 2 +- .../html/document/FtlDocumentProcessor.kt | 2 +- .../html/document/RegexDocumentProcessor.kt | 4 +- 9 files changed, 60 insertions(+), 64 deletions(-) create mode 100644 examples/document/data/loremIpsum/SPC-logo.png diff --git a/examples/document/data/loremIpsum/SPC-logo.png b/examples/document/data/loremIpsum/SPC-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..953de16d4876765010935ffc20cbd1d37351dbb6 GIT binary patch literal 5166 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 dG+}ahdgIi>+tx>p41sTb1Y0Y6EEhcx^&il = ParseAction(this) + public val layoutAction: Action = Action.mapping { + + } + private val allDataNotNull: DataSelector get() = DataSelector { workspace, _ -> workspace.data.filterByType() } diff --git a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/DocumentBuilder.kt b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/DocumentBuilder.kt index 6b7fa68..cb7aedb 100644 --- a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/DocumentBuilder.kt +++ b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/DocumentBuilder.kt @@ -7,14 +7,12 @@ import space.kscience.dataforge.context.info import space.kscience.dataforge.context.logger import space.kscience.dataforge.context.request import space.kscience.dataforge.data.* +import space.kscience.dataforge.io.Binary import space.kscience.dataforge.meta.Laminate import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.cutLast -import space.kscience.dataforge.names.endsWith -import space.kscience.dataforge.names.parseAsName +import space.kscience.dataforge.names.* import space.kscience.snark.SnarkBuilder import space.kscience.snark.SnarkContext import space.kscience.snark.html.* @@ -26,7 +24,7 @@ import kotlin.reflect.typeOf @SnarkBuilder public interface DocumentBuilder : SnarkContext { - public val documentName: Name + public val route: Name public val documentMeta: Meta @@ -51,7 +49,7 @@ private class PageBasedDocumentBuilder( val page: PageContextWithData, private val dataRootName: Name, ) : DocumentBuilder { - override val documentName: Name get() = page.pageRoute + override val route: Name get() = page.pageRoute override val documentMeta: Meta get() = page.pageMeta override val data: DataTree<*> = page.data.branch(dataRootName) ?: DataTree.EMPTY @@ -63,11 +61,10 @@ private class PageBasedDocumentBuilder( override suspend fun fragment(fragment: DocumentFragment, overrideMeta: Meta?) { when (fragment) { - is ImageDocumentFragment -> fragment { figure("snark-figure") { img(classes = "snark-image") { - src = fragment.path + src = resolveRef(this@PageBasedDocumentBuilder.route.toWebPath() + "/" + fragment.ref) alt = fragment.meta["alt"].string ?: "" } fragment.meta["caption"].string?.let { caption -> @@ -115,53 +112,41 @@ private class PageBasedDocumentBuilder( } public fun SiteContextWithData.document( - documentName: Name, - documentMeta: Meta = Meta.EMPTY, - headers: MetaDataContent.() -> Unit = {}, - block: suspend DocumentBuilder.() -> Unit, -): Unit = page(documentName, documentMeta) { - val documentBuilder = runBlocking { PageBasedDocumentBuilder(page, documentName).apply { block() } } - head { - title(documentMeta["title"].string ?: "Snark document") - headers() - } - body { - postprocess(FtlDocumentProcessor(this@document.context, documentBuilder)) { - documentBuilder.fragments.forEach { - fragment(it) - } - } - } -} - -public fun SiteContextWithData.document( - route: Name, dataName: Name, - descriptor: DocumentDescriptor, + descriptor: DocumentDescriptor = DocumentDescriptor.empty(), + route: Name = dataName, headers: MetaDataContent.() -> Unit = {}, -): Unit = page(route, descriptor.documentMeta ?: Meta.EMPTY) { - val documentBuilder = runBlocking { - PageBasedDocumentBuilder(page, dataName).apply { - descriptor.fragments.forEach { - fragment(it) + documentBlock: DocumentBuilder.() -> Unit = {}, +): Unit { + siteData.branch(dataName)?.filterByType()?.forEach { + static(route + it.name.last(), it.data) + } + page(route, descriptor.documentMeta ?: Meta.EMPTY) { + //TODO think about avoiding blocking + val documentBuilder = runBlocking { + PageBasedDocumentBuilder(page, dataName).apply { + descriptor.fragments.forEach { + fragment(it) + } + documentBlock() } } - } - head { - title(descriptor.title ?: "Snark document") - headers() - } - body { - h1("title") { +(descriptor.title ?: dataName.toString()) } - descriptor.authors.forEach { - div("author") { - div("author-name") { +it.name } - it.affiliation?.let { affiliation -> div("author-affiliation") { +affiliation } } - } + head { + title(descriptor.title ?: "Snark document") + headers() } - postprocess(FtlDocumentProcessor(this@document.context, documentBuilder)) { - documentBuilder.fragments.forEach { - fragment(it) + body { + h1("title") { +(descriptor.title ?: dataName.toString()) } + descriptor.authors.forEach { + div("author") { + div("author-name") { +it.name } + it.affiliation?.let { affiliation -> div("author-affiliation") { +affiliation } } + } + } + postprocess(FtlDocumentProcessor(this@document.context, documentBuilder)) { + documentBuilder.fragments.forEach { + fragment(it) + } } } } @@ -178,9 +163,9 @@ public fun SiteContextWithData.allDocuments( val route = descriptor.route?.parseAsName(false) ?: directory context.logger.info { "Loading document $route" } document( - route = route, dataName = directory, descriptor = descriptor, + route = route, headers = headers ) } diff --git a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/DocumentFragment.kt b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/DocumentFragment.kt index 998c642..87a81f3 100644 --- a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/DocumentFragment.kt +++ b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/DocumentFragment.kt @@ -20,7 +20,7 @@ public class MarkupDocumentFragment( @Serializable @SerialName("image") public class ImageDocumentFragment( - public val path: String, + public val ref: String, override val meta: Meta = Meta.EMPTY, ) : DocumentFragment diff --git a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/FtlDocumentProcessor.kt b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/FtlDocumentProcessor.kt index 12a5b9c..b55672f 100644 --- a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/FtlDocumentProcessor.kt +++ b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/FtlDocumentProcessor.kt @@ -59,7 +59,7 @@ public class FtlDocumentProcessor( } private val data = mapOf( - "documentName" to document.documentName.toStringUnescaped(), + "documentName" to document.route.toStringUnescaped(), "label" to TemplateMethodModelEx { args: List -> val counter = args.getOrNull(0)?.toString() ?: "@default" diff --git a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/RegexDocumentProcessor.kt b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/RegexDocumentProcessor.kt index 944a952..c72d26e 100644 --- a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/RegexDocumentProcessor.kt +++ b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/document/RegexDocumentProcessor.kt @@ -42,7 +42,7 @@ public class RegexDocumentProcessor(public val document: DocumentBuilder) : Text when (match.groups["function"]?.value) { "documentName" -> { - document.documentName.toStringUnescaped() + document.route.toStringUnescaped() } "label" -> { @@ -77,7 +77,7 @@ public class RegexDocumentProcessor(public val document: DocumentBuilder) : Text }.replace(attributeRegex) { match -> val uri = URI(match.groups["uri"]!!.value) val snarkUrl = when (uri.authority) { - "documentName" -> document.documentName.toStringUnescaped() + "documentName" -> document.route.toStringUnescaped() // "ref" -> page.resolveRef(uri.path) "meta" -> document.documentMeta[uri.path.parseAsName()].string ?: "@null" else -> match.value