Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qppdprintdevice.cpp
Go to the documentation of this file.
1// Copyright (C) 2014 John Layt <jlayt@kde.org>
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 "qppdprintdevice.h"
5
7#include "private/qcups_p.h" // Only needed for PDPK_*
8
9#if QT_CONFIG(mimetype)
10#include <QtCore/QMimeDatabase>
11#endif
12
13#ifndef QT_LINUXBASE // LSB merges everything into cups.h
14#include <cups/language.h>
15#endif
16
18
19// avoid all the warnings about using deprecated API from CUPS (as there is no real replacement)
22
25 m_cupsDest(0),
26 m_ppd(0)
27{
28 if (!id.isEmpty()) {
29
30 // TODO For now each dest is an individual device
31 const auto parts = QStringView{id}.split(u'/');
32 m_cupsName = parts.at(0).toUtf8();
33 if (parts.size() > 1)
34 m_cupsInstance = parts.at(1).toUtf8();
35
36 // Get the print instance and PPD file
37 m_cupsDest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, m_cupsName, m_cupsInstance.isNull() ? nullptr : m_cupsInstance.constData());
38 if (m_cupsDest) {
39 const char *ppdFile = cupsGetPPD(m_cupsName);
40 if (ppdFile) {
41 m_ppd = ppdOpenFile(ppdFile);
42 unlink(ppdFile);
43 }
44 if (m_ppd) {
45 ppdMarkDefaults(m_ppd);
46 cupsMarkOptions(m_ppd, m_cupsDest->num_options, m_cupsDest->options);
47 ppdLocalize(m_ppd);
48
49 m_minimumPhysicalPageSize = QSize(m_ppd->custom_min[0], m_ppd->custom_min[1]);
50 m_maximumPhysicalPageSize = QSize(m_ppd->custom_max[0], m_ppd->custom_max[1]);
51 m_customMargins = QMarginsF(m_ppd->custom_margins[0], m_ppd->custom_margins[3],
52 m_ppd->custom_margins[2], m_ppd->custom_margins[1]);
53 }
54
55 m_name = printerOption("printer-info");
56 m_location = printerOption("printer-location");
57 m_makeAndModel = printerOption("printer-make-and-model");
58 cups_ptype_e type = printerTypeFlags();
59 m_isRemote = type & CUPS_PRINTER_REMOTE;
60 // Note this is if the hardware does multiple copies, not if Cups can
61 m_supportsMultipleCopies = type & CUPS_PRINTER_COPIES;
62 // Note this is if the hardware does collation, not if Cups can
63 m_supportsCollateCopies = type & CUPS_PRINTER_COLLATE;
64
65 // Custom Page Size support
66 // Cups cups_ptype_e CUPS_PRINTER_VARIABLE
67 // Cups ppd_file_t variable_sizes custom_min custom_max
68 // PPD MaxMediaWidth MaxMediaHeight
69 m_supportsCustomPageSizes = type & CUPS_PRINTER_VARIABLE;
70 }
71 }
72}
73
75{
76 if (m_ppd)
77 ppdClose(m_ppd);
78 if (m_cupsDest)
79 cupsFreeDests(1, m_cupsDest);
80 m_cupsDest = 0;
81 m_ppd = 0;
82}
83
85{
86 return m_cupsDest;
87}
88
90{
91 // There seems to be a bug in cups in which printerTypeFlags
92 // returns CUPS_PRINTER_DEFAULT based only on system values, ignoring user lpoptions
93 // so we can't use that. And also there seems to be a bug in which dests returned
94 // by cupsGetNamedDest don't have is_default set at all so we can't use that either
95 // so go the long route and compare our id against the defaultPrintDeviceId
97}
98
100{
101 // 3 = idle, 4 = printing, 5 = stopped
102 // More details available from printer-state-message and printer-state-reasons
103 int state = printerOption(QStringLiteral("printer-state")).toInt();
104 if (state == 3)
105 return QPrint::Idle;
106 else if (state == 4)
107 return QPrint::Active;
108 else
109 return QPrint::Error;
110}
111
113{
115 m_printableMargins.clear();
116
117 ppd_option_t *pageSizes = ppdFindOption(m_ppd, "PageSize");
118 if (pageSizes) {
119 for (int i = 0; i < pageSizes->num_choices; ++i) {
120 const ppd_size_t *ppdSize = ppdPageSize(m_ppd, pageSizes->choices[i].choice);
121 if (ppdSize) {
122 // Returned size is in points
123 QString key = QString::fromUtf8(ppdSize->name);
124 QSize size = QSize(qRound(ppdSize->width), qRound(ppdSize->length));
125 QString name = QString::fromUtf8(pageSizes->choices[i].text);
126 if (!size.isEmpty()) {
128 if (ps.isValid()) {
130 m_printableMargins.insert(key, QMarginsF(ppdSize->left, ppdSize->length - ppdSize->top,
131 ppdSize->width - ppdSize->right, ppdSize->bottom));
132 }
133 }
134 }
135 }
136 }
137 m_havePageSizes = true;
138}
139
141{
142 ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "PageSize");
143 if (defaultChoice) {
144 ppd_size_t *ppdSize = ppdPageSize(m_ppd, defaultChoice->choice);
145 if (ppdSize) {
146 // Returned size is in points
147 QString key = QString::fromUtf8(ppdSize->name);
148 QSize size = QSize(qRound(ppdSize->width), qRound(ppdSize->length));
149 QString name = QString::fromUtf8(defaultChoice->text);
150 return createPageSize(key, size, name);
151 }
152 }
153 return QPageSize();
154}
155
157 QPageLayout::Orientation orientation,
158 int resolution) const
159{
160 Q_UNUSED(orientation);
161 Q_UNUSED(resolution);
162 if (!m_havePageSizes)
164 // TODO Orientation?
165 if (m_printableMargins.contains(pageSize.key()))
166 return m_printableMargins.value(pageSize.key());
167 return m_customMargins;
168}
169
171{
173
174 // Try load standard PPD options first
175 ppd_option_t *resolutions = ppdFindOption(m_ppd, "Resolution");
176 if (resolutions) {
177 for (int i = 0; i < resolutions->num_choices; ++i) {
178 int res = QPrintUtils::parsePpdResolution(resolutions->choices[i].choice);
179 if (res > 0)
181 }
182 }
183 // If no result, try just the default
184 if (m_resolutions.size() == 0) {
185 resolutions = ppdFindOption(m_ppd, "DefaultResolution");
186 if (resolutions) {
187 int res = QPrintUtils::parsePpdResolution(resolutions->choices[0].choice);
188 if (res > 0)
190 }
191 }
192 // If still no result, then try HP's custom options
193 if (m_resolutions.size() == 0) {
194 resolutions = ppdFindOption(m_ppd, "HPPrintQuality");
195 if (resolutions) {
196 for (int i = 0; i < resolutions->num_choices; ++i) {
197 int res = QPrintUtils::parsePpdResolution(resolutions->choices[i].choice);
198 if (res > 0)
200 }
201 }
202 }
203 if (m_resolutions.size() == 0) {
204 resolutions = ppdFindOption(m_ppd, "DefaultHPPrintQuality");
205 if (resolutions) {
206 int res = QPrintUtils::parsePpdResolution(resolutions->choices[0].choice);
207 if (res > 0)
209 }
210 }
211 m_haveResolutions = true;
212}
213
215{
216 // Try load standard PPD option first
217 ppd_option_t *resolution = ppdFindOption(m_ppd, "DefaultResolution");
218 if (resolution) {
219 int res = QPrintUtils::parsePpdResolution(resolution->choices[0].choice);
220 if (res > 0)
221 return res;
222 }
223 // If no result, then try a marked option
224 ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "Resolution");
225 if (defaultChoice) {
226 int res = QPrintUtils::parsePpdResolution(defaultChoice->choice);
227 if (res > 0)
228 return res;
229 }
230 // If still no result, then try HP's custom options
231 resolution = ppdFindOption(m_ppd, "DefaultHPPrintQuality");
232 if (resolution) {
233 int res = QPrintUtils::parsePpdResolution(resolution->choices[0].choice);
234 if (res > 0)
235 return res;
236 }
237 defaultChoice = ppdFindMarkedChoice(m_ppd, "HPPrintQuality");
238 if (defaultChoice) {
239 int res = QPrintUtils::parsePpdResolution(defaultChoice->choice);
240 if (res > 0)
241 return res;
242 }
243 // Otherwise return a sensible default.
244 // TODO What is sensible? 150? 300?
245 return 72;
246}
247
249{
250 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
251 // TODO Deal with concatenated names like Tray1Manual or Tray1_Man,
252 // will currently show as CustomInputSlot
253 // TODO Deal with separate ManualFeed key
254 // Try load standard PPD options first
256 if (m_ppd) {
257 ppd_option_t *inputSlots = ppdFindOption(m_ppd, "InputSlot");
258 if (inputSlots) {
259 m_inputSlots.reserve(inputSlots->num_choices);
260 for (int i = 0; i < inputSlots->num_choices; ++i)
261 m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[i]));
262 }
263 // If no result, try just the default
264 if (m_inputSlots.size() == 0) {
265 inputSlots = ppdFindOption(m_ppd, "DefaultInputSlot");
266 if (inputSlots)
267 m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[0]));
268 }
269 }
270 // If still no result, just use Auto
271 if (m_inputSlots.size() == 0)
273 m_haveInputSlots = true;
274}
275
277{
278 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
279 // Try load standard PPD option first
280 if (m_ppd) {
281 ppd_option_t *inputSlot = ppdFindOption(m_ppd, "DefaultInputSlot");
282 if (inputSlot)
283 return QPrintUtils::ppdChoiceToInputSlot(inputSlot->choices[0]);
284 // If no result, then try a marked option
285 ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "InputSlot");
286 if (defaultChoice)
287 return QPrintUtils::ppdChoiceToInputSlot(*defaultChoice);
288 }
289 // Otherwise return Auto
291}
292
294{
295 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
297 if (m_ppd) {
298 ppd_option_t *outputBins = ppdFindOption(m_ppd, "OutputBin");
299 if (outputBins) {
300 m_outputBins.reserve(outputBins->num_choices);
301 for (int i = 0; i < outputBins->num_choices; ++i)
302 m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[i]));
303 }
304 // If no result, try just the default
305 if (m_outputBins.size() == 0) {
306 outputBins = ppdFindOption(m_ppd, "DefaultOutputBin");
307 if (outputBins)
308 m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[0]));
309 }
310 }
311 // If still no result, just use Auto
312 if (m_outputBins.size() == 0)
314 m_haveOutputBins = true;
315}
316
318{
319 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
320 // Try load standard PPD option first
321 if (m_ppd) {
322 ppd_option_t *outputBin = ppdFindOption(m_ppd, "DefaultOutputBin");
323 if (outputBin)
324 return QPrintUtils::ppdChoiceToOutputBin(outputBin->choices[0]);
325 // If no result, then try a marked option
326 ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "OutputBin");
327 if (defaultChoice)
328 return QPrintUtils::ppdChoiceToOutputBin(*defaultChoice);
329 }
330 // Otherwise return AutoBin
332}
333
335{
336 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
337 // Try load standard PPD options first
339 if (m_ppd) {
340 ppd_option_t *duplexModes = ppdFindOption(m_ppd, "Duplex");
341 if (duplexModes) {
342 m_duplexModes.reserve(duplexModes->num_choices);
343 for (int i = 0; i < duplexModes->num_choices; ++i) {
344 if (ppdInstallableConflict(m_ppd, duplexModes->keyword, duplexModes->choices[i].choice) == 0) {
345 m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[i].choice));
346 }
347 }
348 }
349 // If no result, try just the default
350 if (m_duplexModes.size() == 0) {
351 duplexModes = ppdFindOption(m_ppd, "DefaultDuplex");
352 if (duplexModes && (ppdInstallableConflict(m_ppd, duplexModes->keyword, duplexModes->choices[0].choice) == 0)) {
353 m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[0].choice));
354 }
355 }
356 }
357 // If still no result, or not added in PPD, then add None
360 // If have both modes, then can support DuplexAuto
363 m_haveDuplexModes = true;
364}
365
367{
368 // Try load standard PPD option first
369 if (m_ppd) {
370 ppd_option_t *inputSlot = ppdFindOption(m_ppd, "DefaultDuplex");
371 if (inputSlot)
372 return QPrintUtils::ppdChoiceToDuplexMode(inputSlot->choices[0].choice);
373 // If no result, then try a marked option
374 ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "Duplex");
375 if (defaultChoice)
376 return QPrintUtils::ppdChoiceToDuplexMode(defaultChoice->choice);
377 }
378 // Otherwise return None
379 return QPrint::DuplexNone;
380}
381
383{
384 // Cups cups_ptype_e CUPS_PRINTER_BW CUPS_PRINTER_COLOR
385 // Cups ppd_file_t color_device
386 // PPD ColorDevice
388 cups_ptype_e type = printerTypeFlags();
389 if (type & CUPS_PRINTER_BW)
391 if (type & CUPS_PRINTER_COLOR)
393 m_haveColorModes = true;
394}
395
397{
398 // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync
399 // Not a proper option, usually only know if supports color or not, but some
400 // users known to abuse ColorModel to always force GrayScale.
402 ppd_option_t *colorModel = ppdFindOption(m_ppd, "DefaultColorModel");
403 if (!colorModel)
404 colorModel = ppdFindOption(m_ppd, "ColorModel");
405 if (!colorModel || qstrcmp(colorModel->defchoice, "Gray") != 0)
406 return QPrint::Color;
407 }
408 return QPrint::GrayScale;
409}
410
412{
413 if (key == PDPK_PpdFile)
414 return QVariant::fromValue<ppd_file_t *>(m_ppd);
415 else if (key == PDPK_CupsJobPriority)
416 return printerOption(QStringLiteral("job-priority"));
417 else if (key == PDPK_CupsJobSheets)
418 return printerOption(QStringLiteral("job-sheets"));
419 else if (key == PDPK_CupsJobBilling)
420 return printerOption(QStringLiteral("job-billing"));
421 else if (key == PDPK_CupsJobHoldUntil)
422 return printerOption(QStringLiteral("job-hold-until"));
423
425}
426
428{
429 if (key == PDPK_PpdOption) {
430 const QStringList values = value.toStringList();
431 if (values.size() == 2) {
432 ppdMarkOption(m_ppd, values[0].toLatin1(), values[1].toLatin1());
433 return true;
434 }
435 }
436
438}
439
441{
443 const QStringList values = params.toStringList();
444 if (values.size() == 2)
445 return ppdInstallableConflict(m_ppd, values[0].toLatin1(), values[1].toLatin1());
446 }
447
449}
450
451#if QT_CONFIG(mimetype)
452void QPpdPrintDevice::loadMimeTypes() const
453{
454 // TODO No CUPS api? Need to manually load CUPS mime.types file?
455 // For now hard-code most common support types
457 m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("application/pdf")));
458 m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("application/postscript")));
459 m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/gif")));
460 m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/png")));
461 m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/jpeg")));
462 m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/tiff")));
463 m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("text/html")));
464 m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("text/plain")));
465 m_haveMimeTypes = true;
466}
467#endif
468
469QString QPpdPrintDevice::printerOption(const QString &key) const
470{
471 return cupsGetOption(key.toUtf8(), m_cupsDest->num_options, m_cupsDest->options);
472}
473
474cups_ptype_e QPpdPrintDevice::printerTypeFlags() const
475{
476 return static_cast<cups_ptype_e>(printerOption("printer-type").toUInt());
477}
478
480
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
char at(qsizetype i) const
Returns the byte at index position i in the byte array.
Definition qbytearray.h:600
bool isNull() const noexcept
Returns true if this byte array is null; otherwise returns false.
static QString staticDefaultPrintDeviceId()
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
T value(const Key &key) const noexcept
Definition qhash.h:1054
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
qsizetype size() const noexcept
Definition qlist.h:397
void reserve(qsizetype size)
Definition qlist.h:753
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
\inmodule QtCore
Definition qmargins.h:270
\inmodule QtCore
QMimeType mimeTypeForName(const QString &nameOrAlias) const
Returns a MIME type for nameOrAlias or an invalid one if none found.
Orientation
This enum type defines the page orientation.
Definition qpagelayout.h:33
\inmodule QtGui
Definition qpagesize.h:22
bool isValid() const
Returns true if this page size is valid.
QString key() const
Returns the unique key of the page size.
virtual QString id() const
virtual bool isFeatureAvailable(QPrintDevice::PrintDevicePropertyKey key, const QVariant &params) const
static QPageSize createPageSize(const QString &key, const QSize &size, const QString &localizedName)
virtual QList< QPrint::ColorMode > supportedColorModes() const
QList< QPrint::InputSlot > m_inputSlots
virtual QVariant property(QPrintDevice::PrintDevicePropertyKey key) const
QList< QPrint::ColorMode > m_colorModes
virtual bool setProperty(QPrintDevice::PrintDevicePropertyKey key, const QVariant &value)
QList< QPageSize > m_pageSizes
virtual QPrint::InputSlot defaultInputSlot() const
QList< QPrint::OutputBin > m_outputBins
QList< QPrint::DuplexMode > m_duplexModes
virtual QPrint::OutputBin defaultOutputBin() const
void loadResolutions() const override
QPrint::InputSlot defaultInputSlot() const override
int defaultResolution() const override
QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation, int resolution) const override
QPrint::OutputBin defaultOutputBin() const override
void loadPageSizes() const override
QPageSize defaultPageSize() const override
bool isFeatureAvailable(QPrintDevice::PrintDevicePropertyKey key, const QVariant &params) const override
void loadInputSlots() const override
QPrint::DeviceState state() const override
void loadDuplexModes() const override
QPpdPrintDevice(const QString &id)
QPrint::ColorMode defaultColorMode() const override
void loadColorModes() const override
QVariant property(QPrintDevice::PrintDevicePropertyKey key) const override
QPrint::DuplexMode defaultDuplexMode() const override
void loadOutputBins() const override
virtual ~QPpdPrintDevice()
bool setProperty(QPrintDevice::PrintDevicePropertyKey key, const QVariant &value) override
bool isDefault() const override
bool isValid() const override
\inmodule QtCore
Definition qsize.h:25
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
Q_CORE_EXPORT QList< QStringView > split(QStringView sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the view into substring views wherever sep occurs, and returns the list of those string views.
Definition qstring.cpp:8249
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
int toInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an int using base base, which is 10 by default and must be between 2 ...
Definition qstring.h:731
uint toUInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an {unsigned int} using base base, which is 10 by default and must be...
Definition qstring.h:733
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
\inmodule QtCore
Definition qvariant.h:65
ColorMode
Definition qprint_p.h:72
@ Color
Definition qprint_p.h:74
@ GrayScale
Definition qprint_p.h:73
DuplexMode
Definition qprint_p.h:64
@ DuplexLongSide
Definition qprint_p.h:67
@ DuplexShortSide
Definition qprint_p.h:68
@ DuplexNone
Definition qprint_p.h:65
@ DuplexAuto
Definition qprint_p.h:66
DeviceState
Definition qprint_p.h:56
@ Idle
Definition qprint_p.h:57
@ Error
Definition qprint_p.h:60
@ Active
Definition qprint_p.h:58
Combined button and popup list for selecting options.
Q_CORE_EXPORT int qstrcmp(const char *str1, const char *str2)
#define QT_WARNING_POP
#define QT_WARNING_DISABLE_DEPRECATED
#define QT_WARNING_PUSH
#define PDPK_CupsJobBilling
Definition qcups_p.h:41
#define PDPK_PpdFile
Definition qcups_p.h:37
#define PDPK_CupsJobPriority
Definition qcups_p.h:39
#define PDPK_PpdChoiceIsInstallableConflict
Definition qcups_p.h:43
#define PDPK_CupsJobHoldUntil
Definition qcups_p.h:42
#define PDPK_CupsJobSheets
Definition qcups_p.h:40
#define PDPK_PpdOption
Definition qcups_p.h:38
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLenum GLsizei GLsizei GLint * values
[15]
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint id
[7]
GLenum type
GLuint name
void ** params
GLuint res
#define QStringLiteral(str)
#define Q_UNUSED(x)
QMimeDatabase db
[0]
bool contains(const AT &t) const noexcept
Definition qlist.h:45