1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qcups_p.h"
41 
42 #include "qprintdevice_p.h"
43 #include "qprintengine.h"
44 
45 QT_BEGIN_NAMESPACE
46 
cupsOptionsList(QPrinter * printer)47 static QStringList cupsOptionsList(QPrinter *printer) noexcept
48 {
49     return printer->printEngine()->property(PPK_CupsOptions).toStringList();
50 }
51 
setCupsOptions(QPrinter * printer,const QStringList & cupsOptions)52 void setCupsOptions(QPrinter *printer, const QStringList &cupsOptions) noexcept
53 {
54     printer->printEngine()->setProperty(PPK_CupsOptions, QVariant(cupsOptions));
55 }
56 
setCupsOption(QPrinter * printer,const QString & option,const QString & value)57 void QCUPSSupport::setCupsOption(QPrinter *printer, const QString &option, const QString &value)
58 {
59     QStringList cupsOptions = cupsOptionsList(printer);
60     if (cupsOptions.contains(option)) {
61         cupsOptions.replace(cupsOptions.indexOf(option) + 1, value);
62     } else {
63         cupsOptions.append(option);
64         cupsOptions.append(value);
65     }
66     setCupsOptions(printer, cupsOptions);
67 }
68 
clearCupsOption(QPrinter * printer,const QString & option)69 void QCUPSSupport::clearCupsOption(QPrinter *printer, const QString &option)
70 {
71     QStringList cupsOptions = cupsOptionsList(printer);
72     // ### use const_iterator once QList::erase takes them
73     const QStringList::iterator it = std::find(cupsOptions.begin(), cupsOptions.end(), option);
74     if (it != cupsOptions.end()) {
75         Q_ASSERT(it + 1 < cupsOptions.end());
76         cupsOptions.erase(it, it+1);
77         setCupsOptions(printer, cupsOptions);
78     }
79 }
80 
clearCupsOptions(QPrinter * printer)81 void QCUPSSupport::clearCupsOptions(QPrinter *printer)
82 {
83     setCupsOptions(printer, QStringList());
84 }
85 
jobHoldToString(const QCUPSSupport::JobHoldUntil jobHold,const QTime holdUntilTime)86 static inline QString jobHoldToString(const QCUPSSupport::JobHoldUntil jobHold, const QTime holdUntilTime)
87 {
88     switch (jobHold) {
89     case QCUPSSupport::Indefinite:
90         return QStringLiteral("indefinite");
91     case QCUPSSupport::DayTime:
92         return QStringLiteral("day-time");
93     case QCUPSSupport::Night:
94         return QStringLiteral("night");
95     case QCUPSSupport::SecondShift:
96         return QStringLiteral("second-shift");
97     case QCUPSSupport::ThirdShift:
98         return QStringLiteral("third-shift");
99     case QCUPSSupport::Weekend:
100         return QStringLiteral("weekend");
101     case QCUPSSupport::SpecificTime:
102         if (!holdUntilTime.isNull()) {
103             // CUPS expects the time in UTC, user has entered in local time, so get the UTS equivalent
104             QDateTime localDateTime = QDateTime::currentDateTime();
105             // Check if time is for tomorrow in case of DST change overnight
106             if (holdUntilTime < localDateTime.time())
107                 localDateTime = localDateTime.addDays(1);
108             localDateTime.setTime(holdUntilTime);
109             return localDateTime.toUTC().time().toString(u"HH:mm");
110         }
111         // else fall through:
112         Q_FALLTHROUGH();
113     case QCUPSSupport::NoHold:
114         return QString();
115     }
116     Q_UNREACHABLE();
117     return QString();
118 }
119 
parseJobHoldUntil(const QString & jobHoldUntil)120 QCUPSSupport::JobHoldUntilWithTime QCUPSSupport::parseJobHoldUntil(const QString &jobHoldUntil)
121 {
122     if (jobHoldUntil == QLatin1String("indefinite")) {
123         return { QCUPSSupport::Indefinite, QTime() };
124     } else if (jobHoldUntil == QLatin1String("day-time")) {
125         return { QCUPSSupport::DayTime, QTime() };
126     } else if (jobHoldUntil == QLatin1String("night")) {
127         return { QCUPSSupport::Night, QTime() };
128     } else if (jobHoldUntil == QLatin1String("second-shift")) {
129         return { QCUPSSupport::SecondShift, QTime() };
130     } else if (jobHoldUntil == QLatin1String("third-shift")) {
131         return { QCUPSSupport::ThirdShift, QTime() };
132     } else if (jobHoldUntil == QLatin1String("weekend")) {
133         return { QCUPSSupport::Weekend, QTime() };
134     }
135 
136 
137     QTime parsedTime = QTime::fromString(jobHoldUntil, QStringLiteral("h:m:s"));
138     if (!parsedTime.isValid())
139         parsedTime = QTime::fromString(jobHoldUntil, QStringLiteral("h:m"));
140     if (parsedTime.isValid()) {
141         // CUPS time is in UTC, user expects local time, so get the equivalent
142         QDateTime dateTimeUtc = QDateTime::currentDateTimeUtc();
143         dateTimeUtc.setTime(parsedTime);
144         return { QCUPSSupport::SpecificTime, dateTimeUtc.toLocalTime().time() };
145     }
146 
147     return { QCUPSSupport::NoHold, QTime() };
148 }
149 
findPpdOption(const char * optionName,QPrintDevice * printDevice)150 ppd_option_t *QCUPSSupport::findPpdOption(const char *optionName, QPrintDevice *printDevice)
151 {
152     ppd_file_t *ppd = qvariant_cast<ppd_file_t*>(printDevice->property(PDPK_PpdFile));
153 
154     if (ppd) {
155         for (int i = 0; i < ppd->num_groups; ++i) {
156             ppd_group_t *group = &ppd->groups[i];
157 
158             for (int i = 0; i < group->num_options; ++i) {
159                 ppd_option_t *option = &group->options[i];
160 
161                 if (qstrcmp(option->keyword, optionName) == 0)
162                     return option;
163             }
164         }
165     }
166 
167     return nullptr;
168 }
169 
setJobHold(QPrinter * printer,const JobHoldUntil jobHold,const QTime & holdUntilTime)170 void QCUPSSupport::setJobHold(QPrinter *printer, const JobHoldUntil jobHold, const QTime &holdUntilTime)
171 {
172     const QString jobHoldUntilArgument = jobHoldToString(jobHold, holdUntilTime);
173     if (!jobHoldUntilArgument.isEmpty()) {
174         setCupsOption(printer,
175                       QStringLiteral("job-hold-until"),
176                       jobHoldUntilArgument);
177     } else {
178         clearCupsOption(printer, QStringLiteral("job-hold-until"));
179     }
180 }
181 
setJobBilling(QPrinter * printer,const QString & jobBilling)182 void QCUPSSupport::setJobBilling(QPrinter *printer, const QString &jobBilling)
183 {
184     setCupsOption(printer, QStringLiteral("job-billing"), jobBilling);
185 }
186 
setJobPriority(QPrinter * printer,int priority)187 void QCUPSSupport::setJobPriority(QPrinter *printer, int priority)
188 {
189     setCupsOption(printer, QStringLiteral("job-priority"), QString::number(priority));
190 }
191 
bannerPageToString(const QCUPSSupport::BannerPage bannerPage)192 static inline QString bannerPageToString(const QCUPSSupport::BannerPage bannerPage)
193 {
194     switch (bannerPage) {
195     case QCUPSSupport::NoBanner:     return QStringLiteral("none");
196     case QCUPSSupport::Standard:     return QStringLiteral("standard");
197     case QCUPSSupport::Unclassified: return QStringLiteral("unclassified");
198     case QCUPSSupport::Confidential: return QStringLiteral("confidential");
199     case QCUPSSupport::Classified:   return QStringLiteral("classified");
200     case QCUPSSupport::Secret:       return QStringLiteral("secret");
201     case QCUPSSupport::TopSecret:    return QStringLiteral("topsecret");
202     }
203     Q_UNREACHABLE();
204     return QString();
205 }
206 
stringToBannerPage(const QString & bannerPage)207 static inline QCUPSSupport::BannerPage stringToBannerPage(const QString &bannerPage)
208 {
209     if (bannerPage == QLatin1String("none")) return QCUPSSupport::NoBanner;
210     else if (bannerPage == QLatin1String("standard")) return QCUPSSupport::Standard;
211     else if (bannerPage == QLatin1String("unclassified")) return QCUPSSupport::Unclassified;
212     else if (bannerPage == QLatin1String("confidential")) return QCUPSSupport::Confidential;
213     else if (bannerPage == QLatin1String("classified")) return QCUPSSupport::Classified;
214     else if (bannerPage == QLatin1String("secret")) return QCUPSSupport::Secret;
215     else if (bannerPage == QLatin1String("topsecret")) return QCUPSSupport::TopSecret;
216 
217     return QCUPSSupport::NoBanner;
218 }
219 
parseJobSheets(const QString & jobSheets)220 QCUPSSupport::JobSheets QCUPSSupport::parseJobSheets(const QString &jobSheets)
221 {
222     JobSheets result;
223 
224     const QStringList parts = jobSheets.split(QLatin1Char(','));
225     if (parts.count() == 2) {
226         result.startBannerPage = stringToBannerPage(parts[0]);
227         result.endBannerPage = stringToBannerPage(parts[1]);
228     }
229 
230     return result;
231 }
232 
setBannerPages(QPrinter * printer,const BannerPage startBannerPage,const BannerPage endBannerPage)233 void QCUPSSupport::setBannerPages(QPrinter *printer, const BannerPage startBannerPage, const BannerPage endBannerPage)
234 {
235     const QString startBanner = bannerPageToString(startBannerPage);
236     const QString endBanner   = bannerPageToString(endBannerPage);
237 
238     setCupsOption(printer, QStringLiteral("job-sheets"), startBanner + QLatin1Char(',') + endBanner);
239 }
240 
setPageSet(QPrinter * printer,const PageSet pageSet)241 void QCUPSSupport::setPageSet(QPrinter *printer, const PageSet pageSet)
242 {
243     QString pageSetString;
244 
245     switch (pageSet) {
246     case OddPages:
247         pageSetString = QStringLiteral("odd");
248         break;
249     case EvenPages:
250         pageSetString = QStringLiteral("even");
251         break;
252     case AllPages:
253         pageSetString = QStringLiteral("all");
254         break;
255     }
256 
257     setCupsOption(printer, QStringLiteral("page-set"), pageSetString);
258 }
259 
setPagesPerSheetLayout(QPrinter * printer,const PagesPerSheet pagesPerSheet,const PagesPerSheetLayout pagesPerSheetLayout)260 void QCUPSSupport::setPagesPerSheetLayout(QPrinter *printer,  const PagesPerSheet pagesPerSheet,
261                                           const PagesPerSheetLayout pagesPerSheetLayout)
262 {
263     // WARNING: the following trick (with a [2]-extent) only works as
264     // WARNING: long as there's only one two-digit number in the list
265     // WARNING: and it is the last one (before the "\0")!
266     static const char pagesPerSheetData[][2] = { "1", "2", "4", "6", "9", {'1', '6'}, "\0" };
267     static const char pageLayoutData[][5] = {"lrtb", "lrbt", "rlbt", "rltb", "btlr", "btrl", "tblr", "tbrl"};
268     setCupsOption(printer, QStringLiteral("number-up"), QLatin1String(pagesPerSheetData[pagesPerSheet]));
269     setCupsOption(printer, QStringLiteral("number-up-layout"), QLatin1String(pageLayoutData[pagesPerSheetLayout]));
270 }
271 
setPageRange(QPrinter * printer,int pageFrom,int pageTo)272 void QCUPSSupport::setPageRange(QPrinter *printer, int pageFrom, int pageTo)
273 {
274     setPageRange(printer, QStringLiteral("%1-%2").arg(pageFrom).arg(pageTo));
275 }
276 
setPageRange(QPrinter * printer,const QString & pageRange)277 void QCUPSSupport::setPageRange(QPrinter *printer, const QString &pageRange)
278 {
279     setCupsOption(printer, QStringLiteral("page-ranges"), pageRange);
280 }
281 
282 QT_END_NAMESPACE
283