To enable features such as document saving, loading, and potential real-time collaboration, store strokes and associated data in a serialized format. For the Ink API, manual serialization and deserialization are necessary.

To accurately restore a stroke, save its`Brush`and`StrokeInputBatch`.

- [`Brush`](https://developer.android.com/reference/kotlin/androidx/ink/brush/Brush): Includes numeric fields (size, epsilon), color, and[`BrushFamily`](https://developer.android.com/reference/kotlin/androidx/ink/brush/BrushFamily).
- [`StrokeInputBatch`](https://developer.android.com/reference/kotlin/androidx/ink/strokes/StrokeInputBatch): A list of input points with numeric fields.

#### Basic serialization

Define a serialization object structure that mirrors the Ink library objects.

Encode the serialized data using your preferred framework like Gson, Moshi, Protobuf, and others, and use compression for optimization.  

    data class SerializedStroke(
      val inputs: SerializedStrokeInputBatch,
      val brush: SerializedBrush
    )

    data class SerializedBrush(
      val size: Float,
      val color: Long,
      val epsilon: Float,
      val stockBrush: SerializedStockBrush
    )

    enum class SerializedStockBrush {
      MARKER_V1,
      PRESSURE_PEN_V1,
      HIGHLIGHTER_V1
    }

    data class SerializedStrokeInputBatch(
      val toolType: SerializedToolType,
      val strokeUnitLengthCm: Float,
      val inputs: List<SerializedStrokeInput>
    )

    data class SerializedStrokeInput(
      val x: Float,
      val y: Float,
      val timeMillis: Float,
      val pressure: Float,
      val tiltRadians: Float,
      val orientationRadians: Float,
      val strokeUnitLengthCm: Float
    )

    enum class SerializedToolType {
      STYLUS,
      TOUCH,
      MOUSE,
      UNKNOWN
    }

    class Converters {

      private val gson: Gson = GsonBuilder().create()

      companion object {
        private val stockBrushToEnumValues =
          mapOf(
            StockBrushes.markerV1 to SerializedStockBrush.MARKER_V1,
            StockBrushes.pressurePenV1 to SerializedStockBrush.PRESSURE_PEN_V1,
            StockBrushes.highlighterV1 to SerializedStockBrush.HIGHLIGHTER_V1,
          )

        private val enumToStockBrush =
          stockBrushToEnumValues.entries.associate { (key, value) -> value to key }
      }

      private fun serializeBrush(brush: Brush): SerializedBrush {
        return SerializedBrush(
          size = brush.size,
          color = brush.colorLong,
          epsilon = brush.epsilon,
          stockBrush = stockBrushToEnumValues[brush.family] ?: SerializedStockBrush.MARKER_V1,
        )
      }

      private fun serializeStrokeInputBatch(inputs: StrokeInputBatch): SerializedStrokeInputBatch {
        val serializedInputs = mutableListOf<SerializedStrokeInput>()
        val scratchInput = StrokeInput()

        for (i in 0 until inputs.size) {
          inputs.populate(i, scratchInput)
          serializedInputs.add(
            SerializedStrokeInput(
              x = scratchInput.x,
              y = scratchInput.y,
              timeMillis = scratchInput.elapsedTimeMillis.toFloat(),
              pressure = scratchInput.pressure,
              tiltRadians = scratchInput.tiltRadians,
              orientationRadians = scratchInput.orientationRadians,
              strokeUnitLengthCm = scratchInput.strokeUnitLengthCm,
            )
          )
        }

        val toolType =
          when (inputs.getToolType()) {
            InputToolType.STYLUS -> SerializedToolType.STYLUS
            InputToolType.TOUCH -> SerializedToolType.TOUCH
            InputToolType.MOUSE -> SerializedToolType.MOUSE
            else -> SerializedToolType.UNKNOWN
          }

        return SerializedStrokeInputBatch(
          toolType = toolType,
          strokeUnitLengthCm = inputs.getStrokeUnitLengthCm(),
          inputs = serializedInputs,
        )
      }

      private fun deserializeStroke(serializedStroke: SerializedStroke): Stroke? {
        val inputs = deserializeStrokeInputBatch(serializedStroke.inputs) ?: return null
        val brush = deserializeBrush(serializedStroke.brush) ?: return null
        return Stroke(brush = brush, inputs = inputs)
      }

      private fun deserializeBrush(serializedBrush: SerializedBrush): Brush {
        val stockBrushFamily = enumToStockBrush[serializedBrush.stockBrush] ?: StockBrushes.markerV1

        return Brush.createWithColorLong(
          family = stockBrushFamily,
          colorLong = serializedBrush.color,
          size = serializedBrush.size,
          epsilon = serializedBrush.epsilon,
        )
      }

      private fun deserializeStrokeInputBatch(
        serializedBatch: SerializedStrokeInputBatch
      ): StrokeInputBatch {
        val toolType =
          when (serializedBatch.toolType) {
            SerializedToolType.STYLUS -> InputToolType.STYLUS
            SerializedToolType.TOUCH -> InputToolType.TOUCH
            SerializedToolType.MOUSE -> InputToolType.MOUSE
            else -> InputToolType.UNKNOWN
          }

        val batch = MutableStrokeInputBatch()

        serializedBatch.inputs.forEach { input ->
          batch.addOrThrow(
            type = toolType,
            x = input.x,
            y = input.y,
            elapsedTimeMillis = input.timeMillis.toLong(),
            pressure = input.pressure,
            tiltRadians = input.tiltRadians,
            orientationRadians = input.orientationRadians,
          )
        }

        return batch
      }

      fun serializeStrokeToEntity(stroke: Stroke): StrokeEntity {
        val serializedBrush = serializeBrush(stroke.brush)
        val serializedInputs = serializeStrokeInputBatch(stroke.inputs)
        return StrokeEntity(
          brushSize = serializedBrush.size,
          brushColor = serializedBrush.color,
          brushEpsilon = serializedBrush.epsilon,
          stockBrush = serializedBrush.stockBrush,
          strokeInputs = gson.toJson(serializedInputs),
        )
      }

      fun deserializeEntityToStroke(entity: StrokeEntity): Stroke {
        val serializedBrush =
          SerializedBrush(
            size = entity.brushSize,
            color = entity.brushColor,
            epsilon = entity.brushEpsilon,
            stockBrush = entity.stockBrush,
          )

        val serializedInputs =
          gson.fromJson(entity.strokeInputs, SerializedStrokeInputBatch::class.java)

        val brush = deserializeBrush(serializedBrush)
        val inputs = deserializeStrokeInputBatch(serializedInputs)

        return Stroke(brush = brush, inputs = inputs)
      }

      fun brushToString(brush: Brush): String {
            val serializedBrush = serializeBrush(brush)
            return gson.toJson(serializedBrush)
        }

      fun stringToBrush(jsonString: String): Brush {
            val serializedBrush = gson.fromJson(jsonString, SerializedBrush::class.java)
            return deserializeBrush(serializedBrush)
        }

    }