#include "util/ragel/ragel.h" #include "stack.h" #include "default-font.h" #include "parser.h" #include #include #include #include #include #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; }