EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / Hardware / ฉลากพิมพ์ไม่ตรงกระดาษ ตั้งระยะเริ่มพิมพ์ให้จบทีเดียว
Hardware

ฉลากพิมพ์ไม่ตรงกระดาษ ตั้งระยะเริ่มพิมพ์ให้จบทีเดียว

โดย Ton-Or ·25 ต.ค. 2026·อ่าน 9 นาที
ฉลากพิมพ์ไม่ตรงกระดาษ ตั้งระยะเริ่มพิมพ์ให้จบทีเดียว

เครื่องพิมพ์ฉลากตัวเล็กไม่มีเซนเซอร์บอกเราว่ากระดาษวางเยื้องไปเท่าไหร่ มันพิมพ์ตรงที่เราบอกให้พิมพ์ทุกครั้ง ปัญหาคือระบบพิกัดที่มันใช้คือขอบหัวพิมพ์ ส่วนระบบพิกัดที่เราคิดอยู่ในหัวคือขอบดวงสติกเกอร์ สองอันนี้ไม่ใช่อันเดียวกัน

พอดีผมต่อเครื่องพิมพ์ฉลากบลูทูธเข้ากับแอปขายหน้าร้านที่เขียนเองเสร็จแล้ว ถึงขั้นที่หัวพิมพ์ยิงออกมาเป็นตัวหนังสืออ่านออกได้ ตอนนั้นคิดว่างานจบแล้ว จนเอาสติกเกอร์ที่พิมพ์เสร็จไปแปะขวดสินค้าจริง แล้วเห็นว่าคำนำหน้าชื่อสินค้าโดนกินหายไปทางซ้าย ส่วนทางขวาเหลือที่ว่างเปล่าอยู่เกือบเซนติเมตร

เล่มนี้เป็นภาคต่อ

ขั้นตอนก่อนหน้านี้ คือการหาว่าเครื่องพิมพ์รับคำสั่งแบบไหนและทำยังไงให้หัวพิมพ์ยิงออกมาได้ ผมเขียนไว้แล้วที่ ดักโปรโตคอลเครื่องพิมพ์ฉลากบลูทูธจาก btsnoop เล่มนี้เริ่มจากจุดที่พิมพ์ออกมาได้แล้ว แต่ไม่ตรงกระดาษ และทุกอย่างในนี้เป็นการวัดจากฮาร์ดแวร์ที่ผมมีอยู่ในมือล้วนๆ คือพิมพ์ออกมาจริงแล้วเอาไม้บรรทัดวัด ไม่ได้ไปยุ่งกับซอฟต์แวร์ของผู้ผลิต

สามอาการที่ต่อกันเป็นขั้นบันได

ลำดับอาการก่อนจะได้ฉลากที่ใช้ได้เครื่องร้อง 2 ครั้งกระดาษเปล่า->กระดาษเลื่อนไม่มีตัวหนังสือ->พิมพ์ออกแต่เยื้อง->ตั้งระยะแล้วตรงดวง

อาการแรกคือเครื่องร้องปิ๊บสองครั้งทุกครั้งที่สั่งพิมพ์ แล้วกระดาษออกมาเปล่า ตอนนั้นผมดีใจที่มันร้อง เพราะแปลว่ามันได้รับข้อมูล ที่จริงแล้วเสียงร้องสองครั้งของเครื่องรุ่นนี้คือเสียงปฏิเสธ ไม่ใช่เสียงยืนยัน มันกำลังบอกว่าไบต์ที่ส่งมาอ่านไม่ออก

บทเรียนข้อแรกเลยเป็นเรื่องง่ายๆ ว่าเสียงจากเครื่องพิมพ์ไม่ได้บอกอะไรเกี่ยวกับหัวพิมพ์เลย ต้องดูที่กระดาษอย่างเดียว

อาการที่สองคือกระดาษเลื่อนออกมาตามจำนวนดวงที่สั่งพอดี แต่ไม่มีหมึกติดสักจุด อันนี้คือเฟิร์มแวร์รับไบต์ไว้แล้วแต่ไม่ยิงหัวพิมพ์ สาเหตุอยู่ที่ลำดับคำสั่งนำหน้าไม่ครบ เครื่องรุ่นนี้ต้องการคำสั่งเปิดงานกับคำสั่งตั้งขอบซ้ายก่อนทุกครั้ง ขาดตัวใดตัวหนึ่งมันกลืนข้อมูลเงียบๆ

object EscPosLabelOpcodes {
    /** ESC @ - เคลียร์บัฟเฟอร์และรีเซ็ตโหมด */
    val RESET: ByteArray = byteArrayOf(0x1B, 0x40)

    /** GS v 0, m=0 - พิมพ์บิตแมปแบบ raster ความเข้มปกติ */
    val PRINT_RASTER_M0: ByteArray = byteArrayOf(0x1D, 0x76, 0x30, 0x00)

    /** คำสั่งเปิดงานฉลาก ขาดตัวนี้เฟิร์มแวร์รับไบต์แล้วไม่ยิงหัวพิมพ์ */
    val BEGIN_LABEL_JOB: ByteArray = byteArrayOf(0x1F, 0x11, 0x0A)

    /** ตั้งขอบซ้ายเป็นหน่วยไบต์ (1 ไบต์ = 8 จุด) */
    fun setLeftMarginBytes(marginByte: Int): ByteArray =
        byteArrayOf(0x1F, 0x11, 0x24, (marginByte and 0xFF).toByte())

    fun feedLines(n: Int): ByteArray =
        byteArrayOf(0x1B, 0x64, (n.coerceIn(0, 255) and 0xFF).toByte())
}

จุดที่คนพลาดกันบ่อยคือ 0x30 ในคำสั่งพิมพ์ raster ครับ มันคือตัวอักษร '0' ที่เป็นส่วนหนึ่งของโอปโค้ด ไม่ใช่พารามิเตอร์ ส่วน 0x00 ตัวหลังต่างหากที่เป็นพารามิเตอร์ความเข้ม ตัดตัวไหนออกก็ได้ผลเดียวกันคือกระดาษเปล่า

ลำดับที่ส่งจริงต่อฉลากหนึ่งดวงเป็นแบบนี้ และส่งซ้ำทั้งชุดทุกดวง ไม่ใช่ส่งหัวครั้งเดียวแล้วยิงรูปรัว

repeat(quantity.coerceAtLeast(1)) {
    out.write(EscPosLabelOpcodes.BEGIN_LABEL_JOB)       // 1F 11 0A
    out.write(EscPosLabelOpcodes.setLeftMarginBytes(0)) // 1F 11 24 00
    out.write(EscPosLabelOpcodes.RESET)                 // 1B 40
    out.write(EscPosLabelOpcodes.PRINT_RASTER_M0)       // 1D 76 30 00
    out.write(rasterBytes)                              // wL wH hL hH + ข้อมูลจุด
    out.flush()
    Thread.sleep(400)
}
Thread.sleep(800)
อย่าสั่งเลื่อนกระดาษเอง

ในชุดโอปโค้ดข้างบนผมมีฟังก์ชัน feedLines เตรียมไว้ แต่ไม่ได้เรียกใช้เลยสักที่ เพราะเฟิร์มแวร์เลื่อนกระดาษไปหารอยต่อดวงถัดไปให้เองอยู่แล้วด้วยเซนเซอร์ของมัน ถ้าเราสั่งเลื่อนซ้ำเข้าไปอีก มันจะเลื่อนสองเท่า กินสติกเกอร์ทิ้งดวงหนึ่งทุกครั้งที่พิมพ์ ตรงนี้ต่างจากเครื่องพิมพ์ใบเสร็จกระดาษต่อเนื่องที่ต้องสั่งเลื่อนท้ายงานเสมอ

พอพิมพ์ออกแล้ว ปัญหาเปลี่ยนเป็นเรื่องเรขาคณิตล้วนๆ

พอหัวพิมพ์ยิงได้ ที่เหลือไม่เกี่ยวกับโปรโตคอลอีกแล้วครับ มันกลายเป็นเรื่องว่าเราวางรูปลงบนผืนผ้าใบตรงไหน ตัวเข้ารหัส raster รับบิตแมปที่กว้างเท่ากับความกว้างหัวพิมพ์เป๊ะๆ เท่านั้น ไม่มีการเติมขอบให้

val wBytes = headDots / 8          // 384 จุด -> 48 ไบต์ต่อแถว
val h = bmp.height
val out = ByteArray(4 + wBytes * h)
out[0] = (wBytes and 0xFF).toByte()          // ความกว้างเป็น "ไบต์"
out[1] = ((wBytes ushr 8) and 0xFF).toByte()
out[2] = (h and 0xFF).toByte()               // ความสูงเป็น "จุด"
out[3] = ((h ushr 8) and 0xFF).toByte()
require(headDots % 8 == 0)
// ถ้าบิตแมปกว้างไม่เท่าหัวพิมพ์ ให้ผู้เรียกไปเติมขอบมาก่อน
require(bmp.width == headDots) {
    "bitmap ${bmp.width}x$h ไม่ตรงกับ head=$headDots"
}

การแพ็กบิตเป็นแบบบิตสูงอยู่ซ้าย และ 1 คือยิงจุด ตรงนี้ถ้ากลับด้านจะได้ฉลากดำทั้งดวง ซึ่งเป็นความผิดพลาดที่เห็นปุ๊บรู้ปั๊บ ไม่อันตราย

for (xb in 0 until wBytes) {
    var packed = 0
    for (bit in 0..7) {
        val c = rowPixels[xb * 8 + bit]
        val dark = if (Color.alpha(c) < 16) false else luminance(c) < threshold
        if (dark) packed = packed or (1 shl (7 - bit))
    }
    out[idx++] = packed.toByte()
}

ค่าคงที่ที่ต้องรู้ตัวเดียวคือความละเอียด เครื่องกลุ่มนี้ส่วนใหญ่เป็น 8 จุดต่อมิลลิเมตร (203 dpi) บางรุ่นใหญ่เป็น 12 จุดต่อมิลลิเมตร ผมเก็บไว้ที่รุ่นเครื่อง ไม่ได้เก็บไว้ที่กระดาษ เพราะมันเป็นคุณสมบัติของหัวพิมพ์

/** ความละเอียดหัวพิมพ์ หน่วยเป็นจุดต่อมิลลิเมตร (8 = 203dpi, 12 = 300dpi) */
val dotsPerMm: Int = 8,
/** เลื่อนเนื้อหาไปขวา (+) หรือซ้าย (-) หน่วยมิลลิเมตร */
val adjustXMm: Double = 0.0,
/** เลื่อนเนื้อหาลง (+) หรือขึ้น (-) หน่วยมิลลิเมตร */
val adjustYMm: Double = 0.0,

สามค่าที่คนละเรื่องกัน แต่ถูกจับมารวมเป็นค่าเดียว

นี่คือใจกลางของปัญหาทั้งหมดครับ เวลาเราพูดว่า "กระดาษ 38 x 25" มันมีค่าที่ต้องแยกจากกันอยู่สามค่า แล้วผมเอามาปนกันตั้งแต่แรก

384 จุดความกว้างหัว
48 มม.หัวพิมพ์กว้าง
38x25ขนาดกระดาษ
30x20พื้นที่พิมพ์

หนึ่ง ความกว้างหัวพิมพ์ 384 จุด ที่ 8 จุดต่อมิลลิเมตร คือ 48 มม. นี่คือความกว้างของผืนผ้าใบที่เราส่งไปได้ ไม่เกี่ยวกับกระดาษที่ใส่อยู่เลย

สอง ขนาดกระดาษจริง 38 x 25 มม. คือดวงสติกเกอร์ที่ลอกออกมาแปะ แคบกว่าหัวพิมพ์อยู่ 10 มม.

สาม พื้นที่พิมพ์จริง ประมาณ 30 x 20 มม. คือส่วนที่หมึกติดแล้วไม่ล้นออกนอกดวง กระดาษ 38 กว้างก็จริง แต่ขอบรอบดวงกับรอยตัดกินไปข้างละ 4 มม. ถ้าเราพิมพ์เต็ม 38 หมึกจะไปติดบนกระดาษรองกาวแล้วเลอะลูกกลิ้ง

ความกว้างหัวพิมพ์ 48 มม. (384 จุด)8 จุด/มม.กระดาษ 38 x 25 มม.แคบกว่าหัวพิมพ์ 10 มม.พื้นที่พิมพ์ 30 x 20 มม.ขอบข้างละ 4 มม.กลไกนำกระดาษดันกระดาษชิดไปด้านหนึ่ง ต้องมีค่าชดเชย adjustX / adjustYวาดแถบชิดซ้ายเพื่อเทียบสัดส่วนความกว้างเท่านั้น
ความกว้างสามค่าที่ต้องแยกกัน หัวพิมพ์กว้าง 48 มม. กระดาษกว้าง 38 มม. และพื้นที่พิมพ์กว้าง 30 มม. โดยขอบรอบดวงกินไปข้างละ 4 มม.

โค้ดเดิมของผมคำนวณจุดวางแบบนี้ ซึ่งอ่านแล้วดูสมเหตุสมผลมาก

val headWidthMm = headDots.toDouble() / dpmm            // 384 / 8 = 48.0
val centerXMm = (headWidthMm - paper.printableWidthMm) / 2.0
val centerYMm = (paper.heightMm - paper.printableHeightMm) / 2.0
val xPosDots = ((centerXMm + paper.adjustXMm) * dpmm).toInt()
val yPosDots = ((centerYMm + paper.adjustYMm) * dpmm).toInt()

val canvas = Bitmap.createBitmap(headDots, canvasH, Bitmap.Config.ARGB_8888)
c.drawColor(Color.WHITE)
c.drawBitmap(content, xPosDots.toFloat(), yPosDots.toFloat(), null)

สังเกตความไม่สมมาตรตรงสองบรรทัดกลางนะครับ แนวนอนผมจัดกลางเทียบกับหัวพิมพ์ ส่วนแนวตั้งจัดกลางเทียบกับดวงกระดาษ ที่เป็นแบบนี้เพราะผมตั้งสมมติฐานไว้เองว่ากระดาษจะถูกวางกึ่งกลางหัวพิมพ์พอดี

ของจริงไม่ใช่แบบนั้นครับ กลไกนำกระดาษของเครื่องรุ่นนี้ดันกระดาษชิดไปด้านหนึ่ง ไม่ได้อยู่กึ่งกลาง ค่าที่ผมคำนวณได้เลยเลื่อนไปทั้งแถบ ตัวหนังสือด้านซ้ายโดนกินหาย ส่วนขวาเหลือที่ว่าง นี่คือที่มาของค่าชดเชยที่ต้องมี

เลิกเดา แล้วพิมพ์ไม้บรรทัดออกมาวัด

รอบแรกผมแก้แบบเดา คือใส่ 5 มม. แล้วพิมพ์ดู ยังไม่พอ ใส่ 8 ยังไม่พอ ใส่ 12 ถึงตรง เสียสติกเกอร์ไปสิบกว่าดวงกับเวลาอีกครึ่งชั่วโมง เพราะทุกรอบผมต้องเดาจากการมองว่า "น่าจะขาดไปอีกนิดนึง"

เดาแล้วพิมพ์ซ้ำ

ไม่รู้ว่าขาดไปเท่าไหร่

ต้องพิมพ์ฉลากจริงทุกรอบ แล้วเทียบด้วยสายตาว่าเยื้องไปมากน้อยแค่ไหน ประมาณค่าถัดไปจากความรู้สึก ถ้าเปลี่ยนขนาดกระดาษหรือเปลี่ยนเครื่อง ก็ต้องเริ่มเดาใหม่หมด

พิมพ์ไม้บรรทัดออกมา

อ่านค่าจากดวงที่พิมพ์ได้เลย

ขีดสเกลมิลลิเมตรอยู่บนดวงสติกเกอร์แล้ว มองปุ๊บรู้ว่าขอบซ้ายหายไปกี่มิลลิเมตร ใส่ค่านั้นลงไปตรงๆ จบในหนึ่งหรือสองรอบ และใช้ได้กับกระดาษทุกขนาด

ตัวสร้างภาพทดสอบผมวาดสี่อย่างลงบนผืนผ้าใบที่มีขนาดเท่ากับพื้นที่พิมพ์ พอดี ไม่ใช่ขนาดกระดาษ เพราะเราต้องการรู้ว่าพื้นที่พิมพ์ที่เราประกาศไว้มันไปตกอยู่ตรงไหนของดวงจริง

  1. กรอบรอบพื้นที่พิมพ์ทั้งผืน ถ้าพิมพ์ออกมาแล้วขอบด้านไหนหายไป แปลว่าล้นออกนอกดวงด้านนั้น
  2. หมุดสี่มุมที่หน้าตาไม่เหมือนกันสักมุม ขนาด 3 มม. คือสี่เหลี่ยมทึบ สี่เหลี่ยมโปร่ง วงกลมทึบ วงกลมโปร่ง ทำให้ดูดวงเดียวก็รู้ว่าภาพกลับหัวหรือกลับด้านหรือเปล่า
  3. กากบาทกลางดวง แขนยาวข้างละ 2 มม. ไว้เทียบจุดกึ่งกลางจริงกับกึ่งกลางที่ตาเห็น
  4. ไม้บรรทัดมิลลิเมตรตามขอบบนและขอบซ้าย ขีดเล็กทุก 1 มม. ขีดใหญ่พร้อมตัวเลขทุก 4 มม.
  5. เวลาที่พิมพ์กำกับไว้ เพราะพอทดสอบไปสิบกว่ารอบ ดวงที่กองอยู่บนโต๊ะจะเรียงไม่ถูกว่าอันไหนมาก่อนมาหลัง
val mk = (3 * dpmm).toFloat()
c.drawRect(0f, 0f, mk, mk, fill)                      // มุมบนซ้าย สี่เหลี่ยมทึบ
c.drawRect(w - mk, 0f, w.toFloat(), mk, pen)          // มุมบนขวา สี่เหลี่ยมโปร่ง
c.drawCircle(mk / 2, h - mk / 2, mk / 2, fill)        // มุมล่างซ้าย วงกลมทึบ
c.drawCircle(w - mk / 2, h - mk / 2, mk / 2, pen)     // มุมล่างขวา วงกลมโปร่ง

for (mm in 0..paper.printableWidthMm.toInt()) {
    val x = (mm * dpmm).toFloat().coerceAtMost((w - 1).toFloat())
    val long = mm % 4 == 0
    c.drawLine(x, 0f, x, if (long) (2 * dpmm).toFloat() else (dpmm / 2f), pen)
    if (long) c.drawText(mm.toString(), x + 1f, 3.2f * dpmm, numPaint)
}

จุดสำคัญที่ทำให้วงจรทดสอบสั้นลงมากคือ ภาพทดสอบต้องพิมพ์ด้วยค่าที่กำลังปรับอยู่ตอนนั้น ยังไม่ได้กดบันทึก คนหน้าร้านกดลูกศรปรับทีละครึ่งมิลลิเมตร กดพิมพ์ทดสอบ ดูดวงที่ออกมา กดปรับต่อ จนพอใจแล้วค่อยกดบันทึกทีเดียว

// เอาค่าที่ยังไม่ได้บันทึกมาทับสเปกกระดาษ ให้ดวงทดสอบตรงกับที่กำลังปรับ
val tuned = paper.copy(adjustXMm = s.currentX, adjustYMm = s.currentY)

ผมจำกัดช่วงปรับไว้ที่บวกลบ 15 มม. ทีละ 0.5 มม. เกินกว่านั้นแปลว่าเลือกขนาดกระดาษผิดตั้งแต่ต้น ไม่ใช่เรื่องที่จะมาแก้ด้วยค่าชดเชย และปุ่มต้องใหญ่พอให้คนขายของกดด้วยนิ้วโป้งได้ ไม่ใช่ปุ่มขนาดที่ต้องเล็งด้วยปลายเล็บ

เก็บค่าชดเชยเป็นรายเครื่อง ไม่ใช่รายรุ่น

ค่าที่ได้จากการวัดผมไม่ได้เอาไปแก้ในโค้ด แต่เก็บลงที่เก็บค่าตั้งของแอป โดยคีย์ด้วยคู่ของหมายเลขเครื่อง กับ รหัสชนิดกระดาษ

fun save(printerSn: String, paperId: String, calibration: Calibration) {
    prefs.edit()
        .putString(keyFor(printerSn, paperId),
                   "${calibration.adjustXMm}|${calibration.adjustYMm}")
        .apply()
}

/** ไม่มีค่าที่บันทึกไว้ = ใช้ค่าเริ่มต้นของสเปกกระดาษนั้น */
fun resolved(printerSn: String, paper: PaperSpec): PaperSpec {
    val o = loadOverride(printerSn, paper.id) ?: return paper
    return paper.copy(adjustXMm = o.adjustXMm, adjustYMm = o.adjustYMm)
}

ที่ต้องคีย์ด้วยหมายเลขเครื่อง ไม่ใช่ชื่อรุ่น เพราะเครื่องรุ่นเดียวกันสองตัวให้ค่าไม่เท่ากันครับ กลไกนำกระดาษเป็นงานประกอบ มีค่าคลาดเคลื่อนของมันอยู่ ร้านที่มีเครื่องพิมพ์สองตัวสำรองกันเลยต้องคาลิเบรตทีละตัว ส่วนกระดาษก็ต้องแยกด้วย เพราะสติกเกอร์คนละล็อตกันความกว้างกระดาษรองไม่เท่ากัน

และในทางเดินของการสั่งพิมพ์จริง ต้องเรียกผ่านตัวรวมค่าตัวนี้เสมอ ห้ามไปหยิบค่าจากสเปกกระดาษดิบๆ ใช้เอง ไม่งั้นค่าที่คนหน้าร้านนั่งปรับไว้จะไม่มีผลอะไรเลย ซึ่งเป็นบั๊กที่หายากมากเพราะทุกอย่างดูถูกไปหมด

เรื่องที่แก้จากฝั่งแอปไม่ได้

โหมดกระดาษอยู่ในเฟิร์มแวร์ ไม่ได้อยู่ในคำสั่งพิมพ์

เครื่องกลุ่มนี้จำไว้ในตัวมันเองว่าตอนนี้ใส่กระดาษต่อเนื่องหรือสติกเกอร์ตัดเป็นดวงที่มีช่องว่างให้เซนเซอร์อ่าน ค่านี้ตั้งครั้งเดียวตอนจับคู่เครื่องแล้วค้างอยู่ในเครื่อง ชุดคำสั่งพิมพ์ของเราไม่ได้ส่งค่านี้ไปด้วย แปลว่าถ้าใครเอาเครื่องใหม่มาตั้งเป็นโหมดกระดาษต่อเนื่องแล้วใส่สติกเกอร์เข้าไป มันจะพิมพ์คร่อมรอยต่อดวง และไม่มีอะไรในแอปแก้ได้เลย ต้องไปตั้งที่ตัวเครื่อง ตรงนี้ผมเขียนไว้ในคู่มือหน้าร้านเป็นข้อแรก เพราะอาการมันเหมือนกับค่าชดเชยผิดมาก

อีกเรื่องที่ต้องเผื่อไว้คือระยะเงียบรอบบาร์โค้ด ผมตั้งขอบข้างของบาร์โค้ดเป็นศูนย์ ให้แท่งกินเต็มความกว้างพื้นที่พิมพ์ แล้วยืมขอบกระดาษที่พิมพ์ไม่ถึงข้างละ 4 มม. เป็นระยะเงียบแทน วิธีนี้ได้บาร์โค้ดใหญ่ที่สแกนง่ายกว่าเดิมมาก แต่มันใช้ได้ก็ต่อเมื่อค่าชดเชยถูกต้องเท่านั้น ถ้าเยื้องไปข้างเดียว ระยะเงียบด้านนั้นจะหายไปแล้วเครื่องสแกนอ่านไม่ออก

val padL = 2.5f * dpmm     // ซ้ายเผื่อมากกว่าขวานิดหน่อยตามที่หน้าร้านบอกมา
val padR = 2.0f * dpmm
val padT = 1.0f * dpmm
val padB = 0.6f * dpmm
val bcPadX = 0f            // บาร์โค้ดกินเต็มความกว้าง ยืมขอบกระดาษเป็นระยะเงียบ

ส่วนความสูง ผมกันไว้ให้แท่งบาร์โค้ดประมาณ 8 มม. บนฉลากสูง 20 มม. เพราะแท่งเตี้ยกว่านั้นกล้องของเครื่องสแกนราคาถูกจับไม่ติด และปิดการลบรอยหยักของตัวอักษรทั้งหมด เพราะสุดท้ายตัวเข้ารหัสตัดสินขาวดำที่ค่าเดียวอยู่แล้ว พิกเซลสีเทาที่ได้มาจากการลบรอยหยักมีแต่ทำให้ตัวอักษรอ้วนขึ้นแบบเดาไม่ได้

สรุปเป็นลำดับที่ผมใช้กับเครื่องใหม่ทุกตัว

ส่วนตัวผมคิดว่าบทเรียนของงานนี้ไม่ใช่เรื่องโอปโค้ดครับ มันคือการยอมเสียเวลาครึ่งชั่วโมงเขียนตัวพิมพ์ไม้บรรทัด แทนที่จะเดาไปเรื่อยๆ ทีละครึ่งมิลลิเมตร ตัวพิมพ์ทดสอบใช้เวลาเขียนน้อยกว่าเวลาที่ผมเสียไปกับการเดารอบเดียวด้วยซ้ำ แล้วมันใช้ได้ตลอดไปกับเครื่องทุกตัวที่ร้านซื้อมาเพิ่ม

ลำดับที่ผมใช้ตอนนี้คือ ตั้งโหมดกระดาษที่ตัวเครื่องให้ตรงกับสติกเกอร์ที่ใส่ก่อน แล้วเลือกขนาดกระดาษในแอป แล้วพิมพ์ไม้บรรทัดหนึ่งดวง อ่านค่าที่ขาดจากขีดสเกล ใส่ค่านั้นลงไป พิมพ์ยืนยันอีกดวง แล้วกดบันทึกผูกกับเครื่องตัวนั้น จบภายในสองดวง

ใครกำลังทำแอปที่ต้องพิมพ์ฉลาก แนะนำให้แยกสามค่าที่ผมพูดถึงออกจากกันตั้งแต่ออกแบบโครงข้อมูลเลยครับ ขนาดกระดาษ พื้นที่พิมพ์ และค่าชดเชย อย่ายุบเหลือค่าเดียวเพราะมันดูซ้ำซ้อน เพราะวันที่ร้านเปลี่ยนซัพพลายเออร์สติกเกอร์ ค่าที่ต้องแก้มีแค่ค่าเดียวในสามค่านั้น ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ

เคยเจองานแบบนี้ไหม? ติดตรงไหน?

มาแลกวิธีกันต่อในบอร์ด LinuxThai

→ คุยต่อในบอร์ด

ความคิดเห็น 0

ยังไม่มีความคิดเห็น — เป็นคนแรกเลย