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