5 Commits

Author SHA1 Message Date
knb
d0e5846012 Bump project version metadata to 1.0.1.
Synchronize CMake, README, and Ruby gem version strings with the released v1.0.1 tag to keep build and packaging metadata consistent.

Made-with: Cursor
2026-04-17 05:21:39 +09:00
knb
d2fd6cc1f9 Fix print completion flow for PT-P710BT and PT-P900W.
Align raster protocol bytes and print-end sequencing by printer family, add safer status polling/retry behavior, and document the changes with regression coverage to prevent protocol regressions.

Made-with: Cursor
2026-04-17 05:17:22 +09:00
knb
779a50747d Bluetooth 対応状況追記 2026-04-17 03:53:13 +09:00
knb
29072dc20c Update docs and add sample image for label workflows.
Capture design notes for merge-print and PNG fit behavior, clean up Ruby README formatting, and include a new sample PNG for print testing.

Made-with: Cursor
2026-04-17 03:25:39 +09:00
knb
42a785f086 Fix PT-P710BT raster protocol handling and add regression coverage.
Align print command payload generation with model-specific protocol requirements, add verbose CLI diagnostics and robust status retries, and introduce protocol regression tests to prevent future GF/ESC i z regressions.

Made-with: Cursor
2026-04-17 03:24:00 +09:00
13 changed files with 347 additions and 52 deletions

19
CHANGELOG.md Normal file
View File

@@ -0,0 +1,19 @@
# Changelog
## Unreleased
- Fix PT-P710BT/PT-P900W print completion flow and protocol bytes.
- Correct `G` raster transfer header length (`n1/n2`) from per-model payload size.
- Fix `ESC i z` page index (`n9`) and adjust print-information flags/media kind handling.
- Keep `ESC i A` disabled on 128-dot family and split `ESC i K` by head width.
- Use `0x0C -> 0x1A` end sequence on 560-dot family to complete feed/cut reliably.
- Improve status handling around print completion.
- Retry status reads with backoff and safer command ordering.
- Add verbose post-print short polling until print-end status is observed.
- Add small wait before `--status` command retrieval.
- Add protocol regression safeguards.
- Introduce `libptouch_protocol` helpers for shared command-byte generation.
- Add `protocol_regression_test` and wire it into CTest.
- Update documentation.
- Add `ctest` step to build instructions.
- Link to changelog from README.

View File

@@ -4,7 +4,7 @@
# Email: knb@artif.org # Email: knb@artif.org
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(ptouch_label VERSION 1.0.0 LANGUAGES C) project(ptouch_label VERSION 1.0.1 LANGUAGES C)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD_REQUIRED ON)
@@ -25,6 +25,7 @@ configure_file(
set(LIBPTOUCH_SOURCES set(LIBPTOUCH_SOURCES
src/lib/libptouch_core.c src/lib/libptouch_core.c
src/lib/libptouch_usb.c src/lib/libptouch_usb.c
src/lib/libptouch_protocol.c
src/lib/libptouch_layout.c src/lib/libptouch_layout.c
src/lib/libptouch_media_info.c src/lib/libptouch_media_info.c
src/lib/libptouch_trim.c src/lib/libptouch_trim.c
@@ -74,6 +75,19 @@ if(NOT MSVC)
target_compile_options(ptouch-print PRIVATE -Wall -Wextra -Wpedantic) target_compile_options(ptouch-print PRIVATE -Wall -Wextra -Wpedantic)
endif() endif()
enable_testing()
add_executable(ptouch-protocol-regression-test
tests/protocol_regression_test.c
src/lib/libptouch_protocol.c
)
target_include_directories(ptouch-protocol-regression-test PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/src/lib"
)
if(NOT MSVC)
target_compile_options(ptouch-protocol-regression-test PRIVATE -Wall -Wextra -Wpedantic)
endif()
add_test(NAME protocol_regression_test COMMAND ptouch-protocol-regression-test)
install(TARGETS ptouch ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") install(TARGETS ptouch ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}")
install(TARGETS ptouch_shared LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" install(TARGETS ptouch_shared LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")

View File

@@ -1,15 +1,18 @@
# ptouch_label # ptouch_label
**バージョン 1.0.0**(初回リリース) **バージョン 1.0.1**
Brother P-touch シリーズ向けのラベル印刷用 **C コアライブラリlibptouch** と、動作確認用 **CLI`ptouch-print`** のリポジトリです。 Brother P-touch シリーズ向けのラベル印刷用 **C コアライブラリlibptouch** と、動作確認用 **CLI`ptouch-print`** のリポジトリです。
対象機種: **PT-P900W**560 ドットヘッド)、**PT-P750W** / **PT-P710BT**128 ドットヘッド・USB`libptouch_open_usb_vid_pid` に各機種の VID/PID を渡すP900W 既定は `libptouch_open_usb`。P750/P710 のラスター仕様は `reference/cv_ptp750w_710bt_jpn_raster_102.pdf` 対象機種: **PT-P900W**560 ドットヘッド)、**PT-P750W** / **PT-P710BT**128 ドットヘッド・USB`libptouch_open_usb_vid_pid` に各機種の VID/PID を渡すP900W 既定は `libptouch_open_usb`。P750/P710 のラスター仕様は `reference/cv_ptp750w_710bt_jpn_raster_102.pdf`
現状は USB only / 将来 Bluetooth 対応予定。
## レイアウト ## レイアウト
| パス | 内容 | | パス | 内容 |
|------|------| | ----------------------- | --------------------------------------------------------------------------- |
| `include/libptouch.h` | 公開 API | | `include/libptouch.h` | 公開 API |
| `src/lib/libptouch_*.c` | ライブラリ本体core / usb / print / status / png / svg | | `src/lib/libptouch_*.c` | ライブラリ本体core / usb / print / status / png / svg |
| `src/cli/main.c` | `ptouch-print` エントリ | | `src/cli/main.c` | `ptouch-print` エントリ |
@@ -17,6 +20,7 @@ Brother P-touch シリーズ向けのラベル印刷用 **C コアライブラ
| `ruby/` | Ruby FFI gem`libptouch`)・コマンド `ptouch-print-png`PNG のみ)— `ruby/README.md` | | `ruby/` | Ruby FFI gem`libptouch`)・コマンド `ptouch-print-png`PNG のみ)— `ruby/README.md` |
| `reference/` | 仕様・参考資料(例: ラスター PDF | | `reference/` | 仕様・参考資料(例: ラスター PDF |
## ビルド ## ビルド
依存: **CMake 3.16+**、**libusb-1.0**、**libpng**、**librsvg-2.0**(開発パッケージ例: `libusb-1.0-0-dev``libpng-dev``librsvg2-dev`)。 依存: **CMake 3.16+**、**libusb-1.0**、**libpng**、**librsvg-2.0**(開発パッケージ例: `libusb-1.0-0-dev``libpng-dev``librsvg2-dev`)。
@@ -24,6 +28,7 @@ Brother P-touch シリーズ向けのラベル印刷用 **C コアライブラ
```bash ```bash
cmake -S . -B build cmake -S . -B build
cmake --build build cmake --build build
ctest --test-dir build --output-on-failure
``` ```
成果物(`build/` 以下): 成果物(`build/` 以下):
@@ -96,7 +101,7 @@ cmake --build build
- 接続は **libusb-1.0** のみ。機種ごとに **VID/PID**`lsusb` 等)を調べ、`libptouch_open_usb_vid_pid` に渡すか、既定の PT-P900W なら `libptouch_open_usb` を使います。PT-P750W は **04f9:2062**、PT-P710BT は **04f9:20af**`include/libptouch.h` の定数参照)。 - 接続は **libusb-1.0** のみ。機種ごとに **VID/PID**`lsusb` 等)を調べ、`libptouch_open_usb_vid_pid` に渡すか、既定の PT-P900W なら `libptouch_open_usb` を使います。PT-P750W は **04f9:2062**、PT-P710BT は **04f9:20af**`include/libptouch.h` の定数参照)。
- PT-P750W / PT-P710BT ではラスター幅方向は **180 dpi**P900W は 360 dpi。PNG から印刷する場合は解像度に合わせて画像を用意してください。 - PT-P750W / PT-P710BT ではラスター幅方向は **180 dpi**P900W は 360 dpi。PNG から印刷する場合は解像度に合わせて画像を用意してください。
- ラスターコマンドの詳細は **`reference/` の PDF** および Brother 公開資料に沿って `src/lib/libptouch_*.c` に実装してください。 - ラスターコマンドの詳細は `**reference/` の PDF** および Brother 公開資料に沿って `src/lib/libptouch_*.c` に実装してください。
### Ubuntu で sudo なしで USB を開くudev ### Ubuntu で sudo なしで USB を開くudev
@@ -116,3 +121,7 @@ sudo usermod -aG plugdev "$USER"
## ライセンス ## ライセンス
[MIT License](LICENSE)`LICENSE` ファイルを参照)。 [MIT License](LICENSE)`LICENSE` ファイルを参照)。
## 変更履歴
リリース間の変更点は `CHANGELOG.md` を参照してください。

View File

@@ -0,0 +1,44 @@
= pTouch Label ノート
== 差込印刷機能追加(案)
SVG ファイルを template として、JSON/YAML ファイルで受け取った内容を差込印刷する。
SVG template の text element の data-field attribute をキーにして 印字テキストを JSON/YAML ファイルのデータで置き換える
----
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 124 124" fill="none">
<rect width="124" height="124" rx="24" fill="#FFFFFFFF"/>
<text fill="#000000" x="5" y="60" data-field="label" font-size="40">
Label
</text>
</svg>
----
という SVG ファイルと
^^^^
label: "ラベル"
----
という YAML ファイルを受け取ったら、
SVG の text の中身のテキストを"ラベル" に置き換えるという感じです。
== PNG 自動拡大・縮小(後日実装メモ)
PNG でも、現在テープ幅printable_dotsに合わせた自動拡大・縮小を可能にしたい。
既定動作は現状維持(自動拡大・縮小 OFFとし、オプションで ON/OFF を切り替える。
実装方針(案):
- lib 側に「現在テープ幅へ raster をフィットする」共通 API を追加する。
- PNG/SVG とも最終的に raster になるため、fit 処理は共通化する。
- CLI には `--fit-current-tape`(仮)を追加し、明示時のみ有効化する。
- まずは `contain` 相当(縦横比維持で収める)を実装する。
検討ポイント:
- 1bit 画像の縮小品質(単純 nearest だと潰れやすい)。
- 既存の length x width 表示/内部座標との整合。
- 既存ユーザー互換性(デフォルト OFF を維持)。

View File

@@ -5,12 +5,10 @@
## 前提 ## 前提
1. リポジトリルートで共有ライブラリをビルドする(`libptouch.so``build/` に生成されます)。 1. リポジトリルートで共有ライブラリをビルドする(`libptouch.so``build/` に生成されます)。
```bash ```bash
cmake -S .. -B ../build cmake -S .. -B ../build
cmake --build ../build cmake --build ../build
``` ```
2. Ruby 3.0 以上と `ffi` gem。 2. Ruby 3.0 以上と `ffi` gem。
## インストール(開発時) ## インストール(開発時)
@@ -20,7 +18,7 @@ cd ruby
bundle install # または gem install ffi bundle install # または gem install ffi
bundle exec rubocop # 任意: スタイルチェック(.rubocop.yml bundle exec rubocop # 任意: スタイルチェック(.rubocop.yml
gem build libptouch.gemspec gem build libptouch.gemspec
gem install ./libptouch-1.0.0.gem gem install ./libptouch-1.0.1.gem
``` ```
ビルド済みの `../build/libptouch.so` を自動で読みに行きます。別のパスにある場合は環境変数で指定できます。 ビルド済みの `../build/libptouch.so` を自動で読みに行きます。別のパスにある場合は環境変数で指定できます。
@@ -37,7 +35,7 @@ C の `ptouch-print` と同様の流れで、**PNG/SVG 入力**`-w`/`-H` や
SVG は現在装着テープの印字可能幅に合わせて自動拡大・縮小しますUSB 接続が必要)。 SVG は現在装着テープの印字可能幅に合わせて自動拡大・縮小しますUSB 接続が必要)。
後方互換のため `ptouch-print-png` も引き続き使えます。 後方互換のため `ptouch-print-png` も引き続き使えます。
オプションは C 側に合わせ、**`-p` / `--pid`** で USB 製品 ID16 進可)を指定できます。省略時は PT-P900W`Libptouch::USB_PID_PTP900W` = `0x2085`)。例: PT-P750W `0x2062`、PT-P710BT `0x20af``libptouch.h` / `Libptouch` 定数と同じ)。 オプションは C 側に合わせ、`**-p` / `--pid`** で USB 製品 ID16 進可)を指定できます。省略時は PT-P900W`Libptouch::USB_PID_PTP900W` = `0x2085`)。例: PT-P750W `0x2062`、PT-P710BT `0x20af``libptouch.h` / `Libptouch` 定数と同じ)。
また、`--template`SVGと `--data`JSON/YAMLを使うと `data-field` 属性をキーにした差込印刷が可能です。 また、`--template`SVGと `--data`JSON/YAMLを使うと `data-field` 属性をキーにした差込印刷が可能です。
`--trim-right[=DOTS]` を付けると、libptouch 側の共通処理でラベル右側の空白ドット列を削減します。`DOTS` 省略時は左余白ドット数を使い、取得失敗時は `0` にフォールバックします。 `--trim-right[=DOTS]` を付けると、libptouch 側の共通処理でラベル右側の空白ドット列を削減します。`DOTS` 省略時は左余白ドット数を使い、取得失敗時は `0` にフォールバックします。
@@ -104,4 +102,5 @@ ctx.dispose
- `check_raster` / `print_raster` / `png_file_to_raster` / `svg_file_to_raster_fit_current_tape` / `status_bytes` / `status_hash` / `current_media_info` - `check_raster` / `print_raster` / `png_file_to_raster` / `svg_file_to_raster_fit_current_tape` / `status_bytes` / `status_hash` / `current_media_info`
- `current_media_info` には `print_dpi` / `feed_dpi` / `tape_width_mm` / `printable_height_dots` / `min_feed_mm` などを含む(テープ幅方向は `printable_height_dots` - `current_media_info` には `print_dpi` / `feed_dpi` / `tape_width_mm` / `printable_height_dots` / `min_feed_mm` などを含む(テープ幅方向は `printable_height_dots`
- `Libptouch.parse_status(raw)` … 32 バイトを Hash に展開(機種・テープ幅・**テープ種類**・色・**状態status_kind**・エラービット・`raw_hex` など) - `Libptouch.parse_status(raw)` … 32 バイトを Hash に展開(機種・テープ幅・**テープ種類**・色・**状態status_kind**・エラービット・`raw_hex` など)
- C の `libptouch_status_fprint``FILE *`)は FFI からはバインドしていません。テキスト出力の代わりに `parse_status` / `status_hash` を使ってください。 - C の `libptouch_status_fprint``FILE` *)は FFI からはバインドしていません。テキスト出力の代わりに `parse_status` / `status_hash` を使ってください。

View File

@@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
module Libptouch module Libptouch
VERSION = "1.0.0" VERSION = "1.0.1"
end end

BIN
samples/aBw70.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -13,6 +13,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <unistd.h>
static void usage(const char *argv0) static void usage(const char *argv0)
{ {
@@ -24,6 +25,7 @@ static void usage(const char *argv0)
" -t, --threshold N しきい値 0255既定 %u、PNG/SVG\n" " -t, --threshold N しきい値 0255既定 %u、PNG/SVG\n"
" --trim-right[=DOTS] 右側空白を削減(省略時は左余白、失敗時 0\n" " --trim-right[=DOTS] 右側空白を削減(省略時は左余白、失敗時 0\n"
" -n, --dry-run 読み込みと check_raster のみUSB なし)\n" " -n, --dry-run 読み込みと check_raster のみUSB なし)\n"
" -v, --verbose 印刷前情報と印刷後ステータスを標準出力\n"
" -p, --pid HEX USB 製品 ID既定: P900W の 0x2085。例: P750W 0x2062、P710BT 0x20af\n" " -p, --pid HEX USB 製品 ID既定: P900W の 0x2085。例: P750W 0x2062、P710BT 0x20af\n"
" -S, --status ステータスを表示して終了\n" " -S, --status ステータスを表示して終了\n"
" -V, --version バージョンを表示して終了\n" " -V, --version バージョンを表示して終了\n"
@@ -101,6 +103,69 @@ static int read_file(const char *path, uint8_t **out, size_t *out_len)
return 0; return 0;
} }
static void verbose_print_pre_print_info(libptouch_ctx *ctx,
const libptouch_raster_params_t *params,
size_t data_len)
{
if (!ctx || !params)
return;
printf("=== verbose: pre-print info ===\n");
printf("raster bytes: %zu\n", data_len);
printf("raster size: %ux%u dots (length x width)\n",
(unsigned)params->width_dots, (unsigned)params->height_dots);
printf("margin_mm: %u\n", (unsigned)params->margin_mm);
libptouch_media_info_t mi;
if (libptouch_get_current_media_info(ctx, &mi) == LIBPTOUCH_OK) {
printf("media width code: 0x%02X\n", (unsigned)mi.media_width_code);
printf("media kind code: 0x%02X\n", (unsigned)mi.media_kind_code);
printf("tape width: %.1f mm\n", mi.tape_width_mm);
printf("printable dots: %u\n", (unsigned)mi.printable_dots);
printf("left/right margins: %u/%u dots\n",
(unsigned)mi.left_margin_dots, (unsigned)mi.right_margin_dots);
printf("print/feed dpi: %.1f/%.1f\n", mi.print_dpi, mi.feed_dpi);
} else {
printf("media info: unavailable (%s)\n", libptouch_strerror(ctx));
}
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
if (libptouch_get_status(ctx, st) == LIBPTOUCH_OK) {
libptouch_status_fprint(stdout, st);
} else {
printf("status: unavailable (%s)\n", libptouch_strerror(ctx));
}
}
static void verbose_print_post_print_status(libptouch_ctx *ctx, const char *label)
{
if (!ctx)
return;
printf("=== verbose: post-print status (%s) ===\n", label);
/*
* 印刷直後は phase change (status[18] != 0x00) が続くことがあるため、
* 短時間ポーリングで最終状態まで追跡する。
*/
const int max_polls = 12; /* ~2.4s */
const useconds_t poll_interval_us = 200000;
for (int i = 0; i < max_polls; i++) {
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
if (libptouch_get_status(ctx, st) != LIBPTOUCH_OK) {
printf("status poll %d/%d: unavailable (%s)\n", i + 1,
max_polls, libptouch_strerror(ctx));
} else {
printf("status poll %d/%d:\n", i + 1, max_polls);
libptouch_status_fprint(stdout, st);
if (st[18] == 0x00u) {
printf("post-print polling settled: print end.\n");
return;
}
}
usleep(poll_interval_us);
}
printf("post-print polling timeout: phase did not settle.\n");
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
const char *file = NULL; const char *file = NULL;
@@ -108,6 +173,7 @@ int main(int argc, char **argv)
unsigned threshold = LIBPTOUCH_PNG_DEFAULT_THRESHOLD; unsigned threshold = LIBPTOUCH_PNG_DEFAULT_THRESHOLD;
unsigned usb_pid_arg = 0; unsigned usb_pid_arg = 0;
int dry_run = 0; int dry_run = 0;
int verbose = 0;
int trim_right_enabled = 0; int trim_right_enabled = 0;
int trim_right_auto = 0; int trim_right_auto = 0;
unsigned trim_right_dots = 0; unsigned trim_right_dots = 0;
@@ -120,6 +186,7 @@ int main(int argc, char **argv)
{ "threshold", required_argument, NULL, 't' }, { "threshold", required_argument, NULL, 't' },
{ "trim-right", optional_argument, NULL, 'r' }, { "trim-right", optional_argument, NULL, 'r' },
{ "dry-run", no_argument, NULL, 'n' }, { "dry-run", no_argument, NULL, 'n' },
{ "verbose", no_argument, NULL, 'v' },
{ "pid", required_argument, NULL, 'p' }, { "pid", required_argument, NULL, 'p' },
{ "status", no_argument, NULL, 'S' }, { "status", no_argument, NULL, 'S' },
{ "version", no_argument, NULL, 'V' }, { "version", no_argument, NULL, 'V' },
@@ -128,7 +195,7 @@ int main(int argc, char **argv)
}; };
int c; int c;
while ((c = getopt_long(argc, argv, "w:H:f:t:r::p:nhSV", longopts, NULL)) != while ((c = getopt_long(argc, argv, "w:H:f:t:r::p:nvhSV", longopts, NULL)) !=
-1) { -1) {
switch (c) { switch (c) {
case 'w': case 'w':
@@ -151,6 +218,9 @@ int main(int argc, char **argv)
case 'n': case 'n':
dry_run = 1; dry_run = 1;
break; break;
case 'v':
verbose = 1;
break;
case 'r': case 'r':
trim_right_enabled = 1; trim_right_enabled = 1;
if (!optarg) { if (!optarg) {
@@ -208,6 +278,10 @@ int main(int argc, char **argv)
libptouch_destroy(sctx); libptouch_destroy(sctx);
return 1; return 1;
} }
/* 印刷直後などは内部処理中で最初のステータス応答が不安定なことがあるため、
* 少し待ってからステータスを取りに行く。 */
usleep(300000); /* 300ms */
uint8_t st[LIBPTOUCH_STATUS_LENGTH]; uint8_t st[LIBPTOUCH_STATUS_LENGTH];
se = libptouch_get_status(sctx, st); se = libptouch_get_status(sctx, st);
if (se != LIBPTOUCH_OK) { if (se != LIBPTOUCH_OK) {
@@ -397,8 +471,13 @@ int main(int argc, char **argv)
} }
} }
if (verbose)
verbose_print_pre_print_info(ctx, &params, data_len);
e = libptouch_print_raster(ctx, data, data_len, &params); e = libptouch_print_raster(ctx, data, data_len, &params);
if (e != LIBPTOUCH_OK) { if (e != LIBPTOUCH_OK) {
if (verbose)
verbose_print_post_print_status(ctx, "print failed");
fprintf(stderr, "print_raster: %s\n", fprintf(stderr, "print_raster: %s\n",
libptouch_strerror(ctx)); libptouch_strerror(ctx));
libptouch_close(ctx); libptouch_close(ctx);
@@ -410,6 +489,9 @@ int main(int argc, char **argv)
return 1; return 1;
} }
if (verbose)
verbose_print_post_print_status(ctx, "print success");
libptouch_close(ctx); libptouch_close(ctx);
libptouch_destroy(ctx); libptouch_destroy(ctx);
if (data_from_lib) if (data_from_lib)

View File

@@ -7,6 +7,7 @@
#include "libptouch_internal.h" #include "libptouch_internal.h"
#include "libptouch_layout.h" #include "libptouch_layout.h"
#include "libptouch_protocol.h"
#include <libusb.h> #include <libusb.h>
#include <stdio.h> #include <stdio.h>
@@ -102,7 +103,7 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
(void)right_dots; (void)right_dots;
unsigned head_dots = prof->head_width_dots; unsigned head_dots = prof->head_width_dots;
size_t line_payload = (size_t)((head_dots + 7u) / 8u); size_t line_payload = ptouch_line_payload_bytes(head_dots);
size_t gf_packet = 3u + line_payload; size_t gf_packet = 3u + line_payload;
uint32_t wd = params->width_dots; uint32_t wd = params->width_dots;
@@ -139,21 +140,6 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
} }
uint32_t lines = ht; 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]; uint8_t head[256];
size_t pos = 0; size_t pos = 0;
memset(head + pos, 0, 200); memset(head + pos, 0, 200);
@@ -165,18 +151,27 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
memcpy(head + pos, raster_mode, sizeof(raster_mode)); memcpy(head + pos, raster_mode, sizeof(raster_mode));
pos += sizeof(raster_mode); pos += sizeof(raster_mode);
uint8_t esc_iz[] = { 0x1B, 0x69, 0x7A, 0x0Eu, n2_paper, media_w, uint8_t esc_iz[13];
0x00u, n5, n6, n7, n8, 0x02u, 0x00u }; ptouch_fill_esc_iz(esc_iz, media_kind, media_w, lines);
memcpy(head + pos, esc_iz, sizeof(esc_iz)); memcpy(head + pos, esc_iz, sizeof(esc_iz));
pos += sizeof(esc_iz); pos += sizeof(esc_iz);
static const uint8_t esc_im[] = { 0x1B, 0x69, 0x4D, 0x40 }; static const uint8_t esc_im[] = { 0x1B, 0x69, 0x4D, 0x40 };
memcpy(head + pos, esc_im, sizeof(esc_im)); memcpy(head + pos, esc_im, sizeof(esc_im));
pos += 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 }; static const uint8_t esc_ia[] = { 0x1B, 0x69, 0x41, 0x01 };
memcpy(head + pos, esc_ia, sizeof(esc_ia)); memcpy(head + pos, esc_ia, sizeof(esc_ia));
pos += sizeof(esc_ia); pos += sizeof(esc_ia);
static const uint8_t esc_ik[] = { 0x1B, 0x69, 0x4B, 0x0C }; }
uint8_t esc_ik[] = { 0x1B, 0x69, 0x4B, ptouch_esc_ik_value(head_dots) };
memcpy(head + pos, esc_ik, sizeof(esc_ik)); memcpy(head + pos, esc_ik, sizeof(esc_ik));
pos += sizeof(esc_ik); pos += sizeof(esc_ik);
@@ -199,7 +194,6 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
} }
size_t row_b = ((size_t)wd + 7u) / 8u; size_t row_b = ((size_t)wd + 7u) / 8u;
static const uint8_t g_hdr[] = { 0x47, 0x46, 0x00 };
uint8_t *gbuf = (uint8_t *)malloc(gf_packet); uint8_t *gbuf = (uint8_t *)malloc(gf_packet);
if (!gbuf) { if (!gbuf) {
@@ -213,7 +207,7 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
const uint8_t *row = src + (size_t)y * row_b; const uint8_t *row = src + (size_t)y * row_b;
pack_line(gbuf + 3, line_payload, head_dots, row, wd, left_dots, pack_line(gbuf + 3, line_payload, head_dots, row, wd, left_dots,
print_dots); print_dots);
memcpy(gbuf, g_hdr, sizeof(g_hdr)); ptouch_fill_gf_header(gbuf, line_payload);
v = ptouch_bulk_send_job(ctx, gbuf, gf_packet, "raster line"); v = ptouch_bulk_send_job(ctx, gbuf, gf_packet, "raster line");
if (v != LIBPTOUCH_OK) { if (v != LIBPTOUCH_OK) {
free(gbuf); free(gbuf);
@@ -223,6 +217,21 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
} }
free(gbuf); 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 }; static const uint8_t print_end[] = { 0x1A };
v = ptouch_bulk_send_job(ctx, print_end, sizeof(print_end), "print end"); v = ptouch_bulk_send_job(ctx, print_end, sizeof(print_end), "print end");
if (v != LIBPTOUCH_OK) { if (v != LIBPTOUCH_OK) {

View File

@@ -0,0 +1,39 @@
#include "libptouch_protocol.h"
size_t ptouch_line_payload_bytes(unsigned head_dots)
{
return (size_t)((head_dots + 7u) / 8u);
}
void ptouch_fill_gf_header(uint8_t out[3], size_t line_payload_bytes)
{
out[0] = 0x47u;
out[1] = (uint8_t)(line_payload_bytes & 0xFFu);
out[2] = (uint8_t)((line_payload_bytes >> 8) & 0xFFu);
}
void ptouch_fill_esc_iz(uint8_t out[13], uint8_t media_kind, uint8_t media_width,
uint32_t raster_lines)
{
out[0] = 0x1Bu;
out[1] = 0x69u;
out[2] = 0x7Au;
/* PI flags: enable media/width/length with quality bit for broad compatibility. */
out[3] = 0x8Eu;
/* Use status media-kind byte directly (e.g., 0x01 laminated, 0x03 non-laminate). */
out[4] = media_kind;
out[5] = media_width;
out[6] = 0x00u;
out[7] = (uint8_t)(raster_lines & 0xFFu);
out[8] = (uint8_t)((raster_lines >> 8) & 0xFFu);
out[9] = (uint8_t)((raster_lines >> 16) & 0xFFu);
out[10] = (uint8_t)((raster_lines >> 24) & 0xFFu);
out[11] = 0x00u; /* first page */
out[12] = 0x00u; /* fixed */
}
uint8_t ptouch_esc_ik_value(unsigned head_dots)
{
/* Preserve legacy 560-dot behaviour (0x0C), 128-dot uses 0x08. */
return head_dots > 128u ? 0x0Cu : 0x08u;
}

View File

@@ -0,0 +1,13 @@
#ifndef LIBPTOUCH_PROTOCOL_H
#define LIBPTOUCH_PROTOCOL_H
#include <stddef.h>
#include <stdint.h>
size_t ptouch_line_payload_bytes(unsigned head_dots);
void ptouch_fill_gf_header(uint8_t out[3], size_t line_payload_bytes);
void ptouch_fill_esc_iz(uint8_t out[13], uint8_t media_kind, uint8_t media_width,
uint32_t raster_lines);
uint8_t ptouch_esc_ik_value(unsigned head_dots);
#endif /* LIBPTOUCH_PROTOCOL_H */

View File

@@ -9,6 +9,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h>
libptouch_err_t libptouch_get_status(libptouch_ctx *ctx, uint8_t *status) libptouch_err_t libptouch_get_status(libptouch_ctx *ctx, uint8_t *status)
{ {
@@ -28,18 +29,34 @@ libptouch_err_t libptouch_get_status(libptouch_ctx *ctx, uint8_t *status)
static const uint8_t req[] = { 0x1B, 0x69, 0x53 }; static const uint8_t req[] = { 0x1B, 0x69, 0x53 };
int r = LIBUSB_ERROR_OTHER; int r = LIBUSB_ERROR_OTHER;
for (int attempt = 0; attempt < 2; attempt++) { for (int attempt = 0; attempt < 5; attempt++) {
if (attempt > 0) { /* First status read after reconnect can fail on some P-touch units. */
(void)libusb_clear_halt(h, ctx->bulk_in_ep); (void)libusb_clear_halt(h, ctx->bulk_in_ep);
(void)libusb_clear_halt(h, ctx->bulk_out_ep); (void)libusb_clear_halt(h, ctx->bulk_out_ep);
if (attempt > 0) {
usleep(120000); /* 120ms backoff */
} }
/*
* Avoid unconditional ESC @ here:
* some models can be in phase-change right after print end, and
* re-initializing there may disturb cutter/feed completion.
* Try plain ESC i S first, use ESC @ only on retry paths.
*/
if (attempt > 0) {
r = ptouch_bulk_out(h, ctx->bulk_out_ep, init, 2, 5000u); r = ptouch_bulk_out(h, ctx->bulk_out_ep, init, 2, 5000u);
if (r != 0) { if (r != 0) {
if (r == LIBUSB_ERROR_IO || r == LIBUSB_ERROR_PIPE ||
r == LIBUSB_ERROR_TIMEOUT)
continue;
ptouch_set_error_usb(ctx, r, "bulk OUT ESC @"); ptouch_set_error_usb(ctx, r, "bulk OUT ESC @");
return LIBPTOUCH_ERR_USB; return LIBPTOUCH_ERR_USB;
} }
}
r = ptouch_bulk_out(h, ctx->bulk_out_ep, req, 3, 5000u); r = ptouch_bulk_out(h, ctx->bulk_out_ep, req, 3, 5000u);
if (r != 0) { if (r != 0) {
if (r == LIBUSB_ERROR_IO || r == LIBUSB_ERROR_PIPE ||
r == LIBUSB_ERROR_TIMEOUT)
continue;
ptouch_set_error_usb(ctx, r, "bulk OUT ESC i S"); ptouch_set_error_usb(ctx, r, "bulk OUT ESC i S");
return LIBPTOUCH_ERR_USB; return LIBPTOUCH_ERR_USB;
} }
@@ -47,7 +64,8 @@ libptouch_err_t libptouch_get_status(libptouch_ctx *ctx, uint8_t *status)
(int)LIBPTOUCH_STATUS_LENGTH, 5000u); (int)LIBPTOUCH_STATUS_LENGTH, 5000u);
if (r == 0) if (r == 0)
break; break;
if (r != LIBUSB_ERROR_IO && r != LIBUSB_ERROR_PIPE) if (r != LIBUSB_ERROR_IO && r != LIBUSB_ERROR_PIPE &&
r != LIBUSB_ERROR_TIMEOUT)
break; break;
} }
if (r != 0) { if (r != 0) {

View File

@@ -0,0 +1,49 @@
#include "libptouch_protocol.h"
#include <stdio.h>
#include <stdlib.h>
static int expect_int(const char *name, int got, int want)
{
if (got == want)
return 0;
fprintf(stderr, "%s mismatch: got=%d want=%d\n", name, got, want);
return 1;
}
int main(void)
{
int fail = 0;
fail |= expect_int("line_payload_128", (int)ptouch_line_payload_bytes(128u), 16);
fail |= expect_int("line_payload_560", (int)ptouch_line_payload_bytes(560u), 70);
uint8_t gf[3];
ptouch_fill_gf_header(gf, 16u);
fail |= expect_int("gf16_cmd", gf[0], 0x47);
fail |= expect_int("gf16_n1", gf[1], 0x10);
fail |= expect_int("gf16_n2", gf[2], 0x00);
ptouch_fill_gf_header(gf, 70u);
fail |= expect_int("gf70_cmd", gf[0], 0x47);
fail |= expect_int("gf70_n1", gf[1], 0x46);
fail |= expect_int("gf70_n2", gf[2], 0x00);
uint8_t iz[13];
ptouch_fill_esc_iz(iz, 0x01u, 0x0Cu, 70u);
fail |= expect_int("iz_cmd_0", iz[0], 0x1B);
fail |= expect_int("iz_cmd_1", iz[1], 0x69);
fail |= expect_int("iz_cmd_2", iz[2], 0x7A);
fail |= expect_int("iz_n1_flags", iz[3], 0x8E);
fail |= expect_int("iz_media_kind_passthrough", iz[4], 0x01);
fail |= expect_int("iz_media_width", iz[5], 0x0C);
fail |= expect_int("iz_lines_lsb", iz[7], 70);
fail |= expect_int("iz_page_index", iz[11], 0x00);
fail |= expect_int("iz_last_fixed", iz[12], 0x00);
/* ESC i K mode byte: 128-dot vs 560-dot families */
fail |= expect_int("esc_ik_128", ptouch_esc_ik_value(128u), 0x08);
fail |= expect_int("esc_ik_560", ptouch_esc_ik_value(560u), 0x0C);
return fail ? EXIT_FAILURE : EXIT_SUCCESS;
}