From add08ba9b21f258858dda58d74d74b0ae57d9396 Mon Sep 17 00:00:00 2001 From: knb Date: Mon, 13 Apr 2026 09:43:33 +0900 Subject: [PATCH] =?UTF-8?q?libptouch=20=E3=82=92=E8=A4=87=E6=95=B0?= =?UTF-8?q?=E3=82=BD=E3=83=BC=E3=82=B9=E3=81=AB=E5=88=86=E5=89=B2=E3=81=97?= =?UTF-8?q?=20src/lib=20=E3=81=AB=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - core / usb / print / status / png と libptouch_internal.h に分割 - 旧単一ファイル src/libptouch.c を削除 - CMake のソース一覧と include パスを更新 - README・libptouch.h の参照パスを追随 Made-with: Cursor --- CMakeLists.txt | 18 +- README.md | 4 +- include/libptouch.h | 2 +- src/lib/libptouch_core.c | 100 +++ src/lib/libptouch_internal.h | 37 ++ src/lib/libptouch_png.c | 164 +++++ src/lib/libptouch_print.c | 296 +++++++++ src/lib/libptouch_status.c | 432 +++++++++++++ src/lib/libptouch_usb.c | 196 ++++++ src/libptouch.c | 1164 ---------------------------------- 10 files changed, 1244 insertions(+), 1169 deletions(-) create mode 100644 src/lib/libptouch_core.c create mode 100644 src/lib/libptouch_internal.h create mode 100644 src/lib/libptouch_png.c create mode 100644 src/lib/libptouch_print.c create mode 100644 src/lib/libptouch_status.c create mode 100644 src/lib/libptouch_usb.c delete mode 100644 src/libptouch.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a936d9..a971ca4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,8 +21,16 @@ configure_file( @ONLY ) -add_library(ptouch STATIC src/libptouch.c) -add_library(ptouch_shared SHARED src/libptouch.c) +set(LIBPTOUCH_SOURCES + src/lib/libptouch_core.c + src/lib/libptouch_usb.c + src/lib/libptouch_print.c + src/lib/libptouch_status.c + src/lib/libptouch_png.c +) + +add_library(ptouch STATIC ${LIBPTOUCH_SOURCES}) +add_library(ptouch_shared SHARED ${LIBPTOUCH_SOURCES}) set_target_properties(ptouch_shared PROPERTIES OUTPUT_NAME ptouch SOVERSION ${PROJECT_VERSION_MAJOR}) target_include_directories(ptouch PUBLIC @@ -30,11 +38,17 @@ target_include_directories(ptouch PUBLIC "$" "$" ) +target_include_directories(ptouch PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src/lib" +) target_include_directories(ptouch_shared PUBLIC "$" "$" "$" ) +target_include_directories(ptouch_shared PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src/lib" +) target_link_libraries(ptouch PRIVATE PkgConfig::LIBUSB PNG::PNG) target_link_libraries(ptouch_shared PRIVATE PkgConfig::LIBUSB PNG::PNG) if(NOT MSVC) diff --git a/README.md b/README.md index 6acfaa9..f869569 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Brother P-touch シリーズ向けのラベル印刷用 **C コアライブラ | パス | 内容 | |------|------| | `include/libptouch.h` | 公開 API | -| `src/libptouch.c` | ライブラリ本体(スタブ) | +| `src/lib/libptouch_*.c` | ライブラリ本体(core / usb / print / status / png) | | `src/cli/main.c` | `ptouch-print` エントリ | | `samples/` | 試験用サンプル画像の置き場(PNG 等) | | `ruby/` | Ruby FFI gem(`libptouch`)・コマンド `ptouch-print-png`(PNG のみ)— `ruby/README.md` | @@ -80,7 +80,7 @@ cmake --build build ## PT-P900W / Linux でのメモ - 接続は **libusb-1.0** のみ。機種ごとに **VID/PID**(`lsusb` 等)を調べ、`libptouch_open_usb_vid_pid` に渡すか、既定の PT-P900W なら `libptouch_open_usb` を使います。 -- ラスターコマンドの詳細は **`reference/` の PDF** および Brother 公開資料に沿って `src/libptouch.c` に実装してください。 +- ラスターコマンドの詳細は **`reference/` の PDF** および Brother 公開資料に沿って `src/lib/libptouch_*.c` に実装してください。 ### Ubuntu で sudo なしで USB を開く(udev) diff --git a/include/libptouch.h b/include/libptouch.h index 7e82748..2929d4f 100644 --- a/include/libptouch.h +++ b/include/libptouch.h @@ -8,7 +8,7 @@ /** * libptouch — Brother P-touch ラスター印刷 (USB) 用 C API * - * 対象例: PT-P900W(ラスターコマンド)。実装は src/libptouch.c を参照。 + * 対象例: PT-P900W(ラスターコマンド)。実装は src/lib/libptouch_*.c を参照。 */ #ifndef LIBPTOUCH_H diff --git a/src/lib/libptouch_core.c b/src/lib/libptouch_core.c new file mode 100644 index 0000000..2b5473e --- /dev/null +++ b/src/lib/libptouch_core.c @@ -0,0 +1,100 @@ +/* + * libptouch — context, errors, raster validation + * + * Author: knb + * Email: knb@artif.org + */ + +#include "libptouch_internal.h" + +#include +#include +#include + +void ptouch_set_error(libptouch_ctx *ctx, libptouch_err_t code, const char *msg) +{ + if (!ctx) + return; + ctx->last_code = code; + if (msg) { + snprintf(ctx->last_msg, sizeof(ctx->last_msg), "%s", msg); + } else { + ctx->last_msg[0] = '\0'; + } +} + +void ptouch_set_error_usb(libptouch_ctx *ctx, int libusb_err, const char *what) +{ + char buf[256]; + snprintf(buf, sizeof(buf), "%s: %s", what, + libusb_strerror((enum libusb_error)libusb_err)); + ptouch_set_error(ctx, LIBPTOUCH_ERR_USB, buf); +} + +libptouch_ctx *libptouch_create(void) +{ + libptouch_ctx *ctx = calloc(1, sizeof(*ctx)); + if (!ctx) + return NULL; + ctx->last_code = LIBPTOUCH_OK; + ctx->last_msg[0] = '\0'; + ctx->usb_open = 0; + ctx->usb_ctx = NULL; + ctx->usb_handle = NULL; + ctx->claimed_interface = -1; + ctx->bulk_out_ep = 0; + ctx->bulk_in_ep = 0; + return ctx; +} + +void libptouch_destroy(libptouch_ctx *ctx) +{ + if (!ctx) + return; + libptouch_close(ctx); + free(ctx); +} + +const char *libptouch_strerror(const libptouch_ctx *ctx) +{ + if (!ctx) + return "(null ctx)"; + return ctx->last_msg[0] ? ctx->last_msg : "ok"; +} + +libptouch_err_t libptouch_last_error(const libptouch_ctx *ctx) +{ + if (!ctx) + return LIBPTOUCH_ERR_ARG; + return ctx->last_code; +} + +libptouch_err_t libptouch_check_raster(libptouch_ctx *ctx, + const uint8_t *data, size_t data_len, + const libptouch_raster_params_t *params) +{ + if (!ctx || !params) { + if (ctx) + ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, "null argument"); + return LIBPTOUCH_ERR_ARG; + } + if (!data && data_len > 0) { + ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, "data is null but len > 0"); + return LIBPTOUCH_ERR_ARG; + } + + size_t row = (params->width_dots + 7u) / 8u; + size_t expected = (size_t)params->height_dots * row; + if (data_len != expected) { + char buf[160]; + snprintf(buf, sizeof(buf), + "data_len mismatch: got %zu, expected %zu (w=%u h=%u)", + data_len, expected, params->width_dots, + params->height_dots); + ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, buf); + return LIBPTOUCH_ERR_ARG; + } + + ptouch_set_error(ctx, LIBPTOUCH_OK, ""); + return LIBPTOUCH_OK; +} diff --git a/src/lib/libptouch_internal.h b/src/lib/libptouch_internal.h new file mode 100644 index 0000000..0d07b12 --- /dev/null +++ b/src/lib/libptouch_internal.h @@ -0,0 +1,37 @@ +/* + * libptouch — internal declarations (not installed) + * + * Author: knb + * Email: knb@artif.org + */ + +#ifndef LIBPTOUCH_INTERNAL_H +#define LIBPTOUCH_INTERNAL_H + +#include "libptouch.h" + +#include +#include + +struct libptouch_ctx { + libptouch_err_t last_code; + char last_msg[256]; + int usb_open; + struct libusb_context *usb_ctx; + struct libusb_device_handle *usb_handle; + int claimed_interface; /* -1 if none */ + uint8_t bulk_out_ep; + uint8_t bulk_in_ep; +}; + +void ptouch_set_error(libptouch_ctx *ctx, libptouch_err_t code, const char *msg); +void ptouch_set_error_usb(libptouch_ctx *ctx, int libusb_err, const char *what); + +int ptouch_bulk_out(libusb_device_handle *h, uint8_t ep, const uint8_t *data, + int len, unsigned int timeout_ms); +int ptouch_bulk_in_exact(libusb_device_handle *h, uint8_t ep, uint8_t *buf, + int len, unsigned int timeout_ms); +libptouch_err_t ptouch_bulk_send_job(libptouch_ctx *ctx, const uint8_t *buf, + size_t len, const char *what); + +#endif /* LIBPTOUCH_INTERNAL_H */ diff --git a/src/lib/libptouch_png.c b/src/lib/libptouch_png.c new file mode 100644 index 0000000..4c45d74 --- /dev/null +++ b/src/lib/libptouch_png.c @@ -0,0 +1,164 @@ +/* + * libptouch — PNG → 1bit packed raster (libpng) + * + * Author: knb + * Email: knb@artif.org + */ + +#include "libptouch_internal.h" + +#include +#include +#include +#include + +#include + +void libptouch_free_raster(uint8_t *raster) +{ + free(raster); +} + +libptouch_err_t libptouch_png_file_to_raster(libptouch_ctx *ctx, const char *path, + const libptouch_png_options_t *options, + uint8_t **out_raster, size_t *out_raster_bytes, + libptouch_raster_params_t *out_params) +{ + if (!ctx || !path || !out_raster || !out_raster_bytes || !out_params) { + if (ctx) + ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, "null argument"); + return LIBPTOUCH_ERR_ARG; + } + + uint8_t thr = LIBPTOUCH_PNG_DEFAULT_THRESHOLD; + if (options) + thr = options->threshold; + + *out_raster = NULL; + *out_raster_bytes = 0; + out_params->width_dots = 0; + out_params->height_dots = 0; + out_params->margin_mm = 0; + + FILE *fp = fopen(path, "rb"); + if (!fp) { + char buf[192]; + snprintf(buf, sizeof(buf), "open %s: %s", path, strerror(errno)); + ptouch_set_error(ctx, LIBPTOUCH_ERR_IO, buf); + return LIBPTOUCH_ERR_IO; + } + + unsigned char sig[8]; + if (fread(sig, 1, 8, fp) != 8) { + fclose(fp); + ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "short read (not a PNG?)"); + return LIBPTOUCH_ERR_IMAGE; + } + if (png_sig_cmp(sig, 0, 8) != 0) { + fclose(fp); + ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "not a PNG file"); + return LIBPTOUCH_ERR_IMAGE; + } + + png_structp png_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + fclose(fp); + ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM, "png_create_read_struct failed"); + return LIBPTOUCH_ERR_NOMEM; + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + fclose(fp); + ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM, "png_create_info_struct failed"); + return LIBPTOUCH_ERR_NOMEM; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "PNG decode error"); + return LIBPTOUCH_ERR_IMAGE; + } + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, 8); + + const int transforms = (int)(PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16 | + PNG_TRANSFORM_GRAY_TO_RGB); + png_read_png(png_ptr, info_ptr, transforms, NULL); + + png_uint_32 width = png_get_image_width(png_ptr, info_ptr); + png_uint_32 height = png_get_image_height(png_ptr, info_ptr); + int channels = (int)png_get_channels(png_ptr, info_ptr); + png_bytepp rows = png_get_rows(png_ptr, info_ptr); + + if (width == 0 || height == 0 || !rows) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "invalid PNG dimensions"); + return LIBPTOUCH_ERR_IMAGE; + } + if (width > 100000u || height > 100000u) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "PNG dimensions too large"); + return LIBPTOUCH_ERR_IMAGE; + } + if (channels != 3 && channels != 4) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "unsupported PNG channel count"); + return LIBPTOUCH_ERR_IMAGE; + } + + size_t row_bytes = ((size_t)width + 7u) / 8u; + if (height > SIZE_MAX / row_bytes) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "raster size overflow"); + return LIBPTOUCH_ERR_IMAGE; + } + size_t total = row_bytes * (size_t)height; + uint8_t *out = (uint8_t *)calloc(1, total); + if (!out) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM, "calloc raster failed"); + return LIBPTOUCH_ERR_NOMEM; + } + + for (png_uint_32 y = 0; y < height; y++) { + const png_byte *row = rows[y]; + uint8_t *dst_row = out + (size_t)y * row_bytes; + for (png_uint_32 x = 0; x < width; x++) { + size_t o = (size_t)x * (size_t)channels; + unsigned r = row[o + 0]; + unsigned g = row[o + 1]; + unsigned b = row[o + 2]; + unsigned a = channels == 4 ? row[o + 3] : 255u; + if (a < 128u) { + continue; + } + unsigned yv = (77u * r + 150u * g + 29u * b) >> 8; + int black = (yv < (unsigned)thr) ? 1 : 0; + if (!black) + continue; + size_t bit = (size_t)x % 8u; + size_t byte = (size_t)x / 8u; + dst_row[byte] |= (uint8_t)(1u << (7u - bit)); + } + } + + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + + out_params->width_dots = (uint32_t)width; + out_params->height_dots = (uint32_t)height; + out_params->margin_mm = 0; + *out_raster = out; + *out_raster_bytes = total; + ptouch_set_error(ctx, LIBPTOUCH_OK, ""); + return LIBPTOUCH_OK; +} diff --git a/src/lib/libptouch_print.c b/src/lib/libptouch_print.c new file mode 100644 index 0000000..ae46479 --- /dev/null +++ b/src/lib/libptouch_print.c @@ -0,0 +1,296 @@ +/* + * libptouch — raster layout, transpose, P-touch print job (ESC/P bulk) + * + * Author: knb + * Email: knb@artif.org + */ + +#include "libptouch_internal.h" + +#include +#include +#include +#include + +/* cv_ptp900_jpn_raster_102.pdf: テープ幅から印刷可能ドット */ +static libptouch_err_t layout_from_status(uint8_t media_kind, uint8_t media_wbyte, + uint16_t *left, uint16_t *print_dots, + uint16_t *right) +{ + if (media_kind == 0x11u || media_kind == 0x17u) { + switch (media_wbyte) { + case 0x06: + *left = 244; + *print_dots = 56; + *right = 260; + return LIBPTOUCH_OK; + case 0x09: + *left = 224; + *print_dots = 96; + *right = 240; + return LIBPTOUCH_OK; + case 0x0C: + *left = 206; + *print_dots = 132; + *right = 222; + return LIBPTOUCH_OK; + case 0x12: + *left = 166; + *print_dots = 212; + *right = 182; + return LIBPTOUCH_OK; + case 0x18: + *left = 144; + *print_dots = 256; + *right = 160; + return LIBPTOUCH_OK; + default: + break; + } + } + switch (media_wbyte) { + case 0x04: + *left = 248; + *print_dots = 48; + *right = 264; + return LIBPTOUCH_OK; + case 0x06: + *left = 240; + *print_dots = 64; + *right = 256; + return LIBPTOUCH_OK; + case 0x09: + *left = 219; + *print_dots = 106; + *right = 235; + return LIBPTOUCH_OK; + case 0x0C: + *left = 197; + *print_dots = 150; + *right = 213; + return LIBPTOUCH_OK; + case 0x12: + *left = 155; + *print_dots = 234; + *right = 171; + return LIBPTOUCH_OK; + case 0x18: + *left = 112; + *print_dots = 320; + *right = 128; + return LIBPTOUCH_OK; + case 0x24: + *left = 45; + *print_dots = 454; + *right = 61; + return LIBPTOUCH_OK; + default: + break; + } + (void)media_kind; + return LIBPTOUCH_ERR_UNSUPPORTED; +} + +/* cv_ptp900_jpn_raster_102.pdf: 2.3.5 ラスターライン(全 560 ドット = 70 バイト) */ +static void pack_line_560(uint8_t line[70], const uint8_t *row, uint32_t width_dots, + uint16_t left_dots, uint16_t print_dots) +{ + memset(line, 0, 70u); + 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 >= 560u) + 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; + + uint8_t media_kind = st[11]; + uint8_t media_w = st[10]; + uint16_t left_dots, print_dots, right_dots; + v = layout_from_status(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; + + 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; + } + + unsigned margin_dots = 14u; + if (params->margin_mm > 0) { + margin_dots = (unsigned)((double)params->margin_mm * 360.0 / + 25.4 + + 0.5); + if (margin_dots < 14u) + margin_dots = 14u; + if (margin_dots > 1800u) + margin_dots = 1800u; + } + + uint32_t lines = ht; + uint8_t n5 = (uint8_t)(lines & 0xFFu); + uint8_t n6 = (uint8_t)((lines >> 8) & 0xFFu); + uint8_t n7 = (uint8_t)((lines >> 16) & 0xFFu); + uint8_t n8 = (uint8_t)((lines >> 24) & 0xFFu); + + uint8_t n2_paper = 0x09u; + if (media_kind == 0x03u) + n2_paper = 0x00u; + else if (media_kind == 0x11u) + n2_paper = 0x11u; + else if (media_kind == 0x17u) + n2_paper = 0x17u; + else if (media_kind == 0x13u) + n2_paper = 0x13u; + + 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[] = { 0x1B, 0x69, 0x7A, 0x0Eu, n2_paper, media_w, + 0x00u, n5, n6, n7, n8, 0x02u, 0x00u }; + 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); + static const uint8_t esc_ia[] = { 0x1B, 0x69, 0x41, 0x01 }; + memcpy(head + pos, esc_ia, sizeof(esc_ia)); + pos += sizeof(esc_ia); + static const uint8_t esc_ik[] = { 0x1B, 0x69, 0x4B, 0x0C }; + 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[73]; + static const uint8_t g_hdr[] = { 0x47, 0x46, 0x00 }; + + for (uint32_t y = 0; y < lines; y++) { + const uint8_t *row = src + (size_t)y * row_b; + pack_line_560(gbuf + 3, row, wd, left_dots, print_dots); + memcpy(gbuf, g_hdr, sizeof(g_hdr)); + v = ptouch_bulk_send_job(ctx, gbuf, sizeof(gbuf), "raster line"); + 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; +} diff --git a/src/lib/libptouch_status.c b/src/lib/libptouch_status.c new file mode 100644 index 0000000..d41c377 --- /dev/null +++ b/src/lib/libptouch_status.c @@ -0,0 +1,432 @@ +/* + * libptouch — printer status (ESC i S) and human-readable dump + * + * Author: knb + * Email: knb@artif.org + */ + +#include "libptouch_internal.h" + +#include +#include + +libptouch_err_t libptouch_get_status(libptouch_ctx *ctx, uint8_t *status) +{ + if (!ctx || !status) { + if (ctx) + ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, "null argument"); + return LIBPTOUCH_ERR_ARG; + } + if (!ctx->usb_handle || !ctx->bulk_out_ep || !ctx->bulk_in_ep) { + ptouch_set_error(ctx, LIBPTOUCH_ERR_IO, + "USB not open or bulk endpoints missing"); + return LIBPTOUCH_ERR_IO; + } + + libusb_device_handle *h = ctx->usb_handle; + static const uint8_t init[] = { 0x1B, 0x40 }; + static const uint8_t req[] = { 0x1B, 0x69, 0x53 }; + + int r = LIBUSB_ERROR_OTHER; + for (int attempt = 0; attempt < 2; attempt++) { + if (attempt > 0) { + (void)libusb_clear_halt(h, ctx->bulk_in_ep); + (void)libusb_clear_halt(h, ctx->bulk_out_ep); + } + r = ptouch_bulk_out(h, ctx->bulk_out_ep, init, 2, 5000u); + if (r != 0) { + ptouch_set_error_usb(ctx, r, "bulk OUT ESC @"); + return LIBPTOUCH_ERR_USB; + } + r = ptouch_bulk_out(h, ctx->bulk_out_ep, req, 3, 5000u); + if (r != 0) { + ptouch_set_error_usb(ctx, r, "bulk OUT ESC i S"); + return LIBPTOUCH_ERR_USB; + } + r = ptouch_bulk_in_exact(h, ctx->bulk_in_ep, status, + (int)LIBPTOUCH_STATUS_LENGTH, 5000u); + if (r == 0) + break; + if (r != LIBUSB_ERROR_IO && r != LIBUSB_ERROR_PIPE) + break; + } + if (r != 0) { + ptouch_set_error_usb(ctx, r, "bulk IN status"); + return LIBPTOUCH_ERR_USB; + } + + ptouch_set_error(ctx, LIBPTOUCH_OK, ""); + return LIBPTOUCH_OK; +} + +static void fprint_model(FILE *fp, uint8_t code) +{ + const char *name = "unknown"; + switch (code) { + case 0x6F: + name = "PT-P900W"; + break; + case 0x70: + name = "PT-P950NW"; + break; + case 0x71: + name = "PT-P900"; + break; + case 0x78: + name = "PT-P910BT"; + break; + default: + break; + } + fprintf(fp, "機種コード: 0x%02X ('%c') — %s\n", (unsigned)code, + (code >= 32 && code < 127) ? (char)code : '?', name); +} + +static void fprint_media_width(FILE *fp, uint8_t w, uint8_t len_byte) +{ + const char *desc = NULL; + switch (w) { + case 0x00: + desc = "テープなし / 未装着"; + break; + case 0x04: + desc = "3.5 mm"; + break; + case 0x06: + desc = "6 mm"; + break; + case 0x09: + desc = "9 mm"; + break; + case 0x0C: + desc = "12 mm"; + break; + case 0x12: + desc = "18 mm"; + break; + case 0x18: + desc = "24 mm"; + break; + case 0x24: + desc = "36 mm"; + break; + case 0x15: + desc = "FLe 21 mm 幅(長さはメディア長バイト参照)"; + break; + default: + break; + } + if (desc) + fprintf(fp, "メディア幅: 0x%02X — %s\n", (unsigned)w, desc); + else + fprintf(fp, "メディア幅: 0x%02X\n", (unsigned)w); + if (w == 0x15 && len_byte != 0) + fprintf(fp, "メディア長: 0x%02X (%u mm 相当の表記参照)\n", + (unsigned)len_byte, (unsigned)len_byte); +} + +static void fprint_media_kind(FILE *fp, uint8_t k) +{ + const char *desc = NULL; + switch (k) { + case 0x00: + desc = "テープなし"; + break; + case 0x01: + desc = "ラミネートテープ"; + break; + case 0x03: + desc = "ノンラミネートテープ"; + break; + case 0x04: + desc = "ファブリックテープ"; + break; + case 0x11: + desc = "ヒートシュリンクチューブ (HS 2:1)"; + break; + case 0x13: + desc = "FLe テープ"; + break; + case 0x14: + desc = "フレキシブルIDテープ"; + break; + case 0x15: + desc = "サテンテープ"; + break; + case 0x17: + desc = "ヒートシュリンクチューブ (HS 3:1)"; + break; + case 0xFF: + desc = "非対応テープ"; + break; + default: + break; + } + if (desc) + fprintf(fp, "テープ種類: 0x%02X — %s\n", (unsigned)k, desc); + else + fprintf(fp, "テープ種類: 0x%02X\n", (unsigned)k); +} + +static void fprint_battery(FILE *fp, uint8_t b) +{ + const char *desc = NULL; + switch (b) { + case 0x00: + desc = "フル"; + break; + case 0x01: + desc = "ハーフ"; + break; + case 0x02: + desc = "ロー"; + break; + case 0x03: + desc = "要充電"; + break; + case 0x04: + desc = "AC アダプター使用中"; + break; + case 0xFF: + desc = "不明"; + break; + default: + break; + } + if (desc) + fprintf(fp, "電池残量: 0x%02X — %s\n", (unsigned)b, desc); + else + fprintf(fp, "電池残量: 0x%02X (PT-P910BT 等は別表参照)\n", + (unsigned)b); +} + +static void fprint_tape_color(FILE *fp, uint8_t c) +{ + const char *desc = NULL; + switch (c) { + case 0x01: + desc = "白 (White)"; + break; + case 0x02: + desc = "その他 (Other)"; + break; + case 0x03: + desc = "透明 (Clear)"; + break; + case 0x04: + desc = "赤 (Red)"; + break; + case 0x05: + desc = "青 (Blue)"; + break; + case 0x06: + desc = "黄 (Yellow)"; + break; + case 0x07: + desc = "緑 (Green)"; + break; + case 0x08: + desc = "黒 (Black)"; + break; + case 0x09: + desc = "透明(文字白)"; + break; + case 0x20: + desc = "白(マット) (Matte White)"; + break; + case 0x21: + desc = "透明(マット) (Matte Clear)"; + break; + case 0x22: + desc = "銀(マット) (Matte Silver)"; + break; + case 0x23: + desc = "金(サテン) (Satin Gold)"; + break; + case 0x24: + desc = "銀(サテン) (Satin Silver)"; + break; + case 0x30: + desc = "青(D)"; + break; + case 0x31: + desc = "赤(D)"; + break; + case 0x40: + desc = "オレンジ(蛍光)"; + break; + case 0x41: + desc = "黄(蛍光)"; + break; + case 0x50: + desc = "ピンク(S)"; + break; + case 0x51: + desc = "グレー(S)"; + break; + case 0x52: + desc = "グリーン(S)"; + break; + case 0x60: + desc = "イエロー(F)"; + break; + case 0x61: + desc = "ピンク(F)"; + break; + case 0x62: + desc = "ブルー(F)"; + break; + case 0x70: + desc = "白(チューブ)"; + break; + case 0x90: + desc = "白(フレキ)"; + break; + case 0x91: + desc = "黄(フレキ)"; + break; + case 0xF0: + desc = "クリーニング"; + break; + case 0xF1: + desc = "ステンシル"; + break; + case 0xFF: + desc = "非対応"; + break; + default: + break; + } + if (desc) + fprintf(fp, "テープ色: 0x%02X — %s\n", (unsigned)c, desc); + else + fprintf(fp, "テープ色: 0x%02X\n", (unsigned)c); +} + +static void fprint_status_kind(FILE *fp, uint8_t s) +{ + const char *desc = NULL; + switch (s) { + case 0x00: + desc = "印刷終了"; + break; + case 0x01: + desc = "エラー発生"; + break; + case 0x02: + desc = "IF モード終了"; + break; + case 0x03: + desc = "パワーオフ(未使用扱い)"; + break; + case 0x04: + desc = "通知"; + break; + case 0x05: + desc = "フェーズ変更"; + break; + default: + break; + } + if (desc) + fprintf(fp, "ステータス種類: 0x%02X — %s\n", (unsigned)s, desc); + else + fprintf(fp, "ステータス種類: 0x%02X\n", (unsigned)s); +} + +void libptouch_status_fprint(FILE *fp, const uint8_t *status) +{ + if (!fp || !status) + return; + + fprintf(fp, "=== P-touch ステータス (32 バイト) ===\n"); + if (status[0] != 0x80u || status[1] != 0x20u) + fprintf(fp, + "※ 先頭マーク異常: [0]=0x%02X [1]=0x%02X (通常 80 20)\n", + (unsigned)status[0], (unsigned)status[1]); + + fprintf(fp, "ヘッダ: 0x%02X 0x%02X\n", (unsigned)status[0], + (unsigned)status[1]); + fprintf(fp, "Brother コード: %c (0x%02X)\n", + (status[2] >= 32 && status[2] < 127) ? (char)status[2] : '?', + (unsigned)status[2]); + fprint_model(fp, status[4]); + fprintf(fp, "国別コード: %c\n", + (status[5] >= 32 && status[5] < 127) ? (char)status[5] : '?'); + + fprint_battery(fp, status[6]); + + if (status[7] != 0) { + fprintf(fp, "拡張エラー: 0x%02X", (unsigned)status[7]); + switch (status[7]) { + case 0x10: + fprintf(fp, " — FLE のテープエンド"); + break; + case 0x1D: + fprintf(fp, " — 高解像度/ドラフト印刷エラー"); + break; + case 0x1E: + fprintf(fp, " — アダプター抜き挿しエラー"); + break; + case 0x21: + fprintf(fp, " — 非対応メディアエラー"); + break; + default: + break; + } + fprintf(fp, "\n"); + } else { + fprintf(fp, "拡張エラー: なし (0x00)\n"); + } + + fprintf(fp, "エラー情報1: 0x%02X\n", (unsigned)status[8]); + if (status[8] & 0x01) + fprintf(fp, " - メディア無し\n"); + if (status[8] & 0x02) + fprintf(fp, " - メディア終了\n"); + if (status[8] & 0x04) + fprintf(fp, " - カッタージャム\n"); + if (status[8] & 0x08) + fprintf(fp, " - バッテリー弱\n"); + if (status[8] & 0x40) + fprintf(fp, " - 高圧アダプター\n"); + + fprintf(fp, "エラー情報2: 0x%02X\n", (unsigned)status[9]); + if (status[9] & 0x01) + fprintf(fp, " - メディア交換(メディア違い)\n"); + if (status[9] & 0x04) + fprintf(fp, " - 通信エラー\n"); + if (status[9] & 0x08) + fprintf(fp, " - 通信バッファーフル\n"); + if (status[9] & 0x10) + fprintf(fp, " - カバーオープン\n"); + if (status[9] & 0x20) + fprintf(fp, " - 高温エラー\n"); + if (status[9] & 0x40) + fprintf(fp, " - 先端検出エラー\n"); + if (status[9] & 0x80) + fprintf(fp, " - システムエラー\n"); + + fprint_media_width(fp, status[10], status[17]); + fprint_media_kind(fp, status[11]); + fprintf(fp, "色数: 0x%02X フォント/日本語フォント: 0x%02X / 0x%02X\n", + (unsigned)status[12], (unsigned)status[13], + (unsigned)status[14]); + fprintf(fp, "モード: 0x%02X 濃度: 0x%02X\n", (unsigned)status[15], + (unsigned)status[16]); + + fprint_status_kind(fp, status[18]); + fprintf(fp, "フェーズ種類: 0x%02X フェーズ番号: %02X %02X\n", + (unsigned)status[19], (unsigned)status[20], + (unsigned)status[21]); + fprintf(fp, "通知番号: 0x%02X\n", (unsigned)status[22]); + fprintf(fp, "拡張部バイト数: 0x%02X\n", (unsigned)status[23]); + + fprint_tape_color(fp, status[24]); + fprintf(fp, "文字色: 0x%02X\n", (unsigned)status[25]); + + fprintf(fp, "生データ: "); + for (unsigned i = 0; i < LIBPTOUCH_STATUS_LENGTH; i++) + fprintf(fp, "%02X%s", (unsigned)status[i], + i + 1 == LIBPTOUCH_STATUS_LENGTH ? "\n" : " "); +} diff --git a/src/lib/libptouch_usb.c b/src/lib/libptouch_usb.c new file mode 100644 index 0000000..9d1d6f4 --- /dev/null +++ b/src/lib/libptouch_usb.c @@ -0,0 +1,196 @@ +/* + * libptouch — USB (libusb): open/close, bulk transfers + * + * Author: knb + * Email: knb@artif.org + */ + +#include "libptouch_internal.h" + +#include +#include + +/** + * バルク IN/OUT を両方持つインタフェースを探して claim する。 + * USB Printer クラス (bInterfaceClass==7) を優先する。 + */ +static int claim_interface_with_bulk(libusb_device_handle *h, int *out_iface, + uint8_t *out_ep, uint8_t *in_ep) +{ + libusb_device *dev = libusb_get_device(h); + struct libusb_config_descriptor *cfg = NULL; + int r = libusb_get_active_config_descriptor(dev, &cfg); + if (r != 0 || !cfg) + return -1; + + for (int pass = 0; pass < 2; pass++) { + for (int i = 0; i < cfg->bNumInterfaces; i++) { + const struct libusb_interface_descriptor *alt = + &cfg->interface[i].altsetting[0]; + if (pass == 0 && + alt->bInterfaceClass != LIBUSB_CLASS_PRINTER) + continue; + + uint8_t ep_out = 0, ep_in = 0; + for (uint8_t k = 0; k < alt->bNumEndpoints; k++) { + const struct libusb_endpoint_descriptor *ep = + &alt->endpoint[k]; + if ((ep->bmAttributes & + LIBUSB_TRANSFER_TYPE_MASK) == + LIBUSB_TRANSFER_TYPE_BULK) { + if (ep->bEndpointAddress & + LIBUSB_ENDPOINT_IN) + ep_in = ep->bEndpointAddress; + else + ep_out = ep->bEndpointAddress; + } + } + if (!ep_out || !ep_in) + continue; + r = libusb_claim_interface(h, i); + if (r == 0) { + *out_iface = i; + *out_ep = ep_out; + *in_ep = ep_in; + libusb_free_config_descriptor(cfg); + return 0; + } + } + } + libusb_free_config_descriptor(cfg); + return -1; +} + +libptouch_err_t libptouch_open_usb_vid_pid(libptouch_ctx *ctx, uint16_t vid, + uint16_t pid) +{ + if (!ctx) { + return LIBPTOUCH_ERR_ARG; + } + if (ctx->usb_handle) { + ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, "USB already open"); + return LIBPTOUCH_ERR_ARG; + } + + int r = libusb_init(&ctx->usb_ctx); + if (r != LIBUSB_SUCCESS) { + ptouch_set_error_usb(ctx, r, "libusb_init"); + ctx->usb_ctx = NULL; + return LIBPTOUCH_ERR_USB; + } + + libusb_device_handle *h = + libusb_open_device_with_vid_pid(ctx->usb_ctx, vid, pid); + if (!h) { + libusb_exit(ctx->usb_ctx); + ctx->usb_ctx = NULL; + char buf[160]; + snprintf(buf, sizeof(buf), + "no USB device 0x%04x:0x%04x (check cable, power, udev)", + (unsigned)vid, (unsigned)pid); + ptouch_set_error(ctx, LIBPTOUCH_ERR_NOT_FOUND, buf); + return LIBPTOUCH_ERR_NOT_FOUND; + } + ctx->usb_handle = h; + +#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000106) + (void)libusb_set_auto_detach_kernel_driver(h, 1); +#endif + + if (claim_interface_with_bulk(h, &ctx->claimed_interface, + &ctx->bulk_out_ep, &ctx->bulk_in_ep) != 0) { + ptouch_set_error(ctx, LIBPTOUCH_ERR_USB, + "no bulk IN/OUT interface (permissions or driver?)"); + libusb_close(h); + ctx->usb_handle = NULL; + libusb_exit(ctx->usb_ctx); + ctx->usb_ctx = NULL; + return LIBPTOUCH_ERR_USB; + } + + (void)libusb_clear_halt(h, ctx->bulk_out_ep); + (void)libusb_clear_halt(h, ctx->bulk_in_ep); + + ctx->usb_open = 1; + ptouch_set_error(ctx, LIBPTOUCH_OK, ""); + return LIBPTOUCH_OK; +} + +libptouch_err_t libptouch_open_usb(libptouch_ctx *ctx) +{ + return libptouch_open_usb_vid_pid(ctx, LIBPTOUCH_USB_VID_BROTHER, + LIBPTOUCH_USB_PID_PTP900W); +} + +void libptouch_close(libptouch_ctx *ctx) +{ + if (!ctx) + return; + if (ctx->usb_handle) { + if (ctx->claimed_interface >= 0) { + (void)libusb_release_interface(ctx->usb_handle, + ctx->claimed_interface); + ctx->claimed_interface = -1; + } + libusb_close(ctx->usb_handle); + ctx->usb_handle = NULL; + } + if (ctx->usb_ctx) { + libusb_exit(ctx->usb_ctx); + ctx->usb_ctx = NULL; + } + ctx->bulk_out_ep = 0; + ctx->bulk_in_ep = 0; + ctx->usb_open = 0; +} + +int ptouch_bulk_out(libusb_device_handle *h, uint8_t ep, const uint8_t *data, + int len, unsigned int timeout_ms) +{ + int tr = 0; + int r = libusb_bulk_transfer(h, ep, (unsigned char *)data, len, &tr, + (int)timeout_ms); + if (r != 0) + return r; + if (tr != len) + return LIBUSB_ERROR_IO; + return 0; +} + +int ptouch_bulk_in_exact(libusb_device_handle *h, uint8_t ep, uint8_t *buf, + int len, unsigned int timeout_ms) +{ + int got = 0; + while (got < len) { + int tr = 0; + int r = libusb_bulk_transfer(h, ep, buf + got, len - got, &tr, + (int)timeout_ms); + if (r != 0) + return r; + if (tr == 0) + return LIBUSB_ERROR_IO; + got += tr; + } + return 0; +} + +libptouch_err_t ptouch_bulk_send_job(libptouch_ctx *ctx, const uint8_t *buf, + size_t len, const char *what) +{ + int tr = 0; + int r = libusb_bulk_transfer(ctx->usb_handle, ctx->bulk_out_ep, + (unsigned char *)buf, (int)len, &tr, + 120000); + if (r != 0) { + ptouch_set_error_usb(ctx, r, what); + return LIBPTOUCH_ERR_USB; + } + if ((size_t)tr != len) { + char msg[96]; + snprintf(msg, sizeof(msg), "%s: short write %d/%zu", what, tr, + len); + ptouch_set_error(ctx, LIBPTOUCH_ERR_USB, msg); + return LIBPTOUCH_ERR_USB; + } + return LIBPTOUCH_OK; +} diff --git a/src/libptouch.c b/src/libptouch.c deleted file mode 100644 index bf0434b..0000000 --- a/src/libptouch.c +++ /dev/null @@ -1,1164 +0,0 @@ -/* - * libptouch — Brother P-touch raster printing (USB) - * - * Author: knb - * Email: knb@artif.org - */ - -#include "libptouch.h" - -#include -#include -#include -#include - -#include -#include - -struct libptouch_ctx { - libptouch_err_t last_code; - char last_msg[256]; - int usb_open; - struct libusb_context *usb_ctx; - struct libusb_device_handle *usb_handle; - int claimed_interface; /* -1 if none */ - uint8_t bulk_out_ep; - uint8_t bulk_in_ep; -}; - -static void set_error(libptouch_ctx *ctx, libptouch_err_t code, const char *msg) -{ - if (!ctx) - return; - ctx->last_code = code; - if (msg) { - snprintf(ctx->last_msg, sizeof(ctx->last_msg), "%s", msg); - } else { - ctx->last_msg[0] = '\0'; - } -} - -static void set_error_usb(libptouch_ctx *ctx, int libusb_err, const char *what) -{ - char buf[256]; - snprintf(buf, sizeof(buf), "%s: %s", what, - libusb_strerror((enum libusb_error)libusb_err)); - set_error(ctx, LIBPTOUCH_ERR_USB, buf); -} - -/** - * バルク IN/OUT を両方持つインタフェースを探して claim する。 - * USB Printer クラス (bInterfaceClass==7) を優先する。先頭のバルクだけを取ると - * 別機能のインタフェースを掴み、bulk IN が I/O エラーになる機種がある。 - * 成功時 0、失敗時 -1。エンドポイントは *out_ep / *in_ep に格納。 - */ -static int claim_interface_with_bulk(libusb_device_handle *h, int *out_iface, - uint8_t *out_ep, uint8_t *in_ep) -{ - libusb_device *dev = libusb_get_device(h); - struct libusb_config_descriptor *cfg = NULL; - int r = libusb_get_active_config_descriptor(dev, &cfg); - if (r != 0 || !cfg) - return -1; - - for (int pass = 0; pass < 2; pass++) { - for (int i = 0; i < cfg->bNumInterfaces; i++) { - const struct libusb_interface_descriptor *alt = - &cfg->interface[i].altsetting[0]; - if (pass == 0 && - alt->bInterfaceClass != LIBUSB_CLASS_PRINTER) - continue; - - uint8_t ep_out = 0, ep_in = 0; - for (uint8_t k = 0; k < alt->bNumEndpoints; k++) { - const struct libusb_endpoint_descriptor *ep = - &alt->endpoint[k]; - if ((ep->bmAttributes & - LIBUSB_TRANSFER_TYPE_MASK) == - LIBUSB_TRANSFER_TYPE_BULK) { - if (ep->bEndpointAddress & - LIBUSB_ENDPOINT_IN) - ep_in = ep->bEndpointAddress; - else - ep_out = ep->bEndpointAddress; - } - } - if (!ep_out || !ep_in) - continue; - r = libusb_claim_interface(h, i); - if (r == 0) { - *out_iface = i; - *out_ep = ep_out; - *in_ep = ep_in; - libusb_free_config_descriptor(cfg); - return 0; - } - } - } - libusb_free_config_descriptor(cfg); - return -1; -} - -libptouch_ctx *libptouch_create(void) -{ - libptouch_ctx *ctx = calloc(1, sizeof(*ctx)); - if (!ctx) - return NULL; - ctx->last_code = LIBPTOUCH_OK; - ctx->last_msg[0] = '\0'; - ctx->usb_open = 0; - ctx->usb_ctx = NULL; - ctx->usb_handle = NULL; - ctx->claimed_interface = -1; - ctx->bulk_out_ep = 0; - ctx->bulk_in_ep = 0; - return ctx; -} - -void libptouch_destroy(libptouch_ctx *ctx) -{ - if (!ctx) - return; - libptouch_close(ctx); - free(ctx); -} - -const char *libptouch_strerror(const libptouch_ctx *ctx) -{ - if (!ctx) - return "(null ctx)"; - return ctx->last_msg[0] ? ctx->last_msg : "ok"; -} - -libptouch_err_t libptouch_last_error(const libptouch_ctx *ctx) -{ - if (!ctx) - return LIBPTOUCH_ERR_ARG; - return ctx->last_code; -} - -libptouch_err_t libptouch_open_usb_vid_pid(libptouch_ctx *ctx, uint16_t vid, - uint16_t pid) -{ - if (!ctx) { - return LIBPTOUCH_ERR_ARG; - } - if (ctx->usb_handle) { - set_error(ctx, LIBPTOUCH_ERR_ARG, "USB already open"); - return LIBPTOUCH_ERR_ARG; - } - - int r = libusb_init(&ctx->usb_ctx); - if (r != LIBUSB_SUCCESS) { - set_error_usb(ctx, r, "libusb_init"); - ctx->usb_ctx = NULL; - return LIBPTOUCH_ERR_USB; - } - - libusb_device_handle *h = - libusb_open_device_with_vid_pid(ctx->usb_ctx, vid, pid); - if (!h) { - libusb_exit(ctx->usb_ctx); - ctx->usb_ctx = NULL; - char buf[160]; - snprintf(buf, sizeof(buf), - "no USB device 0x%04x:0x%04x (check cable, power, udev)", - (unsigned)vid, (unsigned)pid); - set_error(ctx, LIBPTOUCH_ERR_NOT_FOUND, buf); - return LIBPTOUCH_ERR_NOT_FOUND; - } - ctx->usb_handle = h; - -#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000106) - (void)libusb_set_auto_detach_kernel_driver(h, 1); -#endif - - if (claim_interface_with_bulk(h, &ctx->claimed_interface, - &ctx->bulk_out_ep, &ctx->bulk_in_ep) != 0) { - set_error(ctx, LIBPTOUCH_ERR_USB, - "no bulk IN/OUT interface (permissions or driver?)"); - libusb_close(h); - ctx->usb_handle = NULL; - libusb_exit(ctx->usb_ctx); - ctx->usb_ctx = NULL; - return LIBPTOUCH_ERR_USB; - } - - (void)libusb_clear_halt(h, ctx->bulk_out_ep); - (void)libusb_clear_halt(h, ctx->bulk_in_ep); - - ctx->usb_open = 1; - set_error(ctx, LIBPTOUCH_OK, ""); - return LIBPTOUCH_OK; -} - -libptouch_err_t libptouch_open_usb(libptouch_ctx *ctx) -{ - return libptouch_open_usb_vid_pid(ctx, LIBPTOUCH_USB_VID_BROTHER, - LIBPTOUCH_USB_PID_PTP900W); -} - -void libptouch_close(libptouch_ctx *ctx) -{ - if (!ctx) - return; - if (ctx->usb_handle) { - if (ctx->claimed_interface >= 0) { - (void)libusb_release_interface(ctx->usb_handle, - ctx->claimed_interface); - ctx->claimed_interface = -1; - } - libusb_close(ctx->usb_handle); - ctx->usb_handle = NULL; - } - if (ctx->usb_ctx) { - libusb_exit(ctx->usb_ctx); - ctx->usb_ctx = NULL; - } - ctx->bulk_out_ep = 0; - ctx->bulk_in_ep = 0; - ctx->usb_open = 0; -} - -libptouch_err_t libptouch_check_raster(libptouch_ctx *ctx, - const uint8_t *data, size_t data_len, - const libptouch_raster_params_t *params) -{ - if (!ctx || !params) { - if (ctx) - set_error(ctx, LIBPTOUCH_ERR_ARG, "null argument"); - return LIBPTOUCH_ERR_ARG; - } - if (!data && data_len > 0) { - set_error(ctx, LIBPTOUCH_ERR_ARG, "data is null but len > 0"); - return LIBPTOUCH_ERR_ARG; - } - - size_t row = (params->width_dots + 7u) / 8u; - size_t expected = (size_t)params->height_dots * row; - if (data_len != expected) { - char buf[160]; - snprintf(buf, sizeof(buf), - "data_len mismatch: got %zu, expected %zu (w=%u h=%u)", - data_len, expected, params->width_dots, - params->height_dots); - set_error(ctx, LIBPTOUCH_ERR_ARG, buf); - return LIBPTOUCH_ERR_ARG; - } - - set_error(ctx, LIBPTOUCH_OK, ""); - return LIBPTOUCH_OK; -} - -/* cv_ptp900_jpn_raster_102.pdf: 2.3.5 ラスターライン(全 560 ドット = 70 バイト) */ -static libptouch_err_t layout_from_status(uint8_t media_kind, uint8_t media_wbyte, - uint16_t *left, uint16_t *print_dots, - uint16_t *right) -{ - /* ヒートシュリンク (HS) — 種類バイト 11h / 17h */ - if (media_kind == 0x11u || media_kind == 0x17u) { - switch (media_wbyte) { - case 0x06: - *left = 244; - *print_dots = 56; - *right = 260; - return LIBPTOUCH_OK; - case 0x09: - *left = 224; - *print_dots = 96; - *right = 240; - return LIBPTOUCH_OK; - case 0x0C: - *left = 206; - *print_dots = 132; - *right = 222; - return LIBPTOUCH_OK; - case 0x12: - *left = 166; - *print_dots = 212; - *right = 182; - return LIBPTOUCH_OK; - case 0x18: - *left = 144; - *print_dots = 256; - *right = 160; - return LIBPTOUCH_OK; - default: - break; - } - } - /* TZe 系(ラミネート / ノンラミ等)および幅バイトのみ一致する一般テープ */ - switch (media_wbyte) { - case 0x04: - *left = 248; - *print_dots = 48; - *right = 264; - return LIBPTOUCH_OK; - case 0x06: - *left = 240; - *print_dots = 64; - *right = 256; - return LIBPTOUCH_OK; - case 0x09: - *left = 219; - *print_dots = 106; - *right = 235; - return LIBPTOUCH_OK; - case 0x0C: - *left = 197; - *print_dots = 150; - *right = 213; - return LIBPTOUCH_OK; - case 0x12: - *left = 155; - *print_dots = 234; - *right = 171; - return LIBPTOUCH_OK; - case 0x18: - *left = 112; - *print_dots = 320; - *right = 128; - return LIBPTOUCH_OK; - case 0x24: - *left = 45; - *print_dots = 454; - *right = 61; - return LIBPTOUCH_OK; - default: - break; - } - (void)media_kind; - return LIBPTOUCH_ERR_UNSUPPORTED; -} - -static void pack_line_560(uint8_t line[70], const uint8_t *row, uint32_t width_dots, - uint16_t left_dots, uint16_t print_dots) -{ - memset(line, 0, 70u); - 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 >= 560u) - break; - uint32_t b = dot / 8u; - uint32_t bi = 7u - (dot % 8u); - line[b] |= (uint8_t)(1u << bi); - } -} - -/** W×H の行優先 packed を転置し H×W のバッファを新規確保(印刷向き用) */ -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; -} - -static libptouch_err_t bulk_send_job(libptouch_ctx *ctx, const uint8_t *buf, - size_t len, const char *what) -{ - int tr = 0; - int r = libusb_bulk_transfer(ctx->usb_handle, ctx->bulk_out_ep, - (unsigned char *)buf, (int)len, &tr, - 120000); - if (r != 0) { - set_error_usb(ctx, r, what); - return LIBPTOUCH_ERR_USB; - } - if ((size_t)tr != len) { - char msg[96]; - snprintf(msg, sizeof(msg), "%s: short write %d/%zu", what, tr, - len); - set_error(ctx, LIBPTOUCH_ERR_USB, msg); - return LIBPTOUCH_ERR_USB; - } - return LIBPTOUCH_OK; -} - -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) { - 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; - - uint8_t media_kind = st[11]; - uint8_t media_w = st[10]; - uint16_t left_dots, print_dots, right_dots; - v = layout_from_status(media_kind, media_w, &left_dots, &print_dots, - &right_dots); - if (v != LIBPTOUCH_OK) { - 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; - - 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) { - 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); - set_error(ctx, LIBPTOUCH_ERR_ARG, buf); - free(transposed); - return LIBPTOUCH_ERR_ARG; - } - - unsigned margin_dots = 14u; - if (params->margin_mm > 0) { - margin_dots = (unsigned)((double)params->margin_mm * 360.0 / - 25.4 + - 0.5); - if (margin_dots < 14u) - margin_dots = 14u; - if (margin_dots > 1800u) - margin_dots = 1800u; - } - - uint32_t lines = ht; - uint8_t n5 = (uint8_t)(lines & 0xFFu); - uint8_t n6 = (uint8_t)((lines >> 8) & 0xFFu); - uint8_t n7 = (uint8_t)((lines >> 16) & 0xFFu); - uint8_t n8 = (uint8_t)((lines >> 24) & 0xFFu); - - uint8_t n2_paper = 0x09u; - if (media_kind == 0x03u) - n2_paper = 0x00u; - else if (media_kind == 0x11u) - n2_paper = 0x11u; - else if (media_kind == 0x17u) - n2_paper = 0x17u; - else if (media_kind == 0x13u) - n2_paper = 0x13u; - - 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[] = { 0x1B, 0x69, 0x7A, 0x0Eu, n2_paper, media_w, - 0x00u, n5, n6, n7, n8, 0x02u, 0x00u }; - 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); - static const uint8_t esc_ia[] = { 0x1B, 0x69, 0x41, 0x01 }; - memcpy(head + pos, esc_ia, sizeof(esc_ia)); - pos += sizeof(esc_ia); - static const uint8_t esc_ik[] = { 0x1B, 0x69, 0x4B, 0x0C }; - 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 = 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[73]; - static const uint8_t g_hdr[] = { 0x47, 0x46, 0x00 }; - - for (uint32_t y = 0; y < lines; y++) { - const uint8_t *row = src + (size_t)y * row_b; - pack_line_560(gbuf + 3, row, wd, left_dots, print_dots); - memcpy(gbuf, g_hdr, sizeof(g_hdr)); - v = bulk_send_job(ctx, gbuf, sizeof(gbuf), "raster line"); - if (v != LIBPTOUCH_OK) { - free(transposed); - return v; - } - } - - static const uint8_t print_end[] = { 0x1A }; - v = bulk_send_job(ctx, print_end, sizeof(print_end), "print end"); - if (v != LIBPTOUCH_OK) { - free(transposed); - return v; - } - - /* 印字完了ステータスを 1 パケットだけ読み捨て(あれば) */ - 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); - set_error(ctx, LIBPTOUCH_OK, ""); - return LIBPTOUCH_OK; -} - -/* --- PNG → 1bit packed raster (libpng) --- */ - -void libptouch_free_raster(uint8_t *raster) -{ - free(raster); -} - -libptouch_err_t libptouch_png_file_to_raster(libptouch_ctx *ctx, const char *path, - const libptouch_png_options_t *options, - uint8_t **out_raster, size_t *out_raster_bytes, - libptouch_raster_params_t *out_params) -{ - if (!ctx || !path || !out_raster || !out_raster_bytes || !out_params) { - if (ctx) - set_error(ctx, LIBPTOUCH_ERR_ARG, "null argument"); - return LIBPTOUCH_ERR_ARG; - } - - uint8_t thr = LIBPTOUCH_PNG_DEFAULT_THRESHOLD; - if (options) - thr = options->threshold; - - *out_raster = NULL; - *out_raster_bytes = 0; - out_params->width_dots = 0; - out_params->height_dots = 0; - out_params->margin_mm = 0; - - FILE *fp = fopen(path, "rb"); - if (!fp) { - char buf[192]; - snprintf(buf, sizeof(buf), "open %s: %s", path, strerror(errno)); - set_error(ctx, LIBPTOUCH_ERR_IO, buf); - return LIBPTOUCH_ERR_IO; - } - - unsigned char sig[8]; - if (fread(sig, 1, 8, fp) != 8) { - fclose(fp); - set_error(ctx, LIBPTOUCH_ERR_IMAGE, "short read (not a PNG?)"); - return LIBPTOUCH_ERR_IMAGE; - } - if (png_sig_cmp(sig, 0, 8) != 0) { - fclose(fp); - set_error(ctx, LIBPTOUCH_ERR_IMAGE, "not a PNG file"); - return LIBPTOUCH_ERR_IMAGE; - } - - png_structp png_ptr = - png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { - fclose(fp); - set_error(ctx, LIBPTOUCH_ERR_NOMEM, "png_create_read_struct failed"); - return LIBPTOUCH_ERR_NOMEM; - } - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - png_destroy_read_struct(&png_ptr, NULL, NULL); - fclose(fp); - set_error(ctx, LIBPTOUCH_ERR_NOMEM, "png_create_info_struct failed"); - return LIBPTOUCH_ERR_NOMEM; - } - - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(fp); - set_error(ctx, LIBPTOUCH_ERR_IMAGE, "PNG decode error"); - return LIBPTOUCH_ERR_IMAGE; - } - - png_init_io(png_ptr, fp); - png_set_sig_bytes(png_ptr, 8); - - const int transforms = (int)(PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16 | - PNG_TRANSFORM_GRAY_TO_RGB); - png_read_png(png_ptr, info_ptr, transforms, NULL); - - png_uint_32 width = png_get_image_width(png_ptr, info_ptr); - png_uint_32 height = png_get_image_height(png_ptr, info_ptr); - int channels = (int)png_get_channels(png_ptr, info_ptr); - png_bytepp rows = png_get_rows(png_ptr, info_ptr); - - if (width == 0 || height == 0 || !rows) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(fp); - set_error(ctx, LIBPTOUCH_ERR_IMAGE, "invalid PNG dimensions"); - return LIBPTOUCH_ERR_IMAGE; - } - if (width > 100000u || height > 100000u) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(fp); - set_error(ctx, LIBPTOUCH_ERR_IMAGE, "PNG dimensions too large"); - return LIBPTOUCH_ERR_IMAGE; - } - if (channels != 3 && channels != 4) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(fp); - set_error(ctx, LIBPTOUCH_ERR_IMAGE, "unsupported PNG channel count"); - return LIBPTOUCH_ERR_IMAGE; - } - - size_t row_bytes = ((size_t)width + 7u) / 8u; - if (height > SIZE_MAX / row_bytes) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(fp); - set_error(ctx, LIBPTOUCH_ERR_IMAGE, "raster size overflow"); - return LIBPTOUCH_ERR_IMAGE; - } - size_t total = row_bytes * (size_t)height; - uint8_t *out = (uint8_t *)calloc(1, total); - if (!out) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(fp); - set_error(ctx, LIBPTOUCH_ERR_NOMEM, "calloc raster failed"); - return LIBPTOUCH_ERR_NOMEM; - } - - for (png_uint_32 y = 0; y < height; y++) { - const png_byte *row = rows[y]; - uint8_t *dst_row = out + (size_t)y * row_bytes; - for (png_uint_32 x = 0; x < width; x++) { - size_t o = (size_t)x * (size_t)channels; - unsigned r = row[o + 0]; - unsigned g = row[o + 1]; - unsigned b = row[o + 2]; - unsigned a = channels == 4 ? row[o + 3] : 255u; - /* 透明に近いピクセルは白(非印刷) */ - if (a < 128u) { - continue; - } - /* Y = 0.299 R + 0.587 G + 0.114 B(整数近似) */ - unsigned yv = (77u * r + 150u * g + 29u * b) >> 8; - int black = (yv < (unsigned)thr) ? 1 : 0; - if (!black) - continue; - size_t bit = (size_t)x % 8u; - size_t byte = (size_t)x / 8u; - dst_row[byte] |= (uint8_t)(1u << (7u - bit)); - } - } - - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(fp); - - out_params->width_dots = (uint32_t)width; - out_params->height_dots = (uint32_t)height; - out_params->margin_mm = 0; - *out_raster = out; - *out_raster_bytes = total; - set_error(ctx, LIBPTOUCH_OK, ""); - return LIBPTOUCH_OK; -} - -/* --- ステータス (ESC i S, 32 バイト) --- */ - -static int bulk_out(libusb_device_handle *h, uint8_t ep, const uint8_t *data, - int len, unsigned int timeout_ms) -{ - int tr = 0; - int r = libusb_bulk_transfer(h, ep, (unsigned char *)data, len, &tr, - (int)timeout_ms); - if (r != 0) - return r; - if (tr != len) - return LIBUSB_ERROR_IO; - return 0; -} - -static int bulk_in_exact(libusb_device_handle *h, uint8_t ep, uint8_t *buf, - int len, unsigned int timeout_ms) -{ - int got = 0; - while (got < len) { - int tr = 0; - int r = libusb_bulk_transfer(h, ep, buf + got, len - got, &tr, - (int)timeout_ms); - if (r != 0) - return r; - if (tr == 0) - return LIBUSB_ERROR_IO; - got += tr; - } - return 0; -} - -libptouch_err_t libptouch_get_status(libptouch_ctx *ctx, uint8_t *status) -{ - if (!ctx || !status) { - if (ctx) - set_error(ctx, LIBPTOUCH_ERR_ARG, "null argument"); - return LIBPTOUCH_ERR_ARG; - } - if (!ctx->usb_handle || !ctx->bulk_out_ep || !ctx->bulk_in_ep) { - set_error(ctx, LIBPTOUCH_ERR_IO, - "USB not open or bulk endpoints missing"); - return LIBPTOUCH_ERR_IO; - } - - libusb_device_handle *h = ctx->usb_handle; - static const uint8_t init[] = { 0x1B, 0x40 }; - static const uint8_t req[] = { 0x1B, 0x69, 0x53 }; - - int r = LIBUSB_ERROR_OTHER; - for (int attempt = 0; attempt < 2; attempt++) { - if (attempt > 0) { - (void)libusb_clear_halt(h, ctx->bulk_in_ep); - (void)libusb_clear_halt(h, ctx->bulk_out_ep); - } - r = bulk_out(h, ctx->bulk_out_ep, init, 2, 5000u); - if (r != 0) { - set_error_usb(ctx, r, "bulk OUT ESC @"); - return LIBPTOUCH_ERR_USB; - } - r = bulk_out(h, ctx->bulk_out_ep, req, 3, 5000u); - if (r != 0) { - set_error_usb(ctx, r, "bulk OUT ESC i S"); - return LIBPTOUCH_ERR_USB; - } - r = bulk_in_exact(h, ctx->bulk_in_ep, status, - (int)LIBPTOUCH_STATUS_LENGTH, 5000u); - if (r == 0) - break; - if (r != LIBUSB_ERROR_IO && r != LIBUSB_ERROR_PIPE) - break; - } - if (r != 0) { - set_error_usb(ctx, r, "bulk IN status"); - return LIBPTOUCH_ERR_USB; - } - - set_error(ctx, LIBPTOUCH_OK, ""); - return LIBPTOUCH_OK; -} - -static void fprint_model(FILE *fp, uint8_t code) -{ - const char *name = "unknown"; - switch (code) { - case 0x6F: - name = "PT-P900W"; - break; - case 0x70: - name = "PT-P950NW"; - break; - case 0x71: - name = "PT-P900"; - break; - case 0x78: - name = "PT-P910BT"; - break; - default: - break; - } - fprintf(fp, "機種コード: 0x%02X ('%c') — %s\n", (unsigned)code, - (code >= 32 && code < 127) ? (char)code : '?', name); -} - -static void fprint_media_width(FILE *fp, uint8_t w, uint8_t len_byte) -{ - const char *desc = NULL; - switch (w) { - case 0x00: - desc = "テープなし / 未装着"; - break; - case 0x04: - desc = "3.5 mm"; - break; - case 0x06: - desc = "6 mm"; - break; - case 0x09: - desc = "9 mm"; - break; - case 0x0C: - desc = "12 mm"; - break; - case 0x12: - desc = "18 mm"; - break; - case 0x18: - desc = "24 mm"; - break; - case 0x24: - desc = "36 mm"; - break; - case 0x15: - desc = "FLe 21 mm 幅(長さはメディア長バイト参照)"; - break; - default: - break; - } - if (desc) - fprintf(fp, "メディア幅: 0x%02X — %s\n", (unsigned)w, desc); - else - fprintf(fp, "メディア幅: 0x%02X\n", (unsigned)w); - if (w == 0x15 && len_byte != 0) - fprintf(fp, "メディア長: 0x%02X (%u mm 相当の表記参照)\n", - (unsigned)len_byte, (unsigned)len_byte); -} - -static void fprint_media_kind(FILE *fp, uint8_t k) -{ - const char *desc = NULL; - switch (k) { - case 0x00: - desc = "テープなし"; - break; - case 0x01: - desc = "ラミネートテープ"; - break; - case 0x03: - desc = "ノンラミネートテープ"; - break; - case 0x04: - desc = "ファブリックテープ"; - break; - case 0x11: - desc = "ヒートシュリンクチューブ (HS 2:1)"; - break; - case 0x13: - desc = "FLe テープ"; - break; - case 0x14: - desc = "フレキシブルIDテープ"; - break; - case 0x15: - desc = "サテンテープ"; - break; - case 0x17: - desc = "ヒートシュリンクチューブ (HS 3:1)"; - break; - case 0xFF: - desc = "非対応テープ"; - break; - default: - break; - } - if (desc) - fprintf(fp, "テープ種類: 0x%02X — %s\n", (unsigned)k, desc); - else - fprintf(fp, "テープ種類: 0x%02X\n", (unsigned)k); -} - -static void fprint_battery(FILE *fp, uint8_t b) -{ - const char *desc = NULL; - switch (b) { - case 0x00: - desc = "フル"; - break; - case 0x01: - desc = "ハーフ"; - break; - case 0x02: - desc = "ロー"; - break; - case 0x03: - desc = "要充電"; - break; - case 0x04: - desc = "AC アダプター使用中"; - break; - case 0xFF: - desc = "不明"; - break; - default: - break; - } - if (desc) - fprintf(fp, "電池残量: 0x%02X — %s\n", (unsigned)b, desc); - else - fprintf(fp, "電池残量: 0x%02X (PT-P910BT 等は別表参照)\n", - (unsigned)b); -} - -static void fprint_tape_color(FILE *fp, uint8_t c) -{ - const char *desc = NULL; - switch (c) { - case 0x01: - desc = "白 (White)"; - break; - case 0x02: - desc = "その他 (Other)"; - break; - case 0x03: - desc = "透明 (Clear)"; - break; - case 0x04: - desc = "赤 (Red)"; - break; - case 0x05: - desc = "青 (Blue)"; - break; - case 0x06: - desc = "黄 (Yellow)"; - break; - case 0x07: - desc = "緑 (Green)"; - break; - case 0x08: - desc = "黒 (Black)"; - break; - case 0x09: - desc = "透明(文字白)"; - break; - case 0x20: - desc = "白(マット) (Matte White)"; - break; - case 0x21: - desc = "透明(マット) (Matte Clear)"; - break; - case 0x22: - desc = "銀(マット) (Matte Silver)"; - break; - case 0x23: - desc = "金(サテン) (Satin Gold)"; - break; - case 0x24: - desc = "銀(サテン) (Satin Silver)"; - break; - case 0x30: - desc = "青(D)"; - break; - case 0x31: - desc = "赤(D)"; - break; - case 0x40: - desc = "オレンジ(蛍光)"; - break; - case 0x41: - desc = "黄(蛍光)"; - break; - case 0x50: - desc = "ピンク(S)"; - break; - case 0x51: - desc = "グレー(S)"; - break; - case 0x52: - desc = "グリーン(S)"; - break; - case 0x60: - desc = "イエロー(F)"; - break; - case 0x61: - desc = "ピンク(F)"; - break; - case 0x62: - desc = "ブルー(F)"; - break; - case 0x70: - desc = "白(チューブ)"; - break; - case 0x90: - desc = "白(フレキ)"; - break; - case 0x91: - desc = "黄(フレキ)"; - break; - case 0xF0: - desc = "クリーニング"; - break; - case 0xF1: - desc = "ステンシル"; - break; - case 0xFF: - desc = "非対応"; - break; - default: - break; - } - if (desc) - fprintf(fp, "テープ色: 0x%02X — %s\n", (unsigned)c, desc); - else - fprintf(fp, "テープ色: 0x%02X\n", (unsigned)c); -} - -static void fprint_status_kind(FILE *fp, uint8_t s) -{ - const char *desc = NULL; - switch (s) { - case 0x00: - desc = "印刷終了"; - break; - case 0x01: - desc = "エラー発生"; - break; - case 0x02: - desc = "IF モード終了"; - break; - case 0x03: - desc = "パワーオフ(未使用扱い)"; - break; - case 0x04: - desc = "通知"; - break; - case 0x05: - desc = "フェーズ変更"; - break; - default: - break; - } - if (desc) - fprintf(fp, "ステータス種類: 0x%02X — %s\n", (unsigned)s, desc); - else - fprintf(fp, "ステータス種類: 0x%02X\n", (unsigned)s); -} - -void libptouch_status_fprint(FILE *fp, const uint8_t *status) -{ - if (!fp || !status) - return; - - fprintf(fp, "=== P-touch ステータス (32 バイト) ===\n"); - if (status[0] != 0x80u || status[1] != 0x20u) - fprintf(fp, - "※ 先頭マーク異常: [0]=0x%02X [1]=0x%02X (通常 80 20)\n", - (unsigned)status[0], (unsigned)status[1]); - - fprintf(fp, "ヘッダ: 0x%02X 0x%02X\n", (unsigned)status[0], - (unsigned)status[1]); - fprintf(fp, "Brother コード: %c (0x%02X)\n", - (status[2] >= 32 && status[2] < 127) ? (char)status[2] : '?', - (unsigned)status[2]); - fprint_model(fp, status[4]); - fprintf(fp, "国別コード: %c\n", - (status[5] >= 32 && status[5] < 127) ? (char)status[5] : '?'); - - fprint_battery(fp, status[6]); - - if (status[7] != 0) { - fprintf(fp, "拡張エラー: 0x%02X", (unsigned)status[7]); - switch (status[7]) { - case 0x10: - fprintf(fp, " — FLE のテープエンド"); - break; - case 0x1D: - fprintf(fp, " — 高解像度/ドラフト印刷エラー"); - break; - case 0x1E: - fprintf(fp, " — アダプター抜き挿しエラー"); - break; - case 0x21: - fprintf(fp, " — 非対応メディアエラー"); - break; - default: - break; - } - fprintf(fp, "\n"); - } else { - fprintf(fp, "拡張エラー: なし (0x00)\n"); - } - - fprintf(fp, "エラー情報1: 0x%02X\n", (unsigned)status[8]); - if (status[8] & 0x01) - fprintf(fp, " - メディア無し\n"); - if (status[8] & 0x02) - fprintf(fp, " - メディア終了\n"); - if (status[8] & 0x04) - fprintf(fp, " - カッタージャム\n"); - if (status[8] & 0x08) - fprintf(fp, " - バッテリー弱\n"); - if (status[8] & 0x40) - fprintf(fp, " - 高圧アダプター\n"); - - fprintf(fp, "エラー情報2: 0x%02X\n", (unsigned)status[9]); - if (status[9] & 0x01) - fprintf(fp, " - メディア交換(メディア違い)\n"); - if (status[9] & 0x04) - fprintf(fp, " - 通信エラー\n"); - if (status[9] & 0x08) - fprintf(fp, " - 通信バッファーフル\n"); - if (status[9] & 0x10) - fprintf(fp, " - カバーオープン\n"); - if (status[9] & 0x20) - fprintf(fp, " - 高温エラー\n"); - if (status[9] & 0x40) - fprintf(fp, " - 先端検出エラー\n"); - if (status[9] & 0x80) - fprintf(fp, " - システムエラー\n"); - - fprint_media_width(fp, status[10], status[17]); - fprint_media_kind(fp, status[11]); - fprintf(fp, "色数: 0x%02X フォント/日本語フォント: 0x%02X / 0x%02X\n", - (unsigned)status[12], (unsigned)status[13], - (unsigned)status[14]); - fprintf(fp, "モード: 0x%02X 濃度: 0x%02X\n", (unsigned)status[15], - (unsigned)status[16]); - - fprint_status_kind(fp, status[18]); - fprintf(fp, "フェーズ種類: 0x%02X フェーズ番号: %02X %02X\n", - (unsigned)status[19], (unsigned)status[20], - (unsigned)status[21]); - fprintf(fp, "通知番号: 0x%02X\n", (unsigned)status[22]); - fprintf(fp, "拡張部バイト数: 0x%02X\n", (unsigned)status[23]); - - fprint_tape_color(fp, status[24]); - fprintf(fp, "文字色: 0x%02X\n", (unsigned)status[25]); - - fprintf(fp, "生データ: "); - for (unsigned i = 0; i < LIBPTOUCH_STATUS_LENGTH; i++) - fprintf(fp, "%02X%s", (unsigned)status[i], - i + 1 == LIBPTOUCH_STATUS_LENGTH ? "\n" : " "); -}