/* 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 "PtouchPrint.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CLI/Option.hpp" #include "PrinterDriverFactory.hpp" #include "graphics/Label.hpp" #include "graphics/interface/ILabel.hpp" #include "libusbwrap/UsbDeviceFactory.hpp" namespace ptprnt { PtouchPrint::PtouchPrint(const char* versionString) : mVersionString{versionString} {} int PtouchPrint::init(int argc, char** argv) { setupCliParser(); try { mApp.parse(argc, argv); } catch (const CLI::ParseError& e) { mApp.exit(e); return -1; } if (mVerboseFlag) { setupLogger(spdlog::level::debug); } else { setupLogger(spdlog::level::warn); } if (!mUsbDeviceFactory.init()) { spdlog::error("Could not initialize libusb"); return -1; } return 0; } int PtouchPrint::run() { spdlog::info("ptprnt version {}", mVersionString); SPDLOG_TRACE("testing trace"); mDetectedPrinters = getCompatiblePrinters(); auto numFoundPrinters = mDetectedPrinters.size(); if (numFoundPrinters == 0) { spdlog::error("No compatible printers found, please make sure that they are turned on and connected"); return -1; } else if (numFoundPrinters > 1) { spdlog::warn("Found more than one compatible printer. Currently not supported."); return -1; } auto printer = mDetectedPrinters[0]; const auto printerUsbId = printer->getUsbId(); auto devices = mUsbDeviceFactory.findDevices(printerUsbId.first, printerUsbId.second); if (devices.size() != 1) { spdlog::warn("Found more than one device of the same printer on bus. Currently not supported"); return -1; } printer->attachUsbDevice(std::move(devices[0])); auto status = printer->getPrinterStatus(); spdlog::info("Detected tape width is {}mm", status.tapeWidthMm); if (0 == mCommands.size()) { spdlog::warn("No command specified, nothing to do..."); return 0; } auto label = graphics::Label(); std::string labelText{}; for (const auto& [cmd, value] : mCommands) { switch (cmd) { case CliCmdType::Text: if (labelText.empty()) { labelText = value; } else { labelText = labelText + '\n' + value; } break; case CliCmdType::Font: spdlog::debug("Setting font to {}", value); label.setFontFamily(value); break; case CliCmdType::FontSize: spdlog::debug("Setting font size to {}", std::stod(value)); label.setFontSize(std::stod(value)); break; case CliCmdType::HAlign: spdlog::debug("Setting text horizontal alignment to {}", value); { auto hPos = HALignPositionMap.find(value); if (hPos == HALignPositionMap.end()) { spdlog::warn("Invalid horizontal alignment specified!"); label.setHAlign(HAlignPosition::UNKNOWN); } else { label.setHAlign(hPos->second); } } break; case CliCmdType::VAlign: spdlog::debug("Setting text vertical alignment to {}", value); { auto vPos = VALignPositionMap.find(value); if (vPos == VALignPositionMap.end()) { spdlog::warn("Invalid verical alignment specified!"); label.setVAlign(VAlignPosition::UNKNOWN); } else { label.setVAlign(vPos->second); } } break; case CliCmdType::None: [[fallthrough]]; default: spdlog::warn("This command is currently not supported."); break; } } if (!printer->print()) { spdlog::error("An error occured while printing"); return -1; } label.create(labelText, 128); label.writeToPng("./testlabel.png"); return 0; } std::vector> PtouchPrint::getCompatiblePrinters() { auto usbDevs = mUsbDeviceFactory.findAllDevices(); auto driverFactory = std::make_unique(); std::vector> foundPrinterDrivers{}; for (auto& usbDev : usbDevs) { auto driver = driverFactory->create(usbDev->getUsbId()); if (driver != nullptr) { foundPrinterDrivers.push_back(driver); } } return foundPrinterDrivers; } void PtouchPrint::setupLogger(spdlog::level::level_enum lvl) { auto consoleSink = std::make_shared(); consoleSink->set_level(lvl); if (spdlog::level::level_enum::debug == lvl || spdlog::level::level_enum::trace == lvl) { // This will enable file and line number for debug and trace macros consoleSink->set_pattern("%^%L:%$ %v (%s:%#)"); } else { consoleSink->set_pattern("%^%L:%$ %v"); } auto fileSink = std::make_shared("ptprnt.log", true); fileSink->set_level(spdlog::level::trace); fileSink->set_pattern("%Y-%m-%d %H:%m:%S:%e [pid:%P tid:%t] [%^%l%$] %v (%@)"); std::vector sinks{consoleSink, fileSink}; auto logger = std::make_shared("default_logger", sinks.begin(), sinks.end()); logger->set_level(spdlog::level::trace); spdlog::set_default_logger(logger); } void PtouchPrint::setupCliParser() { auto printVersion = [this](std::size_t) { fmt::print("ptprnt version: {}\n", mVersionString); }; // General options mApp.add_flag("-v,--verbose", mVerboseFlag, "Enable verbose output"); mApp.add_flag("-V,--version", printVersion, "Prints the ptprnt's version"); // Text printing options mApp.add_option("-t,--text", "Text to print (can be used multple times, use formatting options before to " "influence text layout)") ->group("Printing") ->multi_option_policy(CLI::MultiOptionPolicy::TakeAll) ->trigger_on_parse() ->each([this](std::string text) { mCommands.emplace_back(CliCmdType::Text, text); }); mApp.add_option("-f,--font", "Font used for the following text occurences") ->group("Text printing ") ->multi_option_policy(CLI::MultiOptionPolicy::TakeFirst) ->trigger_on_parse() ->each([this](std::string font) { mCommands.emplace_back(CliCmdType::Font, font); }); mApp.add_option("-s,--fontsize", "Font size of the following text occurences") ->group("Text printing ") ->multi_option_policy(CLI::MultiOptionPolicy::TakeFirst) ->trigger_on_parse() ->each([this](std::string size) { mCommands.emplace_back(CliCmdType::FontSize, size); }); mApp.add_option("--valign", "Vertical alignment of the following text occurences") ->group("Text printing ") ->multi_option_policy(CLI::MultiOptionPolicy::TakeFirst) ->trigger_on_parse() ->transform([](std::string in) -> std::string { std::unordered_set validValignOptions{"top", "middle", "bottom"}; std::transform(in.begin(), in.end(), in.begin(), [](unsigned char c) { return std::tolower(c); }); if (validValignOptions.find(in) == validValignOptions.end()) { return {""}; } return in; }) ->each([this](std::string valign) { mCommands.emplace_back(CliCmdType::VAlign, valign); }); mApp.add_option("--halign", "Vertical alignment of the following text occurences") ->group("Text printing ") ->multi_option_policy(CLI::MultiOptionPolicy::TakeFirst) ->trigger_on_parse() ->transform([](std::string in) -> std::string { std::unordered_set validValignOptions{"left", "center", "right", "justify"}; std::transform(in.begin(), in.end(), in.begin(), [](unsigned char c) { return std::tolower(c); }); if (validValignOptions.find(in) == validValignOptions.end()) { return {""}; } return in; }) ->each([this](std::string halign) { mCommands.emplace_back(CliCmdType::HAlign, halign); }); // Image options mApp.add_option("-i,--image", "Image to print. Excludes all text printing ")->group("Image printing"); } } // namespace ptprnt