Fix the label corruption issue
All checks were successful
Build ptprnt / build (push) Successful in 4m43s

This commit is contained in:
2025-10-11 17:04:55 +02:00
parent 6e3a5bd12f
commit 59b3b34edc
6 changed files with 149 additions and 27 deletions

View File

@@ -1,5 +1,8 @@
{
"clangd.arguments": ["-background-index", "-compile-commands-dir=builddir/"],
"clangd.arguments": [
"-background-index",
"-compile-commands-dir=builddir/"
],
"editor.formatOnType": false,
"editor.formatOnSave": true,
"files.associations": {
@@ -84,4 +87,7 @@
"*.ipp": "cpp"
},
"clangd.onConfigChanged": "restart",
"cSpell.words": [
"ptrnt"
],
}

View File

@@ -271,8 +271,7 @@ void PtouchPrint::setupCliParser() {
"Select printer driver (default: auto). Use --list-all-drivers to see available options")
->default_val("auto");
mApp.add_flag("--list-all-drivers", mListDriversFlag,
"List all available printer drivers and exit");
mApp.add_flag("--list-all-drivers", mListDriversFlag, "List all available printer drivers and exit");
// Text printing options
mApp.add_option("-t,--text",
@@ -298,7 +297,7 @@ void PtouchPrint::setupCliParser() {
->trigger_on_parse()
->transform([](std::string in) -> std::string {
std::unordered_set<std::string> validValignOptions{"top", "middle", "bottom"};
std::transform(in.begin(), in.end(), in.begin(), [](unsigned char c) { return std::tolower(c); });
std::ranges::transform(in, in.begin(), [](unsigned char c) { return std::tolower(c); });
if (validValignOptions.find(in) == validValignOptions.end()) {
return {""};
}

View File

@@ -45,29 +45,49 @@ Label::Label(const uint16_t heightPixel)
std::vector<uint8_t> Label::getRaw() {
assert(mSurface != nullptr);
auto* surface = mSurface.get();
size_t len = cairo_image_surface_get_height(surface) * cairo_image_surface_get_stride(surface);
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));
int width = cairo_image_surface_get_width(surface);
int height = cairo_image_surface_get_height(surface);
int stride = cairo_image_surface_get_stride(surface);
spdlog::debug("Cairo Surface data: W: {}; H: {}; S:{}", width, height, stride);
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); //
// If stride equals width, we can return data directly
if (stride == width) {
size_t len = height * stride;
return {data, data + len};
}
// Otherwise, we need to copy row by row, removing stride padding
std::vector<uint8_t> result;
result.reserve(width * height);
for (int y = 0; y < height; ++y) {
uint8_t* row_start = data + (y * stride);
result.insert(result.end(), row_start, row_start + width);
}
spdlog::debug("getRaw: Removed stride padding, returning {} bytes ({}x{})", result.size(), width, height);
return result;
}
uint8_t Label::getNumLines(std::string_view strv) {
return std::count(strv.begin(), strv.end(), '\n');
}
int Label::getWidth() {
return mPrinterHeight;
// Return the actual Cairo surface width (which is the layout width)
return mLayoutWidth;
}
int Label::getHeight() {
return getLayoutWidth();
// Return the actual Cairo surface height (which is the printer height)
return mPrinterHeight;
}
int Label::getLayoutHeight() {

View File

@@ -20,10 +20,14 @@
#include "FakePrinter.hpp"
#include <spdlog/spdlog.h>
#include <cairo.h>
#include <cstdint>
#include <stdexcept>
#include <vector>
#include <chrono>
#include <iomanip>
#include <sstream>
#include "../graphics/Monochrome.hpp"
@@ -93,16 +97,30 @@ bool FakePrinter::printMonochromeData(const graphics::MonochromeData& data) {
spdlog::info("FakePrinter: Successfully 'printed' label ({}x{} pixels)",
mLastPrint->getWidth(), mLastPrint->getHeight());
// Save to timestamped PNG file
std::string filename = generateTimestampedFilename();
if (saveBitmapToPng(*mLastPrint, filename)) {
spdlog::info("FakePrinter: Saved output to {}", filename);
} else {
spdlog::error("FakePrinter: Failed to save output to {}", filename);
}
return true;
}
bool FakePrinter::printLabel(const std::unique_ptr<graphics::ILabel> label) {
// Convert label directly to MonochromeData
// getRaw() returns data in Cairo surface coordinates matching getWidth() × getHeight()
auto pixels = label->getRaw();
auto mono = graphics::Monochrome(pixels, label->getWidth(), label->getHeight(), graphics::Orientation::PORTRAIT);
// Create monochrome data in landscape orientation (as stored in Cairo surface)
auto mono = graphics::Monochrome(pixels, label->getWidth(), label->getHeight(), graphics::Orientation::LANDSCAPE);
auto monoData = mono.get();
spdlog::debug("FakePrinter: Label has {}x{} pixel size", label->getWidth(), label->getHeight());
// Transform to portrait orientation for printing
monoData.transformTo(graphics::Orientation::PORTRAIT);
spdlog::debug("FakePrinter: Label surface is {}x{}, transformed to portrait", label->getWidth(), label->getHeight());
return printMonochromeData(monoData);
}
@@ -179,10 +197,68 @@ bool FakePrinter::saveLastPrintToPng(const std::string& filename) const {
return false;
}
// For now, just log it - actual PNG saving would require Cairo/PNG library integration
spdlog::info("FakePrinter: Successfully 'saved' {}x{} bitmap to {} (simulated)",
mLastPrint->getWidth(), mLastPrint->getHeight(), filename);
return saveBitmapToPng(*mLastPrint, filename);
}
bool FakePrinter::saveBitmapToPng(const graphics::Bitmap<graphics::ALPHA8>& bitmap, const std::string& filename) const {
// Create Cairo surface from bitmap data
auto pixels = bitmap.getPixelsCpy();
uint16_t width = bitmap.getWidth();
uint16_t height = bitmap.getHeight();
// Cairo expects ARGB32 format, but we have ALPHA8
// Convert ALPHA8 (grayscale) to ARGB32
std::vector<uint32_t> argbPixels(width * height);
for (size_t i = 0; i < pixels.size(); i++) {
uint8_t gray = pixels[i];
// ARGB32 format: 0xAARRGGBB
// For grayscale: use gray value for R, G, B and 255 for alpha
argbPixels[i] = 0xFF000000 | (gray << 16) | (gray << 8) | gray;
}
// Create Cairo surface
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
cairo_surface_t* surface = cairo_image_surface_create_for_data(
reinterpret_cast<unsigned char*>(argbPixels.data()),
CAIRO_FORMAT_ARGB32,
width,
height,
stride
);
if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
spdlog::error("FakePrinter: Failed to create Cairo surface: {}",
cairo_status_to_string(cairo_surface_status(surface)));
cairo_surface_destroy(surface);
return false;
}
// Write to PNG file
cairo_status_t status = cairo_surface_write_to_png(surface, filename.c_str());
cairo_surface_destroy(surface);
if (status != CAIRO_STATUS_SUCCESS) {
spdlog::error("FakePrinter: Failed to write PNG file: {}", cairo_status_to_string(status));
return false;
}
return true;
}
std::string FakePrinter::generateTimestampedFilename() const {
// Get current time
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
// Format: fakelabel_YYYYMMDD_HHMMSS.png
std::stringstream ss;
ss << "fakelabel_"
<< std::put_time(std::localtime(&time), "%Y%m%d_%H%M%S")
<< ".png";
return ss.str();
}
} // namespace ptprnt::printer

View File

@@ -86,6 +86,20 @@ class FakePrinter : public ::ptprnt::IPrinterDriver {
*/
graphics::Bitmap<graphics::ALPHA8> simulatePrinting(const graphics::MonochromeData& data);
/**
* @brief Save bitmap to PNG file using Cairo
* @param bitmap The bitmap to save
* @param filename Output filename
* @return true if successful
*/
bool saveBitmapToPng(const graphics::Bitmap<graphics::ALPHA8>& bitmap, const std::string& filename) const;
/**
* @brief Generate timestamped filename for fake label output
* @return Filename like "fakelabel_20231011_123456.png"
*/
std::string generateTimestampedFilename() const;
std::unique_ptr<graphics::Bitmap<graphics::ALPHA8>> mLastPrint;
bool mHasAttachedDevice = false;
PrinterStatus mStatus{.tapeWidthMm = 12}; // Default to 12mm tape

View File

@@ -173,12 +173,19 @@ bool P700Printer::printMonochromeData(const graphics::MonochromeData& data) {
bool P700Printer::printLabel(std::unique_ptr<graphics::ILabel> label) {
// Convert label directly to MonochromeData
// getRaw() returns data in Cairo surface coordinates matching getWidth() × getHeight()
auto pixels = label->getRaw();
auto mono = graphics::Monochrome(pixels, label->getWidth(), label->getHeight(), graphics::Orientation::PORTRAIT);
// Create monochrome data in landscape orientation (as stored in Cairo surface)
auto mono = graphics::Monochrome(pixels, label->getWidth(), label->getHeight(), graphics::Orientation::LANDSCAPE);
auto monoData = mono.get();
// Transform to portrait orientation for printing
monoData.transformTo(graphics::Orientation::PORTRAIT);
spdlog::debug("Label surface is {}x{}, transformed to portrait", label->getWidth(), label->getHeight());
monoData.visualize();
spdlog::debug("Label has {}x{}px size", label->getWidth(), label->getHeight());
return printMonochromeData(monoData);
}