211 lines
6.0 KiB
C++
211 lines
6.0 KiB
C++
/*
|
|
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 <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "P700Printer.hpp"
|
|
|
|
#include <spdlog/spdlog.h>
|
|
|
|
#include <cstdint>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
#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 ;)
|
|
#define DRYRUN
|
|
|
|
namespace ptprnt::printer {
|
|
|
|
const PrinterInfo P700Printer::mInfo = {.driverName = "P700",
|
|
.name = "Brother P-touch P700",
|
|
.version = "v1.0",
|
|
.usbId{0x04f9, 0x2061},
|
|
.pixelLines = 128};
|
|
|
|
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;
|
|
send(p700::commands::GET_STATUS);
|
|
|
|
int tx = 0;
|
|
int tries = 0;
|
|
std::vector<uint8_t> recvBuf(32);
|
|
while (tries++ < MAX_TRIES_GET_STATUS) {
|
|
std::this_thread::sleep_for(100ms);
|
|
mUsbHndl->bulkTransfer(p700::commands::PRINTER_INFO[0], recvBuf, &tx, 0);
|
|
}
|
|
|
|
return PrinterStatus{.tapeWidthMm = recvBuf[10]};
|
|
}
|
|
|
|
const libusbwrap::usbId P700Printer::getUsbId() {
|
|
return mInfo.usbId;
|
|
}
|
|
|
|
bool P700Printer::attachUsbDevice(std::shared_ptr<libusbwrap::IUsbDevice> 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<graphics::ALPHA8>& bitmap) {
|
|
#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(p700::commands::RASTER_START);
|
|
std::vector<uint8_t> rastercmd(4);
|
|
rastercmd[0] = 0x47;
|
|
rastercmd[1] = 0x00; // size +1
|
|
rastercmd[2] = 0x00;
|
|
rastercmd[3] = 0x00; // size -1
|
|
for (unsigned int i = 0; i < bitmap.getWidth(); i++) {
|
|
auto bmcol = bitmap.getCol(i);
|
|
if (!bmcol) {
|
|
spdlog::error("Out of bounds bitmap access");
|
|
break;
|
|
}
|
|
auto monocol = graphics::Monochrome(*bmcol);
|
|
auto col = monocol.get();
|
|
|
|
std::vector<uint8_t> 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)) {
|
|
spdlog::error("Error sending buffer to printer");
|
|
break;
|
|
};
|
|
}
|
|
|
|
send(p700::commands::EJECT);
|
|
return true;
|
|
}
|
|
|
|
bool P700Printer::printLabel(std::unique_ptr<graphics::ILabel> label) {
|
|
// not quite sure if I should stack allocate Bitmap, but data is held on the heap anyway (std::vector).
|
|
auto bm = graphics::Bitmap<graphics::ALPHA8>(label->getWidth(), label->getHeight());
|
|
spdlog::debug("Label has {}x{}px size", label->getWidth(), label->getHeight());
|
|
bm.setPixels(label->getRaw());
|
|
printBitmap(bm);
|
|
return true;
|
|
}
|
|
|
|
bool P700Printer::print() {
|
|
send(p700::commands::LF);
|
|
send(p700::commands::FF);
|
|
send(p700::commands::EJECT);
|
|
return true;
|
|
}
|
|
|
|
bool P700Printer::send(const std::vector<uint8_t>& 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::trace("USB raw data(len {}): {}", data.size(), spdlog::to_hex(data));
|
|
#endif
|
|
|
|
if (tx != static_cast<int>(data.size())) {
|
|
spdlog::error("Could not transfer all data via USB bulk transfer. Only sent {} of {} bytes", tx, data.size());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool P700Printer::init() {
|
|
std::vector<uint8_t> cmd(102);
|
|
cmd[100] = 0x1b; /* ESC */
|
|
cmd[101] = 0x40; /* @ */
|
|
return send(cmd);
|
|
}
|
|
} // namespace ptprnt::printer
|