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 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | using namespace Qt::StringLiterals; |
12 | |
13 | QT_IMPL_METATYPE_EXTERN_TAGGED(QCUPSSupport::JobHoldUntil, |
14 | QCUPSSupport__JobHoldUntil) |
15 | QT_IMPL_METATYPE_EXTERN_TAGGED(QCUPSSupport::BannerPage, |
16 | QCUPSSupport__BannerPage) |
17 | QT_IMPL_METATYPE_EXTERN_TAGGED(QCUPSSupport::PageSet, QCUPSSupport__PageSet) |
18 | QT_IMPL_METATYPE_EXTERN_TAGGED(QCUPSSupport::PagesPerSheetLayout, |
19 | QCUPSSupport__PagesPerSheetLayout) |
20 | QT_IMPL_METATYPE_EXTERN_TAGGED(QCUPSSupport::PagesPerSheet, |
21 | QCUPSSupport__PagesPerSheet) |
22 | |
23 | static QStringList cupsOptionsList(QPrinter *printer) noexcept |
24 | { |
25 | return printer->printEngine()->property(PPK_CupsOptions).toStringList(); |
26 | } |
27 | |
28 | void setCupsOptions(QPrinter *printer, const QStringList &cupsOptions) noexcept |
29 | { |
30 | printer->printEngine()->setProperty(PPK_CupsOptions, value: QVariant(cupsOptions)); |
31 | } |
32 | |
33 | void 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 | |
45 | void 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 | |
57 | void QCUPSSupport::clearCupsOptions(QPrinter *printer) |
58 | { |
59 | setCupsOptions(printer, cupsOptions: QStringList()); |
60 | } |
61 | |
62 | static 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 | |
95 | QCUPSSupport::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 | |
125 | ppd_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 | |
145 | void 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 | |
157 | void QCUPSSupport::setJobBilling(QPrinter *printer, const QString &jobBilling) |
158 | { |
159 | setCupsOption(printer, QStringLiteral("job-billing" ), value: jobBilling); |
160 | } |
161 | |
162 | void QCUPSSupport::setJobPriority(QPrinter *printer, int priority) |
163 | { |
164 | setCupsOption(printer, QStringLiteral("job-priority" ), value: QString::number(priority)); |
165 | } |
166 | |
167 | static 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 | |
181 | static inline QCUPSSupport::BannerPage (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 | |
194 | QCUPSSupport::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 | |
207 | void 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 | |
215 | void 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 | |
234 | void 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 | |
246 | void QCUPSSupport::(QPrinter *printer, int pageFrom, int pageTo) |
247 | { |
248 | setPageRange(printer, QStringLiteral("%1-%2" ).arg(a: pageFrom).arg(a: pageTo)); |
249 | } |
250 | |
251 | void QCUPSSupport::(QPrinter *printer, const QString &) |
252 | { |
253 | setCupsOption(printer, QStringLiteral("page-ranges" ), value: pageRange); |
254 | } |
255 | |
256 | QT_END_NAMESPACE |
257 | |