| /* poppler-page.cc: qt interface to poppler |
| * Copyright (C) 2005, Net Integration Technologies, Inc. |
| * Copyright (C) 2005, Brad Hards <bradh@frogmouth.net> |
| * Copyright (C) 2005-2011, Albert Astals Cid <aacid@kde.org> |
| * Copyright (C) 2005, Stefan Kebekus <stefan.kebekus@math.uni-koeln.de> |
| * Copyright (C) 2006-2011, Pino Toscano <pino@kde.org> |
| * Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org> |
| * Copyright (C) 2009 Shawn Rutledge <shawn.t.rutledge@gmail.com> |
| * Copyright (C) 2010, Guillermo Amaral <gamaral@kdab.com> |
| * Copyright (C) 2010 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp> |
| * Copyright (C) 2010 Matthias Fauconneau <matthias.fauconneau@gmail.com> |
| * Copyright (C) 2010 Hib Eris <hib@hiberis.nl> |
| * |
| * 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, or (at your option) |
| * any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. |
| */ |
| |
| #include <poppler-qt4.h> |
| |
| #include <QtCore/QHash> |
| #include <QtCore/QMap> |
| #include <QtCore/QVarLengthArray> |
| #include <QtGui/QImage> |
| #include <QtGui/QPainter> |
| |
| #include <config.h> |
| #include <PDFDoc.h> |
| #include <Catalog.h> |
| #include <Form.h> |
| #include <ErrorCodes.h> |
| #include <TextOutputDev.h> |
| #include <Annot.h> |
| #include <Link.h> |
| #include <FileSpec.h> |
| #include <ArthurOutputDev.h> |
| #if defined(HAVE_SPLASH) |
| #include <SplashOutputDev.h> |
| #include <splash/SplashBitmap.h> |
| #endif |
| |
| #include "poppler-private.h" |
| #include "poppler-page-transition-private.h" |
| #include "poppler-page-private.h" |
| #include "poppler-link-extractor-private.h" |
| #include "poppler-annotation-helper.h" |
| #include "poppler-annotation-private.h" |
| #include "poppler-form.h" |
| |
| namespace Poppler { |
| |
| class DummyAnnotation : public Annotation |
| { |
| public: |
| DummyAnnotation() |
| : Annotation( *new AnnotationPrivate() ) |
| { |
| } |
| |
| virtual SubType subType() const { return A_BASE; } |
| }; |
| |
| Link* PageData::convertLinkActionToLink(::LinkAction * a, const QRectF &linkArea) |
| { |
| return convertLinkActionToLink(a, parentDoc, linkArea); |
| } |
| |
| Link* PageData::convertLinkActionToLink(::LinkAction * a, DocumentData *parentDoc, const QRectF &linkArea) |
| { |
| if ( !a ) |
| return NULL; |
| |
| Link * popplerLink = NULL; |
| switch ( a->getKind() ) |
| { |
| case actionGoTo: |
| { |
| LinkGoTo * g = (LinkGoTo *) a; |
| const LinkDestinationData ldd( g->getDest(), g->getNamedDest(), parentDoc, false ); |
| // create link: no ext file, namedDest, object pointer |
| popplerLink = new LinkGoto( linkArea, QString::null, LinkDestination( ldd ) ); |
| } |
| break; |
| |
| case actionGoToR: |
| { |
| LinkGoToR * g = (LinkGoToR *) a; |
| // copy link file |
| const QString fileName = UnicodeParsedString( g->getFileName() ); |
| const LinkDestinationData ldd( g->getDest(), g->getNamedDest(), parentDoc, !fileName.isEmpty() ); |
| // ceate link: fileName, namedDest, object pointer |
| popplerLink = new LinkGoto( linkArea, fileName, LinkDestination( ldd ) ); |
| } |
| break; |
| |
| case actionLaunch: |
| { |
| LinkLaunch * e = (LinkLaunch *)a; |
| GooString * p = e->getParams(); |
| popplerLink = new LinkExecute( linkArea, e->getFileName()->getCString(), p ? p->getCString() : 0 ); |
| } |
| break; |
| |
| case actionNamed: |
| { |
| const char * name = ((LinkNamed *)a)->getName()->getCString(); |
| if ( !strcmp( name, "NextPage" ) ) |
| popplerLink = new LinkAction( linkArea, LinkAction::PageNext ); |
| else if ( !strcmp( name, "PrevPage" ) ) |
| popplerLink = new LinkAction( linkArea, LinkAction::PagePrev ); |
| else if ( !strcmp( name, "FirstPage" ) ) |
| popplerLink = new LinkAction( linkArea, LinkAction::PageFirst ); |
| else if ( !strcmp( name, "LastPage" ) ) |
| popplerLink = new LinkAction( linkArea, LinkAction::PageLast ); |
| else if ( !strcmp( name, "GoBack" ) ) |
| popplerLink = new LinkAction( linkArea, LinkAction::HistoryBack ); |
| else if ( !strcmp( name, "GoForward" ) ) |
| popplerLink = new LinkAction( linkArea, LinkAction::HistoryForward ); |
| else if ( !strcmp( name, "Quit" ) ) |
| popplerLink = new LinkAction( linkArea, LinkAction::Quit ); |
| else if ( !strcmp( name, "GoToPage" ) ) |
| popplerLink = new LinkAction( linkArea, LinkAction::GoToPage ); |
| else if ( !strcmp( name, "Find" ) ) |
| popplerLink = new LinkAction( linkArea, LinkAction::Find ); |
| else if ( !strcmp( name, "FullScreen" ) ) |
| popplerLink = new LinkAction( linkArea, LinkAction::Presentation ); |
| else if ( !strcmp( name, "Print" ) ) |
| popplerLink = new LinkAction( linkArea, LinkAction::Print ); |
| else if ( !strcmp( name, "Close" ) ) |
| { |
| // acroread closes the document always, doesnt care whether |
| // its presentation mode or not |
| // popplerLink = new LinkAction( linkArea, LinkAction::EndPresentation ); |
| popplerLink = new LinkAction( linkArea, LinkAction::Close ); |
| } |
| else |
| { |
| // TODO |
| } |
| } |
| break; |
| |
| case actionURI: |
| { |
| popplerLink = new LinkBrowse( linkArea, ((LinkURI *)a)->getURI()->getCString() ); |
| } |
| break; |
| |
| case actionSound: |
| { |
| ::LinkSound *ls = (::LinkSound *)a; |
| popplerLink = new LinkSound( linkArea, ls->getVolume(), ls->getSynchronous(), ls->getRepeat(), ls->getMix(), new SoundObject( ls->getSound() ) ); |
| } |
| break; |
| |
| case actionJavaScript: |
| { |
| ::LinkJavaScript *ljs = (::LinkJavaScript *)a; |
| popplerLink = new LinkJavaScript( linkArea, UnicodeParsedString(ljs->getScript()) ); |
| } |
| break; |
| |
| case actionMovie: |
| /* TODO this (Movie link) |
| m_type = Movie; |
| LinkMovie * m = (LinkMovie *) a; |
| // copy Movie parameters (2 IDs and a const char *) |
| Ref * r = m->getAnnotRef(); |
| m_refNum = r->num; |
| m_refGen = r->gen; |
| copyString( m_uri, m->getTitle()->getCString() ); |
| */ break; |
| |
| case actionUnknown: |
| break; |
| } |
| |
| return popplerLink; |
| } |
| |
| |
| Page::Page(DocumentData *doc, int index) { |
| m_page = new PageData(); |
| m_page->index = index; |
| m_page->parentDoc = doc; |
| m_page->page = doc->doc->getPage(m_page->index + 1); |
| m_page->transition = 0; |
| } |
| |
| Page::~Page() |
| { |
| delete m_page->transition; |
| delete m_page; |
| } |
| |
| QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate) const |
| { |
| int rotation = (int)rotate * 90; |
| QImage img; |
| switch(m_page->parentDoc->m_backend) |
| { |
| case Poppler::Document::SplashBackend: |
| { |
| #if defined(HAVE_SPLASH) |
| SplashOutputDev *splash_output = static_cast<SplashOutputDev *>(m_page->parentDoc->getOutputDev()); |
| |
| m_page->parentDoc->doc->displayPageSlice(splash_output, m_page->index + 1, xres, yres, |
| rotation, false, true, false, x, y, w, h); |
| |
| SplashBitmap *bitmap = splash_output->getBitmap(); |
| int bw = bitmap->getWidth(); |
| int bh = bitmap->getHeight(); |
| |
| SplashColorPtr dataPtr = splash_output->getBitmap()->getDataPtr(); |
| |
| if (QSysInfo::BigEndian == QSysInfo::ByteOrder) |
| { |
| uchar c; |
| int count = bw * bh * 4; |
| for (int k = 0; k < count; k += 4) |
| { |
| c = dataPtr[k]; |
| dataPtr[k] = dataPtr[k+3]; |
| dataPtr[k+3] = c; |
| |
| c = dataPtr[k+1]; |
| dataPtr[k+1] = dataPtr[k+2]; |
| dataPtr[k+2] = c; |
| } |
| } |
| |
| // construct a qimage SHARING the raw bitmap data in memory |
| QImage tmpimg( dataPtr, bw, bh, QImage::Format_ARGB32 ); |
| img = tmpimg.copy(); |
| // unload underlying xpdf bitmap |
| splash_output->startPage( 0, NULL ); |
| #endif |
| break; |
| } |
| case Poppler::Document::ArthurBackend: |
| { |
| QSize size = pageSize(); |
| QImage tmpimg(w == -1 ? qRound( size.width() * xres / 72.0 ) : w, h == -1 ? qRound( size.height() * yres / 72.0 ) : h, QImage::Format_ARGB32); |
| |
| QPainter painter(&tmpimg); |
| renderToPainter(&painter, xres, yres, x, y, w, h, rotate, DontSaveAndRestore); |
| painter.end(); |
| img = tmpimg; |
| break; |
| } |
| } |
| |
| return img; |
| } |
| |
| bool Page::renderToPainter(QPainter* painter, double xres, double yres, int x, int y, int w, int h, Rotation rotate, PainterFlags flags) const |
| { |
| if (!painter) |
| return false; |
| |
| switch(m_page->parentDoc->m_backend) |
| { |
| case Poppler::Document::SplashBackend: |
| return false; |
| case Poppler::Document::ArthurBackend: |
| { |
| const bool savePainter = !(flags & DontSaveAndRestore); |
| if (savePainter) |
| painter->save(); |
| if (m_page->parentDoc->m_hints & Document::Antialiasing) |
| painter->setRenderHint(QPainter::Antialiasing); |
| if (m_page->parentDoc->m_hints & Document::TextAntialiasing) |
| painter->setRenderHint(QPainter::TextAntialiasing); |
| painter->translate(x == -1 ? 0 : -x, y == -1 ? 0 : -y); |
| ArthurOutputDev arthur_output(painter); |
| arthur_output.startDoc(m_page->parentDoc->doc->getXRef()); |
| m_page->parentDoc->doc->displayPageSlice(&arthur_output, |
| m_page->index + 1, |
| xres, |
| yres, |
| (int)rotate * 90, |
| false, |
| true, |
| false, |
| x, |
| y, |
| w, |
| h); |
| if (savePainter) |
| painter->restore(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| QImage Page::thumbnail() const |
| { |
| unsigned char* data = 0; |
| int w = 0; |
| int h = 0; |
| int rowstride = 0; |
| GBool r = m_page->page->loadThumb(&data, &w, &h, &rowstride); |
| QImage ret; |
| if (r) |
| { |
| // first construct a temporary image with the data got, |
| // then force a copy of it so we can free the raw thumbnail data |
| ret = QImage(data, w, h, rowstride, QImage::Format_RGB888).copy(); |
| gfree(data); |
| } |
| return ret; |
| } |
| |
| QString Page::text(const QRectF &r, TextLayout textLayout) const |
| { |
| TextOutputDev *output_dev; |
| GooString *s; |
| PDFRectangle *rect; |
| QString result; |
| |
| const GBool rawOrder = textLayout == RawOrderLayout; |
| output_dev = new TextOutputDev(0, gFalse, rawOrder, gFalse); |
| m_page->parentDoc->doc->displayPageSlice(output_dev, m_page->index + 1, 72, 72, |
| 0, false, true, false, -1, -1, -1, -1); |
| if (r.isNull()) |
| { |
| rect = m_page->page->getCropBox(); |
| s = output_dev->getText(rect->x1, rect->y1, rect->x2, rect->y2); |
| } |
| else |
| { |
| s = output_dev->getText(r.left(), r.top(), r.right(), r.bottom()); |
| } |
| |
| result = QString::fromUtf8(s->getCString()); |
| |
| delete output_dev; |
| delete s; |
| return result; |
| } |
| |
| QString Page::text(const QRectF &r) const |
| { |
| return text(r, PhysicalLayout); |
| } |
| |
| bool Page::search(const QString &text, double &sLeft, double &sTop, double &sRight, double &sBottom, SearchDirection direction, SearchMode caseSensitive, Rotation rotate) const |
| { |
| const QChar * str = text.unicode(); |
| int len = text.length(); |
| QVector<Unicode> u(len); |
| for (int i = 0; i < len; ++i) u[i] = str[i].unicode(); |
| |
| GBool sCase; |
| if (caseSensitive == CaseSensitive) sCase = gTrue; |
| else sCase = gFalse; |
| |
| bool found = false; |
| |
| int rotation = (int)rotate * 90; |
| |
| // fetch ourselves a textpage |
| TextOutputDev td(NULL, gTrue, gFalse, gFalse); |
| m_page->parentDoc->doc->displayPage( &td, m_page->index + 1, 72, 72, rotation, false, true, false ); |
| TextPage *textPage=td.takeText(); |
| |
| if (direction == FromTop) |
| found = textPage->findText( u.data(), len, |
| gTrue, gTrue, gFalse, gFalse, sCase, gFalse, &sLeft, &sTop, &sRight, &sBottom ); |
| else if ( direction == NextResult ) |
| found = textPage->findText( u.data(), len, |
| gFalse, gTrue, gTrue, gFalse, sCase, gFalse, &sLeft, &sTop, &sRight, &sBottom ); |
| else if ( direction == PreviousResult ) |
| found = textPage->findText( u.data(), len, |
| gFalse, gTrue, gTrue, gFalse, sCase, gTrue, &sLeft, &sTop, &sRight, &sBottom ); |
| |
| textPage->decRefCnt(); |
| |
| return found; |
| } |
| |
| bool Page::search(const QString &text, QRectF &rect, SearchDirection direction, SearchMode caseSensitive, Rotation rotate) const |
| { |
| double sLeft, sTop, sRight, sBottom; |
| sLeft = rect.left(); |
| sTop = rect.top(); |
| sRight = rect.right(); |
| sBottom = rect.bottom(); |
| |
| bool found = search(text, sLeft, sTop, sRight, sBottom, direction, caseSensitive, rotate); |
| |
| rect.setLeft( sLeft ); |
| rect.setTop( sTop ); |
| rect.setRight( sRight ); |
| rect.setBottom( sBottom ); |
| |
| return found; |
| } |
| |
| QList<TextBox*> Page::textList(Rotation rotate) const |
| { |
| TextOutputDev *output_dev; |
| |
| QList<TextBox*> output_list; |
| |
| output_dev = new TextOutputDev(0, gFalse, gFalse, gFalse); |
| |
| int rotation = (int)rotate * 90; |
| |
| m_page->parentDoc->doc->displayPageSlice(output_dev, m_page->index + 1, 72, 72, |
| rotation, false, false, false, -1, -1, -1, -1); |
| |
| TextWordList *word_list = output_dev->makeWordList(); |
| |
| if (!word_list) { |
| delete output_dev; |
| return output_list; |
| } |
| |
| QHash<TextWord *, TextBox*> wordBoxMap; |
| |
| for (int i = 0; i < word_list->getLength(); i++) { |
| TextWord *word = word_list->get(i); |
| GooString *gooWord = word->getText(); |
| QString string = QString::fromUtf8(gooWord->getCString()); |
| delete gooWord; |
| double xMin, yMin, xMax, yMax; |
| word->getBBox(&xMin, &yMin, &xMax, &yMax); |
| |
| TextBox* text_box = new TextBox(string, QRectF(xMin, yMin, xMax-xMin, yMax-yMin)); |
| text_box->m_data->hasSpaceAfter = word->hasSpaceAfter() == gTrue; |
| text_box->m_data->charBBoxes.reserve(word->getLength()); |
| for (int j = 0; j < word->getLength(); ++j) |
| { |
| word->getCharBBox(j, &xMin, &yMin, &xMax, &yMax); |
| text_box->m_data->charBBoxes.append(QRectF(xMin, yMin, xMax-xMin, yMax-yMin)); |
| } |
| |
| wordBoxMap.insert(word, text_box); |
| |
| output_list.append(text_box); |
| } |
| |
| for (int i = 0; i < word_list->getLength(); i++) { |
| TextWord *word = word_list->get(i); |
| TextBox* text_box = wordBoxMap.value(word); |
| text_box->m_data->nextWord = wordBoxMap.value(word->nextWord()); |
| } |
| |
| delete word_list; |
| delete output_dev; |
| |
| return output_list; |
| } |
| |
| PageTransition *Page::transition() const |
| { |
| if (!m_page->transition) { |
| Object o; |
| PageTransitionParams params; |
| params.dictObj = m_page->page->getTrans(&o); |
| if (params.dictObj->isDict()) m_page->transition = new PageTransition(params); |
| o.free(); |
| } |
| return m_page->transition; |
| } |
| |
| Link *Page::action( PageAction act ) const |
| { |
| if ( act == Page::Opening || act == Page::Closing ) |
| { |
| Object o; |
| m_page->page->getActions(&o); |
| if (!o.isDict()) |
| { |
| o.free(); |
| return 0; |
| } |
| Dict *dict = o.getDict(); |
| Object o2; |
| const char *key = act == Page::Opening ? "O" : "C"; |
| dict->lookup((char*)key, &o2); |
| ::LinkAction *lact = ::LinkAction::parseAction(&o2, m_page->parentDoc->doc->getCatalog()->getBaseURI() ); |
| o2.free(); |
| o.free(); |
| Link *popplerLink = NULL; |
| if (lact != NULL) |
| { |
| popplerLink = m_page->convertLinkActionToLink(lact, QRectF()); |
| delete lact; |
| } |
| return popplerLink; |
| } |
| return 0; |
| } |
| |
| QSizeF Page::pageSizeF() const |
| { |
| Page::Orientation orient = orientation(); |
| if ( ( Page::Landscape == orient ) || (Page::Seascape == orient ) ) { |
| return QSizeF( m_page->page->getCropHeight(), m_page->page->getCropWidth() ); |
| } else { |
| return QSizeF( m_page->page->getCropWidth(), m_page->page->getCropHeight() ); |
| } |
| } |
| |
| QSize Page::pageSize() const |
| { |
| return pageSizeF().toSize(); |
| } |
| |
| Page::Orientation Page::orientation() const |
| { |
| const int rotation = m_page->page->getRotate(); |
| switch (rotation) { |
| case 90: |
| return Page::Landscape; |
| break; |
| case 180: |
| return Page::UpsideDown; |
| break; |
| case 270: |
| return Page::Seascape; |
| break; |
| default: |
| return Page::Portrait; |
| } |
| } |
| |
| void Page::defaultCTM(double *CTM, double dpiX, double dpiY, int rotate, bool upsideDown) |
| { |
| m_page->page->getDefaultCTM(CTM, dpiX, dpiY, rotate, gFalse, upsideDown); |
| } |
| |
| QList<Link*> Page::links() const |
| { |
| LinkExtractorOutputDev link_dev(m_page); |
| m_page->parentDoc->doc->processLinks(&link_dev, m_page->index + 1); |
| QList<Link*> popplerLinks = link_dev.links(); |
| |
| return popplerLinks; |
| } |
| |
| QList<Annotation*> Page::annotations() const |
| { |
| ::Page *pdfPage = m_page->page; |
| Annots* annots = pdfPage->getAnnots(m_page->parentDoc->doc->getCatalog()); |
| const uint numAnnotations = annots->getNumAnnots(); |
| if ( numAnnotations == 0 ) |
| { |
| return QList<Annotation*>(); |
| } |
| |
| // ID to Annotation/PopupWindow maps |
| QMap< int, Annotation * > annotationsMap; |
| QHash< AnnotPopup *, PopupWindow * > popupsMap; |
| // lists of Windows and Revisions that needs resolution |
| QLinkedList< ResolveRevision > resolveRevList; |
| QLinkedList< ResolveWindow > resolvePopList; |
| QLinkedList< PostProcessText > ppTextList; |
| |
| // build a normalized transform matrix for this page at 100% scale |
| GfxState * gfxState = new GfxState( 72.0, 72.0, pdfPage->getCropBox(), pdfPage->getRotate(), gTrue ); |
| double * gfxCTM = gfxState->getCTM(); |
| double MTX[6]; |
| for ( int i = 0; i < 6; i+=2 ) |
| { |
| MTX[i] = gfxCTM[i] / pdfPage->getCropWidth(); |
| MTX[i+1] = gfxCTM[i+1] / pdfPage->getCropHeight(); |
| } |
| delete gfxState; |
| |
| /** 1 - PARSE ALL ANNOTATIONS AND POPUPS FROM THE PAGE */ |
| for ( uint j = 0; j < numAnnotations; j++ ) |
| { |
| // get the j-th annotation |
| Annot * ann = annots->getAnnot( j ); |
| if ( !ann ) |
| { |
| qDebug() << "Annot" << j << "is null."; |
| continue; |
| } |
| |
| Annotation * annotation = 0; |
| int annotID = ann->getId(); |
| AnnotMarkup * markupann = dynamic_cast< AnnotMarkup * >( ann ); |
| bool addToPage = true; // Popup annots are added to custom queue |
| |
| /** 1.1. GET Subtype */ |
| Annot::AnnotSubtype subType = ann->getType(); |
| |
| /** 1.2. CREATE Annotation / PopupWindow and PARSE specific params */ |
| switch ( subType ) |
| { |
| case Annot::typeText: |
| { |
| // parse TextAnnotation params |
| AnnotText * textann = static_cast< AnnotText * >( ann ); |
| TextAnnotation * t = new TextAnnotation(); |
| annotation = t; |
| // -> textType |
| t->setTextType( TextAnnotation::Linked ); |
| // -> textIcon |
| t->setTextIcon( QString::fromLatin1( textann->getIcon()->getCString() ) ); |
| // request for postprocessing window geometry |
| PostProcessText request; |
| request.textAnnotation = t; |
| request.opened = textann->getOpen(); |
| ppTextList.append( request ); |
| break; |
| } |
| case Annot::typeFreeText: |
| { |
| // parse TextAnnotation params |
| AnnotFreeText * textann = static_cast< AnnotFreeText * >( ann ); |
| TextAnnotation * t = new TextAnnotation(); |
| annotation = t; |
| // -> textType |
| t->setTextType( TextAnnotation::InPlace ); |
| #if 0 |
| // -> textFont |
| QString textFormat; |
| XPDFReader::lookupString( annotDict, "DA", textFormat ); |
| // TODO, fill t->textFont using textFormat if not empty |
| #endif |
| // -> inplaceAlign |
| t->setInplaceAlign( textann->getQuadding() ); |
| // -> inplaceText (simple) |
| QString tmpstring; |
| GooString *styleString = textann->getStyleString(); |
| if ( styleString ) |
| tmpstring = UnicodeParsedString( styleString ); |
| #if 0 |
| // -> inplaceText (complex override) |
| XPDFReader::lookupString( annotDict, "RC", tmpstring ); |
| #endif |
| t->setInplaceText( tmpstring ); |
| // -> inplaceCallout |
| AnnotCalloutLine *callout = textann->getCalloutLine(); |
| if ( callout ) |
| { |
| QPointF tmppoint; |
| XPDFReader::transform( MTX, callout->getX1(), callout->getY1(), tmppoint ); |
| t->setCalloutPoint( 0, tmppoint ); |
| XPDFReader::transform( MTX, callout->getX2(), callout->getY2(), tmppoint ); |
| t->setCalloutPoint( 1, tmppoint ); |
| if ( AnnotCalloutMultiLine * callout_v6 = dynamic_cast< AnnotCalloutMultiLine * >( callout ) ) |
| { |
| XPDFReader::transform( MTX, callout_v6->getX3(), callout_v6->getY3(), tmppoint ); |
| t->setCalloutPoint( 2, tmppoint ); |
| } |
| } |
| // -> inplaceIntent |
| t->setInplaceIntent( (TextAnnotation::InplaceIntent)textann->getIntent() ); |
| AnnotBorderEffect *border_effect = textann->getBorderEffect(); |
| if ( border_effect ) |
| { |
| // -> style.effect |
| t->style.effect = (Annotation::LineEffect)border_effect->getEffectType(); |
| // -> style.effectIntensity |
| t->style.effectIntensity = (int)border_effect->getIntensity(); |
| } |
| break; |
| } |
| case Annot::typeLine: |
| { |
| // parse LineAnnotation params |
| AnnotLine * lineann = static_cast< AnnotLine * >( ann ); |
| LineAnnotation * l = new LineAnnotation(); |
| annotation = l; |
| |
| // -> linePoints |
| QLinkedList<QPointF> linePoints; |
| QPointF p; |
| XPDFReader::transform( MTX, lineann->getX1(), lineann->getY1(), p ); |
| linePoints.push_back( p ); |
| XPDFReader::transform( MTX, lineann->getX2(), lineann->getY2(), p ); |
| linePoints.push_back( p ); |
| l->setLinePoints( linePoints ); |
| // -> lineStartStyle |
| l->setLineStartStyle( (LineAnnotation::TermStyle)lineann->getStartStyle() ); |
| // -> lineEndStyle |
| l->setLineEndStyle( (LineAnnotation::TermStyle)lineann->getEndStyle() ); |
| // -> lineClosed |
| l->setLineClosed( false ); |
| // -> lineInnerColor |
| l->setLineInnerColor( convertAnnotColor( lineann->getInteriorColor() ) ); |
| // -> lineLeadingFwdPt |
| l->setLineLeadingForwardPoint( lineann->getLeaderLineLength() ); |
| // -> lineLeadingBackPt |
| l->setLineLeadingBackPoint( lineann->getLeaderLineExtension() ); |
| // -> lineShowCaption |
| l->setLineShowCaption( lineann->getCaption() ); |
| // -> lineIntent |
| l->setLineIntent( (LineAnnotation::LineIntent)( lineann->getIntent() + 1 ) ); |
| break; |
| } |
| case Annot::typePolygon: |
| case Annot::typePolyLine: |
| { |
| // parse LineAnnotation params |
| AnnotPolygon * polyann = static_cast< AnnotPolygon * >( ann ); |
| LineAnnotation * l = new LineAnnotation(); |
| annotation = l; |
| |
| // -> polyPoints |
| QLinkedList<QPointF> polyPoints; |
| AnnotPath * vertices = polyann->getVertices(); |
| for ( int i = 0; i < vertices->getCoordsLength(); ++i ) |
| { |
| QPointF p; |
| XPDFReader::transform( MTX, vertices->getX( i ), vertices->getY( i ), p ); |
| polyPoints.push_back( p ); |
| } |
| l->setLinePoints( polyPoints ); |
| // -> polyStartStyle |
| l->setLineStartStyle( (LineAnnotation::TermStyle)polyann->getStartStyle() ); |
| // -> polyEndStyle |
| l->setLineEndStyle( (LineAnnotation::TermStyle)polyann->getEndStyle() ); |
| // -> polyClosed |
| l->setLineClosed( subType == Annot::typePolygon ); |
| // -> polyInnerColor |
| l->setLineInnerColor( convertAnnotColor( polyann->getInteriorColor() ) ); |
| // -> polyIntent |
| switch ( polyann->getIntent() ) |
| { |
| case AnnotPolygon::polygonCloud: |
| l->setLineIntent( LineAnnotation::PolygonCloud ); |
| break; |
| case AnnotPolygon::polylineDimension: |
| case AnnotPolygon::polygonDimension: |
| l->setLineIntent( LineAnnotation::Dimension ); |
| break; |
| } |
| break; |
| } |
| case Annot::typeSquare: |
| case Annot::typeCircle: |
| { |
| // parse GeomAnnotation params |
| AnnotGeometry * geomann = static_cast< AnnotGeometry * >( ann ); |
| GeomAnnotation * g = new GeomAnnotation(); |
| annotation = g; |
| |
| // -> highlightType |
| if ( subType == Annot::typeSquare ) |
| g->setGeomType( GeomAnnotation::InscribedSquare ); |
| else if ( subType == Annot::typeCircle ) |
| g->setGeomType( GeomAnnotation::InscribedCircle ); |
| |
| // -> geomInnerColor |
| g->setGeomInnerColor( convertAnnotColor( geomann->getInteriorColor() ) ); |
| |
| AnnotBorderEffect *border_effect = geomann->getBorderEffect(); |
| if ( border_effect ) |
| { |
| // -> style.effect |
| g->style.effect = (Annotation::LineEffect)border_effect->getEffectType(); |
| // -> style.effectIntensity |
| g->style.effectIntensity = (int)border_effect->getIntensity(); |
| } |
| |
| // TODO RD |
| |
| break; |
| } |
| case Annot::typeHighlight: |
| case Annot::typeUnderline: |
| case Annot::typeSquiggly: |
| case Annot::typeStrikeOut: |
| { |
| AnnotTextMarkup * hlann = static_cast< AnnotTextMarkup * >( ann ); |
| HighlightAnnotation * h = new HighlightAnnotation(); |
| annotation = h; |
| |
| // -> highlightType |
| if ( subType == Annot::typeHighlight ) |
| h->setHighlightType( HighlightAnnotation::Highlight ); |
| else if ( subType == Annot::typeUnderline ) |
| h->setHighlightType( HighlightAnnotation::Underline ); |
| else if ( subType == Annot::typeSquiggly ) |
| h->setHighlightType( HighlightAnnotation::Squiggly ); |
| else if ( subType == Annot::typeStrikeOut ) |
| h->setHighlightType( HighlightAnnotation::StrikeOut ); |
| |
| // -> highlightQuads |
| AnnotQuadrilaterals *hlquads = hlann->getQuadrilaterals(); |
| if ( !hlquads || !hlquads->getQuadrilateralsLength() ) |
| { |
| qDebug() << "Not enough quads for a Highlight annotation."; |
| delete annotation; |
| continue; |
| } |
| QList< HighlightAnnotation::Quad > quads; |
| const int quadsCount = hlquads->getQuadrilateralsLength(); |
| for ( int q = 0; q < quadsCount; ++q ) |
| { |
| HighlightAnnotation::Quad quad; |
| XPDFReader::transform( MTX, hlquads->getX1( q ), hlquads->getY1( q ), quad.points[ 0 ] ); |
| XPDFReader::transform( MTX, hlquads->getX2( q ), hlquads->getY2( q ), quad.points[ 1 ] ); |
| XPDFReader::transform( MTX, hlquads->getX3( q ), hlquads->getY3( q ), quad.points[ 2 ] ); |
| XPDFReader::transform( MTX, hlquads->getX4( q ), hlquads->getY4( q ), quad.points[ 3 ] ); |
| // ### PDF1.6 specs says that point are in ccw order, but in fact |
| // points 3 and 4 are swapped in every PDF around! |
| QPointF tmpPoint = quad.points[ 2 ]; |
| quad.points[ 2 ] = quad.points[ 3 ]; |
| quad.points[ 3 ] = tmpPoint; |
| // initialize other properties and append quad |
| quad.capStart = true; // unlinked quads are always capped |
| quad.capEnd = true; // unlinked quads are always capped |
| quad.feather = 0.1; // default feather |
| quads.append( quad ); |
| } |
| h->setHighlightQuads( quads ); |
| break; |
| } |
| case Annot::typeStamp: |
| { |
| AnnotStamp * stampann = static_cast< AnnotStamp * >( ann ); |
| StampAnnotation * s = new StampAnnotation(); |
| annotation = s; |
| // -> stampIcon |
| s->setStampIconName( QString::fromLatin1( stampann->getIcon()->getCString() ) ); |
| break; |
| } |
| case Annot::typeInk: |
| { |
| // parse InkAnnotation params |
| AnnotInk * inkann = static_cast< AnnotInk * >( ann ); |
| InkAnnotation * k = new InkAnnotation(); |
| annotation = k; |
| |
| // -> inkPaths |
| AnnotPath ** paths = inkann->getInkList(); |
| if ( !paths || !inkann->getInkListLength() ) |
| { |
| qDebug() << "InkList not present for ink annot"; |
| delete annotation; |
| continue; |
| } |
| QList< QLinkedList<QPointF> > inkPaths; |
| const int pathsNumber = inkann->getInkListLength(); |
| for ( int m = 0; m < pathsNumber; m++ ) |
| { |
| // transform each path in a list of normalized points .. |
| QLinkedList<QPointF> localList; |
| AnnotPath * path = paths[ m ]; |
| const int pointsNumber = path ? path->getCoordsLength() : 0; |
| for ( int n = 0; n < pointsNumber; ++n ) |
| { |
| // get the x,y numbers for current point |
| double x = path->getX( n ); |
| double y = path->getY( n ); |
| // add normalized point to localList |
| QPointF np; |
| XPDFReader::transform( MTX, x, y, np ); |
| localList.push_back( np ); |
| } |
| // ..and add it to the annotation |
| inkPaths.push_back( localList ); |
| } |
| k->setInkPaths( inkPaths ); |
| break; |
| } |
| case Annot::typePopup: |
| { |
| AnnotPopup * popupann = static_cast< AnnotPopup * >( ann ); |
| |
| // create PopupWindow and add it to the popupsMap |
| PopupWindow * popup = new PopupWindow(); |
| popupsMap[ popupann ] = popup; |
| addToPage = false; |
| |
| // get window specific properties if any |
| popup->shown = popupann->getOpen(); |
| // no need to parse parent annotation id |
| //XPDFReader::lookupIntRef( annotDict, "Parent", popup->... ); |
| |
| // use the 'dummy annotation' for getting other parameters |
| popup->dummyAnnotation = new DummyAnnotation(); |
| annotation = popup->dummyAnnotation; |
| |
| break; |
| } |
| case Annot::typeLink: |
| { |
| // parse Link params |
| AnnotLink * linkann = static_cast< AnnotLink * >( ann ); |
| LinkAnnotation * l = new LinkAnnotation(); |
| annotation = l; |
| |
| // -> hlMode |
| l->setLinkHighlightMode( (LinkAnnotation::HighlightMode)linkann->getLinkEffect() ); |
| |
| // -> link region |
| // TODO |
| |
| // reading link action |
| if ( linkann->getAction() ) |
| { |
| Link * popplerLink = m_page->convertLinkActionToLink( linkann->getAction(), QRectF() ); |
| if ( popplerLink ) |
| { |
| l->setLinkDestination( popplerLink ); |
| } |
| } |
| |
| break; |
| } |
| case Annot::typeCaret: |
| { |
| // parse CaretAnnotation params |
| AnnotCaret * caretann = static_cast< AnnotCaret * >( ann ); |
| CaretAnnotation * c = new CaretAnnotation(); |
| annotation = c; |
| |
| // -> caretSymbol |
| c->setCaretSymbol( (CaretAnnotation::CaretSymbol)caretann->getSymbol() ); |
| |
| // TODO RD |
| |
| break; |
| } |
| case Annot::typeFileAttachment: |
| { |
| AnnotFileAttachment * attachann = static_cast< AnnotFileAttachment * >( ann ); |
| FileAttachmentAnnotation * f = new FileAttachmentAnnotation(); |
| annotation = f; |
| // -> fileIcon |
| f->setFileIconName( QString::fromLatin1( attachann->getName()->getCString() ) ); |
| // -> embeddedFile |
| FileSpec *filespec = new FileSpec( attachann->getFile() ); |
| f->setEmbeddedFile( new EmbeddedFile( *new EmbeddedFileData( filespec ) ) ); |
| break; |
| } |
| case Annot::typeSound: |
| { |
| AnnotSound * soundann = static_cast< AnnotSound * >( ann ); |
| SoundAnnotation * s = new SoundAnnotation(); |
| annotation = s; |
| |
| // -> soundIcon |
| s->setSoundIconName( QString::fromLatin1( soundann->getName()->getCString() ) ); |
| // -> sound |
| s->setSound( new SoundObject( soundann->getSound() ) ); |
| |
| break; |
| } |
| case Annot::typeMovie: |
| { |
| AnnotMovie * movieann = static_cast< AnnotMovie * >( ann ); |
| MovieAnnotation * m = new MovieAnnotation(); |
| annotation = m; |
| |
| // -> movie |
| MovieObject *movie = new MovieObject( movieann ); |
| m->setMovie( movie ); |
| // -> movieTitle |
| GooString * movietitle = movieann->getTitle(); |
| if ( movietitle ) |
| { |
| m->setMovieTitle( QString::fromLatin1( movietitle->getCString() ) ); |
| } |
| |
| break; |
| } |
| // special case for ignoring unknwon annotations |
| case Annot::typeUnknown: |
| continue; |
| // handled as forms or some other way |
| case Annot::typeWidget: |
| continue; |
| default: |
| { |
| #define CASE_FOR_TYPE( thetype ) \ |
| case Annot::type ## thetype: \ |
| type = #thetype ; \ |
| break; |
| QByteArray type; |
| switch ( subType ) |
| { |
| CASE_FOR_TYPE( Screen ) |
| CASE_FOR_TYPE( PrinterMark ) |
| CASE_FOR_TYPE( TrapNet ) |
| CASE_FOR_TYPE( Watermark ) |
| CASE_FOR_TYPE( 3D ) |
| default: type = QByteArray::number( subType ); |
| } |
| // MISSING: Widget, Screen, PrinterMark, TrapNet, Watermark, 3D |
| qDebug() << "Annotation" << type.constData() << "not supported."; |
| continue; |
| #undef CASE_FOR_TYPE |
| } |
| } |
| |
| /** 1.3. PARSE common parameters */ |
| // -> boundary |
| PDFRectangle *boundary = ann->getRect(); |
| // transform annotation rect to uniform coords |
| QPointF topLeft, bottomRight; |
| XPDFReader::transform( MTX, boundary->x1, boundary->y1, topLeft ); |
| XPDFReader::transform( MTX, boundary->x2, boundary->y2, bottomRight ); |
| QRectF boundaryRect; |
| boundaryRect.setTopLeft(topLeft); |
| boundaryRect.setBottomRight(bottomRight); |
| if ( boundaryRect.left() > boundaryRect.right() ) |
| { |
| double aux = boundaryRect.left(); |
| boundaryRect.setLeft( boundaryRect.right() ); |
| boundaryRect.setRight(aux); |
| } |
| if ( boundaryRect.top() > boundaryRect.bottom() ) |
| { |
| double aux = boundaryRect.top(); |
| boundaryRect.setTop( boundaryRect.bottom() ); |
| boundaryRect.setBottom(aux); |
| //annotation->rUnscaledWidth = (r[2] > r[0]) ? r[2] - r[0] : r[0] - r[2]; |
| //annotation->rUnscaledHeight = (r[3] > r[1]) ? r[3] - r[1] : r[1] - r[3]; |
| } |
| annotation->setBoundary( boundaryRect ); |
| // -> contents |
| annotation->setContents( UnicodeParsedString( ann->getContents() ) ); |
| // -> uniqueName |
| annotation->setUniqueName( UnicodeParsedString( ann->getName() ) ); |
| // -> modifyDate (and -> creationDate) |
| GooString *modDate = ann->getModified(); |
| if ( modDate ) |
| { |
| annotation->setModificationDate( convertDate( modDate->getCString() ) ); |
| } |
| if ( annotation->creationDate().isNull() && !annotation->modificationDate().isNull() ) |
| annotation->setCreationDate( annotation->modificationDate() ); |
| // -> flags: set the external attribute since it's embedded on file |
| int annoflags = 0; |
| annoflags |= Annotation::External; |
| // -> flags |
| int flags = ann->getFlags(); |
| if ( flags & Annot::flagHidden ) |
| annoflags |= Annotation::Hidden; |
| if ( flags & Annot::flagNoZoom ) |
| annoflags |= Annotation::FixedSize; |
| if ( flags & Annot::flagNoRotate ) |
| annoflags |= Annotation::FixedRotation; |
| if ( !( flags & Annot::flagPrint ) ) |
| annoflags |= Annotation::DenyPrint; |
| if ( flags & Annot::flagReadOnly ) |
| annoflags |= (Annotation::DenyWrite | Annotation::DenyDelete); |
| if ( flags & Annot::flagLocked ) |
| annoflags |= Annotation::DenyDelete; |
| if ( flags & Annot::flagToggleNoView ) |
| annoflags |= Annotation::ToggleHidingOnMouse; |
| annotation->setFlags( annoflags ); |
| // -> style |
| AnnotBorder *border = ann->getBorder(); |
| if ( border ) |
| { |
| if ( border->getType() == AnnotBorder::typeArray ) |
| { |
| AnnotBorderArray * border_array = static_cast< AnnotBorderArray * >( border ); |
| // -> style.xCorners |
| annotation->style.xCorners = border_array->getHorizontalCorner(); |
| // -> style.yCorners |
| annotation->style.yCorners = border_array->getVerticalCorner(); |
| } |
| // -> style.width |
| annotation->style.width = border->getWidth(); |
| // -> style.style |
| annotation->style.style = (Annotation::LineStyle)( 1 << border->getStyle() ); |
| #if 0 |
| // -> style.marks and style.spaces |
| // TODO |
| Object dashArray; |
| bsObj.getDict()->lookup( "D", &dashArray ); |
| if ( dashArray.isArray() ) |
| { |
| int dashMarks = 3; |
| int dashSpaces = 0; |
| Object intObj; |
| dashArray.arrayGet( 0, &intObj ); |
| if ( intObj.isInt() ) |
| dashMarks = intObj.getInt(); |
| intObj.free(); |
| dashArray.arrayGet( 1, &intObj ); |
| if ( intObj.isInt() ) |
| dashSpaces = intObj.getInt(); |
| intObj.free(); |
| annotation->style.marks = dashMarks; |
| annotation->style.spaces = dashSpaces; |
| } |
| #endif |
| } |
| // -> style.color |
| annotation->style.color = convertAnnotColor( ann->getColor() ); |
| |
| /** 1.4. PARSE markup { common, Popup, Revision } parameters */ |
| if ( markupann ) |
| { |
| // -> creationDate |
| GooString *createDate = markupann->getDate(); |
| if ( createDate ) |
| { |
| annotation->setCreationDate( convertDate( createDate->getCString() ) ); |
| } |
| // -> style.opacity |
| annotation->style.opacity = markupann->getOpacity(); |
| // -> window.title and author |
| annotation->window.title = UnicodeParsedString( markupann->getLabel() ); |
| annotation->setAuthor( annotation->window.title ); |
| // -> window.summary |
| annotation->window.summary = UnicodeParsedString( markupann->getSubject() ); |
| #if 0 |
| // -> window.text |
| // TODO |
| XPDFReader::lookupString( annotDict, "RC", annotation->window.text ); |
| #endif |
| |
| // if a popup is referenced, schedule for resolving it later |
| AnnotPopup *popup = markupann->getPopup(); |
| if ( popup ) |
| { |
| ResolveWindow request; |
| request.popup = popup; |
| request.annotation = annotation; |
| resolvePopList.append( request ); |
| } |
| |
| // if an older version is referenced, schedule for reparenting |
| int parentID = markupann->getInReplyToID(); |
| if ( parentID ) |
| { |
| ResolveRevision request; |
| request.nextAnnotation = annotation; |
| request.nextAnnotationID = annotID; |
| request.prevAnnotationID = parentID; |
| |
| // -> request.nextScope |
| request.nextScope = Annotation::Reply; |
| switch ( markupann->getReplyTo() ) |
| { |
| case AnnotMarkup::replyTypeR: |
| request.nextScope = Annotation::Reply; |
| break; |
| case AnnotMarkup::replyTypeGroup: |
| request.nextScope = Annotation::Group; |
| break; |
| } |
| |
| // -> revision.type (StateModel is deduced from type, not parsed) |
| request.nextType = Annotation::None; |
| if ( subType == Annot::typeText ) |
| { |
| AnnotText * textann = static_cast< AnnotText * >( ann ); |
| switch ( textann->getState() ) |
| { |
| case AnnotText::stateMarked: |
| request.nextType = Annotation::Marked; |
| break; |
| case AnnotText::stateUnmarked: |
| request.nextType = Annotation::Unmarked; |
| break; |
| case AnnotText::stateAccepted: |
| request.nextType = Annotation::Accepted; |
| break; |
| case AnnotText::stateRejected: |
| request.nextType = Annotation::Rejected; |
| break; |
| case AnnotText::stateCancelled: |
| request.nextType = Annotation::Cancelled; |
| break; |
| case AnnotText::stateCompleted: |
| request.nextType = Annotation::Completed; |
| break; |
| case AnnotText::stateNone: |
| case AnnotText::stateUnknown: |
| ; |
| } |
| } |
| |
| // schedule for later reparenting |
| resolveRevList.append( request ); |
| } |
| } |
| |
| /** 1.5. ADD ANNOTATION to the annotationsMap */ |
| if ( addToPage ) |
| { |
| if ( annotationsMap.contains( annotID ) ) |
| qDebug() << "Clash for annotations with ID:" << annotID; |
| annotationsMap[ annotID ] = annotation; |
| } |
| } // end Annotation/PopupWindow parsing loop |
| |
| /** 2 - RESOLVE POPUPS (popup.* -> annotation.window) */ |
| if ( !resolvePopList.isEmpty() && !popupsMap.isEmpty() ) |
| { |
| QLinkedList< ResolveWindow >::iterator it = resolvePopList.begin(), |
| end = resolvePopList.end(); |
| for ( ; it != end; ++it ) |
| { |
| const ResolveWindow & request = *it; |
| if ( !popupsMap.contains( request.popup ) ) |
| // warn aboud problems in popup resolving logic |
| qDebug() << "Cannot resolve popup" |
| << request.popup << "."; |
| else |
| { |
| // set annotation's window properties taking ones from popup |
| PopupWindow * pop = popupsMap[ request.popup ]; |
| Annotation * pa = pop->dummyAnnotation; |
| Annotation::Window & w = request.annotation->window; |
| |
| // transfer properties to Annotation's window |
| w.flags = pa->flags() & (Annotation::Hidden | |
| Annotation::FixedSize | Annotation::FixedRotation); |
| if ( !pop->shown ) |
| w.flags |= Annotation::Hidden; |
| w.topLeft.setX(pa->boundary().left()); |
| w.topLeft.setY(pa->boundary().top()); |
| w.width = (int)( pa->boundary().right() - pa->boundary().left() ); |
| w.height = (int)( pa->boundary().bottom() - pa->boundary().top() ); |
| } |
| } |
| |
| // clear data |
| QHash< AnnotPopup *, PopupWindow * >::Iterator dIt = popupsMap.begin(), dEnd = popupsMap.end(); |
| for ( ; dIt != dEnd; ++dIt ) |
| { |
| PopupWindow * p = dIt.value(); |
| delete p->dummyAnnotation; |
| delete p; |
| } |
| } |
| |
| /** 3 - RESOLVE REVISIONS (parent.revisions.append( children )) */ |
| if ( !resolveRevList.isEmpty() ) |
| { |
| // append children to parents |
| QVarLengthArray< int > excludeIDs( resolveRevList.count() ); // can't even reach this size |
| int excludeIndex = 0; // index in excludeIDs array |
| QLinkedList< ResolveRevision >::iterator it = resolveRevList.begin(), end = resolveRevList.end(); |
| for ( ; it != end; ++it ) |
| { |
| const ResolveRevision & request = *it; |
| int parentID = request.prevAnnotationID; |
| if ( !annotationsMap.contains( parentID ) ) |
| // warn about problems in reparenting logic |
| qDebug() << "Cannot reparent annotation to" |
| << parentID << "."; |
| else |
| { |
| // compile and add a Revision structure to the parent annotation |
| Annotation::Revision childRevision; |
| childRevision.annotation = request.nextAnnotation; |
| childRevision.scope = request.nextScope; |
| childRevision.type = request.nextType; |
| annotationsMap[ parentID ]->revisions().append( childRevision ); |
| // exclude child annotation from being rooted in page |
| excludeIDs[ excludeIndex++ ] = request.nextAnnotationID; |
| } |
| } |
| |
| // prevent children from being attached to page as roots |
| for ( int i = 0; i < excludeIndex; i++ ) |
| annotationsMap.remove( excludeIDs[ i ] ); |
| } |
| |
| /** 4 - POSTPROCESS TextAnnotations (when window geom is embedded) */ |
| if ( !ppTextList.isEmpty() ) |
| { |
| QLinkedList< PostProcessText >::const_iterator it = ppTextList.begin(), end = ppTextList.end(); |
| for ( ; it != end; ++it ) |
| { |
| const PostProcessText & request = *it; |
| Annotation::Window & window = request.textAnnotation->window; |
| // if not present, 'create' the window in-place over the annotation |
| if ( window.flags == -1 ) |
| { |
| window.flags = 0; |
| QRectF geom = request.textAnnotation->boundary(); |
| // initialize window geometry to annotation's one |
| window.width = (int)( geom.right() - geom.left() ); |
| window.height = (int)( geom.bottom() - geom.top() ); |
| window.topLeft.setX( geom.left() > 0.0 ? geom.left() : 0.0 ); |
| window.topLeft.setY( geom.top() > 0.0 ? geom.top() : 0.0 ); |
| } |
| // (pdf) if text is not 'opened', force window hiding. if the window |
| // was parsed from popup, the flag should already be set |
| if ( !request.opened && window.flags != -1 ) |
| window.flags |= Annotation::Hidden; |
| } |
| } |
| |
| /** 5 - finally RETURN ANNOTATIONS */ |
| return annotationsMap.values(); |
| } |
| |
| QList<FormField*> Page::formFields() const |
| { |
| QList<FormField*> fields; |
| ::Page *p = m_page->page; |
| ::FormPageWidgets * form = p->getFormWidgets(m_page->parentDoc->doc->getCatalog()); |
| int formcount = form->getNumWidgets(); |
| for (int i = 0; i < formcount; ++i) |
| { |
| ::FormWidget *fm = form->getWidget(i); |
| FormField * ff = NULL; |
| switch (fm->getType()) |
| { |
| case formButton: |
| { |
| ff = new FormFieldButton(m_page->parentDoc, p, static_cast<FormWidgetButton*>(fm)); |
| } |
| break; |
| |
| case formText: |
| { |
| ff = new FormFieldText(m_page->parentDoc, p, static_cast<FormWidgetText*>(fm)); |
| } |
| break; |
| |
| case formChoice: |
| { |
| ff = new FormFieldChoice(m_page->parentDoc, p, static_cast<FormWidgetChoice*>(fm)); |
| } |
| break; |
| |
| default: ; |
| } |
| |
| if (ff) |
| fields.append(ff); |
| } |
| |
| delete form; |
| |
| return fields; |
| } |
| |
| double Page::duration() const |
| { |
| return m_page->page->getDuration(); |
| } |
| |
| QString Page::label() const |
| { |
| GooString goo; |
| if (!m_page->parentDoc->doc->getCatalog()->indexToLabel(m_page->index, &goo)) |
| return QString(); |
| |
| return UnicodeParsedString(&goo); |
| } |
| |
| |
| } |