/* * libptouch — raster layout, transpose, P-touch print job (ESC/P bulk) * * Author: knb * Email: knb@artif.org */ #include "libptouch_internal.h" #include "libptouch_layout.h" #include "libptouch_protocol.h" #include #include #include #include static void pack_line(uint8_t *line, size_t line_bytes, unsigned head_dots, const uint8_t *row, uint32_t width_dots, uint16_t left_dots, uint16_t print_dots) { memset(line, 0, line_bytes); if (width_dots > (uint32_t)print_dots) return; uint32_t start = (uint32_t)left_dots + ((uint32_t)print_dots - width_dots) / 2u; for (uint32_t x = 0; x < width_dots; x++) { uint32_t ubyte = x / 8u; uint32_t ubit = 7u - (x % 8u); if (((row[ubyte] >> ubit) & 1u) == 0) continue; uint32_t dot = start + x; if (dot >= (uint32_t)head_dots) break; uint32_t b = dot / 8u; uint32_t bi = 7u - (dot % 8u); line[b] |= (uint8_t)(1u << bi); } } static uint8_t *transpose_raster_alloc(const uint8_t *in, uint32_t W, uint32_t H, uint32_t *outW, uint32_t *outH) { *outW = H; *outH = W; size_t old_rb = (W + 7u) / 8u; size_t new_rb = (H + 7u) / 8u; uint8_t *out = (uint8_t *)calloc(new_rb * W, 1); if (!out) return NULL; for (uint32_t y = 0; y < H; y++) { for (uint32_t x = 0; x < W; x++) { size_t ob = (size_t)y * old_rb + x / 8u; int bit = (int)((in[ob] >> (7u - x % 8u)) & 1u); if (!bit) continue; uint32_t nx = y; uint32_t ny = x; size_t nb = (size_t)ny * new_rb + nx / 8u; out[nb] |= (uint8_t)(1u << (7u - nx % 8u)); } } return out; } libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx, const uint8_t *data, size_t data_len, const libptouch_raster_params_t *params) { libptouch_err_t v = libptouch_check_raster(ctx, data, data_len, params); if (v != LIBPTOUCH_OK) return v; if (!ctx->usb_open || !ctx->bulk_out_ep) { ptouch_set_error(ctx, LIBPTOUCH_ERR_IO, "not connected"); return LIBPTOUCH_ERR_IO; } uint8_t st[LIBPTOUCH_STATUS_LENGTH]; v = libptouch_get_status(ctx, st); if (v != LIBPTOUCH_OK) return v; const ptouch_printer_profile_t *prof = ptouch_layout_resolve_profile(ctx->usb_pid, st[4]); if (!prof) { ptouch_set_error(ctx, LIBPTOUCH_ERR_UNSUPPORTED, "no layout profile for this printer"); return LIBPTOUCH_ERR_UNSUPPORTED; } uint8_t media_kind = st[11]; uint8_t media_w = st[10]; uint16_t left_dots, print_dots, right_dots; v = ptouch_layout_from_status(prof, media_kind, media_w, &left_dots, &print_dots, &right_dots); if (v != LIBPTOUCH_OK) { ptouch_set_error(ctx, LIBPTOUCH_ERR_UNSUPPORTED, "tape width/layout not supported for this media " "(check status media bytes)"); return LIBPTOUCH_ERR_UNSUPPORTED; } (void)right_dots; unsigned head_dots = prof->head_width_dots; size_t line_payload = ptouch_line_payload_bytes(head_dots); size_t gf_packet = 3u + line_payload; uint32_t wd = params->width_dots; uint32_t ht = params->height_dots; uint8_t *transposed = transpose_raster_alloc(data, wd, ht, &wd, &ht); if (!transposed) { ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM, "transpose raster: out of memory"); return LIBPTOUCH_ERR_NOMEM; } const uint8_t *src = transposed; if (wd > (uint32_t)print_dots) { char buf[160]; snprintf(buf, sizeof(buf), "image width %u dots > printable %u for loaded tape", (unsigned)wd, (unsigned)print_dots); ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, buf); free(transposed); return LIBPTOUCH_ERR_ARG; } double margin_dpi = prof->margin_feed_dpi; unsigned margin_max = prof->margin_feed_max_dots; unsigned margin_dots = 14u; if (params->margin_mm > 0) { margin_dots = (unsigned)((double)params->margin_mm * margin_dpi / 25.4 + 0.5); if (margin_dots < 14u) margin_dots = 14u; if (margin_dots > margin_max) margin_dots = margin_max; } uint32_t lines = ht; uint8_t head[256]; size_t pos = 0; memset(head + pos, 0, 200); pos += 200u; static const uint8_t esc_at[] = { 0x1B, 0x40 }; memcpy(head + pos, esc_at, sizeof(esc_at)); pos += sizeof(esc_at); static const uint8_t raster_mode[] = { 0x1B, 0x69, 0x61, 0x01 }; memcpy(head + pos, raster_mode, sizeof(raster_mode)); pos += sizeof(raster_mode); uint8_t esc_iz[13]; ptouch_fill_esc_iz(esc_iz, media_kind, media_w, lines); memcpy(head + pos, esc_iz, sizeof(esc_iz)); pos += sizeof(esc_iz); static const uint8_t esc_im[] = { 0x1B, 0x69, 0x4D, 0x40 }; memcpy(head + pos, esc_im, sizeof(esc_im)); pos += sizeof(esc_im); /* * ESC i A ("cut each n labels") is not supported on PT-P710BT class * devices. Sending it can trigger a communication error (red LED blink). * Keep it only for 560-dot family. */ if (prof->head_width_dots > 128u) { static const uint8_t esc_ia[] = { 0x1B, 0x69, 0x41, 0x01 }; memcpy(head + pos, esc_ia, sizeof(esc_ia)); pos += sizeof(esc_ia); } uint8_t esc_ik[] = { 0x1B, 0x69, 0x4B, ptouch_esc_ik_value(head_dots) }; memcpy(head + pos, esc_ik, sizeof(esc_ik)); pos += sizeof(esc_ik); uint8_t esc_id[] = { 0x1B, 0x69, 0x64, (uint8_t)(margin_dots & 0xFFu), (uint8_t)((margin_dots >> 8) & 0xFFu) }; memcpy(head + pos, esc_id, sizeof(esc_id)); pos += sizeof(esc_id); static const uint8_t mode_m[] = { 0x4D, 0x00 }; memcpy(head + pos, mode_m, sizeof(mode_m)); pos += sizeof(mode_m); v = ptouch_bulk_send_job(ctx, head, pos, "print preamble"); if (v != LIBPTOUCH_OK) { free(transposed); return v; } size_t row_b = ((size_t)wd + 7u) / 8u; uint8_t *gbuf = (uint8_t *)malloc(gf_packet); if (!gbuf) { ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM, "raster line buffer"); free(transposed); return LIBPTOUCH_ERR_NOMEM; } for (uint32_t y = 0; y < lines; y++) { const uint8_t *row = src + (size_t)y * row_b; pack_line(gbuf + 3, line_payload, head_dots, row, wd, left_dots, print_dots); ptouch_fill_gf_header(gbuf, line_payload); v = ptouch_bulk_send_job(ctx, gbuf, gf_packet, "raster line"); if (v != LIBPTOUCH_OK) { free(gbuf); free(transposed); return v; } } free(gbuf); /* * Some 560-dot family devices are sensitive to end-of-page sequencing. * Send FF (0x0C) then Control-Z (0x1A) on that family to explicitly * terminate page and final feed/cut. */ if (head_dots > 128u) { static const uint8_t page_end[] = { 0x0C }; v = ptouch_bulk_send_job(ctx, page_end, sizeof(page_end), "print page end"); if (v != LIBPTOUCH_OK) { free(transposed); return v; } } static const uint8_t print_end[] = { 0x1A }; v = ptouch_bulk_send_job(ctx, print_end, sizeof(print_end), "print end"); if (v != LIBPTOUCH_OK) { free(transposed); return v; } uint8_t sink[64]; int tr = 0; (void)libusb_bulk_transfer(ctx->usb_handle, ctx->bulk_in_ep, sink, (int)sizeof(sink), &tr, 3000); (void)tr; free(transposed); ptouch_set_error(ctx, LIBPTOUCH_OK, ""); return LIBPTOUCH_OK; }