From b87976793e0f45328d563eb80c4001a717a49563 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sun, 8 Mar 2015 16:10:59 +0100 Subject: pdf-stock: new tool for stocktaking --- src/pdf-stock/.gitignore | 3 + src/pdf-stock/Makefile | 19 +++ src/pdf-stock/main.vala | 36 ++++++ src/pdf-stock/pdf-stock-interface.vala | 19 +++ src/pdf-stock/pdf-stock.vala | 218 +++++++++++++++++++++++++++++++++ src/pdf-stock/test.vala | 28 +++++ 6 files changed, 323 insertions(+) create mode 100644 src/pdf-stock/.gitignore create mode 100644 src/pdf-stock/Makefile create mode 100644 src/pdf-stock/main.vala create mode 100644 src/pdf-stock/pdf-stock-interface.vala create mode 100644 src/pdf-stock/pdf-stock.vala create mode 100644 src/pdf-stock/test.vala (limited to 'src/pdf-stock') diff --git a/src/pdf-stock/.gitignore b/src/pdf-stock/.gitignore new file mode 100644 index 0000000..58a1e54 --- /dev/null +++ b/src/pdf-stock/.gitignore @@ -0,0 +1,3 @@ +pdf-stock +test +test.pdf diff --git a/src/pdf-stock/Makefile b/src/pdf-stock/Makefile new file mode 100644 index 0000000..e0e190e --- /dev/null +++ b/src/pdf-stock/Makefile @@ -0,0 +1,19 @@ +include ../../config.mk + +LIBCAIROBARCODE=-X -I../../libcairobarcode -X -L../../libcairobarcode -X -lcairobarcode + +all: pdf-stock + +pdf-stock: main.vala pdf-stock.vala ../database/db-interface.vala ../price.vapi ../../libcairobarcode/libcairobarcode.vapi + ${VALAC} -X -w ${LIBCAIROBARCODE} -o $@ --pkg cairo --pkg pangocairo --pkg gio-2.0 --pkg posix $^ + +test: test.vala pdf-stock-interface.vala + ${VALAC} -X -w -o $@ --pkg gio-2.0 $^ + +run: pdf-stock + LD_LIBRARY_PATH=../../libcairobarcode ./pdf-stock + +clean: + rm -rf pdf-stock test + +.PHONY: all clean run diff --git a/src/pdf-stock/main.vala b/src/pdf-stock/main.vala new file mode 100644 index 0000000..fee62af --- /dev/null +++ b/src/pdf-stock/main.vala @@ -0,0 +1,36 @@ +/* Copyright 2015, 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) { + Bus.own_name( + BusType.SESSION, + "io.mainframe.shopsystem.StockPDF", + BusNameOwnerFlags.NONE, + on_bus_aquired, + () => {}, + () => stderr.printf("Could not aquire name\n")); + + new MainLoop().run(); + + return 0; +} + +void on_bus_aquired(DBusConnection conn) { + try { + conn.register_object("/io/mainframe/shopsystem/stockpdf", new StockPDF()); + } catch(IOError e) { + stderr.printf("Could not register service\n"); + } +} diff --git a/src/pdf-stock/pdf-stock-interface.vala b/src/pdf-stock/pdf-stock-interface.vala new file mode 100644 index 0000000..415916f --- /dev/null +++ b/src/pdf-stock/pdf-stock-interface.vala @@ -0,0 +1,19 @@ +/* Copyright 2015, 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. + */ + +[DBus (name = "io.mainframe.shopsystem.StockPDF")] +public interface PDFStock : Object { + public abstract uint8[] generate() throws IOError; +} diff --git a/src/pdf-stock/pdf-stock.vala b/src/pdf-stock/pdf-stock.vala new file mode 100644 index 0000000..c1e9b6a --- /dev/null +++ b/src/pdf-stock/pdf-stock.vala @@ -0,0 +1,218 @@ +/* Copyright 2015, 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. + */ + +[DBus (name = "io.mainframe.shopsystem.StockPDF")] +public class StockPDF { + /* A4 sizes (in points, 72 DPI) */ + private const double a4w = 595.27559; /* 210mm */ + private const double a4h = 841.88976; /* 297mm */ + + private const double eanw = 100.0; + private const double eanh = 15.0; + + private const double col1 = 40; + private const double col2 = 145; + private const double col3 = 400; + private const double col4 = 480; + private const double col5 = 550; + + private const double row0 = 50; + private const double row1 = 65; + + private const double padding = 3; + + private int64 timestamp; + uint page; + double y; + + EAN ean; + Cairo.Context ctx; + Pango.Layout layout; + StockEntry[] stock; + + /* pdf data */ + private uint8[] data; + private uint useddatalength; + + private void render_page_header() { + var dt = new DateTime.from_unix_local(timestamp); + ctx.move_to(145, 25); + ctx.set_font_size(15.0); + var date = dt.format("%Y-%m-%d %H:%M:%S"); + ctx.show_text(@"Shopsystem Stock - State: $date"); + } + + private void render_page_footer() { + ctx.move_to(277, 820); + ctx.set_font_size(12.0); + ctx.show_text(@"Page $page"); + } + + private void render_table_header() { + ctx.save(); + + /* border & font color */ + ctx.set_source_rgb(0, 0, 0); + + /* line width of the border */ + ctx.set_line_width(0.8); + + /* header font */ + ctx.select_font_face("LMSans10", Cairo.FontSlant.NORMAL, Cairo.FontWeight.BOLD); + ctx.set_font_size(12); + + ctx.move_to(col1, row0); + ctx.line_to(col5, row0); + ctx.line_to(col5, row1); + ctx.line_to(col1, row1); + ctx.line_to(col1, row0); + ctx.move_to(col2, row0); + ctx.line_to(col2, row1); + ctx.move_to(col3, row0); + ctx.line_to(col3, row1); + ctx.move_to(col4, row0); + ctx.line_to(col4, row1); + ctx.stroke(); + + /* header text */ + ctx.move_to(col1 + padding, row1 - padding); + ctx.show_text("EAN"); + ctx.move_to(col2 + padding, row1 - padding); + ctx.show_text("Produkt"); + ctx.move_to(col3 + padding, row1 - padding); + ctx.show_text("Anzahl"); + ctx.move_to(col4 + padding, row1 - padding); + ctx.show_text("Zählung"); + + ctx.restore(); + } + + private void render_table_row(StockEntry product) throws BarcodeError { + ctx.set_line_width(0.8); + + /* borders */ + ctx.move_to(col1, y); + ctx.line_to(col1, y + eanh + 2*padding); + ctx.line_to(col5, y + eanh + 2*padding); + ctx.line_to(col5, y); + ctx.move_to(col2, y); + ctx.line_to(col2, y + eanh + 2*padding); + ctx.move_to(col3, y); + ctx.line_to(col3, y + eanh + 2*padding); + ctx.move_to(col4, y); + ctx.line_to(col4, y + eanh + 2*padding); + ctx.stroke(); + + /* EAN */ + ctx.move_to(col1 + padding, y + padding); + ean.draw(product.id); + + /* Product Name */ + ctx.move_to(col2 + padding, y); + layout.set_alignment(Pango.Alignment.LEFT); + layout.set_wrap(Pango.WrapMode.WORD_CHAR); + layout.set_spacing((int) (-padding * Pango.SCALE)); + layout.set_width((int) (col3-col2) * Pango.SCALE); + var text = @"$(product.id)\n$(product.name)"; + layout.set_text(text, text.length); + Pango.cairo_update_layout(ctx, layout); + Pango.cairo_show_layout(ctx, layout); + + /* Amount */ + ctx.set_font_size(16.0); + ctx.move_to(col3 + padding, y + eanh + 1); + ctx.show_text(@"$(product.amount)"); + } + + private Cairo.Status pdf_write(uchar[] newdata) { + if(data == null) { + data = newdata; + useddatalength = newdata.length; + } else { + if(useddatalength + newdata.length > data.length) { + uint8[] alldata = new uint8[data.length + newdata.length + 512]; + Posix.memcpy(alldata, data, data.length); + data = alldata; + } + + Posix.memcpy((uint8*) data + useddatalength, newdata, newdata.length); + useddatalength += newdata.length; + } + + return Cairo.Status.SUCCESS; + } + + public uint8[] generate() { + data = null; + + var surface = new Cairo.PdfSurface.for_stream(pdf_write, a4w, a4h); + ctx = new Cairo.Context(surface); + ean = new EAN(ctx, eanw, eanh); + layout = Pango.cairo_create_layout(ctx); + + var font = new Pango.FontDescription(); + font.set_family("LMRoman8"); + font.set_size((int) 6.0 * Pango.SCALE); + layout.set_font_description(font); + + /* get stock */ + try { + Database db = Bus.get_proxy_sync(BusType.SESSION, "io.mainframe.shopsystem.Database", "/io/mainframe/shopsystem/database"); + stock = db.get_stock(); + timestamp = new DateTime.now_local().to_unix(); + } catch(IOError e) { + return data; + } + + /* render pdf */ + ctx.select_font_face("LMSans10", Cairo.FontSlant.NORMAL, Cairo.FontWeight.NORMAL); + + try { + y = row1; + page = 1; + + render_page_header(); + render_table_header(); + + foreach(var p in stock) { + render_table_row(p); + y += eanh + 6; + + if(y > 780) { + y = row1; + render_page_footer(); + ctx.show_page(); + page++; + render_page_header(); + render_table_header(); + } + } + render_page_footer(); + } catch(BarcodeError e) { + stderr.printf(e.message + "\n"); + } + + surface.finish(); + surface.flush(); + + /* cleanup */ + ean = null; + ctx = null; + surface = null; + + return data; + } + +} diff --git a/src/pdf-stock/test.vala b/src/pdf-stock/test.vala new file mode 100644 index 0000000..9cd3a7e --- /dev/null +++ b/src/pdf-stock/test.vala @@ -0,0 +1,28 @@ +/* Copyright 2015, 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[]) { + try { + PDFStock stock = Bus.get_proxy_sync(BusType.SESSION, "io.mainframe.shopsystem.StockPDF", "/io/mainframe/shopsystem/stockpdf"); + var pdfdata = stock.generate(); + FileUtils.set_contents("test.pdf", (string) pdfdata, pdfdata.length); + } catch(IOError e) { + error("IOError: %s", e.message); + } catch(FileError e) { + error("FileError: %s", e.message); + } + + return 0; +} -- cgit v1.2.3