1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qcups_p.h"
5
6#include "qprintdevice_p.h"
7#include "qprintengine.h"
8
9QT_BEGIN_NAMESPACE
10
11using namespace Qt::StringLiterals;
12
13QT_IMPL_METATYPE_EXTERN_TAGGED(QCUPSSupport::JobHoldUntil,
14 QCUPSSupport__JobHoldUntil)
15QT_IMPL_METATYPE_EXTERN_TAGGED(QCUPSSupport::BannerPage,
16 QCUPSSupport__BannerPage)
17QT_IMPL_METATYPE_EXTERN_TAGGED(QCUPSSupport::PageSet, QCUPSSupport__PageSet)
18QT_IMPL_METATYPE_EXTERN_TAGGED(QCUPSSupport::PagesPerSheetLayout,
19 QCUPSSupport__PagesPerSheetLayout)
20QT_IMPL_METATYPE_EXTERN_TAGGED(QCUPSSupport::PagesPerSheet,
21 QCUPSSupport__PagesPerSheet)
22
23static QStringList cupsOptionsList(QPrinter *printer) noexcept
24{
25 return printer->printEngine()->property(PPK_CupsOptions).toStringList();
26}
27
28void setCupsOptions(QPrinter *printer, const QStringList &cupsOptions) noexcept
29{
30 printer->printEngine()->setProperty(PPK_CupsOptions, value: QVariant(cupsOptions));
31}
32
33void QCUPSSupport::setCupsOption(QPrinter *printer, const QString &option, const QString &value)
34{
35 QStringList cupsOptions = cupsOptionsList(printer);
36 if (cupsOptions.contains(str: option)) {
37 cupsOptions.replace(i: cupsOptions.indexOf(str: option) + 1, t: value);
38 } else {
39 cupsOptions.append(t: option);
40 cupsOptions.append(t: value);
41 }
42 setCupsOptions(printer, cupsOptions);
43}
44
45void QCUPSSupport::clearCupsOption(QPrinter *printer, const QString &option)
46{
47 QStringList cupsOptions = cupsOptionsList(printer);
48 // ### use const_iterator once QList::erase takes them
49 const QStringList::iterator it = std::find(first: cupsOptions.begin(), last: cupsOptions.end(), val: option);
50 if (it != cupsOptions.end()) {
51 Q_ASSERT(it + 1 < cupsOptions.end());
52 cupsOptions.erase(abegin: it, aend: it+1);
53 setCupsOptions(printer, cupsOptions);
54 }
55}
56
57void QCUPSSupport::clearCupsOptions(QPrinter *printer)
58{
59 setCupsOptions(printer, cupsOptions: QStringList());
60}
61
62static inline QString jobHoldToString(const QCUPSSupport::JobHoldUntil jobHold, QTime holdUntilTime)
63{
64 switch (jobHold) {
65 case QCUPSSupport::Indefinite:
66 return QStringLiteral("indefinite");
67 case QCUPSSupport::DayTime:
68 return QStringLiteral("day-time");
69 case QCUPSSupport::Night:
70 return QStringLiteral("night");
71 case QCUPSSupport::SecondShift:
72 return QStringLiteral("second-shift");
73 case QCUPSSupport::ThirdShift:
74 return QStringLiteral("third-shift");
75 case QCUPSSupport::Weekend:
76 return QStringLiteral("weekend");
77 case QCUPSSupport::SpecificTime:
78 if (!holdUntilTime.isNull()) {
79 // CUPS expects the time in UTC, user has entered in local time, so get the UTS equivalent
80 QDateTime localDateTime = QDateTime::currentDateTime();
81 // Check if time is for tomorrow in case of DST change overnight
82 if (holdUntilTime < localDateTime.time())
83 localDateTime = localDateTime.addDays(days: 1);
84 localDateTime.setTime(holdUntilTime);
85 return localDateTime.toUTC().time().toString(format: u"HH:mm");
86 }
87 // else fall through:
88 Q_FALLTHROUGH();
89 case QCUPSSupport::NoHold:
90 return QString();
91 }
92 Q_UNREACHABLE_RETURN(QString());
93}
94
95QCUPSSupport::JobHoldUntilWithTime QCUPSSupport::parseJobHoldUntil(const QString &jobHoldUntil)
96{
97 if (jobHoldUntil == "indefinite"_L1) {
98 return { QCUPSSupport::Indefinite, QTime() };
99 } else if (jobHoldUntil == "day-time"_L1) {
100 return { QCUPSSupport::DayTime, QTime() };
101 } else if (jobHoldUntil == "night"_L1) {
102 return { QCUPSSupport::Night, QTime() };
103 } else if (jobHoldUntil == "second-shift"_L1) {
104 return { QCUPSSupport::SecondShift, QTime() };
105 } else if (jobHoldUntil == "third-shift"_L1) {
106 return { QCUPSSupport::ThirdShift, QTime() };
107 } else if (jobHoldUntil == "weekend"_L1) {
108 return { QCUPSSupport::Weekend, QTime() };
109 }
110
111
112 QTime parsedTime = QTime::fromString(string: jobHoldUntil, QStringLiteral("h:m:s"));
113 if (!parsedTime.isValid())
114 parsedTime = QTime::fromString(string: jobHoldUntil, QStringLiteral("h:m"));
115 if (parsedTime.isValid()) {
116 // CUPS time is in UTC, user expects local time, so get the equivalent
117 QDateTime dateTimeUtc = QDateTime::currentDateTimeUtc();
118 dateTimeUtc.setTime(parsedTime);
119 return { QCUPSSupport::SpecificTime, dateTimeUtc.toLocalTime().time() };
120 }
121
122 return { QCUPSSupport::NoHold, QTime() };
123}
124
125ppd_option_t *QCUPSSupport::findPpdOption(const char *optionName, QPrintDevice *printDevice)
126{
127 ppd_file_t *ppd = qvariant_cast<ppd_file_t*>(v: printDevice->property(PDPK_PpdFile));
128
129 if (ppd) {
130 for (int i = 0; i < ppd->num_groups; ++i) {
131 ppd_group_t *group = &ppd->groups[i];
132
133 for (int i = 0; i < group->num_options; ++i) {
134 ppd_option_t *option = &group->options[i];
135
136 if (qstrcmp(str1: option->keyword, str2: optionName) == 0)
137 return option;
138 }
139 }
140 }
141
142 return nullptr;
143}
144
145void QCUPSSupport::setJobHold(QPrinter *printer, const JobHoldUntil jobHold, QTime holdUntilTime)
146{
147 const QString jobHoldUntilArgument = jobHoldToString(jobHold, holdUntilTime);
148 if (!jobHoldUntilArgument.isEmpty()) {
149 setCupsOption(printer,
150 QStringLiteral("job-hold-until"),
151 value: jobHoldUntilArgument);
152 } else {
153 clearCupsOption(printer, QStringLiteral("job-hold-until"));
154 }
155}
156
157void QCUPSSupport::setJobBilling(QPrinter *printer, const QString &jobBilling)
158{
159 setCupsOption(printer, QStringLiteral("job-billing"), value: jobBilling);
160}
161
162void QCUPSSupport::setJobPriority(QPrinter *printer, int priority)
163{
164 setCupsOption(printer, QStringLiteral("job-priority"), value: QString::number(priority));
165}
166
167static inline QString bannerPageToString(const QCUPSSupport::BannerPage bannerPage)
168{
169 switch (bannerPage) {
170 case QCUPSSupport::NoBanner: return QStringLiteral("none");
171 case QCUPSSupport::Standard: return QStringLiteral("standard");
172 case QCUPSSupport::Unclassified: return QStringLiteral("unclassified");
173 case QCUPSSupport::Confidential: return QStringLiteral("confidential");
174 case QCUPSSupport::Classified: return QStringLiteral("classified");
175 case QCUPSSupport::Secret: return QStringLiteral("secret");
176 case QCUPSSupport::TopSecret: return QStringLiteral("topsecret");
177 }
178 Q_UNREACHABLE_RETURN(QString());
179}
180
181static inline QCUPSSupport::BannerPage stringToBannerPage(const QString &bannerPage)
182{
183 if (bannerPage == "none"_L1) return QCUPSSupport::NoBanner;
184 else if (bannerPage == "standard"_L1) return QCUPSSupport::Standard;
185 else if (bannerPage == "unclassified"_L1) return QCUPSSupport::Unclassified;
186 else if (bannerPage == "confidential"_L1) return QCUPSSupport::Confidential;
187 else if (bannerPage == "classified"_L1) return QCUPSSupport::Classified;
188 else if (bannerPage == "secret"_L1) return QCUPSSupport::Secret;
189 else if (bannerPage == "topsecret"_L1) return QCUPSSupport::TopSecret;
190
191 return QCUPSSupport::NoBanner;
192}
193
194QCUPSSupport::JobSheets QCUPSSupport::parseJobSheets(const QString &jobSheets)
195{
196 JobSheets result;
197
198 const QStringList parts = jobSheets.split(sep: u',');
199 if (parts.size() == 2) {
200 result.startBannerPage = stringToBannerPage(bannerPage: parts[0]);
201 result.endBannerPage = stringToBannerPage(bannerPage: parts[1]);
202 }
203
204 return result;
205}
206
207void QCUPSSupport::setBannerPages(QPrinter *printer, const BannerPage startBannerPage, const BannerPage endBannerPage)
208{
209 const QString startBanner = bannerPageToString(bannerPage: startBannerPage);
210 const QString endBanner = bannerPageToString(bannerPage: endBannerPage);
211
212 setCupsOption(printer, QStringLiteral("job-sheets"), value: startBanner + u',' + endBanner);
213}
214
215void QCUPSSupport::setPageSet(QPrinter *printer, const PageSet pageSet)
216{
217 QString pageSetString;
218
219 switch (pageSet) {
220 case OddPages:
221 pageSetString = QStringLiteral("odd");
222 break;
223 case EvenPages:
224 pageSetString = QStringLiteral("even");
225 break;
226 case AllPages:
227 pageSetString = QStringLiteral("all");
228 break;
229 }
230
231 setCupsOption(printer, QStringLiteral("page-set"), value: pageSetString);
232}
233
234void QCUPSSupport::setPagesPerSheetLayout(QPrinter *printer, const PagesPerSheet pagesPerSheet,
235 const PagesPerSheetLayout pagesPerSheetLayout)
236{
237 // WARNING: the following trick (with a [2]-extent) only works as
238 // WARNING: long as there's only one two-digit number in the list
239 // WARNING: and it is the last one (before the "\0")!
240 static const char pagesPerSheetData[][2] = { "1", "2", "4", "6", "9", {'1', '6'}, "\0" };
241 static const char pageLayoutData[][5] = {"lrtb", "lrbt", "rlbt", "rltb", "btlr", "btrl", "tblr", "tbrl"};
242 setCupsOption(printer, QStringLiteral("number-up"), value: QLatin1StringView(pagesPerSheetData[pagesPerSheet]));
243 setCupsOption(printer, QStringLiteral("number-up-layout"), value: QLatin1StringView(pageLayoutData[pagesPerSheetLayout]));
244}
245
246void QCUPSSupport::setPageRange(QPrinter *printer, int pageFrom, int pageTo)
247{
248 setPageRange(printer, QStringLiteral("%1-%2").arg(a: pageFrom).arg(a: pageTo));
249}
250
251void QCUPSSupport::setPageRange(QPrinter *printer, const QString &pageRange)
252{
253 setCupsOption(printer, QStringLiteral("page-ranges"), value: pageRange);
254}
255
256QT_END_NAMESPACE
257

source code of qtbase/src/printsupport/kernel/qcups.cpp