SmartPDF - lightweight pdf viewer app for rosapps
[reactos.git] / rosapps / smartpdf / src / PdfEngine.cc
1 /* Copyright Krzysztof Kowalczyk 2006-2007
2 License: GPLv2 */
3 #include "base_util.h"
4 #include "PdfEngine.h"
5
6 #include "ErrorCodes.h"
7 #include "GooString.h"
8 #include "GooList.h"
9 #include "GlobalParams.h"
10 #include "SplashBitmap.h"
11 #include "Object.h" /* must be included before SplashOutputDev.h because of sloppiness in SplashOutputDev.h */
12 #include "SplashOutputDev.h"
13 #include "TextOutputDev.h"
14 #include "PDFDoc.h"
15 #include "SecurityHandler.h"
16 #include "Link.h"
17 #include "str_util.h"
18
19 // in SumatraPDF.cpp
20 extern "C" char *GetPasswordForFile(WindowInfo *win, const char *fileName);
21
22 const char* const LINK_ACTION_GOTO = "linkActionGoTo";
23 const char* const LINK_ACTION_GOTOR = "linkActionGoToR";
24 const char* const LINK_ACTION_LAUNCH = "linkActionLaunch";
25 const char* const LINK_ACTION_URI = "linkActionUri";
26 const char* const LINK_ACTION_NAMED = "linkActionNamed";
27 const char* const LINK_ACTION_MOVIE = "linkActionMovie";
28 const char* const LINK_ACTION_UNKNOWN = "linkActionUnknown";
29
30 static SplashColorMode gSplashColorMode = splashModeBGR8;
31
32 static SplashColor splashColRed;
33 static SplashColor splashColGreen;
34 static SplashColor splashColBlue;
35 static SplashColor splashColWhite;
36 static SplashColor splashColBlack;
37
38 #define SPLASH_COL_RED_PTR (SplashColorPtr)&(splashColRed[0])
39 #define SPLASH_COL_GREEN_PTR (SplashColorPtr)&(splashColGreen[0])
40 #define SPLASH_COL_BLUE_PTR (SplashColorPtr)&(splashColBlue[0])
41 #define SPLASH_COL_WHITE_PTR (SplashColorPtr)&(splashColWhite[0])
42 #define SPLASH_COL_BLACK_PTR (SplashColorPtr)&(splashColBlack[0])
43
44 static SplashColorPtr gBgColor = SPLASH_COL_WHITE_PTR;
45
46 static void splashColorSet(SplashColorPtr col, Guchar red, Guchar green, Guchar blue, Guchar alpha)
47 {
48 switch (gSplashColorMode)
49 {
50 case splashModeBGR8:
51 col[0] = blue;
52 col[1] = green;
53 col[2] = red;
54 break;
55 case splashModeRGB8:
56 col[0] = red;
57 col[1] = green;
58 col[2] = blue;
59 break;
60 default:
61 assert(0);
62 break;
63 }
64 }
65
66 void SplashColorsInit(void)
67 {
68 splashColorSet(SPLASH_COL_RED_PTR, 0xff, 0, 0, 0);
69 splashColorSet(SPLASH_COL_GREEN_PTR, 0, 0xff, 0, 0);
70 splashColorSet(SPLASH_COL_BLUE_PTR, 0, 0, 0xff, 0);
71 splashColorSet(SPLASH_COL_BLACK_PTR, 0, 0, 0, 0);
72 splashColorSet(SPLASH_COL_WHITE_PTR, 0xff, 0xff, 0xff, 0);
73 }
74
75 static HBITMAP createDIBitmapCommon(RenderedBitmap *bmp, HDC hdc)
76 {
77 int bmpDx = bmp->dx();
78 int bmpDy = bmp->dy();
79 int bmpRowSize = bmp->rowSize();
80
81 BITMAPINFOHEADER bmih;
82 bmih.biSize = sizeof(bmih);
83 bmih.biHeight = -bmpDy;
84 bmih.biWidth = bmpDx;
85 bmih.biPlanes = 1;
86 bmih.biBitCount = 24;
87 bmih.biCompression = BI_RGB;
88 bmih.biSizeImage = bmpDy * bmpRowSize;;
89 bmih.biXPelsPerMeter = bmih.biYPelsPerMeter = 0;
90 bmih.biClrUsed = bmih.biClrImportant = 0;
91
92 unsigned char* bmpData = bmp->data();
93 HBITMAP hbmp = ::CreateDIBitmap(hdc, &bmih, CBM_INIT, bmpData, (BITMAPINFO *)&bmih , DIB_RGB_COLORS);
94 return hbmp;
95 }
96
97 static void stretchDIBitsCommon(RenderedBitmap *bmp, HDC hdc, int leftMargin, int topMargin, int pageDx, int pageDy)
98 {
99 int bmpDx = bmp->dx();
100 int bmpDy = bmp->dy();
101 int bmpRowSize = bmp->rowSize();
102
103 BITMAPINFOHEADER bmih;
104 bmih.biSize = sizeof(bmih);
105 bmih.biHeight = -bmpDy;
106 bmih.biWidth = bmpDx;
107 bmih.biPlanes = 1;
108 // we could create this dibsection in monochrome
109 // if the printer is monochrome, to reduce memory consumption
110 // but splash is currently setup to return a full colour bitmap
111 bmih.biBitCount = 24;
112 bmih.biCompression = BI_RGB;
113 bmih.biSizeImage = bmpDy * bmpRowSize;;
114 bmih.biXPelsPerMeter = bmih.biYPelsPerMeter = 0;
115 bmih.biClrUsed = bmih.biClrImportant = 0;
116 SplashColorPtr bmpData = bmp->data();
117
118 ::StretchDIBits(hdc,
119 // destination rectangle
120 -leftMargin, -topMargin, pageDx, pageDy,
121 // source rectangle
122 0, 0, bmpDx, bmpDy,
123 bmpData,
124 (BITMAPINFO *)&bmih ,
125 DIB_RGB_COLORS,
126 SRCCOPY);
127 }
128
129 RenderedBitmapFitz::RenderedBitmapFitz(fz_pixmap *bitmap)
130 {
131 _bitmap = bitmap;
132 }
133
134 RenderedBitmapFitz::~RenderedBitmapFitz()
135 {
136 if (_bitmap)
137 fz_droppixmap(_bitmap);
138 }
139
140 int RenderedBitmapFitz::rowSize()
141 {
142 int rowSize = ((_bitmap->w * 3 + 3) / 4) * 4;
143 return rowSize;
144 }
145
146 unsigned char *RenderedBitmapFitz::data()
147 {
148 #ifdef FITZ_HEAD
149 unsigned char* bmpData = _bitmap->p;
150 #else
151 unsigned char* bmpData = _bitmap->samples;
152 #endif
153 return bmpData;
154 }
155
156 HBITMAP RenderedBitmapFitz::createDIBitmap(HDC hdc)
157 {
158 return createDIBitmapCommon(this, hdc);
159 }
160
161 void RenderedBitmapFitz::stretchDIBits(HDC hdc, int leftMargin, int topMargin, int pageDx, int pageDy)
162 {
163 stretchDIBitsCommon(this, hdc, leftMargin, topMargin, pageDx, pageDy);
164 }
165
166 RenderedBitmapSplash::RenderedBitmapSplash(SplashBitmap *bitmap)
167 {
168 _bitmap = bitmap;
169 }
170
171 RenderedBitmapSplash::~RenderedBitmapSplash() {
172 delete _bitmap;
173 }
174
175 int RenderedBitmapSplash::dx()
176 {
177 return _bitmap->getWidth();
178 }
179
180 int RenderedBitmapSplash::dy()
181 {
182 return _bitmap->getHeight();
183 }
184
185 int RenderedBitmapSplash::rowSize()
186 {
187 return _bitmap->getRowSize();
188 }
189
190 unsigned char *RenderedBitmapSplash::data()
191 {
192 return _bitmap->getDataPtr();
193 }
194
195 HBITMAP RenderedBitmapSplash::createDIBitmap(HDC hdc)
196 {
197 return createDIBitmapCommon(this, hdc);
198 }
199
200 void RenderedBitmapSplash::stretchDIBits(HDC hdc, int leftMargin, int topMargin, int pageDx, int pageDy)
201 {
202 stretchDIBitsCommon(this, hdc, leftMargin, topMargin, pageDx, pageDy);
203 }
204
205 PdfEnginePoppler::PdfEnginePoppler() :
206 PdfEngine()
207 , _pdfDoc(NULL)
208 , _outputDev(NULL)
209 , _linksForPage(NULL)
210 {
211 }
212
213 PdfEnginePoppler::~PdfEnginePoppler()
214 {
215 delete _outputDev;
216 delete _pdfDoc;
217 for (int i = 0; (i < _pageCount) && _linksForPage; i++)
218 delete _linksForPage[i];
219 free(_linksForPage);
220 }
221
222 bool PdfEnginePoppler::load(const char *fileName, WindowInfo *win)
223 {
224 setFileName(fileName);
225 _windowInfo = win;
226 /* note: don't delete fileNameStr since PDFDoc takes ownership and deletes them itself */
227 GooString *fileNameStr = new GooString(fileName);
228 if (!fileNameStr) return false;
229
230 _pdfDoc = new PDFDoc(fileNameStr, NULL, NULL, (void*)win);
231 if (!_pdfDoc->isOk()) {
232 return false;
233 }
234 _pageCount = _pdfDoc->getNumPages();
235 _linksForPage = (Links**)malloc(_pageCount * sizeof(Links*));
236 if (!_linksForPage) return false;
237 for (int i=0; i < _pageCount; i++)
238 _linksForPage[i] = NULL;
239 return true;
240 }
241
242 int PdfEnginePoppler::pageRotation(int pageNo)
243 {
244 assert(validPageNo(pageNo));
245 return pdfDoc()->getPageRotate(pageNo);
246 }
247
248 SizeD PdfEnginePoppler::pageSize(int pageNo)
249 {
250 double dx = pdfDoc()->getPageCropWidth(pageNo);
251 double dy = pdfDoc()->getPageCropHeight(pageNo);
252 return SizeD(dx, dy);
253 }
254
255 SplashOutputDev * PdfEnginePoppler::outputDevice() {
256 if (!_outputDev) {
257 GBool bitmapTopDown = gTrue;
258 _outputDev = new SplashOutputDev(gSplashColorMode, 4, gFalse, gBgColor, bitmapTopDown);
259 if (_outputDev)
260 _outputDev->startDoc(_pdfDoc->getXRef());
261 }
262 return _outputDev;
263 }
264
265 RenderedBitmap *PdfEnginePoppler::renderBitmap(
266 int pageNo, double zoomReal, int rotation,
267 BOOL (*abortCheckCbkA)(void *data),
268 void *abortCheckCbkDataA)
269 {
270 assert(outputDevice());
271 if (!outputDevice()) return NULL;
272
273 //DBG_OUT("PdfEnginePoppler::RenderBitmap(pageNo=%d) rotate=%d, zoomReal=%.2f%%\n", pageNo, rotation, zoomReal);
274
275 double hDPI = (double)PDF_FILE_DPI * zoomReal * 0.01;
276 double vDPI = (double)PDF_FILE_DPI * zoomReal * 0.01;
277 GBool useMediaBox = gFalse;
278 GBool crop = gTrue;
279 GBool doLinks = gTrue;
280 _pdfDoc->displayPage(_outputDev, pageNo, hDPI, vDPI, rotation, useMediaBox, crop, doLinks,
281 abortCheckCbkA, abortCheckCbkDataA);
282
283 #if 0
284 PdfPageInfo *pageInfo = getPageInfo(pageNo);
285 if (!pageInfo->links) {
286 /* displayPage calculates links for this page (if doLinks is true)
287 and puts inside pdfDoc */
288 pageInfo->links = pdfDoc->takeLinks();
289 if (pageInfo->links->getNumLinks() > 0)
290 RecalcLinks();
291 }
292 #endif
293 SplashBitmap* bmp = _outputDev->takeBitmap();
294 if (bmp)
295 return new RenderedBitmapSplash(bmp);
296
297 return NULL;
298 }
299
300 bool PdfEnginePoppler::printingAllowed()
301 {
302 if (_pdfDoc->okToPrint())
303 return true;
304 return false;
305 }
306
307 Links* PdfEnginePoppler::getLinksForPage(int pageNo)
308 {
309 if (!_linksForPage)
310 return NULL;
311 if (_linksForPage[pageNo-1])
312 return NULL;
313
314 Object obj;
315 Catalog *catalog = _pdfDoc->getCatalog();
316 Page *page = catalog->getPage(pageNo);
317 Links *links = new Links(page->getAnnots(&obj), catalog->getBaseURI());
318 obj.free();
319 _linksForPage[pageNo-1] = links;
320 return _linksForPage[pageNo-1];
321 }
322
323 int PdfEnginePoppler::linkCount(int pageNo) {
324 Links *links = getLinksForPage(pageNo);
325 if (!links) return 0;
326 return links->getNumLinks();
327 }
328
329 const char* linkActionKindToLinkType(LinkActionKind kind) {
330 switch (kind) {
331 case (actionGoTo):
332 return LINK_ACTION_GOTO;
333 case actionGoToR:
334 return LINK_ACTION_GOTOR;
335 case actionLaunch:
336 return LINK_ACTION_LAUNCH;
337 case actionURI:
338 return LINK_ACTION_URI;
339 case actionNamed:
340 return LINK_ACTION_NAMED;
341 case actionMovie:
342 return LINK_ACTION_MOVIE;
343 case actionUnknown:
344 return LINK_ACTION_UNKNOWN;
345 default:
346 assert(0);
347 return LINK_ACTION_UNKNOWN;
348 }
349 }
350
351 const char* PdfEnginePoppler::linkType(int pageNo, int linkNo) {
352 Links *links = getLinksForPage(pageNo);
353 if (!links) return 0;
354 int linkCount = links->getNumLinks();
355 assert(linkNo < linkCount);
356 Link *link = links->getLink(linkNo-1);
357 LinkAction * action = link->getAction();
358 LinkActionKind actionKind = action->getKind();
359 return linkActionKindToLinkType(actionKind);
360 }
361
362 fz_matrix PdfEngineFitz::viewctm (pdf_page *page, float zoom, int rotate)
363 {
364 fz_matrix ctm;
365 ctm = fz_identity();
366 ctm = fz_concat(ctm, fz_translate(0, -page->mediabox.y1));
367 ctm = fz_concat(ctm, fz_scale(zoom, -zoom));
368 ctm = fz_concat(ctm, fz_rotate(rotate + page->rotate));
369 return ctm;
370 }
371
372 PdfEngineFitz::PdfEngineFitz() :
373 PdfEngine()
374 , _popplerEngine(NULL)
375 , _xref(NULL)
376 , _outline(NULL)
377 , _pageTree(NULL)
378 , _pages(NULL)
379 , _rast(NULL)
380 {
381 _getPageSem = CreateSemaphore(NULL, 1, 1, NULL);
382 }
383
384 PdfEngineFitz::~PdfEngineFitz()
385 {
386 if (_pages) {
387 for (int i=0; i < _pageCount; i++) {
388 if (_pages[i])
389 pdf_droppage(_pages[i]);
390 }
391 free(_pages);
392 }
393
394 if (_pageTree)
395 pdf_droppagetree(_pageTree);
396
397 if (_outline)
398 pdf_dropoutline(_outline);
399
400 if (_xref) {
401 if (_xref->store)
402 pdf_dropstore(_xref->store);
403 _xref->store = 0;
404 pdf_closexref(_xref);
405 }
406
407 if (_rast) {
408 #ifdef FITZ_HEAD
409 fz_dropgraphics(_rast);
410 #else
411 fz_droprenderer(_rast);
412 #endif
413 }
414
415 CloseHandle(_getPageSem);
416
417 delete _popplerEngine;
418 }
419
420 bool PdfEngineFitz::load(const char *fileName, WindowInfo *win)
421 {
422 assert(!_popplerEngine);
423 _windowInfo = win;
424 setFileName(fileName);
425 fz_error *error = pdf_newxref(&_xref);
426 if (error)
427 goto Error;
428
429 error = pdf_loadxref(_xref, (char*)fileName);
430 if (error) {
431 if (!strncmp(error->msg, "ioerror", 7))
432 goto Error;
433 error = pdf_repairxref(_xref, (char*)fileName);
434 if (error)
435 goto TryPoppler;
436 }
437
438 error = pdf_decryptxref(_xref);
439 if (error)
440 goto Error;
441
442 if (_xref->crypt) {
443 #ifdef FITZ_HEAD
444 int okay = pdf_setpassword(_xref->crypt, "");
445 if (!okay)
446 goto Error;
447 if (!win) {
448 // win might not be given if called from pdfbench.cc
449 goto Error;
450 }
451 for (int i=0; i<3; i++) {
452 char *pwd = GetPasswordForFile(win, fileName);
453 okay = pdf_setpassword(_xref->crypt, pwd);
454 free(pwd);
455 if (okay)
456 goto DecryptedOk;
457 }
458 goto Error;
459 #else
460 error = pdf_setpassword(_xref->crypt, "");
461 if (!error)
462 goto DecryptedOk;
463 if (!win) {
464 // win might not be given if called from pdfbench.cc
465 goto Error;
466 }
467 for (int i=0; i<3; i++) {
468 char *pwd = GetPasswordForFile(win, fileName);
469 // dialog box was cancelled
470 if (!pwd)
471 goto Error;
472 error = pdf_setpassword(_xref->crypt, pwd);
473 free(pwd);
474 if (!error)
475 goto DecryptedOk;
476 }
477 goto Error;
478 #endif
479 }
480
481 DecryptedOk:
482 error = pdf_loadpagetree(&_pageTree, _xref);
483 if (error)
484 goto Error;
485
486 error = pdf_loadoutline(&_outline, _xref);
487 // TODO: can I ignore this error?
488 if (error)
489 goto Error;
490
491 _pageCount = _pageTree->count;
492 _pages = (pdf_page**)malloc(sizeof(pdf_page*) * _pageCount);
493 for (int i = 0; i < _pageCount; i++)
494 _pages[i] = NULL;
495 return true;
496 Error:
497 return false;
498 TryPoppler:
499 _popplerEngine = new PdfEnginePoppler();
500 if (!_popplerEngine)
501 return false;
502 bool fok = _popplerEngine->load(fileName, win);
503 if (!fok)
504 goto ErrorPoppler;
505 return true;
506
507 ErrorPoppler:
508 delete _popplerEngine;
509 return false;
510 }
511
512 pdf_page *PdfEngineFitz::getPdfPage(int pageNo)
513 {
514 if (!_pages)
515 return NULL;
516
517 WaitForSingleObject(_getPageSem, INFINITE);
518 pdf_page* page = _pages[pageNo-1];
519 if (page) {
520 if (!ReleaseSemaphore(_getPageSem, 1, NULL))
521 DBG_OUT("Fitz: ReleaseSemaphore error!\n");
522 return page;
523 }
524 // TODO: should check for error from pdf_getpageobject?
525 fz_obj * obj = pdf_getpageobject(_pageTree, pageNo - 1);
526 fz_error * error = pdf_loadpage(&page, _xref, obj);
527 assert (!error);
528 if (error) {
529 if (!ReleaseSemaphore(_getPageSem, 1, NULL))
530 DBG_OUT("Fitz: ReleaseSemaphore error!\n");
531 fz_droperror(error);
532 return NULL;
533 }
534 _pages[pageNo-1] = page;
535 if (!ReleaseSemaphore(_getPageSem, 1, NULL))
536 DBG_OUT("Fitz: ReleaseSemaphore error!\n");
537 return page;
538 }
539
540 void PdfEngineFitz::dropPdfPage(int pageNo)
541 {
542 assert(_pages);
543 if (!_pages) return;
544 pdf_page* page = _pages[pageNo-1];
545 assert(page);
546 if (!page) return;
547 pdf_droppage(page);
548 _pages[pageNo-1] = NULL;
549 }
550
551 int PdfEngineFitz::pageRotation(int pageNo)
552 {
553 if (_popplerEngine)
554 return _popplerEngine->pageRotation(pageNo);
555
556 assert(validPageNo(pageNo));
557 fz_obj *dict = pdf_getpageobject(pages(), pageNo - 1);
558 int rotation;
559 fz_error *error = pdf_getpageinfo(_xref, dict, NULL, &rotation);
560 if (error)
561 return INVALID_ROTATION;
562 return rotation;
563 }
564
565 SizeD PdfEngineFitz::pageSize(int pageNo)
566 {
567 if (_popplerEngine)
568 return _popplerEngine->pageSize(pageNo);
569
570 assert(validPageNo(pageNo));
571 fz_obj *dict = pdf_getpageobject(pages(), pageNo - 1);
572 fz_rect bbox;
573 fz_error *error = pdf_getpageinfo(_xref, dict, &bbox, NULL);
574 if (error)
575 return SizeD(0,0);
576 return SizeD(fabs(bbox.x1 - bbox.x0), fabs(bbox.y1 - bbox.y0));
577 }
578
579 bool PdfEngineFitz::printingAllowed()
580 {
581 assert(_xref);
582 int permissionFlags = PDF_DEFAULT_PERM_FLAGS;
583 if (_xref && _xref->crypt)
584 permissionFlags = _xref->crypt->p;
585 if (permissionFlags & PDF_PERM_PRINT)
586 return true;
587 return false;
588 }
589
590 static void ConvertPixmapForWindows(fz_pixmap *image)
591 {
592 int bmpstride = ((image->w * 3 + 3) / 4) * 4;
593 unsigned char *bmpdata = (unsigned char*)fz_malloc(image->h * bmpstride);
594 if (!bmpdata)
595 return;
596
597 for (int y = 0; y < image->h; y++)
598 {
599 unsigned char *p = bmpdata + y * bmpstride;
600 #ifdef FITZ_HEAD
601 unsigned char *s = image->p + y * image->w * 4;
602 #else
603 unsigned char *s = image->samples + y * image->w * 4;
604 #endif
605 for (int x = 0; x < image->w; x++)
606 {
607 p[x * 3 + 0] = s[x * 4 + 3];
608 p[x * 3 + 1] = s[x * 4 + 2];
609 p[x * 3 + 2] = s[x * 4 + 1];
610 }
611 }
612 #ifdef FITZ_HEAD
613 fz_free(image->p);
614 image->p = bmpdata;
615 #else
616 fz_free(image->samples);
617 image->samples = bmpdata;
618 #endif
619 }
620
621 RenderedBitmap *PdfEngineFitz::renderBitmap(
622 int pageNo, double zoomReal, int rotation,
623 BOOL (*abortCheckCbkA)(void *data),
624 void *abortCheckCbkDataA)
625 {
626 fz_error* error;
627 fz_matrix ctm;
628 fz_rect bbox;
629
630 if (_popplerEngine)
631 return _popplerEngine->renderBitmap(pageNo, zoomReal, rotation, abortCheckCbkA, abortCheckCbkDataA);
632
633 if (!_rast) {
634 #ifdef FITZ_HEAD
635 error = fz_newgraphics(&_rast, 1024 * 512);
636 #else
637 error = fz_newrenderer(&_rast, pdf_devicergb, 0, 1024 * 512);
638 #endif
639 }
640
641 fz_pixmap* image = NULL;
642 pdf_page* page = getPdfPage(pageNo);
643 if (!page)
644 goto TryPoppler;
645 zoomReal = zoomReal / 100.0;
646 ctm = viewctm(page, zoomReal, rotation);
647 bbox = fz_transformaabb(ctm, page->mediabox);
648 #ifdef FITZ_HEAD
649 error = fz_drawtree(&image, _rast, page->tree, ctm, pdf_devicergb, fz_roundrect(bbox), 1);
650 #else
651 error = fz_rendertree(&image, _rast, page->tree, ctm, fz_roundrect(bbox), 1);
652 #endif
653 #if CONSERVE_MEMORY
654 dropPdfPage(pageNo);
655 #endif
656 if (error)
657 goto TryPoppler;
658 ConvertPixmapForWindows(image);
659 return new RenderedBitmapFitz(image);
660 TryPoppler:
661 _popplerEngine = new PdfEnginePoppler();
662 if (!_popplerEngine)
663 return false;
664 bool fok = _popplerEngine->load(fileName(), _windowInfo);
665 if (!fok)
666 goto ErrorPoppler;
667 return _popplerEngine->renderBitmap(pageNo, zoomReal, rotation, abortCheckCbkA, abortCheckCbkDataA);
668 ErrorPoppler:
669 return NULL;
670 }
671
672 static int getLinkCount(pdf_link *currLink) {
673 if (!currLink)
674 return 0;
675 int count = 1;
676 while (currLink->next) {
677 ++count;
678 currLink = currLink->next;
679 }
680 return count;
681 }
682
683 int PdfEngineFitz::linkCount(int pageNo) {
684 pdf_page* page = getPdfPage(pageNo);
685 if (!page)
686 return 0;
687 return getLinkCount(page->links);
688 }
689
690 static const char *linkToLinkType(pdf_link *link) {
691 switch (link->kind) {
692 case PDF_LGOTO:
693 return LINK_ACTION_GOTO;
694 case PDF_LURI:
695 return LINK_ACTION_URI;
696 case PDF_LUNKNOWN: /* @note: add unhandled value */
697 OutputDebugString(L"Link unknown");
698 break;
699 }
700 return LINK_ACTION_UNKNOWN;
701 }
702
703 const char* PdfEngineFitz::linkType(int pageNo, int linkNo) {
704 pdf_page* page = getPdfPage(pageNo);
705 pdf_link* currLink = page->links;
706 for (int i = 0; i < linkNo; i++) {
707 assert(currLink);
708 if (!currLink)
709 return NULL;
710 currLink = currLink->next;
711 }
712 return linkToLinkType(currLink);
713 }
714