summaryrefslogtreecommitdiff
path: root/src/escpos/parser.rl
diff options
context:
space:
mode:
Diffstat (limited to 'src/escpos/parser.rl')
-rw-r--r--src/escpos/parser.rl252
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;
+}