Untitled

mail@pastecode.io avatar
unknown
plain_text
5 months ago
9.4 kB
2
Indexable
package com.teamapt.android.pos.hardware_interface.printer

import android.graphics.Bitmap
import android.graphics.Point
import androidx.annotation.DrawableRes
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import com.teamapt.android.pos.hardware_interface.printer.model.ImageFormat
import com.teamapt.android.pos.hardware_interface.printer.model.TextFormat
import com.teamapt.android.pos.hardware_interface.printer.model.elements.*
import com.teamapt.android.pos.hardware_interface.printer.model.enums.PrinterResult
import com.teamapt.android.pos.hardware_interface.printer.model.enums.PrinterStatus
import com.teamapt.android.pos.hardware_interface.printer.model.enums.TextAlignment
import com.teamapt.android.pos.hardware_interface.util.Document
import timber.log.Timber
import kotlin.math.min


const val SPACING = 1
const val COLUMN_1_MAX = 5
const val COLUMN_2_MAX = 12

interface PrinterService {

    /*
    * Available paper width in px. Normally close to 400px after margin deducted
    */
    val paperWidth: Int

    fun setupPage()

    fun getStatus(): PrinterStatus

    /*
    * This represents the actual number of pixels taken up by a character with font size [fontSize]
    */
    fun getFontSizePixels(fontSize: TextFormat.FontSize): Int

    /*
    * Returns the number of characters that can be printed on one line
    * if each character is of [fontSize].
    * The logical width is easier to work with when the text is monospaced and
    * the font size is known
    */
    fun getLogicalWidth(fontSize: TextFormat.FontSize): Int {
        return 2 * paperWidth / getFontSizePixels(fontSize)
    }

    fun addPrintText(
        text: String?,
        startPoint: Point = Point(0, 0),
        alignment: TextAlignment = TextAlignment.CENTER,
        format: TextFormat = TextFormat()
    )

    fun addPrintImage(@DrawableRes image: Int, format: ImageFormat)

    fun addPrintBitmapImage(bitmap: Bitmap, format: ImageFormat)

    fun addPrintNewLine()

    suspend fun print(): PrinterResult

    fun clear()

    fun pushPaperForwardAfterPrinting()

    fun cutPaperAfterPrinting()

    suspend fun printDocument(document: Document): PrinterResult {
        try {
            setupPage()

            for (element in document) {
                when (element) {
                    is KeyValueLine -> printKeyValueLine(element)
                    is SingleLabelLine -> printSingleLabelLine(element)
                    is SeparatorLine -> printSeparatorLine(element)
                    is SingleRowTextLine -> printSingleRowTextLine(element)
                    is NewLine -> addPrintNewLine()
                    is Image -> printImage(element)
                    is BitmapImage -> printBitmapImage(element)
                }
            }
            return print()

        } catch (e: Exception) {
            Timber.e(e)
            Firebase.crashlytics.log("EOD Printer Error Debug is ${e.message}")
            Firebase.crashlytics.recordException(e)
            return PrinterResult.ERROR_UNKNOWN
        }

    }

    private fun printSingleLabelLine(singleLabelLine: SingleLabelLine) {

        // TODO why print SingleLabelLine with KeyValueLine?
        when {
            singleLabelLine.alignment == TextAlignment.LEFT -> {
                printKeyValueLine(
                    KeyValueLine(
                        keySegment = singleLabelLine.labelSegment,
                        textFormat = singleLabelLine.textFormat
                    )
                )
            }
            singleLabelLine.alignment == TextAlignment.RIGHT -> {
                printKeyValueLine(
                    KeyValueLine(
                        valueSegment = singleLabelLine.labelSegment,
                        textFormat = singleLabelLine.textFormat
                    )
                )
            }
            singleLabelLine.alignment == TextAlignment.CENTER -> {
                printCenterAlignedLabel(singleLabelLine)
            }
        }
    }

    private fun printKeyValueLine(keyValueLine: KeyValueLine) {
        if (keyValueLine.keySegment.isEmpty() && keyValueLine.valueSegment.isEmpty()) return

        val logicalWidth = getLogicalWidth(keyValueLine.textFormat.fontSize)

        val bufferArray = CharArray(logicalWidth)
        bufferArray.fill(' ')

        val nextLine = KeyValueLine(textFormat = keyValueLine.textFormat)

        val maxCountForSegment = logicalWidth / 2

        if (keyValueLine.keySegment.isNotEmpty()) {
            if (keyValueLine.leftSegmentCount > maxCountForSegment) {
                val indexOfNextCharacter = copySegmentToCharArrayFromStart(
                    keyValueLine.keySegment,
                    bufferArray, 0, maxCountForSegment - 1
                )
                if (indexOfNextCharacter < keyValueLine.leftSegmentCount) {
                    nextLine.keySegment =
                        LineSegment(keyValueLine.keySegment.text.substring(indexOfNextCharacter))
                }
            } else {
                copySegmentToCharArrayFromStart(
                    keyValueLine.keySegment,
                    bufferArray,
                    0,
                    maxCountForSegment - 1
                )
            }
        }

        if (keyValueLine.valueSegment.isNotEmpty()) {
            if (keyValueLine.rightSegmentCount > maxCountForSegment) {
                val indexOfNextCharacter = copySegmentToCharArrayFromStart(
                    keyValueLine.valueSegment,
                    bufferArray, maxCountForSegment, logicalWidth - 1
                )
                if (indexOfNextCharacter > 0) {
                    nextLine.valueSegment =
                        LineSegment(keyValueLine.valueSegment.text.substring(indexOfNextCharacter))
                }
            } else {
                copySegmentToCharArrayFromStart(
                    keyValueLine.valueSegment,
                    bufferArray,
                    maxCountForSegment,
                    logicalWidth - 1
                )
            }
        }

        val string = String(bufferArray)
        addPrintText(string, format = keyValueLine.textFormat)

        if (nextLine.keySegment.isNotEmpty() || nextLine.valueSegment.isNotEmpty()) {
            printKeyValueLine(nextLine)
        }
    }

    fun printSingleRowTextLine(singleRowTextLine: SingleRowTextLine) {
        val logicalWidth = getLogicalWidth(singleRowTextLine.textFormat.fontSize)
        //logical width for this font size is 37
        val bufferArray = CharArray(logicalWidth)
        bufferArray.fill(' ')
        val spacing = singleRowTextLine.columnBoundaries
        singleRowTextLine.columns.forEachIndexed { index, lineSegment ->
            val columnSpace = spacing.getOrNull(index) ?: IntRange(0, 0)
            copySegmentToCharArrayFromStart(lineSegment, bufferArray, columnSpace.first, columnSpace.last)
        }
        val string = String(bufferArray)
        addPrintText(string, format = singleRowTextLine.textFormat)
    }

    private fun printSeparatorLine(separatorLine: SeparatorLine) {

        val logicalWidth = getLogicalWidth(separatorLine.textFormat.fontSize)

        val bufferArray = CharArray(logicalWidth)
        bufferArray.fill(separatorLine.separator)

        val text = String(bufferArray)

        addPrintText(text)
    }

    private fun printCenterAlignedLabel(singleLabelLine: SingleLabelLine) {

        if (singleLabelLine.labelSegment.isEmpty()) return

        val logicalWidth = getLogicalWidth(singleLabelLine.textFormat.fontSize)

        val bufferArray = CharArray(logicalWidth)
        bufferArray.fill(' ')

        val nextLine = SingleLabelLine(
            textFormat = singleLabelLine.textFormat,
            alignment = singleLabelLine.alignment
        )
        val maxCountForSegment = logicalWidth - 4                // Minimum padding is 2 spaces.

        if (singleLabelLine.count > maxCountForSegment) {
            val indexOfNextCharacter = copySegmentToCharArrayFromStart(
                singleLabelLine.labelSegment,
                bufferArray, 2, logicalWidth - 3
            )
            nextLine.labelSegment =
                LineSegment(singleLabelLine.labelSegment.text.substring(indexOfNextCharacter))
        } else {
            val startIndex = (logicalWidth - singleLabelLine.count) / 2
            copySegmentToCharArrayFromStart(
                singleLabelLine.labelSegment, bufferArray, startIndex, startIndex
                        + singleLabelLine.count
            )
        }

        val string = String(bufferArray)
        addPrintText(string, format = singleLabelLine.textFormat)

        if (nextLine.labelSegment.isNotEmpty()) {
            printSingleLabelLine(nextLine)
        }
    }

    private fun copySegmentToCharArrayFromStart(
        lineSegment: LineSegment, bufferArray: CharArray,
        startIndex: Int, endIndex: Int
    ): Int {
        var i = 0
        var k = startIndex

        while (i < min(lineSegment.text.length, endIndex) && k < bufferArray.size) {
            bufferArray[k++] = lineSegment.text[i++]
        }

        return i
    }

    private fun printImage(image: Image) {
        addPrintImage(image.resource, image.format)
    }

    private fun printBitmapImage(image: BitmapImage) {
        addPrintBitmapImage(image.bitmap, image.format)
    }
}
Leave a Comment