1 /**
2  * Copyright (C) 2011-2013  Charlie Sharpsteen, Stefan Löffler
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 2, or (at your option) any later
7  * version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  */
14 #ifndef PDFBackend_H
15 #define PDFBackend_H
16 
17 #include <PDFAnnotations.h>
18 #include <PDFTransitions.h>
19 
20 #include <QImage>
21 #include <QFileInfo>
22 #include <QSharedPointer>
23 #include <QThread>
24 #include <QStack>
25 #include <QCache>
26 #include <QMutex>
27 #include <QReadWriteLock>
28 #include <QReadLocker>
29 #include <QWriteLocker>
30 #include <QWaitCondition>
31 #include <QEvent>
32 #include <QMap>
33 #include <QWeakPointer>
34 
35 namespace QtPDF {
36 
37 namespace Backend {
38 
39 // Backend Rendering
40 // =================
41 
42 class Page;
43 class Document;
44 
45 // TODO: Find a better place to put this
46 QDateTime fromPDFDate(QString pdfDate);
47 
48 class PDFFontDescriptor
49 {
50 public:
51   enum FontStretch { FontStretch_UltraCondensed, FontStretch_ExtraCondensed, \
52                      FontStretch_Condensed, FontStretch_SemiCondensed, \
53                      FontStretch_Normal, FontStretch_SemiExpanded, \
54                      FontStretch_Expanded, FontStretch_ExtraExpanded, \
55                      FontStretch_UltraExpanded };
56   enum Flag { Flag_FixedPitch = 0x01, Flag_Serif = 0x02, Flag_Symbolic = 0x04, \
57               Flag_Script = 0x08, Flag_Nonsymbolic = 0x20, Flag_Italic = 0x40, \
58               Flag_AllCap = 0x10000, Flag_SmallCap = 0x20000, \
59               Flag_ForceBold = 0x40000 };
60   Q_DECLARE_FLAGS(Flags, Flag)
61 
62   PDFFontDescriptor(const QString fontName = QString());
~PDFFontDescriptor()63   virtual ~PDFFontDescriptor() { }
64 
65   bool isSubset() const;
66 
name()67   QString name() const { return _name; }
68   // pureName() removes the subset tag
69   QString pureName() const;
70 
setName(const QString name)71   void setName(const QString name) { _name = name; }
72   // TODO: Accessor methods for all other properties
73 
74 protected:
75   // From pdf specs
76   QString _name;
77   QString _family;
78   enum FontStretch _stretch;
79   int _weight;
80   Flags _flags;
81   QRectF _bbox;
82   float _italicAngle;
83   float _ascent;
84   float _descent;
85   float _leading;
86   float _capHeight;
87   float _xHeight;
88   float _stemV;
89   float _stemH;
90   float _avgWidth;
91   float _maxWidth;
92   float _missingWidth;
93   QString _charSet;
94 
95   // From pdf specs for CID fonts only
96   // _style
97   // _lang
98   // _fD
99   // _CIDSet
100 };
101 
102 // Note: This is a hack, but since all the information (with the exception of
103 // the type of font) we use (and that is provided by poppler) is encapsulated in
104 // PDFFontDescriptor, there is no use right now to completely implement all the
105 // different font structures
106 class PDFFontInfo
107 {
108 public:
109   enum FontType { FontType_Type0, FontType_Type1, FontType_MMType1, \
110                   FontType_Type3, FontType_TrueType };
111   enum CIDFontType { CIDFont_None, CIDFont_Type0, CIDFont_Type2 };
112   enum FontProgramType { ProgramType_None, ProgramType_Type1, \
113                          ProgramType_TrueType, ProgramType_Type1CFF, \
114                          ProgramType_CIDCFF, ProgramType_OpenType };
115   enum FontSource { Source_Embedded, Source_File, Source_Builtin };
116 
PDFFontInfo()117   PDFFontInfo() : _source(Source_Builtin), _fontType(FontType_Type1), _CIDType(CIDFont_None), _fontProgramType(ProgramType_None) { };
~PDFFontInfo()118   virtual ~PDFFontInfo() { };
119 
fontType()120   FontType fontType() const { return _fontType; }
CIDType()121   CIDFontType CIDType() const { return _CIDType; }
fontProgramType()122   FontProgramType fontProgramType() const { return _fontProgramType; }
descriptor()123   PDFFontDescriptor descriptor() const { return _descriptor; }
124   // returns the path to the file used for rendering this font, or an invalid
125   // QFileInfo for embedded fonts
fileName()126   QFileInfo fileName() const { return _substitutionFile; }
127 
isSubset()128   bool isSubset() const { return _descriptor.isSubset(); }
source()129   FontSource source() const { return _source; }
130 
131   // TODO: Implement some advanced logic; e.g., non-embedded fonts have no font
132   // program type
setFontType(const FontType fontType)133   void setFontType(const FontType fontType) { _fontType = fontType; }
setCIDType(const CIDFontType CIDType)134   void setCIDType(const CIDFontType CIDType) { _CIDType = CIDType; }
setFontProgramType(const FontProgramType programType)135   void setFontProgramType(const FontProgramType programType) { _fontProgramType = programType; }
setDescriptor(const PDFFontDescriptor descriptor)136   void setDescriptor(const PDFFontDescriptor descriptor) { _descriptor = descriptor; }
setFileName(const QFileInfo file)137   void setFileName(const QFileInfo file) { _source = Source_File; _substitutionFile = file; }
setSource(const FontSource source)138   void setSource(const FontSource source) { _source = source; }
139 
140 protected:
141   FontSource _source;
142   PDFFontDescriptor _descriptor;
143   QFileInfo _substitutionFile;
144   FontType _fontType;
145   CIDFontType _CIDType;
146   FontProgramType _fontProgramType;
147 };
148 
149 class PDFPageTile;
150 
151 // Need a hash function in order to allow `PDFPageTile` to be used as a key
152 // object for a `QCache`.
153 uint qHash(const PDFPageTile &tile);
154 
155 class PDFPageTile
156 {
157 public:
158   // TODO:
159   // We may want an application-wide cache instead of a document-specific cache
160   // to keep memory usage down. This may require an additional piece of
161   // information---the document that the page belongs to.
PDFPageTile(double xres,double yres,QRect render_box,int page_num)162   PDFPageTile(double xres, double yres, QRect render_box, int page_num):
163     xres(xres), yres(yres),
164     render_box(render_box),
165     page_num(page_num)
166   {}
167 
168   double xres, yres;
169   QRect render_box;
170   int page_num;
171 
172   bool operator==(const PDFPageTile &other) const
173   {
174     return (xres == other.xres && yres == other.yres && render_box == other.render_box && page_num == other.page_num);
175   }
176 
177   bool operator <(const PDFPageTile &other) const
178   {
179     return qHash(*this) < qHash(other);
180   }
181 
182 #ifdef DEBUG
183   operator QString() const;
184 #endif
185 };
186 
187 // This class is thread-safe
188 class PDFPageCache : protected QCache<PDFPageTile, QSharedPointer<QImage> >
189 {
190   typedef QCache<PDFPageTile, QSharedPointer<QImage> > Super;
191 public:
192   enum TileStatus { UNKNOWN, PLACEHOLDER, CURRENT, OUTDATED };
193 
PDFPageCache()194   PDFPageCache() { }
~PDFPageCache()195   virtual ~PDFPageCache() { }
196 
197   // Note: Each image has a cost of 1
maxSize()198   int maxSize() const { return maxCost(); }
setMaxSize(const int num)199   void setMaxSize(const int num) { setMaxCost(num); }
200 
201   // Returns the image under the key `tile` or NULL if it doesn't exist
202   QSharedPointer<QImage> getImage(const PDFPageTile & tile) const;
203   TileStatus getStatus(const PDFPageTile & tile) const;
204   // Returns the pointer to the image in the cache under they key `tile` after
205   // the insertion. If overwrite == true, this will always be image, otherwise
206   // it can be different
207   QSharedPointer<QImage> setImage(const PDFPageTile & tile, QImage * image, const TileStatus status, const bool overwrite = true);
208 
209 
lock()210   void lock() const { _lock.lockForRead(); }
unlock()211   void unlock() const { _lock.unlock(); }
212 
clear()213   void clear() { QWriteLocker l(&_lock); Super::clear(); _tileStatus.clear(); }
214   // Mark all tiles outdated
215   void markOutdated();
216 
tiles()217   QList<PDFPageTile> tiles() const { return keys(); }
218 protected:
219   mutable QReadWriteLock _lock;
220   // Map to keep track of the current status of tiles; note that the status
221   // information is not deleted when the QCache scraps images to save memory.
222   QMap<PDFPageTile, TileStatus> _tileStatus;
223 };
224 
225 class PageProcessingRequest : public QObject
226 {
227   Q_OBJECT
228   friend class PDFPageProcessingThread;
229 
230   // Protect c'tor and execute() so we can't access them except in derived
231   // classes and friends
232 protected:
PageProcessingRequest(Page * page,QObject * listener)233   PageProcessingRequest(Page *page, QObject *listener) : page(page), listener(listener) { }
234   // Should perform whatever processing it is designed to do
235   // Returns true if finished successfully, false otherwise
236   virtual bool execute() = 0;
237 
238 public:
239   enum Type { PageRendering, LoadLinks };
240 
~PageProcessingRequest()241   virtual ~PageProcessingRequest() { }
242   virtual Type type() const = 0;
243 
244   Page *page;
245   QObject *listener;
246 
247   virtual bool operator==(const PageProcessingRequest & r) const;
248 #ifdef DEBUG
249   virtual operator QString() const = 0;
250 #endif
251 };
252 
253 class PageProcessingRenderPageRequest : public PageProcessingRequest
254 {
255   Q_OBJECT
256   friend class PDFPageProcessingThread;
257 
258 public:
259   PageProcessingRenderPageRequest(Page *page, QObject *listener, double xres, double yres, QRect render_box = QRect(), bool cache = false) :
PageProcessingRequest(page,listener)260     PageProcessingRequest(page, listener),
261     xres(xres), yres(yres),
262     render_box(render_box),
263     cache(cache)
264   {}
type()265   Type type() const { return PageRendering; }
266 
267   virtual bool operator==(const PageProcessingRequest & r) const;
268 #ifdef DEBUG
269   virtual operator QString() const;
270 #endif
271 
272 protected:
273   bool execute();
274 
275   double xres, yres;
276   QRect render_box;
277   bool cache;
278 };
279 
280 
281 class PDFPageRenderedEvent : public QEvent
282 {
283 
284 public:
PDFPageRenderedEvent(double xres,double yres,QRect render_rect,QImage rendered_page)285   PDFPageRenderedEvent(double xres, double yres, QRect render_rect, QImage rendered_page):
286     QEvent(PageRenderedEvent),
287     xres(xres), yres(yres),
288     render_rect(render_rect),
289     rendered_page(rendered_page)
290   {}
291 
292   static const QEvent::Type PageRenderedEvent;
293 
294   const double xres, yres;
295   const QRect render_rect;
296   const QImage rendered_page;
297 
298 };
299 
300 
301 class PageProcessingLoadLinksRequest : public PageProcessingRequest
302 {
303   Q_OBJECT
304   friend class PDFPageProcessingThread;
305 
306 public:
PageProcessingLoadLinksRequest(Page * page,QObject * listener)307   PageProcessingLoadLinksRequest(Page *page, QObject *listener) : PageProcessingRequest(page, listener) { }
type()308   Type type() const { return LoadLinks; }
309 
310 #ifdef DEBUG
311   virtual operator QString() const;
312 #endif
313 
314 protected:
315   bool execute();
316 };
317 
318 
319 class PDFLinksLoadedEvent : public QEvent
320 {
321 
322 public:
PDFLinksLoadedEvent(const QList<QSharedPointer<Annotation::Link>> links)323   PDFLinksLoadedEvent(const QList< QSharedPointer<Annotation::Link> > links):
324     QEvent(LinksLoadedEvent),
325     links(links)
326   {}
327 
328   static const QEvent::Type LinksLoadedEvent;
329 
330   const QList< QSharedPointer<Annotation::Link> > links;
331 
332 };
333 
334 
335 // Class to perform (possibly) lengthy operations on pages in the background
336 // Modelled after the "Blocking Fortune Client Example" in the Qt docs
337 // (http://doc.qt.nokia.com/stable/network-blockingfortuneclient.html)
338 class PDFPageProcessingThread : public QThread
339 {
340   Q_OBJECT
341 
342 public:
343   PDFPageProcessingThread();
344   virtual ~PDFPageProcessingThread();
345 
346   // add a processing request to the work stack
347   // Note: request must have been created on the heap and must be in the scope
348   // of this thread; use requestRenderPage() and requestLoadLinks() for that
349   void addPageProcessingRequest(PageProcessingRequest * request);
350 
351   // drop all remaining processing requests
352   // WARNING: This function *must not* be called while the calling thread holds
353   // any locks that would prevent and work item from finishing. Otherwise, we
354   // could run into the following deadlock scenario:
355   // clearWorkStack() waits for the currently active work items to finish. The
356   // currently active work item waits to acquire a lock necessary for it to
357   // finish. However, that lock is held by the caller of clearWorkStack().
358   void clearWorkStack();
359 
360 protected:
361   virtual void run();
362 
363 private:
364   QStack<PageProcessingRequest*> _workStack;
365   QMutex _mutex;
366   QWaitCondition _waitCondition;
367   bool _idle;
368   QWaitCondition _idleCondition;
369   bool _quit;
370 #ifdef DEBUG
371   QTime _renderTimer;
372   static void dumpWorkStack(const QStack<PageProcessingRequest*> & ws);
373 #endif
374 
375 };
376 
377 class PDFToCItem
378 {
379 public:
380   enum PDFToCItemFlag { Flag_Italic = 0x1, Flag_Bold = 0x2 };
Q_DECLARE_FLAGS(PDFToCItemFlags,PDFToCItemFlag)381   Q_DECLARE_FLAGS(PDFToCItemFlags, PDFToCItemFlag)
382 
383   PDFToCItem(const QString label = QString()) : _label(label), _isOpen(false), _action(NULL) { }
PDFToCItem(const PDFToCItem & o)384   PDFToCItem(const PDFToCItem & o) : _label(o._label), _isOpen(o._isOpen), _color(o._color), _children(o._children), _flags(o._flags) {
385     _action = (o._action ? o._action->clone() : NULL);
386   }
~PDFToCItem()387   virtual ~PDFToCItem() { if (_action) delete _action; }
388 
label()389   QString label() const { return _label; }
isOpen()390   bool isOpen() const { return _isOpen; }
action()391   PDFAction * action() const { return _action; }
color()392   QColor color() const { return _color; }
children()393   const QList<PDFToCItem> & children() const { return _children; }
children()394   QList<PDFToCItem> & children() { return _children; }
flags()395   PDFToCItemFlags flags() const { return _flags; }
flags()396   PDFToCItemFlags & flags() { return _flags; }
397 
setLabel(const QString label)398   void setLabel(const QString label) { _label = label; }
399   void setOpen(const bool isOpen = true) { _isOpen = isOpen; }
setAction(PDFAction * action)400   void setAction(PDFAction * action) {
401     if (_action)
402       delete _action;
403     _action = action;
404   }
setColor(const QColor color)405   void setColor(const QColor color) { _color = color; }
406 
407 protected:
408   QString _label;
409   bool _isOpen; // derived from the sign of the `Count` member of the outline item dictionary
410   PDFAction * _action; // if the `Dest` member of the outline item dictionary is set, it must be converted to a PDFGotoAction
411   QColor _color;
412   QList<PDFToCItem> _children;
413   PDFToCItemFlags _flags;
414 };
415 
416 typedef QList<PDFToCItem> PDFToC;
417 
418 enum SearchFlag { Search_WrapAround = 0x01, Search_CaseInsensitive = 0x02, Search_Backwards = 0x04};
419 Q_DECLARE_FLAGS(SearchFlags, SearchFlag)
420 Q_DECLARE_OPERATORS_FOR_FLAGS(SearchFlags)
421 
422 struct SearchRequest
423 {
424   QWeakPointer<Document> doc;
425   int pageNum;
426   QString searchString;
427   SearchFlags flags;
428 };
429 
430 struct SearchResult
431 {
432   int pageNum;
433   QRectF bbox;
434 };
435 
436 
437 // PDF ABCs
438 // ========
439 // This header file defines a set of Abstract Base Classes (ABCs) for PDF
440 // documents. Having a set of abstract classes allows tools like GUI viewers to
441 // be written that are agnostic to the library that provides the actual PDF
442 // implementation: Poppler, MuPDF, etc.
443 // TODO: Should this class be derived from QObject to emit signals (e.g.,
444 // documentChanged() after reload, unlocking, etc.)?
445 
446 // This class is thread-safe. See implementation for internals.
447 class Document
448 {
449   friend class Page;
450 
451 public:
452   enum TrappedState { Trapped_Unknown, Trapped_True, Trapped_False };
453   enum Permission { Permission_Print = 0x0004,
454                     Permission_Change = 0x0008,
455                     Permission_Extract = 0x0010, // text and graphics
456                     Permission_Annotate = 0x0020, // Also includes filling forms
457                     Permission_FillForm = 0x0100,
458                     Permission_ExtractForAccessibility = 0x0200,
459                     Permission_Assemble = 0x0400,
460                     Permission_PrintHighRes = 0x0800
461                   };
462   Q_DECLARE_FLAGS(Permissions, Permission)
463 
464   Document(const QString fileName);
465   virtual ~Document();
466 
467   // Uses doc-read-lock
468   int numPages();
469   // Uses doc-read-lock
fileName()470   QString fileName() const { QReadLocker docLocker(_docLock.data()); return _fileName; }
471   // Uses doc-read-lock
472   PDFPageProcessingThread& processingThread();
473   // Uses doc-read-lock
474   PDFPageCache& pageCache();
475 
476   // Uses doc-read-lock and may use doc-write-lock
477   virtual QWeakPointer<Page> page(int at) = 0;
478   // Uses doc-read-lock
479   virtual QWeakPointer<Page> page(int at) const = 0;
resolveDestination(const PDFDestination & namedDestination)480   virtual PDFDestination resolveDestination(const PDFDestination & namedDestination) const {
481     return (namedDestination.isExplicit() ? namedDestination : PDFDestination());
482   }
483 
484 
485   // Uses doc-read-lock
permissions()486   Permissions permissions() const { QReadLocker docLocker(_docLock.data()); return _permissions; }
487   // Uses doc-read-lock
permissions()488   Permissions& permissions() { QReadLocker docLocker(_docLock.data()); return _permissions; }
489 
490   // Uses doc-read-lock
491   virtual bool isValid() const = 0;
492   // Uses doc-read-lock
493   virtual bool isLocked() const = 0;
494   // Uses doc-write-lock
495   virtual void reload() = 0;
496 
497   // Returns `true` if unlocking was successful and `false` otherwise.
498   // Uses doc-read-lock and may use doc-write-lock
499   virtual bool unlock(const QString password) = 0;
500 
501   // Override in derived class if it provides access to the document outline
502   // strutures of the pdf file.
toc()503   virtual PDFToC toc() const { return PDFToC(); }
fonts()504   virtual QList<PDFFontInfo> fonts() const { return QList<PDFFontInfo>(); }
505 
506   // <metadata>
title()507   QString title() const { QReadLocker docLocker(_docLock.data()); return _meta_title; }
author()508   QString author() const { QReadLocker docLocker(_docLock.data()); return _meta_author; }
subject()509   QString subject() const { QReadLocker docLocker(_docLock.data()); return _meta_subject; }
keywords()510   QString keywords() const { QReadLocker docLocker(_docLock.data()); return _meta_keywords; }
creator()511   QString creator() const { QReadLocker docLocker(_docLock.data()); return _meta_creator; }
producer()512   QString producer() const { QReadLocker docLocker(_docLock.data()); return _meta_producer; }
creationDate()513   QDateTime creationDate() const { QReadLocker docLocker(_docLock.data()); return _meta_creationDate; }
modDate()514   QDateTime modDate() const { QReadLocker docLocker(_docLock.data()); return _meta_modDate; }
trapped()515   TrappedState trapped() const { QReadLocker docLocker(_docLock.data()); return _meta_trapped; }
metaDataOther()516   QMap<QString, QString> metaDataOther() const { QReadLocker docLocker(_docLock.data()); return _meta_other; }
517   // </metadata>
518 
519   // Searches the entire document for the given string and returns a list of
520   // boxes that contain that text.
521   //
522   // TODO:
523   //
524   //   - Implement as a function that returns a generator object which can
525   //     return the search results one at a time rather than all at once.
526   //
527   //   - See TODO list in `Page::search`
528   virtual QList<SearchResult> search(QString searchText, SearchFlags flags, int startPage = 0);
529 
530 protected:
531   virtual void clearPages();
532   virtual void clearMetaData();
533 
534   int _numPages;
535   PDFPageProcessingThread _processingThread;
536   PDFPageCache _pageCache;
537   QVector< QSharedPointer<Page> > _pages;
538   Permissions _permissions;
539 
540   QString _fileName;
541 
542   QString _meta_title;
543   QString _meta_author;
544   QString _meta_subject;
545   QString _meta_keywords;
546   QString _meta_creator;
547   QString _meta_producer;
548   QDateTime _meta_creationDate;
549   QDateTime _meta_modDate;
550   TrappedState _meta_trapped;
551   QMap<QString, QString> _meta_other;
552   QSharedPointer<QReadWriteLock> _docLock;
553 };
554 
555 // This class is thread-safe. See implementation for internals.
556 class Page
557 {
558   friend class Document;
559 
560 protected:
561   Document *_parent;
562   const int _n;
563   Transition::AbstractTransition * _transition;
564   QReadWriteLock * _pageLock;
565   const QSharedPointer<QReadWriteLock> _docLock;
566 
567   // Getter for derived classes (that are not friends of Document)
docLock()568   QSharedPointer<QReadWriteLock> docLock() const { return _docLock; }
569   // The caller must hold a doc-lock. Uses a page-write-lock.
570   virtual void detachFromParent();
571 
572   Page(Document *parent, int at, QSharedPointer<QReadWriteLock> docLock);
573 
574   // Uses doc-read-lock and page-read-lock.
575   QSharedPointer<QImage> getCachedImage(double xres, double yres, QRect render_box = QRect(), PDFPageCache::TileStatus * status = NULL);
576 
577   // Uses doc-read-lock and page-read-lock.
578   virtual void asyncRenderToImage(QObject *listener, double xres, double yres, QRect render_box = QRect(), bool cache = false);
579 
580 public:
581   // Class to encapsulate boxes, e.g., for selecting
582   class Box {
583   public:
584     QRectF boundingBox;
585     QList<Box> subBoxes;
586   };
587 
588   virtual ~Page();
589 
document()590   Document * document() { QReadLocker pageLocker(_pageLock); return _parent; }
591   int pageNum();
592   virtual QSizeF pageSizeF() const = 0;
transition()593   Transition::AbstractTransition * transition() { QReadLocker pageLocker(_pageLock); return _transition; }
594 
595   virtual QList< QSharedPointer<Annotation::Link> > loadLinks() = 0;
596   // Uses doc-read-lock and page-read-lock.
597   virtual void asyncLoadLinks(QObject *listener);
598 
599   // Returns a list of boxes (e.g., for the purpose of selecting text)
600   // Box rectangles are in pdf coordinates (i.e., bp)
601   // The backend may return big boxes comprised of subboxes (e.g., words made up
602   // of characters) to speed up hit calculations. Only one level of subboxes is
603   // currently supported. The big box boundingBox must completely encompass all
604   // subBoxes' boundingBoxes.
boxes()605   virtual QList<Box> boxes() { return QList<Box>(); }
606   // Return selected text
607   // The returned text should contain all characters inside (at least) one of
608   // the `selection` polygons.
609   // The `selection` polygons must be in pdf coords (i.e., in bp)
610   // Optionally, the function can also return wordBoxes and/or charBoxes for
611   // each character (i.e., a rect enclosing the word the character is part of
612   // and/or a rect enclosing the actual character)
613   virtual QString selectedText(const QList<QPolygonF> & selection, QMap<int, QRectF> * wordBoxes = NULL, QMap<int, QRectF> * charBoxes = NULL, const bool onlyFullyEnclosed = false) {
614     if (wordBoxes) wordBoxes->clear();
615     if (charBoxes) charBoxes->clear();
616     return QString();
617   }
618 
619   // Uses page-read-lock and doc-read-lock.
620   virtual QImage renderToImage(double xres, double yres, QRect render_box = QRect(), bool cache = false) = 0;
621 
622   // Returns either a cached image (if it exists), or triggers a render request.
623   // If listener != NULL, this is an asynchronous render request and the method
624   // returns a dummy image (which is added to the cache to speed up future
625   // requests). Otherwise, the method renders the page synchronously and returns
626   // the result.
627   // Uses page-read-lock and doc-read-lock.
628   QSharedPointer<QImage> getTileImage(QObject * listener, const double xres, const double yres, QRect render_box = QRect());
629 
loadAnnotations()630   virtual QList< QSharedPointer<Annotation::AbstractAnnotation> > loadAnnotations() { return QList< QSharedPointer<Annotation::AbstractAnnotation> >(); }
631 
632   // Searches the page for the given text string and returns a list of boxes
633   // that contain that text.
634   //
635   // TODO:
636   //
637   // Implement as a function that returns a generator object which can return
638   // the search results one at a time rather than all at once which is time
639   // consuming. Even better, allow the returned object to be used as a C++
640   // iterator---then we could pass it off to QtConcurrent to generate results
641   // in the background and access them through a QFuture.
642   //
643   // This is very tricky to do in C++. God I miss Python and its `itertools`
644   // library.
645   virtual QList<SearchResult> search(QString searchText, SearchFlags flags) = 0;
646   static QList<SearchResult> executeSearch(SearchRequest request);
647 };
648 
649 } // namespace Backend
650 
651 class BackendInterface : public QObject
652 {
653   Q_OBJECT
654 public:
~BackendInterface()655   virtual ~BackendInterface() { }
656   virtual QSharedPointer<Backend::Document> newDocument(const QString & fileName) = 0;
657   virtual QString name() const = 0;
658   virtual bool canHandleFile(const QString & fileName) = 0;
659 };
660 
661 } // namespace QtPDF
662 
663 Q_DECLARE_INTERFACE(QtPDF::BackendInterface, "org.tug.QtPDF/1.0")
664 
665 // Backend Implementations
666 // =======================
667 // These provide library-specific concrete impelemntations of the abstract base
668 // classes defined here.
669 // NOTE: The backend implementations must be included _outside_ the namespace,
670 // as that could otherwise interfere with other header files (e.g., those of
671 // poppler-qt4)
672 #ifdef USE_POPPLERQT
673 #include <backends/PopplerQtBackend.h> // Invokes GPL v2+ License
674 #endif
675 #ifdef USE_MUPDF
676 #include <backends/MuPDFBackend.h>   // Invokes GPL v3 License
677 #endif
678 
679 #endif // End header guard
680 // vim: set sw=2 ts=2 et
681 
682