1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <private/qprintengine_qws_p.h>
46 #include <private/qpaintengine_raster_p.h>
50 #include <QCopChannel>
54 #define MM(n) int((n * 720 + 127) / 254)
55 #define IN(n) int(n * 72)
57 extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
59 QtopiaPrintEngine::QtopiaPrintEngine(QPrinter::PrinterMode mode)
60 : QPaintEngine(*(new QtopiaPrintEnginePrivate( mode )))
62 d_func()->initialize();
65 bool QtopiaPrintEngine::begin(QPaintDevice *)
67 Q_D(QtopiaPrintEngine);
68 Q_ASSERT_X(d->printerState == QPrinter::Idle, "QtopiaPrintEngine", "printer already active");
70 // Create a new off-screen monochrome image to handle the drawing process.
71 QSize size = paperRect().size();
74 d->pageImage = new QImage( size, QImage::Format_RGB32 );
75 if ( !(d->pageImage) )
78 // Recreate the paint engine on the new image.
79 delete d->_paintEngine;
81 d->paintEngine()->state = state;
83 // Begin the paint process on the image.
84 if (!d->paintEngine()->begin(d->pageImage))
87 // Clear the first page to all-white.
90 // Clear the print buffer and output the image header.
92 d->writeG3FaxHeader();
94 // The print engine is currently active.
95 d->printerState = QPrinter::Active;
99 bool QtopiaPrintEngine::end()
101 Q_D(QtopiaPrintEngine);
103 d->paintEngine()->end();
105 // Flush the last page.
108 // Output the fax data to a file (TODO: send to the print queuing daemon).
110 if ( !d->outputFileName.isEmpty() )
111 filename = QString::fromLocal8Bit(qgetenv("HOME").constData()) + QLatin1String("/Documents/") + d->outputFileName;
113 filename = QString::fromLocal8Bit(qgetenv("HOME").constData()) + QLatin1String("/tmp/qwsfax.tiff");
115 setProperty(QPrintEngine::PPK_OutputFileName, filename);
116 QFile file( filename );
117 if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) {
118 qDebug( "Failed to open %s for printer output",
119 filename.toLatin1().constData() );
121 file.write( d->buffer.data() );
125 // Free up the memory for the image buffer.
128 // Finalize the print job.
129 d->printerState = QPrinter::Idle;
132 QMap<QString, QVariant> map;
133 for ( int x = 0; x <= QPrintEngine::PPK_Duplex; x++ )
134 map.insert( QString::number(x), property((QPrintEngine::PrintEnginePropertyKey)(x)));
135 QVariant variant(map);
138 QDataStream out(&data, QIODevice::WriteOnly);
140 QCopChannel::send(QLatin1String("QPE/Service/Print"), QLatin1String("print(QVariant)"), data);
145 QPaintEngine *QtopiaPrintEngine::paintEngine() const
147 return const_cast<QtopiaPrintEnginePrivate *>(d_func())->paintEngine();
150 void QtopiaPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
152 Q_D(QtopiaPrintEngine);
153 Q_ASSERT(d->printerState == QPrinter::Active);
154 d->paintEngine()->drawPixmap(r, pm, sr);
157 void QtopiaPrintEngine::drawTextItem(const QPointF &p, const QTextItem &ti)
159 Q_D(QtopiaPrintEngine);
160 Q_ASSERT(d->printerState == QPrinter::Active);
161 d->paintEngine()->drawTextItem(p, ti);
164 void QtopiaPrintEngine::updateState(const QPaintEngineState &state)
166 Q_D(QtopiaPrintEngine);
167 d->paintEngine()->updateState(state);
170 QRect QtopiaPrintEngine::paperRect() const
172 QSizeF s = qt_paperSizeToQSizeF(d_func()->paperSize);
173 s.rwidth() = MM(s.width());
174 s.rheight() = MM(s.height());
175 int w = qRound(s.width()*d_func()->resolution/72.);
176 int h = qRound(s.height()*d_func()->resolution/72.);
177 if (d_func()->orientation == QPrinter::Portrait)
178 return QRect(0, 0, w, h);
180 return QRect(0, 0, h, w);
183 QRect QtopiaPrintEngine::pageRect() const
185 QRect r = paperRect();
186 if (d_func()->fullPage)
188 // would be nice to get better margins than this.
189 return QRect(d_func()->resolution/3, d_func()->resolution/3, r.width()-2*d_func()->resolution/3, r.height()-2*d_func()->resolution/3);
192 bool QtopiaPrintEngine::newPage()
196 ++(d_func()->pageNumber);
200 bool QtopiaPrintEngine::abort()
205 QPrinter::PrinterState QtopiaPrintEngine::printerState() const
207 return d_func()->printerState;
210 int QtopiaPrintEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
213 QRect r = d_func()->fullPage ? paperRect() : pageRect();
214 switch (metricType) {
215 case QPaintDevice::PdmWidth:
218 case QPaintDevice::PdmHeight:
221 case QPaintDevice::PdmDpiX:
222 val = d_func()->resolution;
224 case QPaintDevice::PdmDpiY:
225 val = d_func()->resolution;
227 case QPaintDevice::PdmPhysicalDpiX:
228 case QPaintDevice::PdmPhysicalDpiY:
229 val = QT_QWS_PRINTER_DEFAULT_DPI;
231 case QPaintDevice::PdmWidthMM:
232 val = qRound(r.width()*25.4/d_func()->resolution);
234 case QPaintDevice::PdmHeightMM:
235 val = qRound(r.height()*25.4/d_func()->resolution);
237 case QPaintDevice::PdmNumColors:
240 case QPaintDevice::PdmDepth:
244 qWarning("QtopiaPrintEngine::metric: Invalid metric command");
250 QVariant QtopiaPrintEngine::property(PrintEnginePropertyKey key) const
252 Q_D(const QtopiaPrintEngine);
256 case PPK_CollateCopies:
257 ret = d->collateCopies;
265 case PPK_DocumentName:
271 case PPK_CopyCount: // fallthrough
272 case PPK_NumberOfCopies:
275 case PPK_SupportsMultipleCopies:
278 case PPK_Orientation:
279 ret = d->orientation;
281 case PPK_OutputFileName:
282 ret = d->outputFileName;
296 case PPK_PaperSource:
297 ret = d->paperSource;
299 case PPK_PrinterName:
300 ret = d->printerName;
302 case PPK_PrinterProgram:
303 ret = d->printProgram;
308 case PPK_SupportedResolutions:
309 ret = QList<QVariant>() << QT_QWS_PRINTER_DEFAULT_DPI;
317 void QtopiaPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
319 Q_D(QtopiaPrintEngine);
321 case PPK_CollateCopies:
322 d->collateCopies = value.toBool();
325 d->colorMode = QPrinter::ColorMode(value.toInt());
328 d->creator = value.toString();
330 case PPK_DocumentName:
331 d->docName = value.toString();
334 d->fullPage = value.toBool();
336 case PPK_CopyCount: // fallthrough
337 case PPK_NumberOfCopies:
338 d->numCopies = value.toInt();
340 case PPK_Orientation:
341 d->orientation = QPrinter::Orientation(value.toInt());
343 case PPK_OutputFileName:
344 d->outputFileName = value.toString();
347 d->pageOrder = QPrinter::PageOrder(value.toInt());
350 d->paperSize = QPrinter::PaperSize(value.toInt());
352 case PPK_PaperSource:
353 d->paperSource = QPrinter::PaperSource(value.toInt());
354 case PPK_PrinterName:
355 d->printerName = value.toString();
357 case PPK_PrinterProgram:
358 d->printProgram = value.toString();
361 d->resolution = value.toInt();
368 void QtopiaPrintEngine::clearPage()
370 d_func()->pageImage->fill(QColor(255, 255, 255).rgb());
373 void QtopiaPrintEngine::flushPage()
375 d_func()->writeG3FaxPage();
378 QtopiaPrintEnginePrivate::~QtopiaPrintEnginePrivate()
384 void QtopiaPrintEnginePrivate::initialize()
389 QPaintEngine *QtopiaPrintEnginePrivate::paintEngine()
392 _paintEngine = new QRasterPaintEngine(pageImage);
396 void QtopiaPrintEnginePrivate::writeG3FaxHeader()
398 // Write the TIFF file magic number (little-endian TIFF).
399 buffer.append( (char)'I' );
400 buffer.append( (char)'I' );
401 buffer.append( (char)42 );
402 buffer.append( (char)0 );
404 // Leave a place-holder for the IFD offset of the first page.
405 ifdPatch = buffer.size();
406 buffer.append( (int)0 );
409 // Tag values, from RFC 2301.
410 #define TIFF_IFD_NEW_SUB_FILE_TYPE 254
411 #define TIFF_IFD_IMAGE_WIDTH 256
412 #define TIFF_IFD_IMAGE_LENGTH 257
413 #define TIFF_IFD_BITS_PER_SAMPLE 258
414 #define TIFF_IFD_COMPRESSION 259
415 #define TIFF_IFD_PHOTOMETRIC_INTERP 262
416 #define TIFF_IFD_FILL_ORDER 266
417 #define TIFF_IFD_STRIP_OFFSETS 273
418 #define TIFF_IFD_ORIENTATION 274
419 #define TIFF_IFD_SAMPLES_PER_PIXEL 277
420 #define TIFF_IFD_ROWS_PER_STRIP 278
421 #define TIFF_IFD_STRIP_BYTE_COUNTS 279
422 #define TIFF_IFD_X_RESOLUTION 282
423 #define TIFF_IFD_Y_RESOLUTION 283
424 #define TIFF_IFD_PLANAR_CONFIG 284
425 #define TIFF_IFD_T4_OPTIONS 292
426 #define TIFF_IFD_RESOLUTION_UNIT 296
427 #define TIFF_IFD_PAGE_NUMBER 297
428 #define TIFF_IFD_CLEAN_FAX_DATA 327
431 #define TIFF_TYPE_SHORT 3
432 #define TIFF_TYPE_LONG 4
433 #define TIFF_TYPE_RATIONAL 5
435 // Construct a SHORT pair from two values.
436 #define TIFF_SHORT_PAIR(a,b) (((a) & 0xFFFF) | ((b) << 16))
438 // Width of a FAX page in pixels, in the baseline specification from RFC 2301.
439 // This must be hard-wired, as per the RFC. We truncate any pixels that
440 // are beyond this limit, or pad lines to reach this limit.
441 #define TIFF_FAX_WIDTH 1728
443 void QtopiaPrintEnginePrivate::writeG3FaxPage()
445 // Pad the image file to a word boundary, just in case.
448 // Back-patch the IFD link for the previous page.
449 buffer.patch( ifdPatch, buffer.size() );
451 // Output the contents of the IFD for this page (these must be
452 // in ascending order of tag value).
453 buffer.append( (short)19 ); // Number of IFD entries.
454 writeG3IFDEntry( TIFF_IFD_NEW_SUB_FILE_TYPE, TIFF_TYPE_LONG, 1, 2 );
455 writeG3IFDEntry( TIFF_IFD_IMAGE_WIDTH, TIFF_TYPE_LONG, 1, TIFF_FAX_WIDTH );
457 ( TIFF_IFD_IMAGE_LENGTH, TIFF_TYPE_LONG, 1, pageImage->height() );
458 writeG3IFDEntry( TIFF_IFD_BITS_PER_SAMPLE, TIFF_TYPE_SHORT, 1, 1 );
459 writeG3IFDEntry( TIFF_IFD_COMPRESSION, TIFF_TYPE_SHORT, 1, 3 );
460 writeG3IFDEntry( TIFF_IFD_PHOTOMETRIC_INTERP, TIFF_TYPE_SHORT, 1, 0 );
461 writeG3IFDEntry( TIFF_IFD_FILL_ORDER, TIFF_TYPE_SHORT, 1, 1 );
463 writeG3IFDEntry( TIFF_IFD_STRIP_OFFSETS, TIFF_TYPE_LONG, 1, 0 );
464 writeG3IFDEntry( TIFF_IFD_ORIENTATION, TIFF_TYPE_SHORT, 1, 1 );
465 writeG3IFDEntry( TIFF_IFD_SAMPLES_PER_PIXEL, TIFF_TYPE_SHORT, 1, 1 );
467 ( TIFF_IFD_ROWS_PER_STRIP, TIFF_TYPE_LONG, 1, pageImage->height() );
468 int stripBytes = writeG3IFDEntry
469 ( TIFF_IFD_STRIP_BYTE_COUNTS, TIFF_TYPE_LONG, 1, 0 );
471 writeG3IFDEntry( TIFF_IFD_X_RESOLUTION, TIFF_TYPE_RATIONAL, 1, 0 );
473 writeG3IFDEntry( TIFF_IFD_Y_RESOLUTION, TIFF_TYPE_RATIONAL, 1, 0 );
474 writeG3IFDEntry( TIFF_IFD_PLANAR_CONFIG, TIFF_TYPE_SHORT, 1, 1 );
475 writeG3IFDEntry( TIFF_IFD_T4_OPTIONS, TIFF_TYPE_LONG, 1, 2 );
476 writeG3IFDEntry( TIFF_IFD_RESOLUTION_UNIT, TIFF_TYPE_SHORT, 1, 2 );
477 writeG3IFDEntry( TIFF_IFD_PAGE_NUMBER, TIFF_TYPE_SHORT, 2,
478 TIFF_SHORT_PAIR( pageNumber, 0 ) );
479 writeG3IFDEntry( TIFF_IFD_CLEAN_FAX_DATA, TIFF_TYPE_SHORT, 1, 0 );
481 // Leave a place-holder for the IFD offset of the next page.
482 ifdPatch = buffer.size();
483 buffer.append( (int)0 );
485 // Output the X and Y resolutions, as rational values (usually 200/1).
486 buffer.patch( xres, buffer.size() );
487 buffer.append( (int)resolution );
488 buffer.append( (int)1 );
489 buffer.patch( yres, buffer.size() );
490 buffer.append( (int)resolution );
491 buffer.append( (int)1 );
493 // We are now at the start of the image data - set the strip offset.
494 int start = buffer.size();
495 buffer.patch( stripOffsets, start );
497 // Output the image data.
498 int width = pageImage->width();
499 QImage::Format imageFormat = pageImage->format();
500 for ( int y = 0; y < pageImage->height(); ++y ) {
501 unsigned char *scan = pageImage->scanLine(y);
502 int prev, pixel, len;
507 uint currentColor = qRgb(255, 255, 255); // start with white
509 for ( int x = 0; x < width && x < TIFF_FAX_WIDTH; ++x ) {
510 if ( imageFormat == QImage::Format_RGB32 ) {
511 // read color of the current pixel
512 uint *p = (uint *)scan + x;
514 if ( *p == currentColor ) { // if it is the same color
515 len++; // imcrement length
516 } else { // otherwise write color into the buffer
518 if ( currentColor == qRgb(0, 0, 0) )
519 writeG3BlackRun( len );
521 writeG3WhiteRun( len );
523 // initialise length and color;
527 } else if ( imageFormat == QImage::Format_Mono ) {
528 pixel = ((scan[x >> 3] & (1 << (x & 7))) != 0);
529 if ( pixel != prev ) {
531 writeG3BlackRun( len );
533 writeG3WhiteRun( len );
543 if ( imageFormat == QImage::Format_RGB32 ) {
544 // Output the last run on the line, and pad to TIFF_FAX_WIDTH.
546 if ( currentColor == qRgb(0, 0, 0) )
547 writeG3BlackRun( len );
549 writeG3WhiteRun( len );
551 if ( width < TIFF_FAX_WIDTH )
552 writeG3WhiteRun( TIFF_FAX_WIDTH - width );
553 } else if ( imageFormat == QImage::Format_Mono ) {
556 writeG3BlackRun( len );
557 if ( width < TIFF_FAX_WIDTH ) {
558 writeG3WhiteRun( TIFF_FAX_WIDTH - width );
561 if ( width < TIFF_FAX_WIDTH ) {
562 writeG3WhiteRun( len + ( TIFF_FAX_WIDTH - width ) );
564 writeG3WhiteRun( len );
571 // Flush the last partial byte, which is padded with zero fill bits.
572 if ( partialBits > 0 ) {
573 buffer.append( (char)( partialByte << ( 8 - partialBits ) ) );
578 // end of page add six EOLs
579 for ( int i = 0; i < 6; i++ )
582 // Update the byte count for the image data strip.
583 buffer.patch( stripBytes, buffer.size() - start );
586 int QtopiaPrintEnginePrivate::writeG3IFDEntry
587 ( int tag, int type, int count, int value )
589 buffer.append( (short)tag );
590 buffer.append( (short)type );
591 buffer.append( count );
592 buffer.append( value );
593 return buffer.size() - 4; // Offset of the value for back-patching.
596 void QtopiaPrintEnginePrivate::writeG3Code( int code, int bits )
598 partialByte = ( ( partialByte << bits ) | code );
600 while ( partialBits >= 8 ) {
602 buffer.append( (char)( partialByte >> partialBits ) );
606 void QtopiaPrintEnginePrivate::writeG3WhiteRun( int len )
611 } whiteCodes[64 + 27] = {
676 {0x001B, 5}, // Make up codes: 64
705 int index = 63 + (len >> 6);
706 writeG3Code( whiteCodes[index].code, whiteCodes[index].bits );
709 writeG3Code( whiteCodes[len].code, whiteCodes[len].bits );
712 void QtopiaPrintEnginePrivate::writeG3BlackRun( int len )
717 } blackCodes[64 + 27] = {
782 {0x000F, 10}, // Make up codes: 64
797 {0x0074, 13}, // 1024
798 {0x0075, 13}, // 1088
799 {0x0076, 13}, // 1152
800 {0x0077, 13}, // 1216
801 {0x0052, 13}, // 1280
802 {0x0053, 13}, // 1344
803 {0x0054, 13}, // 1408
804 {0x0055, 13}, // 1472
805 {0x005A, 13}, // 1536
806 {0x005B, 13}, // 1600
807 {0x0064, 13}, // 1664
808 {0x0065, 13}, // 1728
811 int index = 63 + (len >> 6);
812 writeG3Code( blackCodes[index].code, blackCodes[index].bits );
815 writeG3Code( blackCodes[len].code, blackCodes[len].bits );
818 void QtopiaPrintEnginePrivate::writeG3EOL()
821 if ( partialBits <= 4 ) {
822 bitToPad = 4 - partialBits;
824 bitToPad = 8 - partialBits + 4;
827 partialByte = ((partialByte << (bitToPad + 12)) | 0x0001);
828 partialBits += bitToPad + 12;
830 while ( partialBits >= 8 ) {
832 buffer.append( (char)(partialByte >> partialBits ) );
834 // writeG3Code( 0x0001, 12 );
837 void QtopiaPrintBuffer::append( short value )
840 _data.append( (char)(value >> 8) );
841 _data.append( (char)value );
843 _data.append( (char)value );
844 _data.append( (char)(value >> 8) );
848 void QtopiaPrintBuffer::append( int value )
851 _data.append( (char)(value >> 24) );
852 _data.append( (char)(value >> 16) );
853 _data.append( (char)(value >> 8) );
854 _data.append( (char)value );
856 _data.append( (char)value );
857 _data.append( (char)(value >> 8) );
858 _data.append( (char)(value >> 16) );
859 _data.append( (char)(value >> 24) );
863 void QtopiaPrintBuffer::patch( int posn, int value )
866 _data[posn] = (char)(value >> 24);
867 _data[posn + 1] = (char)(value >> 16);
868 _data[posn + 2] = (char)(value >> 8);
869 _data[posn + 3] = (char)value;
871 _data[posn] = (char)value;
872 _data[posn + 1] = (char)(value >> 8);
873 _data[posn + 2] = (char)(value >> 16);
874 _data[posn + 3] = (char)(value >> 24);
878 void QtopiaPrintBuffer::pad()
880 while ( ( _data.size() % 4 ) != 0 )
881 _data.append( (char)0 );
886 #endif // QT_NO_PRINTER