Commit 1b1ed945 authored by John Layt's avatar John Layt

Enable Okular printing features:

* Add FilePrinter class to enable printing via postscript files
* DJVU, PDF, and PS backends print FilePrinter
* All backends enable printing of bookmarked pages
* Print and Print Preview actions enabled/disabled depending on backends
  printing ability

Note that FilePrinter only works on *NIX platforms with Cups, lpr, or lp.



svn path=/trunk/KDE/kdegraphics/okular/; revision=741990
parent 2153ec5e
......@@ -43,6 +43,7 @@ set(okularcore_SRCS
core/textdocumentgenerator.cpp
core/textpage.cpp
core/utils.cpp
core/fileprinter.cpp
)
install( FILES
......@@ -64,6 +65,7 @@ install( FILES
core/textdocumentgenerator.h
core/textpage.h
core/utils.h
core/fileprinter.h
DESTINATION ${INCLUDE_INSTALL_DIR}/okular/core )
install( FILES
......
......@@ -28,6 +28,7 @@
#include <QtGui/QApplication>
#include <QtGui/QLabel>
#include <QtGui/QPrinter>
#include <QtGui/QPrintDialog>
#include <kaboutdata.h>
#include <kauthorized.h>
......@@ -2186,6 +2187,68 @@ BookmarkManager * Document::bookmarkManager() const
return d->m_bookmarkManager;
}
QList<int> Document::bookmarkedPageList() const
{
QList<int> list;
uint docPages = pages();
//pages are 0-indexed internally, but 1-indexed externally
for ( uint i = 0; i < docPages; i++ )
{
if ( bookmarkManager()->isBookmarked( i ) )
{
list << i + 1;
}
}
return list;
}
QString Document::bookmarkedPageRange() const
{
// Code formerly in Part::slotPrint()
// range detecting
QString range;
uint docPages = pages();
int startId = -1;
int endId = -1;
for ( uint i = 0; i < docPages; ++i )
{
if ( bookmarkManager()->isBookmarked( i ) )
{
if ( startId < 0 )
startId = i;
if ( endId < 0 )
endId = startId;
else
++endId;
}
else if ( startId >= 0 && endId >= 0 )
{
if ( !range.isEmpty() )
range += ',';
if ( endId - startId > 0 )
range += QString( "%1-%2" ).arg( startId + 1 ).arg( endId + 1 );
else
range += QString::number( startId + 1 );
startId = -1;
endId = -1;
}
}
if ( startId >= 0 && endId >= 0 )
{
if ( !range.isEmpty() )
range += ',';
if ( endId - startId > 0 )
range += QString( "%1-%2" ).arg( startId + 1 ).arg( endId + 1 );
else
range += QString::number( startId + 1 );
}
return range;
}
void Document::processAction( const Action * action )
{
if ( !action )
......@@ -2396,6 +2459,33 @@ void Document::processSourceReference( const SourceReference * ref )
QProcess::startDetached( p );
}
Document::PrintingType Document::printingSupport() const
{
if ( d->m_generator )
{
if ( d->m_generator->hasFeature( Generator::PrintNative ) )
{
return NativePrinting;
}
#ifndef Q_OS_WIN
if ( d->m_generator->hasFeature( Generator::PrintPostscript ) )
{
return PostscriptPrinting;
}
#endif
}
return NoPrinting;
}
bool Document::supportsPrintToFile() const
{
return d->m_generator ? d->m_generator->hasFeature( Generator::PrintToFile ) : false;
}
bool Document::print( QPrinter &printer )
{
return d->m_generator ? d->m_generator->print( printer ) : false;
......
......@@ -24,6 +24,7 @@
#include <kmimetype.h>
class QPrinter;
class QPrintDialog;
class KComponentData;
class KBookmark;
class KConfigDialog;
......@@ -415,6 +416,16 @@ class OKULAR_EXPORT Document : public QObject
*/
void processAction( const Action *action );
/**
* Returns a list of the bookmarked.pages
*/
QList<int> bookmarkedPageList() const;
/**
* Returns the range of the bookmarked.pages
*/
QString bookmarkedPageRange() const;
/**
* Processes/Executes the given source @p reference.
*/
......@@ -425,6 +436,27 @@ class OKULAR_EXPORT Document : public QObject
*/
bool canConfigurePrinter() const;
/**
* What type of printing a document supports
*/
enum PrintingType
{
NoPrinting, ///< Printing Not Supported
NativePrinting, ///< Native Cross-Platform Printing
PostscriptPrinting ///< Postscript file printing
};
/**
* Returns what sort of printing the document supports:
* Native, Postscript, None
*/
PrintingType printingSupport() const;
/**
* Returns whether the document supports printing to both PDF and PS files.
*/
bool supportsPrintToFile() const;
/**
* Prints the document to the given @p printer.
*/
......
This diff is collapsed.
/***************************************************************************
* Copyright (C) 2007 by John Layt <john@layt.net> *
* *
* FilePrinterPreview based on KPrintPreview (originally LGPL) *
* Copyright (c) 2007 Alex Merry <huntedhacker@tiscali.co.uk> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
// This Class is a temporary addition to Okular for the duration of KDE 4.0.
// In KDE 4.1 this class will either be moved to kdelibs if still required,
// or replaced with a Qt 4.4 based solution.
#ifndef FILEPRINTER_H
#define FILEPRINTER_H
#include <QList>
#include <QString>
#include <kdialog.h>
#include <okular/core/okular_export.h>
class QPrinter;
class QSize;
namespace Okular {
class OKULAR_EXPORT FilePrinter
{
public:
/** Whether file(s) get deleted by the application or by the print system.
*
* You may need to chose system deletion if your temp file clean-up
* deletes the file before the print system is finished with it.
*/
enum FileDeletePolicy { ApplicationDeletesFiles, SystemDeletesFiles };
/** Whether pages to be printed are selected by the application or the print system.
*
* If application side, then the generated file will only contain those pages
* selected by the user, so FilePrinter will print all the pages in the file.
*
* If system side, then the file will contain all the pages in the document, and
* the print system will print the users selected print range from out of the file.
*
* Note system side only works in CUPS, not LPR.
*/
enum PageSelectPolicy { ApplicationSelectsPages, SystemSelectsPages };
/** Print a file using the settings in QPrinter
*
* Only supports CUPS and LPR on *NIX. Page Range only supported in CUPS.
* Most settings unsupported by LPR, some settings unsupported by CUPS.
*
* @param printer the print settings to use
* @param file the file to print
* @param fileDeletePolicy if the application or system deletes the file
* @param pageSelectPolicy if the application or system selects the pages to print
* @param pageRange page range to print if SystemSlectsPages and user chooses Selection in Print Dialog
*
* @returns Returns exit code:
* -9 if lpr not found
* -8 if empty file name
* -7 if unable to find file
* -6 if invalid printer state
* -2 if the KProcess could not be started
* -1 if the KProcess crashed
* otherwise the KProcess exit code
*/
static int printFile( QPrinter &printer, const QString file,
FileDeletePolicy fileDeletePolicy = FilePrinter::ApplicationDeletesFiles,
PageSelectPolicy pageSelectPolicy = FilePrinter::ApplicationSelectsPages,
const QString &pageRange = QString() );
/** Print a list of files using the settings in QPrinter
*
* Only supports CUPS and LPR on *NIX.
* Most settings unsupported by LPR, some settings unsupported by CUPS.
*
* @param printer the print settings to use
* @param fileList the files to print
* @param fileDeletePolicy if the application or system deletes the file
*
* @returns Returns exit code:
* -9 if lpr not found
* -8 if empty file list
* -7 if unable to find a file
* -6 if invalid printer state
* -2 if the KProcess could not be started
* -1 if the KProcess crashed
* otherwise the KProcess exit code
*/
static int printFiles( QPrinter &printer, const QStringList &fileList,
FileDeletePolicy fileDeletePolicy = FilePrinter::ApplicationDeletesFiles );
/** Return the list of pages selected by the user in the Print Dialog
*
* @param printer the print settings to use
* @param lastPage the last page number, needed if AllPages option is selected
* @param selectedPageList list of pages to use if Selection option is selected
* @returns Returns list of pages to print
*/
static QList<int> pageList( QPrinter &printer, int lastPage, const QList<int> &selectedPageList );
/** Return the range of pages selected by the user in the Print Dialog
*
* @param printer the print settings to use
* @param lastPage the last page number, needed if AllPages option is selected
* @param selectedPageList list of pages to use if Selection option is selected
* @returns Returns range of pages to print
*/
static QString pageRange( QPrinter &printer, int lastPage, const QList<int> &selectedPageList );
/** convert a Page List into a Page Range
*
* @param pageList list of pages to convert
* @returns Returns equivalent page range
*/
static QString pageListToPageRange( const QList<int> &pageList );
/** Return if CUPS Print System is available on this system
*
* @returns Returns true if CUPS available
*/
static bool cupsAvailable();
/** Returns the postscript standard page size
*
* @returns Returns paper size in ps points
*/
static QSize psPaperSize( QPrinter &printer );
protected:
bool detectCupsService();
bool detectCupsConfig();
int doPrintFiles( QPrinter &printer, const QStringList fileList,
FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy,
const QString &pageRange );
QStringList printArguments( QPrinter &printer,
FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy,
bool useCupsOptions, const QString &pageRange, const QString &version );
QStringList destination( QPrinter &printer, const QString &version );
QStringList copies( QPrinter &printer, const QString &version );
QStringList jobname( QPrinter &printer, const QString &version );
QStringList deleteFile( QPrinter &printer, FileDeletePolicy fileDeletePolicy,
const QString &version );
QStringList pages( QPrinter &printer, PageSelectPolicy pageSelectPolicy,
const QString &pageRange, bool useCupsOptions, const QString &version );
QStringList cupsOptions( QPrinter &printer );
QStringList optionMedia( QPrinter &printer );
QString mediaPageSize( QPrinter &printer );
QString mediaPaperSource( QPrinter &printer );
QStringList optionOrientation( QPrinter &printer );
QStringList optionDoubleSidedPrinting( QPrinter &printer );
QStringList optionPageOrder( QPrinter &printer );
QStringList optionCollateCopies( QPrinter &printer );
};
// This code copied from KPrintPreview by Alex Merry, adapted to do PS files instead of PDF
class FilePrinterPreviewPrivate;
class OKULAR_EXPORT FilePrinterPreview : public KDialog
{
Q_OBJECT
public:
/**
* Create a Print Preview dialog for a given file.
*
* @param printer file to print preview
* @param parent pointer to the parent widget for the dialog
*/
explicit FilePrinterPreview( const QString &filename, QWidget *parent = 0 );
virtual ~FilePrinterPreview();
protected:
void showEvent( QShowEvent *event );
private:
FilePrinterPreviewPrivate * const d;
};
}
#endif // FILEPRINTER_H
......@@ -33,6 +33,7 @@
class QMutex;
class QPrinter;
class QPrintDialog;
class KAboutData;
class KComponentData;
class KIcon;
......@@ -196,7 +197,10 @@ class OKULAR_EXPORT Generator : public QObject
TextExtraction, ///< Whether the Generator can extract text from the document in the form of TextPage's
ReadRawData, ///< Whether the Generator can read a document directly from its raw data.
FontInfo, ///< Whether the Generator can provide information about the fonts used in the document
PageSizes ///< Whether the Generator can change the size of the document pages.
PageSizes, ///< Whether the Generator can change the size of the document pages.
PrintNative, ///< Whether the Generator supports native cross-platform printing (QPainter-based).
PrintPostscript, ///< Whether the Generator supports postscript-based file printing.
PrintToFile ///< Whether the Generator supports export to PDF & PS through the Print Dialog
};
/**
......
......@@ -12,7 +12,9 @@
#include <QtGui/QPainter>
#include <QtGui/QPrinter>
#include <okular/core/document.h>
#include <okular/core/page.h>
#include <okular/core/fileprinter.h>
OKULAR_EXPORT_PLUGIN(ComicBookGenerator)
......@@ -20,6 +22,8 @@ ComicBookGenerator::ComicBookGenerator()
: Generator()
{
setFeature( Threaded );
setFeature( PrintNative );
setFeature( PrintToFile );
}
ComicBookGenerator::~ComicBookGenerator()
......@@ -70,19 +74,17 @@ bool ComicBookGenerator::print( QPrinter& printer )
{
QPainter p( &printer );
for ( int i = 0; i < mDocument.pages(); ++i ) {
QImage image = mDocument.pageImage( i );
uint left, top, right, bottom;
left = printer.paperRect().left() - printer.pageRect().left();
top = printer.paperRect().top() - printer.pageRect().top();
right = printer.paperRect().right() - printer.pageRect().right();
bottom = printer.paperRect().bottom() - printer.pageRect().bottom();
QList<int> pageList = Okular::FilePrinter::pageList( printer, document()->pages(),
document()->bookmarkedPageList() );
int pageWidth = printer.width() - right;
int pageHeight = printer.height() - bottom;
for ( int i = 0; i < pageList.count(); ++i ) {
if ( (image.width() > pageWidth) || (image.height() > pageHeight) )
image = image.scaled( pageWidth, pageHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
QImage image = mDocument.pageImage( pageList[i] - 1 );
if ( ( image.width() > printer.width() ) || ( image.height() > printer.height() ) )
image = image.scaled( printer.width(), printer.height(),
Qt::KeepAspectRatio, Qt::SmoothTransformation );
if ( i != 0 )
printer.newPage();
......
......@@ -16,6 +16,7 @@
#include <okular/core/page.h>
#include <okular/core/textpage.h>
#include <okular/core/utils.h>
#include <okular/core/fileprinter.h>
#include <qdom.h>
#include <qmutex.h>
......@@ -66,6 +67,7 @@ DjVuGenerator::DjVuGenerator()
{
setFeature( TextExtraction );
setFeature( Threaded );
setFeature( PrintPostscript );
m_djvu = new KDjVu();
......@@ -185,30 +187,31 @@ const Okular::DocumentSynopsis * DjVuGenerator::generateDocumentSynopsis()
bool DjVuGenerator::print( QPrinter& printer )
{
/* This printing method unsupported in QPrinter, looking for alternative.
QList<int> pageList;
if ( !printer.previewOnly() )
pageList = printer.pageList();
else
{
int pages = m_djvu->pages().count();
for( int i = 1; i <= pages; ++i )
pageList.push_back(i);
}
bool result = false;
// Create tempfile to write to
KTemporaryFile tf;
tf.setSuffix( ".ps" );
if ( !tf.open() )
return false;
QMutexLocker locker( userMutex() );
QList<int> pageList = Okular::FilePrinter::pageList( printer, m_djvu->pages().count(),
document()->bookmarkedPageList() );
if ( m_djvu->exportAsPostScript( &tf, pageList ) )
{
return printer.printFiles( QStringList( tf.fileName() ), false );
tf.setAutoRemove( false );
int ret = Okular::FilePrinter::printFile( printer, tf.fileName(),
Okular::FilePrinter::SystemDeletesFiles,
Okular::FilePrinter::ApplicationSelectsPages,
document()->bookmarkedPageRange() );
result = ( ret >=0 );
}
*/
return false;
tf.close();
return result;
}
QVariant DjVuGenerator::metaData( const QString &key, const QVariant &option ) const
......
......@@ -478,9 +478,13 @@ void DVIExportToPS::finished_impl(int exit_code)
{
if (printer_ && !output_name_.isEmpty()) {
const QFileInfo output(output_name_);
if (output.exists() && output.isReadable())
//Unsupported in Qt
//printer_->printFiles(QStringList(output_name_), true);
if (output.exists() && output.isReadable()) {
// I'm not 100% sure on this, think we still need to select pages in export to ps above
FilePrinter::printFile( printer_, output_name_,
FilePrinter::ApplicationDeletesFiles,
FilePrinter::ApplicationSelectsPages,
QString() );
}
}
#ifndef DVIEXPORT_USE_QPROCESS
......
......@@ -31,6 +31,8 @@ KIMGIOGenerator::KIMGIOGenerator()
{
setFeature( ReadRawData );
setFeature( Threaded );
setFeature( PrintNative );
setFeature( PrintToFile );
KAboutData *about = new KAboutData(
"okular_kimgio",
......@@ -122,18 +124,12 @@ bool KIMGIOGenerator::print( QPrinter& printer )
{
QPainter p( &printer );
uint left, top, right, bottom;
left = printer.paperRect().left() - printer.pageRect().left();
top = printer.paperRect().top() - printer.pageRect().top();
right = printer.paperRect().right() - printer.pageRect().right();
bottom = printer.paperRect().bottom() - printer.pageRect().bottom();
QImage image( m_img );
int pageWidth = printer.width() - right;
int pageHeight = printer.height() - bottom;
if ( ( image.width() > printer.width() ) || ( image.height() > printer.height() ) )
QImage image( m_img );
if ( (image.width() > pageWidth) || (image.height() > pageHeight) )
image = image.scaled( pageWidth, pageHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation );
image = image.scaled( printer.width(), printer.height(),
Qt::KeepAspectRatio, Qt::SmoothTransformation );
p.drawImage( 0, 0, image );
......
......@@ -37,6 +37,7 @@
#include <okular/core/sound.h>
#include <okular/core/sourcereference.h>
#include <okular/core/textpage.h>
#include <okular/core/fileprinter.h>
#include <config-okular-poppler.h>
......@@ -106,15 +107,14 @@ class PDFOptionsPage : public QWidget
layout->addStretch(1);
}
void getOptions( QMap<QString,QString>& opts, bool incldef = false )
bool printForceRaster()
{
Q_UNUSED(incldef);
opts[ "kde-okular-poppler-forceRaster" ] = QString::number( m_forceRaster->isChecked() );
return m_forceRaster->isChecked();
}
void setOptions( const QMap<QString,QString>& opts )
void setPrintForceRaster( bool forceRaster )
{
m_forceRaster->setChecked( opts[ "kde-okular-poppler-forceRaster" ].toInt() );
m_forceRaster->setChecked( forceRaster );
}
private:
......@@ -292,7 +292,7 @@ OKULAR_EXPORT_PLUGIN(PDFGenerator)
PDFGenerator::PDFGenerator()
: Generator(), pdfdoc( 0 ), ready( true ),
pixmapRequest( 0 ), docInfoDirty( true ), docSynopsisDirty( true ),
docEmbeddedFilesDirty( true )
docEmbeddedFilesDirty( true ), pdfOptionsPage( 0 )
{
// ### TODO fill after the KDE 4.0 unfreeze
KAboutData *about = new KAboutData(
......@@ -307,8 +307,10 @@ PDFGenerator::PDFGenerator()
setAboutData( about );
setFeature( TextExtraction );
setFeature( FontInfo );
setFeature( PrintPostscript );
#ifdef HAVE_POPPLER_0_6
setFeature( ReadRawData );
pdfOptionsPage = new PDFOptionsPage();
#endif
// update the configuration
reparseConfig();
......@@ -325,6 +327,8 @@ PDFGenerator::~PDFGenerator()
generatorThread->wait();
delete generatorThread;
}
delete pdfOptionsPage;
}
//BEGIN Generator inherited functions
......@@ -842,119 +846,104 @@ Okular::TextPage* PDFGenerator::textPage( Okular::Page *page )
bool PDFGenerator::print( QPrinter& printer )
{
/* This printing method unsupported in QPrinter, looking for alternative.
int width, height;
// PageSize is a CUPS artificially created setting
QString ps = printer.option( "PageSize" );
QRegExp sizere( "w(\\d+)h(\\d+)" );
int marginTop, marginLeft, marginRight, marginBottom;
marginTop = (int)printer.option("kde-margin-top").toDouble();
marginLeft = (int)printer.option("kde-margin-left").toDouble();
marginRight = (int)printer.option("kde-margin-right").toDouble();
marginBottom = (int)printer.option("kde-margin-bottom").toDouble();
if ( sizere.exactMatch( ps ) )
{
// size not supported by Qt, CUPS gives us the size as wWIDTHhHEIGHT, at least on the printers i tested
width = sizere.cap( 1 ).toInt();
height = sizere.cap( 2 ).toInt();
}
else
{
// size is supported by Qt, we get either the pageSize name or nothing because the CUPS driver
// does not do any translation, then use KPrinter::pageSize to get the page size
KPrinter::PageSize qtPageSize;
if (!ps.isEmpty())
{
bool ok;
qtPageSize = pageNameToPageSize(ps, &ok);
// If we could not decode page size from the cups text try KPrinter::pageSize as last resort :-D
if (!ok) qtPageSize = printer.pageSize();
}
else qtPageSize = printer.pageSize();
QPrinter dummy(QPrinter::PrinterResolution);
dummy.setOrientation((QPrinter::Orientation)printer.orientation());
dummy.setFullPage(true);
dummy.setPageSize((QPrinter::PageSize)qtPageSize);
width = dummy.width();
height = dummy.height();
}
// Get the real page size to pass to the ps generator
QPrinter dummy( QPrinter::PrinterResolution );
dummy.setFullPage( true );
dummy.setOrientation( printer.orientation() );
int width = dummy.width();
int height = dummy.height();
// Create the tempfile to send to FilePrinter, which will manage the deletion
KTemporaryFile tf;
tf.setSuffix( ".ps" );
if ( !tf.open() )
return false;
QString tempfilename = tf.fileName();
#if !POPPLER_HAVE_PSCONVERTER_SETOUTPUTDEVICE
tf.close();
#endif
QList<int> pageList;
if (!printer.previewOnly()) pageList = printer.pageList();
else for(int i = 1; i <= pdfdoc->numPages(); i++) pageList.push_back(i);
// Generate the list of pages to be printed as selected in the print dialog
QList<int> pageList = Okular::FilePrinter::pageList( printer, pdfdoc->numPages(),
document()->bookmarkedPageList() );
// TODO rotation
#ifdef HAVE_POPPLER_0_6
double xScale = ((double)width - (double)marginLeft - (double)marginRight) / (double)width;
double yScale = ((double)height - (double)marginBottom - (double)marginTop) / (double)height;
bool strictMargins = false;
if ( abs((int)(xScale * 100) - (int)(yScale * 100)) > 5 ) {
int result = KMessageBox::questionYesNo(document()->widget(),
i18n("The margins you specified are changing the page aspect ratio. Do you want to print with the aspect ratio changed or do you want the margins to be adapted so that aspect ratio is preserved?"),
i18n("Aspect ratio change"),
KGuiItem( i18n("Print with specified margins") ),
KGuiItem( i18n("Print adapting margins to keep aspect ratio") ),
"kpdfStrictlyObeyMargins");
if (result == KMessageBox::Yes) strictMargins = true;
}
#if POPPLER_HAVE_PSCONVERTER_SETOUTPUTDEVICE
tf.setAutoRemove(false);
#else
tf.close();
#endif
QString pstitle = metaData(QLatin1String("Title"), QVariant()).toString();
if ( pstitle.trimmed().isEmpty() )
{
pstitle = document()->currentDocument().fileName();
}
bool forceRasterize = printer.option("kde-okular-poppler-forceRaster").toInt();
bool forceRasterize = pdfOptionsPage->printForceRaster();
Poppler::PSConverter *psConverter = pdfdoc->psConverter();
#if POPPLER_HAVE_PSCONVERTER_SETOUTPUTDEVICE
psConverter->setOutputDevice(&tf);
#else
psConverter->setOutputFileName(tempfilename);
#endif
psConverter->setPageList(pageList);
psConverter->setPaperWidth(width);
psConverter->setPaperHeight(height);
psConverter->setRightMargin(marginRight);