Compare commits
5 Commits
e92273a747
...
v1.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
| d0e5846012 | |||
| d2fd6cc1f9 | |||
| 779a50747d | |||
| 29072dc20c | |||
| 42a785f086 |
19
CHANGELOG.md
Normal file
19
CHANGELOG.md
Normal 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.
|
||||
@@ -4,7 +4,7 @@
|
||||
# Email: knb@artif.org
|
||||
|
||||
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_REQUIRED ON)
|
||||
@@ -25,6 +25,7 @@ configure_file(
|
||||
set(LIBPTOUCH_SOURCES
|
||||
src/lib/libptouch_core.c
|
||||
src/lib/libptouch_usb.c
|
||||
src/lib/libptouch_protocol.c
|
||||
src/lib/libptouch_layout.c
|
||||
src/lib/libptouch_media_info.c
|
||||
src/lib/libptouch_trim.c
|
||||
@@ -74,6 +75,19 @@ if(NOT MSVC)
|
||||
target_compile_options(ptouch-print PRIVATE -Wall -Wextra -Wpedantic)
|
||||
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_shared LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
|
||||
15
README.md
15
README.md
@@ -1,15 +1,18 @@
|
||||
# ptouch_label
|
||||
|
||||
**バージョン 1.0.0**(初回リリース)
|
||||
**バージョン 1.0.1**
|
||||
|
||||
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`。
|
||||
|
||||
現状は USB only / 将来 Bluetooth 対応予定。
|
||||
|
||||
## レイアウト
|
||||
|
||||
|
||||
| パス | 内容 |
|
||||
|------|------|
|
||||
| ----------------------- | --------------------------------------------------------------------------- |
|
||||
| `include/libptouch.h` | 公開 API |
|
||||
| `src/lib/libptouch_*.c` | ライブラリ本体(core / usb / print / status / png / svg) |
|
||||
| `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` |
|
||||
| `reference/` | 仕様・参考資料(例: ラスター PDF) |
|
||||
|
||||
|
||||
## ビルド
|
||||
|
||||
依存: **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
|
||||
cmake -S . -B build
|
||||
cmake --build build
|
||||
ctest --test-dir build --output-on-failure
|
||||
```
|
||||
|
||||
成果物(`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` の定数参照)。
|
||||
- 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)
|
||||
|
||||
@@ -116,3 +121,7 @@ sudo usermod -aG plugdev "$USER"
|
||||
## ライセンス
|
||||
|
||||
[MIT License](LICENSE)(`LICENSE` ファイルを参照)。
|
||||
|
||||
## 変更履歴
|
||||
|
||||
リリース間の変更点は `CHANGELOG.md` を参照してください。
|
||||
@@ -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 を維持)。
|
||||
|
||||
@@ -5,12 +5,10 @@
|
||||
## 前提
|
||||
|
||||
1. リポジトリルートで共有ライブラリをビルドする(`libptouch.so` が `build/` に生成されます)。
|
||||
|
||||
```bash
|
||||
cmake -S .. -B ../build
|
||||
cmake --build ../build
|
||||
```
|
||||
|
||||
2. Ruby 3.0 以上と `ffi` gem。
|
||||
|
||||
## インストール(開発時)
|
||||
@@ -20,7 +18,7 @@ cd ruby
|
||||
bundle install # または gem install ffi
|
||||
bundle exec rubocop # 任意: スタイルチェック(.rubocop.yml)
|
||||
gem build libptouch.gemspec
|
||||
gem install ./libptouch-1.0.0.gem
|
||||
gem install ./libptouch-1.0.1.gem
|
||||
```
|
||||
|
||||
ビルド済みの `../build/libptouch.so` を自動で読みに行きます。別のパスにある場合は環境変数で指定できます。
|
||||
@@ -37,7 +35,7 @@ C の `ptouch-print` と同様の流れで、**PNG/SVG 入力**(`-w`/`-H` や
|
||||
SVG は現在装着テープの印字可能幅に合わせて自動拡大・縮小します(USB 接続が必要)。
|
||||
後方互換のため `ptouch-print-png` も引き続き使えます。
|
||||
|
||||
オプションは C 側に合わせ、**`-p` / `--pid`** で USB 製品 ID(16 進可)を指定できます。省略時は PT-P900W(`Libptouch::USB_PID_PTP900W` = `0x2085`)。例: PT-P750W `0x2062`、PT-P710BT `0x20af`(`libptouch.h` / `Libptouch` 定数と同じ)。
|
||||
オプションは C 側に合わせ、`**-p` / `--pid`** で USB 製品 ID(16 進可)を指定できます。省略時は PT-P900W(`Libptouch::USB_PID_PTP900W` = `0x2085`)。例: PT-P750W `0x2062`、PT-P710BT `0x20af`(`libptouch.h` / `Libptouch` 定数と同じ)。
|
||||
また、`--template`(SVG)と `--data`(JSON/YAML)を使うと `data-field` 属性をキーにした差込印刷が可能です。
|
||||
`--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`
|
||||
- `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` など)
|
||||
- C の `libptouch_status_fprint`(`FILE *`)は FFI からはバインドしていません。テキスト出力の代わりに `parse_status` / `status_hash` を使ってください。
|
||||
- C の `libptouch_status_fprint`(`FILE` *)は FFI からはバインドしていません。テキスト出力の代わりに `parse_status` / `status_hash` を使ってください。
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Libptouch
|
||||
VERSION = "1.0.0"
|
||||
VERSION = "1.0.1"
|
||||
end
|
||||
|
||||
BIN
samples/aBw70.png
Normal file
BIN
samples/aBw70.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
@@ -13,6 +13,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void usage(const char *argv0)
|
||||
{
|
||||
@@ -24,6 +25,7 @@ static void usage(const char *argv0)
|
||||
" -t, --threshold N しきい値 0–255(既定 %u、PNG/SVG)\n"
|
||||
" --trim-right[=DOTS] 右側空白を削減(省略時は左余白、失敗時 0)\n"
|
||||
" -n, --dry-run 読み込みと check_raster のみ(USB なし)\n"
|
||||
" -v, --verbose 印刷前情報と印刷後ステータスを標準出力\n"
|
||||
" -p, --pid HEX USB 製品 ID(既定: P900W の 0x2085)。例: P750W 0x2062、P710BT 0x20af\n"
|
||||
" -S, --status ステータスを表示して終了\n"
|
||||
" -V, --version バージョンを表示して終了\n"
|
||||
@@ -101,6 +103,69 @@ static int read_file(const char *path, uint8_t **out, size_t *out_len)
|
||||
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)
|
||||
{
|
||||
const char *file = NULL;
|
||||
@@ -108,6 +173,7 @@ int main(int argc, char **argv)
|
||||
unsigned threshold = LIBPTOUCH_PNG_DEFAULT_THRESHOLD;
|
||||
unsigned usb_pid_arg = 0;
|
||||
int dry_run = 0;
|
||||
int verbose = 0;
|
||||
int trim_right_enabled = 0;
|
||||
int trim_right_auto = 0;
|
||||
unsigned trim_right_dots = 0;
|
||||
@@ -120,6 +186,7 @@ int main(int argc, char **argv)
|
||||
{ "threshold", required_argument, NULL, 't' },
|
||||
{ "trim-right", optional_argument, NULL, 'r' },
|
||||
{ "dry-run", no_argument, NULL, 'n' },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "pid", required_argument, NULL, 'p' },
|
||||
{ "status", no_argument, NULL, 'S' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
@@ -128,7 +195,7 @@ int main(int argc, char **argv)
|
||||
};
|
||||
|
||||
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) {
|
||||
switch (c) {
|
||||
case 'w':
|
||||
@@ -151,6 +218,9 @@ int main(int argc, char **argv)
|
||||
case 'n':
|
||||
dry_run = 1;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'r':
|
||||
trim_right_enabled = 1;
|
||||
if (!optarg) {
|
||||
@@ -208,6 +278,10 @@ int main(int argc, char **argv)
|
||||
libptouch_destroy(sctx);
|
||||
return 1;
|
||||
}
|
||||
/* 印刷直後などは内部処理中で最初のステータス応答が不安定なことがあるため、
|
||||
* 少し待ってからステータスを取りに行く。 */
|
||||
usleep(300000); /* 300ms */
|
||||
|
||||
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
|
||||
se = libptouch_get_status(sctx, st);
|
||||
if (se != LIBPTOUCH_OK) {
|
||||
@@ -397,8 +471,13 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
verbose_print_pre_print_info(ctx, ¶ms, data_len);
|
||||
|
||||
e = libptouch_print_raster(ctx, data, data_len, ¶ms);
|
||||
if (e != LIBPTOUCH_OK) {
|
||||
if (verbose)
|
||||
verbose_print_post_print_status(ctx, "print failed");
|
||||
fprintf(stderr, "print_raster: %s\n",
|
||||
libptouch_strerror(ctx));
|
||||
libptouch_close(ctx);
|
||||
@@ -410,6 +489,9 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
verbose_print_post_print_status(ctx, "print success");
|
||||
|
||||
libptouch_close(ctx);
|
||||
libptouch_destroy(ctx);
|
||||
if (data_from_lib)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "libptouch_internal.h"
|
||||
#include "libptouch_layout.h"
|
||||
#include "libptouch_protocol.h"
|
||||
|
||||
#include <libusb.h>
|
||||
#include <stdio.h>
|
||||
@@ -102,7 +103,7 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
|
||||
(void)right_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;
|
||||
|
||||
uint32_t wd = params->width_dots;
|
||||
@@ -139,21 +140,6 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -165,18 +151,27 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
|
||||
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 };
|
||||
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);
|
||||
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));
|
||||
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;
|
||||
static const uint8_t g_hdr[] = { 0x47, 0x46, 0x00 };
|
||||
|
||||
uint8_t *gbuf = (uint8_t *)malloc(gf_packet);
|
||||
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;
|
||||
pack_line(gbuf + 3, line_payload, head_dots, row, wd, left_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");
|
||||
if (v != LIBPTOUCH_OK) {
|
||||
free(gbuf);
|
||||
@@ -223,6 +217,21 @@ libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
|
||||
}
|
||||
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) {
|
||||
|
||||
39
src/lib/libptouch_protocol.c
Normal file
39
src/lib/libptouch_protocol.c
Normal 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;
|
||||
}
|
||||
13
src/lib/libptouch_protocol.h
Normal file
13
src/lib/libptouch_protocol.h
Normal 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 */
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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 };
|
||||
|
||||
int r = LIBUSB_ERROR_OTHER;
|
||||
for (int attempt = 0; attempt < 2; attempt++) {
|
||||
if (attempt > 0) {
|
||||
for (int attempt = 0; attempt < 5; attempt++) {
|
||||
/* 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_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);
|
||||
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 @");
|
||||
return LIBPTOUCH_ERR_USB;
|
||||
}
|
||||
}
|
||||
r = ptouch_bulk_out(h, ctx->bulk_out_ep, req, 3, 5000u);
|
||||
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");
|
||||
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);
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r != LIBUSB_ERROR_IO && r != LIBUSB_ERROR_PIPE)
|
||||
if (r != LIBUSB_ERROR_IO && r != LIBUSB_ERROR_PIPE &&
|
||||
r != LIBUSB_ERROR_TIMEOUT)
|
||||
break;
|
||||
}
|
||||
if (r != 0) {
|
||||
|
||||
49
tests/protocol_regression_test.c
Normal file
49
tests/protocol_regression_test.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user