From 258c7c1dce42fed4b1a66900f8d3b45c42aa71e0 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 3 Feb 2014 00:26:01 +0100 Subject: initial libcairobarcode --- TODO.md | 3 +- libcairobarcode/.gitignore | 2 + libcairobarcode/Makefile | 14 +++++ libcairobarcode/code39.vala | 128 +++++++++++++++++++++++++++++++++++++ libcairobarcode/ean.vala | 150 ++++++++++++++++++++++++++++++++++++++++++++ libcairobarcode/error.vala | 19 ++++++ libcairobarcode/test.vala | 53 ++++++++++++++++ 7 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 libcairobarcode/.gitignore create mode 100644 libcairobarcode/Makefile create mode 100644 libcairobarcode/code39.vala create mode 100644 libcairobarcode/ean.vala create mode 100644 libcairobarcode/error.vala create mode 100644 libcairobarcode/test.vala diff --git a/TODO.md b/TODO.md index 2324aa2..5e9ed73 100644 --- a/TODO.md +++ b/TODO.md @@ -11,7 +11,8 @@ - [old code](https://github.com/ktt-ol/serial-barcode-scanner/commit/504cefec4a93a9b52fa9d25d6f353a4676485c43) #### libcairobarcode - * write vala library using cairo to generate a barcode + * make this a library instead of a test binary + * cleanup code #### USERLIST-PDF * write new vala process, which generates a user list pdf using libcairobarcode diff --git a/libcairobarcode/.gitignore b/libcairobarcode/.gitignore new file mode 100644 index 0000000..c0503b2 --- /dev/null +++ b/libcairobarcode/.gitignore @@ -0,0 +1,2 @@ +test +test.svg diff --git a/libcairobarcode/Makefile b/libcairobarcode/Makefile new file mode 100644 index 0000000..b47d3b3 --- /dev/null +++ b/libcairobarcode/Makefile @@ -0,0 +1,14 @@ +include ../config.mk + +all: test + +libcairobarcode.so: + #TODO + +test: test.vala ean.vala code39.vala error.vala + ${VALAC} -o $@ --pkg cairo $^ + +clean: + rm -f test test.svg + +.PHONY: all clean diff --git a/libcairobarcode/code39.vala b/libcairobarcode/code39.vala new file mode 100644 index 0000000..dd3d8c5 --- /dev/null +++ b/libcairobarcode/code39.vala @@ -0,0 +1,128 @@ +/* Copyright 2014, Sebastian Reichel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +public class Code39 { + Cairo.Context ctx; + double width; + double height; + + /* 0 = wide black, 1 = narrow black, 2 = wide white, 3 = narrow white */ + static const uint32[] lookup_table = { + 0x1d8cd, 0xd9dc, 0x1c9dc, 0xc9dd, 0x1d8dc, 0xd8dd, 0x1c8dd, 0x1d9cc, + 0xd9cd, 0x1c9cd, 0xdd9c, 0x1cd9c, 0xcd9d, 0x1dc9c, 0xdc9d, 0x1cc9d, + 0x1dd8c, 0xdd8d, 0x1cd8d, 0x1dc8d, 0xddd8, 0x1cdd8, 0xcdd9, 0x1dcd8, + 0xdcd9, 0x1ccd9, 0x1ddc8, 0xddc9, 0x1cdc9, 0x1dcc9, 0x9ddc, 0x18ddc, + 0x8ddd, 0x19cdc, 0x9cdd, 0x18cdd, 0x19dcc, 0x9dcd, 0x18dcd, 0x1999d, + 0x199d9, 0x19d99, 0x1d999, 0x19ccd + }; + + public Code39(Cairo.Context ctx, double width, double height) { + this.ctx = ctx; + this.width = width; + this.height = height; + } + + private int lookup_index(char c) throws BarcodeError { + if(c >= '0' && c <= '9') + return c - '0'; + if(c >= 'A' && c <= 'Z') + return c - 'A' + 10; + switch (c) { + case '-': + return 36; + case '.': + return 37; + case ' ': + return 38; + case '$': + return 39; + case '/': + return 40; + case '+': + return 41; + case '%': + return 42; + case '*': + return 43; + default: + throw new BarcodeError.UNEXPECTED_CHARACTER("Character '%c' is not allowed in Code 39".printf(c)); + } + } + + private uint32 lookup(char c) throws BarcodeError { + return lookup_table[lookup_index(c)]; + } + + private void draw_line(bool black, double linewidth) { + double x,y; + + if(black) + ctx.set_source_rgb(0, 0, 0); + else + ctx.set_source_rgb(1, 1, 1); + + ctx.rel_line_to(0,height); + ctx.rel_move_to(linewidth,-height); + + ctx.get_current_point(out x, out y); + ctx.stroke(); + ctx.move_to(x,y); + } + + public void draw(string code) throws BarcodeError { + string mycode = code; + + if(!mycode.has_prefix("*")) + mycode = "*" + mycode; + if(!mycode.has_suffix("*")) + mycode = mycode + "*"; + + double linewidth = width / (mycode.length * 13.0); + + ctx.save(); + ctx.set_line_width(linewidth); + ctx.move_to(0,0); + ctx.rel_move_to(0.5*linewidth,0); + + for(int i=0; i=0; j--) { + var line = (format >> (2*j)) & 0x3; + + switch(line) { + case 0: + draw_line(true, linewidth); + draw_line(true, linewidth); + break; + case 1: + draw_line(true, linewidth); + break; + case 2: + draw_line(false, linewidth); + draw_line(false, linewidth); + break; + default: + draw_line(false, linewidth); + break; + } + } + + draw_line(false, linewidth); + } + + ctx.restore(); + } +} diff --git a/libcairobarcode/ean.vala b/libcairobarcode/ean.vala new file mode 100644 index 0000000..d76f079 --- /dev/null +++ b/libcairobarcode/ean.vala @@ -0,0 +1,150 @@ +/* Copyright 2014, Sebastian Reichel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +public class EAN { + Cairo.Context ctx; + double width; + double height; + const double marker_length_diff = 10.0; + + static const uint8[] C = { 0x00, 0x0b, 0x0d, 0x0e, 0x13, 0x19, 0x1c, 0x15, 0x16, 0x1a }; + + static uint8[,] lookup = { + { 0x0d, 0x19, 0x13, 0x3d, 0x23, 0x31, 0x2f, 0x3b, 0x37, 0x0b }, /* L */ + { 0x27, 0x33, 0x1b, 0x21, 0x1d, 0x39, 0x05, 0x11, 0x09, 0x17 }, /* G */ + { 0x72, 0x66, 0x6c, 0x42, 0x5c, 0x4e, 0x50, 0x44, 0x48, 0x74 } /* R */ + }; + + public EAN(Cairo.Context ctx, double width, double height) { + this.ctx = ctx; + this.width = width / 95.0; + this.height = height; + } + + private void draw_line(bool black, bool marker=false) { + double x,y; + + if(black) + ctx.set_source_rgb(0, 0, 0); + else + ctx.set_source_rgb(1, 1, 1); + + if(marker) { + ctx.rel_line_to(0,height); + ctx.rel_move_to(width,-height); + } else { + ctx.rel_line_to(0,height-marker_length_diff); + ctx.rel_move_to(width,-(height-marker_length_diff)); + } + + ctx.get_current_point(out x, out y); + ctx.stroke(); + ctx.move_to(x,y); + } + + private void draw_startstop() { + draw_line(true, true); + draw_line(false, true); + draw_line(true, true); + } + + private void draw_middle() { + draw_line(false, true); + draw_line(true, true); + draw_line(false, true); + draw_line(true, true); + draw_line(false, true); + } + + private void draw_number_text(uint8 number) { + double x, y; + ctx.get_current_point(out x, out y); + ctx.set_source_rgb(0, 0, 0); + ctx.rel_move_to(2.5,height); + + ctx.show_text("%d".printf(number)); + + ctx.stroke(); + ctx.move_to(x,y); + } + + private void draw_number(uint8 number, uint8 type) { + draw_number_text(number); + + for(int i=6; i>=0; i--) { + var bit = (lookup[type,number] >> i) & 1; + draw_line(bit == 1); + } + } + + private uint8 get_number(string ean, int pos) { + return (uint8) int.parse("%c".printf(ean[pos])); + } + + private void draw13(string ean) { + /* need some extra space for the checksum */ + width = (width * 95.0) / 102.0; + + uint8 LG = C[get_number(ean, 0)]; + draw_number_text(get_number(ean, 0)); + ctx.move_to(7*width,0); + + draw_startstop(); + for(int i=1, x = 5; i<7; i++, x--) { + uint8 type = (uint8) (LG >> x) & 1; + draw_number(get_number(ean,i), type); + } + draw_middle(); + for(int i=7; i<13; i++) + draw_number(get_number(ean,i), 2); + draw_startstop(); + + /* remove extra space for the checksum */ + width = (width * 102.0) / 95.0; + + } + + private void draw8(string ean) { + draw_startstop(); + for(int i=0; i<4; i++) + draw_number(get_number(ean,i), 0); + draw_middle(); + for(int i=4; i<8; i++) + draw_number(get_number(ean,i), 2); + draw_startstop(); + } + + public void draw(string ean) throws BarcodeError { + ctx.save(); + ctx.set_line_width(width); + ctx.move_to(0,0); + ctx.rel_move_to(0.5*width,0); + + for(int i=0; i '9') { + throw new BarcodeError.UNEXPECTED_CHARACTER("Character '%c' is not allowed in EAN".printf(ean[i])); + } + } + + if(ean.length == 13) + draw13(ean); + else if(ean.length == 8) + draw8(ean); + else + throw new BarcodeError.UNEXPECTED_LENGTH("length of EAN is incorrect (must be 8 or 13)"); + + ctx.restore(); + } +} diff --git a/libcairobarcode/error.vala b/libcairobarcode/error.vala new file mode 100644 index 0000000..f81df91 --- /dev/null +++ b/libcairobarcode/error.vala @@ -0,0 +1,19 @@ +/* Copyright 2014, Sebastian Reichel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +public errordomain BarcodeError { + UNEXPECTED_CHARACTER, + UNEXPECTED_LENGTH +} diff --git a/libcairobarcode/test.vala b/libcairobarcode/test.vala new file mode 100644 index 0000000..de42d80 --- /dev/null +++ b/libcairobarcode/test.vala @@ -0,0 +1,53 @@ +/* Copyright 2014, Sebastian Reichel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +public static int main(string[] args) { + double width = 190.0; + double height = 40.0; + + var surface = new Cairo.SvgSurface("test.svg", width, height); + var ctx = new Cairo.Context(surface); + var ean = new EAN(ctx, width, height); + var code39 = new Code39(ctx, width, height); + + if(args.length < 3) { + stderr.printf("Usage: %s \n", args[0]); + return 1; + } + + try { + switch(args[1]) { + case "ean": + ean.draw(args[2]); + break; + case "code39": + code39.draw(args[2]); + break; + default: + stderr.printf("Usage: %s \n", args[0]); + return 1; + } + } catch (BarcodeError e) { + stderr.printf(e.message + "\n"); + } + + /* cleanup */ + code39 = null; + ean = null; + ctx = null; + surface = null; + + return 0; + } -- cgit v1.2.3