1st version

This commit is contained in:
knb
2026-04-12 08:52:54 +09:00
parent 2d8879f45f
commit d649b1b014
12 changed files with 1797 additions and 0 deletions

292
src/cli/main.c Normal file
View File

@@ -0,0 +1,292 @@
/*
* ptouch-print — CLI for libptouch
*
* Author: knb
* Email: knb@artif.org
*/
#include "libptouch.h"
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
static void usage(const char *argv0)
{
fprintf(stderr,
"Usage: %s [options]\n"
" -f, --file PATH 入力ファイル\n"
" -w, --width DOTS 1bit ラスター時: 幅(ドット)\n"
" -H, --height DOTS 1bit ラスター時: 高さ(ドット)\n"
" -t, --threshold N PNG 二値化しきい値 0255既定 %u、PNG のみ)\n"
" -n, --dry-run 読み込みと check_raster のみUSB なし)\n"
" -S, --status USB 接続プリンタのステータス(テープ種・幅・色等)を表示して終了\n"
" -V, --version バージョンを表示して終了\n"
" -h, --help このヘルプ\n"
"\n"
"PNG の場合は幅・高さはファイルから取得(-w/-H 不要)。\n"
"1bit packed ラスター(行優先)の場合は -f -w -H が必須。\n"
"--status のときは -f は不要(他オプションは無視されます)。\n",
argv0, (unsigned)LIBPTOUCH_PNG_DEFAULT_THRESHOLD);
}
/** パスが .png で終わる、または先頭 8 バイトが PNG シグネチャ */
static int input_is_png(const char *path)
{
const char *dot = strrchr(path, '.');
if (dot && strcasecmp(dot, ".png") == 0)
return 1;
FILE *fp = fopen(path, "rb");
if (!fp)
return 0;
unsigned char sig[8];
size_t n = fread(sig, 1, sizeof(sig), fp);
fclose(fp);
if (n != sizeof(sig))
return 0;
static const unsigned char png_magic[8] = { 137, 80, 78, 71,
13, 10, 26, 10 };
return memcmp(sig, png_magic, sizeof(png_magic)) == 0;
}
static int read_file(const char *path, uint8_t **out, size_t *out_len)
{
FILE *fp = fopen(path, "rb");
if (!fp) {
fprintf(stderr, "open %s: %s\n", path, strerror(errno));
return -1;
}
if (fseek(fp, 0, SEEK_END) != 0) {
fprintf(stderr, "fseek: %s\n", strerror(errno));
fclose(fp);
return -1;
}
long sz = ftell(fp);
if (sz < 0) {
fprintf(stderr, "ftell: %s\n", strerror(errno));
fclose(fp);
return -1;
}
rewind(fp);
uint8_t *buf = malloc((size_t)sz);
if (!buf) {
fprintf(stderr, "malloc failed\n");
fclose(fp);
return -1;
}
size_t n = fread(buf, 1, (size_t)sz, fp);
fclose(fp);
if (n != (size_t)sz) {
fprintf(stderr, "short read\n");
free(buf);
return -1;
}
*out = buf;
*out_len = n;
return 0;
}
int main(int argc, char **argv)
{
const char *file = NULL;
unsigned width = 0, height = 0;
unsigned threshold = LIBPTOUCH_PNG_DEFAULT_THRESHOLD;
int dry_run = 0;
int has_threshold = 0;
int want_status = 0;
static struct option longopts[] = {
{ "width", required_argument, NULL, 'w' },
{ "height", required_argument, NULL, 'H' },
{ "file", required_argument, NULL, 'f' },
{ "threshold", required_argument, NULL, 't' },
{ "dry-run", no_argument, NULL, 'n' },
{ "status", no_argument, NULL, 'S' },
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 },
};
int c;
while ((c = getopt_long(argc, argv, "w:H:f:t:nhSV", longopts, NULL)) !=
-1) {
switch (c) {
case 'w':
width = (unsigned)strtoul(optarg, NULL, 10);
break;
case 'H':
height = (unsigned)strtoul(optarg, NULL, 10);
break;
case 'f':
file = optarg;
break;
case 't':
threshold = (unsigned)strtoul(optarg, NULL, 10);
if (threshold > 255u) {
fprintf(stderr, "-t must be 0..255\n");
return 2;
}
has_threshold = 1;
break;
case 'n':
dry_run = 1;
break;
case 'S':
want_status = 1;
break;
case 'V':
printf("ptouch-print %s\n", LIBPTOUCH_VERSION_STRING);
return 0;
case 'h':
usage(argv[0]);
return 0;
default:
usage(argv[0]);
return 2;
}
}
if (want_status) {
if (file || width || height || dry_run || has_threshold)
fprintf(stderr,
"warning: options other than --status are ignored\n");
libptouch_ctx *sctx = libptouch_create();
if (!sctx) {
fprintf(stderr, "libptouch_create failed\n");
return 1;
}
libptouch_err_t se = libptouch_open_usb(sctx);
if (se != LIBPTOUCH_OK) {
fprintf(stderr, "open_usb: %s\n", libptouch_strerror(sctx));
libptouch_destroy(sctx);
return 1;
}
uint8_t st[LIBPTOUCH_STATUS_LENGTH];
se = libptouch_get_status(sctx, st);
if (se != LIBPTOUCH_OK) {
fprintf(stderr, "get_status: %s\n", libptouch_strerror(sctx));
libptouch_close(sctx);
libptouch_destroy(sctx);
return 1;
}
libptouch_status_fprint(stdout, st);
libptouch_close(sctx);
libptouch_destroy(sctx);
return 0;
}
if (!file) {
fprintf(stderr, "-f is required (or use --status)\n");
usage(argv[0]);
return 2;
}
int png = input_is_png(file);
if (png) {
if (width != 0 || height != 0)
fprintf(stderr,
"warning: -w/-H ignored for PNG (using image size)\n");
} else {
if (width == 0 || height == 0) {
fprintf(stderr,
"1bit raster requires -w and -H (or use a PNG file)\n");
usage(argv[0]);
return 2;
}
if (has_threshold)
fprintf(stderr,
"warning: -t applies to PNG only (ignored)\n");
}
libptouch_ctx *ctx = libptouch_create();
if (!ctx) {
fprintf(stderr, "libptouch_create failed\n");
return 1;
}
uint8_t *data = NULL;
size_t data_len = 0;
libptouch_raster_params_t params = { 0, 0, 0 };
libptouch_err_t e;
if (png) {
libptouch_png_options_t opt = { .threshold = (uint8_t)threshold };
e = libptouch_png_file_to_raster(ctx, file,
has_threshold ? &opt : NULL,
&data, &data_len, &params);
if (e != LIBPTOUCH_OK) {
fprintf(stderr, "png_file_to_raster: %s\n",
libptouch_strerror(ctx));
libptouch_destroy(ctx);
return 1;
}
} else {
if (read_file(file, &data, &data_len) != 0) {
libptouch_destroy(ctx);
return 1;
}
params.width_dots = width;
params.height_dots = height;
params.margin_mm = 0;
}
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)
libptouch_free_raster(data);
else
free(data);
return 1;
}
if (dry_run) {
printf("dry-run OK: %zu bytes, %ux%u dots\n", data_len,
(unsigned)params.width_dots,
(unsigned)params.height_dots);
libptouch_destroy(ctx);
if (png)
libptouch_free_raster(data);
else
free(data);
return 0;
}
e = libptouch_open_usb(ctx);
if (e != LIBPTOUCH_OK) {
fprintf(stderr, "open_usb: %s\n", libptouch_strerror(ctx));
libptouch_destroy(ctx);
if (png)
libptouch_free_raster(data);
else
free(data);
return 1;
}
e = libptouch_print_raster(ctx, data, data_len, &params);
if (e != LIBPTOUCH_OK) {
fprintf(stderr, "print_raster: %s\n",
libptouch_strerror(ctx));
libptouch_close(ctx);
libptouch_destroy(ctx);
if (png)
libptouch_free_raster(data);
else
free(data);
return 1;
}
libptouch_close(ctx);
libptouch_destroy(ctx);
if (png)
libptouch_free_raster(data);
else
free(data);
return 0;
}