diff --git a/.vscode/settings.json b/.vscode/settings.json index c15385a..d24ff8e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -85,5 +85,6 @@ }, "clang-tidy.buildPath": "builddir/", "clangd.onConfigChanged": "restart", - "C_Cpp.default.compileCommands": "./builddir/compile_commands.json" + "C_Cpp.default.compileCommands": "/home/moritz/Projekte/ptouch-prnt/builddir/compile_commands.json", + "gcovViewer.buildDirectories": ["/home/moritz/Projekte/ptouch-prnt/builddir"] } diff --git a/generate_coverage.sh b/generate_coverage.sh index 32b1ef4..0494499 100755 --- a/generate_coverage.sh +++ b/generate_coverage.sh @@ -2,6 +2,7 @@ SCRIPT_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" HTML_COV_PATH="coverageReport/html" +XML_COV_PATH="coverageReport/xml" HTML_START_FILE="index.html" echo "Generating Coverage report for ptouch-prnt" @@ -12,10 +13,15 @@ ninja -C builddir test mkdir -p ${HTML_COV_PATH} gcovr --html --html-details --html-syntax-highlighting --filter src --output ${HTML_COV_PATH}/${HTML_START_FILE} +mkdir -p ${XML_COV_PATH} +gcovr --xml-pretty --filter src --output ${XML_COV_PATH}/cov.xml + if [ $? ] then echo "Coverage report successful generated!" echo "Open: file://${SCRIPT_PATH}/${HTML_COV_PATH}/${HTML_START_FILE}" else echo "Error generating coverage report!" -fi \ No newline at end of file +fi + +rm *.gcov \ No newline at end of file diff --git a/meson.build b/meson.build index d523b8d..2ad425a 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project('ptprnt', 'cpp', version: 'v0.1.0-'+run_command('git', 'rev-parse', '--short', 'HEAD', check: true).stdout().strip(), license: 'GPLv3', - default_options : ['c_std=c11', 'cpp_std=c++17', 'b_sanitize=address,undefined', 'b_lto=true', 'b_lto_mode=thin', 'b_thinlto_cache=true'] + default_options : ['c_std=c11', 'cpp_std=c++17', 'b_sanitize=none', 'b_lto=true', 'b_lto_mode=thin', 'b_thinlto_cache=true'] ) usb_dep = dependency('libusb-1.0') diff --git a/src/P700Printer.cpp b/src/P700Printer.cpp index 67ecdbd..6632294 100644 --- a/src/P700Printer.cpp +++ b/src/P700Printer.cpp @@ -122,21 +122,23 @@ bool P700Printer::detachUsbDevice() { } bool P700Printer::printBitmap(const graphics::Bitmap& bitmap) { - auto bm = graphics::Bitmap(512, 128); - { - auto img = graphics::Label(); - bm.setPixels(std::vector(img.getRaw(), img.getRaw() + 512 * 128)); +#ifdef DRYRUN + SPDLOG_DEBUG("DRYRUN enabled"); + for (unsigned int lineNo = 0; lineNo < bitmap.getHeight(); lineNo++) { + auto line = bitmap.getLine(lineNo); + auto monoLine = graphics::Monochrome(*line); + monoLine.visualize(); } +#endif send(commands["rasterstart"]); - std::vector rastercmd(4); rastercmd[0] = 0x47; rastercmd[1] = 0x00; // size +1 rastercmd[2] = 0x00; rastercmd[3] = 0x00; // size -1 - for (unsigned int i = 0; i < bm.getWidth(); i++) { - auto bmcol = bm.getCol(i); + for (unsigned int i = 0; i < bitmap.getWidth(); i++) { + auto bmcol = bitmap.getCol(i); if (!bmcol) { spdlog::error("Out of bounds bitmap access"); break; @@ -151,6 +153,7 @@ bool P700Printer::printBitmap(const graphics::Bitmap& bitmap) buf[1] = col.size() + 1; buf[3] = col.size() - 1; if (!send(buf)) { + spdlog::error("Error sending buffer to printer"); break; }; } @@ -182,11 +185,11 @@ bool P700Printer::send(std::vector& data) { } #else tx = data.size(); - SPDLOG_DEBUG("USB raw data(len {}): {}", data.size(), spdlog::to_hex(data)); + spdlog::info("USB raw data(len {}): {}", data.size(), spdlog::to_hex(data)); #endif if (tx != static_cast(data.size())) { - spdlog::error("Could not transfer all data via USB bulk transfer. Only send {} of {} bytes", + spdlog::error("Could not transfer all data via USB bulk transfer. Only sent {} of {} bytes", tx, data.size()); return false; } diff --git a/src/P700Printer.hpp b/src/P700Printer.hpp index a21fc55..0c26562 100644 --- a/src/P700Printer.hpp +++ b/src/P700Printer.hpp @@ -17,6 +17,7 @@ */ +#include #include #include @@ -67,7 +68,8 @@ class P700Printer : public ::ptprnt::IPrinterDriver { .name = "Brother P-touch P700", .version = "v1.0", .vid = 0x04f9, - .pid = 0x2061}; + .pid = 0x2061, + .pixelLines = 128}; std::map> commands{ {"rasterstart", {0x1b, 0x69, 0x61, diff --git a/src/PtouchPrint.cpp b/src/PtouchPrint.cpp index 42224d3..3f27ce6 100644 --- a/src/PtouchPrint.cpp +++ b/src/PtouchPrint.cpp @@ -16,12 +16,13 @@ along with this program. If not, see . */ +#include + +#include "graphics/Monochrome.hpp" #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE #define SPDLOG_DEBUG_ON #define SPDLOG_TRACE_ON -#include "PtouchPrint.hpp" - #include #include #include @@ -38,6 +39,7 @@ #include "CLI/Option.hpp" #include "P700Printer.hpp" +#include "PtouchPrint.hpp" #include "graphics/Bitmap.hpp" #include "graphics/Label.hpp" #include "libusbwrap/UsbDeviceFactory.hpp" @@ -93,9 +95,6 @@ int PtouchPrint::run() { printer->attachUsbDevice(std::move(devices[0])); auto status = printer->getPrinterStatus(); spdlog::info("Detected tape width is {}mm", status.tapeWidthMm); - auto bm = ptprnt::graphics::Bitmap(512, 128); - //printer->printBitmap(bm); - //printer->printText("wurst", 1); if (0 == mCommands.size()) { spdlog::warn("No command specified, nothing to do..."); @@ -106,7 +105,15 @@ int PtouchPrint::run() { SPDLOG_DEBUG("Command: {}", cmd.second); if (cmd.first == CliCmdType::Text) { auto label{graphics::Label()}; - label.createLabel(cmd.second); + label.create(cmd.second, printer->getPrinterInfo().pixelLines); + auto bm = graphics::Bitmap(label.getLayoutWidth(), + printer->getPrinterInfo().pixelLines); + if (!bm.setPixels(label.getRaw())) { + spdlog::error("Non-matching bitmap size"); + return -1; + } + label.writeToPng("salat.png"); + printer->printBitmap(bm); } } diff --git a/src/graphics/Bitmap.cpp b/src/graphics/Bitmap.cpp index bffb917..b31f045 100644 --- a/src/graphics/Bitmap.cpp +++ b/src/graphics/Bitmap.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -32,19 +33,20 @@ Bitmap::Bitmap(uint16_t width, uint16_t height) : mWidth{width}, mHeight{height}, mPixels(width * height) {} template -uint16_t Bitmap::getWidth() { +[[nodiscard]] uint16_t Bitmap::getWidth() const { return mWidth; } template -uint16_t Bitmap::getHeight() { +[[nodiscard]] uint16_t Bitmap::getHeight() const { return mHeight; } template bool Bitmap::setPixels(const std::vector& pixels) { if (pixels.size() != mPixels.size()) { - spdlog::error("Invalid pixel buffer size."); + spdlog::error("Invalid pixel buffer size (got {} vs. {} bitmap size).", pixels.size(), + mPixels.size()); return false; } @@ -53,12 +55,12 @@ bool Bitmap::setPixels(const std::vector& pixels) { } template -std::vector Bitmap::getPixelsCpy() { +[[nodiscard]] std::vector Bitmap::getPixelsCpy() const { return mPixels; } template -std::optional> Bitmap::getLine(uint16_t line) { +[[nodiscard]] std::optional> Bitmap::getLine(uint16_t line) const { if (line >= mHeight) { // out of bound return std::nullopt; @@ -70,7 +72,7 @@ std::optional> Bitmap::getLine(uint16_t line) { } template -std::optional> Bitmap::getCol(uint16_t col) { +[[nodiscard]] std::optional> Bitmap::getCol(uint16_t col) const { if (col >= mWidth) { // out of bound return std::nullopt; diff --git a/src/graphics/Bitmap.hpp b/src/graphics/Bitmap.hpp index 16325e8..1a2a84a 100644 --- a/src/graphics/Bitmap.hpp +++ b/src/graphics/Bitmap.hpp @@ -39,12 +39,18 @@ class Bitmap { Bitmap(uint16_t width, uint16_t height); ~Bitmap() = default; - uint16_t getWidth(); - uint16_t getHeight(); + Bitmap(const Bitmap&) = default; + Bitmap& operator=(const Bitmap&) = default; + Bitmap(Bitmap&&) = default; + Bitmap& operator=(Bitmap&&) = default; + + [[nodiscard]] uint16_t getWidth() const; + [[nodiscard]] uint16_t getHeight() const; bool setPixels(const std::vector& pixels); - std::vector getPixelsCpy(); - std::optional> getLine(uint16_t line); - std::optional> getCol(uint16_t col); + [[nodiscard]] std::vector getPixelsCpy() const; + [[nodiscard]] std::optional> getLine(uint16_t line) const; + [[nodiscard]] std::optional> getCol(uint16_t col) const; + void visualize() const; private: uint16_t mWidth; diff --git a/src/graphics/Label.cpp b/src/graphics/Label.cpp index 8409755..1c5791e 100644 --- a/src/graphics/Label.cpp +++ b/src/graphics/Label.cpp @@ -22,9 +22,11 @@ #include #include +#include #include #include // remove me #include +#include #include "cairo.h" #include "pango/pango-context.h" @@ -35,67 +37,69 @@ #include "pango/pangocairo.h" namespace ptprnt::graphics { -Label::Label() { - /*mSurface = cairo_image_surface_create(CAIRO_FORMAT_A8, 512, 128); - cairo_t* cr = cairo_create(mSurface); - mFontDescription = pango_font_description_new(); - pango_font_description_set_family(mFontDescription, "sans"); - pango_font_description_set_weight(mFontDescription, PANGO_WEIGHT_SEMIBOLD); - pango_font_description_set_size(mFontDescription, 60 * PANGO_SCALE); +Label::Label() + : mPangoCtx(pango_font_map_create_context(pango_cairo_font_map_get_default())), + mPangoLyt(pango_layout_new(mPangoCtx)), + mPangoFontDesc(pango_font_description_from_string("Noto sans 32")) {} - std::string printThis("Mist 💩"); +std::vector Label::getRaw() { + assert(mSurface != nullptr); + size_t len = mPrinterHeight * mLayoutWidth; - mLayout = pango_cairo_create_layout(cr); - pango_layout_set_font_description(mLayout, mFontDescription); - pango_layout_set_text(mLayout, printThis.c_str(), -1); - - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_move_to(cr, 0.0, 94.0); - pango_cairo_show_layout_line(cr, pango_layout_get_line(mLayout, 0)); - - cairo_surface_write_to_png(mSurface, "hello.png");*/ -} - -uint8_t* Label::getRaw() { cairo_surface_flush(mSurface); - return cairo_image_surface_get_data(mSurface); + auto data = cairo_image_surface_get_data(mSurface); + return {data, data + len}; } uint8_t Label::getNumLines(std::string_view strv) { return std::count(strv.begin(), strv.end(), '\n'); } -void Label::createLabel(const std::string& str) { +int Label::getLayoutHeight() { + return mLayoutHeight; +} - // create Pango layout first, to get the render dimensions - auto pangoCtx{pango_font_map_create_context(pango_cairo_font_map_get_default())}; - auto pangoLyt{pango_layout_new(pangoCtx)}; - auto pangoFontDesc{pango_font_description_from_string("Noto sans 32")}; +int Label::getLayoutWidth() { + return mLayoutWidth; +} - pango_layout_set_single_paragraph_mode(pangoLyt, true); - pango_layout_set_height(pangoLyt, getNumLines(str) * -1); - pango_layout_set_alignment(pangoLyt, PANGO_ALIGN_CENTER); - pango_layout_set_font_description(pangoLyt, pangoFontDesc); - pango_context_load_font(pangoCtx, pangoFontDesc); - pango_layout_set_text(pangoLyt, str.c_str(), static_cast(str.length())); +void Label::create(const std::string& labelText, const uint16_t heightPixel) { + mPrinterHeight = heightPixel; + pango_layout_set_single_paragraph_mode(mPangoLyt, true); + pango_layout_set_height(mPangoLyt, getNumLines(labelText) * -1); + pango_layout_set_alignment(mPangoLyt, PANGO_ALIGN_CENTER); + pango_layout_set_font_description(mPangoLyt, mPangoFontDesc); + pango_context_load_font(mPangoCtx, mPangoFontDesc); + pango_layout_set_text(mPangoLyt, labelText.c_str(), static_cast(labelText.length())); - int width = 0, height = 0; + pango_layout_get_size(mPangoLyt, &mLayoutWidth, &mLayoutHeight); - pango_layout_get_size(pangoLyt, &width, &height); + mLayoutWidth /= PANGO_SCALE; + mLayoutHeight /= PANGO_SCALE; - SPDLOG_DEBUG("Layout width: {}, height: {}", width / PANGO_SCALE, height / PANGO_SCALE); + SPDLOG_DEBUG("Layout width: {}, height: {}", mLayoutWidth, mLayoutHeight); - auto surf = cairo_image_surface_create(CAIRO_FORMAT_A8, width / PANGO_SCALE, 128); - cairo_t* cr = cairo_create(surf); - pango_cairo_show_layout(cr, pangoLyt); + mSurface = cairo_image_surface_create(CAIRO_FORMAT_A8, mLayoutWidth, mPrinterHeight); + cairo_t* cr = cairo_create(mSurface); + pango_cairo_show_layout(cr, mPangoLyt); cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_surface_flush(mSurface); + cairo_destroy(cr); +} - // Debug only - cairo_surface_write_to_png(surf, "hellow.png"); +void Label::writeToPng(const std::string& file) { + if (mSurface) { + cairo_surface_flush(mSurface); + cairo_surface_write_to_png(mSurface, file.c_str()); + } } Label::~Label() { SPDLOG_DEBUG("Image dtor..."); + pango_font_description_free(mPangoFontDesc); + g_object_unref(mPangoCtx); + g_object_unref(mPangoLyt); + cairo_surface_destroy(mSurface); } } // namespace ptprnt::graphics \ No newline at end of file diff --git a/src/graphics/Label.hpp b/src/graphics/Label.hpp index db880de..ff6927a 100644 --- a/src/graphics/Label.hpp +++ b/src/graphics/Label.hpp @@ -23,6 +23,11 @@ #include #include +#include + +#include "cairo.h" +#include "pango/pango-font.h" +#include "pango/pango-types.h" namespace ptprnt::graphics { @@ -39,15 +44,21 @@ class Label { Label(Label&&) = delete; Label& operator=(Label&&) = delete; - uint8_t* getRaw(); - void createLabel(const std::string& labelText); + std::vector getRaw(); + void create(const std::string& labelText, const uint16_t heightPixel); + void writeToPng(const std::string& file); + int getLayoutWidth(); + int getLayoutHeight(); private: // methods uint8_t getNumLines(std::string_view str); // members - PangoLayout* mLayout; - PangoFontDescription* mFontDescription; - cairo_surface_t* mSurface; + PangoContext* mPangoCtx{nullptr}; + PangoLayout* mPangoLyt{nullptr}; + PangoFontDescription* mPangoFontDesc{nullptr}; + cairo_surface_t* mSurface{nullptr}; + int mLayoutWidth = 0, mLayoutHeight = 0; + int mPrinterHeight = 0; }; } // namespace ptprnt::graphics \ No newline at end of file diff --git a/src/graphics/Monochrome.cpp b/src/graphics/Monochrome.cpp index b14b3fd..2c1a892 100644 --- a/src/graphics/Monochrome.cpp +++ b/src/graphics/Monochrome.cpp @@ -21,7 +21,8 @@ #include -#include +#include +#include #include #include @@ -57,4 +58,20 @@ std::vector Monochrome::get() { } return outPixels; } + +void Monochrome::visualize() { + auto mono = get(); + for (unsigned char pix : mono) { + std::cout << ((pix & (1 << 7)) == 0 ? "." : "x"); + std::cout << ((pix & (1 << 6)) == 0 ? "." : "x"); + std::cout << ((pix & (1 << 5)) == 0 ? "." : "x"); + std::cout << ((pix & (1 << 4)) == 0 ? "." : "x"); + std::cout << ((pix & (1 << 3)) == 0 ? "." : "x"); + std::cout << ((pix & (1 << 2)) == 0 ? "." : "x"); + std::cout << ((pix & (1 << 1)) == 0 ? "." : "x"); + std::cout << ((pix & (1 << 0)) == 0 ? "." : "x"); + } + std::cout << std::endl; +} + } // namespace ptprnt::graphics \ No newline at end of file diff --git a/src/graphics/Monochrome.hpp b/src/graphics/Monochrome.hpp index b029de9..9b7db86 100644 --- a/src/graphics/Monochrome.hpp +++ b/src/graphics/Monochrome.hpp @@ -31,11 +31,12 @@ class Monochrome { void setThreshold(uint8_t); void invert(bool shouldInvert); + void visualize(); std::vector get(); private: const std::vector& mPixels; - uint8_t mThreshhold = 127; + uint8_t mThreshhold = UINT8_MAX / 2; bool mShouldInvert = false; }; } // namespace ptprnt::graphics \ No newline at end of file diff --git a/src/interface/IPrinterTypes.hpp b/src/interface/IPrinterTypes.hpp index 9e6e66d..dfcdfbc 100644 --- a/src/interface/IPrinterTypes.hpp +++ b/src/interface/IPrinterTypes.hpp @@ -30,6 +30,7 @@ struct PrinterInfo { std::string_view version = ""; uint16_t vid = 0x00; uint16_t pid = 0x00; + uint16_t pixelLines = 0; }; struct PrinterStatus { diff --git a/src/libusbwrap/UsbDeviceFactory.cpp b/src/libusbwrap/UsbDeviceFactory.cpp index f475fff..20991ba 100644 --- a/src/libusbwrap/UsbDeviceFactory.cpp +++ b/src/libusbwrap/UsbDeviceFactory.cpp @@ -32,7 +32,11 @@ namespace libusbwrap { -UsbDeviceFactory::~UsbDeviceFactory() = default; +UsbDeviceFactory::~UsbDeviceFactory() { + if (mLibusbCtx) { + libusb_exit(mLibusbCtx); + } +}; std::vector> UsbDeviceFactory::findAllDevices() { refreshDeviceList();