1 /* poppler-page.cc: qt interface to poppler
2 * Copyright (C) 2005, Net Integration Technologies, Inc.
3 * Copyright (C) 2005, Brad Hards <bradh@frogmouth.net>
4 * Copyright (C) 2005-2010, Albert Astals Cid <aacid@kde.org>
5 * Copyright (C) 2005, Stefan Kebekus <stefan.kebekus@math.uni-koeln.de>
6 * Copyright (C) 2006-2010, Pino Toscano <pino@kde.org>
7 * Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
8 * Copyright (C) 2009 Shawn Rutledge <shawn.t.rutledge@gmail.com>
9 * Copyright (C) 2010, Guillermo Amaral <gamaral@kdab.com>
10 * Copyright (C) 2010 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
11 * Copyright (C) 2010 Matthias Fauconneau <matthias.fauconneau@gmail.com>
12 * Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2, or (at your option)
17 * any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
27 */
28
29 #include <poppler-qt4.h>
30
31 #include <QtCore/QHash>
32 #include <QtCore/QMap>
33 #include <QtCore/QVarLengthArray>
34 #include <QtGui/QImage>
35 #include <QtGui/QPainter>
36
37 #include <config.h>
38 #include <PDFDoc.h>
39 #include <Catalog.h>
40 #include <Form.h>
41 #include <ErrorCodes.h>
42 #include <TextOutputDev.h>
43 #include <Annot.h>
44 #include <Link.h>
45 #include <ArthurOutputDev.h>
46 #if defined(HAVE_SPLASH)
47 #include <SplashOutputDev.h>
48 #include <splash/SplashBitmap.h>
49 #endif
50
51 #include "poppler-private.h"
52 #include "poppler-page-transition-private.h"
53 #include "poppler-page-private.h"
54 #include "poppler-link-extractor-private.h"
55 #include "poppler-annotation-helper.h"
56 #include "poppler-annotation-private.h"
57 #include "poppler-form.h"
58
59 namespace Poppler {
60
61 class DummyAnnotation : public Annotation
62 {
63 public:
DummyAnnotation()64 DummyAnnotation()
65 : Annotation( *new AnnotationPrivate() )
66 {
67 }
68
subType() const69 virtual SubType subType() const { return A_BASE; }
70 };
71
convertLinkActionToLink(::LinkAction * a,const QRectF & linkArea)72 Link* PageData::convertLinkActionToLink(::LinkAction * a, const QRectF &linkArea)
73 {
74 return convertLinkActionToLink(a, parentDoc, linkArea);
75 }
76
convertLinkActionToLink(::LinkAction * a,DocumentData * parentDoc,const QRectF & linkArea)77 Link* PageData::convertLinkActionToLink(::LinkAction * a, DocumentData *parentDoc, const QRectF &linkArea)
78 {
79 if ( !a )
80 return NULL;
81
82 Link * popplerLink = NULL;
83 switch ( a->getKind() )
84 {
85 case actionGoTo:
86 {
87 LinkGoTo * g = (LinkGoTo *) a;
88 const LinkDestinationData ldd( g->getDest(), g->getNamedDest(), parentDoc, false );
89 // create link: no ext file, namedDest, object pointer
90 popplerLink = new LinkGoto( linkArea, QString::null, LinkDestination( ldd ) );
91 }
92 break;
93
94 case actionGoToR:
95 {
96 LinkGoToR * g = (LinkGoToR *) a;
97 // copy link file
98 const QString fileName = UnicodeParsedString( g->getFileName() );
99 const LinkDestinationData ldd( g->getDest(), g->getNamedDest(), parentDoc, !fileName.isEmpty() );
100 // ceate link: fileName, namedDest, object pointer
101 popplerLink = new LinkGoto( linkArea, fileName, LinkDestination( ldd ) );
102 }
103 break;
104
105 case actionLaunch:
106 {
107 LinkLaunch * e = (LinkLaunch *)a;
108 GooString * p = e->getParams();
109 popplerLink = new LinkExecute( linkArea, e->getFileName()->getCString(), p ? p->getCString() : 0 );
110 }
111 break;
112
113 case actionNamed:
114 {
115 const char * name = ((LinkNamed *)a)->getName()->getCString();
116 if ( !strcmp( name, "NextPage" ) )
117 popplerLink = new LinkAction( linkArea, LinkAction::PageNext );
118 else if ( !strcmp( name, "PrevPage" ) )
119 popplerLink = new LinkAction( linkArea, LinkAction::PagePrev );
120 else if ( !strcmp( name, "FirstPage" ) )
121 popplerLink = new LinkAction( linkArea, LinkAction::PageFirst );
122 else if ( !strcmp( name, "LastPage" ) )
123 popplerLink = new LinkAction( linkArea, LinkAction::PageLast );
124 else if ( !strcmp( name, "GoBack" ) )
125 popplerLink = new LinkAction( linkArea, LinkAction::HistoryBack );
126 else if ( !strcmp( name, "GoForward" ) )
127 popplerLink = new LinkAction( linkArea, LinkAction::HistoryForward );
128 else if ( !strcmp( name, "Quit" ) )
129 popplerLink = new LinkAction( linkArea, LinkAction::Quit );
130 else if ( !strcmp( name, "GoToPage" ) )
131 popplerLink = new LinkAction( linkArea, LinkAction::GoToPage );
132 else if ( !strcmp( name, "Find" ) )
133 popplerLink = new LinkAction( linkArea, LinkAction::Find );
134 else if ( !strcmp( name, "FullScreen" ) )
135 popplerLink = new LinkAction( linkArea, LinkAction::Presentation );
136 else if ( !strcmp( name, "Print" ) )
137 popplerLink = new LinkAction( linkArea, LinkAction::Print );
138 else if ( !strcmp( name, "Close" ) )
139 {
140 // acroread closes the document always, doesnt care whether
141 // its presentation mode or not
142 // popplerLink = new LinkAction( linkArea, LinkAction::EndPresentation );
143 popplerLink = new LinkAction( linkArea, LinkAction::Close );
144 }
145 else
146 {
147 // TODO
148 }
149 }
150 break;
151
152 case actionURI:
153 {
154 popplerLink = new LinkBrowse( linkArea, ((LinkURI *)a)->getURI()->getCString() );
155 }
156 break;
157
158 case actionSound:
159 {
160 ::LinkSound *ls = (::LinkSound *)a;
161 popplerLink = new LinkSound( linkArea, ls->getVolume(), ls->getSynchronous(), ls->getRepeat(), ls->getMix(), new SoundObject( ls->getSound() ) );
162 }
163 break;
164
165 case actionJavaScript:
166 {
167 ::LinkJavaScript *ljs = (::LinkJavaScript *)a;
168 popplerLink = new LinkJavaScript( linkArea, UnicodeParsedString(ljs->getScript()) );
169 }
170 break;
171
172 case actionMovie:
173 /* TODO this (Movie link)
174 m_type = Movie;
175 LinkMovie * m = (LinkMovie *) a;
176 // copy Movie parameters (2 IDs and a const char *)
177 Ref * r = m->getAnnotRef();
178 m_refNum = r->num;
179 m_refGen = r->gen;
180 copyString( m_uri, m->getTitle()->getCString() );
181 */ break;
182
183 case actionUnknown:
184 break;
185 }
186
187 return popplerLink;
188 }
189
190
Page(DocumentData * doc,int index)191 Page::Page(DocumentData *doc, int index) {
192 m_page = new PageData();
193 m_page->index = index;
194 m_page->parentDoc = doc;
195 m_page->page = doc->doc->getPage(m_page->index + 1);
196 m_page->transition = 0;
197 }
198
~Page()199 Page::~Page()
200 {
201 delete m_page->transition;
202 delete m_page;
203 }
204
renderToImage(double xres,double yres,int x,int y,int w,int h,Rotation rotate) const205 QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate) const
206 {
207 int rotation = (int)rotate * 90;
208 QImage img;
209 switch(m_page->parentDoc->m_backend)
210 {
211 case Poppler::Document::SplashBackend:
212 {
213 #if defined(HAVE_SPLASH)
214 SplashOutputDev *splash_output = static_cast<SplashOutputDev *>(m_page->parentDoc->getOutputDev());
215
216 m_page->parentDoc->doc->displayPageSlice(splash_output, m_page->index + 1, xres, yres,
217 rotation, false, true, false, x, y, w, h);
218
219 SplashBitmap *bitmap = splash_output->getBitmap();
220 int bw = bitmap->getWidth();
221 int bh = bitmap->getHeight();
222
223 SplashColorPtr dataPtr = splash_output->getBitmap()->getDataPtr();
224
225 if (QSysInfo::BigEndian == QSysInfo::ByteOrder)
226 {
227 uchar c;
228 int count = bw * bh * 4;
229 for (int k = 0; k < count; k += 4)
230 {
231 c = dataPtr[k];
232 dataPtr[k] = dataPtr[k+3];
233 dataPtr[k+3] = c;
234
235 c = dataPtr[k+1];
236 dataPtr[k+1] = dataPtr[k+2];
237 dataPtr[k+2] = c;
238 }
239 }
240
241 // construct a qimage SHARING the raw bitmap data in memory
242 QImage tmpimg( dataPtr, bw, bh, QImage::Format_ARGB32 );
243 img = tmpimg.copy();
244 // unload underlying xpdf bitmap
245 splash_output->startPage( 0, NULL );
246 #endif
247 break;
248 }
249 case Poppler::Document::ArthurBackend:
250 {
251 QSize size = pageSize();
252 QImage tmpimg(w == -1 ? qRound( size.width() * xres / 72.0 ) : w, h == -1 ? qRound( size.height() * yres / 72.0 ) : h, QImage::Format_ARGB32);
253
254 QPainter painter(&tmpimg);
255 renderToPainter(&painter, xres, yres, x, y, w, h, rotate, DontSaveAndRestore);
256 painter.end();
257 img = tmpimg;
258 break;
259 }
260 }
261
262 return img;
263 }
264
renderToPainter(QPainter * painter,double xres,double yres,int x,int y,int w,int h,Rotation rotate,PainterFlags flags) const265 bool Page::renderToPainter(QPainter* painter, double xres, double yres, int x, int y, int w, int h, Rotation rotate, PainterFlags flags) const
266 {
267 if (!painter)
268 return false;
269
270 switch(m_page->parentDoc->m_backend)
271 {
272 case Poppler::Document::SplashBackend:
273 return false;
274 case Poppler::Document::ArthurBackend:
275 {
276 const bool savePainter = !(flags & DontSaveAndRestore);
277 if (savePainter)
278 painter->save();
279 if (m_page->parentDoc->m_hints & Document::Antialiasing)
280 painter->setRenderHint(QPainter::Antialiasing);
281 if (m_page->parentDoc->m_hints & Document::TextAntialiasing)
282 painter->setRenderHint(QPainter::TextAntialiasing);
283 painter->translate(x == -1 ? 0 : -x, y == -1 ? 0 : -y);
284 ArthurOutputDev arthur_output(painter);
285 arthur_output.startDoc(m_page->parentDoc->doc->getXRef());
286 m_page->parentDoc->doc->displayPageSlice(&arthur_output,
287 m_page->index + 1,
288 xres,
289 yres,
290 (int)rotate * 90,
291 false,
292 true,
293 false,
294 x,
295 y,
296 w,
297 h);
298 if (savePainter)
299 painter->restore();
300 return true;
301 }
302 }
303 return false;
304 }
305
thumbnail() const306 QImage Page::thumbnail() const
307 {
308 unsigned char* data = 0;
309 int w = 0;
310 int h = 0;
311 int rowstride = 0;
312 GBool r = m_page->page->loadThumb(&data, &w, &h, &rowstride);
313 QImage ret;
314 if (r)
315 {
316 // first construct a temporary image with the data got,
317 // then force a copy of it so we can free the raw thumbnail data
318 ret = QImage(data, w, h, rowstride, QImage::Format_RGB888).copy();
319 gfree(data);
320 }
321 return ret;
322 }
323
text(const QRectF & r,TextLayout textLayout) const324 QString Page::text(const QRectF &r, TextLayout textLayout) const
325 {
326 TextOutputDev *output_dev;
327 GooString *s;
328 PDFRectangle *rect;
329 QString result;
330
331 const GBool rawOrder = textLayout == RawOrderLayout;
332 output_dev = new TextOutputDev(0, gFalse, rawOrder, gFalse);
333 m_page->parentDoc->doc->displayPageSlice(output_dev, m_page->index + 1, 72, 72,
334 0, false, true, false, -1, -1, -1, -1);
335 if (r.isNull())
336 {
337 rect = m_page->page->getCropBox();
338 s = output_dev->getText(rect->x1, rect->y1, rect->x2, rect->y2);
339 }
340 else
341 {
342 s = output_dev->getText(r.left(), r.top(), r.right(), r.bottom());
343 }
344
345 result = QString::fromUtf8(s->getCString());
346
347 delete output_dev;
348 delete s;
349 return result;
350 }
351
text(const QRectF & r) const352 QString Page::text(const QRectF &r) const
353 {
354 return text(r, PhysicalLayout);
355 }
356
search(const QString & text,double & sLeft,double & sTop,double & sRight,double & sBottom,SearchDirection direction,SearchMode caseSensitive,Rotation rotate) const357 bool Page::search(const QString &text, double &sLeft, double &sTop, double &sRight, double &sBottom, SearchDirection direction, SearchMode caseSensitive, Rotation rotate) const
358 {
359 const QChar * str = text.unicode();
360 int len = text.length();
361 QVector<Unicode> u(len);
362 for (int i = 0; i < len; ++i) u[i] = str[i].unicode();
363
364 GBool sCase;
365 if (caseSensitive == CaseSensitive) sCase = gTrue;
366 else sCase = gFalse;
367
368 bool found = false;
369
370 int rotation = (int)rotate * 90;
371
372 // fetch ourselves a textpage
373 TextOutputDev td(NULL, gTrue, gFalse, gFalse);
374 m_page->parentDoc->doc->displayPage( &td, m_page->index + 1, 72, 72, rotation, false, true, false );
375 TextPage *textPage=td.takeText();
376
377 if (direction == FromTop)
378 found = textPage->findText( u.data(), len,
379 gTrue, gTrue, gFalse, gFalse, sCase, gFalse, &sLeft, &sTop, &sRight, &sBottom );
380 else if ( direction == NextResult )
381 found = textPage->findText( u.data(), len,
382 gFalse, gTrue, gTrue, gFalse, sCase, gFalse, &sLeft, &sTop, &sRight, &sBottom );
383 else if ( direction == PreviousResult )
384 found = textPage->findText( u.data(), len,
385 gFalse, gTrue, gTrue, gFalse, sCase, gTrue, &sLeft, &sTop, &sRight, &sBottom );
386
387 textPage->decRefCnt();
388
389 return found;
390 }
391
search(const QString & text,QRectF & rect,SearchDirection direction,SearchMode caseSensitive,Rotation rotate) const392 bool Page::search(const QString &text, QRectF &rect, SearchDirection direction, SearchMode caseSensitive, Rotation rotate) const
393 {
394 double sLeft, sTop, sRight, sBottom;
395 sLeft = rect.left();
396 sTop = rect.top();
397 sRight = rect.right();
398 sBottom = rect.bottom();
399
400 bool found = search(text, sLeft, sTop, sRight, sBottom, direction, caseSensitive, rotate);
401
402 rect.setLeft( sLeft );
403 rect.setTop( sTop );
404 rect.setRight( sRight );
405 rect.setBottom( sBottom );
406
407 return found;
408 }
409
textList(Rotation rotate) const410 QList<TextBox*> Page::textList(Rotation rotate) const
411 {
412 TextOutputDev *output_dev;
413
414 QList<TextBox*> output_list;
415
416 output_dev = new TextOutputDev(0, gFalse, gFalse, gFalse);
417
418 int rotation = (int)rotate * 90;
419
420 m_page->parentDoc->doc->displayPageSlice(output_dev, m_page->index + 1, 72, 72,
421 rotation, false, false, false, -1, -1, -1, -1);
422
423 TextWordList *word_list = output_dev->makeWordList();
424
425 if (!word_list) {
426 delete output_dev;
427 return output_list;
428 }
429
430 QHash<TextWord *, TextBox*> wordBoxMap;
431
432 for (int i = 0; i < word_list->getLength(); i++) {
433 TextWord *word = word_list->get(i);
434 GooString *gooWord = word->getText();
435 QString string = QString::fromUtf8(gooWord->getCString());
436 delete gooWord;
437 double xMin, yMin, xMax, yMax;
438 word->getBBox(&xMin, &yMin, &xMax, &yMax);
439
440 TextBox* text_box = new TextBox(string, QRectF(xMin, yMin, xMax-xMin, yMax-yMin));
441 text_box->m_data->hasSpaceAfter = word->hasSpaceAfter() == gTrue;
442 text_box->m_data->charBBoxes.reserve(word->getLength());
443 for (int j = 0; j < word->getLength(); ++j)
444 {
445 word->getCharBBox(j, &xMin, &yMin, &xMax, &yMax);
446 text_box->m_data->charBBoxes.append(QRectF(xMin, yMin, xMax-xMin, yMax-yMin));
447 }
448
449 wordBoxMap.insert(word, text_box);
450
451 output_list.append(text_box);
452 }
453
454 for (int i = 0; i < word_list->getLength(); i++) {
455 TextWord *word = word_list->get(i);
456 TextBox* text_box = wordBoxMap.value(word);
457 text_box->m_data->nextWord = wordBoxMap.value(word->nextWord());
458 }
459
460 delete word_list;
461 delete output_dev;
462
463 return output_list;
464 }
465
transition() const466 PageTransition *Page::transition() const
467 {
468 if (!m_page->transition) {
469 Object o;
470 PageTransitionParams params;
471 params.dictObj = m_page->page->getTrans(&o);
472 if (params.dictObj->isDict()) m_page->transition = new PageTransition(params);
473 o.free();
474 }
475 return m_page->transition;
476 }
477
action(PageAction act) const478 Link *Page::action( PageAction act ) const
479 {
480 if ( act == Page::Opening || act == Page::Closing )
481 {
482 Object o;
483 m_page->page->getActions(&o);
484 if (!o.isDict())
485 {
486 o.free();
487 return 0;
488 }
489 Dict *dict = o.getDict();
490 Object o2;
491 const char *key = act == Page::Opening ? "O" : "C";
492 dict->lookup((char*)key, &o2);
493 ::LinkAction *lact = ::LinkAction::parseAction(&o2, m_page->parentDoc->doc->getCatalog()->getBaseURI() );
494 o2.free();
495 o.free();
496 Link *popplerLink = NULL;
497 if (lact != NULL)
498 {
499 popplerLink = m_page->convertLinkActionToLink(lact, QRectF());
500 delete lact;
501 }
502 return popplerLink;
503 }
504 return 0;
505 }
506
pageSizeF() const507 QSizeF Page::pageSizeF() const
508 {
509 Page::Orientation orient = orientation();
510 if ( ( Page::Landscape == orient ) || (Page::Seascape == orient ) ) {
511 return QSizeF( m_page->page->getCropHeight(), m_page->page->getCropWidth() );
512 } else {
513 return QSizeF( m_page->page->getCropWidth(), m_page->page->getCropHeight() );
514 }
515 }
516
pageSize() const517 QSize Page::pageSize() const
518 {
519 return pageSizeF().toSize();
520 }
521
orientation() const522 Page::Orientation Page::orientation() const
523 {
524 const int rotation = m_page->page->getRotate();
525 switch (rotation) {
526 case 90:
527 return Page::Landscape;
528 break;
529 case 180:
530 return Page::UpsideDown;
531 break;
532 case 270:
533 return Page::Seascape;
534 break;
535 default:
536 return Page::Portrait;
537 }
538 }
539
defaultCTM(double * CTM,double dpiX,double dpiY,int rotate,bool upsideDown)540 void Page::defaultCTM(double *CTM, double dpiX, double dpiY, int rotate, bool upsideDown)
541 {
542 m_page->page->getDefaultCTM(CTM, dpiX, dpiY, rotate, gFalse, upsideDown);
543 }
544
links() const545 QList<Link*> Page::links() const
546 {
547 LinkExtractorOutputDev link_dev(m_page);
548 m_page->parentDoc->doc->processLinks(&link_dev, m_page->index + 1);
549 QList<Link*> popplerLinks = link_dev.links();
550
551 return popplerLinks;
552 }
553
annotations() const554 QList<Annotation*> Page::annotations() const
555 {
556 ::Page *pdfPage = m_page->page;
557 Annots* annots = pdfPage->getAnnots(m_page->parentDoc->doc->getCatalog());
558 const uint numAnnotations = annots->getNumAnnots();
559 if ( numAnnotations == 0 )
560 {
561 delete annots;
562 return QList<Annotation*>();
563 }
564
565 // ID to Annotation/PopupWindow maps
566 QMap< int, Annotation * > annotationsMap;
567 QHash< AnnotPopup *, PopupWindow * > popupsMap;
568 // lists of Windows and Revisions that needs resolution
569 QLinkedList< ResolveRevision > resolveRevList;
570 QLinkedList< ResolveWindow > resolvePopList;
571 QLinkedList< PostProcessText > ppTextList;
572
573 // build a normalized transform matrix for this page at 100% scale
574 GfxState * gfxState = new GfxState( 72.0, 72.0, pdfPage->getCropBox(), pdfPage->getRotate(), gTrue );
575 double * gfxCTM = gfxState->getCTM();
576 double MTX[6];
577 for ( int i = 0; i < 6; i+=2 )
578 {
579 MTX[i] = gfxCTM[i] / pdfPage->getCropWidth();
580 MTX[i+1] = gfxCTM[i+1] / pdfPage->getCropHeight();
581 }
582 delete gfxState;
583
584 /** 1 - PARSE ALL ANNOTATIONS AND POPUPS FROM THE PAGE */
585 for ( uint j = 0; j < numAnnotations; j++ )
586 {
587 // get the j-th annotation
588 Annot * ann = annots->getAnnot( j );
589 if ( !ann )
590 {
591 qDebug() << "Annot" << j << "is null.";
592 continue;
593 }
594
595 Annotation * annotation = 0;
596 int annotID = ann->getId();
597 AnnotMarkup * markupann = dynamic_cast< AnnotMarkup * >( ann );
598 bool addToPage = true; // Popup annots are added to custom queue
599
600 /** 1.1. GET Subtype */
601 Annot::AnnotSubtype subType = ann->getType();
602
603 /** 1.2. CREATE Annotation / PopupWindow and PARSE specific params */
604 switch ( subType )
605 {
606 case Annot::typeText:
607 {
608 // parse TextAnnotation params
609 AnnotText * textann = static_cast< AnnotText * >( ann );
610 TextAnnotation * t = new TextAnnotation();
611 annotation = t;
612 // -> textType
613 t->setTextType( TextAnnotation::Linked );
614 // -> textIcon
615 t->setTextIcon( QString::fromLatin1( textann->getIcon()->getCString() ) );
616 // request for postprocessing window geometry
617 PostProcessText request;
618 request.textAnnotation = t;
619 request.opened = textann->getOpen();
620 ppTextList.append( request );
621 break;
622 }
623 case Annot::typeFreeText:
624 {
625 // parse TextAnnotation params
626 AnnotFreeText * textann = static_cast< AnnotFreeText * >( ann );
627 TextAnnotation * t = new TextAnnotation();
628 annotation = t;
629 // -> textType
630 t->setTextType( TextAnnotation::InPlace );
631 #if 0
632 // -> textFont
633 QString textFormat;
634 XPDFReader::lookupString( annotDict, "DA", textFormat );
635 // TODO, fill t->textFont using textFormat if not empty
636 #endif
637 // -> inplaceAlign
638 t->setInplaceAlign( textann->getQuadding() );
639 // -> inplaceText (simple)
640 QString tmpstring;
641 GooString *styleString = textann->getStyleString();
642 if ( styleString )
643 tmpstring = UnicodeParsedString( styleString );
644 #if 0
645 // -> inplaceText (complex override)
646 XPDFReader::lookupString( annotDict, "RC", tmpstring );
647 #endif
648 t->setInplaceText( tmpstring );
649 // -> inplaceCallout
650 AnnotCalloutLine *callout = textann->getCalloutLine();
651 if ( callout )
652 {
653 QPointF tmppoint;
654 XPDFReader::transform( MTX, callout->getX1(), callout->getY1(), tmppoint );
655 t->setCalloutPoint( 0, tmppoint );
656 XPDFReader::transform( MTX, callout->getX2(), callout->getY2(), tmppoint );
657 t->setCalloutPoint( 1, tmppoint );
658 if ( AnnotCalloutMultiLine * callout_v6 = dynamic_cast< AnnotCalloutMultiLine * >( callout ) )
659 {
660 XPDFReader::transform( MTX, callout_v6->getX3(), callout_v6->getY3(), tmppoint );
661 t->setCalloutPoint( 2, tmppoint );
662 }
663 }
664 // -> inplaceIntent
665 t->setInplaceIntent( (TextAnnotation::InplaceIntent)textann->getIntent() );
666 AnnotBorderEffect *border_effect = textann->getBorderEffect();
667 if ( border_effect )
668 {
669 // -> style.effect
670 t->style.effect = (Annotation::LineEffect)border_effect->getEffectType();
671 // -> style.effectIntensity
672 t->style.effectIntensity = (int)border_effect->getIntensity();
673 }
674 break;
675 }
676 case Annot::typeLine:
677 {
678 // parse LineAnnotation params
679 AnnotLine * lineann = static_cast< AnnotLine * >( ann );
680 LineAnnotation * l = new LineAnnotation();
681 annotation = l;
682
683 // -> linePoints
684 QLinkedList<QPointF> linePoints;
685 QPointF p;
686 XPDFReader::transform( MTX, lineann->getX1(), lineann->getY1(), p );
687 linePoints.push_back( p );
688 XPDFReader::transform( MTX, lineann->getX2(), lineann->getY2(), p );
689 linePoints.push_back( p );
690 l->setLinePoints( linePoints );
691 // -> lineStartStyle
692 l->setLineStartStyle( (LineAnnotation::TermStyle)lineann->getStartStyle() );
693 // -> lineEndStyle
694 l->setLineEndStyle( (LineAnnotation::TermStyle)lineann->getEndStyle() );
695 // -> lineClosed
696 l->setLineClosed( false );
697 // -> lineInnerColor
698 l->setLineInnerColor( convertAnnotColor( lineann->getInteriorColor() ) );
699 // -> lineLeadingFwdPt
700 l->setLineLeadingForwardPoint( lineann->getLeaderLineLength() );
701 // -> lineLeadingBackPt
702 l->setLineLeadingBackPoint( lineann->getLeaderLineExtension() );
703 // -> lineShowCaption
704 l->setLineShowCaption( lineann->getCaption() );
705 // -> lineIntent
706 l->setLineIntent( (LineAnnotation::LineIntent)( lineann->getIntent() + 1 ) );
707 break;
708 }
709 case Annot::typePolygon:
710 case Annot::typePolyLine:
711 {
712 // parse LineAnnotation params
713 AnnotPolygon * polyann = static_cast< AnnotPolygon * >( ann );
714 LineAnnotation * l = new LineAnnotation();
715 annotation = l;
716
717 // -> polyPoints
718 QLinkedList<QPointF> polyPoints;
719 AnnotPath * vertices = polyann->getVertices();
720 for ( int i = 0; i < vertices->getCoordsLength(); ++i )
721 {
722 QPointF p;
723 XPDFReader::transform( MTX, vertices->getX( i ), vertices->getY( i ), p );
724 polyPoints.push_back( p );
725 }
726 l->setLinePoints( polyPoints );
727 // -> polyStartStyle
728 l->setLineStartStyle( (LineAnnotation::TermStyle)polyann->getStartStyle() );
729 // -> polyEndStyle
730 l->setLineEndStyle( (LineAnnotation::TermStyle)polyann->getEndStyle() );
731 // -> polyClosed
732 l->setLineClosed( subType == Annot::typePolygon );
733 // -> polyInnerColor
734 l->setLineInnerColor( convertAnnotColor( polyann->getInteriorColor() ) );
735 // -> polyIntent
736 switch ( polyann->getIntent() )
737 {
738 case AnnotPolygon::polygonCloud:
739 l->setLineIntent( LineAnnotation::PolygonCloud );
740 break;
741 case AnnotPolygon::polylineDimension:
742 case AnnotPolygon::polygonDimension:
743 l->setLineIntent( LineAnnotation::Dimension );
744 break;
745 }
746 break;
747 }
748 case Annot::typeSquare:
749 case Annot::typeCircle:
750 {
751 // parse GeomAnnotation params
752 AnnotGeometry * geomann = static_cast< AnnotGeometry * >( ann );
753 GeomAnnotation * g = new GeomAnnotation();
754 annotation = g;
755
756 // -> highlightType
757 if ( subType == Annot::typeSquare )
758 g->setGeomType( GeomAnnotation::InscribedSquare );
759 else if ( subType == Annot::typeCircle )
760 g->setGeomType( GeomAnnotation::InscribedCircle );
761
762 // -> geomInnerColor
763 g->setGeomInnerColor( convertAnnotColor( geomann->getInteriorColor() ) );
764
765 AnnotBorderEffect *border_effect = geomann->getBorderEffect();
766 if ( border_effect )
767 {
768 // -> style.effect
769 g->style.effect = (Annotation::LineEffect)border_effect->getEffectType();
770 // -> style.effectIntensity
771 g->style.effectIntensity = (int)border_effect->getIntensity();
772 }
773
774 // TODO RD
775
776 break;
777 }
778 case Annot::typeHighlight:
779 case Annot::typeUnderline:
780 case Annot::typeSquiggly:
781 case Annot::typeStrikeOut:
782 {
783 AnnotTextMarkup * hlann = static_cast< AnnotTextMarkup * >( ann );
784 HighlightAnnotation * h = new HighlightAnnotation();
785 annotation = h;
786
787 // -> highlightType
788 if ( subType == Annot::typeHighlight )
789 h->setHighlightType( HighlightAnnotation::Highlight );
790 else if ( subType == Annot::typeUnderline )
791 h->setHighlightType( HighlightAnnotation::Underline );
792 else if ( subType == Annot::typeSquiggly )
793 h->setHighlightType( HighlightAnnotation::Squiggly );
794 else if ( subType == Annot::typeStrikeOut )
795 h->setHighlightType( HighlightAnnotation::StrikeOut );
796
797 // -> highlightQuads
798 AnnotQuadrilaterals *hlquads = hlann->getQuadrilaterals();
799 if ( !hlquads || !hlquads->getQuadrilateralsLength() )
800 {
801 qDebug() << "Not enough quads for a Highlight annotation.";
802 delete annotation;
803 continue;
804 }
805 QList< HighlightAnnotation::Quad > quads;
806 const int quadsCount = hlquads->getQuadrilateralsLength();
807 for ( int q = 0; q < quadsCount; ++q )
808 {
809 HighlightAnnotation::Quad quad;
810 XPDFReader::transform( MTX, hlquads->getX1( q ), hlquads->getY1( q ), quad.points[ 0 ] );
811 XPDFReader::transform( MTX, hlquads->getX2( q ), hlquads->getY2( q ), quad.points[ 1 ] );
812 XPDFReader::transform( MTX, hlquads->getX3( q ), hlquads->getY3( q ), quad.points[ 2 ] );
813 XPDFReader::transform( MTX, hlquads->getX4( q ), hlquads->getY4( q ), quad.points[ 3 ] );
814 // ### PDF1.6 specs says that point are in ccw order, but in fact
815 // points 3 and 4 are swapped in every PDF around!
816 QPointF tmpPoint = quad.points[ 2 ];
817 quad.points[ 2 ] = quad.points[ 3 ];
818 quad.points[ 3 ] = tmpPoint;
819 // initialize other properties and append quad
820 quad.capStart = true; // unlinked quads are always capped
821 quad.capEnd = true; // unlinked quads are always capped
822 quad.feather = 0.1; // default feather
823 quads.append( quad );
824 }
825 h->setHighlightQuads( quads );
826 break;
827 }
828 case Annot::typeStamp:
829 {
830 AnnotStamp * stampann = static_cast< AnnotStamp * >( ann );
831 StampAnnotation * s = new StampAnnotation();
832 annotation = s;
833 // -> stampIcon
834 s->setStampIconName( QString::fromLatin1( stampann->getIcon()->getCString() ) );
835 break;
836 }
837 case Annot::typeInk:
838 {
839 // parse InkAnnotation params
840 AnnotInk * inkann = static_cast< AnnotInk * >( ann );
841 InkAnnotation * k = new InkAnnotation();
842 annotation = k;
843
844 // -> inkPaths
845 AnnotPath ** paths = inkann->getInkList();
846 if ( !paths || !inkann->getInkListLength() )
847 {
848 qDebug() << "InkList not present for ink annot";
849 delete annotation;
850 continue;
851 }
852 QList< QLinkedList<QPointF> > inkPaths;
853 const int pathsNumber = inkann->getInkListLength();
854 for ( int m = 0; m < pathsNumber; m++ )
855 {
856 // transform each path in a list of normalized points ..
857 QLinkedList<QPointF> localList;
858 AnnotPath * path = paths[ m ];
859 const int pointsNumber = path ? path->getCoordsLength() : 0;
860 for ( int n = 0; n < pointsNumber; ++n )
861 {
862 // get the x,y numbers for current point
863 double x = path->getX( n );
864 double y = path->getY( n );
865 // add normalized point to localList
866 QPointF np;
867 XPDFReader::transform( MTX, x, y, np );
868 localList.push_back( np );
869 }
870 // ..and add it to the annotation
871 inkPaths.push_back( localList );
872 }
873 k->setInkPaths( inkPaths );
874 break;
875 }
876 case Annot::typePopup:
877 {
878 AnnotPopup * popupann = static_cast< AnnotPopup * >( ann );
879
880 // create PopupWindow and add it to the popupsMap
881 PopupWindow * popup = new PopupWindow();
882 popupsMap[ popupann ] = popup;
883 addToPage = false;
884
885 // get window specific properties if any
886 popup->shown = popupann->getOpen();
887 // no need to parse parent annotation id
888 //XPDFReader::lookupIntRef( annotDict, "Parent", popup->... );
889
890 // use the 'dummy annotation' for getting other parameters
891 popup->dummyAnnotation = new DummyAnnotation();
892 annotation = popup->dummyAnnotation;
893
894 break;
895 }
896 case Annot::typeLink:
897 {
898 // parse Link params
899 AnnotLink * linkann = static_cast< AnnotLink * >( ann );
900 LinkAnnotation * l = new LinkAnnotation();
901 annotation = l;
902
903 // -> hlMode
904 l->setLinkHighlightMode( (LinkAnnotation::HighlightMode)linkann->getLinkEffect() );
905
906 // -> link region
907 // TODO
908
909 // reading link action
910 if ( !linkann->getDest()->isNull() )
911 {
912 ::LinkAction *act = ::LinkAction::parseDest( linkann->getDest() );
913 Link * popplerLink = m_page->convertLinkActionToLink( act, QRectF() );
914 delete act;
915 if ( popplerLink )
916 {
917 l->setLinkDestination( popplerLink );
918 }
919 }
920
921 break;
922 }
923 case Annot::typeCaret:
924 {
925 // parse CaretAnnotation params
926 AnnotCaret * caretann = static_cast< AnnotCaret * >( ann );
927 CaretAnnotation * c = new CaretAnnotation();
928 annotation = c;
929
930 // -> caretSymbol
931 c->setCaretSymbol( (CaretAnnotation::CaretSymbol)caretann->getSymbol() );
932
933 // TODO RD
934
935 break;
936 }
937 case Annot::typeFileAttachment:
938 {
939 AnnotFileAttachment * attachann = static_cast< AnnotFileAttachment * >( ann );
940 FileAttachmentAnnotation * f = new FileAttachmentAnnotation();
941 annotation = f;
942 // -> fileIcon
943 f->setFileIconName( QString::fromLatin1( attachann->getName()->getCString() ) );
944 // -> embeddedFile
945 EmbFile *embfile = new EmbFile( attachann->getFile(), attachann->getContents() );
946 f->setEmbeddedFile( new EmbeddedFile( embfile ) );
947 break;
948 }
949 case Annot::typeSound:
950 {
951 AnnotSound * soundann = static_cast< AnnotSound * >( ann );
952 SoundAnnotation * s = new SoundAnnotation();
953 annotation = s;
954
955 // -> soundIcon
956 s->setSoundIconName( QString::fromLatin1( soundann->getName()->getCString() ) );
957 // -> sound
958 s->setSound( new SoundObject( soundann->getSound() ) );
959
960 break;
961 }
962 case Annot::typeMovie:
963 {
964 AnnotMovie * movieann = static_cast< AnnotMovie * >( ann );
965 MovieAnnotation * m = new MovieAnnotation();
966 annotation = m;
967
968 // -> movie
969 MovieObject *movie = new MovieObject( movieann );
970 m->setMovie( movie );
971 // -> movieTitle
972 GooString * movietitle = movieann->getTitle();
973 if ( movietitle )
974 {
975 m->setMovieTitle( QString::fromLatin1( movietitle->getCString() ) );
976 }
977
978 break;
979 }
980 // special case for ignoring unknwon annotations
981 case Annot::typeUnknown:
982 continue;
983 default:
984 {
985 #define CASE_FOR_TYPE( thetype ) \
986 case Annot::type ## thetype: \
987 type = #thetype ; \
988 break;
989 QByteArray type;
990 switch ( subType )
991 {
992 CASE_FOR_TYPE( Widget )
993 CASE_FOR_TYPE( Screen )
994 CASE_FOR_TYPE( PrinterMark )
995 CASE_FOR_TYPE( TrapNet )
996 CASE_FOR_TYPE( Watermark )
997 CASE_FOR_TYPE( 3D )
998 default: type = QByteArray::number( subType );
999 }
1000 // MISSING: Widget, Screen, PrinterMark, TrapNet, Watermark, 3D
1001 qDebug() << "Annotation" << type.constData() << "not supported.";
1002 continue;
1003 #undef CASE_FOR_TYPE
1004 }
1005 }
1006
1007 /** 1.3. PARSE common parameters */
1008 // -> boundary
1009 PDFRectangle *boundary = ann->getRect();
1010 // transform annotation rect to uniform coords
1011 QPointF topLeft, bottomRight;
1012 XPDFReader::transform( MTX, boundary->x1, boundary->y1, topLeft );
1013 XPDFReader::transform( MTX, boundary->x2, boundary->y2, bottomRight );
1014 QRectF boundaryRect;
1015 boundaryRect.setTopLeft(topLeft);
1016 boundaryRect.setBottomRight(bottomRight);
1017 if ( boundaryRect.left() > boundaryRect.right() )
1018 {
1019 double aux = boundaryRect.left();
1020 boundaryRect.setLeft( boundaryRect.right() );
1021 boundaryRect.setRight(aux);
1022 }
1023 if ( boundaryRect.top() > boundaryRect.bottom() )
1024 {
1025 double aux = boundaryRect.top();
1026 boundaryRect.setTop( boundaryRect.bottom() );
1027 boundaryRect.setBottom(aux);
1028 //annotation->rUnscaledWidth = (r[2] > r[0]) ? r[2] - r[0] : r[0] - r[2];
1029 //annotation->rUnscaledHeight = (r[3] > r[1]) ? r[3] - r[1] : r[1] - r[3];
1030 }
1031 annotation->setBoundary( boundaryRect );
1032 // -> contents
1033 annotation->setContents( UnicodeParsedString( ann->getContents() ) );
1034 // -> uniqueName
1035 annotation->setUniqueName( UnicodeParsedString( ann->getName() ) );
1036 // -> modifyDate (and -> creationDate)
1037 GooString *modDate = ann->getModified();
1038 if ( modDate )
1039 {
1040 annotation->setModificationDate( convertDate( modDate->getCString() ) );
1041 }
1042 if ( annotation->creationDate().isNull() && !annotation->modificationDate().isNull() )
1043 annotation->setCreationDate( annotation->modificationDate() );
1044 // -> flags: set the external attribute since it's embedded on file
1045 int annoflags = 0;
1046 annoflags |= Annotation::External;
1047 // -> flags
1048 int flags = ann->getFlags();
1049 if ( flags & Annot::flagHidden )
1050 annoflags |= Annotation::Hidden;
1051 if ( flags & Annot::flagNoZoom )
1052 annoflags |= Annotation::FixedSize;
1053 if ( flags & Annot::flagNoRotate )
1054 annoflags |= Annotation::FixedRotation;
1055 if ( !( flags & Annot::flagPrint ) )
1056 annoflags |= Annotation::DenyPrint;
1057 if ( flags & Annot::flagReadOnly )
1058 annoflags |= (Annotation::DenyWrite | Annotation::DenyDelete);
1059 if ( flags & Annot::flagLocked )
1060 annoflags |= Annotation::DenyDelete;
1061 if ( flags & Annot::flagToggleNoView )
1062 annoflags |= Annotation::ToggleHidingOnMouse;
1063 annotation->setFlags( annoflags );
1064 // -> style
1065 AnnotBorder *border = ann->getBorder();
1066 if ( border )
1067 {
1068 if ( border->getType() == AnnotBorder::typeArray )
1069 {
1070 AnnotBorderArray * border_array = static_cast< AnnotBorderArray * >( border );
1071 // -> style.xCorners
1072 annotation->style.xCorners = border_array->getHorizontalCorner();
1073 // -> style.yCorners
1074 annotation->style.yCorners = border_array->getVerticalCorner();
1075 }
1076 // -> style.width
1077 annotation->style.width = border->getWidth();
1078 // -> style.style
1079 annotation->style.style = (Annotation::LineStyle)( 1 << border->getStyle() );
1080 #if 0
1081 // -> style.marks and style.spaces
1082 // TODO
1083 Object dashArray;
1084 bsObj.getDict()->lookup( "D", &dashArray );
1085 if ( dashArray.isArray() )
1086 {
1087 int dashMarks = 3;
1088 int dashSpaces = 0;
1089 Object intObj;
1090 dashArray.arrayGet( 0, &intObj );
1091 if ( intObj.isInt() )
1092 dashMarks = intObj.getInt();
1093 intObj.free();
1094 dashArray.arrayGet( 1, &intObj );
1095 if ( intObj.isInt() )
1096 dashSpaces = intObj.getInt();
1097 intObj.free();
1098 annotation->style.marks = dashMarks;
1099 annotation->style.spaces = dashSpaces;
1100 }
1101 #endif
1102 }
1103 // -> style.color
1104 annotation->style.color = convertAnnotColor( ann->getColor() );
1105
1106 /** 1.4. PARSE markup { common, Popup, Revision } parameters */
1107 if ( markupann )
1108 {
1109 // -> creationDate
1110 GooString *createDate = markupann->getDate();
1111 if ( createDate )
1112 {
1113 annotation->setCreationDate( convertDate( createDate->getCString() ) );
1114 }
1115 // -> style.opacity
1116 annotation->style.opacity = markupann->getOpacity();
1117 // -> window.title and author
1118 annotation->window.title = UnicodeParsedString( markupann->getLabel() );
1119 annotation->setAuthor( annotation->window.title );
1120 // -> window.summary
1121 annotation->window.summary = UnicodeParsedString( markupann->getSubject() );
1122 #if 0
1123 // -> window.text
1124 // TODO
1125 XPDFReader::lookupString( annotDict, "RC", annotation->window.text );
1126 #endif
1127
1128 // if a popup is referenced, schedule for resolving it later
1129 AnnotPopup *popup = markupann->getPopup();
1130 if ( popup )
1131 {
1132 ResolveWindow request;
1133 request.popup = popup;
1134 request.annotation = annotation;
1135 resolvePopList.append( request );
1136 }
1137
1138 // if an older version is referenced, schedule for reparenting
1139 int parentID = markupann->getInReplyToID();
1140 if ( parentID )
1141 {
1142 ResolveRevision request;
1143 request.nextAnnotation = annotation;
1144 request.nextAnnotationID = annotID;
1145 request.prevAnnotationID = parentID;
1146
1147 // -> request.nextScope
1148 request.nextScope = Annotation::Reply;
1149 switch ( markupann->getReplyTo() )
1150 {
1151 case AnnotMarkup::replyTypeR:
1152 request.nextScope = Annotation::Reply;
1153 break;
1154 case AnnotMarkup::replyTypeGroup:
1155 request.nextScope = Annotation::Group;
1156 break;
1157 }
1158
1159 // -> revision.type (StateModel is deduced from type, not parsed)
1160 request.nextType = Annotation::None;
1161 if ( subType == Annot::typeText )
1162 {
1163 AnnotText * textann = static_cast< AnnotText * >( ann );
1164 switch ( textann->getState() )
1165 {
1166 case AnnotText::stateMarked:
1167 request.nextType = Annotation::Marked;
1168 break;
1169 case AnnotText::stateUnmarked:
1170 request.nextType = Annotation::Unmarked;
1171 break;
1172 case AnnotText::stateAccepted:
1173 request.nextType = Annotation::Accepted;
1174 break;
1175 case AnnotText::stateRejected:
1176 request.nextType = Annotation::Rejected;
1177 break;
1178 case AnnotText::stateCancelled:
1179 request.nextType = Annotation::Cancelled;
1180 break;
1181 case AnnotText::stateCompleted:
1182 request.nextType = Annotation::Completed;
1183 break;
1184 case AnnotText::stateNone:
1185 case AnnotText::stateUnknown:
1186 ;
1187 }
1188 }
1189
1190 // schedule for later reparenting
1191 resolveRevList.append( request );
1192 }
1193 }
1194
1195 /** 1.5. ADD ANNOTATION to the annotationsMap */
1196 if ( addToPage )
1197 {
1198 if ( annotationsMap.contains( annotID ) )
1199 qDebug() << "Clash for annotations with ID:" << annotID;
1200 annotationsMap[ annotID ] = annotation;
1201 }
1202 } // end Annotation/PopupWindow parsing loop
1203
1204 /** 2 - RESOLVE POPUPS (popup.* -> annotation.window) */
1205 if ( !resolvePopList.isEmpty() && !popupsMap.isEmpty() )
1206 {
1207 QLinkedList< ResolveWindow >::iterator it = resolvePopList.begin(),
1208 end = resolvePopList.end();
1209 for ( ; it != end; ++it )
1210 {
1211 const ResolveWindow & request = *it;
1212 if ( !popupsMap.contains( request.popup ) )
1213 // warn aboud problems in popup resolving logic
1214 qDebug() << "Cannot resolve popup"
1215 << request.popup << ".";
1216 else
1217 {
1218 // set annotation's window properties taking ones from popup
1219 PopupWindow * pop = popupsMap[ request.popup ];
1220 Annotation * pa = pop->dummyAnnotation;
1221 Annotation::Window & w = request.annotation->window;
1222
1223 // transfer properties to Annotation's window
1224 w.flags = pa->flags() & (Annotation::Hidden |
1225 Annotation::FixedSize | Annotation::FixedRotation);
1226 if ( !pop->shown )
1227 w.flags |= Annotation::Hidden;
1228 w.topLeft.setX(pa->boundary().left());
1229 w.topLeft.setY(pa->boundary().top());
1230 w.width = (int)( pa->boundary().right() - pa->boundary().left() );
1231 w.height = (int)( pa->boundary().bottom() - pa->boundary().top() );
1232 }
1233 }
1234
1235 // clear data
1236 QHash< AnnotPopup *, PopupWindow * >::Iterator dIt = popupsMap.begin(), dEnd = popupsMap.end();
1237 for ( ; dIt != dEnd; ++dIt )
1238 {
1239 PopupWindow * p = dIt.value();
1240 delete p->dummyAnnotation;
1241 delete p;
1242 }
1243 }
1244
1245 /** 3 - RESOLVE REVISIONS (parent.revisions.append( children )) */
1246 if ( !resolveRevList.isEmpty() )
1247 {
1248 // append children to parents
1249 QVarLengthArray< int > excludeIDs( resolveRevList.count() ); // can't even reach this size
1250 int excludeIndex = 0; // index in excludeIDs array
1251 QLinkedList< ResolveRevision >::iterator it = resolveRevList.begin(), end = resolveRevList.end();
1252 for ( ; it != end; ++it )
1253 {
1254 const ResolveRevision & request = *it;
1255 int parentID = request.prevAnnotationID;
1256 if ( !annotationsMap.contains( parentID ) )
1257 // warn about problems in reparenting logic
1258 qDebug() << "Cannot reparent annotation to"
1259 << parentID << ".";
1260 else
1261 {
1262 // compile and add a Revision structure to the parent annotation
1263 Annotation::Revision childRevision;
1264 childRevision.annotation = request.nextAnnotation;
1265 childRevision.scope = request.nextScope;
1266 childRevision.type = request.nextType;
1267 annotationsMap[ parentID ]->revisions().append( childRevision );
1268 // exclude child annotation from being rooted in page
1269 excludeIDs[ excludeIndex++ ] = request.nextAnnotationID;
1270 }
1271 }
1272
1273 // prevent children from being attached to page as roots
1274 for ( int i = 0; i < excludeIndex; i++ )
1275 annotationsMap.remove( excludeIDs[ i ] );
1276 }
1277
1278 /** 4 - POSTPROCESS TextAnnotations (when window geom is embedded) */
1279 if ( !ppTextList.isEmpty() )
1280 {
1281 QLinkedList< PostProcessText >::const_iterator it = ppTextList.begin(), end = ppTextList.end();
1282 for ( ; it != end; ++it )
1283 {
1284 const PostProcessText & request = *it;
1285 Annotation::Window & window = request.textAnnotation->window;
1286 // if not present, 'create' the window in-place over the annotation
1287 if ( window.flags == -1 )
1288 {
1289 window.flags = 0;
1290 QRectF geom = request.textAnnotation->boundary();
1291 // initialize window geometry to annotation's one
1292 window.width = (int)( geom.right() - geom.left() );
1293 window.height = (int)( geom.bottom() - geom.top() );
1294 window.topLeft.setX( geom.left() > 0.0 ? geom.left() : 0.0 );
1295 window.topLeft.setY( geom.top() > 0.0 ? geom.top() : 0.0 );
1296 }
1297 // (pdf) if text is not 'opened', force window hiding. if the window
1298 // was parsed from popup, the flag should already be set
1299 if ( !request.opened && window.flags != -1 )
1300 window.flags |= Annotation::Hidden;
1301 }
1302 }
1303
1304 delete annots;
1305 /** 5 - finally RETURN ANNOTATIONS */
1306 return annotationsMap.values();
1307 }
1308
formFields() const1309 QList<FormField*> Page::formFields() const
1310 {
1311 QList<FormField*> fields;
1312 ::Page *p = m_page->page;
1313 ::FormPageWidgets * form = p->getPageWidgets();
1314 int formcount = form->getNumWidgets();
1315 for (int i = 0; i < formcount; ++i)
1316 {
1317 ::FormWidget *fm = form->getWidget(i);
1318 FormField * ff = NULL;
1319 switch (fm->getType())
1320 {
1321 case formButton:
1322 {
1323 ff = new FormFieldButton(m_page->parentDoc, p, static_cast<FormWidgetButton*>(fm));
1324 }
1325 break;
1326
1327 case formText:
1328 {
1329 ff = new FormFieldText(m_page->parentDoc, p, static_cast<FormWidgetText*>(fm));
1330 }
1331 break;
1332
1333 case formChoice:
1334 {
1335 ff = new FormFieldChoice(m_page->parentDoc, p, static_cast<FormWidgetChoice*>(fm));
1336 }
1337 break;
1338
1339 default: ;
1340 }
1341
1342 if (ff)
1343 fields.append(ff);
1344 }
1345
1346 return fields;
1347 }
1348
duration() const1349 double Page::duration() const
1350 {
1351 return m_page->page->getDuration();
1352 }
1353
label() const1354 QString Page::label() const
1355 {
1356 GooString goo;
1357 if (!m_page->parentDoc->doc->getCatalog()->indexToLabel(m_page->index, &goo))
1358 return QString();
1359
1360 return QString(goo.getCString());
1361 }
1362
1363
1364 }
1365