From 0b8ff28a60be064605d114d9467a3a8886047013 Mon Sep 17 00:00:00 2001 From: Moritz Martinius Date: Sat, 11 Oct 2025 12:29:43 +0200 Subject: [PATCH] Start refactoring printers into own directory --- .vscode/launch.json | 11 +- src/PrinterDriverFactory.cpp | 2 +- src/PtouchPrint.cpp | 9 +- src/graphics/Label.cpp | 137 +++++++++++++--------- src/graphics/Label.hpp | 38 ++++-- src/graphics/Monochrome.cpp | 120 ++++++++++--------- src/graphics/Monochrome.hpp | 67 +++++++---- src/meson.build | 4 +- src/{ => printers}/P700Printer.cpp | 36 +++--- src/{ => printers}/P700Printer.hpp | 8 +- tests/monochrome_test/monochrome_test.cpp | 16 +-- 11 files changed, 271 insertions(+), 177 deletions(-) rename src/{ => printers}/P700Printer.cpp (92%) rename src/{ => printers}/P700Printer.hpp (94%) diff --git a/.vscode/launch.json b/.vscode/launch.json index a958035..50664cf 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,12 +9,15 @@ "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/builddir/ptprnt", - "args": ["-t Hello"], + "args": [ + "-t i" + ], "stopAtEntry": false, - "cwd": "${fileDirname}", + "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, - "MIMode": "lldb", + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", "setupCommands": [ { "description": "Automatische Strukturierung und Einrückung für \"gdb\" aktivieren", @@ -24,4 +27,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/src/PrinterDriverFactory.cpp b/src/PrinterDriverFactory.cpp index ce34d68..908de40 100644 --- a/src/PrinterDriverFactory.cpp +++ b/src/PrinterDriverFactory.cpp @@ -4,7 +4,7 @@ #include -#include "P700Printer.hpp" +#include "printers/P700Printer.hpp" #include "libusbwrap/LibUsbTypes.hpp" namespace ptprnt { diff --git a/src/PtouchPrint.cpp b/src/PtouchPrint.cpp index 4641b98..034c893 100644 --- a/src/PtouchPrint.cpp +++ b/src/PtouchPrint.cpp @@ -88,7 +88,10 @@ int PtouchPrint::run() { spdlog::warn("Found more than one device of the same printer on bus. Currently not supported"); return -1; } - printer->attachUsbDevice(std::move(devices[0])); + if (!printer->attachUsbDevice(std::move(devices[0]))) { + spdlog::error("Failed to attach USB device to printer"); + return -1; + } auto status = printer->getPrinterStatus(); spdlog::info("Detected tape width is {}mm", status.tapeWidthMm); @@ -150,10 +153,10 @@ int PtouchPrint::run() { } label->create(labelText); label->writeToPng("./testlabel.png"); - /*if (!printer->printLabel(std::move(label))) { + if (!printer->printLabel(std::move(label))) { spdlog::error("An error occured while printing"); return -1; - }*/ + } return 0; } diff --git a/src/graphics/Label.cpp b/src/graphics/Label.cpp index 6f9f584..72a853e 100644 --- a/src/graphics/Label.cpp +++ b/src/graphics/Label.cpp @@ -37,18 +37,24 @@ namespace ptprnt::graphics { Label::Label(const uint16_t heightPixel) - : mCairoCtx(cairo_create(mSurface)), - mPangoCtx(pango_cairo_create_context(mCairoCtx)), - mPangoLyt(pango_layout_new(mPangoCtx)), - mFontMap(pango_cairo_font_map_new()), - mPrinterHeight(heightPixel) {} + : mPrinterHeight(heightPixel) { + // Initialize resources in correct order with RAII + mFontMap.reset(pango_cairo_font_map_new()); +} std::vector Label::getRaw() { assert(mSurface != nullptr); - size_t len = mPrinterHeight * mLayoutWidth; + auto* surface = mSurface.get(); + size_t len = cairo_image_surface_get_height(surface) * cairo_image_surface_get_stride(surface); - cairo_surface_flush(mSurface); - auto data = cairo_image_surface_get_data(mSurface); + cairo_surface_flush(surface); + assert(cairo_image_surface_get_format(surface) == CAIRO_FORMAT_A8); + spdlog::debug("Cairo Surface data: W: {}; H: {}; S:{}", cairo_image_surface_get_width(surface), + cairo_image_surface_get_height(surface), cairo_image_surface_get_stride(surface)); + auto data = cairo_image_surface_get_data(surface); + FILE* write_ptr; + write_ptr = fopen("test.bin", "wb"); // w for write, b for binary + fwrite(data, len, 1, write_ptr); // return {data, data + len}; } @@ -72,6 +78,35 @@ int Label::getLayoutWidth() { return mLayoutWidth; } +void Label::configureLayout(PangoLayout* layout, const std::string& text, PangoFontDescription* fontDesc) { + pango_layout_set_font_description(layout, fontDesc); + pango_layout_set_text(layout, text.c_str(), static_cast(text.length())); + pango_layout_set_height(layout, getNumLines(text) * -1); +} + +void Label::applyHorizontalAlignment(PangoLayout* layout) { + switch (mHAlign) { + case HAlignPosition::LEFT: + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + break; + case HAlignPosition::RIGHT: + pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); + break; + case HAlignPosition::JUSTIFY: + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + pango_layout_set_justify(layout, true); +#if PANGO_VERSION_MAJOR >= 1 && PANGO_VERSION_MINOR >= 50 + pango_layout_set_justify_last_line(layout, true); +#endif + break; + case HAlignPosition::CENTER: + [[fallthrough]]; + default: + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + break; + } +} + bool Label::create(PrintableText printableText) { setFontFamily(printableText.fontFamily); setFontSize(printableText.fontSize); @@ -80,79 +115,78 @@ bool Label::create(PrintableText printableText) { } bool Label::create(const std::string& labelText) { - // TODO: we need to create a custom fontconfig here so that Noto Emoji does not load the systems default - // fontconfig here. For this, we need to create a PangoFcFontMap and a custom FcConfig + // TODO: we need to create a custom font config here so that Noto Emoji does not load the systems default + // font config here. For this, we need to create a PangoFcFontMap and a custom FcConfig // see: https://docs.gtk.org/PangoFc/method.FontMap.set_config.html // see: https://gist.github.com/CallumDev/7c66b3f9cf7a876ef75f + + // Create a temporary surface for layout size calculations + auto* tempSurface = cairo_image_surface_create(CAIRO_FORMAT_A8, 1, 1); + auto* tempCr = cairo_create(tempSurface); + auto* tempPangoCtx = pango_cairo_create_context(tempCr); + auto* tempPangoLyt = pango_layout_new(tempPangoCtx); + PangoFontDescription* regularFont = pango_font_description_new(); pango_font_description_set_size(regularFont, static_cast(mFontSize * PANGO_SCALE)); pango_font_description_set_family(regularFont, mFontFamily.c_str()); - //pango_layout_set_single_paragraph_mode(mPangoLyt, true); // this will force a single line in the label width width -1 - pango_layout_set_height(mPangoLyt, getNumLines(labelText) * -1); - pango_layout_set_font_description(mPangoLyt, regularFont); - pango_layout_set_text(mPangoLyt, labelText.c_str(), static_cast(labelText.length())); - // Set horizontal alignment - switch (mHAlign) { - case HAlignPosition::LEFT: - pango_layout_set_alignment(mPangoLyt, PANGO_ALIGN_LEFT); - break; - case HAlignPosition::RIGHT: - pango_layout_set_alignment(mPangoLyt, PANGO_ALIGN_RIGHT); - break; - case HAlignPosition::JUSTIFY: - pango_layout_set_alignment(mPangoLyt, PANGO_ALIGN_LEFT); // not sure if needed - pango_layout_set_justify(mPangoLyt, true); -// only enabled in pango 1.50 and greater -#if PANGO_VERSION_MAJOR >= 1 && PANGO_VERSION_MINOR >= 50 - pango_layout_set_justify_last_line(mPangoLyt, true); -#endif - break; - case HAlignPosition::CENTER: - [[fallthrough]]; - default: - pango_layout_set_alignment(mPangoLyt, PANGO_ALIGN_CENTER); - break; - } + // Configure temporary layout for size calculation + configureLayout(tempPangoLyt, labelText, regularFont); + applyHorizontalAlignment(tempPangoLyt); - // calculate label size for Cairo surface creation - pango_layout_get_size(mPangoLyt, &mLayoutWidth, &mLayoutHeight); + // Calculate label size from temporary layout + pango_layout_get_size(tempPangoLyt, &mLayoutWidth, &mLayoutHeight); mLayoutWidth /= PANGO_SCALE; mLayoutHeight /= PANGO_SCALE; spdlog::debug("Layout width: {}, height: {}", mLayoutWidth, mLayoutHeight); - auto alignedWidth = mLayoutWidth + (8 - (mLayoutWidth % 8)); + //auto alignedWidth = mLayoutWidth + (8 - (mLayoutWidth % 8)); + //spdlog::debug("Aligned Layout width: {}, height: {}", alignedWidth, mLayoutHeight); - mSurface = cairo_image_surface_create(CAIRO_FORMAT_A8, alignedWidth, mPrinterHeight); - cairo_t* cr = cairo_create(mSurface); + // Clean up temporary resources + g_object_unref(tempPangoLyt); + g_object_unref(tempPangoCtx); + cairo_destroy(tempCr); + cairo_surface_destroy(tempSurface); + + // Now create the final surface and Pango context for actual rendering + mSurface.reset(cairo_image_surface_create(CAIRO_FORMAT_A8, mLayoutWidth, mPrinterHeight)); + cairo_t* cr = cairo_create(mSurface.get()); + mCairoCtx.reset(cr); + mPangoCtx.reset(pango_cairo_create_context(cr)); + mPangoLyt.reset(pango_layout_new(mPangoCtx.get())); + + // Configure final layout with same settings + configureLayout(mPangoLyt.get(), labelText, regularFont); + applyHorizontalAlignment(mPangoLyt.get()); // Adjust Cairo cursor position to respect the vertical alignment switch (mVAlign) { case VAlignPosition::TOP: break; case VAlignPosition::BOTTOM: - cairo_move_to(cr, 0.0, mPrinterHeight - mLayoutHeight); + cairo_move_to(mCairoCtx.get(), 0.0, mPrinterHeight - mLayoutHeight); break; case VAlignPosition::MIDDLE: - cairo_move_to(cr, 0.0, (mPrinterHeight - mLayoutHeight) / 2); + cairo_move_to(mCairoCtx.get(), 0.0, (mPrinterHeight - mLayoutHeight) / 2); [[fallthrough]]; default: break; } // Finally show the layout on the Cairo surface - pango_cairo_show_layout(cr, mPangoLyt); + pango_cairo_show_layout(mCairoCtx.get(), mPangoLyt.get()); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_surface_flush(mSurface); - cairo_destroy(cr); + cairo_set_source_rgb(mCairoCtx.get(), 0.0, 0.0, 0.0); + cairo_surface_flush(mSurface.get()); + // mCairoCtx smart pointer will handle cleanup return true; } void Label::writeToPng(const std::string& file) { if (mSurface) { - cairo_surface_flush(mSurface); - cairo_surface_write_to_png(mSurface, file.c_str()); + cairo_surface_flush(mSurface.get()); + cairo_surface_write_to_png(mSurface.get(), file.c_str()); } } @@ -178,9 +212,6 @@ void Label::setText(const std::string& text) { Label::~Label() { spdlog::debug("Image dtor..."); - g_object_unref(mPangoCtx); - g_object_unref(mPangoLyt); - g_object_unref(mFontMap); - cairo_surface_destroy(mSurface); + // RAII smart pointers handle cleanup automatically } } // namespace ptprnt::graphics \ No newline at end of file diff --git a/src/graphics/Label.hpp b/src/graphics/Label.hpp index 3b77c1b..2dc6bc1 100644 --- a/src/graphics/Label.hpp +++ b/src/graphics/Label.hpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -31,6 +32,28 @@ namespace ptprnt::graphics { +// Custom deleters for Cairo/Pango resources +struct CairoSurfaceDeleter { + void operator()(cairo_surface_t* surface) const { + if (surface) + cairo_surface_destroy(surface); + } +}; + +struct CairoDeleter { + void operator()(cairo_t* cr) const { + if (cr) + cairo_destroy(cr); + } +}; + +struct GObjectDeleter { + void operator()(gpointer obj) const { + if (obj) + g_object_unref(obj); + } +}; + class Label : public ILabel { public: Label(const uint16_t heightPixel); @@ -60,13 +83,14 @@ class Label : public ILabel { // methods [[nodiscard]] uint8_t getNumLines(std::string_view str); [[nodiscard]] PangoFontMap* createCustomFontMap(); - // members - // TODO: convert raw pointers here into std::unique_ptr with custom deleters, calling g_object_unref() - cairo_surface_t* mSurface{nullptr}; - cairo_t* mCairoCtx{nullptr}; - PangoContext* mPangoCtx{nullptr}; - PangoLayout* mPangoLyt{nullptr}; - PangoFontMap* mFontMap{nullptr}; + void configureLayout(PangoLayout* layout, const std::string& text, PangoFontDescription* fontDesc); + void applyHorizontalAlignment(PangoLayout* layout); + + std::unique_ptr mSurface{nullptr}; + std::unique_ptr mCairoCtx{nullptr}; + std::unique_ptr mPangoCtx{nullptr}; + std::unique_ptr mPangoLyt{nullptr}; + std::unique_ptr mFontMap{nullptr}; double mFontSize{DEFAULT_FONT_SIZE}; std::string mFontFamily{DEFAULT_FONT_FAMILY}; HAlignPosition mHAlign = HAlignPosition::LEFT; diff --git a/src/graphics/Monochrome.cpp b/src/graphics/Monochrome.cpp index 9de0f2c..333e114 100644 --- a/src/graphics/Monochrome.cpp +++ b/src/graphics/Monochrome.cpp @@ -25,71 +25,85 @@ #include namespace ptprnt::graphics { -Monochrome::Monochrome(const std::vector& grayscale, uint32_t width, uint32_t height) - : mPixels(grayscale), mWidth(width), mHeight(height) {} +// Constructor from grayscale data +MonochromeData::MonochromeData(const std::vector& grayscale, uint32_t width, uint32_t height, + Orientation orient) + : stride(0), + orientation(orient), + width(width), + height(height), + mPixels(grayscale), + mIsProcessed(false) {} -Monochrome::Monochrome(const std::span grayscale, uint32_t width, uint32_t height) - : mPixels(grayscale.begin(), grayscale.end()), mWidth(width), mHeight(height) {} +MonochromeData::MonochromeData(const std::span grayscale, uint32_t width, uint32_t height, + Orientation orient) + : stride(0), + orientation(orient), + width(width), + height(height), + mPixels(grayscale.begin(), grayscale.end()), + mIsProcessed(false) {} -void Monochrome::setThreshold(uint8_t threshhold) { - mThreshhold = threshhold; +void MonochromeData::setThreshold(uint8_t threshold) { + mThreshold = threshold; + mIsProcessed = false; // Mark as needing reprocessing } -void Monochrome::invert(bool shouldInvert) { +void MonochromeData::invert(bool shouldInvert) { mShouldInvert = shouldInvert; + mIsProcessed = false; // Mark as needing reprocessing } -std::vector Monochrome::get() { - // Calculate output size for packed format: (width + 7) / 8 bytes per row - uint32_t stride = (mWidth + 7) / 8; - size_t outputSize = stride * mHeight; - std::vector outPixels(outputSize, 0); +MonochromeData MonochromeData::get() { + if (!mIsProcessed) { + processGrayscaleToMonochrome(); + mIsProcessed = true; + } - // Pack pixels row by row for correct 2D layout - for (uint32_t y = 0; y < mHeight; ++y) { - for (uint32_t x = 0; x < mWidth; ++x) { - size_t pixelIndex = y * mWidth + x; // Row-major index in input - size_t byteIndex = y * stride + x / 8; // Byte index in packed output - size_t bitIndex = 7 - (x % 8); // MSB first + // Return a copy of the processed data + MonochromeData result; + result.bytes = bytes; + result.stride = stride; + result.orientation = orientation; + result.width = width; + result.height = height; + result.mIsProcessed = true; + return result; +} - // Convert grayscale pixel to bit based on threshold - bool pixelOn = mPixels[pixelIndex] > mThreshhold; - if (mShouldInvert) { - pixelOn = !pixelOn; - } +void MonochromeData::processGrayscaleToMonochrome() { + // Calculate stride based on packed monochrome data (1 bit per pixel, 8 pixels per byte) + stride = static_cast((width + 7) / 8); - if (pixelOn) { - outPixels[byteIndex] |= (1 << bitIndex); + // Create the monochrome byte array + bytes.clear(); + bytes.resize(stride * height, 0); + + // Convert grayscale to monochrome + for (uint32_t y = 0; y < height; ++y) { + for (uint32_t x = 0; x < width; ++x) { + uint32_t pixelIndex = y * width + x; + if (pixelIndex < mPixels.size()) { + uint8_t pixelValue = mPixels[pixelIndex]; + + // Apply threshold + bool isSet = pixelValue >= mThreshold; + + // Apply inversion if needed + if (mShouldInvert) { + isSet = !isSet; + } + + // Set the bit in the monochrome data + if (isSet) { + setBit(x, y, true); + } } } } - - 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; -} - -MonochromeData Monochrome::getMonochromeData() { - auto processedBytes = get(); - // Calculate stride based on packed monochrome data (1 bit per pixel, 8 pixels per byte) - auto stride = static_cast((mWidth + 7) / 8); - return {std::move(processedBytes), stride, Orientation::LANDSCAPE, mWidth, mHeight}; -} - -// MonochromeData transformation methods implementation +// Transformation methods implementation void MonochromeData::transformTo(Orientation targetOrientation) { if (orientation == targetOrientation) { return; // No transformation needed @@ -151,7 +165,7 @@ void MonochromeData::setBit(uint32_t x, uint32_t y, bool value) { } std::vector MonochromeData::createRotatedData(Orientation targetOrientation) const { - uint32_t newWidth, newHeight; + uint32_t newWidth = 0, newHeight = 0; // Determine new dimensions switch (targetOrientation) { @@ -182,8 +196,8 @@ std::vector MonochromeData::createRotatedData(Orientation targetOrienta // Copy pixels with appropriate transformation for (uint32_t y = 0; y < height; ++y) { for (uint32_t x = 0; x < width; ++x) { - bool pixel = getBit(x, y); - uint32_t newX, newY; + bool pixel = getBit(x, y); + uint32_t newX = 0, newY = 0; switch (targetOrientation) { case Orientation::LANDSCAPE: diff --git a/src/graphics/Monochrome.hpp b/src/graphics/Monochrome.hpp index f87f69a..958deb7 100644 --- a/src/graphics/Monochrome.hpp +++ b/src/graphics/Monochrome.hpp @@ -34,19 +34,38 @@ enum class Orientation { PORTRAIT_FLIPPED = 3 // 270 degrees clockwise (90 counter-clockwise) }; -struct MonochromeData { - std::vector bytes; - uint32_t stride; - Orientation orientation; - uint32_t width; // Width in pixels - uint32_t height; // Height in pixels - +class MonochromeData { + public: + // Constructors MonochromeData() : stride(0), orientation(Orientation::LANDSCAPE), width(0), height(0) {} MonochromeData(std::vector data, uint32_t stride_bytes, Orientation orient = Orientation::LANDSCAPE, uint32_t w = 0, uint32_t h = 0) : bytes(std::move(data)), stride(stride_bytes), orientation(orient), width(w), height(h) {} + // Constructor from grayscale data (replaces old Monochrome class) + MonochromeData(const std::vector& grayscale, uint32_t width, uint32_t height, + Orientation orient = Orientation::LANDSCAPE); + MonochromeData(const std::span grayscale, uint32_t width, uint32_t height, + Orientation orient = Orientation::LANDSCAPE); + + ~MonochromeData() = default; + + // Copy constructor and assignment + MonochromeData(const MonochromeData&) = default; + MonochromeData& operator=(const MonochromeData&) = default; + + // Move constructor and assignment + MonochromeData(MonochromeData&&) = default; + MonochromeData& operator=(MonochromeData&&) = default; + + // Configuration methods + void setThreshold(uint8_t threshold); + void invert(bool shouldInvert); + + // Get processed monochrome data + MonochromeData get(); + // Transform the image data to the target orientation void transformTo(Orientation targetOrientation); @@ -57,25 +76,25 @@ struct MonochromeData { [[nodiscard]] bool getBit(uint32_t x, uint32_t y) const; void setBit(uint32_t x, uint32_t y, bool value); [[nodiscard]] std::vector createRotatedData(Orientation targetOrientation) const; -}; -class Monochrome { - public: - Monochrome(const std::vector& grayscale, uint32_t width, uint32_t height); - Monochrome(const std::span grayscale, uint32_t width, uint32_t height); - ~Monochrome() = default; - - void setThreshold(uint8_t); - void invert(bool shouldInvert); - void visualize(); - std::vector get(); - MonochromeData getMonochromeData(); + // Public member access for backward compatibility + std::vector bytes; + uint32_t stride; + Orientation orientation; + uint32_t width; // Width in pixels + uint32_t height; // Height in pixels private: - std::vector mPixels; - uint32_t mWidth; - uint32_t mHeight; - uint8_t mThreshhold = UINT8_MAX / 2; - bool mShouldInvert = false; + // Processing parameters (for old Monochrome class compatibility) + std::vector mPixels; // Original grayscale pixels + uint8_t mThreshold = UINT8_MAX / 2; + bool mShouldInvert = false; + bool mIsProcessed = false; // Flag to indicate if conversion has been done + + // Helper method to convert grayscale to monochrome + void processGrayscaleToMonochrome(); }; + +// For backward compatibility, create a type alias +using Monochrome = MonochromeData; } // namespace ptprnt::graphics \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index ee66606..2d4fa05 100644 --- a/src/meson.build +++ b/src/meson.build @@ -6,7 +6,7 @@ ptprnt_hpps = files ( 'libusbwrap/UsbDevice.hpp', 'interface/IPrinterDriver.hpp', 'interface/IPrinterTypes.hpp', - 'P700Printer.hpp', + 'printers/P700Printer.hpp', 'PtouchPrint.hpp', 'PrinterDriverFactory.hpp', 'graphics/Bitmap.hpp', @@ -17,7 +17,7 @@ ptprnt_hpps = files ( ptprnt_srcs = files ( 'PtouchPrint.cpp', 'PrinterDriverFactory.cpp', - 'P700Printer.cpp', + 'printers/P700Printer.cpp', 'graphics/Label.cpp', 'graphics/Bitmap.cpp', 'graphics/Monochrome.cpp', diff --git a/src/P700Printer.cpp b/src/printers/P700Printer.cpp similarity index 92% rename from src/P700Printer.cpp rename to src/printers/P700Printer.cpp index dd80361..3c900ad 100644 --- a/src/P700Printer.cpp +++ b/src/printers/P700Printer.cpp @@ -27,9 +27,9 @@ #include #include -#include "graphics/Bitmap.hpp" -#include "graphics/Monochrome.hpp" -#include "libusbwrap/LibUsbTypes.hpp" +#include "../graphics/Bitmap.hpp" +#include "../graphics/Monochrome.hpp" +#include "../libusbwrap/LibUsbTypes.hpp" #include "spdlog/fmt/bin_to_hex.h" // as long as DRYRUN is defined, no data is actually send to the printer, we need to save some tape ;) @@ -121,30 +121,29 @@ bool P700Printer::detachUsbDevice() { bool P700Printer::printBitmap(const graphics::Bitmap& bitmap) { // Convert bitmap to MonochromeData and delegate to printMonochromeData - auto pixels = bitmap.getPixelsCpy(); - auto mono = graphics::Monochrome(pixels, bitmap.getWidth(), bitmap.getHeight()); - auto monoData = mono.getMonochromeData(); - + auto pixels = bitmap.getPixelsCpy(); + auto mono = graphics::Monochrome(pixels, bitmap.getWidth(), bitmap.getHeight()); + auto monoData = mono.get(); + return printMonochromeData(monoData); } bool P700Printer::printMonochromeData(const graphics::MonochromeData& data) { #ifdef DRYRUN - spdlog::debug("DRYRUN enabled"); - data.visualize(); + spdlog::debug("DRYRUN enabled, printing nothing"); #endif - + send(p700::commands::RASTER_START); std::vector rastercmd(4); rastercmd[0] = 0x47; rastercmd[1] = 0x00; // size +1 rastercmd[2] = 0x00; rastercmd[3] = 0x00; // size -1 - + // Process data column by column for the printer for (uint32_t col = 0; col < data.width; col++) { std::vector columnData; - + // Extract column data bit by bit for (uint32_t row = 0; row < data.height; row += 8) { uint8_t byte = 0; @@ -155,19 +154,19 @@ bool P700Printer::printMonochromeData(const graphics::MonochromeData& data) { } columnData.push_back(byte); } - + std::vector buf; buf.insert(buf.begin(), rastercmd.begin(), rastercmd.end()); buf.insert(std::next(buf.begin(), 4), columnData.begin(), columnData.end()); buf[1] = columnData.size() + 1; buf[3] = columnData.size() - 1; - + if (!send(buf)) { spdlog::error("Error sending buffer to printer"); break; } } - + send(p700::commands::EJECT); return true; } @@ -175,9 +174,10 @@ bool P700Printer::printMonochromeData(const graphics::MonochromeData& data) { bool P700Printer::printLabel(std::unique_ptr label) { // Convert label directly to MonochromeData auto pixels = label->getRaw(); - auto mono = graphics::Monochrome(pixels, label->getWidth(), label->getHeight()); - auto monoData = mono.getMonochromeData(); - + auto mono = graphics::Monochrome(pixels, label->getWidth(), label->getHeight(), graphics::Orientation::PORTRAIT); + auto monoData = mono.get(); + monoData.visualize(); + spdlog::debug("Label has {}x{}px size", label->getWidth(), label->getHeight()); return printMonochromeData(monoData); } diff --git a/src/P700Printer.hpp b/src/printers/P700Printer.hpp similarity index 94% rename from src/P700Printer.hpp rename to src/printers/P700Printer.hpp index 1309db6..5ebb6b4 100644 --- a/src/P700Printer.hpp +++ b/src/printers/P700Printer.hpp @@ -23,10 +23,10 @@ #include #include -#include "interface/IPrinterDriver.hpp" -#include "interface/IPrinterTypes.hpp" -#include "libusbwrap/LibUsbTypes.hpp" -#include "libusbwrap/interface/IUsbDevice.hpp" +#include "../interface/IPrinterDriver.hpp" +#include "../interface/IPrinterTypes.hpp" +#include "../libusbwrap/LibUsbTypes.hpp" +#include "../libusbwrap/interface/IUsbDevice.hpp" #pragma once diff --git a/tests/monochrome_test/monochrome_test.cpp b/tests/monochrome_test/monochrome_test.cpp index 93a153f..2b5decf 100644 --- a/tests/monochrome_test/monochrome_test.cpp +++ b/tests/monochrome_test/monochrome_test.cpp @@ -29,7 +29,7 @@ TEST(basic_test, Monochrome_convertGrayscale_yieldsMonochrome) { auto mono = ptprnt::graphics::Monochrome(pixels, 16, 1); auto out = mono.get(); - EXPECT_EQ(out, expected); + EXPECT_EQ(out.bytes, expected); } TEST(basic_test, Monochrome_convertInvertedGrayscale_yieldsInvertedMonochrome) { @@ -41,7 +41,7 @@ TEST(basic_test, Monochrome_convertInvertedGrayscale_yieldsInvertedMonochrome) { mono.invert(true); auto out = mono.get(); - EXPECT_EQ(out, expected); + EXPECT_EQ(out.bytes, expected); } TEST(basic_test, Monochrome_convertWithCustomThreshhold_yieldsMonochromeRespectingThreshhold) { @@ -53,7 +53,7 @@ TEST(basic_test, Monochrome_convertWithCustomThreshhold_yieldsMonochromeRespecti mono.setThreshold(16); auto out = mono.get(); - EXPECT_EQ(out, expected); + EXPECT_EQ(out.bytes, expected); } TEST(basic_test, Monochrome_convertNonAlignedPixels_spillsOverIntoNewByte) { @@ -67,14 +67,14 @@ TEST(basic_test, Monochrome_convertNonAlignedPixels_spillsOverIntoNewByte) { auto mono = ptprnt::graphics::Monochrome(pixels, 17, 1); auto out = mono.get(); - EXPECT_EQ(out, expected); + EXPECT_EQ(out.bytes, expected); } TEST(MonochromeData_test, MonochromeData_getMonochromeData_returnsStructWithCorrectData) { const std::vector pixels({0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00}); auto mono = ptprnt::graphics::Monochrome(pixels, 8, 1); - auto monoData = mono.getMonochromeData(); + auto monoData = mono.get(); EXPECT_EQ(monoData.bytes.size(), 1); EXPECT_EQ(monoData.bytes[0], 0b10101010); @@ -90,7 +90,7 @@ TEST(MonochromeData_test, MonochromeData2x2_transformToPortrait_rotatesCorrectly const std::vector pixels({0xFF, 0x00, 0x00, 0xFF}); auto mono = ptprnt::graphics::Monochrome(pixels, 2, 2); - auto monoData = mono.getMonochromeData(); + auto monoData = mono.get(); monoData.transformTo(ptprnt::graphics::Orientation::PORTRAIT); @@ -114,7 +114,7 @@ TEST(MonochromeData_test, MonochromeData3x2_transformToPortrait_rotatesCorrectly const std::vector pixels({0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF}); auto mono = ptprnt::graphics::Monochrome(pixels, 3, 2); - auto monoData = mono.getMonochromeData(); + auto monoData = mono.get(); monoData.transformTo(ptprnt::graphics::Orientation::PORTRAIT); @@ -141,7 +141,7 @@ TEST(MonochromeData_test, MonochromeData3x2_transformToPortrait_rotatesCorrectly const std::vector pixels({0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF}); auto mono = ptprnt::graphics::Monochrome(pixels, 3, 2); - auto monoData = mono.getMonochromeData(); + auto monoData = mono.get(); monoData.transformTo(ptprnt::graphics::Orientation::PORTRAIT_FLIPPED);