ptouch-label/ptouch-print のオプション整理と trim-right 対応を反映

CLIヘルプ文言を簡潔化しつつ、右余白トリム機能と関連API・ドキュメント更新をまとめて取り込み、PNG/SVG/テンプレート経路での利用体験を揃える。

Made-with: Cursor
This commit is contained in:
knb
2026-04-16 19:09:37 +09:00
parent 32ab12f661
commit 094f183994
13 changed files with 272 additions and 24 deletions

View File

@@ -21,15 +21,16 @@ static void usage(const char *argv0)
" -f, --file PATH 入力ファイル\n"
" -w, --width DOTS 1bit ラスター時: 幅(ドット)\n"
" -H, --height DOTS 1bit ラスター時: 高さ(ドット)\n"
" -t, --threshold N 二値化しきい値 0255既定 %u、PNG/SVG\n"
" -t, --threshold N しきい値 0255既定 %u、PNG/SVG\n"
" --trim-right[=DOTS] 右側空白を削減(省略時は左余白、失敗時 0\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 ステータスを表示して終了\n"
" -V, --version バージョンを表示して終了\n"
" -h, --help このヘルプ\n"
"\n"
"PNG は幅・高さを画像から取得-w/-H 不要)。\n"
"SVG は現在テープの印字可能幅に合わせて自動拡大・縮小USB 必須)。\n"
"PNG は画像サイズを使用-w/-H 不要)。\n"
"SVG は現在テープ幅に自動フィットUSB 必須)。\n"
"1bit packed ラスター(行優先)の場合は -f -w -H が必須。\n"
"--status のときは -f は不要(他オプションは無視されます)。\n",
argv0, (unsigned)LIBPTOUCH_PNG_DEFAULT_THRESHOLD);
@@ -107,6 +108,9 @@ int main(int argc, char **argv)
unsigned threshold = LIBPTOUCH_PNG_DEFAULT_THRESHOLD;
unsigned usb_pid_arg = 0;
int dry_run = 0;
int trim_right_enabled = 0;
int trim_right_auto = 0;
unsigned trim_right_dots = 0;
int has_threshold = 0;
int want_status = 0;
static struct option longopts[] = {
@@ -114,6 +118,7 @@ int main(int argc, char **argv)
{ "height", required_argument, NULL, 'H' },
{ "file", required_argument, NULL, 'f' },
{ "threshold", required_argument, NULL, 't' },
{ "trim-right", optional_argument, NULL, 'r' },
{ "dry-run", no_argument, NULL, 'n' },
{ "pid", required_argument, NULL, 'p' },
{ "status", no_argument, NULL, 'S' },
@@ -123,7 +128,7 @@ int main(int argc, char **argv)
};
int c;
while ((c = getopt_long(argc, argv, "w:H:f:t:p:nhSV", longopts, NULL)) !=
while ((c = getopt_long(argc, argv, "w:H:f:t:r::p:nhSV", longopts, NULL)) !=
-1) {
switch (c) {
case 'w':
@@ -146,6 +151,20 @@ int main(int argc, char **argv)
case 'n':
dry_run = 1;
break;
case 'r':
trim_right_enabled = 1;
if (!optarg) {
trim_right_auto = 1;
trim_right_dots = 0;
break;
}
trim_right_auto = 0;
trim_right_dots = (unsigned)strtoul(optarg, NULL, 10);
if (trim_right_dots > 0xFFFFu) {
fprintf(stderr, "--trim-right must be 0..65535\n");
return 2;
}
break;
case 'p':
usb_pid_arg = (unsigned)strtoul(optarg, NULL, 0);
if (usb_pid_arg == 0u || usb_pid_arg > 0xFFFFu) {
@@ -238,6 +257,7 @@ int main(int argc, char **argv)
libptouch_raster_params_t params = { 0, 0, 0 };
libptouch_err_t e;
int usb_opened = 0;
int data_from_lib = 0;
if (png) {
libptouch_png_options_t opt = { .threshold = (uint8_t)threshold };
@@ -250,6 +270,7 @@ int main(int argc, char **argv)
libptouch_destroy(ctx);
return 1;
}
data_from_lib = 1;
} else if (svg) {
e = usb_pid_arg != 0
? libptouch_open_usb_vid_pid(ctx, LIBPTOUCH_USB_VID_BROTHER,
@@ -272,6 +293,7 @@ int main(int argc, char **argv)
libptouch_destroy(ctx);
return 1;
}
data_from_lib = 1;
} else {
if (read_file(file, &data, &data_len) != 0) {
libptouch_destroy(ctx);
@@ -282,12 +304,60 @@ int main(int argc, char **argv)
params.margin_mm = 0;
}
if (trim_right_enabled) {
uint16_t pad = (uint16_t)trim_right_dots;
if (trim_right_auto) {
pad = 0u;
if (!usb_opened) {
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)
usb_opened = 1;
}
if (usb_opened) {
libptouch_media_info_t mi;
if (libptouch_get_current_media_info(ctx, &mi) ==
LIBPTOUCH_OK) {
pad = mi.left_margin_dots;
}
}
}
uint8_t *trimmed = NULL;
size_t trimmed_len = 0;
libptouch_raster_params_t trimmed_params = { 0, 0, 0 };
e = libptouch_trim_right_blank_columns(
ctx, data, data_len, &params, pad, &trimmed, &trimmed_len,
&trimmed_params);
if (e != LIBPTOUCH_OK) {
fprintf(stderr, "trim_right_blank_columns: %s\n",
libptouch_strerror(ctx));
libptouch_destroy(ctx);
if (data_from_lib)
libptouch_free_raster(data);
else
free(data);
return 1;
}
if (data_from_lib)
libptouch_free_raster(data);
else
free(data);
data = trimmed;
data_len = trimmed_len;
params = trimmed_params;
data_from_lib = 1;
}
e = libptouch_check_raster(ctx, data, data_len, &params);
if (e != LIBPTOUCH_OK) {
fprintf(stderr, "check_raster: %s\n",
libptouch_strerror(ctx));
libptouch_destroy(ctx);
if (png || svg)
if (data_from_lib)
libptouch_free_raster(data);
else
free(data);
@@ -304,7 +374,7 @@ int main(int argc, char **argv)
if (usb_opened)
libptouch_close(ctx);
libptouch_destroy(ctx);
if (png || svg)
if (data_from_lib)
libptouch_free_raster(data);
else
free(data);
@@ -319,7 +389,7 @@ int main(int argc, char **argv)
if (e != LIBPTOUCH_OK) {
fprintf(stderr, "open_usb: %s\n", libptouch_strerror(ctx));
libptouch_destroy(ctx);
if (png || svg)
if (data_from_lib)
libptouch_free_raster(data);
else
free(data);
@@ -333,7 +403,7 @@ int main(int argc, char **argv)
libptouch_strerror(ctx));
libptouch_close(ctx);
libptouch_destroy(ctx);
if (png || svg)
if (data_from_lib)
libptouch_free_raster(data);
else
free(data);
@@ -342,7 +412,7 @@ int main(int argc, char **argv)
libptouch_close(ctx);
libptouch_destroy(ctx);
if (png || svg)
if (data_from_lib)
libptouch_free_raster(data);
else
free(data);

84
src/lib/libptouch_trim.c Normal file
View File

@@ -0,0 +1,84 @@
/*
* libptouch — raster right blank trim
*
* Author: knb
* Email: knb@artif.org
*/
#include "libptouch_internal.h"
#include <stdlib.h>
#include <string.h>
libptouch_err_t libptouch_trim_right_blank_columns(
libptouch_ctx *ctx, const uint8_t *data, size_t data_len,
const libptouch_raster_params_t *in_params, uint16_t right_padding_dots,
uint8_t **out_raster, size_t *out_raster_bytes,
libptouch_raster_params_t *out_params)
{
if (!ctx || !data || !in_params || !out_raster || !out_raster_bytes ||
!out_params) {
if (ctx)
ptouch_set_error(ctx, LIBPTOUCH_ERR_ARG, "null argument");
return LIBPTOUCH_ERR_ARG;
}
libptouch_err_t v = libptouch_check_raster(ctx, data, data_len, in_params);
if (v != LIBPTOUCH_OK)
return v;
uint32_t width = in_params->width_dots;
uint32_t height = in_params->height_dots;
size_t row_bytes = ((size_t)width + 7u) / 8u;
int32_t rightmost = -1;
for (uint32_t y = 0; y < height; y++) {
const uint8_t *row = data + (size_t)y * row_bytes;
for (int32_t x = (int32_t)width - 1; x >= 0; x--) {
uint8_t b = row[(size_t)x / 8u];
uint8_t bit = (uint8_t)(7u - ((uint32_t)x % 8u));
if (((b >> bit) & 1u) == 0)
continue;
if (x > rightmost)
rightmost = x;
break;
}
}
uint32_t new_width = width;
if (rightmost >= 0) {
uint32_t trimmed = (uint32_t)(rightmost + 1);
uint32_t padded = trimmed + (uint32_t)right_padding_dots;
if (padded > width)
padded = width;
if (padded < width)
new_width = padded;
}
size_t new_row_bytes = ((size_t)new_width + 7u) / 8u;
size_t total = new_row_bytes * (size_t)height;
uint8_t *out = (uint8_t *)calloc(1u, total);
if (!out) {
ptouch_set_error(ctx, LIBPTOUCH_ERR_NOMEM, "calloc raster failed");
return LIBPTOUCH_ERR_NOMEM;
}
uint8_t last_mask = 0xFFu;
if ((new_width % 8u) != 0u)
last_mask = (uint8_t)((0xFFu << (8u - (new_width % 8u))) & 0xFFu);
for (uint32_t y = 0; y < height; y++) {
const uint8_t *src = data + (size_t)y * row_bytes;
uint8_t *dst = out + (size_t)y * new_row_bytes;
memcpy(dst, src, new_row_bytes);
dst[new_row_bytes - 1u] &= last_mask;
}
*out_raster = out;
*out_raster_bytes = total;
out_params->width_dots = new_width;
out_params->height_dots = height;
out_params->margin_mm = in_params->margin_mm;
ptouch_set_error(ctx, LIBPTOUCH_OK, "");
return LIBPTOUCH_OK;
}