summaryrefslogtreecommitdiff
path: root/modules/zero-bridge-api/src/commonMain/kotlin/jp/orgflow/bridge/HostDocumentIo.kt
blob: b2bc60d2489729fb838d07e1f1379dc4f2576718 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package jp.orgflow.bridge

import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json

interface HostDocumentIo {
    suspend fun importSlides(bytes: ByteArray): List<SlideSpecData>
    suspend fun renderSlidePng(bytes: ByteArray, index: Int): ByteArray?
    suspend fun exportPptx(slides: List<SlideSpecData>): ByteArray
    suspend fun importXlsxGrid(bytes: ByteArray, sheet: Int): List<List<String>>
    suspend fun exportXlsxGrid(rows: List<List<String>>, formulas: Map<String, String>): ByteArray
}

@Serializable
data class SlideSpecData(
    val id: String,
    val title: String = "",
    val backgroundArgb: Long? = null,
    val boxes: List<SlideSpecBox> = emptyList(),
    val images: Map<String, ByteArray> = emptyMap(),
    val notes: String = "",
) {
    override fun equals(other: Any?): Boolean =
        other is SlideSpecData &&
            id == other.id &&
            title == other.title &&
            backgroundArgb == other.backgroundArgb &&
            boxes == other.boxes &&
            images.size == other.images.size &&
            images.all { (key, value) -> other.images[key]?.contentEquals(value) == true } &&
            notes == other.notes

    override fun hashCode(): Int {
        var result = id.hashCode()
        result = 31 * result + title.hashCode()
        result = 31 * result + (backgroundArgb?.hashCode() ?: 0)
        result = 31 * result + boxes.hashCode()
        result = 31 * result + images.entries.fold(0) { acc, entry -> 31 * acc + entry.key.hashCode() * 31 + entry.value.contentHashCode() }
        result = 31 * result + notes.hashCode()
        return result
    }

    companion object {
        const val WIDTH_PT = 720.0
        const val HEIGHT_PT = 405.0
    }
}

@Serializable
enum class SlideSpecBoxKind {
    TEXT,
    IMAGE,
    TABLE,
}

@Serializable
data class SlideSpecBox(
    val kind: SlideSpecBoxKind,
    val xPt: Double,
    val yPt: Double,
    val widthPt: Double,
    val heightPt: Double,
    val textRuns: List<SlideSpecTextRun> = emptyList(),
    val imageRef: String? = null,
    val tableCells: List<List<String>> = emptyList(),
)

@Serializable
data class SlideSpecTextRun(
    val text: String,
    val bold: Boolean = false,
    val italic: Boolean = false,
    val sizePt: Double? = null,
    val colorArgb: Long? = null,
)

object SlideSpecCodec {
    private val json = Json { ignoreUnknownKeys = true }

    fun encode(slides: List<SlideSpecData>): String =
        json.encodeToString(ListSerializer(SlideSpecData.serializer()), slides)

    fun decode(text: String): List<SlideSpecData> =
        json.decodeFromString(ListSerializer(SlideSpecData.serializer()), text)
}