/* ptrnt - print labels on linux Copyright (C) 2023 Moritz Martinius This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "P700Printer.hpp" #include #include #include #include #include #include #include #include #include "graphics/Bitmap.hpp" #include "graphics/Image.hpp" #include "graphics/Monochrome.hpp" #include "libusb.h" #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 ;) //#define DRYRUN namespace ptprnt::printer { const PrinterInfo P700Printer::mInfo = {.driverName = "P700", .name = "Brother P-touch P700", .version = "v1.0", .usbId{0x04f9, 0x2061}}; P700Printer::~P700Printer() { detachUsbDevice(); if (mUsbHndl) { mUsbHndl->close(); } } const std::string_view P700Printer::getDriverName() { return mInfo.driverName; } const std::string_view P700Printer::getName() { return mInfo.name; } const std::string_view P700Printer::getVersion() { return mInfo.version; } const PrinterInfo P700Printer::getPrinterInfo() { return mInfo; } const PrinterStatus P700Printer::getPrinterStatus() { using namespace std::chrono_literals; std::vector getStatusCmd({0x1b, 0x69, 0x53}); // status info request send(getStatusCmd); int tx = 0; int tries = 0; std::vector recvBuf(32); while (tries++ < MAX_TRIES_GET_STATUS) { std::this_thread::sleep_for(100ms); mUsbHndl->bulkTransfer(commands["printerinfo"][0], recvBuf, &tx, 0); } return PrinterStatus{.tapeWidthMm = recvBuf[10]}; } const libusbwrap::usbId P700Printer::getUsbId() { return mInfo.usbId; } bool P700Printer::attachUsbDevice(std::shared_ptr usbHndl) { if (!usbHndl->open()) { spdlog::error("Unable to open USB device: {}", usbHndl->getLastErrorString()); return false; } if (!usbHndl->detachKernelDriver(0)) { spdlog::error("Device is already in use or couldn't be detached from kernel: {}", usbHndl->getLastErrorString()); return false; } if (!usbHndl->claimInterface(0)) { spdlog::error("Could not claim interface 0: {}", usbHndl->getLastErrorString()); return false; } mUsbHndl = std::move(usbHndl); return true; } bool P700Printer::detachUsbDevice() { if (!mUsbHndl) { spdlog::warn("No device to detach..."); return true; } if (!mUsbHndl->releaseInterface(0)) { spdlog::error("Could not release interface 0: {}", mUsbHndl->getLastErrorString()); return false; } return true; } bool P700Printer::printBitmap(const graphics::Bitmap& bitmap) { auto bm = graphics::Bitmap(512, 128); { auto img = graphics::Image(); bm.setPixels(std::vector(img.getRaw(), img.getRaw() + 512 * 128)); } 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); if (!bmcol) { spdlog::error("Out of bounds bitmap access"); break; } auto monocol = graphics::Monochrome(*bmcol); auto col = monocol.get(); std::vector buf(0); buf.insert(buf.begin(), rastercmd.begin(), rastercmd.end()); buf.insert(std::next(buf.begin(), 4), col.begin(), col.end()); buf[1] = col.size() + 1; buf[3] = col.size() - 1; if (!send(buf)) { break; }; } send(commands["eject"]); return true; } bool P700Printer::printText(const std::string& text, uint16_t fontSize) { send(commands["lf"]); send(commands["ff"]); send(commands["eject"]); return true; } bool P700Printer::send(std::vector& data) { if (mUsbHndl == nullptr || data.size() > 128) { spdlog::error("Invalid device handle or invalid data."); return false; } int tx = 0; #ifndef DRYRUN if (!mUsbHndl->bulkTransfer(0x02, data, &tx, 0)) { spdlog::error("Error writing command to Printer: {}", mUsbHndl->getLastErrorString()); return false; } #else tx = data.size(); spdlog::debug("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", tx, data.size()); return false; } return true; } bool P700Printer::init() { std::vector cmd(102); cmd[100] = 0x1b; /* ESC */ cmd[101] = 0x40; /* @ */ return send(cmd); } } // namespace ptprnt::printer