Shawn Rutledge

New QtLabs PDF module

Published Monday January 30th, 2017
27 Comments on New QtLabs PDF module
Posted in Dev Loop

A couple of years ago during a hackathon, a couple of us wrote a Qt wrapper around PDFium, the open-source PDF rendering engine which is used for viewing PDFs in Chromium.  There have been a few fixes and improvements since then.  Now we (finally) have made this module available under the LGPLv3 license.

QPdfDocument can render a PDF page to a QImage, and there is QPdfBookmarkModel which can be used for a bookmarks view. There is an example application: a widget-based PDF viewer.

If you want to try it out, here’s how to get started:

git clone git://code.qt.io/qt-labs/qtpdf
cd qtpdf
git submodule update --init --recursive
qmake
make
cd examples/pdf/pdfviewer
qmake
make
./pdfviewer /path/to/my/file.pdf

qtpdf-example
There is some work in progress to add support for PDF in QtQuick, to treat it as an ordinary image format and to add support for multi-page image formats in general, but we don’t have any particular target release date yet.

Do you like this? Share it
Share on LinkedInGoogle+Share on FacebookTweet about this on Twitter

Posted in Dev Loop

27 comments

Ilya says:

Nice !
Is it working only with the Qt 5.9 dev branch ?

Shawn Rutledge Shawn Rutledge says:

No, I just verified that it still works fine with Qt 5.6. We don’t promise to keep it that way, but so far we have only needed fairly old APIs to implement this.

Tomaz says:

What about Poppler?

Tokoe says:

PDFium is BSD-like licensed, therefore QtPdf can also be used in GPL-incompatible projects (unlike poppler). Additionally PDFium has a better rendering performance with graphic-intensive PDF files.

Shawn Rutledge Shawn Rutledge says:

Poppler still exists, works fine, and has had Qt bindings for a long time. I’ve used it. The licensing is the main reason for using PDFium: commercial Qt customers can use it.

piomiq says:

I performed all mentioned steps and unfortunately I’m not able to build. I receive following error message:
../3rdparty/pdfium/third_party/freetype/src/base/ftinit.c:40:22: fatal error: ft2build.h: Nie ma takiego pliku ani katalogu
[eng.: File or directory does not exist]
but file exists:
$ cd qtpdf
(dev)$ find . -name ft2build.h
./src/3rdparty/pdfium/third_party/freetype/include/ft2build.h

So from begin:
I cloned repository with success.
$ cd qtpdf
(dev)$ git submodule update –init –recursive
Submodule ‘src/3rdparty/pdfium’ (https://pdfium.googlesource.com/pdfium) registered for path ‘src/3rdparty/pdfium’
Cloning into ‘/home/piotra/tmp/pdf-viewer/qtpdf/src/3rdparty/pdfium’…
Submodule path ‘src/3rdparty/pdfium’: checked out ‘8d5315004400be520bd988c1789e3b0a800fb100’

$ cd ../
$ du -sh qtpdf
134M qtpdf
$ cd qtpdf
(dev)$ qmake
Info: creating stash file /home/piotra/tmp/pdf-viewer/qtpdf/.qmake.stash
Info: creating cache file /home/piotra/tmp/pdf-viewer/qtpdf/.qmake.cache

$
$ make
In result I received couple errors related with ft2build.h file

Shawn Rutledge Shawn Rutledge says:

Unfortunately there’s a python script which is not working well with Python 3, which generates that file. You’ll have better luck if Python 2 is the one in your path (symlink python -> python2 for example, if you have both). At least I think that’s the reason. 😉

I see that in newer upstream versions of PDFium, this no longer appears necessary, so hopefully we can eliminate that soon.

Jonathan Courtois says:

Does this support the display of digital signatures?

Shawn Rutledge Shawn Rutledge says:

We haven’t implemented anything for that. You can check whether the upstream project does that. The only encryption-related feature so far is that if the PDF requires a password for viewing, you should be able to provide it.

Ilya says:

It’s working in Chromium, so it should probably work in Pdfium. Maybe with the latest Pdfium version …?

Ilya says:

In `qpdfdocument`, `FPDF_RenderPageBitmap` is called with a 0 value for the last parameter . With value 0x01 the annotations are shown (including signatures). 0x01 should be the default value IMO.
See https://pdfium.patagames.com/help/html/349ab75c-d5b0-b741-b9e8-802f4016581a.htm

Сковорода Никита says:

Great work!
It would be wolderful to see PDF support in QML in the future. =)

Charlie says:

Create our own component from C++ to QML is not difficult, but the problem is the Android compatibility (for example).
On my mac, I have build and installed QtPdf for desktop and this works (qmake PREFIX=/path/to/clang_64 && make && make install). So I can use QtPdf in my project.
But I can’t do this for Android (with QMake of Android Qt, spec android-g++, … ). Somebody has it tested ?

Sebastien says:

Hey, congratulation for your work ! This is a great news and as you pointed it out already, Poppler is fine but can’t be used by commercial licences.
I have a small request (after reading : QPdfDocument):
It would be great to have some control about the rendered image (its resolution). I see that you can pass the pagesize, but regarding the resolution, you can not pass it. I hope that the current code is choosing the best resolution, but it can generate some huge images. Imagine a 2 pass process : we extract all pages at low resolution (i.e. : 50 dpi max, so the user click on the one he wants to display) and for the second pass, one specific page is requested at max resolution.
(I suggest to add some “float maxResolution = 0” parameter to your render function, with no max if resolution is 0, but I do not know if such request is compatible with pdfium API)
FYI, I ran qmake & nmake and everything was fine on Win10 & visual 2015, until the link which complain about pdfium lib:
LINK : fatal error LNK1104: cannot open file ‘\qtbase\lib\qtpdfiumd.lib’
I guess that we must build this lib manually… I’ll try to find some time for that soon.
Thanks

Shawn Rutledge Shawn Rutledge says:

I haven’t tried it on Windows. Patches are welcome if they turn out to be necessary.

The resolution issue is the same as it is for SVG. If you have a zoom feature (especially pinch zoom) in your application, you want it to avoid re-rendering at every little step (because it takes some significant CPU time) but do re-render when the change in zoom level is large enough. We will of course make sure that’s possible, as the API develops further.

The idea of a max resolution is interesting, but I wonder if it will lead to surprise: you could get an enormous image which takes more CPU time and memory than you are prepared to spend.

Sebastien says:

Hi Shawn,
Thanks for your reply !

> you could get an enormous image
Depending on how you understand a max res. TMHO, a max resolution means that if the default resolution is higher, it could be lowered by passing a max res, but not the reverse
(ex : default pdf res, determined by internal raster images would be 300 dpi…
You pass 72 dpi because you just need a fast preview => 72 dpi QImage is returned.
You pass 600 dpi, the returned image would still be 300 dpi, especially if pdf contains only a raster image at 300 dpi)

Of course, as pdf contains vectorial + raster, for some specific needs, (as you pointed it out, pinch zoom is a good reason), it is convenient to get some very high dpi rendering. But in such case, some rect (not only the pageSize) must be passed to target only a part of the pdf, and keep an acceptable size for the returned image…

Shawn Rutledge Shawn Rutledge says:

Good point about needing to render only a portion of a page sometimes; that could be useful. But that also goes for large images in general, so maybe we need a general solution for tiling in QtQuick.

Charlie says:

Hi,

Congratulations for your job.
Is it possible to use QtPdf with Android Qt ?

Charlie

Philippe says:

Naive question: is the QtWebEngine needed to use this module?

Shawn Rutledge Shawn Rutledge says:

No. git submodule update --init --recursive pulls in the PDFium submodule.

Anders says:

Do you plan to implement a vector based render function? QSvgRenderer and Poppler both have a render function that interacts with a QPainter* instead of returning a rasterized image.

Right now QPdfDocument renders using FPDF_RenderPageBitmap and the vector data is never translated to Qt calls like drawPath and drawText. Implementing a Qt drawing surface in PDFium is probably not going to be trivial but I do believe the library has the modularity to allow it.

If the target QPaintDevice is another vector file like EMF, CGM or SVG then you would want to avoid rasterization whenever possible.

Shawn Rutledge Shawn Rutledge says:

We don’t have such a plan at this time, nor have I explored the API enough to know if it’s possible. Looks like class CPDF_PageObject in src/3rdparty/pdfium/core/fpdfapi/fpdf_page/include/cpdf_pageobject.h might be the object of interest, and there is a function GetPageObjectList… so maybe we could iterate those and try to render them somehow. But I’m just guessing at this point.

Teha says:

This sounds great, can’t wait for mingw upstream support. Any idea when we’ll be able to compile this on Windows?

Konstantin Tokarev says:

Have you seen QtPdfium project? https://github.com/paulovap/qtpdfium

Shawn Rutledge Shawn Rutledge says:

Yep. That was apparently done a few months later.

Adam Twardoch says:

Shawn,

PDFium has a Skia backend, which means that it can render a PDF onto an SkCanvas canvas:
[8] https://pdfium.googlesource.com/pdfium/+/master/core/fxge/skia/
[9] https://github.com/amplab/ray-core/blob/master/src/examples/ui/pdf_viewer/pdf_viewer.cc

And on the other hand, Skia has an SkSVGCanvas backend, which can render an SkCanvas into SVG:
[10] https://github.com/google/skia/tree/master/include/svg
[11] https://github.com/google/skia/tree/master/src/svg
[12] https://github.com/google/skia/blob/master/include/svg/SkSVGCanvas.h
[13] https://github.com/google/skia/blob/master/src/svg/SkSVGCanvas.cpp

And this seems to be a project that translates QPainter API calls to Skia API calls:
[14] https://github.com/telishev/sneakPic/blob/master/src/renderer/qt2skia.h
[15] https://github.com/telishev/sneakPic/blob/master/src/renderer/qt2skia.cpp
[16] https://github.com/telishev/sneakPic
[17] https://github.com/telishev/sneakPic/blob/master/src/renderer/renderer_item_svg.cpp

The `qt2skia.cpp` looks very trivial.

There seem to be several options:

1. Bundle Skia with the Qt app and use [8] to render the PDF via PDFium onto a Skia canvas, and then use [10] to generate an SVG from the Skia canvas, which then can be consumed by the Qt app. This would have the advantage of actually being free from any Qt dependencies, i.e. one could make a “sk-pdf2svg” commandline app that converts PDF to SVG via PDFium+Skia.

2. Bundle Skia with the Qt app, use [8] to render the PDF via PDFium onto a Skia canvas, and then adapt [17] to do the reverse (translate Skia canvas API calls to QPainter API calls).

3. Skip Skia altogether, and develop a QPainter backend for PDFium modeled after [8].

If anyone is up to doing any of the above and publishes the results under a liberal opensource license, I’ll be most happy.

Shawn Rutledge Shawn Rutledge says:

I think I’d like to use QPainter or just populate nodes into the scene graph, in the interest of reusing code, for the sake of compactness.

Commenting closed.

Get started today with Qt Download now