Add CMYK support for pens/fills in the PDF engine
Insofar, painting with a CMYK color (pen/brush) was completely ignored by QPdfWriter, although the PDF format can faithfully represent CMYK colors. This commit adds support for CMYK colors in the PDF engine. The support is opt-in, in the name of backwards compatibility; an enumeration on QPdfWriter controls the output. QPrinter was using a hidden hook in QPdfEngine in order to do grayscale printing; this hook can now be made public API through the same enumeration. This work has been kindly sponsored by the QGIS project (https://qgis.org/). [ChangeLog][QtGui][QPdfWriter] QPdfWriter can now use CMYK colors directly, without converting them into RGB colors. Change-Id: Ia27c19ec81a58ab68ddc8b9c89c4e57d7d637301 Reviewed-by: Lars Knoll <lars@knoll.priv.no>
This commit is contained in:
parent
efc579145e
commit
0092f06a64
@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE
|
|||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
inline QPaintEngine::PaintEngineFeatures qt_pdf_decide_features()
|
constexpr QPaintEngine::PaintEngineFeatures qt_pdf_decide_features()
|
||||||
{
|
{
|
||||||
QPaintEngine::PaintEngineFeatures f = QPaintEngine::AllFeatures;
|
QPaintEngine::PaintEngineFeatures f = QPaintEngine::AllFeatures;
|
||||||
f &= ~(QPaintEngine::PorterDuff
|
f &= ~(QPaintEngine::PorterDuff
|
||||||
@ -1239,17 +1239,8 @@ void QPdfEngine::setPen()
|
|||||||
QBrush b = d->pen.brush();
|
QBrush b = d->pen.brush();
|
||||||
Q_ASSERT(b.style() == Qt::SolidPattern && b.isOpaque());
|
Q_ASSERT(b.style() == Qt::SolidPattern && b.isOpaque());
|
||||||
|
|
||||||
QColor rgba = b.color();
|
d->writeColor(QPdfEnginePrivate::ColorDomain::Stroking, b.color());
|
||||||
if (d->grayscale) {
|
|
||||||
qreal gray = qGray(rgba.rgba())/255.;
|
|
||||||
*d->currentPage << gray << gray << gray;
|
|
||||||
} else {
|
|
||||||
*d->currentPage << rgba.redF()
|
|
||||||
<< rgba.greenF()
|
|
||||||
<< rgba.blueF();
|
|
||||||
}
|
|
||||||
*d->currentPage << "SCN\n";
|
*d->currentPage << "SCN\n";
|
||||||
|
|
||||||
*d->currentPage << d->pen.widthF() << "w ";
|
*d->currentPage << d->pen.widthF() << "w ";
|
||||||
|
|
||||||
int pdfCapStyle = 0;
|
int pdfCapStyle = 0;
|
||||||
@ -1303,18 +1294,9 @@ void QPdfEngine::setBrush()
|
|||||||
if (!patternObject && !specifyColor)
|
if (!patternObject && !specifyColor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
*d->currentPage << (patternObject ? "/PCSp cs " : "/CSp cs ");
|
const auto domain = patternObject ? QPdfEnginePrivate::ColorDomain::NonStrokingPattern
|
||||||
if (specifyColor) {
|
: QPdfEnginePrivate::ColorDomain::NonStroking;
|
||||||
QColor rgba = d->brush.color();
|
d->writeColor(domain, specifyColor ? d->brush.color() : QColor());
|
||||||
if (d->grayscale) {
|
|
||||||
qreal gray = qGray(rgba.rgba())/255.;
|
|
||||||
*d->currentPage << gray << gray << gray;
|
|
||||||
} else {
|
|
||||||
*d->currentPage << rgba.redF()
|
|
||||||
<< rgba.greenF()
|
|
||||||
<< rgba.blueF();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (patternObject)
|
if (patternObject)
|
||||||
*d->currentPage << "/Pat" << patternObject;
|
*d->currentPage << "/Pat" << patternObject;
|
||||||
*d->currentPage << "scn\n";
|
*d->currentPage << "scn\n";
|
||||||
@ -1454,9 +1436,9 @@ int QPdfEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
|
|||||||
QPdfEnginePrivate::QPdfEnginePrivate()
|
QPdfEnginePrivate::QPdfEnginePrivate()
|
||||||
: clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false),
|
: clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false),
|
||||||
needsTransform(false), pdfVersion(QPdfEngine::Version_1_4),
|
needsTransform(false), pdfVersion(QPdfEngine::Version_1_4),
|
||||||
|
colorModel(QPdfEngine::ColorModel::RGB),
|
||||||
outDevice(nullptr), ownsDevice(false),
|
outDevice(nullptr), ownsDevice(false),
|
||||||
embedFonts(true),
|
embedFonts(true),
|
||||||
grayscale(false),
|
|
||||||
m_pageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(10, 10, 10, 10))
|
m_pageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(10, 10, 10, 10))
|
||||||
{
|
{
|
||||||
initResources();
|
initResources();
|
||||||
@ -1511,7 +1493,9 @@ bool QPdfEngine::begin(QPaintDevice *pdev)
|
|||||||
d->catalog = 0;
|
d->catalog = 0;
|
||||||
d->info = 0;
|
d->info = 0;
|
||||||
d->graphicsState = 0;
|
d->graphicsState = 0;
|
||||||
d->patternColorSpace = 0;
|
d->patternColorSpaceRGB = 0;
|
||||||
|
d->patternColorSpaceGrayscale = 0;
|
||||||
|
d->patternColorSpaceCMYK = 0;
|
||||||
d->simplePen = false;
|
d->simplePen = false;
|
||||||
d->needsTransform = false;
|
d->needsTransform = false;
|
||||||
|
|
||||||
@ -1629,10 +1613,99 @@ void QPdfEnginePrivate::writeHeader()
|
|||||||
">>\n"
|
">>\n"
|
||||||
"endobj\n");
|
"endobj\n");
|
||||||
|
|
||||||
// color space for pattern
|
// color spaces for pattern
|
||||||
patternColorSpace = addXrefEntry(-1);
|
patternColorSpaceRGB = addXrefEntry(-1);
|
||||||
xprintf("[/Pattern /DeviceRGB]\n"
|
xprintf("[/Pattern /DeviceRGB]\n"
|
||||||
"endobj\n");
|
"endobj\n");
|
||||||
|
patternColorSpaceGrayscale = addXrefEntry(-1);
|
||||||
|
xprintf("[/Pattern /DeviceGray]\n"
|
||||||
|
"endobj\n");
|
||||||
|
patternColorSpaceCMYK = addXrefEntry(-1);
|
||||||
|
xprintf("[/Pattern /DeviceCMYK]\n"
|
||||||
|
"endobj\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
QPdfEngine::ColorModel QPdfEnginePrivate::colorModelForColor(const QColor &color) const
|
||||||
|
{
|
||||||
|
switch (colorModel) {
|
||||||
|
case QPdfEngine::ColorModel::RGB:
|
||||||
|
case QPdfEngine::ColorModel::Grayscale:
|
||||||
|
case QPdfEngine::ColorModel::CMYK:
|
||||||
|
return colorModel;
|
||||||
|
case QPdfEngine::ColorModel::Auto:
|
||||||
|
switch (color.spec()) {
|
||||||
|
case QColor::Invalid:
|
||||||
|
case QColor::Rgb:
|
||||||
|
case QColor::Hsv:
|
||||||
|
case QColor::Hsl:
|
||||||
|
case QColor::ExtendedRgb:
|
||||||
|
return QPdfEngine::ColorModel::RGB;
|
||||||
|
case QColor::Cmyk:
|
||||||
|
return QPdfEngine::ColorModel::CMYK;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_UNREACHABLE_RETURN(QPdfEngine::ColorModel::RGB);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QPdfEnginePrivate::writeColor(ColorDomain domain, const QColor &color)
|
||||||
|
{
|
||||||
|
// Switch to the right colorspace.
|
||||||
|
// For simplicity: do it even if it redundant (= already in that colorspace)
|
||||||
|
const QPdfEngine::ColorModel actualColorModel = colorModelForColor(color);
|
||||||
|
|
||||||
|
switch (actualColorModel) {
|
||||||
|
case QPdfEngine::ColorModel::RGB:
|
||||||
|
case QPdfEngine::ColorModel::Grayscale:
|
||||||
|
switch (domain) {
|
||||||
|
case ColorDomain::Stroking:
|
||||||
|
*currentPage << "/CSp CS\n"; break;
|
||||||
|
case ColorDomain::NonStroking:
|
||||||
|
*currentPage << "/CSp cs\n"; break;
|
||||||
|
case ColorDomain::NonStrokingPattern:
|
||||||
|
*currentPage << "/PCSp cs\n"; break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QPdfEngine::ColorModel::CMYK:
|
||||||
|
switch (domain) {
|
||||||
|
case ColorDomain::Stroking:
|
||||||
|
*currentPage << "/CSpcmyk CS\n"; break;
|
||||||
|
case ColorDomain::NonStroking:
|
||||||
|
*currentPage << "/CSpcmyk cs\n"; break;
|
||||||
|
case ColorDomain::NonStrokingPattern:
|
||||||
|
*currentPage << "/PCSpcmyk cs\n"; break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QPdfEngine::ColorModel::Auto:
|
||||||
|
Q_UNREACHABLE_RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we also have a color specified, write it out.
|
||||||
|
if (!color.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (actualColorModel) {
|
||||||
|
case QPdfEngine::ColorModel::RGB:
|
||||||
|
*currentPage << color.redF()
|
||||||
|
<< color.greenF()
|
||||||
|
<< color.blueF();
|
||||||
|
break;
|
||||||
|
case QPdfEngine::ColorModel::Grayscale: {
|
||||||
|
const qreal gray = qGray(color.rgba()) / 255.;
|
||||||
|
*currentPage << gray << gray << gray;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QPdfEngine::ColorModel::CMYK:
|
||||||
|
*currentPage << color.cyanF()
|
||||||
|
<< color.magentaF()
|
||||||
|
<< color.yellowF()
|
||||||
|
<< color.blackF();
|
||||||
|
break;
|
||||||
|
case QPdfEngine::ColorModel::Auto:
|
||||||
|
Q_UNREACHABLE_RETURN();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QPdfEnginePrivate::writeInfo()
|
void QPdfEnginePrivate::writeInfo()
|
||||||
@ -2078,12 +2151,18 @@ void QPdfEnginePrivate::writePage()
|
|||||||
xprintf("<<\n"
|
xprintf("<<\n"
|
||||||
"/ColorSpace <<\n"
|
"/ColorSpace <<\n"
|
||||||
"/PCSp %d 0 R\n"
|
"/PCSp %d 0 R\n"
|
||||||
|
"/PCSpg %d 0 R\n"
|
||||||
|
"/PCSpcmyk %d 0 R\n"
|
||||||
"/CSp /DeviceRGB\n"
|
"/CSp /DeviceRGB\n"
|
||||||
"/CSpg /DeviceGray\n"
|
"/CSpg /DeviceGray\n"
|
||||||
|
"/CSpcmyk /DeviceCMYK\n"
|
||||||
">>\n"
|
">>\n"
|
||||||
"/ExtGState <<\n"
|
"/ExtGState <<\n"
|
||||||
"/GSa %d 0 R\n",
|
"/GSa %d 0 R\n",
|
||||||
patternColorSpace, graphicsState);
|
patternColorSpaceRGB,
|
||||||
|
patternColorSpaceGrayscale,
|
||||||
|
patternColorSpaceCMYK,
|
||||||
|
graphicsState);
|
||||||
|
|
||||||
for (int i = 0; i < currentPage->graphicStates.size(); ++i)
|
for (int i = 0; i < currentPage->graphicStates.size(); ++i)
|
||||||
xprintf("/GState%d %d 0 R\n", currentPage->graphicStates.at(i), currentPage->graphicStates.at(i));
|
xprintf("/GState%d %d 0 R\n", currentPage->graphicStates.at(i), currentPage->graphicStates.at(i));
|
||||||
@ -2396,7 +2475,22 @@ struct QGradientBound {
|
|||||||
};
|
};
|
||||||
Q_DECLARE_TYPEINFO(QGradientBound, Q_PRIMITIVE_TYPE);
|
Q_DECLARE_TYPEINFO(QGradientBound, Q_PRIMITIVE_TYPE);
|
||||||
|
|
||||||
int QPdfEnginePrivate::createShadingFunction(const QGradient *gradient, int from, int to, bool reflect, bool alpha)
|
void QPdfEnginePrivate::ShadingFunctionResult::writeColorSpace(QPdf::ByteStream *stream) const
|
||||||
|
{
|
||||||
|
*stream << "/ColorSpace ";
|
||||||
|
switch (colorModel) {
|
||||||
|
case QPdfEngine::ColorModel::RGB:
|
||||||
|
case QPdfEngine::ColorModel::Grayscale:
|
||||||
|
*stream << "/DeviceRGB\n"; break;
|
||||||
|
case QPdfEngine::ColorModel::CMYK:
|
||||||
|
*stream << "/DeviceCMYK\n"; break;
|
||||||
|
case QPdfEngine::ColorModel::Auto:
|
||||||
|
Q_UNREACHABLE(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPdfEnginePrivate::ShadingFunctionResult
|
||||||
|
QPdfEnginePrivate::createShadingFunction(const QGradient *gradient, int from, int to, bool reflect, bool alpha)
|
||||||
{
|
{
|
||||||
QGradientStops stops = gradient->stops();
|
QGradientStops stops = gradient->stops();
|
||||||
if (stops.isEmpty()) {
|
if (stops.isEmpty()) {
|
||||||
@ -2408,6 +2502,35 @@ int QPdfEnginePrivate::createShadingFunction(const QGradient *gradient, int from
|
|||||||
if (stops.at(stops.size() - 1).first < 1)
|
if (stops.at(stops.size() - 1).first < 1)
|
||||||
stops.append(QGradientStop(1, stops.at(stops.size() - 1).second));
|
stops.append(QGradientStop(1, stops.at(stops.size() - 1).second));
|
||||||
|
|
||||||
|
// Color to use which colorspace to use
|
||||||
|
const QColor referenceColor = stops.constFirst().second;
|
||||||
|
|
||||||
|
switch (colorModel) {
|
||||||
|
case QPdfEngine::ColorModel::RGB:
|
||||||
|
case QPdfEngine::ColorModel::Grayscale:
|
||||||
|
case QPdfEngine::ColorModel::CMYK:
|
||||||
|
break;
|
||||||
|
case QPdfEngine::ColorModel::Auto: {
|
||||||
|
// Make sure that all the stops have the same color spec
|
||||||
|
// (we don't support anything else)
|
||||||
|
const QColor::Spec referenceSpec = referenceColor.spec();
|
||||||
|
bool warned = false;
|
||||||
|
for (QGradientStop &stop : stops) {
|
||||||
|
if (stop.second.spec() != referenceSpec) {
|
||||||
|
if (!warned) {
|
||||||
|
qWarning("QPdfEngine: unable to create a gradient between colors of different spec");
|
||||||
|
warned = true;
|
||||||
|
}
|
||||||
|
stop.second = stop.second.convertTo(referenceSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShadingFunctionResult result;
|
||||||
|
result.colorModel = colorModelForColor(referenceColor);
|
||||||
|
|
||||||
QList<int> functions;
|
QList<int> functions;
|
||||||
const int numStops = stops.size();
|
const int numStops = stops.size();
|
||||||
functions.reserve(numStops - 1);
|
functions.reserve(numStops - 1);
|
||||||
@ -2423,8 +2546,30 @@ int QPdfEnginePrivate::createShadingFunction(const QGradient *gradient, int from
|
|||||||
s << "/C0 [" << stops.at(i).second.alphaF() << "]\n"
|
s << "/C0 [" << stops.at(i).second.alphaF() << "]\n"
|
||||||
"/C1 [" << stops.at(i + 1).second.alphaF() << "]\n";
|
"/C1 [" << stops.at(i + 1).second.alphaF() << "]\n";
|
||||||
} else {
|
} else {
|
||||||
|
switch (result.colorModel) {
|
||||||
|
case QPdfEngine::ColorModel::RGB:
|
||||||
|
case QPdfEngine::ColorModel::Grayscale:
|
||||||
|
// For backwards compatibility, Grayscale emits RGB colors
|
||||||
s << "/C0 [" << stops.at(i).second.redF() << stops.at(i).second.greenF() << stops.at(i).second.blueF() << "]\n"
|
s << "/C0 [" << stops.at(i).second.redF() << stops.at(i).second.greenF() << stops.at(i).second.blueF() << "]\n"
|
||||||
"/C1 [" << stops.at(i + 1).second.redF() << stops.at(i + 1).second.greenF() << stops.at(i + 1).second.blueF() << "]\n";
|
"/C1 [" << stops.at(i + 1).second.redF() << stops.at(i + 1).second.greenF() << stops.at(i + 1).second.blueF() << "]\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QPdfEngine::ColorModel::CMYK:
|
||||||
|
s << "/C0 [" << stops.at(i).second.cyanF()
|
||||||
|
<< stops.at(i).second.magentaF()
|
||||||
|
<< stops.at(i).second.yellowF()
|
||||||
|
<< stops.at(i).second.blackF() << "]\n"
|
||||||
|
"/C1 [" << stops.at(i + 1).second.cyanF()
|
||||||
|
<< stops.at(i + 1).second.magentaF()
|
||||||
|
<< stops.at(i + 1).second.yellowF()
|
||||||
|
<< stops.at(i + 1).second.blackF() << "]\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QPdfEngine::ColorModel::Auto:
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
s << ">>\n"
|
s << ">>\n"
|
||||||
"endobj\n";
|
"endobj\n";
|
||||||
@ -2492,7 +2637,8 @@ int QPdfEnginePrivate::createShadingFunction(const QGradient *gradient, int from
|
|||||||
} else {
|
} else {
|
||||||
function = functions.at(0);
|
function = functions.at(0);
|
||||||
}
|
}
|
||||||
return function;
|
result.function = function;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int QPdfEnginePrivate::generateLinearGradientShader(const QLinearGradient *gradient, const QTransform &matrix, bool alpha)
|
int QPdfEnginePrivate::generateLinearGradientShader(const QLinearGradient *gradient, const QTransform &matrix, bool alpha)
|
||||||
@ -2538,17 +2684,22 @@ int QPdfEnginePrivate::generateLinearGradientShader(const QLinearGradient *gradi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int function = createShadingFunction(gradient, from, to, reflect, alpha);
|
const auto shadingFunctionResult = createShadingFunction(gradient, from, to, reflect, alpha);
|
||||||
|
|
||||||
QByteArray shader;
|
QByteArray shader;
|
||||||
QPdf::ByteStream s(&shader);
|
QPdf::ByteStream s(&shader);
|
||||||
s << "<<\n"
|
s << "<<\n"
|
||||||
"/ShadingType 2\n"
|
"/ShadingType 2\n";
|
||||||
"/ColorSpace " << (alpha ? "/DeviceGray\n" : "/DeviceRGB\n") <<
|
|
||||||
"/AntiAlias true\n"
|
if (alpha)
|
||||||
|
s << "/ColorSpace /DeviceGray\n";
|
||||||
|
else
|
||||||
|
shadingFunctionResult.writeColorSpace(&s);
|
||||||
|
|
||||||
|
s << "/AntiAlias true\n"
|
||||||
"/Coords [" << start.x() << start.y() << stop.x() << stop.y() << "]\n"
|
"/Coords [" << start.x() << start.y() << stop.x() << stop.y() << "]\n"
|
||||||
"/Extend [true true]\n"
|
"/Extend [true true]\n"
|
||||||
"/Function " << function << "0 R\n"
|
"/Function " << shadingFunctionResult.function << "0 R\n"
|
||||||
">>\n"
|
">>\n"
|
||||||
"endobj\n";
|
"endobj\n";
|
||||||
int shaderObject = addXrefEntry(-1);
|
int shaderObject = addXrefEntry(-1);
|
||||||
@ -2606,18 +2757,23 @@ int QPdfEnginePrivate::generateRadialGradientShader(const QRadialGradient *gradi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int function = createShadingFunction(gradient, from, to, reflect, alpha);
|
const auto shadingFunctionResult = createShadingFunction(gradient, from, to, reflect, alpha);
|
||||||
|
|
||||||
QByteArray shader;
|
QByteArray shader;
|
||||||
QPdf::ByteStream s(&shader);
|
QPdf::ByteStream s(&shader);
|
||||||
s << "<<\n"
|
s << "<<\n"
|
||||||
"/ShadingType 3\n"
|
"/ShadingType 3\n";
|
||||||
"/ColorSpace " << (alpha ? "/DeviceGray\n" : "/DeviceRGB\n") <<
|
|
||||||
"/AntiAlias true\n"
|
if (alpha)
|
||||||
|
s << "/ColorSpace /DeviceGray\n";
|
||||||
|
else
|
||||||
|
shadingFunctionResult.writeColorSpace(&s);
|
||||||
|
|
||||||
|
s << "/AntiAlias true\n"
|
||||||
"/Domain [0 1]\n"
|
"/Domain [0 1]\n"
|
||||||
"/Coords [" << p0.x() << p0.y() << r0 << p1.x() << p1.y() << r1 << "]\n"
|
"/Coords [" << p0.x() << p0.y() << r0 << p1.x() << p1.y() << r1 << "]\n"
|
||||||
"/Extend [true true]\n"
|
"/Extend [true true]\n"
|
||||||
"/Function " << function << "0 R\n"
|
"/Function " << shadingFunctionResult.function << "0 R\n"
|
||||||
">>\n"
|
">>\n"
|
||||||
"endobj\n";
|
"endobj\n";
|
||||||
int shaderObject = addXrefEntry(-1);
|
int shaderObject = addXrefEntry(-1);
|
||||||
@ -2856,6 +3012,7 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, bool lossless,
|
|||||||
|
|
||||||
QImage image = img;
|
QImage image = img;
|
||||||
QImage::Format format = image.format();
|
QImage::Format format = image.format();
|
||||||
|
const bool grayscale = (colorModel == QPdfEngine::ColorModel::Grayscale);
|
||||||
|
|
||||||
if (pdfVersion == QPdfEngine::Version_A1b) {
|
if (pdfVersion == QPdfEngine::Version_A1b) {
|
||||||
if (image.hasAlphaChannel()) {
|
if (image.hasAlphaChannel()) {
|
||||||
|
@ -142,7 +142,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
QPdfEngine();
|
QPdfEngine();
|
||||||
QPdfEngine(QPdfEnginePrivate &d);
|
explicit QPdfEngine(QPdfEnginePrivate &d);
|
||||||
~QPdfEngine() {}
|
~QPdfEngine() {}
|
||||||
|
|
||||||
void setOutputFilename(const QString &filename);
|
void setOutputFilename(const QString &filename);
|
||||||
@ -157,6 +157,18 @@ public:
|
|||||||
|
|
||||||
void addFileAttachment(const QString &fileName, const QByteArray &data, const QString &mimeType);
|
void addFileAttachment(const QString &fileName, const QByteArray &data, const QString &mimeType);
|
||||||
|
|
||||||
|
// keep in sync with QPdfWriter
|
||||||
|
enum class ColorModel
|
||||||
|
{
|
||||||
|
RGB,
|
||||||
|
Grayscale,
|
||||||
|
CMYK,
|
||||||
|
Auto,
|
||||||
|
};
|
||||||
|
|
||||||
|
ColorModel colorModel() const;
|
||||||
|
void setColorModel(ColorModel model);
|
||||||
|
|
||||||
// reimplementations QPaintEngine
|
// reimplementations QPaintEngine
|
||||||
bool begin(QPaintDevice *pdev) override;
|
bool begin(QPaintDevice *pdev) override;
|
||||||
bool end() override;
|
bool end() override;
|
||||||
@ -240,6 +252,7 @@ public:
|
|||||||
bool needsTransform;
|
bool needsTransform;
|
||||||
qreal opacity;
|
qreal opacity;
|
||||||
QPdfEngine::PdfVersion pdfVersion;
|
QPdfEngine::PdfVersion pdfVersion;
|
||||||
|
QPdfEngine::ColorModel colorModel;
|
||||||
|
|
||||||
QHash<QFontEngine::FaceId, QFontSubset *> fonts;
|
QHash<QFontEngine::FaceId, QFontSubset *> fonts;
|
||||||
|
|
||||||
@ -255,7 +268,6 @@ public:
|
|||||||
QString creator;
|
QString creator;
|
||||||
bool embedFonts;
|
bool embedFonts;
|
||||||
int resolution;
|
int resolution;
|
||||||
bool grayscale;
|
|
||||||
|
|
||||||
// Page layout: size, orientation and margins
|
// Page layout: size, orientation and margins
|
||||||
QPageLayout m_pageLayout;
|
QPageLayout m_pageLayout;
|
||||||
@ -265,8 +277,22 @@ private:
|
|||||||
int generateGradientShader(const QGradient *gradient, const QTransform &matrix, bool alpha = false);
|
int generateGradientShader(const QGradient *gradient, const QTransform &matrix, bool alpha = false);
|
||||||
int generateLinearGradientShader(const QLinearGradient *lg, const QTransform &matrix, bool alpha);
|
int generateLinearGradientShader(const QLinearGradient *lg, const QTransform &matrix, bool alpha);
|
||||||
int generateRadialGradientShader(const QRadialGradient *gradient, const QTransform &matrix, bool alpha);
|
int generateRadialGradientShader(const QRadialGradient *gradient, const QTransform &matrix, bool alpha);
|
||||||
int createShadingFunction(const QGradient *gradient, int from, int to, bool reflect, bool alpha);
|
struct ShadingFunctionResult
|
||||||
|
{
|
||||||
|
int function;
|
||||||
|
QPdfEngine::ColorModel colorModel;
|
||||||
|
void writeColorSpace(QPdf::ByteStream *stream) const;
|
||||||
|
};
|
||||||
|
ShadingFunctionResult createShadingFunction(const QGradient *gradient, int from, int to, bool reflect, bool alpha);
|
||||||
|
|
||||||
|
enum class ColorDomain {
|
||||||
|
Stroking,
|
||||||
|
NonStroking,
|
||||||
|
NonStrokingPattern,
|
||||||
|
};
|
||||||
|
|
||||||
|
QPdfEngine::ColorModel colorModelForColor(const QColor &color) const;
|
||||||
|
void writeColor(ColorDomain domain, const QColor &color);
|
||||||
void writeInfo();
|
void writeInfo();
|
||||||
int writeXmpDcumentMetaData();
|
int writeXmpDcumentMetaData();
|
||||||
int writeOutputIntent();
|
int writeOutputIntent();
|
||||||
@ -316,7 +342,10 @@ private:
|
|||||||
|
|
||||||
// various PDF objects
|
// various PDF objects
|
||||||
int pageRoot, namesRoot, destsRoot, attachmentsRoot, catalog, info;
|
int pageRoot, namesRoot, destsRoot, attachmentsRoot, catalog, info;
|
||||||
int graphicsState, patternColorSpace;
|
int graphicsState;
|
||||||
|
int patternColorSpaceRGB;
|
||||||
|
int patternColorSpaceGrayscale;
|
||||||
|
int patternColorSpaceCMYK;
|
||||||
QList<uint> pages;
|
QList<uint> pages;
|
||||||
QHash<qint64, uint> imageCache;
|
QHash<qint64, uint> imageCache;
|
||||||
QHash<QPair<uint, uint>, uint > alphaCache;
|
QHash<QPair<uint, uint>, uint > alphaCache;
|
||||||
|
@ -295,6 +295,52 @@ bool QPdfWriter::newPage()
|
|||||||
return d->engine->newPage();
|
return d->engine->newPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\enum QPdfWriter::ColorModel
|
||||||
|
\since 6.8
|
||||||
|
|
||||||
|
This enumeration describes the way in which the PDF engine interprets
|
||||||
|
stroking and filling colors, set as a QPainter's pen or brush (via
|
||||||
|
QPen and QBrush).
|
||||||
|
|
||||||
|
\value RGB All colors are converted to RGB and saved as such in the
|
||||||
|
PDF. This is the default.
|
||||||
|
|
||||||
|
\value Grayscale All colors are converted to grayscale. For backwards
|
||||||
|
compatibility, they are emitted in the PDF output as RGB colors, with
|
||||||
|
identical quantities of red, green and blue.
|
||||||
|
|
||||||
|
\value CMYK All colors are converted to CMYK and saved as such.
|
||||||
|
|
||||||
|
\value Auto RGB colors are emitted as RGB; CMYK colors are emitted as
|
||||||
|
CMYK. Colors of any other color spec are converted to RGB.
|
||||||
|
|
||||||
|
\sa QColor, QGradient
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 6.8
|
||||||
|
|
||||||
|
Returns the color model used by this PDF writer.
|
||||||
|
The default is QPdfWriter::ColorModel::RGB.
|
||||||
|
*/
|
||||||
|
QPdfWriter::ColorModel QPdfWriter::colorModel() const
|
||||||
|
{
|
||||||
|
Q_D(const QPdfWriter);
|
||||||
|
return static_cast<ColorModel>(d->engine->d_func()->colorModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 6.8
|
||||||
|
|
||||||
|
Sets the color model used by this PDF writer to \a model.
|
||||||
|
*/
|
||||||
|
void QPdfWriter::setColorModel(ColorModel model)
|
||||||
|
{
|
||||||
|
Q_D(QPdfWriter);
|
||||||
|
d->engine->d_func()->colorModel = static_cast<QPdfEngine::ColorModel>(model);
|
||||||
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
#include "moc_qpdfwriter.cpp"
|
#include "moc_qpdfwriter.cpp"
|
||||||
|
@ -44,6 +44,18 @@ public:
|
|||||||
|
|
||||||
void addFileAttachment(const QString &fileName, const QByteArray &data, const QString &mimeType = QString());
|
void addFileAttachment(const QString &fileName, const QByteArray &data, const QString &mimeType = QString());
|
||||||
|
|
||||||
|
enum class ColorModel
|
||||||
|
{
|
||||||
|
RGB,
|
||||||
|
Grayscale,
|
||||||
|
CMYK,
|
||||||
|
Auto,
|
||||||
|
};
|
||||||
|
Q_ENUM(ColorModel)
|
||||||
|
|
||||||
|
ColorModel colorModel() const;
|
||||||
|
void setColorModel(ColorModel model);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QPaintEngine *paintEngine() const override;
|
QPaintEngine *paintEngine() const override;
|
||||||
int metric(PaintDeviceMetric id) const override;
|
int metric(PaintDeviceMetric id) const override;
|
||||||
|
@ -249,9 +249,12 @@ void QCupsPrintEnginePrivate::changePrinter(const QString &newPrinter)
|
|||||||
duplex = m_printDevice.defaultDuplexMode();
|
duplex = m_printDevice.defaultDuplexMode();
|
||||||
duplexRequestedExplicitly = false;
|
duplexRequestedExplicitly = false;
|
||||||
}
|
}
|
||||||
QPrint::ColorMode colorMode = grayscale ? QPrint::GrayScale : QPrint::Color;
|
QPrint::ColorMode colorMode = static_cast<QPrint::ColorMode>(printerColorMode());
|
||||||
if (!m_printDevice.supportedColorModes().contains(colorMode))
|
if (!m_printDevice.supportedColorModes().contains(colorMode)) {
|
||||||
grayscale = m_printDevice.defaultColorMode() == QPrint::GrayScale;
|
colorModel = (m_printDevice.defaultColorMode() == QPrint::GrayScale)
|
||||||
|
? QPdfEngine::ColorModel::Grayscale
|
||||||
|
: QPdfEngine::ColorModel::RGB;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the equivalent page size for this printer as supported names may be different
|
// Get the equivalent page size for this printer as supported names may be different
|
||||||
if (m_printDevice.supportedPageSize(m_pageLayout.pageSize()).isValid())
|
if (m_printDevice.supportedPageSize(m_pageLayout.pageSize()).isValid())
|
||||||
|
@ -68,6 +68,7 @@ namespace QPrint {
|
|||||||
DuplexShortSide
|
DuplexShortSide
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Note: Keep in sync with QPrinter::ColorMode
|
||||||
enum ColorMode {
|
enum ColorMode {
|
||||||
GrayScale,
|
GrayScale,
|
||||||
Color
|
Color
|
||||||
|
@ -104,7 +104,14 @@ void QPdfPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va
|
|||||||
d->collate = value.toBool();
|
d->collate = value.toBool();
|
||||||
break;
|
break;
|
||||||
case PPK_ColorMode:
|
case PPK_ColorMode:
|
||||||
d->grayscale = (QPrinter::ColorMode(value.toInt()) == QPrinter::GrayScale);
|
switch (QPrinter::ColorMode(value.toInt())) {
|
||||||
|
case QPrinter::GrayScale:
|
||||||
|
d->colorModel = QPdfEngine::ColorModel::Grayscale;
|
||||||
|
break;
|
||||||
|
case QPrinter::Color:
|
||||||
|
d->colorModel = QPdfEngine::ColorModel::RGB;
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PPK_Creator:
|
case PPK_Creator:
|
||||||
d->creator = value.toString();
|
d->creator = value.toString();
|
||||||
@ -221,7 +228,7 @@ QVariant QPdfPrintEngine::property(PrintEnginePropertyKey key) const
|
|||||||
ret = d->collate;
|
ret = d->collate;
|
||||||
break;
|
break;
|
||||||
case PPK_ColorMode:
|
case PPK_ColorMode:
|
||||||
ret = d->grayscale ? QPrinter::GrayScale : QPrinter::Color;
|
ret = d->printerColorMode();
|
||||||
break;
|
break;
|
||||||
case PPK_Creator:
|
case PPK_Creator:
|
||||||
ret = d->creator;
|
ret = d->creator;
|
||||||
@ -367,6 +374,22 @@ QPdfPrintEnginePrivate::~QPdfPrintEnginePrivate()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPrinter::ColorMode QPdfPrintEnginePrivate::printerColorMode() const
|
||||||
|
{
|
||||||
|
switch (colorModel) {
|
||||||
|
case QPdfEngine::ColorModel::RGB:
|
||||||
|
case QPdfEngine::ColorModel::CMYK:
|
||||||
|
case QPdfEngine::ColorModel::Auto:
|
||||||
|
return QPrinter::Color;
|
||||||
|
case QPdfEngine::ColorModel::Grayscale:
|
||||||
|
return QPrinter::GrayScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
return QPrinter::Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // QT_NO_PRINTER
|
#endif // QT_NO_PRINTER
|
||||||
|
@ -79,6 +79,8 @@ public:
|
|||||||
QPdfPrintEnginePrivate(QPrinter::PrinterMode m);
|
QPdfPrintEnginePrivate(QPrinter::PrinterMode m);
|
||||||
~QPdfPrintEnginePrivate();
|
~QPdfPrintEnginePrivate();
|
||||||
|
|
||||||
|
QPrinter::ColorMode printerColorMode() const;
|
||||||
|
|
||||||
virtual bool openPrintDevice();
|
virtual bool openPrintDevice();
|
||||||
virtual void closePrintDevice();
|
virtual void closePrintDevice();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user