Compare commits
6 Commits
fdcb4d97fa
...
feature/sv
| Author | SHA1 | Date | |
|---|---|---|---|
| d683bf983a | |||
| 28a0c73f6a | |||
| 2ed0bfc0be | |||
| f26a1186a3 | |||
| ae67a8b288 | |||
| add08ba9b2 |
@@ -14,6 +14,7 @@ include(GNUInstallDirs)
|
|||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(LIBUSB REQUIRED IMPORTED_TARGET libusb-1.0)
|
pkg_check_modules(LIBUSB REQUIRED IMPORTED_TARGET libusb-1.0)
|
||||||
find_package(PNG REQUIRED)
|
find_package(PNG REQUIRED)
|
||||||
|
pkg_check_modules(LIBRSVG IMPORTED_TARGET librsvg-2.0)
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/libptouch_version.h.in"
|
"${CMAKE_CURRENT_SOURCE_DIR}/include/libptouch_version.h.in"
|
||||||
@@ -21,8 +22,19 @@ configure_file(
|
|||||||
@ONLY
|
@ONLY
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(ptouch STATIC src/libptouch.c)
|
set(LIBPTOUCH_SOURCES
|
||||||
add_library(ptouch_shared SHARED src/libptouch.c)
|
src/lib/libptouch_core.c
|
||||||
|
src/lib/libptouch_usb.c
|
||||||
|
src/lib/libptouch_layout.c
|
||||||
|
src/lib/libptouch_media_info.c
|
||||||
|
src/lib/libptouch_print.c
|
||||||
|
src/lib/libptouch_status.c
|
||||||
|
src/lib/libptouch_png.c
|
||||||
|
src/lib/libptouch_svg.c
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(ptouch STATIC ${LIBPTOUCH_SOURCES})
|
||||||
|
add_library(ptouch_shared SHARED ${LIBPTOUCH_SOURCES})
|
||||||
set_target_properties(ptouch_shared PROPERTIES OUTPUT_NAME ptouch
|
set_target_properties(ptouch_shared PROPERTIES OUTPUT_NAME ptouch
|
||||||
SOVERSION ${PROJECT_VERSION_MAJOR})
|
SOVERSION ${PROJECT_VERSION_MAJOR})
|
||||||
target_include_directories(ptouch PUBLIC
|
target_include_directories(ptouch PUBLIC
|
||||||
@@ -30,13 +42,25 @@ target_include_directories(ptouch PUBLIC
|
|||||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
|
||||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||||
)
|
)
|
||||||
|
target_include_directories(ptouch PRIVATE
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/lib"
|
||||||
|
)
|
||||||
target_include_directories(ptouch_shared PUBLIC
|
target_include_directories(ptouch_shared PUBLIC
|
||||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
|
||||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||||
)
|
)
|
||||||
|
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 PRIVATE PkgConfig::LIBUSB PNG::PNG)
|
||||||
target_link_libraries(ptouch_shared PRIVATE PkgConfig::LIBUSB PNG::PNG)
|
target_link_libraries(ptouch_shared PRIVATE PkgConfig::LIBUSB PNG::PNG)
|
||||||
|
if(LIBRSVG_FOUND)
|
||||||
|
target_link_libraries(ptouch PRIVATE PkgConfig::LIBRSVG)
|
||||||
|
target_link_libraries(ptouch_shared PRIVATE PkgConfig::LIBRSVG)
|
||||||
|
target_compile_definitions(ptouch PRIVATE LIBPTOUCH_HAS_RSVG=1)
|
||||||
|
target_compile_definitions(ptouch_shared PRIVATE LIBPTOUCH_HAS_RSVG=1)
|
||||||
|
endif()
|
||||||
if(NOT MSVC)
|
if(NOT MSVC)
|
||||||
target_compile_options(ptouch PRIVATE -Wall -Wextra -Wpedantic)
|
target_compile_options(ptouch PRIVATE -Wall -Wextra -Wpedantic)
|
||||||
target_compile_options(ptouch_shared PRIVATE -Wall -Wextra -Wpedantic)
|
target_compile_options(ptouch_shared PRIVATE -Wall -Wextra -Wpedantic)
|
||||||
|
|||||||
29
README.md
29
README.md
@@ -4,22 +4,22 @@
|
|||||||
|
|
||||||
Brother P-touch シリーズ向けのラベル印刷用 **C コアライブラリ(libptouch)** と、動作確認用 **CLI(`ptouch-print`)** のリポジトリです。
|
Brother P-touch シリーズ向けのラベル印刷用 **C コアライブラリ(libptouch)** と、動作確認用 **CLI(`ptouch-print`)** のリポジトリです。
|
||||||
|
|
||||||
保有機種: **PT-P900W**(USB・ラスターコマンド)。
|
対象機種: **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`。
|
||||||
|
|
||||||
## レイアウト
|
## レイアウト
|
||||||
|
|
||||||
| パス | 内容 |
|
| パス | 内容 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `include/libptouch.h` | 公開 API |
|
| `include/libptouch.h` | 公開 API |
|
||||||
| `src/libptouch.c` | ライブラリ本体(スタブ) |
|
| `src/lib/libptouch_*.c` | ライブラリ本体(core / usb / print / status / png / svg) |
|
||||||
| `src/cli/main.c` | `ptouch-print` エントリ |
|
| `src/cli/main.c` | `ptouch-print` エントリ |
|
||||||
| `samples/` | 試験用サンプル画像の置き場(PNG 等) |
|
| `samples/` | 試験用サンプル画像の置き場(PNG/SVG 等) |
|
||||||
| `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**(開発パッケージ例: `libusb-1.0-0-dev`、`libpng-dev`)。
|
依存: **CMake 3.16+**、**libusb-1.0**、**libpng**、**librsvg-2.0**(開発パッケージ例: `libusb-1.0-0-dev`、`libpng-dev`、`librsvg2-dev`)。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cmake -S . -B build
|
cmake -S . -B build
|
||||||
@@ -38,7 +38,9 @@ cmake --build build
|
|||||||
|
|
||||||
## CLI の使い方(雛形)
|
## CLI の使い方(雛形)
|
||||||
|
|
||||||
**PNG**(拡張子 `.png` または PNG シグネチャ)の場合は幅・高さは画像から取得します。任意で `-t`(0–255)で二値化しきい値を指定できます。
|
**PNG**(拡張子 `.png` または PNG シグネチャ)の場合は幅・高さは画像から取得します。
|
||||||
|
**SVG**(拡張子 `.svg`)の場合は現在装着中テープの印字可能ドット幅に合わせて自動拡大・縮小します(USB 接続が必要)。
|
||||||
|
任意で `-t`(0–255)で二値化しきい値を指定できます。
|
||||||
|
|
||||||
**1bit packed ラスター**(行優先、行あたり `ceil(width/8)` バイト × 行数)の場合は `-f` に加え `-w` / `-H` が必須です。
|
**1bit packed ラスター**(行優先、行あたり `ceil(width/8)` バイト × 行数)の場合は `-f` に加え `-w` / `-H` が必須です。
|
||||||
|
|
||||||
@@ -48,6 +50,9 @@ cmake --build build
|
|||||||
# PNG — 検証のみ(USB 不要)
|
# PNG — 検証のみ(USB 不要)
|
||||||
./build/ptouch-print -n -f label.png
|
./build/ptouch-print -n -f label.png
|
||||||
|
|
||||||
|
# SVG — 幅を現在テープにフィットさせて検証(USB 必要)
|
||||||
|
./build/ptouch-print -n -f label.svg
|
||||||
|
|
||||||
# PNG — しきい値を指定
|
# PNG — しきい値を指定
|
||||||
./build/ptouch-print -n -f label.png -t 160
|
./build/ptouch-print -n -f label.png -t 160
|
||||||
|
|
||||||
@@ -59,7 +64,12 @@ cmake --build build
|
|||||||
|
|
||||||
# USB 接続時
|
# USB 接続時
|
||||||
./build/ptouch-print -f label.png
|
./build/ptouch-print -f label.png
|
||||||
|
./build/ptouch-print -f label.svg
|
||||||
./build/ptouch-print -f sample.raster -w 128 -H 64
|
./build/ptouch-print -f sample.raster -w 128 -H 64
|
||||||
|
|
||||||
|
# PT-P750W / PT-P710BT(`lsusb` の PID に合わせる)
|
||||||
|
./build/ptouch-print --status -p 0x2062
|
||||||
|
./build/ptouch-print -f label.png -p 0x20af
|
||||||
```
|
```
|
||||||
|
|
||||||
`-n`(`--dry-run`)では読み込みと `libptouch_check_raster` まで実行します。
|
`-n`(`--dry-run`)では読み込みと `libptouch_check_raster` まで実行します。
|
||||||
@@ -70,8 +80,10 @@ cmake --build build
|
|||||||
- `libptouch_create` / `libptouch_destroy` — コンテキスト
|
- `libptouch_create` / `libptouch_destroy` — コンテキスト
|
||||||
- `libptouch_open_usb` / `libptouch_open_usb_vid_pid` / `libptouch_close` — USB(libusb・VID/PID)
|
- `libptouch_open_usb` / `libptouch_open_usb_vid_pid` / `libptouch_close` — USB(libusb・VID/PID)
|
||||||
- `libptouch_get_status` / `libptouch_status_fprint` — ステータス情報リクエスト(ESC i S)の応答
|
- `libptouch_get_status` / `libptouch_status_fprint` — ステータス情報リクエスト(ESC i S)の応答
|
||||||
|
- `libptouch_get_current_media_info` — 現在テープ幅(mm)・印字可能幅(dots)・DPI・最小余白(mm)を取得
|
||||||
- `libptouch_check_raster` — ラスターバッファの検証のみ
|
- `libptouch_check_raster` — ラスターバッファの検証のみ
|
||||||
- `libptouch_png_file_to_raster` / `libptouch_free_raster` — PNG を 1bit ラスターに変換(libpng)
|
- `libptouch_png_file_to_raster` / `libptouch_free_raster` — PNG を 1bit ラスターに変換(libpng)
|
||||||
|
- `libptouch_svg_file_to_raster_fit_current_tape` — SVG を現在テープ幅に合わせて 1bit ラスターへ変換(librsvg + cairo、USB 必須)
|
||||||
- `libptouch_print_raster` — ラスター印刷(USB・PDF のコマンド列、内部で転置)
|
- `libptouch_print_raster` — ラスター印刷(USB・PDF のコマンド列、内部で転置)
|
||||||
- `libptouch_strerror` / `libptouch_last_error` — エラー情報
|
- `libptouch_strerror` / `libptouch_last_error` — エラー情報
|
||||||
|
|
||||||
@@ -79,12 +91,13 @@ cmake --build build
|
|||||||
|
|
||||||
## PT-P900W / Linux でのメモ
|
## PT-P900W / Linux でのメモ
|
||||||
|
|
||||||
- 接続は **libusb-1.0** のみ。機種ごとに **VID/PID**(`lsusb` 等)を調べ、`libptouch_open_usb_vid_pid` に渡すか、既定の PT-P900W なら `libptouch_open_usb` を使います。
|
- 接続は **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` の定数参照)。
|
||||||
- ラスターコマンドの詳細は **`reference/` の PDF** および Brother 公開資料に沿って `src/libptouch.c` に実装してください。
|
- PT-P750W / PT-P710BT ではラスター幅方向は **180 dpi**(P900W は 360 dpi)。PNG から印刷する場合は解像度に合わせて画像を用意してください。
|
||||||
|
- ラスターコマンドの詳細は **`reference/` の PDF** および Brother 公開資料に沿って `src/lib/libptouch_*.c` に実装してください。
|
||||||
|
|
||||||
### Ubuntu で sudo なしで USB を開く(udev)
|
### Ubuntu で sudo なしで USB を開く(udev)
|
||||||
|
|
||||||
既定の **04f9:2085**(PT-P900W)向けルールを `udev/99-ptouch-label-brother.rules` に置いています。
|
**04f9:2085**(PT-P900W)、**04f9:2062**(PT-P750W)、**04f9:20af**(PT-P710BT)向けルールを `udev/99-ptouch-label-brother.rules` に置いています。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo cp udev/99-ptouch-label-brother.rules /etc/udev/rules.d/
|
sudo cp udev/99-ptouch-label-brother.rules /etc/udev/rules.d/
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
/**
|
/**
|
||||||
* libptouch — Brother P-touch ラスター印刷 (USB) 用 C API
|
* libptouch — Brother P-touch ラスター印刷 (USB) 用 C API
|
||||||
*
|
*
|
||||||
* 対象例: PT-P900W(ラスターコマンド)。実装は src/libptouch.c を参照。
|
* 対象例: PT-P900W(560 ドットヘッド)、PT-P750W / PT-P710BT(128 ドットヘッド)。
|
||||||
|
* 実装は src/lib/libptouch_*.c を参照。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LIBPTOUCH_H
|
#ifndef LIBPTOUCH_H
|
||||||
@@ -41,6 +42,9 @@ typedef enum {
|
|||||||
/** lsusb 例: PT-P900W — Brother Industries, Ltd (04f9:2085) */
|
/** lsusb 例: PT-P900W — Brother Industries, Ltd (04f9:2085) */
|
||||||
#define LIBPTOUCH_USB_VID_BROTHER 0x04f9u
|
#define LIBPTOUCH_USB_VID_BROTHER 0x04f9u
|
||||||
#define LIBPTOUCH_USB_PID_PTP900W 0x2085u
|
#define LIBPTOUCH_USB_PID_PTP900W 0x2085u
|
||||||
|
/** PT-P750W / PT-P710BT(cv_ptp750w_710bt_jpn_raster_102.pdf Appendix A) */
|
||||||
|
#define LIBPTOUCH_USB_PID_PTP750W 0x2062u
|
||||||
|
#define LIBPTOUCH_USB_PID_PTP710BT 0x20afu
|
||||||
|
|
||||||
libptouch_ctx *libptouch_create(void);
|
libptouch_ctx *libptouch_create(void);
|
||||||
void libptouch_destroy(libptouch_ctx *ctx);
|
void libptouch_destroy(libptouch_ctx *ctx);
|
||||||
@@ -69,6 +73,19 @@ typedef struct {
|
|||||||
uint8_t margin_mm; /**< 余白など(機種・仕様に合わせて使用) */
|
uint8_t margin_mm; /**< 余白など(機種・仕様に合わせて使用) */
|
||||||
} libptouch_raster_params_t;
|
} libptouch_raster_params_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t media_width_code; /**< status[10] */
|
||||||
|
uint8_t media_kind_code; /**< status[11] */
|
||||||
|
double print_dpi; /**< 印字幅方向 DPI(現状 180 or 360) */
|
||||||
|
double feed_dpi; /**< 送り方向 DPI(ESC i d の基準) */
|
||||||
|
double tape_width_mm; /**< 装着テープ幅(mm)。例: 3.5, 6, 9, 12, 18, 24, 36 */
|
||||||
|
uint16_t printable_dots; /**< 現在テープで印字可能な幅(ドット) */
|
||||||
|
uint16_t left_margin_dots; /**< 左余白(ドット) */
|
||||||
|
uint16_t right_margin_dots; /**< 右余白(ドット) */
|
||||||
|
uint16_t min_feed_dots; /**< 仕様上の最小送り量(ドット)。現在は 14 */
|
||||||
|
double min_feed_mm; /**< 最小送り量(mm) */
|
||||||
|
} libptouch_media_info_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* バッファサイズとパラメータの整合性のみ検査(USB 不要)。--dry-run 用。
|
* バッファサイズとパラメータの整合性のみ検査(USB 不要)。--dry-run 用。
|
||||||
*/
|
*/
|
||||||
@@ -77,9 +94,17 @@ libptouch_err_t libptouch_check_raster(libptouch_ctx *ctx,
|
|||||||
const libptouch_raster_params_t *params);
|
const libptouch_raster_params_t *params);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1 ビット packed ラスターを USB で印刷(cv_ptp900_jpn_raster_102.pdf 準拠)。
|
* 現在装着テープの情報を取得する(USB 接続済み必須)。
|
||||||
|
* ステータスと機種プロファイルからテープ幅・印字可能幅・最小余白量を返す。
|
||||||
|
*/
|
||||||
|
libptouch_err_t libptouch_get_current_media_info(libptouch_ctx *ctx,
|
||||||
|
libptouch_media_info_t *out_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1 ビット packed ラスターを USB で印刷(各機種のラスター PDF 準拠)。
|
||||||
* 印字前にステータスでテープ幅を読み、印刷可能ドット内に画像を中央配置する。
|
* 印字前にステータスでテープ幅を読み、印刷可能ドット内に画像を中央配置する。
|
||||||
* width_dots は装着テープの印刷可能幅以下であること。360dpi 相当のドット列を想定。
|
* width_dots は装着テープの印刷可能幅以下であること。PT-P900 系は幅 360dpi、
|
||||||
|
* PT-P750W / PT-P710BT は幅 180dpi(cv_ptp750w_710bt_jpn_raster_102.pdf)のドット列を想定。
|
||||||
* @param margin_mm 余白(フィード)量。0 のとき PDF の最小 1mm(14 ドット)相当を送る。
|
* @param margin_mm 余白(フィード)量。0 のとき PDF の最小 1mm(14 ドット)相当を送る。
|
||||||
* 印刷時は内部でドット列を転置する(テープ幅方向とバッファの縦横の対応)。
|
* 印刷時は内部でドット列を転置する(テープ幅方向とバッファの縦横の対応)。
|
||||||
* @param data 1 行あたり width_dots ビットを ceil(width_dots/8) バイトで並べた連続領域
|
* @param data 1 行あたり width_dots ビットを ceil(width_dots/8) バイトで並べた連続領域
|
||||||
@@ -96,6 +121,10 @@ typedef struct {
|
|||||||
uint8_t threshold; /**< 輝度がこれ未満なら黒(1)、以上なら白(0) */
|
uint8_t threshold; /**< 輝度がこれ未満なら黒(1)、以上なら白(0) */
|
||||||
} libptouch_png_options_t;
|
} libptouch_png_options_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t threshold; /**< 輝度がこれ未満なら黒(1)、以上なら白(0) */
|
||||||
|
} libptouch_svg_options_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PNG ファイルを読み、1bit packed ラスターに変換する(libpng)。
|
* PNG ファイルを読み、1bit packed ラスターに変換する(libpng)。
|
||||||
* @param out_raster malloc 済みバッファのポインタを返す。不要時は @ref libptouch_free_raster で解放。
|
* @param out_raster malloc 済みバッファのポインタを返す。不要時は @ref libptouch_free_raster で解放。
|
||||||
@@ -105,6 +134,17 @@ libptouch_err_t libptouch_png_file_to_raster(libptouch_ctx *ctx, const char *pat
|
|||||||
uint8_t **out_raster, size_t *out_raster_bytes,
|
uint8_t **out_raster, size_t *out_raster_bytes,
|
||||||
libptouch_raster_params_t *out_params);
|
libptouch_raster_params_t *out_params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SVG ファイルを現在装着中テープの印字可能幅に合わせて 1bit packed ラスターへ変換する
|
||||||
|
* (librsvg + cairo)。
|
||||||
|
* @note 現在テープ幅を取得するため、事前に @ref libptouch_open_usb などで USB 接続が必要。
|
||||||
|
* @param out_raster malloc 済みバッファのポインタを返す。不要時は @ref libptouch_free_raster で解放。
|
||||||
|
*/
|
||||||
|
libptouch_err_t libptouch_svg_file_to_raster_fit_current_tape(
|
||||||
|
libptouch_ctx *ctx, const char *path,
|
||||||
|
const libptouch_svg_options_t *options, uint8_t **out_raster,
|
||||||
|
size_t *out_raster_bytes, libptouch_raster_params_t *out_params);
|
||||||
|
|
||||||
void libptouch_free_raster(uint8_t *raster);
|
void libptouch_free_raster(uint8_t *raster);
|
||||||
|
|
||||||
/** ステータス情報リクエスト(ESC i S)の応答バイト数(cv_ptp900_jpn_raster_102.pdf) */
|
/** ステータス情報リクエスト(ESC i S)の応答バイト数(cv_ptp900_jpn_raster_102.pdf) */
|
||||||
|
|||||||
BIN
reference/cv_ptp750w_710bt_jpn_raster_102.pdf
Normal file
BIN
reference/cv_ptp750w_710bt_jpn_raster_102.pdf
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
# libptouch(Ruby gem)
|
# libptouch(Ruby gem)
|
||||||
|
|
||||||
[ptouch_label](../) の **libptouch** を [ffi](https://github.com/ffi/ffi) 経由で使うための Gem です。
|
[ptouch_label](../) の **libptouch** を [ffi](https://github.com/ffi/ffi) 経由で使うための Gem です。C ライブラリと同様、**PT-P900W**(既定 USB PID)、**PT-P750W** / **PT-P710BT** などは `open_usb_vid_pid` または CLI の `-p` で選びます。
|
||||||
|
|
||||||
## 前提
|
## 前提
|
||||||
|
|
||||||
@@ -31,15 +31,21 @@ export LIBPTOUCH_LIB=/usr/local/lib/libptouch.so
|
|||||||
|
|
||||||
(`cmake --install` で共有ライブラリをインストールした場合は、通常は `libptouch` 名でローダが解決します。)
|
(`cmake --install` で共有ライブラリをインストールした場合は、通常は `libptouch` 名でローダが解決します。)
|
||||||
|
|
||||||
## コマンド `ptouch-print-png`(PNG のみ)
|
## コマンド `ptouch-print-png`(PNG/SVG)
|
||||||
|
|
||||||
C の `ptouch-print` と同様の流れですが、**PNG 入力のみ**(`-w`/`-H` や 1bit ラスターは扱いません)。`gem install` 後は PATH に `ptouch-print-png` が入ります。
|
C の `ptouch-print` と同様の流れで、**PNG/SVG 入力**(`-w`/`-H` や 1bit ラスターは扱いません)を扱います。`gem install` 後は PATH に `ptouch-print-png` が入ります。
|
||||||
|
SVG は現在装着テープの印字可能幅に合わせて自動拡大・縮小します(USB 接続が必要)。
|
||||||
|
|
||||||
|
オプションは C 側に合わせ、**`-p` / `--pid`** で USB 製品 ID(16 進可)を指定できます。省略時は PT-P900W(`Libptouch::USB_PID_PTP900W` = `0x2085`)。例: PT-P750W `0x2062`、PT-P710BT `0x20af`(`libptouch.h` / `Libptouch` 定数と同じ)。
|
||||||
|
|
||||||
開発ツリーからそのまま試す例:
|
開発ツリーからそのまま試す例:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bundle exec ruby -I lib exe/ptouch-print-png --help
|
bundle exec ruby -I lib exe/ptouch-print-png --help
|
||||||
bundle exec ruby -I lib exe/ptouch-print-png -n -f ../samples/your.png
|
bundle exec ruby -I lib exe/ptouch-print-png -n -f ../samples/your.png
|
||||||
|
bundle exec ruby -I lib exe/ptouch-print-png -n -f ../samples/your.svg
|
||||||
|
bundle exec ruby -I lib exe/ptouch-print-png --status -p 0x2062
|
||||||
|
bundle exec ruby -I lib exe/ptouch-print-png -f ../samples/your.png -p 0x20af
|
||||||
```
|
```
|
||||||
|
|
||||||
## 使用例
|
## 使用例
|
||||||
@@ -49,8 +55,11 @@ require "libptouch"
|
|||||||
|
|
||||||
Libptouch::Context.new.tap do |ctx|
|
Libptouch::Context.new.tap do |ctx|
|
||||||
ctx.open_usb
|
ctx.open_usb
|
||||||
|
# PT-P750W など別 PID のとき:
|
||||||
|
# ctx.open_usb_vid_pid(Libptouch::USB_VID_BROTHER, Libptouch::USB_PID_PTP750W)
|
||||||
p ctx.status_bytes.bytesize # => 32
|
p ctx.status_bytes.bytesize # => 32
|
||||||
p ctx.status_hash[:tape_kind] # => {:code=>..., :label=>"ラミネートテープ"} など
|
p ctx.status_hash[:tape_kind] # => {:code=>..., :label=>"ラミネートテープ"} など
|
||||||
|
p ctx.status_hash[:model] # => {:code=>..., :name=>"PT-P750W"} など(対応機種のみ名前あり)
|
||||||
p ctx.status_hash[:status_kind] # => 状態(ステータス種類)
|
p ctx.status_hash[:status_kind] # => 状態(ステータス種類)
|
||||||
ensure
|
ensure
|
||||||
ctx.dispose
|
ctx.dispose
|
||||||
@@ -64,15 +73,28 @@ PNG からラスターへ:
|
|||||||
```ruby
|
```ruby
|
||||||
ctx = Libptouch::Context.new
|
ctx = Libptouch::Context.new
|
||||||
data, w, h = ctx.png_file_to_raster("/path/to/label.png")
|
data, w, h = ctx.png_file_to_raster("/path/to/label.png")
|
||||||
ctx.open_usb
|
ctx.open_usb_vid_pid(Libptouch::USB_VID_BROTHER, Libptouch::USB_PID_PTP750W)
|
||||||
ctx.print_raster(data, width_dots: w, height_dots: h, margin_mm: 0)
|
ctx.print_raster(data, width_dots: w, height_dots: h, margin_mm: 0)
|
||||||
ctx.dispose
|
ctx.dispose
|
||||||
```
|
```
|
||||||
|
|
||||||
|
SVG から現在テープ幅に合わせてラスターへ:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
ctx = Libptouch::Context.new
|
||||||
|
ctx.open_usb
|
||||||
|
data, w, h = ctx.svg_file_to_raster_fit_current_tape("/path/to/label.svg")
|
||||||
|
ctx.print_raster(data, width_dots: w, height_dots: h, margin_mm: 0)
|
||||||
|
ctx.dispose
|
||||||
|
```
|
||||||
|
|
||||||
|
**解像度:** P750W / P710BT はラスター幅方向が **180 dpi**、P900W 系は **360 dpi**(C の `libptouch_print` / 各機種ラスター PDF に合わせて画像を用意してください)。
|
||||||
|
|
||||||
## API の範囲
|
## API の範囲
|
||||||
|
|
||||||
- 実行ファイル `ptouch-print-png` … PNG のみ(`-f`, `-t`, `-n`, `-S`, `-V`, `-h`)。ステータスは JSON(`status_bytes` を `parse_status` したもの、`raw_bytes` 除く)
|
- 実行ファイル `ptouch-print-png` … PNG/SVG(`-f`, `-t`, `-p`, `-n`, `-S`, `-V`, `-h`)。ステータスは JSON(`status_bytes` を `parse_status` したもの、`raw_bytes` 除く)
|
||||||
- `Libptouch::Context` … `open_usb` / `open_usb_vid_pid` / `close` / `dispose`
|
- `Libptouch::Context` … `open_usb` / `open_usb_vid_pid` / `close` / `dispose`
|
||||||
- `check_raster` / `print_raster` / `png_file_to_raster` / `status_bytes` / `status_hash`
|
- `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` / `min_feed_mm` などを含む
|
||||||
- `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` を使ってください。
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ module Libptouch
|
|||||||
|
|
||||||
USB_VID_BROTHER = 0x04f9
|
USB_VID_BROTHER = 0x04f9
|
||||||
USB_PID_PTP900W = 0x2085
|
USB_PID_PTP900W = 0x2085
|
||||||
|
USB_PID_PTP750W = 0x2062
|
||||||
|
USB_PID_PTP710BT = 0x20af
|
||||||
|
|
||||||
STATUS_LENGTH = 32
|
STATUS_LENGTH = 32
|
||||||
PNG_DEFAULT_THRESHOLD = 128
|
PNG_DEFAULT_THRESHOLD = 128
|
||||||
|
|||||||
@@ -28,10 +28,28 @@ module Libptouch
|
|||||||
:_pad, [:uint8, 3]
|
:_pad, [:uint8, 3]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class MediaInfo < FFI::Struct
|
||||||
|
layout :media_width_code, :uint8,
|
||||||
|
:media_kind_code, :uint8,
|
||||||
|
:_pad0, [:uint8, 6],
|
||||||
|
:print_dpi, :double,
|
||||||
|
:feed_dpi, :double,
|
||||||
|
:tape_width_mm, :double,
|
||||||
|
:printable_dots, :uint16,
|
||||||
|
:left_margin_dots, :uint16,
|
||||||
|
:right_margin_dots, :uint16,
|
||||||
|
:min_feed_dots, :uint16,
|
||||||
|
:min_feed_mm, :double
|
||||||
|
end
|
||||||
|
|
||||||
class PngOptions < FFI::Struct
|
class PngOptions < FFI::Struct
|
||||||
layout :threshold, :uint8
|
layout :threshold, :uint8
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class SvgOptions < FFI::Struct
|
||||||
|
layout :threshold, :uint8
|
||||||
|
end
|
||||||
|
|
||||||
attach_function :libptouch_create, [], :pointer
|
attach_function :libptouch_create, [], :pointer
|
||||||
attach_function :libptouch_destroy, [:pointer], :void
|
attach_function :libptouch_destroy, [:pointer], :void
|
||||||
attach_function :libptouch_strerror, [:pointer], :string
|
attach_function :libptouch_strerror, [:pointer], :string
|
||||||
@@ -40,9 +58,12 @@ module Libptouch
|
|||||||
attach_function :libptouch_open_usb_vid_pid, %i[pointer uint16 uint16], :int
|
attach_function :libptouch_open_usb_vid_pid, %i[pointer uint16 uint16], :int
|
||||||
attach_function :libptouch_close, [:pointer], :void
|
attach_function :libptouch_close, [:pointer], :void
|
||||||
attach_function :libptouch_check_raster, %i[pointer pointer size_t pointer], :int
|
attach_function :libptouch_check_raster, %i[pointer pointer size_t pointer], :int
|
||||||
|
attach_function :libptouch_get_current_media_info, %i[pointer pointer], :int
|
||||||
attach_function :libptouch_print_raster, %i[pointer pointer size_t pointer], :int
|
attach_function :libptouch_print_raster, %i[pointer pointer size_t pointer], :int
|
||||||
attach_function :libptouch_png_file_to_raster,
|
attach_function :libptouch_png_file_to_raster,
|
||||||
%i[pointer string pointer pointer pointer pointer], :int
|
%i[pointer string pointer pointer pointer pointer], :int
|
||||||
|
attach_function :libptouch_svg_file_to_raster_fit_current_tape,
|
||||||
|
%i[pointer string pointer pointer pointer pointer], :int
|
||||||
attach_function :libptouch_free_raster, [:pointer], :void
|
attach_function :libptouch_free_raster, [:pointer], :void
|
||||||
attach_function :libptouch_get_status, %i[pointer pointer], :int
|
attach_function :libptouch_get_status, %i[pointer pointer], :int
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ require "libptouch"
|
|||||||
|
|
||||||
module Libptouch
|
module Libptouch
|
||||||
module Cli
|
module Cli
|
||||||
# PNG のみを扱う ptouch-print 相当の CLI(1bit ラスター経路なし)。
|
# PNG/SVG を扱う ptouch-print 相当の CLI(1bit ラスター経路なし)。
|
||||||
module PngPrint
|
module PngPrint
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
@@ -20,15 +20,16 @@ module Libptouch
|
|||||||
|
|
||||||
if opts[:status]
|
if opts[:status]
|
||||||
warn_unused_file_options(opts)
|
warn_unused_file_options(opts)
|
||||||
return run_status
|
return run_status(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
return usage_error("-f is required (or use --status)") if opts[:file].to_s.empty?
|
return usage_error("-f is required (or use --status)") if opts[:file].to_s.empty?
|
||||||
|
|
||||||
path = opts[:file]
|
path = opts[:file]
|
||||||
return usage_error("not a PNG file: #{path}") unless png_file?(path)
|
kind = image_kind(path)
|
||||||
|
return usage_error("not a PNG/SVG file: #{path}") if kind.nil?
|
||||||
|
|
||||||
run_print(path, opts)
|
run_print(path, opts, kind)
|
||||||
end
|
end
|
||||||
|
|
||||||
def png_file?(path)
|
def png_file?(path)
|
||||||
@@ -43,43 +44,98 @@ module Libptouch
|
|||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def svg_file?(path)
|
||||||
|
path.downcase.end_with?(".svg")
|
||||||
|
end
|
||||||
|
|
||||||
|
def image_kind(path)
|
||||||
|
return :png if png_file?(path)
|
||||||
|
return :svg if svg_file?(path)
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
def parse(argv)
|
def parse(argv)
|
||||||
o = {
|
opts_hash = default_cli_opts
|
||||||
|
build_cli_parser(opts_hash).parse!(argv)
|
||||||
|
return nil unless threshold_option_ok?(opts_hash)
|
||||||
|
return nil unless usb_pid_option_ok?(opts_hash)
|
||||||
|
|
||||||
|
opts_hash.delete(:usb_pid_invalid)
|
||||||
|
opts_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_cli_opts
|
||||||
|
{
|
||||||
file: nil,
|
file: nil,
|
||||||
threshold: nil,
|
threshold: nil,
|
||||||
|
usb_pid: nil,
|
||||||
|
usb_pid_invalid: false,
|
||||||
dry_run: false,
|
dry_run: false,
|
||||||
status: false,
|
status: false,
|
||||||
version: false,
|
version: false,
|
||||||
help: false
|
help: false
|
||||||
}
|
}
|
||||||
parser = OptionParser.new do |p|
|
end
|
||||||
|
|
||||||
|
def pid_option_description
|
||||||
|
p900 = Libptouch::USB_PID_PTP900W
|
||||||
|
p750 = Libptouch::USB_PID_PTP750W
|
||||||
|
p710 = Libptouch::USB_PID_PTP710BT
|
||||||
|
"USB 製品 ID(16 進可)。既定 P900W 0x#{p900.to_s(16)}; " \
|
||||||
|
"P750W 0x#{p750.to_s(16)}; P710BT 0x#{p710.to_s(16)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def apply_usb_pid_option(opts_hash, pid_str)
|
||||||
|
opts_hash[:usb_pid] = Integer(pid_str, 0)
|
||||||
|
rescue ArgumentError
|
||||||
|
warn "invalid --pid: #{pid_str.inspect}"
|
||||||
|
opts_hash[:usb_pid_invalid] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def threshold_option_ok?(opts_hash)
|
||||||
|
return true if opts_hash[:threshold].nil? || (0..255).cover?(opts_hash[:threshold])
|
||||||
|
|
||||||
|
warn "-t must be 0..255"
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def usb_pid_option_ok?(opts_hash)
|
||||||
|
return false if opts_hash[:usb_pid_invalid]
|
||||||
|
return true if opts_hash[:usb_pid].nil?
|
||||||
|
return true if opts_hash[:usb_pid].between?(1, 0xFFFF)
|
||||||
|
|
||||||
|
warn "-p/--pid must be 1..0xFFFF"
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_cli_parser(opts_hash)
|
||||||
|
OptionParser.new do |p|
|
||||||
p.banner = usage_banner
|
p.banner = usage_banner
|
||||||
p.separator ""
|
p.separator ""
|
||||||
p.on("-f", "--file PATH", "入力 PNG ファイル") { |v| o[:file] = v }
|
p.on("-f", "--file PATH", "入力 PNG/SVG ファイル") { |v| opts_hash[:file] = v }
|
||||||
p.on("-t", "--threshold N", Integer,
|
p.on("-t", "--threshold N", Integer,
|
||||||
"二値化しきい値 0–255(既定 #{Libptouch::PNG_DEFAULT_THRESHOLD})") do |v|
|
"二値化しきい値 0–255(既定 #{Libptouch::PNG_DEFAULT_THRESHOLD}、PNG/SVG)") do |v|
|
||||||
o[:threshold] = v
|
opts_hash[:threshold] = v
|
||||||
end
|
end
|
||||||
p.on("-n", "--dry-run", "読み込みと検証のみ(USB なし)") { o[:dry_run] = true }
|
p.on("-p", "--pid PID", pid_option_description) do |v|
|
||||||
p.on("-S", "--status", "USB プリンタのステータスを JSON で表示して終了") { o[:status] = true }
|
apply_usb_pid_option(opts_hash, v)
|
||||||
p.on("-V", "--version", "バージョンを表示して終了") { o[:version] = true }
|
end
|
||||||
p.on("-h", "--help", "このヘルプ") { o[:help] = true }
|
p.on("-n", "--dry-run", "読み込みと検証のみ(USB なし)") { opts_hash[:dry_run] = true }
|
||||||
|
p.on("-S", "--status", "USB プリンタのステータスを JSON で表示して終了") do
|
||||||
|
opts_hash[:status] = true
|
||||||
|
end
|
||||||
|
p.on("-V", "--version", "バージョンを表示して終了") { opts_hash[:version] = true }
|
||||||
|
p.on("-h", "--help", "このヘルプ") { opts_hash[:help] = true }
|
||||||
end
|
end
|
||||||
parser.parse!(argv)
|
|
||||||
|
|
||||||
unless o[:threshold].nil? || (0..255).cover?(o[:threshold])
|
|
||||||
warn "-t must be 0..255"
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def usage_banner
|
def usage_banner
|
||||||
<<~BANNER
|
<<~BANNER
|
||||||
Usage: ptouch-print-png [options]
|
Usage: ptouch-print-png [options]
|
||||||
|
|
||||||
PNG のみ対応。幅・高さは画像から取得します(-w/-H はありません)。
|
PNG/SVG 対応。幅・高さは画像から取得します(-w/-H はありません)。
|
||||||
|
SVG は現在テープ幅に合わせて自動拡大・縮小します(USB 接続必須)。
|
||||||
--status のときは -f は不要です。
|
--status のときは -f は不要です。
|
||||||
BANNER
|
BANNER
|
||||||
end
|
end
|
||||||
@@ -103,34 +159,40 @@ module Libptouch
|
|||||||
end
|
end
|
||||||
|
|
||||||
def parser_help_text
|
def parser_help_text
|
||||||
o = {
|
opts_hash = default_cli_opts
|
||||||
file: nil,
|
|
||||||
threshold: nil,
|
|
||||||
dry_run: false,
|
|
||||||
status: false,
|
|
||||||
version: false,
|
|
||||||
help: false
|
|
||||||
}
|
|
||||||
p = OptionParser.new do |parser|
|
p = OptionParser.new do |parser|
|
||||||
parser.banner = "ptouch-print-png [options]"
|
parser.banner = "ptouch-print-png [options]"
|
||||||
parser.on("-f", "--file PATH", "入力 PNG ファイル") { |v| o[:file] = v }
|
parser.on("-f", "--file PATH", "入力 PNG/SVG ファイル") { |v| opts_hash[:file] = v }
|
||||||
parser.on("-t", "--threshold N", Integer,
|
parser.on("-t", "--threshold N", Integer,
|
||||||
"二値化しきい値 0–255(既定 #{Libptouch::PNG_DEFAULT_THRESHOLD})") do |v|
|
"二値化しきい値 0–255(既定 #{Libptouch::PNG_DEFAULT_THRESHOLD}、PNG/SVG)") do |v|
|
||||||
o[:threshold] = v
|
opts_hash[:threshold] = v
|
||||||
end
|
end
|
||||||
parser.on("-n", "--dry-run", "読み込みと検証のみ(USB なし)") { o[:dry_run] = true }
|
parser.on("-p", "--pid PID", pid_option_description) do |v|
|
||||||
parser.on("-S", "--status", "USB プリンタのステータスを JSON で表示して終了") { o[:status] = true }
|
apply_usb_pid_option(opts_hash, v)
|
||||||
parser.on("-V", "--version", "バージョンを表示して終了") { o[:version] = true }
|
end
|
||||||
parser.on("-h", "--help", "このヘルプ") { o[:help] = true }
|
parser.on("-n", "--dry-run", "読み込みと検証のみ(USB なし)") { opts_hash[:dry_run] = true }
|
||||||
|
parser.on("-S", "--status", "USB プリンタのステータスを JSON で表示して終了") do
|
||||||
|
opts_hash[:status] = true
|
||||||
|
end
|
||||||
|
parser.on("-V", "--version", "バージョンを表示して終了") { opts_hash[:version] = true }
|
||||||
|
parser.on("-h", "--help", "このヘルプ") { opts_hash[:help] = true }
|
||||||
end
|
end
|
||||||
p.help
|
p.help
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_status
|
def open_usb_for_opts(ctx, opts)
|
||||||
|
if opts[:usb_pid]
|
||||||
|
ctx.open_usb_vid_pid(Libptouch::USB_VID_BROTHER, opts[:usb_pid])
|
||||||
|
else
|
||||||
|
ctx.open_usb
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_status(opts)
|
||||||
ctx = nil
|
ctx = nil
|
||||||
begin
|
begin
|
||||||
ctx = Libptouch::Context.new
|
ctx = Libptouch::Context.new
|
||||||
ctx.open_usb
|
open_usb_for_opts(ctx, opts)
|
||||||
h = Libptouch.parse_status(ctx.status_bytes)
|
h = Libptouch.parse_status(ctx.status_bytes)
|
||||||
h.delete(:raw_bytes)
|
h.delete(:raw_bytes)
|
||||||
puts JSON.pretty_generate(h)
|
puts JSON.pretty_generate(h)
|
||||||
@@ -143,12 +205,19 @@ module Libptouch
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_print(path, opts)
|
def run_print(path, opts, kind)
|
||||||
ctx = nil
|
ctx = nil
|
||||||
begin
|
begin
|
||||||
ctx = Libptouch::Context.new
|
ctx = Libptouch::Context.new
|
||||||
threshold = opts[:threshold]
|
threshold = opts[:threshold]
|
||||||
data, width, height = if threshold.nil?
|
data, width, height = if kind == :svg
|
||||||
|
open_usb_for_opts(ctx, opts)
|
||||||
|
if threshold.nil?
|
||||||
|
ctx.svg_file_to_raster_fit_current_tape(path)
|
||||||
|
else
|
||||||
|
ctx.svg_file_to_raster_fit_current_tape(path, threshold: threshold)
|
||||||
|
end
|
||||||
|
elsif threshold.nil?
|
||||||
ctx.png_file_to_raster(path)
|
ctx.png_file_to_raster(path)
|
||||||
else
|
else
|
||||||
ctx.png_file_to_raster(path, threshold: threshold)
|
ctx.png_file_to_raster(path, threshold: threshold)
|
||||||
@@ -157,11 +226,11 @@ module Libptouch
|
|||||||
ctx.check_raster(data, width_dots: width, height_dots: height)
|
ctx.check_raster(data, width_dots: width, height_dots: height)
|
||||||
|
|
||||||
if opts[:dry_run]
|
if opts[:dry_run]
|
||||||
puts "dry-run OK: #{data.bytesize} bytes, #{width}x#{height} dots"
|
puts "dry-run OK: #{data.bytesize} bytes, src #{width}x#{height} dots (print lengthxwidth #{width}x#{height})"
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
ctx.open_usb
|
open_usb_for_opts(ctx, opts) if kind == :png
|
||||||
ctx.print_raster(data, width_dots: width, height_dots: height)
|
ctx.print_raster(data, width_dots: width, height_dots: height)
|
||||||
0
|
0
|
||||||
rescue Libptouch::Error => e
|
rescue Libptouch::Error => e
|
||||||
|
|||||||
@@ -87,7 +87,29 @@ module Libptouch
|
|||||||
raw = out_pp.read_pointer
|
raw = out_pp.read_pointer
|
||||||
raise Libptouch::Error.new(OK, "null raster from PNG") if raw.null?
|
raise Libptouch::Error.new(OK, "null raster from PNG") if raw.null?
|
||||||
|
|
||||||
len = out_len.read_size_t
|
len = out_len.get(:size_t, 0)
|
||||||
|
bytes = raw.read_bytes(len)
|
||||||
|
Binding.libptouch_free_raster(raw)
|
||||||
|
[bytes, out_params[:width_dots], out_params[:height_dots]]
|
||||||
|
end
|
||||||
|
|
||||||
|
def svg_file_to_raster_fit_current_tape(path, threshold: nil)
|
||||||
|
opt_ptr = nil
|
||||||
|
unless threshold.nil?
|
||||||
|
o = Binding::SvgOptions.new
|
||||||
|
o[:threshold] = threshold
|
||||||
|
opt_ptr = o.pointer
|
||||||
|
end
|
||||||
|
out_pp = FFI::MemoryPointer.new(:pointer)
|
||||||
|
out_len = FFI::MemoryPointer.new(:size_t)
|
||||||
|
out_params = Binding::RasterParams.new
|
||||||
|
raise_on_error(Binding.libptouch_svg_file_to_raster_fit_current_tape(
|
||||||
|
@native, path, opt_ptr, out_pp, out_len, out_params.pointer
|
||||||
|
))
|
||||||
|
raw = out_pp.read_pointer
|
||||||
|
raise Libptouch::Error.new(OK, "null raster from SVG") if raw.null?
|
||||||
|
|
||||||
|
len = out_len.get(:size_t, 0)
|
||||||
bytes = raw.read_bytes(len)
|
bytes = raw.read_bytes(len)
|
||||||
Binding.libptouch_free_raster(raw)
|
Binding.libptouch_free_raster(raw)
|
||||||
[bytes, out_params[:width_dots], out_params[:height_dots]]
|
[bytes, out_params[:width_dots], out_params[:height_dots]]
|
||||||
@@ -102,5 +124,22 @@ module Libptouch
|
|||||||
def status_hash
|
def status_hash
|
||||||
Libptouch.parse_status(status_bytes)
|
Libptouch.parse_status(status_bytes)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def current_media_info
|
||||||
|
info = Binding::MediaInfo.new
|
||||||
|
raise_on_error(Binding.libptouch_get_current_media_info(@native, info.pointer))
|
||||||
|
{
|
||||||
|
media_width_code: info[:media_width_code],
|
||||||
|
media_kind_code: info[:media_kind_code],
|
||||||
|
print_dpi: info[:print_dpi],
|
||||||
|
feed_dpi: info[:feed_dpi],
|
||||||
|
tape_width_mm: info[:tape_width_mm],
|
||||||
|
printable_dots: info[:printable_dots],
|
||||||
|
left_margin_dots: info[:left_margin_dots],
|
||||||
|
right_margin_dots: info[:right_margin_dots],
|
||||||
|
min_feed_dots: info[:min_feed_dots],
|
||||||
|
min_feed_mm: info[:min_feed_mm]
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -104,7 +104,9 @@ module Libptouch
|
|||||||
0x6F => "PT-P900W",
|
0x6F => "PT-P900W",
|
||||||
0x70 => "PT-P950NW",
|
0x70 => "PT-P950NW",
|
||||||
0x71 => "PT-P900",
|
0x71 => "PT-P900",
|
||||||
0x78 => "PT-P910BT"
|
0x78 => "PT-P910BT",
|
||||||
|
0x68 => "PT-P750W",
|
||||||
|
0x76 => "PT-P710BT"
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
MEDIA_WIDTH = {
|
MEDIA_WIDTH = {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Gem::Specification.new do |spec|
|
|||||||
spec.summary = "FFI bindings for libptouch (Brother P-touch USB raster printing)"
|
spec.summary = "FFI bindings for libptouch (Brother P-touch USB raster printing)"
|
||||||
spec.description = [
|
spec.description = [
|
||||||
"Ruby wrapper around the ptouch_label C library libptouch.",
|
"Ruby wrapper around the ptouch_label C library libptouch.",
|
||||||
|
"Supports PT-P900W, PT-P750W, PT-P710BT and related open_usb_vid_pid / --pid.",
|
||||||
"Requires libptouch shared library (libusb, libpng)."
|
"Requires libptouch shared library (libusb, libpng)."
|
||||||
].join(" ")
|
].join(" ")
|
||||||
spec.license = "MIT"
|
spec.license = "MIT"
|
||||||
|
|||||||
40
samples/sample.svg
Normal file
40
samples/sample.svg
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="84.467712mm"
|
||||||
|
height="27.798302mm"
|
||||||
|
viewBox="0 0 84.467714 27.798302"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<g
|
||||||
|
id="layer1"
|
||||||
|
transform="matrix(1.7339801,0,0,1.6120341,-33.843459,-17.498256)">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:3.79808px;font-family:'Noto Sans CJK JP';-inkscape-font-specification:'Noto Sans CJK JP, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:0.383995;stroke-dasharray:none"
|
||||||
|
x="20.38328"
|
||||||
|
y="14.980056"
|
||||||
|
id="text234"><tspan
|
||||||
|
id="tspan232"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:3.79808px;font-family:'Noto Sans CJK JP';-inkscape-font-specification:'Noto Sans CJK JP, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.383995;stroke-dasharray:none"
|
||||||
|
x="20.38328"
|
||||||
|
y="14.980056">印刷出来るかな?</tspan><tspan
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:3.79808px;font-family:'Noto Sans CJK JP';-inkscape-font-specification:'Noto Sans CJK JP, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.383995;stroke-dasharray:none"
|
||||||
|
x="20.38328"
|
||||||
|
y="18.948805"
|
||||||
|
id="tspan236">取り敢えず何か書いてみる</tspan><tspan
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:3.79808px;font-family:'Noto Sans CJK JP';-inkscape-font-specification:'Noto Sans CJK JP, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.383995;stroke-dasharray:none"
|
||||||
|
x="20.38328"
|
||||||
|
y="22.917555"
|
||||||
|
id="tspan238">4行くらい入りそう</tspan><tspan
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:3.79808px;font-family:'Noto Sans CJK JP';-inkscape-font-specification:'Noto Sans CJK JP, Medium';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.383995;stroke-dasharray:none"
|
||||||
|
x="20.38328"
|
||||||
|
y="26.886305"
|
||||||
|
id="tspan240">123456</tspan></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
102
src/cli/main.c
102
src/cli/main.c
@@ -21,13 +21,15 @@ static void usage(const char *argv0)
|
|||||||
" -f, --file PATH 入力ファイル\n"
|
" -f, --file PATH 入力ファイル\n"
|
||||||
" -w, --width DOTS 1bit ラスター時: 幅(ドット)\n"
|
" -w, --width DOTS 1bit ラスター時: 幅(ドット)\n"
|
||||||
" -H, --height DOTS 1bit ラスター時: 高さ(ドット)\n"
|
" -H, --height DOTS 1bit ラスター時: 高さ(ドット)\n"
|
||||||
" -t, --threshold N PNG 二値化しきい値 0–255(既定 %u、PNG のみ)\n"
|
" -t, --threshold N 二値化しきい値 0–255(既定 %u、PNG/SVG)\n"
|
||||||
" -n, --dry-run 読み込みと check_raster のみ(USB なし)\n"
|
" -n, --dry-run 読み込みと check_raster のみ(USB なし)\n"
|
||||||
|
" -p, --pid HEX USB 製品 ID(既定: P900W の 0x2085)。例: P750W 0x2062、P710BT 0x20af\n"
|
||||||
" -S, --status USB 接続プリンタのステータス(テープ種・幅・色等)を表示して終了\n"
|
" -S, --status USB 接続プリンタのステータス(テープ種・幅・色等)を表示して終了\n"
|
||||||
" -V, --version バージョンを表示して終了\n"
|
" -V, --version バージョンを表示して終了\n"
|
||||||
" -h, --help このヘルプ\n"
|
" -h, --help このヘルプ\n"
|
||||||
"\n"
|
"\n"
|
||||||
"PNG の場合は幅・高さはファイルから取得(-w/-H 不要)。\n"
|
"PNG は幅・高さを画像から取得(-w/-H 不要)。\n"
|
||||||
|
"SVG は現在テープの印字可能幅に合わせて自動拡大・縮小(USB 必須)。\n"
|
||||||
"1bit packed ラスター(行優先)の場合は -f -w -H が必須。\n"
|
"1bit packed ラスター(行優先)の場合は -f -w -H が必須。\n"
|
||||||
"--status のときは -f は不要(他オプションは無視されます)。\n",
|
"--status のときは -f は不要(他オプションは無視されます)。\n",
|
||||||
argv0, (unsigned)LIBPTOUCH_PNG_DEFAULT_THRESHOLD);
|
argv0, (unsigned)LIBPTOUCH_PNG_DEFAULT_THRESHOLD);
|
||||||
@@ -53,6 +55,14 @@ static int input_is_png(const char *path)
|
|||||||
return memcmp(sig, png_magic, sizeof(png_magic)) == 0;
|
return memcmp(sig, png_magic, sizeof(png_magic)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int input_is_svg(const char *path)
|
||||||
|
{
|
||||||
|
const char *dot = strrchr(path, '.');
|
||||||
|
if (dot && strcasecmp(dot, ".svg") == 0)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int read_file(const char *path, uint8_t **out, size_t *out_len)
|
static int read_file(const char *path, uint8_t **out, size_t *out_len)
|
||||||
{
|
{
|
||||||
FILE *fp = fopen(path, "rb");
|
FILE *fp = fopen(path, "rb");
|
||||||
@@ -95,6 +105,7 @@ int main(int argc, char **argv)
|
|||||||
const char *file = NULL;
|
const char *file = NULL;
|
||||||
unsigned width = 0, height = 0;
|
unsigned width = 0, height = 0;
|
||||||
unsigned threshold = LIBPTOUCH_PNG_DEFAULT_THRESHOLD;
|
unsigned threshold = LIBPTOUCH_PNG_DEFAULT_THRESHOLD;
|
||||||
|
unsigned usb_pid_arg = 0;
|
||||||
int dry_run = 0;
|
int dry_run = 0;
|
||||||
int has_threshold = 0;
|
int has_threshold = 0;
|
||||||
int want_status = 0;
|
int want_status = 0;
|
||||||
@@ -104,6 +115,7 @@ int main(int argc, char **argv)
|
|||||||
{ "file", required_argument, NULL, 'f' },
|
{ "file", required_argument, NULL, 'f' },
|
||||||
{ "threshold", required_argument, NULL, 't' },
|
{ "threshold", required_argument, NULL, 't' },
|
||||||
{ "dry-run", no_argument, NULL, 'n' },
|
{ "dry-run", no_argument, NULL, 'n' },
|
||||||
|
{ "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' },
|
||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
@@ -111,7 +123,7 @@ int main(int argc, char **argv)
|
|||||||
};
|
};
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt_long(argc, argv, "w:H:f:t:nhSV", longopts, NULL)) !=
|
while ((c = getopt_long(argc, argv, "w:H:f:t:p:nhSV", longopts, NULL)) !=
|
||||||
-1) {
|
-1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'w':
|
case 'w':
|
||||||
@@ -134,6 +146,13 @@ int main(int argc, char **argv)
|
|||||||
case 'n':
|
case 'n':
|
||||||
dry_run = 1;
|
dry_run = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'p':
|
||||||
|
usb_pid_arg = (unsigned)strtoul(optarg, NULL, 0);
|
||||||
|
if (usb_pid_arg == 0u || usb_pid_arg > 0xFFFFu) {
|
||||||
|
fprintf(stderr, "-p/--pid must be 1..0xFFFF\n");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
want_status = 1;
|
want_status = 1;
|
||||||
break;
|
break;
|
||||||
@@ -159,7 +178,12 @@ int main(int argc, char **argv)
|
|||||||
fprintf(stderr, "libptouch_create failed\n");
|
fprintf(stderr, "libptouch_create failed\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
libptouch_err_t se = libptouch_open_usb(sctx);
|
libptouch_err_t se =
|
||||||
|
usb_pid_arg != 0
|
||||||
|
? libptouch_open_usb_vid_pid(
|
||||||
|
sctx, LIBPTOUCH_USB_VID_BROTHER,
|
||||||
|
(uint16_t)usb_pid_arg)
|
||||||
|
: libptouch_open_usb(sctx);
|
||||||
if (se != LIBPTOUCH_OK) {
|
if (se != LIBPTOUCH_OK) {
|
||||||
fprintf(stderr, "open_usb: %s\n", libptouch_strerror(sctx));
|
fprintf(stderr, "open_usb: %s\n", libptouch_strerror(sctx));
|
||||||
libptouch_destroy(sctx);
|
libptouch_destroy(sctx);
|
||||||
@@ -186,20 +210,21 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int png = input_is_png(file);
|
int png = input_is_png(file);
|
||||||
if (png) {
|
int svg = input_is_svg(file);
|
||||||
|
if (png || svg) {
|
||||||
if (width != 0 || height != 0)
|
if (width != 0 || height != 0)
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"warning: -w/-H ignored for PNG (using image size)\n");
|
"warning: -w/-H ignored for image inputs\n");
|
||||||
} else {
|
} else {
|
||||||
if (width == 0 || height == 0) {
|
if (width == 0 || height == 0) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"1bit raster requires -w and -H (or use a PNG file)\n");
|
"1bit raster requires -w and -H (or use a PNG/SVG file)\n");
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if (has_threshold)
|
if (has_threshold)
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"warning: -t applies to PNG only (ignored)\n");
|
"warning: -t applies to PNG/SVG only (ignored)\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
libptouch_ctx *ctx = libptouch_create();
|
libptouch_ctx *ctx = libptouch_create();
|
||||||
@@ -212,6 +237,7 @@ int main(int argc, char **argv)
|
|||||||
size_t data_len = 0;
|
size_t data_len = 0;
|
||||||
libptouch_raster_params_t params = { 0, 0, 0 };
|
libptouch_raster_params_t params = { 0, 0, 0 };
|
||||||
libptouch_err_t e;
|
libptouch_err_t e;
|
||||||
|
int usb_opened = 0;
|
||||||
|
|
||||||
if (png) {
|
if (png) {
|
||||||
libptouch_png_options_t opt = { .threshold = (uint8_t)threshold };
|
libptouch_png_options_t opt = { .threshold = (uint8_t)threshold };
|
||||||
@@ -224,6 +250,28 @@ int main(int argc, char **argv)
|
|||||||
libptouch_destroy(ctx);
|
libptouch_destroy(ctx);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
} else if (svg) {
|
||||||
|
e = usb_pid_arg != 0
|
||||||
|
? libptouch_open_usb_vid_pid(ctx, LIBPTOUCH_USB_VID_BROTHER,
|
||||||
|
(uint16_t)usb_pid_arg)
|
||||||
|
: libptouch_open_usb(ctx);
|
||||||
|
if (e != LIBPTOUCH_OK) {
|
||||||
|
fprintf(stderr, "open_usb: %s\n", libptouch_strerror(ctx));
|
||||||
|
libptouch_destroy(ctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
usb_opened = 1;
|
||||||
|
libptouch_svg_options_t opt = { .threshold = (uint8_t)threshold };
|
||||||
|
e = libptouch_svg_file_to_raster_fit_current_tape(
|
||||||
|
ctx, file, has_threshold ? &opt : NULL, &data, &data_len,
|
||||||
|
¶ms);
|
||||||
|
if (e != LIBPTOUCH_OK) {
|
||||||
|
fprintf(stderr, "svg_file_to_raster_fit_current_tape: %s\n",
|
||||||
|
libptouch_strerror(ctx));
|
||||||
|
libptouch_close(ctx);
|
||||||
|
libptouch_destroy(ctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (read_file(file, &data, &data_len) != 0) {
|
if (read_file(file, &data, &data_len) != 0) {
|
||||||
libptouch_destroy(ctx);
|
libptouch_destroy(ctx);
|
||||||
@@ -239,7 +287,7 @@ int main(int argc, char **argv)
|
|||||||
fprintf(stderr, "check_raster: %s\n",
|
fprintf(stderr, "check_raster: %s\n",
|
||||||
libptouch_strerror(ctx));
|
libptouch_strerror(ctx));
|
||||||
libptouch_destroy(ctx);
|
libptouch_destroy(ctx);
|
||||||
if (png)
|
if (png || svg)
|
||||||
libptouch_free_raster(data);
|
libptouch_free_raster(data);
|
||||||
else
|
else
|
||||||
free(data);
|
free(data);
|
||||||
@@ -247,26 +295,36 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dry_run) {
|
if (dry_run) {
|
||||||
printf("dry-run OK: %zu bytes, %ux%u dots\n", data_len,
|
printf("dry-run OK: %zu bytes, src %ux%u dots (print lengthxwidth %ux%u)\n",
|
||||||
|
data_len,
|
||||||
|
(unsigned)params.width_dots,
|
||||||
|
(unsigned)params.height_dots,
|
||||||
(unsigned)params.width_dots,
|
(unsigned)params.width_dots,
|
||||||
(unsigned)params.height_dots);
|
(unsigned)params.height_dots);
|
||||||
|
if (usb_opened)
|
||||||
|
libptouch_close(ctx);
|
||||||
libptouch_destroy(ctx);
|
libptouch_destroy(ctx);
|
||||||
if (png)
|
if (png || svg)
|
||||||
libptouch_free_raster(data);
|
libptouch_free_raster(data);
|
||||||
else
|
else
|
||||||
free(data);
|
free(data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
e = libptouch_open_usb(ctx);
|
if (!usb_opened) {
|
||||||
if (e != LIBPTOUCH_OK) {
|
e = usb_pid_arg != 0
|
||||||
fprintf(stderr, "open_usb: %s\n", libptouch_strerror(ctx));
|
? libptouch_open_usb_vid_pid(ctx, LIBPTOUCH_USB_VID_BROTHER,
|
||||||
libptouch_destroy(ctx);
|
(uint16_t)usb_pid_arg)
|
||||||
if (png)
|
: libptouch_open_usb(ctx);
|
||||||
libptouch_free_raster(data);
|
if (e != LIBPTOUCH_OK) {
|
||||||
else
|
fprintf(stderr, "open_usb: %s\n", libptouch_strerror(ctx));
|
||||||
free(data);
|
libptouch_destroy(ctx);
|
||||||
return 1;
|
if (png || svg)
|
||||||
|
libptouch_free_raster(data);
|
||||||
|
else
|
||||||
|
free(data);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e = libptouch_print_raster(ctx, data, data_len, ¶ms);
|
e = libptouch_print_raster(ctx, data, data_len, ¶ms);
|
||||||
@@ -275,7 +333,7 @@ int main(int argc, char **argv)
|
|||||||
libptouch_strerror(ctx));
|
libptouch_strerror(ctx));
|
||||||
libptouch_close(ctx);
|
libptouch_close(ctx);
|
||||||
libptouch_destroy(ctx);
|
libptouch_destroy(ctx);
|
||||||
if (png)
|
if (png || svg)
|
||||||
libptouch_free_raster(data);
|
libptouch_free_raster(data);
|
||||||
else
|
else
|
||||||
free(data);
|
free(data);
|
||||||
@@ -284,7 +342,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
libptouch_close(ctx);
|
libptouch_close(ctx);
|
||||||
libptouch_destroy(ctx);
|
libptouch_destroy(ctx);
|
||||||
if (png)
|
if (png || svg)
|
||||||
libptouch_free_raster(data);
|
libptouch_free_raster(data);
|
||||||
else
|
else
|
||||||
free(data);
|
free(data);
|
||||||
|
|||||||
100
src/lib/libptouch_core.c
Normal file
100
src/lib/libptouch_core.c
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* libptouch — context, errors, raster validation
|
||||||
|
*
|
||||||
|
* Author: knb
|
||||||
|
* Email: knb@artif.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libptouch_internal.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
43
src/lib/libptouch_internal.h
Normal file
43
src/lib/libptouch_internal.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* libptouch — internal declarations (not installed)
|
||||||
|
*
|
||||||
|
* Author: knb
|
||||||
|
* Email: knb@artif.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBPTOUCH_INTERNAL_H
|
||||||
|
#define LIBPTOUCH_INTERNAL_H
|
||||||
|
|
||||||
|
#include "libptouch.h"
|
||||||
|
|
||||||
|
#include <libusb.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
uint16_t usb_pid; /* 0 if USB not open; used to pick 128- vs 560-dot raster */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ctx に最終エラー(コードとメッセージ)を記録する。公開 API のエラー返却前に使う。 */
|
||||||
|
void ptouch_set_error(libptouch_ctx *ctx, libptouch_err_t code, const char *msg);
|
||||||
|
/* libusb のエラー番号を人が読めるメッセージにして ptouch_set_error に渡す。 */
|
||||||
|
void ptouch_set_error_usb(libptouch_ctx *ctx, int libusb_err, const char *what);
|
||||||
|
|
||||||
|
/* bulk OUT 転送。成功時 0、失敗時 libusb の負のエラーコード。 */
|
||||||
|
int ptouch_bulk_out(libusb_device_handle *h, uint8_t ep, const uint8_t *data,
|
||||||
|
int len, unsigned int timeout_ms);
|
||||||
|
/* bulk IN で len バイトを揃えるまで読む。成功時 0、失敗時 libusb の負のエラーコード。 */
|
||||||
|
int ptouch_bulk_in_exact(libusb_device_handle *h, uint8_t ep, uint8_t *buf,
|
||||||
|
int len, unsigned int timeout_ms);
|
||||||
|
/* ctx の bulk OUT エンドポイントへ buf を送る。印刷ジョブのチャンク送信用。 */
|
||||||
|
libptouch_err_t ptouch_bulk_send_job(libptouch_ctx *ctx, const uint8_t *buf,
|
||||||
|
size_t len, const char *what);
|
||||||
|
|
||||||
|
#endif /* LIBPTOUCH_INTERNAL_H */
|
||||||
159
src/lib/libptouch_layout.c
Normal file
159
src/lib/libptouch_layout.c
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* libptouch — layout tables and profile resolution (Brother raster PDFs)
|
||||||
|
*
|
||||||
|
* Author: knb
|
||||||
|
* Email: knb@artif.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libptouch_layout.h"
|
||||||
|
|
||||||
|
/* --- cv_ptp900_jpn_raster_102.pdf: TZe / その他(HS 以外で共通レイアウトを使う帯) --- */
|
||||||
|
static const ptouch_layout_row_t layout_tze_560[] = {
|
||||||
|
{ 0x04, 248, 48, 264 },
|
||||||
|
{ 0x06, 240, 64, 256 },
|
||||||
|
{ 0x09, 219, 106, 235 },
|
||||||
|
{ 0x0C, 197, 150, 213 },
|
||||||
|
{ 0x12, 155, 234, 171 },
|
||||||
|
{ 0x18, 112, 320, 128 },
|
||||||
|
{ 0x24, 45, 454, 61 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const ptouch_layout_row_t layout_hs_560[] = {
|
||||||
|
{ 0x06, 244, 56, 260 },
|
||||||
|
{ 0x09, 224, 96, 240 },
|
||||||
|
{ 0x0C, 206, 132, 222 },
|
||||||
|
{ 0x12, 166, 212, 182 },
|
||||||
|
{ 0x18, 144, 256, 160 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* --- cv_ptp750w_710bt_jpn_raster_102.pdf --- */
|
||||||
|
static const ptouch_layout_row_t layout_tze_128[] = {
|
||||||
|
{ 0x04, 52, 24, 52 },
|
||||||
|
{ 0x06, 48, 32, 48 },
|
||||||
|
{ 0x09, 39, 50, 39 },
|
||||||
|
{ 0x0C, 29, 70, 29 },
|
||||||
|
{ 0x12, 8, 112, 8 },
|
||||||
|
{ 0x18, 0, 128, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const ptouch_layout_row_t layout_hs_128[] = {
|
||||||
|
{ 0x06, 50, 28, 50 },
|
||||||
|
{ 0x09, 40, 48, 40 },
|
||||||
|
{ 0x0C, 31, 66, 31 },
|
||||||
|
{ 0x12, 11, 106, 11 },
|
||||||
|
{ 0x18, 0, 128, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint16_t pids_128pin[] = {
|
||||||
|
LIBPTOUCH_USB_PID_PTP750W,
|
||||||
|
LIBPTOUCH_USB_PID_PTP710BT,
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t models_128pin[] = {
|
||||||
|
0x68, /* PT-P750W */
|
||||||
|
0x76, /* PT-P710BT */
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const ptouch_printer_profile_t ptouch_layout_profiles[] = {
|
||||||
|
{
|
||||||
|
.usb_pids = pids_128pin,
|
||||||
|
.model_codes = models_128pin,
|
||||||
|
.is_default = 0,
|
||||||
|
.head_width_dots = 128u,
|
||||||
|
.margin_feed_dpi = 180.0,
|
||||||
|
.margin_feed_max_dots = 900u,
|
||||||
|
.tze = layout_tze_128,
|
||||||
|
.tze_count = sizeof(layout_tze_128) / sizeof(layout_tze_128[0]),
|
||||||
|
.hs = layout_hs_128,
|
||||||
|
.hs_count = sizeof(layout_hs_128) / sizeof(layout_hs_128[0]),
|
||||||
|
.doc_ref = "cv_ptp750w_710bt_jpn_raster_102.pdf",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.usb_pids = NULL,
|
||||||
|
.model_codes = NULL,
|
||||||
|
.is_default = 1,
|
||||||
|
.head_width_dots = 560u,
|
||||||
|
.margin_feed_dpi = 360.0,
|
||||||
|
.margin_feed_max_dots = 1800u,
|
||||||
|
.tze = layout_tze_560,
|
||||||
|
.tze_count = sizeof(layout_tze_560) / sizeof(layout_tze_560[0]),
|
||||||
|
.hs = layout_hs_560,
|
||||||
|
.hs_count = sizeof(layout_hs_560) / sizeof(layout_hs_560[0]),
|
||||||
|
.doc_ref = "cv_ptp900_jpn_raster_102.pdf (560-dot family)",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int pid_in_list(const uint16_t *list, uint16_t pid)
|
||||||
|
{
|
||||||
|
if (!list)
|
||||||
|
return 0;
|
||||||
|
for (; *list; list++) {
|
||||||
|
if (*list == pid)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int model_in_list(const uint8_t *list, uint8_t code)
|
||||||
|
{
|
||||||
|
if (!list)
|
||||||
|
return 0;
|
||||||
|
for (; *list; list++) {
|
||||||
|
if (*list == code)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ptouch_printer_profile_t *ptouch_layout_resolve_profile(uint16_t usb_pid,
|
||||||
|
uint8_t status_model_byte)
|
||||||
|
{
|
||||||
|
const ptouch_printer_profile_t *fallback = NULL;
|
||||||
|
|
||||||
|
for (size_t i = 0;
|
||||||
|
i < sizeof(ptouch_layout_profiles) / sizeof(ptouch_layout_profiles[0]);
|
||||||
|
i++) {
|
||||||
|
const ptouch_printer_profile_t *p = &ptouch_layout_profiles[i];
|
||||||
|
if (p->is_default) {
|
||||||
|
fallback = p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pid_in_list(p->usb_pids, usb_pid) ||
|
||||||
|
model_in_list(p->model_codes, status_model_byte))
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
static libptouch_err_t layout_lookup_table(const ptouch_layout_row_t *table, size_t n,
|
||||||
|
uint8_t media_wbyte, uint16_t *left,
|
||||||
|
uint16_t *print_dots, uint16_t *right)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
if (table[i].media_wbyte != media_wbyte)
|
||||||
|
continue;
|
||||||
|
*left = table[i].left_dots;
|
||||||
|
*print_dots = table[i].print_dots;
|
||||||
|
*right = table[i].right_dots;
|
||||||
|
return LIBPTOUCH_OK;
|
||||||
|
}
|
||||||
|
return LIBPTOUCH_ERR_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
libptouch_err_t ptouch_layout_from_status(const ptouch_printer_profile_t *prof,
|
||||||
|
uint8_t media_kind, uint8_t media_wbyte,
|
||||||
|
uint16_t *left_dots, uint16_t *print_dots,
|
||||||
|
uint16_t *right_dots)
|
||||||
|
{
|
||||||
|
if (!prof || !left_dots || !print_dots || !right_dots)
|
||||||
|
return LIBPTOUCH_ERR_ARG;
|
||||||
|
|
||||||
|
int is_hs = (media_kind == 0x11u || media_kind == 0x17u);
|
||||||
|
const ptouch_layout_row_t *table = is_hs ? prof->hs : prof->tze;
|
||||||
|
size_t n = is_hs ? prof->hs_count : prof->tze_count;
|
||||||
|
|
||||||
|
return layout_lookup_table(table, n, media_wbyte, left_dots, print_dots,
|
||||||
|
right_dots);
|
||||||
|
}
|
||||||
50
src/lib/libptouch_layout.h
Normal file
50
src/lib/libptouch_layout.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* libptouch — tape width → printable dots (data tables per printer family)
|
||||||
|
*
|
||||||
|
* Author: knb
|
||||||
|
* Email: knb@artif.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBPTOUCH_LAYOUT_H
|
||||||
|
#define LIBPTOUCH_LAYOUT_H
|
||||||
|
|
||||||
|
#include "libptouch.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/** One row: status byte st[10] (media width code) when it matches this tape width slot */
|
||||||
|
typedef struct ptouch_layout_row {
|
||||||
|
uint8_t media_wbyte;
|
||||||
|
uint16_t left_dots;
|
||||||
|
uint16_t print_dots;
|
||||||
|
uint16_t right_dots;
|
||||||
|
} ptouch_layout_row_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Printer family: USB PID / status model byte select a profile; tables supply layouts.
|
||||||
|
* New models: add a row to @ref ptouch_layout_profiles or add TZe/HS rows to existing tables.
|
||||||
|
*/
|
||||||
|
typedef struct ptouch_printer_profile {
|
||||||
|
const uint16_t *usb_pids; /* 0-terminated; NULL = do not match on PID */
|
||||||
|
const uint8_t *model_codes; /* 0-terminated; NULL = do not match on status[4] */
|
||||||
|
int is_default; /* 1 = fallback when no other profile matches */
|
||||||
|
unsigned head_width_dots; /* 128 or 560; full raster width for pack/GF payload */
|
||||||
|
double margin_feed_dpi; /* ESC i d feed dots per inch (PDF) */
|
||||||
|
unsigned margin_feed_max_dots;
|
||||||
|
const ptouch_layout_row_t *tze;
|
||||||
|
size_t tze_count;
|
||||||
|
const ptouch_layout_row_t *hs;
|
||||||
|
size_t hs_count;
|
||||||
|
const char *doc_ref;
|
||||||
|
} ptouch_printer_profile_t;
|
||||||
|
|
||||||
|
const ptouch_printer_profile_t *ptouch_layout_resolve_profile(uint16_t usb_pid,
|
||||||
|
uint8_t status_model_byte);
|
||||||
|
|
||||||
|
libptouch_err_t ptouch_layout_from_status(const ptouch_printer_profile_t *prof,
|
||||||
|
uint8_t media_kind, uint8_t media_wbyte,
|
||||||
|
uint16_t *left_dots, uint16_t *print_dots,
|
||||||
|
uint16_t *right_dots);
|
||||||
|
|
||||||
|
#endif /* LIBPTOUCH_LAYOUT_H */
|
||||||
100
src/lib/libptouch_media_info.c
Normal file
100
src/lib/libptouch_media_info.c
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* libptouch — current tape/media information helper
|
||||||
|
*
|
||||||
|
* Author: knb
|
||||||
|
* Email: knb@artif.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libptouch_internal.h"
|
||||||
|
#include "libptouch_layout.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static double tape_width_mm_from_code(uint8_t media_w)
|
||||||
|
{
|
||||||
|
switch (media_w) {
|
||||||
|
case 0x04:
|
||||||
|
return 3.5;
|
||||||
|
case 0x06:
|
||||||
|
return 6.0;
|
||||||
|
case 0x09:
|
||||||
|
return 9.0;
|
||||||
|
case 0x0C:
|
||||||
|
return 12.0;
|
||||||
|
case 0x12:
|
||||||
|
return 18.0;
|
||||||
|
case 0x18:
|
||||||
|
return 24.0;
|
||||||
|
case 0x24:
|
||||||
|
return 36.0;
|
||||||
|
case 0x15:
|
||||||
|
return 21.0; /* FLe width code */
|
||||||
|
default:
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static double print_dpi_from_profile(const ptouch_printer_profile_t *prof)
|
||||||
|
{
|
||||||
|
if (!prof)
|
||||||
|
return 0.0;
|
||||||
|
if (prof->head_width_dots <= 128u)
|
||||||
|
return 180.0;
|
||||||
|
return 360.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
libptouch_err_t libptouch_get_current_media_info(libptouch_ctx *ctx,
|
||||||
|
libptouch_media_info_t *out_info)
|
||||||
|
{
|
||||||
|
if (!ctx || !out_info) {
|
||||||
|
if (ctx)
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, "null argument");
|
||||||
|
return LIBPTOUCH_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(out_info, 0, sizeof(*out_info));
|
||||||
|
|
||||||
|
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
|
||||||
|
libptouch_err_t v = libptouch_get_status(ctx, st);
|
||||||
|
if (v != LIBPTOUCH_OK)
|
||||||
|
return v;
|
||||||
|
|
||||||
|
const ptouch_printer_profile_t *prof =
|
||||||
|
ptouch_layout_resolve_profile(ctx->usb_pid, st[4]);
|
||||||
|
if (!prof) {
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_UNSUPPORTED,
|
||||||
|
"no layout profile for this printer");
|
||||||
|
return LIBPTOUCH_ERR_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t left_dots = 0, print_dots = 0, right_dots = 0;
|
||||||
|
v = ptouch_layout_from_status(prof, st[11], st[10], &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");
|
||||||
|
return LIBPTOUCH_ERR_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
double tape_mm = tape_width_mm_from_code(st[10]);
|
||||||
|
if (tape_mm <= 0.0) {
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_UNSUPPORTED,
|
||||||
|
"unknown tape width code");
|
||||||
|
return LIBPTOUCH_ERR_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_info->media_width_code = st[10];
|
||||||
|
out_info->media_kind_code = st[11];
|
||||||
|
out_info->print_dpi = print_dpi_from_profile(prof);
|
||||||
|
out_info->feed_dpi = prof->margin_feed_dpi;
|
||||||
|
out_info->tape_width_mm = tape_mm;
|
||||||
|
out_info->printable_dots = print_dots;
|
||||||
|
out_info->left_margin_dots = left_dots;
|
||||||
|
out_info->right_margin_dots = right_dots;
|
||||||
|
out_info->min_feed_dots = 14u;
|
||||||
|
out_info->min_feed_mm =
|
||||||
|
((double)out_info->min_feed_dots * 25.4) / prof->margin_feed_dpi;
|
||||||
|
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_OK, "");
|
||||||
|
return LIBPTOUCH_OK;
|
||||||
|
}
|
||||||
164
src/lib/libptouch_png.c
Normal file
164
src/lib/libptouch_png.c
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* libptouch — PNG → 1bit packed raster (libpng)
|
||||||
|
*
|
||||||
|
* Author: knb
|
||||||
|
* Email: knb@artif.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libptouch_internal.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <png.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
242
src/lib/libptouch_print.c
Normal file
242
src/lib/libptouch_print.c
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
* libptouch — raster layout, transpose, P-touch print job (ESC/P bulk)
|
||||||
|
*
|
||||||
|
* Author: knb
|
||||||
|
* Email: knb@artif.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libptouch_internal.h"
|
||||||
|
#include "libptouch_layout.h"
|
||||||
|
|
||||||
|
#include <libusb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void pack_line(uint8_t *line, size_t line_bytes, unsigned head_dots,
|
||||||
|
const uint8_t *row, uint32_t width_dots, uint16_t left_dots,
|
||||||
|
uint16_t print_dots)
|
||||||
|
{
|
||||||
|
memset(line, 0, line_bytes);
|
||||||
|
if (width_dots > (uint32_t)print_dots)
|
||||||
|
return;
|
||||||
|
uint32_t start = (uint32_t)left_dots +
|
||||||
|
((uint32_t)print_dots - width_dots) / 2u;
|
||||||
|
for (uint32_t x = 0; x < width_dots; x++) {
|
||||||
|
uint32_t ubyte = x / 8u;
|
||||||
|
uint32_t ubit = 7u - (x % 8u);
|
||||||
|
if (((row[ubyte] >> ubit) & 1u) == 0)
|
||||||
|
continue;
|
||||||
|
uint32_t dot = start + x;
|
||||||
|
if (dot >= (uint32_t)head_dots)
|
||||||
|
break;
|
||||||
|
uint32_t b = dot / 8u;
|
||||||
|
uint32_t bi = 7u - (dot % 8u);
|
||||||
|
line[b] |= (uint8_t)(1u << bi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *transpose_raster_alloc(const uint8_t *in, uint32_t W, uint32_t H,
|
||||||
|
uint32_t *outW, uint32_t *outH)
|
||||||
|
{
|
||||||
|
*outW = H;
|
||||||
|
*outH = W;
|
||||||
|
size_t old_rb = (W + 7u) / 8u;
|
||||||
|
size_t new_rb = (H + 7u) / 8u;
|
||||||
|
uint8_t *out = (uint8_t *)calloc(new_rb * W, 1);
|
||||||
|
if (!out)
|
||||||
|
return NULL;
|
||||||
|
for (uint32_t y = 0; y < H; y++) {
|
||||||
|
for (uint32_t x = 0; x < W; x++) {
|
||||||
|
size_t ob = (size_t)y * old_rb + x / 8u;
|
||||||
|
int bit = (int)((in[ob] >> (7u - x % 8u)) & 1u);
|
||||||
|
if (!bit)
|
||||||
|
continue;
|
||||||
|
uint32_t nx = y;
|
||||||
|
uint32_t ny = x;
|
||||||
|
size_t nb = (size_t)ny * new_rb + nx / 8u;
|
||||||
|
out[nb] |= (uint8_t)(1u << (7u - nx % 8u));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
libptouch_err_t libptouch_print_raster(libptouch_ctx *ctx,
|
||||||
|
const uint8_t *data, size_t data_len,
|
||||||
|
const libptouch_raster_params_t *params)
|
||||||
|
{
|
||||||
|
libptouch_err_t v =
|
||||||
|
libptouch_check_raster(ctx, data, data_len, params);
|
||||||
|
if (v != LIBPTOUCH_OK)
|
||||||
|
return v;
|
||||||
|
|
||||||
|
if (!ctx->usb_open || !ctx->bulk_out_ep) {
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_IO, "not connected");
|
||||||
|
return LIBPTOUCH_ERR_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
|
||||||
|
v = libptouch_get_status(ctx, st);
|
||||||
|
if (v != LIBPTOUCH_OK)
|
||||||
|
return v;
|
||||||
|
|
||||||
|
const ptouch_printer_profile_t *prof =
|
||||||
|
ptouch_layout_resolve_profile(ctx->usb_pid, st[4]);
|
||||||
|
if (!prof) {
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_UNSUPPORTED,
|
||||||
|
"no layout profile for this printer");
|
||||||
|
return LIBPTOUCH_ERR_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t media_kind = st[11];
|
||||||
|
uint8_t media_w = st[10];
|
||||||
|
uint16_t left_dots, print_dots, right_dots;
|
||||||
|
v = ptouch_layout_from_status(prof, media_kind, media_w, &left_dots,
|
||||||
|
&print_dots, &right_dots);
|
||||||
|
if (v != LIBPTOUCH_OK) {
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_UNSUPPORTED,
|
||||||
|
"tape width/layout not supported for this media "
|
||||||
|
"(check status media bytes)");
|
||||||
|
return LIBPTOUCH_ERR_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
(void)right_dots;
|
||||||
|
|
||||||
|
unsigned head_dots = prof->head_width_dots;
|
||||||
|
size_t line_payload = (size_t)((head_dots + 7u) / 8u);
|
||||||
|
size_t gf_packet = 3u + line_payload;
|
||||||
|
|
||||||
|
uint32_t wd = params->width_dots;
|
||||||
|
uint32_t ht = params->height_dots;
|
||||||
|
uint8_t *transposed = transpose_raster_alloc(data, wd, ht, &wd, &ht);
|
||||||
|
if (!transposed) {
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM,
|
||||||
|
"transpose raster: out of memory");
|
||||||
|
return LIBPTOUCH_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
const uint8_t *src = transposed;
|
||||||
|
|
||||||
|
if (wd > (uint32_t)print_dots) {
|
||||||
|
char buf[160];
|
||||||
|
snprintf(buf, sizeof(buf),
|
||||||
|
"image width %u dots > printable %u for loaded tape",
|
||||||
|
(unsigned)wd, (unsigned)print_dots);
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, buf);
|
||||||
|
free(transposed);
|
||||||
|
return LIBPTOUCH_ERR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
double margin_dpi = prof->margin_feed_dpi;
|
||||||
|
unsigned margin_max = prof->margin_feed_max_dots;
|
||||||
|
unsigned margin_dots = 14u;
|
||||||
|
if (params->margin_mm > 0) {
|
||||||
|
margin_dots = (unsigned)((double)params->margin_mm * margin_dpi /
|
||||||
|
25.4 +
|
||||||
|
0.5);
|
||||||
|
if (margin_dots < 14u)
|
||||||
|
margin_dots = 14u;
|
||||||
|
if (margin_dots > margin_max)
|
||||||
|
margin_dots = margin_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t lines = ht;
|
||||||
|
uint8_t 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;
|
||||||
|
static const uint8_t g_hdr[] = { 0x47, 0x46, 0x00 };
|
||||||
|
|
||||||
|
uint8_t *gbuf = (uint8_t *)malloc(gf_packet);
|
||||||
|
if (!gbuf) {
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM,
|
||||||
|
"raster line buffer");
|
||||||
|
free(transposed);
|
||||||
|
return LIBPTOUCH_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t y = 0; y < lines; y++) {
|
||||||
|
const uint8_t *row = src + (size_t)y * row_b;
|
||||||
|
pack_line(gbuf + 3, line_payload, head_dots, row, wd, left_dots,
|
||||||
|
print_dots);
|
||||||
|
memcpy(gbuf, g_hdr, sizeof(g_hdr));
|
||||||
|
v = ptouch_bulk_send_job(ctx, gbuf, gf_packet, "raster line");
|
||||||
|
if (v != LIBPTOUCH_OK) {
|
||||||
|
free(gbuf);
|
||||||
|
free(transposed);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(gbuf);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
438
src/lib/libptouch_status.c
Normal file
438
src/lib/libptouch_status.c
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
/*
|
||||||
|
* libptouch — printer status (ESC i S) and human-readable dump
|
||||||
|
*
|
||||||
|
* Author: knb
|
||||||
|
* Email: knb@artif.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libptouch_internal.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
case 0x68:
|
||||||
|
name = "PT-P750W";
|
||||||
|
break;
|
||||||
|
case 0x76:
|
||||||
|
name = "PT-P710BT";
|
||||||
|
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" : " ");
|
||||||
|
}
|
||||||
211
src/lib/libptouch_svg.c
Normal file
211
src/lib/libptouch_svg.c
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* libptouch — SVG -> 1bit packed raster (librsvg + cairo)
|
||||||
|
*
|
||||||
|
* Author: knb
|
||||||
|
* Email: knb@artif.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libptouch_internal.h"
|
||||||
|
#include "libptouch_layout.h"
|
||||||
|
|
||||||
|
#ifdef LIBPTOUCH_HAS_RSVG
|
||||||
|
#include <cairo.h>
|
||||||
|
#include <librsvg/rsvg.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
libptouch_err_t libptouch_svg_file_to_raster_fit_current_tape(
|
||||||
|
libptouch_ctx *ctx, const char *path,
|
||||||
|
const libptouch_svg_options_t *options, uint8_t **out_raster,
|
||||||
|
size_t *out_raster_bytes, libptouch_raster_params_t *out_params)
|
||||||
|
{
|
||||||
|
#ifndef LIBPTOUCH_HAS_RSVG
|
||||||
|
(void)path;
|
||||||
|
(void)options;
|
||||||
|
(void)out_raster;
|
||||||
|
(void)out_raster_bytes;
|
||||||
|
(void)out_params;
|
||||||
|
if (ctx) {
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_UNSUPPORTED,
|
||||||
|
"SVG support is not built in (missing librsvg-2.0)");
|
||||||
|
}
|
||||||
|
return LIBPTOUCH_ERR_UNSUPPORTED;
|
||||||
|
#else
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (!ctx->usb_open || !ctx->bulk_out_ep) {
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_IO,
|
||||||
|
"not connected (open USB before SVG conversion)");
|
||||||
|
return LIBPTOUCH_ERR_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
|
||||||
|
libptouch_err_t v = libptouch_get_status(ctx, st);
|
||||||
|
if (v != LIBPTOUCH_OK)
|
||||||
|
return v;
|
||||||
|
|
||||||
|
const ptouch_printer_profile_t *prof =
|
||||||
|
ptouch_layout_resolve_profile(ctx->usb_pid, st[4]);
|
||||||
|
if (!prof) {
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_UNSUPPORTED,
|
||||||
|
"no layout profile for this printer");
|
||||||
|
return LIBPTOUCH_ERR_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t left_dots = 0, print_dots = 0, right_dots = 0;
|
||||||
|
v = ptouch_layout_from_status(prof, st[11], st[10], &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");
|
||||||
|
return LIBPTOUCH_ERR_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
(void)left_dots;
|
||||||
|
(void)right_dots;
|
||||||
|
|
||||||
|
GError *gerr = NULL;
|
||||||
|
RsvgHandle *handle = rsvg_handle_new_from_file(path, &gerr);
|
||||||
|
if (!handle) {
|
||||||
|
const char *msg = (gerr && gerr->message) ? gerr->message :
|
||||||
|
"failed to load SVG";
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, msg);
|
||||||
|
if (gerr)
|
||||||
|
g_error_free(gerr);
|
||||||
|
return LIBPTOUCH_ERR_IMAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
double svg_w = 0.0;
|
||||||
|
double svg_h = 0.0;
|
||||||
|
if (!rsvg_handle_get_intrinsic_size_in_pixels(handle, &svg_w, &svg_h) ||
|
||||||
|
svg_w <= 0.0 || svg_h <= 0.0) {
|
||||||
|
g_object_unref(handle);
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE,
|
||||||
|
"invalid SVG dimensions");
|
||||||
|
return LIBPTOUCH_ERR_IMAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* libptouch_print_raster() transposes the incoming raster before packing
|
||||||
|
* lines, so printable tape width maps to the source raster height.
|
||||||
|
*/
|
||||||
|
double scale = (double)print_dots / svg_h;
|
||||||
|
if (scale <= 0.0 || !isfinite(scale)) {
|
||||||
|
g_object_unref(handle);
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "invalid SVG scale");
|
||||||
|
return LIBPTOUCH_ERR_IMAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t out_h = (uint32_t)print_dots;
|
||||||
|
uint32_t out_w = (uint32_t)ceil(svg_w * scale);
|
||||||
|
if (out_h == 0)
|
||||||
|
out_h = 1;
|
||||||
|
if (out_h > 100000u) {
|
||||||
|
g_object_unref(handle);
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "scaled SVG too tall");
|
||||||
|
return LIBPTOUCH_ERR_IMAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_surface_t *surface = cairo_image_surface_create(
|
||||||
|
CAIRO_FORMAT_ARGB32, (int)out_w, (int)out_h);
|
||||||
|
if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
g_object_unref(handle);
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM,
|
||||||
|
"cairo_image_surface_create failed");
|
||||||
|
return LIBPTOUCH_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_t *cr = cairo_create(surface);
|
||||||
|
if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
|
||||||
|
cairo_destroy(cr);
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
g_object_unref(handle);
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM, "cairo_create failed");
|
||||||
|
return LIBPTOUCH_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
|
||||||
|
cairo_paint(cr);
|
||||||
|
RsvgRectangle viewport = { 0.0, 0.0, (double)out_w, (double)out_h };
|
||||||
|
GError *render_err = NULL;
|
||||||
|
if (!rsvg_handle_render_document(handle, cr, &viewport, &render_err)) {
|
||||||
|
cairo_destroy(cr);
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
g_object_unref(handle);
|
||||||
|
const char *msg = (render_err && render_err->message) ?
|
||||||
|
render_err->message :
|
||||||
|
"SVG render failed";
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, msg);
|
||||||
|
if (render_err)
|
||||||
|
g_error_free(render_err);
|
||||||
|
return LIBPTOUCH_ERR_IMAGE;
|
||||||
|
}
|
||||||
|
cairo_destroy(cr);
|
||||||
|
g_object_unref(handle);
|
||||||
|
|
||||||
|
cairo_surface_flush(surface);
|
||||||
|
int stride = cairo_image_surface_get_stride(surface);
|
||||||
|
const uint8_t *pix = cairo_image_surface_get_data(surface);
|
||||||
|
|
||||||
|
size_t row_bytes = ((size_t)out_w + 7u) / 8u;
|
||||||
|
if (out_h > SIZE_MAX / row_bytes) {
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_IMAGE, "raster size overflow");
|
||||||
|
return LIBPTOUCH_ERR_IMAGE;
|
||||||
|
}
|
||||||
|
size_t total = row_bytes * (size_t)out_h;
|
||||||
|
uint8_t *out = (uint8_t *)calloc(1, total);
|
||||||
|
if (!out) {
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM, "calloc raster failed");
|
||||||
|
return LIBPTOUCH_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t y = 0; y < out_h; y++) {
|
||||||
|
const uint32_t *src_row =
|
||||||
|
(const uint32_t *)(pix + (size_t)y * (size_t)stride);
|
||||||
|
uint8_t *dst_row = out + (size_t)y * row_bytes;
|
||||||
|
for (uint32_t x = 0; x < out_w; x++) {
|
||||||
|
uint32_t p = src_row[x];
|
||||||
|
unsigned a = (p >> 24) & 0xFFu;
|
||||||
|
if (a < 128u)
|
||||||
|
continue;
|
||||||
|
unsigned r = (p >> 16) & 0xFFu;
|
||||||
|
unsigned g = (p >> 8) & 0xFFu;
|
||||||
|
unsigned b = p & 0xFFu;
|
||||||
|
unsigned yv = (77u * r + 150u * g + 29u * b) >> 8;
|
||||||
|
if (yv >= (unsigned)thr)
|
||||||
|
continue;
|
||||||
|
size_t byte = (size_t)x / 8u;
|
||||||
|
size_t bit = (size_t)x % 8u;
|
||||||
|
dst_row[byte] |= (uint8_t)(1u << (7u - bit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
|
||||||
|
out_params->width_dots = out_w;
|
||||||
|
out_params->height_dots = out_h;
|
||||||
|
out_params->margin_mm = 0;
|
||||||
|
*out_raster = out;
|
||||||
|
*out_raster_bytes = total;
|
||||||
|
ptouch_set_error(ctx, LIBPTOUCH_OK, "");
|
||||||
|
return LIBPTOUCH_OK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
198
src/lib/libptouch_usb.c
Normal file
198
src/lib/libptouch_usb.c
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* libptouch — USB (libusb): open/close, bulk transfers
|
||||||
|
*
|
||||||
|
* Author: knb
|
||||||
|
* Email: knb@artif.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libptouch_internal.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* バルク 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_pid = pid;
|
||||||
|
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_pid = 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;
|
||||||
|
}
|
||||||
1164
src/libptouch.c
1164
src/libptouch.c
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
|||||||
# Brother P-touch — libusb でユーザから開けるようにする (Ubuntu 等)
|
# Brother P-touch — libusb でユーザから開けるようにする (Ubuntu 等)
|
||||||
# PT-P900W: lsusb で ID 04f9:2085
|
# PT-P900W: lsusb で ID 04f9:2085
|
||||||
|
# PT-P750W: 04f9:2062 PT-P710BT: 04f9:20af(Brother ラスター PDF Appendix A)
|
||||||
#
|
#
|
||||||
# 設置:
|
# 設置:
|
||||||
# sudo cp udev/99-ptouch-label-brother.rules /etc/udev/rules.d/
|
# sudo cp udev/99-ptouch-label-brother.rules /etc/udev/rules.d/
|
||||||
@@ -11,6 +12,8 @@
|
|||||||
# GROUP=plugdev … 従来どおり plugdev に所属していれば読み書き可
|
# GROUP=plugdev … 従来どおり plugdev に所属していれば読み書き可
|
||||||
|
|
||||||
SUBSYSTEM=="usb", ATTR{idVendor}=="04f9", ATTR{idProduct}=="2085", MODE="0660", GROUP="plugdev", TAG+="uaccess"
|
SUBSYSTEM=="usb", ATTR{idVendor}=="04f9", ATTR{idProduct}=="2085", MODE="0660", GROUP="plugdev", TAG+="uaccess"
|
||||||
|
SUBSYSTEM=="usb", ATTR{idVendor}=="04f9", ATTR{idProduct}=="2062", MODE="0660", GROUP="plugdev", TAG+="uaccess"
|
||||||
|
SUBSYSTEM=="usb", ATTR{idVendor}=="04f9", ATTR{idProduct}=="20af", MODE="0660", GROUP="plugdev", TAG+="uaccess"
|
||||||
|
|
||||||
# 他機種を使う場合は lsusb の ID を足す例(コメントを外して複製):
|
# 他機種を使う場合は lsusb の ID を足す例(コメントを外して複製):
|
||||||
# SUBSYSTEM=="usb", ATTR{idVendor}=="04f9", ATTR{idProduct}=="2086", MODE="0660", GROUP="plugdev", TAG+="uaccess"
|
# SUBSYSTEM=="usb", ATTR{idVendor}=="04f9", ATTR{idProduct}=="2086", MODE="0660", GROUP="plugdev", TAG+="uaccess"
|
||||||
|
|||||||
Reference in New Issue
Block a user