/*
ptrnt - print labels on linux
Copyright (C) 2023-2025 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 "cli/CliParser.hpp"
#include "cli/interface/ICliParser.hpp"
#include "constants.hpp"
#include "core/PrinterDriverFactory.hpp"
#include "core/PrinterService.hpp"
#include "core/interface/IPrinterService.hpp"
#include "graphics/LabelBuilder.hpp"
namespace ptprnt {
PtouchPrint::PtouchPrint(const char* versionString)
: PtouchPrint(versionString, std::make_unique(ptprnt::APP_DESC, versionString),
std::make_unique()) {}
PtouchPrint::PtouchPrint(const char* versionString, std::unique_ptr cliParser,
std::unique_ptr printerService)
: mVersionString(versionString), mCliParser(std::move(cliParser)), mPrinterService(std::move(printerService)) {}
PtouchPrint::~PtouchPrint() = default;
int PtouchPrint::init(int argc, char** argv) {
// Parse CLI arguments
int parseResult = mCliParser->parse(argc, argv);
if (parseResult != 0) {
// Pass through: positive = clean exit (help/version), negative = error
return parseResult;
}
// Setup logging based on CLI flags
setupLogger();
// Initialize printer service
if (!mPrinterService->initialize()) {
return -1;
}
return 0;
}
int PtouchPrint::run() {
spdlog::info("ptprnt version {}", mVersionString);
const auto& options = mCliParser->getOptions();
// Handle --list-all-drivers
if (options.listDrivers) {
return handleListDrivers() ? 0 : -1;
}
// Handle printing
return handlePrinting() ? 0 : -1;
}
void PtouchPrint::setupLogger() {
const auto& options = mCliParser->getOptions();
spdlog::level::level_enum level = spdlog::level::warn;
if (options.trace) {
level = spdlog::level::trace;
} else if (options.verbose) {
level = spdlog::level::debug;
}
auto consoleSink = std::make_shared();
consoleSink->set_level(level);
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);
}
bool PtouchPrint::handleListDrivers() {
auto driverFactory = std::make_unique();
auto drivers = driverFactory->listAllDrivers();
fmt::print("Available printer drivers:\n");
for (const auto& driver : drivers) {
fmt::print(" - {}\n", driver);
}
fmt::print("\nUse with: -p or --printer \n");
return true;
}
bool PtouchPrint::handlePrinting() {
const auto& options = mCliParser->getOptions();
// Select printer
auto printer = mPrinterService->selectPrinter(options.printerSelection);
if (!printer) {
spdlog::error("Failed to select printer");
return false;
}
// Get printer status
auto status = printer->getPrinterStatus();
spdlog::info("Detected tape width is {}mm", status.tapeWidthMm);
// Check if there are any commands
if (options.commands.empty()) {
spdlog::warn("No command specified, nothing to do...");
return true;
}
// Build label using LabelBuilder
graphics::LabelBuilder labelBuilder(printer->getPrinterInfo().pixelLines);
for (const auto& [cmdType, value] : options.commands) {
switch (cmdType) {
case cli::CommandType::Text:
labelBuilder.addText(value);
break;
case cli::CommandType::Font:
spdlog::debug("Setting font to {}", value);
labelBuilder.setFontFamily(value);
break;
case cli::CommandType::FontSize:
spdlog::debug("Setting font size to {}", std::stod(value));
labelBuilder.setFontSize(std::stod(value));
break;
case cli::CommandType::HAlign: {
spdlog::debug("Setting text horizontal alignment to {}", value);
auto hPos = HALignPositionMap.find(value);
if (hPos == HALignPositionMap.end()) {
spdlog::warn("Invalid horizontal alignment specified!");
labelBuilder.setHAlign(HAlignPosition::UNKNOWN);
} else {
labelBuilder.setHAlign(hPos->second);
}
break;
}
case cli::CommandType::VAlign: {
spdlog::debug("Setting text vertical alignment to {}", value);
auto vPos = VALignPositionMap.find(value);
if (vPos == VALignPositionMap.end()) {
spdlog::warn("Invalid vertical alignment specified!");
labelBuilder.setVAlign(VAlignPosition::UNKNOWN);
} else {
labelBuilder.setVAlign(vPos->second);
}
break;
}
case cli::CommandType::None:
[[fallthrough]];
default:
spdlog::warn("This command is currently not supported.");
break;
}
}
// Build and print the label
auto label = labelBuilder.build();
if (!mPrinterService->printLabel(std::move(label))) {
spdlog::error("An error occurred while printing");
return false;
}
return true;
}
} // namespace ptprnt