#include "util/ragel/ragel.h" #include "stack.h" #include "default-font.h" #include "parser.h" #include #include #include #include #include #include "utf8.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 utf8 { uint32_t state; }; struct cursor { size_t x, y, wy; }; struct raster { 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 init_canvas(struct escpos_canvas *canvas, struct cursor *cursor) { assert(canvas && cursor); memset(canvas->data, 0, canvas->w * canvas->h); *cursor = (struct cursor){ .x = 0, .y = 0, .wy = 0 }; } static void init_raster(struct raster *raster) { assert(raster); *raster = (struct raster){ .font = _intlfonts_ucs }; } static void render_chr(struct escpos_canvas *canvas, struct cursor *cursor, const struct raster *raster, const uint32_t cp) { assert(canvas && raster); const size_t off = canvas->w * cursor->y + cursor->x; const struct glyph *glyph = &raster->font.ucs2glyph[MIN(MAX(cp, 0), raster->font.max_ucs)]; const size_t width = div_round_up(glyph->width, 8); 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 < width; ++gx) { for (uint8_t b = 0; b < 8; ++b) { const size_t dst = off + (canvas->w * gy + (gx * 4) + b); assert(dst < canvas->w * canvas->h); canvas->data[dst] = data[width * gy + gx] & (1 << (7 - b)); } } } cursor->x += MIN(glyph->width + SPACING, canvas->w - cursor->x); cursor->wy = MIN(cursor->y + MAX(raster->font.height, 1) - 1, canvas->h); } static void apply_canvas(struct escpos_canvas *dst, struct cursor *dst_cursor, struct escpos_canvas *src, struct cursor *src_cursor) { // TODO: According to epson docs, the justification is applied after print buffer is applied // Thus we should apply justification here, need const struct *raster as input. assert(dst && dst_cursor && src && src_cursor); const size_t off = dst->w * dst_cursor->y + dst_cursor->x; assert(off + src->w * src_cursor->wy <= dst->w * dst->h); memcpy((char*)dst->data + off, src->data, src->w * src_cursor->wy); dst_cursor->wy = MAX(dst_cursor->wy, src_cursor->wy); init_canvas(src, src_cursor); } static void render_checker(struct escpos_canvas *canvas) { for (size_t gy = 0; gy < canvas->w; ++gy) { for (size_t gx = 0; gx < canvas->h; ++gx) { const size_t dst = canvas->w * gy + gx; assert(dst < canvas->w * canvas->h); canvas->data[dst] = ((gy * gx) % 2); } } } static void lfd(const struct escpos_canvas *canvas, struct cursor *cursor, const struct raster *raster, const uint8_t num) { assert(canvas && cursor); const size_t lf = (raster->font.height + SPACING) * num; cursor->x = 0; cursor->y += MIN(lf, canvas->h - cursor->y); cursor->wy = cursor->y + raster->font.height + SPACING; } static void cut(const struct escpos_parser *parser, struct escpos_canvas *canvas, struct cursor *cursor) { assert(parser && canvas && cursor); assert(cursor->wy <= canvas->h); if (cursor->wy > 0) { parser->write(parser, canvas->data, canvas->w, MIN(canvas->h, cursor->wy)); init_canvas(canvas, cursor); } } %%{ machine escpos_parser; alphtype unsigned char; 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_canvas(&parser->canvas, &canvas_cursor); init_canvas(&parser->print_buffer, &print_buffer_cursor); init_raster(&raster); } action lfd { fprintf(stderr, "lfd %d\n", stack_get_num(&stack)); apply_canvas(&parser->canvas, &canvas_cursor, &parser->print_buffer, &print_buffer_cursor); lfd(&parser->canvas, &canvas_cursor, &raster, stack_get_num(&stack)); } action lfv { fprintf(stderr, "lfv %d\n", stack_get_num(&stack)); apply_canvas(&parser->canvas, &canvas_cursor, &parser->print_buffer, &print_buffer_cursor); lfd(&parser->canvas, &canvas_cursor, &raster, stack_get_num(&stack)); const size_t lf = (raster.font.height + SPACING) * stack_get_num(&stack); canvas_cursor.x = 0; canvas_cursor.y -= MIN(lf, canvas_cursor.y); canvas_cursor.wy = canvas_cursor.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, &canvas_cursor); } 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); // TODO: raster barcode } action syntax_err { ragel_throw_error(&ragel, "malformed input (machine failed here or in next expression)"); } action text { putc(fc, stderr); uint32_t cp; if (utf8_decode(&utf8.state, &cp, fc) == UTF8_ACCEPT) render_chr(&parser->print_buffer, &print_buffer_cursor, &raster, cp); } action line { fputs("\\n", stderr); ragel_advance_line(&ragel); apply_canvas(&parser->canvas, &canvas_cursor, &parser->print_buffer, &print_buffer_cursor); lfd(&parser->canvas, &canvas_cursor, &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); struct stack stack = { .var.buf.mem = parser->var }; struct ragel ragel = { .name = name, .lineno = 1 }; struct utf8 utf8 = {0}; struct cursor canvas_cursor, print_buffer_cursor; init_canvas(&parser->canvas, &canvas_cursor); init_canvas(&parser->print_buffer, &print_buffer_cursor); struct raster raster; init_raster(&raster); // render_checker(&parser->canvas, &canvas_cursor); 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) { apply_canvas(&parser->canvas, &canvas_cursor, &parser->print_buffer, &print_buffer_cursor); cut(parser, &parser->canvas, &canvas_cursor); } return !ragel.error; }