diff options
Diffstat (limited to 'src/bin')
-rw-r--r-- | src/bin/escpos2raster.c | 80 | ||||
-rw-r--r-- | src/bin/starpbm.c | 153 |
2 files changed, 233 insertions, 0 deletions
diff --git a/src/bin/escpos2raster.c b/src/bin/escpos2raster.c new file mode 100644 index 0000000..e4c9708 --- /dev/null +++ b/src/bin/escpos2raster.c @@ -0,0 +1,80 @@ +#include <escpos/parser.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <err.h> + +static FILE* +fopen_or_die(const char *path, const char *mode) +{ + assert(path && mode); + + FILE *f; + if (!(f = fopen(path, mode))) + err(EXIT_FAILURE, "fopen(%s, %s)", path, mode); + + return f; +} + +struct parser { + struct escpos_parser parser; + FILE *input, *output; +}; + +#define container_of(ptr, type, member) ((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member))) + +size_t +escpos_read(const struct escpos_parser *parser, void *ptr, const size_t size) +{ + assert(parser && ptr); + const struct parser *p = container_of(parser, struct parser, parser); + return fread(ptr, 1, size, p->input); +} + +void +escpos_write(const struct escpos_parser *parser, const uint8_t *ptr, const size_t w, const size_t h) +{ + assert(parser && ptr); + const struct parser *p = container_of(parser, struct parser, parser); + const uint8_t header[] = { 'P', '4', '\n' }; + fwrite(header, 1, sizeof(header), p->output); + fprintf(p->output, "%zu\n", w); + fprintf(p->output, "%zu\n", h); + for (size_t y = 0; y < h; ++y) { + for (size_t x = 0; x < w; x += 8) { + uint8_t p8 = 255; + for (uint8_t b = 0; b < 8 && x + b < w; ++b) + p8 ^= (-(!!ptr[y * w + x + b]) ^ p8) & (1 << (7 - b)); + fwrite(&p8, 1, 1, p->output); + } + } +} + +int +main(int argc, char *argv[]) +{ + { +#define CANVAS_W 256 +#define CANVAS_H 1024 + char window[4096]; + uint8_t canvas_data[CANVAS_W * CANVAS_H]; + struct parser p = { + .parser = { + .read = escpos_read, + .write = escpos_write, + .canvas = { .data = canvas_data, .w = CANVAS_W, .h = CANVAS_H }, + .window = { .data = window, sizeof(window) }, + }, + .input = (argc >= 2 ? fopen_or_die(argv[1], "rb") : stdin), + .output = (argc >= 3 ? fopen_or_die(argv[2], "wb") : stdout), + }; + + if (!escpos_parser_parse(&p.parser, (argc >= 2 ? argv[1] : "stdin"))) + exit(EXIT_FAILURE); + + fclose(p.input); + fclose(p.output); + } + + return EXIT_SUCCESS; +} diff --git a/src/bin/starpbm.c b/src/bin/starpbm.c new file mode 100644 index 0000000..fd3f235 --- /dev/null +++ b/src/bin/starpbm.c @@ -0,0 +1,153 @@ +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <err.h> + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +enum cmd { + CMD_INIT, + CMD_PRINTABLE_WIDTH, + CMD_RASTER_MODE_START, + CMD_RASTER_PAGE_LENGTH, + CMD_START_PAGE, + CMD_END_PAGE, + CMD_END_JOB, +}; + +static void +putcmd(FILE *f, const enum cmd cmd) +{ + assert(f); + + const struct { + const uint8_t *bytes; + size_t len; + } map[] = { +#define CMD(...) { .bytes = (uint8_t[]){__VA_ARGS__ }, .len = sizeof((uint8_t[]){__VA_ARGS__}) } + CMD(0x1b, '@'), // CMD_INIT + CMD(0x1b, 0x1e, 'A', 0x00), // CMD_PRINTABLE_WIDTH + CMD(0x1b, '*', 'r', 'R', 0x1b, '*', 'r', 'A'), // CMD_RASTER_MODE_START + CMD(0x1b, '*', 'r', 'P', '0', 0x00), // CMD_SET_RASTER_PAGE_LENGTH + CMD(0x00), // CMD_START_PAGE + CMD(0x1b, '*', 'r', 'Y', '1', 0x00, 0x1b, 0x0c), // CMD_END_PAGE + CMD(0x04, 0x1b, '*', 'r', 'B'), // CMD_END_JOB +#undef CMD + }; + + if (fwrite(map[cmd].bytes, 1, map[cmd].len, f) != map[cmd].len) + err(EXIT_FAILURE, "fwrite"); +} + +static FILE* +fopen_or_die(const char *path, const char *mode) +{ + assert(path && mode); + + FILE *f; + if (!(f = fopen(path, mode))) + err(EXIT_FAILURE, "fopen(%s, %s)", path, mode); + + return f; +} + +static uint8_t* +find_pixel_not_rev(const uint8_t *data, const size_t len, const uint8_t pixel) +{ + for (const uint8_t *p = data + MAX(len, 1) - 1; p != data; --p) { + if (*p != pixel) + return (uint8_t*)p; + } + return NULL; +} + +uint8_t* +pixels_from_pbm(FILE *f, size_t *out_w, size_t *out_h) +{ + // NOTE: This does not support comments or extra whitespaces + // Assumes PBM is structured! + + uint8_t magic[3]; + if (fread(magic, 1, sizeof(magic), f) != sizeof(magic)) + return NULL; + + if (memcmp(magic, "P4", 2) || !isspace(magic[2])) + errx(EXIT_FAILURE, "not a valid pbm (P4) file"); + + uint32_t cfg[2]; + for (uint8_t i = 0; i < sizeof(cfg) / sizeof(cfg[0]); ++i) { + char buf[32]; + size_t x = 0; + do { + if (fread(buf + x, 1, 1, f) != 1) + err(EXIT_FAILURE, "fread"); + } while (!isspace(buf[x]) && ++x < sizeof(buf)); + + if (!isspace(buf[x])) + errx(EXIT_FAILURE, "not a valid pbm (P4) file"); + + cfg[i] = strtol(buf, NULL, 10); + } + + if (!cfg[0] || !cfg[2]) + errx(EXIT_FAILURE, "invalid width / height"); + + uint8_t *bw; +#define div_round_up(x, y) (1 + ((x - 1) / y)) + const size_t bw_sz = div_round_up(cfg[0], 8) * cfg[1]; + if (!(bw = calloc(bw_sz, 1))) + err(EXIT_FAILURE, "calloc"); + + for (size_t read = 0, r; read < bw_sz; read += r) { + if (!(r = fread(bw + read, 1, bw_sz - read, f))) + errx(EXIT_FAILURE, "fread"); + } + + *out_w = div_round_up(cfg[0], 8), *out_h = cfg[1]; + return bw; +} + +int +main(int argc, char *argv[]) +{ + FILE *input = (argc >= 2 ? fopen_or_die(argv[1], "rb") : stdin); + FILE *output = (argc >= 3 ? fopen_or_die(argv[2], "wb") : stdout); + + putcmd(output, CMD_INIT); + putcmd(output, CMD_PRINTABLE_WIDTH); + putcmd(output, CMD_RASTER_MODE_START); + putcmd(output, CMD_RASTER_PAGE_LENGTH); + + size_t w, h; + uint8_t *pixels; + while ((pixels = pixels_from_pbm(input, &w, &h))) { + putcmd(output, CMD_START_PAGE); + + size_t blank = 0; + for (size_t y = 0; y < h; ++y) { + const uint8_t *last_black; + if (!(last_black = find_pixel_not_rev(&pixels[y * w], w, 0))) { + ++blank; + continue; + } + + if (blank) { + fprintf(output, "\x01b*rY%zu%c", blank, 0); + blank = 0; + } + + const size_t pos = (last_black - &pixels[y * w]) + 1; + fprintf(output, "b%c%c", (char)(pos % 256), (char)(pos / 256)); + fwrite(&pixels[y * w], 1, pos, output); + } + + putcmd(output, CMD_END_PAGE); + free(pixels); + } + + putcmd(output, CMD_END_JOB); + return EXIT_SUCCESS; +} |