Untitled
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