diff options
Diffstat (limited to 'src/escpos/parser.rl')
-rw-r--r-- | src/escpos/parser.rl | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/src/escpos/parser.rl b/src/escpos/parser.rl new file mode 100644 index 0000000..1e045c7 --- /dev/null +++ b/src/escpos/parser.rl @@ -0,0 +1,252 @@ +#include "util/ragel/ragel.h" +#include "stack.h" +#include "default-font.h" +#include "parser.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <err.h> + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define div_round_up(x, y) (1 + ((x - 1) / y)) + +static const uint8_t SPACING = 1; + +struct raster { + size_t x, y, wy; + struct font font; + uint8_t barcode_height; + uint8_t barcode_type; + enum { + UNDERLINE_NONE, + UNDERLINE_LIGHT, + UNDERLINE_HEAVY, + } underline; + enum { + JUSTIFY_LEFT, + JUSTIFY_CENTER, + JUSTIFY_RIGHT + } justify; + bool emphasis; + bool double_strike; + bool double_width; + bool double_height; +}; + +static void +render_chr(struct escpos_canvas *canvas, struct raster *raster, const char chr) +{ + assert(canvas && raster); + const size_t off = canvas->w * raster->y + raster->x; + const struct glyph *glyph = &raster->font.ucs2glyph[MIN((uint8_t)MAX(chr, 0), raster->font.max_ucs)]; + const uint8_t *data = &raster->font.data[glyph->offset]; + for (size_t gy = 0; gy < raster->font.height; ++gy) { + for (size_t gx = 0; gx < glyph->width; gx += 8) { + for (uint8_t b = 0; b < 8; ++b) { + const size_t dst = off + (canvas->w * gy + gx + b); + assert(dst < canvas->w * canvas->h); + canvas->data[dst] = data[div_round_up(glyph->width, 8) * gy + gx] & (1 << (7 - b)); + } + } + } + raster->x += MIN(glyph->width + SPACING, canvas->w - raster->x); + raster->wy = MIN(raster->y + MAX(raster->font.height, 1) - 1, canvas->h); +} + +static void +init(struct escpos_canvas *canvas, struct raster *raster) +{ + assert(canvas && raster); + memset(canvas->data, 0, canvas->w * canvas->h); + *raster = (struct raster){ .font = _intlfonts_ucs }; + raster->x = raster->y = raster->wy = 0; +} + +static void +lfd(const struct escpos_canvas *canvas, struct raster *raster, const uint8_t num) +{ + assert(canvas && raster); + const size_t lf = (raster->font.height + SPACING) * num; + raster->x = 0; + raster->y += MIN(lf, canvas->h - raster->y); + raster->wy = raster->y + raster->font.height + SPACING; +} + +static void +cut(const struct escpos_parser *parser, struct escpos_canvas *canvas, struct raster *raster) +{ + assert(canvas && raster); + assert(raster->wy <= canvas->h); + + if (raster->wy > 0) { + parser->write(parser, canvas->data, canvas->w, MIN(canvas->h, raster->wy)); + memset(canvas->data, 0, canvas->w * canvas->h); + raster->x = raster->y = raster->wy = 0; + } +} + +%%{ + machine escpos_parser; + include escpos_stack "stack.rl"; + variable p ragel.p; + variable pe ragel.pe; + variable eof ragel.eof; + write data noerror nofinal; + + action init { + puts("init"); + init(&parser->canvas, &raster); + } + + action lfd { + fprintf(stderr, "lfd %d\n", stack_get_num(&stack)); + lfd(&parser->canvas, &raster, stack_get_num(&stack)); + } + + action lfv { + fprintf(stderr, "lfv %d\n", stack_get_num(&stack)); + const size_t lf = (raster.font.height + SPACING) * stack_get_num(&stack); + raster.x = 0; + raster.y -= MIN(lf, raster.y); + raster.wy = raster.y + raster.font.height + SPACING; + } + + action print_mode { + fprintf(stderr, "print-mode: %d\n", stack_get_num(&stack)); + enum { + MODE_FONTA = 1<<0, + MODE_FONTB = 1<<1, + MODE_EMPHASIS = 1<<2, + MODE_DOUBLE_HEIGHT = 1<<3, + MODE_DOUBLE_WIDTH = 1<<4, + MODE_UNDERLINE = 1<<5, + }; + const uint8_t mode = stack_get_num(&stack); + raster.emphasis = (mode & MODE_EMPHASIS); + raster.double_width = (mode & MODE_DOUBLE_HEIGHT); + raster.double_width = (mode & MODE_DOUBLE_WIDTH); + raster.underline = (mode & MODE_UNDERLINE); + } + + action underline { + fprintf(stderr, "underline %d\n", stack_get_num(&stack)); + raster.underline = stack_get_num(&stack); + } + + action cut { + fprintf(stderr, "cut %d\n", stack_get_num(&stack)); + cut(parser, &parser->canvas, &raster); + } + + action emphasis { + fprintf(stderr, "emphasis %d\n", stack_get_num(&stack)); + raster.emphasis = stack_get_num(&stack); + } + + action double_strike { + fprintf(stderr, "double-strike %d\n", stack_get_num(&stack)); + raster.double_strike = stack_get_num(&stack); + } + + action font { + fprintf(stderr, "font %d\n", stack_get_num(&stack)); + // XXX: have only one font + } + + action justify { + fprintf(stderr, "justify %d\n", stack_get_num(&stack)); + raster.justify = stack_get_num(&stack); + } + + action barcode_height { + fprintf(stderr, "barcode-height %d\n", stack_get_num(&stack)); + raster.barcode_height = stack_get_num(&stack); + } + + action barcode_type { + fprintf(stderr, "barcode-type %d\n", stack_get_num(&stack)); + raster.barcode_type = stack_get_num(&stack); + } + + action barcode_str { + fprintf(stderr, "barcode-str %s\n", (char*)stack_get_str(&stack)->data); + // XXX: raster barcode + } + + action syntax_err { + ragel_throw_error(&ragel, "malformed input (machine failed here or in next expression)"); + } + + action text { + putc(fc, stderr); + render_chr(&parser->canvas, &raster, fc); + } + + action line { + fputs("\\n", stderr); + ragel_advance_line(&ragel); + lfd(&parser->canvas, &raster, 1); + } + + # Constants + LF = 0x0A; # Line Feed + ESC = 0x1B; # Escape + GS = 0x1D; # Group Separator + + # Commands + # https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=72 + # TODO: Implement rest + init = ESC '@' %init; + linefeed = LF %line | ESC :>> (('d' stack_num) %lfd | ('v' stack_num) %lfv); + print_mode = ESC '!' stack_num %print_mode; + underline = ESC '-' stack_num %underline; + cut = GS 'V' stack_num 0x03 %cut; + emphasis = ESC 'E' stack_num %emphasis; + double_strike = ESC 'G' stack_num %double_strike; + font = ESC 'M' stack_num %font; + justify = ESC 'a' stack_str %justify; + barcode_height = GS 'h' stack_num %barcode_height; + barcode = ESC 'k' stack_num %barcode_type stack_str %barcode_str; + + # Obsolete commands + # https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=74 + # TODO: Implement + + # Logic + cmd = (init | linefeed | print_mode | underline | cut | emphasis | double_strike | font | justify | barcode); + main := (cmd | TEXT $text)* $!syntax_err; +}%% + +bool +escpos_parser_parse(struct escpos_parser *parser, const char *name) +{ + int cs; + %% write init; + + (void)escpos_parser_en_main; + assert(parser); + assert(parser->read); + assert(parser->write); + + char var[256]; + struct stack stack = { .var.buf.mem = { .data = var, .len = sizeof(var) } }; + struct ragel ragel = { .name = name, .lineno = 1 }; + + struct raster raster; + init(&parser->canvas, &raster); + + struct escpos_mem window = parser->window; + for (bool eof = false; !ragel.error && !eof;) { + const size_t bytes = parser->read(parser, window.data, window.len); + const struct ragel_mem input = { .data = window.data, .end = (char*)window.data + bytes, .binary = true }; + ragel_feed_input(&ragel, (eof = (bytes < window.len)), &input); + %% write exec; + } + + if (!ragel.error) + cut(parser, &parser->canvas, &raster); + + return !ragel.error; +} |