Printing/KDE4 Porting

From KDE Community Wiki

KDE4 Porting Work Page

This page holds information about the KDE4 port of KDEPrint. This includes status reports, to do lists, bug triage, etc.

Status

KDEPrint has been removed from kdelibs and kdecore for the 4.0 release. All applications have been ported to use Qt's printing functions instead. Qt 4.4 will bring further enhancements, after which time a decision will be made on printing support in KDE 4.1.

The print system management functions cannot be replaced by Qt, and so will still need to be maintained. Work will continue on KDEPrint in the playground module in the hope of releasing a working version as soon after KDE 4.0 as possible.

Fixing KDEPrint in Playground for release after 4.0

The primary aim is to complete the porting of KDEPrint to Qt4/KDE4 and to deliver a working set of printing administration tools as soon after the release of 4.0 as possible. No major technical, structural, or usability changes will be made in this phase. Upon completion, this branch will be moved into extragear.

Future direction for KDEPrint in 4.1 and beyond

Much of the direction for 4.1 will depend on the changes in Qt 4.4 and if we need to extend them to meet our needs. It is expected that Qt 4.4 will not be able to deliver all the extra features we want, so a thin wrapper around QPrinter/QPrintDialog may be re-introduced. The *NIX desktop printer management component is still required and should become the main focus for KDEPrint.

Technical changes would include:

  • Namespace
  • Better separation into core library functions and front-end
  • If KPrinter/KPrintDialog revived, possible inheritance from Qt instead of composition
  • Revised options management, try move away from 'magic' formatted string keys to named enums where possible

Seele has posted proposed Kubuntu kcm mock-ups at http://weblog.obso1337.org/2007/kubuntu-printer-configuration-ui/ and http://obso1337.org/hci/kde/kubuntu_printer_configuration.pdf. Investigate using these as basis for revised GUI.

Porting Applications from KDEPrint to Qt for 4.0

Please update the Porting List below if you port one of the applications.

Porting from KDEPrint involves the following:

  • Use QPrinter instead of KPrinter
  • Use KdePrint::createPrintDialog() (in kdelibs/kdeui) to get a customised QPrintDialog, rather than using KPrinter::addDialogPage() and KPrintDialog / KPrintDialogPage
  • Use KPrintPreview (in kdelibs/kutils) for print preview, rather than KPrinter::setPreviewOnly(true) or the KPrintDialog tick-box
  • removing the dependency on libkdeprint

Reference documentation can be found at the following locations:


The basic steps are as follows:


1) Change your includes:

Remove:

   #include <kprinter.h>

Add:

   #include <QtGui/QPrinter>
   #include <QtGui/QPrintDialog>  //only if required, see 3 below
   #include <kdeprintdialog.h>  //only if requires, see 4 below
   #include <kprintpreview.h>  //only if required, see 5 below


2) Change all occurrences of KPrinter to QPrinter, both as a class and as a namespace for enums.


3) If you the call the KPrinter::setup() method to display the printer dialog, you need to use QPrintDialog instead.

Replace:

   KPrinter printer;
   if (printer.setup(this, i18n("Print Dialog Title"))) {
   ...

with:

   QPrinter printer;
   QPrintDialog printDialog(&printer, this);
   printDialog.setWindowTitle(i18n("Print Dialog Title"));
   if (printDialog.exec()) {
   ...


4) If you call the KPrinter::addDialogPage() method to add custom options, then you will need to use KdePrint::createPrintDialog() instead.

If your code defines its own dialog page inherited from KPrintDialogPage, you need to inherit from QWidget instead. You should remove the getOptions/setOptions methods and replace them with separate get/set methods for each of your options. Use setWindowTitle() rather than setTitle().

In the printer setup, rather than calling to KPrinter::addDialogPage() to add your page to the dialog, you now call to KdePrint::createPrintDialog() which will return you a QPrintDialog with your widget(s) included. Your code that uses the custom options must then directly access the widget(s) to check their state rather than using KPrinter::option().

If you have multiple custom dialog pages, then all of them must be converted to QWidgets, and add all the QWidgets added to the QList.

Your widget(s) will appear as new tab(s) in the QPrintDialog.

In your custom dialog page .h/.cpp, replace:

   class KYourPrintDialogPage : public KPrintDialogPage
   ...
   void getOptions(QMap<QString,QString>& opts, bool incldef = false);
   void setOptions(const QMap<QString,QString>& opts);
   ...
   setTitle(i18n("Your Options Tab Title"));
   ...

with:

   class KYourPrintDialogWidget : public QWidget
   ...
   bool printAnOption();
   void setPrintAnOption(bool state);
   int printAnotherOption();
   void setPrintAnotherOption(int state);
   ...
   setWindowTitle(i18n("Your Options Tab Title"));
   ...

In your app print method, replace:

   KPrinter printer;
   printer.addDialogPage(new KYourPrintDialogPage(this));
   if (printer.setup(this, i18n("Print Dialog Title"))) {
       ...
       if (printer.option("app-xxx-printAnOption") == "true") {
       ...

with:

   KYourPrintDialogWidget yourOptionsWidget;
   QPrinter printer;
   QPrintDialog *printDialog = KdePrint::createPrintDialog(&printer, QList<QWidget*>() << &yourOptionsWidget, this);
   printDialog->setWindowTitle(i18n("Print Dialog Title"));
   if (printDialog->exec()) {
       ...
       if (yourOptionsWidget.printAnOption()) {
       ...

Refer to the already ported examples in the completed porting list below or contact Alex Merry, Thomas Zander, or John Layt for advice.


5) Print Preview. Qt does not provide a Print Preview function either programatically or from within its print dialog. Alex Merry has created a new KPrintPreview class in kdelibs/kutils to meet this need in 4.0 until Qt 4.4 introduces a new print preview system. This class prints to a PDF file and displays the result in a KPart, usually Okular.

If you want to provide a Print Preview option, then you need to add the KStandardAction for PrintPreview to your File menu and use the new KPrintPreview class.

   KAction* m_printPreview = KStandardAction::printPreview(kapp, SLOT(slotFilePrintPreview()), actionCollection());  //Is this right?
   void KYourApp::slotFilePrintPreview() {
       QPrinter printer;
       KPrintPreview printPreview(&printer);
       yourPrintDrawingRoutine(printer); // draws to the QPrinter
       printPreview.exec();
   }


6) Print to PDF. QPrinter natively supports saving to a PDF file, but this is not intuitive in the QPrintDialog. I'd recommend someone writes a new KAction 'Export as PDF' that uses QPrinter to generate the PDF file and uses a KFileDialog to save it. See KPrintPreview for how to do this. If you do, seek permission to add it to kdelibs/kutils under the KDEPrint exemption and/or let the rest of us know!


7) Dependencies

Remove your build dependency on libkdeprint, you may need to add a different dependency to ensure successful linking.

In CMakeLists.txt:

   target_link_libraries(yourapp  ${KDE4_KDEPRINT_LIBS} )

You may be able to delete this entirely if you have no other link targets included, otherwise you may need to add another suitable dependency such as:

   target_link_libraries(yourapp  ${KDE4_KIO_LIBS} )

If adding the KPrintPreview, you may need to add a dependency on kutils:

   target_link_libraries(yourapp  ${KDE4_KUTILS_LIBS} )

In makefile.am:

   yourapp_LDADD = $(LIB_KDEPRINT)
   or
   yourapp_LDADD = $(KDE4_KDEPRINT_LIBS)


Porting List

All apps now ported (or moved to playground), but see porting issues below.

Porting Issues

  • /KDE/kdeaccessibility/kmag [jlayt commit 724621, 724829] (problem with settings dialog)
  • /KDE/kdegraphics/kghostview [jlayt commit 725617] (uses printFiles, ported to FilePrinter)
  • /KDE/kdegraphics/okular [jlayt commit 725660] (uses printFiles, ported to FilePrinter) (custom dialog)
  • /KDE/kdepim/kaddressbook [jlayt commit 724958] (margins need fixing)
  • /extragear/graphics/digikam [partial port by maintainer] (custom dialog)
  • /extragear/graphics/kuickshow [jlayt 726784] (custom dialog) (wasn't compiling before)
  • /extragear/graphics/ligature [jlayt commit 727412] (custom dialog) (uses printFiles) (partial port done, but app was segfaulting beforehand so unable to test)
  • /extragear/security/pwmanager [jlayt commit 726029] (doesn't compile?)
  • /koffice/kspread [jlayt commit 727947] (crashes on print) (custom dialog)
  • When finished porting, need to go back and check custom dialogs have sensible defaults on platforms where extra page does not appear.

Porting Completed

Place ported apps here along with commit number to keep track of them.

  • /KDE/kdeaccessibility/kmouth [jlayt commit 724622, 724831]
  • /KDE/kdeadmin/kcron [jlayt commit 724658 724752] (custom dialog)
  • /KDE/kdeadmin/ksysv [jlayt commit 724659 724753]
  • /KDE/kdeedu/kig [jlayt commit 725051] (custom dialog)
  • /KDE/kdeedu/kmplot [jlayt commit 725106] (custom dialog)
  • /KDE/kdeedu/kturtle [jlayt commit 725032]
  • /KDE/kdeedu/kwordquiz [jlayt commit 725127] (custom dialog)
  • /KDE/kdeedu/marble [jlayt commit 725033]
  • /KDE/kdeedu/parley [jlayt commit 725034]
  • /KDE/kdegames/kbackgammon [jlayt 725180]
  • /KDE/kdegames/kiriki [aacid commit 724771]
  • /KDE/kdegames/kolf [jlayt 725184] (custom dialog)
  • /KDE/kdegames/ksudoku [jlayt 725189] (custom dialog)
  • /KDE/kdegames/ktuberling [aacid commit 724774]
  • /KDE/kdegraphics/kolourpaint [jlayt commit 725618] (custom dialog)
  • /KDE/kdelibs/khtml [alexmerry commit 723837]
  • /KDE/kdelibs/kate [alexmerry commit 723866]
  • /KDE/kdepim/knotes [jlayt commit 724960]
  • /KDE/kdepim/korganizer [winterz commit 724818]
  • /KDE/kdepim/ktimetracker [jlayt commit 724961]
  • /KDE/kdesdk/kapptemplate/kapp4 [jlayt 724878]
  • /KDE/kdesdk/umbrello [jlayt 724875] (custom dialog)
  • /KDE/kdeutils/kgpg [jlayt commit 724922]
  • /KDE/kdeutils/khexedit [jlayt commit 724919] (custom dialog)
  • /KDE/kdeutils/kjots [jlayt commit 724920]
  • /extragear/graphics/kfax [jlayt commit 726022] (custom dialog)
  • /extragear/graphics/kgrab [jlayt commit 726023]
  • /extragear/graphics/kgraphviewer [kleag commit 725640]
  • /extragear/graphics/kiconedit [jlayt commit 726025]
  • /koffice/karbon [jlayt commit 727452]
  • /koffice/kchart [jlayt commit 727453] (custom dialog)
  • /koffice/kexi [jlayt commit 727455]
  • /koffice/kformula [jlayt commit 727456]
  • /koffice/kivio [jlayt commit 727459]
  • /koffice/kplato [danders commit 725365]
  • /koffice/krita [rempt commit 725175]
  • /koffice/kugar [jlayt commit 727460]
  • /koffice/kpresenter/old [jlayt] (historic non-compiling code, no port required) (custom dialog)
  • /koffice/libs [jlayt commit 727947] (rempt, done but old code needs to remain until rest of koffice is ported)
  • /playground/base/kinstaller [jlayt commit 726032]
  • /playground/edu/kalcul [jlayt commit 726039]
  • /playground/games/hiq [jlayt commit 726040]
  • /playground/graphics/pixieplus [jlayt] (appears to still be KDE3/ Qt3)
  • /playground/multimedia/kalva [jlayt commit 726041]
  • /playground/multimedia/ktabedit [jlayt commit 727545]
  • /playground/sysadmin/ksystemlog [jlayt commit 727489 727490] (custom dialog)
  • /playground/utils/kro [jlayt commit 726038]

Issues

CUPS Support

Unsupported CUPS 1.1.x features:

 http://bugs.kde.org/show_bug.cgi?id=130423

Unsupported CUPS 1.2.x features:

 http://bugs.kde.org/show_bug.cgi?id=130425

Unsupported CUPS 1.3.x features:

 http://bugs.kde.org/show_bug.cgi?id=149546

UNIX Domain Sockets

Kurt Pfeifle on UDS.

The most important one however (IMHO), which seems to break printing for quite a lot of KDEPrint users, is the "unix domain socket" support in CUPS 1.2.x

Let me explain:

In CUPS 1.1.x you could only have cupsd.conf (one or more) statements like (for TCP/IP + UDP socket connections):

Listen localhost:631
Listen 10.162.7.8:21631
Listen [::1]:631
Listen *:631         # This one only, while no other is present

In CUPS 1.2.x support for unix domain socket connections was added (these ones work for local printing only of course, and do speed it up, make it more comfortable, and more secure), where you can add (or exclusively use) one statement like this:

Listen /var/run/cups/cups.sock
Listen /var/run/cups/whatevername.cupssock

The socket device file will be automatically created by cupsd upon starting up, with the correct permissions. Local authentication can then also be handled by automatically generated certificates (something kprinter also doesn't seem to understand).

Unix domain socket will be used by any local printsystem client (these are, in the first place, the CUPS commanline utilities like lp, lpr, lpstat, lpoptions, lpadmin...) to connect to the local CUPS server if the env var CUPS_SERVER does not contain "localhost" or "10.162.7.8", but "/var/run/cups/cups.sock".

Now, users can of course configure their cupsd to *exclusively* use a unix socket (and hence, disallow any remote connection from *any* possible remote client, effectivly blocking any possible DoS attack or worse. That may also disable the CUPS web interface, of course).

I dunno how current 3.5.7 kprinter and kjobviewer are behaving. Last I could look, it was completely non-functional for printing or job management if cupsd was set up like this.

At the very least, the KDEPrint UI should support the setting of the connection to a local cupsd via a domain socket. Look at the interface of

kaddprinterwizard --kdeconfig
--> go to "CUPS Server"

(I hope I quote this correctly from my memory), and notice how you can only set "host" and "port". This should have *both*, a "host:port" part and a "/path/to/whatever/sockfile" part to use (mutually exclusive, of course). When I tried to put the socket file path into the host field and keep the "port" field empty in the past (different consecutive releases of KDE 3.5.x), kprinter crashed...., or it interpreted the path as a hostname...., or it auto_added a port "0" or "631" to the env var (it tried to use CUPS_SERVER=/path/to/whatever/sockfile:631")... In any case, it didn't print  :-(

I don't know if any of the more "security conscious" distros do use this setting as their installation defaults yet... but it would break KDE Printing completely.

Useful Info

A presentation by Michael Goffioul on the architecture and design of KDEPrint

Printing on Windows

Win32API

Ralf Habacker explains how printing works on Windows:

If an application uses QPrinter there is no problem according to the api docs, which say "When printing directly to a printer on Windows or Mac OS X, QPrinter uses the built-in printer drivers. "

It is possible to directly send data to a printer, which allows printing of ascii text.

Postscript for example could be printed using the direct interface too, but this requires a printer which understands postscript directly because Windows by itself does not use postscript internally.

If there is no postscript printer available or non-ascii text output is required, applications have to use the windows gdi for printing. For an example see MSDN: Printing a Document

There is an optional lpd service for tcp/ip printing available but it requires a postscript printer on the end to support postscript printing, so this does not really help much.

It seems that there is a way to extend a native PrintDialog. There is a similar function named PrintDlgEx, of which the documentation says

"The PrintDlgEx function displays a Print property sheet that enables the user to specify the properties of a particular print job. A Print property sheet has a General page containing controls similar to the Print dialog box. The property sheet can also have additional application-specific and driver-specific property pages following the General page."

Especially the "application-specific property pages" feature seems to be the way forward. More information about the customizing can be found on MSDN.

To implement such support the win32 implementation of QPrintDialog would have to be extended.

Alternative method for Windows

Kurt Pfeifle gives an alternative method:

You should also consider/investigate this alternative:

  • Let KDE output PostScript (or PDF)
  • Let kprinter "use external program" for printing
  • (Add feature to let kprinter act without any GUI popup)
  • Use Ghostscript to print to the Windows printer


Here is a high level implementation outline:

  • Ghostscript is also available on Windows (as we all know). And we all know that Ghostscript can consume PostScript and PDF as its input file formats... one (or both) of which KDE4 applications can generate easily.
  • Ghostscript for Windows has an 'output device' (this is how they name in Ghostscript what elsewhere is called a "driver" or a "filter") that is named "mswinpr2" (a selected GS device determines which of the available GS output formats to produce).
  • mswinpr2 however does not work independently to create the output -- it uses the original MS Windows printer drivers. It is supposed to work with any printer that has device-independent bitmap (DIB) raster capabilities.
  • if you do not name a target printer when printing, Ghostscript will invoke the standard Print Setup dialog to prompt the user for the desired printer.
  • if you want to set the printername directly, you have to use a syntax like '-sOutputFile="%printer%Apple LaserWriter II NT"'
  • mswinpr2 supports another parameter, "-dNOCANCEL". If that is used, the standard progress/Cancel dialog is hidden (it shows percent of document already processed. "-dNOCANCEL" is useful to let GS print pages in the background without any user intervention/bothering).


Therefore, it appears to me that one feasible plan to make KDE apps print on Windows is this:

  • compile KDEprint without CUPS support; such a support is useless in any case because there are no CUPS libs on Windows (hmm... possibly you *can't* support it even if you wanted -- because it wouldn't build if you told it to link to a nonexistent library).
  • set kprinter to "Print Through an External Program", and specify as the print command then:
   gs -sDEVICE=mswinpr2 \
      -dNOCANCE \
      -sOutputFile="%printer%The Full Printer Name" \
      inputfile-originating-from-kdeapp.ps

where "The Full Printer Name" must be typed exactly as displayed in the Windows control panel (Note, the "gs" executable may be named differently on Windows).

  • alternatively, you can use as the print command:
   gs setup.ps inputfile.ps

where "setup.ps" is a special file containing the equivalent of above commandline parameters (see Ghostscript documentation for details), and then some more (see below).

It is left to the ingenuity of our KDE@Windows developers to find away for reading The Full Printer Name from the OS, somehow.


One step further (but not a big one) is this:


  • add a little feature to KDEPrint that is on my personal wishlist already since 2002: ability to print without any dialog (it would use simply the "last known settings"). Would be useful for other purposes on Un*x platforms too...
  • the usefulness of this feature on Windows would be this:
    • 'last setting' would be to "Print Through an External Program"
    • commandline would be "gs setup.ps inputfile"
    • (setup.ps may be generated dynamically as needed)
    • setup.ps would have these contents:
     mark
       /NoCancel          true     % don't show the cancel dialog
       /BitsPerPixel      4        % force 4 bits/pixel
       % alternative: /BitsPerPixel 1
                                   % 4 bpp is: CMYK w. screening by GS
                                   % 1 bpp is: monochrome
       % /OutputFile       The Full Printer Name
       /UserSettings
         <<
           /DocumentName 
           (KDE Printjob via Ghostscript)
                                   % name for Windows spooler to display
           /MaxResolution 600      % maximum document resolution allowed
         >>
       (mswinpr2) finddevice         % find required Windows device driver
       putdeviceprops
       setdevice

Notes:

  1. since /OutputFile is commented out, Ghostscript will prompt the user for a Windows printer using the standard Print Setup dialog. After printer is chosen, that driver is used.
  2. you could add the following lines too:
      /QueryUser 3          % silently uses default Win printer
      /QueryUser 2          % shows the printer setup dialog
      /QueryUser 1          % shows dialog for default Win printer

inside "/UserSettings" you can also use:

      /DocumentRange [a b]   % define pagerange contained in doc
      /SelectedRange [c d]   % define pagerange to be printed
                             % displayed in GUI; user-changeable
  1. If the target Win printer supports duplexing, here's how to do it (put line into setup.ps):
      <</Duplex true /Tumble false>> setpagedevice
      <</Duplex true /Tumble true>>  setpagedevice

Letting Ghostscript/Windrivers create raster images for printing can produce huuuuge jobfiles. Therefor the above /MaxResolution is quite useful. With it, you can limit the jobsizes to a degree, even if the final output device supports 1200 dpi or more...

P.S.: How are (the few) Gtk/Gnome programs that run on Windows handling their printing needs there?