diff --git a/src/main/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode.kt b/src/main/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode.kt new file mode 100644 index 0000000..9126eca --- /dev/null +++ b/src/main/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode.kt @@ -0,0 +1,9 @@ +package com.sainsburys.k2zpl.command.barcode + +import com.sainsburys.k2zpl.command.ZplCommand +import com.sainsburys.k2zpl.command.options.ZplFieldOrientation + +internal abstract class BarCode : ZplCommand { + abstract val orientation: ZplFieldOrientation + abstract val height: Int +} \ No newline at end of file diff --git a/src/main/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode128.kt b/src/main/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode128.kt new file mode 100644 index 0000000..8e276eb --- /dev/null +++ b/src/main/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode128.kt @@ -0,0 +1,75 @@ +package com.sainsburys.k2zpl.command.barcode + +import com.sainsburys.k2zpl.builder.ZplBuilder +import com.sainsburys.k2zpl.builder.toZplYesNo +import com.sainsburys.k2zpl.command.ZplParameters +import com.sainsburys.k2zpl.command.fieldData +import com.sainsburys.k2zpl.command.fieldOrigin +import com.sainsburys.k2zpl.command.fieldSeparator +import com.sainsburys.k2zpl.command.options.ZplBarCode128Mode +import com.sainsburys.k2zpl.command.options.ZplFieldOrientation +import com.sainsburys.k2zpl.command.options.ZplYesNo +import com.sainsburys.k2zpl.command.zplParameters + +internal data class BarCode128( + override val orientation: ZplFieldOrientation, + override val height: Int, + val line: ZplYesNo, + val lineAbove: ZplYesNo, + val checkDigit: ZplYesNo, + val mode: ZplBarCode128Mode +) : BarCode() { + + init { + require(height in 1..32000) { "Height must be between 1 and 32000" } + } + + override val command = "^BC" + + override val parameters: ZplParameters = zplParameters( + "o" to orientation.code, + "h" to height, + "l" to line, + "la" to lineAbove.toString(), + "c" to checkDigit.toString(), + "m" to mode.toString() + ) +} + +/** + * Creates a Code 128 barcode marker + * @param data data encoded in the barcode + * @param x horizontal position + * @param y vertical position + * @param orientation The orientation of the barcode. + * @param height The height of the barcode. + * @param interpretationLine print interpretation line + * @param lineAbove print interpretation line above code + * @param checkDigit UCC check digit + * @param mode barcode mode + */ +fun ZplBuilder.barcode128( + data: String, + x: Int, + y: Int, + orientation: ZplFieldOrientation = ZplFieldOrientation.NORMAL, + height: Int, + interpretationLine: Boolean = false, + lineAbove: Boolean = false, + checkDigit: Boolean = false, + mode: ZplBarCode128Mode = ZplBarCode128Mode.NONE +) { + fieldOrigin(x, y) + command( + BarCode128( + orientation = orientation, + height = height, + line = interpretationLine.toZplYesNo(), + lineAbove = lineAbove.toZplYesNo(), + checkDigit = checkDigit.toZplYesNo(), + mode = mode + ) + ) + fieldData(data) + fieldSeparator() +} diff --git a/src/main/kotlin/com/sainsburys/k2zpl/command/BarCode.kt b/src/main/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode39.kt similarity index 59% rename from src/main/kotlin/com/sainsburys/k2zpl/command/BarCode.kt rename to src/main/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode39.kt index 207a914..69ee543 100644 --- a/src/main/kotlin/com/sainsburys/k2zpl/command/BarCode.kt +++ b/src/main/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode39.kt @@ -1,30 +1,34 @@ -package com.sainsburys.k2zpl.command +package com.sainsburys.k2zpl.command.barcode import com.sainsburys.k2zpl.builder.ZplBuilder import com.sainsburys.k2zpl.builder.toZplYesNo -import com.sainsburys.k2zpl.command.options.ZplBarcodeType +import com.sainsburys.k2zpl.command.ZplParameters +import com.sainsburys.k2zpl.command.fieldData +import com.sainsburys.k2zpl.command.fieldOrigin +import com.sainsburys.k2zpl.command.fieldSeparator import com.sainsburys.k2zpl.command.options.ZplFieldOrientation import com.sainsburys.k2zpl.command.options.ZplYesNo +import com.sainsburys.k2zpl.command.zplParameters -internal data class BarCode( - val type: ZplBarcodeType, - val orientation: ZplFieldOrientation, +internal data class BarCode39( + override val height: Int, + override val orientation: ZplFieldOrientation, val checkDigit: ZplYesNo, - val height: Int, - val line: Int, - val lineAbove: ZplYesNo -) : ZplCommand { + val line: ZplYesNo, + val lineAbove: ZplYesNo, +) : BarCode() { + init { require(height in 1..32000) { "Height must be between 1 and 32000" } - require(line in 1..7) { "Line thickness must be between 1 and 7" } } - override val command: CharSequence = "^B1" + override val command = "^B3" + override val parameters: ZplParameters = zplParameters( "o" to orientation.code, "c" to checkDigit.toString(), "h" to height, - "l" to line, + "l" to line.toString(), "la" to lineAbove.toString() ) } @@ -34,32 +38,29 @@ internal data class BarCode( * @param data data encoded in the barcode * @param x horizontal position * @param y vertical position - * @param barcodeType Barcode type * @param orientation The orientation of the barcode. - * @param checkDigit Whether to include a check digit. + * @param checkDigit Mod-43 check digit * @param height The height of the barcode. - * @param lineThickness The line thickness of the barcode. - * @param lineAbove Whether to include a line above the barcode. + * @param interpretationLine print interpretation line + * @param lineAbove print interpretation line above code */ -fun ZplBuilder.barcode( +fun ZplBuilder.barcode39( data: String, x: Int, y: Int, - height: Int, - lineThickness: Int, - barcodeType: ZplBarcodeType = ZplBarcodeType.CODE_39, orientation: ZplFieldOrientation = ZplFieldOrientation.NORMAL, checkDigit: Boolean = false, + height: Int, + interpretationLine: Boolean = false, lineAbove: Boolean = false ) { fieldOrigin(x, y) command( - BarCode( - type = barcodeType, + BarCode39( orientation = orientation, checkDigit = checkDigit.toZplYesNo(), height = height, - line = lineThickness, + line = interpretationLine.toZplYesNo(), lineAbove = lineAbove.toZplYesNo() ) ) diff --git a/src/main/kotlin/com/sainsburys/k2zpl/command/options/ZplBarCode128Mode.kt b/src/main/kotlin/com/sainsburys/k2zpl/command/options/ZplBarCode128Mode.kt new file mode 100644 index 0000000..45672c1 --- /dev/null +++ b/src/main/kotlin/com/sainsburys/k2zpl/command/options/ZplBarCode128Mode.kt @@ -0,0 +1,12 @@ +package com.sainsburys.k2zpl.command.options + +enum class ZplBarCode128Mode(val value: String) { + NONE("N"), + UCC("U"), + AUTOMATIC("A"), + UCC_EAN("D"); + + override fun toString(): String { + return value + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/sainsburys/k2zpl/command/options/ZplBarcodeType.kt b/src/main/kotlin/com/sainsburys/k2zpl/command/options/ZplBarcodeType.kt deleted file mode 100644 index 1e38afc..0000000 --- a/src/main/kotlin/com/sainsburys/k2zpl/command/options/ZplBarcodeType.kt +++ /dev/null @@ -1,7 +0,0 @@ -@file:Suppress("UNUSED") - -package com.sainsburys.k2zpl.command.options - -enum class ZplBarcodeType { - CODE_39 -} diff --git a/src/test/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode128Test.kt b/src/test/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode128Test.kt new file mode 100644 index 0000000..5f76881 --- /dev/null +++ b/src/test/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode128Test.kt @@ -0,0 +1,111 @@ +package com.sainsburys.k2zpl.command.barcode + +import com.sainsburys.k2zpl.command.options.ZplBarCode128Mode +import com.sainsburys.k2zpl.command.options.ZplFieldOrientation +import com.sainsburys.k2zpl.command.options.ZplYesNo +import com.sainsburys.k2zpl.k2zpl +import com.sainsburys.k2zpl.testBuildString +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.IsolationMode +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.data.forAll +import io.kotest.data.headers +import io.kotest.data.row +import io.kotest.data.table +import io.kotest.matchers.shouldBe + +class BarCode128Test : DescribeSpec({ + isolationMode = IsolationMode.InstancePerLeaf + + val subject = BarCode128( + orientation = ZplFieldOrientation.NORMAL, + height = 10, + line = ZplYesNo.NO, + lineAbove = ZplYesNo.NO, + checkDigit = ZplYesNo.NO, + mode = ZplBarCode128Mode.NONE + ) + + describe("BarCode128") { + it("outputs correct command") { + val result = subject.testBuildString() + result shouldBe "^BCN,10,N,N,N,N" + } + it("uses orientation parameter properly") { + ZplFieldOrientation.entries.forEach { + subject.copy(orientation = it).testBuildString() shouldBe "^BC${it.code},10,N,N,N,N" + } + } + it("uses height parameter properly") { + subject.copy(height = 100).testBuildString() shouldBe "^BCN,100,N,N,N,N" + } + it("uses line parameter properly") { + ZplYesNo.entries.forEach { + subject.copy(line = it).testBuildString() shouldBe "^BCN,10,${it},N,N,N" + } + } + it("uses lineAbove parameter properly") { + ZplYesNo.entries.forEach { + subject.copy(lineAbove = it).testBuildString() shouldBe "^BCN,10,N,${it},N,N" + } + } + it("uses checkDigit parameter properly") { + ZplYesNo.entries.forEach { + subject.copy(checkDigit = it).testBuildString() shouldBe "^BCN,10,N,N,${it},N" + } + } + it("uses mode parameter properly") { + ZplBarCode128Mode.entries.forEach { + subject.copy(mode = it).testBuildString() shouldBe "^BCN,10,N,N,N,${it}" + } + } + it("requires valid parameters") { + table( + headers("height"), + row(32001), + row(0), + ).forAll { height -> + shouldThrow { + subject.copy( + height = height + ) + } + } + } + } + describe("barcode128 extension") { + it("outputs the correct command") { + val result = k2zpl { + barcode128( + data = "1234567890", + x = 10, + y = 10, + height = 10, + checkDigit = true, + lineAbove = true, + interpretationLine = true, + mode = ZplBarCode128Mode.UCC + ) + } + result shouldBe """ + ^FO10,10,0 + ^BCN,10,Y,Y,Y,U + ^FD1234567890 + ^FS + + """.trimIndent() + } + it("uses default values") { + val result = k2zpl { + barcode128(data = "1234567890", x = 10, y = 10, height = 10) + } + result shouldBe """ + ^FO10,10,0 + ^BCN,10,N,N,N,N + ^FD1234567890 + ^FS + + """.trimIndent() + } + } +}) \ No newline at end of file diff --git a/src/test/kotlin/com/sainsburys/k2zpl/command/BarCodeTest.kt b/src/test/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode39Test.kt similarity index 59% rename from src/test/kotlin/com/sainsburys/k2zpl/command/BarCodeTest.kt rename to src/test/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode39Test.kt index 6cf0443..c0889b6 100644 --- a/src/test/kotlin/com/sainsburys/k2zpl/command/BarCodeTest.kt +++ b/src/test/kotlin/com/sainsburys/k2zpl/command/barcode/BarCode39Test.kt @@ -1,6 +1,5 @@ -package com.sainsburys.k2zpl.command +package com.sainsburys.k2zpl.command.barcode -import com.sainsburys.k2zpl.command.options.ZplBarcodeType import com.sainsburys.k2zpl.command.options.ZplFieldOrientation import com.sainsburys.k2zpl.command.options.ZplYesNo import com.sainsburys.k2zpl.k2zpl @@ -14,75 +13,75 @@ import io.kotest.data.row import io.kotest.data.table import io.kotest.matchers.shouldBe -class BarCodeTest : DescribeSpec({ +class BarCode39Test : DescribeSpec({ isolationMode = IsolationMode.InstancePerLeaf - val barCode = BarCode( - type = ZplBarcodeType.CODE_39, + val subject = BarCode39( orientation = ZplFieldOrientation.NORMAL, checkDigit = ZplYesNo.NO, height = 10, - line = 7, - lineAbove = ZplYesNo.YES + line = ZplYesNo.NO, + lineAbove = ZplYesNo.NO ) - describe("Barcode") { + describe("Barcode39") { it("outputs correct command") { - val result = barCode.testBuildString() - result shouldBe "^B1N,N,10,7,Y" + val result = subject.testBuildString() + result shouldBe "^B3N,N,10,N,N" } it("uses orientation parameter properly") { ZplFieldOrientation.entries.forEach { - barCode.copy(orientation = it).testBuildString() shouldBe "^B1${it.code},N,10,7,Y" + subject.copy(orientation = it).testBuildString() shouldBe "^B3${it.code},N,10,N,N" } } + it("uses height parameter properly") { + subject.copy(height = 100).testBuildString() shouldBe "^B3N,N,100,N,N" + } it("uses checkDigit parameter properly") { ZplYesNo.entries.forEach { - barCode.copy(checkDigit = it).testBuildString() shouldBe "^B1N,${it},10,7,Y" + subject.copy(checkDigit = it).testBuildString() shouldBe "^B3N,${it},10,N,N" + } + } + it("uses line parameter properly") { + ZplYesNo.entries.forEach { + subject.copy(line = it).testBuildString() shouldBe "^B3N,N,10,${it},N" } } it("uses lineAbove parameter properly") { ZplYesNo.entries.forEach { - barCode.copy(lineAbove = it).testBuildString() shouldBe "^B1N,N,10,7,${it}" + subject.copy(lineAbove = it).testBuildString() shouldBe "^B3N,N,10,N,${it}" } } it("requires valid parameters") { table( - headers("height", "line"), - row(32001, 1), - row(0, 1), - row(1, 0), - row(1, 8) - ).forAll { height, line -> + headers("height"), + row(32001), + row(0), + ).forAll { height -> shouldThrow { - barCode.copy( - height = height, - line = line, + subject.copy( + height = height ) } } } - } - describe("barcode extension") { + describe("barcode39 extension") { it("outputs the correct command") { val result = k2zpl { - barcode( + barcode39( data = "1234567890", x = 10, y = 10, height = 10, - lineThickness = 7, - barcodeType = ZplBarcodeType.CODE_39, - orientation = ZplFieldOrientation.NORMAL, checkDigit = false, lineAbove = true ) } result shouldBe """ ^FO10,10,0 - ^B1N,N,10,7,Y + ^B3N,N,10,N,Y ^FD1234567890 ^FS @@ -90,11 +89,11 @@ class BarCodeTest : DescribeSpec({ } it("uses default values") { val result = k2zpl { - barcode(data = "1234567890", x = 10, y = 10, height = 10, lineThickness = 7) + barcode39(data = "1234567890", x = 10, y = 10, height = 10, interpretationLine = true) } result shouldBe """ ^FO10,10,0 - ^B1N,N,10,7,N + ^B3N,N,10,Y,N ^FD1234567890 ^FS