1 // ePDFView - A lightweight PDF Viewer.
2 // Copyright (C) 2006, 2007, 2009 Emma's Software.
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18 #include <config.h>
19 #include <gdk/gdk.h>
20 #include <time.h>
21 #include <poppler.h>
22 #include <unistd.h>
23 #include <algorithm>
24 #include "epdfview.h"
25
26 using namespace ePDFView;
27
28 // Constants.
29 static const gint PIXBUF_BITS_PER_SAMPLE = 8;
30 static const gint DATE_LENGTH = 100;
31
32 // Forward declarations.
33 static PageLayout convertPageLayout (gint pageLayout);
34 static PageMode convertPageMode (gint pageMode);
35 static gchar *getAbsoluteFileName (const gchar *fileName);
36
37 namespace
38 {
39 void
convert_bgra_to_rgba(guint8 * data,int width,int height)40 convert_bgra_to_rgba (guint8 *data, int width, int height)
41 {
42 using std::swap;
43
44 for (int y = 0; y < height; y++)
45 {
46 for (int x = 0; x < width; x++)
47 {
48 swap(data[0], data[2]);
49 data += 4;
50 }
51 }
52 }
53 }
54 ///
55 /// @brief Constructs a new PDFDocument object.
56 ///
PDFDocument()57 PDFDocument::PDFDocument ():
58 IDocument ()
59 {
60 m_Document = NULL;
61 m_PostScript = NULL;
62 }
63
64 ///
65 /// @brief Deletes all dynamically created objects of PDFDocument.
66 ///
~PDFDocument()67 PDFDocument::~PDFDocument ()
68 {
69 clearCache ();
70 outputPostscriptEnd ();
71 if ( NULL != m_Document )
72 {
73 g_object_unref (G_OBJECT (m_Document));
74 m_Document = NULL;
75 }
76 }
77
78 IDocument *
copy() const79 PDFDocument::copy () const
80 {
81 // Making a deep copy is just making a new document and loading the same
82 // file.
83 PDFDocument *newDocument = new PDFDocument ();
84 newDocument->loadFile (getFileName (), getPassword (), NULL);
85
86 return newDocument;
87 }
88
89 ///
90 /// @brief Creates a new document link.
91 ///
92 /// Based on the passed @a link, this function creates a new document
93 /// links of the best type for the link's action.
94 ///
95 /// @param link The link to create the document link from.
96 /// @param pageHeight The unscaled size of the page's height.
97 /// @param scale The scale to calculate the link's coordinates.
98 ///
99 /// @return The link best suited for @a link or NULL if no link can be
100 /// created.
101 IDocumentLink *
createDocumentLink(const PopplerLinkMapping * link,const gdouble pageHeight,const gdouble scale)102 PDFDocument::createDocumentLink (const PopplerLinkMapping *link,
103 const gdouble pageHeight,
104 const gdouble scale)
105 {
106 PopplerAction *action = link->action;
107 IDocumentLink *documentLink = NULL;
108
109 // Calculate the four link's corners.
110 gdouble topLeft = link->area.x1 * scale;
111 gdouble topRight = link->area.x2 * scale;
112 gdouble bottomLeft = (pageHeight - link->area.y2) * scale;
113 gdouble bottomRight = (pageHeight - link->area.y1) * scale;
114
115 switch (action->type)
116 {
117 // Internal cross-reference link.
118 case POPPLER_ACTION_GOTO_DEST:
119 {
120 PopplerActionGotoDest *actionGoTo = (PopplerActionGotoDest *)action;
121 PopplerDest *destination = actionGoTo->dest;
122 int pageNum = destination->page_num;
123 #if defined (HAVE_POPPLER_0_5_2)
124 if ( POPPLER_DEST_NAMED == destination->type )
125 {
126 destination =
127 poppler_document_find_dest (m_Document,
128 destination->named_dest);
129 if ( NULL != destination )
130 {
131 pageNum = destination->page_num;
132 poppler_dest_free (destination);
133 }
134 }
135 #endif // HAVE_POPPLER_0_5_2
136
137 documentLink = new DocumentLinkGoto (
138 topLeft, bottomLeft, topRight, bottomRight,
139 pageNum);
140 break;
141 }
142 // Internet address.
143 case POPPLER_ACTION_URI:
144 {
145 PopplerActionUri *actionUri = (PopplerActionUri *)action;
146 documentLink = new DocumentLinkUri (
147 topLeft, bottomLeft, topRight, bottomRight,
148 actionUri->uri);
149 break;
150 }
151
152 default:
153 g_warning ("Poppler's link type %d not handled.", action->type);
154 break;
155 }
156
157 return documentLink;
158 }
159
160 GList *
findTextInPage(gint pageNum,const gchar * textToFind)161 PDFDocument::findTextInPage (gint pageNum, const gchar *textToFind)
162 {
163 GList *results = NULL;
164
165 if ( NULL == m_Document )
166 {
167 return results;
168 }
169
170 PopplerPage *page = poppler_document_get_page (m_Document, pageNum - 1);
171 if ( NULL != page )
172 {
173 gdouble height = 1.0;
174 poppler_page_get_size (page, NULL, &height);
175 GList *matches = poppler_page_find_text (page, textToFind);
176 for ( GList *match = g_list_first (matches) ;
177 NULL != match ;
178 match = g_list_next (match) )
179 {
180 PopplerRectangle *matchRect = (PopplerRectangle *)match->data;
181 DocumentRectangle *rect =
182 new DocumentRectangle (matchRect->x1,
183 (height - matchRect->y2),
184 matchRect->x2,
185 (height - matchRect->y1));
186 results = g_list_prepend (results, rect);
187 }
188 g_object_unref (G_OBJECT (page));
189 }
190
191 return g_list_reverse (results);
192 }
193
194 ///
195 /// @brief Checks if the document has been loaded.
196 ///
197 /// @return TRUE if the document has been loaded, FALSE otherwise.
198 ///
199 gboolean
isLoaded()200 PDFDocument::isLoaded ()
201 {
202 return (NULL != m_Document);
203 }
204
205 ///
206 /// @brief Loads a PDF file.
207 ///
208 /// Tries to open the PDF file @a filename using the password in @a password.
209 ///
210 /// @param filename The name of the file name to open. It must be an absolute
211 /// path.
212 /// @param password The password to use to open @a filename.
213 /// @param error Location to store the error occurring or NULL to ignore
214 /// errors.
215 ///
216 /// @return TRUE if the file could be opened, FALSE otherwise.
217 ///
218 gboolean
loadFile(const gchar * filename,const gchar * password,GError ** error)219 PDFDocument::loadFile (const gchar *filename, const gchar *password,
220 GError **error)
221 {
222 g_assert (NULL != filename && "Tried to load a NULL file name");
223
224 gchar *absoluteFileName = getAbsoluteFileName (filename);
225 gchar *filename_uri = g_filename_to_uri (absoluteFileName, NULL, error);
226 g_free (absoluteFileName);
227 if ( NULL == filename_uri )
228 {
229 return FALSE;
230 }
231 // Try to open the PDF document.
232 GError *loadError = NULL;
233 // In case the user specified to read the PDF file from the stdin
234 // (i.e., -), then we save the contents of the stdin to a temporary
235 // file and use the URI to this temporary file to load.
236 if ( g_ascii_strcasecmp ("-", filename) == 0 )
237 {
238 gchar *tmpFileName;
239 gint fd = g_file_open_tmp ("epdfviewXXXXXX", &tmpFileName, error);
240 if ( -1 == fd )
241 {
242 return FALSE;
243 }
244 while ( !feof (stdin) )
245 {
246 gchar inputLine[512];
247 size_t readBytes = fread (inputLine, sizeof (char),
248 sizeof (inputLine) / sizeof (char),
249 stdin);
250 write (fd, inputLine, readBytes);
251 }
252 close (fd);
253
254 g_free (filename_uri);
255 filename_uri = g_filename_to_uri (tmpFileName, NULL, error);
256 g_free (tmpFileName);
257 if ( NULL == filename_uri )
258 {
259 return FALSE;
260 }
261 }
262 PopplerDocument *newDocument =
263 poppler_document_new_from_file (filename_uri, password, &loadError);
264 g_free (filename_uri);
265 // Check if the document couldn't be opened successfully and why.
266 if ( NULL == newDocument )
267 {
268 DocumentError errorCode = DocumentErrorNone;
269 switch ( loadError->code )
270 {
271 case POPPLER_ERROR_OPEN_FILE:
272 case POPPLER_ERROR_INVALID:
273 errorCode = DocumentErrorOpenFile;
274 break;
275
276 case POPPLER_ERROR_BAD_CATALOG:
277 errorCode = DocumentErrorBadCatalog;
278 break;
279
280 case POPPLER_ERROR_DAMAGED:
281 errorCode = DocumentErrorDamaged;
282 break;
283
284 case POPPLER_ERROR_ENCRYPTED:
285 errorCode = DocumentErrorEncrypted;
286 break;
287 }
288 g_error_free (loadError);
289 // Get our error message.
290 gchar *errorMessage = IDocument::getErrorMessage (errorCode);
291 g_set_error (error,
292 EPDFVIEW_DOCUMENT_ERROR, errorCode,
293 _("Failed to load document '%s'.\n%s\n"),
294 filename, errorMessage);
295 g_free (errorMessage);
296
297 return FALSE;
298 }
299
300 // Set the used filename and password to let the user reload the
301 // document.
302 setFileName (filename);
303 setPassword (password);
304 if ( NULL != m_Document )
305 {
306 g_object_unref (G_OBJECT (m_Document));
307 m_Document = NULL;
308 }
309 m_Document = newDocument;
310 // Load the document's information and outline.
311 loadMetadata ();
312 PopplerIndexIter *outline = poppler_index_iter_new (m_Document);
313 m_Outline = new DocumentOutline ();
314 setOutline (m_Outline, outline);
315 // XXX: I still don't know why, but it seems that if I don't request
316 // a page here, when rendering a page Glib tells me
317 // that cannot register existing type `PopplerPage'... Yes, this
318 // is a kludge, but I don't know enough to check it why it does that.
319 PopplerPage *page = poppler_document_get_page (m_Document, 0);
320 if ( NULL != page )
321 {
322 g_object_unref (G_OBJECT (page));
323 }
324
325 return TRUE;
326 }
327
328 ///
329 /// @brief Reads the document's meta data.
330 ///
331 /// After each successful load of a PDF file, its meta data is read and
332 /// keep in member variables of this class, so a call to get*() function
333 /// will return it.
334 ///
335 /// Also resets the rotation degree to 0 and the scale level to 1.0f.
336 ///
337 void
loadMetadata(void)338 PDFDocument::loadMetadata (void)
339 {
340 g_assert (NULL != m_Document && "The document has not been loaded.");
341
342 gchar *author = NULL;
343 GTime creationDate;
344 gchar *creator = NULL;
345 gchar *format = NULL;
346 gchar *keywords = NULL;
347 PopplerPageLayout layout = POPPLER_PAGE_LAYOUT_UNSET;
348 #if defined (HAVE_POPPLER_0_15_1)
349 gboolean *linearized = NULL;
350 #else
351 gchar *linearized = NULL;
352 #endif
353 GTime modDate;
354 PopplerPageMode mode = POPPLER_PAGE_MODE_UNSET;
355 gchar *producer = NULL;
356 gchar *subject = NULL;
357 gchar *title = NULL;
358
359 g_object_get (m_Document,
360 "author", &author,
361 "creation-date", &creationDate,
362 "creator", &creator,
363 "format", &format,
364 "keywords", &keywords,
365 "page-layout", &layout,
366 "linearized", &linearized,
367 "mod-date", &modDate,
368 "page-mode", &mode,
369 "producer", &producer,
370 "subject", &subject,
371 "title", &title,
372 NULL);
373 setAuthor (author);
374 if ( 0 < creationDate )
375 {
376 struct tm *tmpTime = localtime ((const time_t *)&creationDate);
377 if ( NULL != tmpTime )
378 {
379 gchar *date = g_strnfill (DATE_LENGTH + 1, 0);
380 strftime (date, DATE_LENGTH, "%Y-%m-%d %H:%M:%S", tmpTime);
381 setCreationDate (date);
382 }
383 }
384 else
385 {
386 setCreationDate (NULL);
387 }
388 setCreator (creator);
389 setFormat (format);
390 setKeywords (keywords);
391 setLinearized (linearized);
392 if ( 0 < modDate )
393 {
394 struct tm *tmpTime = localtime ((const time_t *)&modDate);
395 if ( NULL != tmpTime )
396 {
397 gchar *date = g_strnfill (DATE_LENGTH + 1, 0);
398 strftime (date, DATE_LENGTH, "%Y-%m-%d %H:%M:%S", tmpTime);
399 setModifiedDate (date);
400 }
401 }
402 else
403 {
404 setModifiedDate (NULL);
405 }
406 setProducer (producer);
407 setSubject (subject);
408 setTitle (title);
409
410 // For the page mode and layout we need the enumerator value
411 GEnumValue *pageLayout = g_enum_get_value (
412 (GEnumClass *)g_type_class_peek (POPPLER_TYPE_PAGE_LAYOUT), layout);
413 setPageLayout (convertPageLayout (pageLayout->value));
414 GEnumValue *pageMode = g_enum_get_value (
415 (GEnumClass *)g_type_class_peek (POPPLER_TYPE_PAGE_MODE), mode);
416 setPageMode (convertPageMode (pageMode->value));
417
418 // Get the number of pages and set the current to the first.
419 setNumPages (poppler_document_get_n_pages (m_Document));
420 }
421
422 ///
423 /// @brief Sets the pages links.
424 ///
425 /// This function adds all links from a page to the rendered page
426 /// image of it.
427 ///
428 /// @param renderedPage The rendered page to add the links to.
429 /// @param popplerPage The page to get the links from.
430 ///
431 void
setLinks(DocumentPage * renderedPage,PopplerPage * popplerPage)432 PDFDocument::setLinks (DocumentPage *renderedPage, PopplerPage *popplerPage)
433 {
434 gdouble pageHeight = 1.0;
435 // Get the height, to calculate the Y position as the document's origin
436 // is at the bottom-left corner, not the top-left as the screen does.
437 poppler_page_get_size (popplerPage, NULL, &pageHeight);
438 // We'll already calculate the positions scaled.
439 gdouble scale = getZoom ();
440 GList *pageLinks = poppler_page_get_link_mapping (popplerPage);
441 for (GList *pageLink = g_list_first (pageLinks) ;
442 NULL != pageLink ;
443 pageLink = g_list_next (pageLink) )
444 {
445 PopplerLinkMapping *link = (PopplerLinkMapping *)pageLink->data;
446 IDocumentLink *documentLink = createDocumentLink (link, pageHeight, scale);
447 if ( NULL != documentLink )
448 {
449 renderedPage->addLink (documentLink);
450 }
451 }
452 poppler_page_free_link_mapping (pageLinks);
453 }
454
455 ///
456 /// @brief Sets the document's outline.
457 ///
458 /// This is a recursive function that adds child outlines
459 /// from the PDF's outline to the passed @a outline.
460 ///
461 /// @param outline The outline to set the nodes to. The first
462 /// call must be set to the root DocumentOutline.
463 /// @param childrenList The list of children for to set to @a outline.
464 /// The first line must be the returned valued of
465 /// poppler_index_iter_new().
466 ///
467 void
setOutline(DocumentOutline * outline,PopplerIndexIter * childrenList)468 PDFDocument::setOutline (DocumentOutline *outline,
469 PopplerIndexIter *childrenList)
470 {
471 if ( NULL != childrenList )
472 {
473 do
474 {
475 PopplerAction *action =
476 poppler_index_iter_get_action (childrenList);
477 if ( POPPLER_ACTION_GOTO_DEST == action->type )
478 {
479 PopplerActionGotoDest *actionGoTo =
480 (PopplerActionGotoDest *)action;
481 DocumentOutline *child = new DocumentOutline ();
482 child->setParent (outline);
483 child->setTitle (actionGoTo->title);
484 PopplerDest *destination = actionGoTo->dest;
485 child->setDestination (destination->page_num);
486 #if defined (HAVE_POPPLER_0_5_2)
487 if ( POPPLER_DEST_NAMED == destination->type )
488 {
489 destination =
490 poppler_document_find_dest (m_Document,
491 destination->named_dest);
492 if ( NULL != destination )
493 {
494 child->setDestination (destination->page_num);
495 poppler_dest_free (destination);
496 destination = NULL;
497 }
498 }
499 #endif // HAVE_POPPLER_0_5_2
500
501 outline->addChild (child);
502 PopplerIndexIter *childIter =
503 poppler_index_iter_get_child (childrenList);
504 setOutline (child, childIter);
505 }
506 }
507 while ( poppler_index_iter_next (childrenList) );
508
509 poppler_index_iter_free (childrenList);
510 }
511 }
512
513 ///
514 /// @brief Gets a document's page's unscaled size.
515 ///
516 /// Retrieves the width and height of a document's page before to scale, but
517 /// after rotation.
518 ///
519 /// @param pageNum The page to get its size.
520 /// @param width The output pointer to save the page's width.
521 /// @param height The output pointer to save the page's height.
522 ///
523 void
getPageSizeForPage(gint pageNum,gdouble * width,gdouble * height)524 PDFDocument::getPageSizeForPage (gint pageNum, gdouble *width, gdouble *height)
525 {
526 g_assert (NULL != m_Document && "Tried to get size of a NULL document.");
527 g_assert (NULL != width && "Tried to save the page's width to NULL.");
528 g_assert (NULL != height && "Tried to save the page's height to NULL.");
529
530 PopplerPage *page = poppler_document_get_page (m_Document, pageNum - 1);
531 if ( NULL != page )
532 {
533 gdouble pageWidth;
534 gdouble pageHeight;
535 // Check which rotation has the document's page to know what is width
536 // and what is height.
537 gint rotate = getRotation ();
538 if ( 90 == rotate || 270 == rotate )
539 {
540 poppler_page_get_size (page, &pageHeight, &pageWidth);
541 }
542 else
543 {
544 poppler_page_get_size (page, &pageWidth, &pageHeight);
545 }
546
547 *width = pageWidth;
548 *height = pageHeight;
549
550 g_object_unref (G_OBJECT (page));
551 }
552 else
553 {
554 // Just give any valor, except 0.
555 *width = 1.0;
556 *height = 1.0;
557 }
558 }
559
560 void
outputPostscriptBegin(const gchar * fileName,guint numOfPages,gfloat pageWidth,gfloat pageHeight)561 PDFDocument::outputPostscriptBegin (const gchar *fileName, guint numOfPages,
562 gfloat pageWidth, gfloat pageHeight)
563 {
564 if ( NULL != m_Document )
565 {
566 if ( NULL != m_PostScript )
567 {
568 outputPostscriptEnd ();
569 }
570 m_PostScript =
571 poppler_ps_file_new (m_Document, fileName, 0, numOfPages);
572 if ( NULL != m_PostScript )
573 {
574 poppler_ps_file_set_paper_size (m_PostScript,
575 pageWidth, pageHeight);
576 }
577 }
578 }
579
580 void
outputPostscriptEnd()581 PDFDocument::outputPostscriptEnd ()
582 {
583 if ( NULL != m_PostScript )
584 {
585 poppler_ps_file_free (m_PostScript);
586 m_PostScript = NULL;
587 }
588 }
589
590 void
outputPostscriptPage(guint pageNum)591 PDFDocument::outputPostscriptPage (guint pageNum)
592 {
593 if ( NULL != m_Document && NULL != m_PostScript )
594 {
595 PopplerPage *page = poppler_document_get_page (m_Document, pageNum - 1);
596 if ( NULL != page )
597 {
598 poppler_page_render_to_ps (page, m_PostScript);
599 }
600 }
601 }
602
603 ///
604 /// @brief Renders a document's page.
605 ///
606 /// Rendering a document's page means to get the pixels for the page,
607 /// given the current rotation level and scale.
608 ///
609 /// @param pageNum The page to render.
610 ///
611 /// @return A DocumentPage with the image. The returned page must be freed
612 /// by calling delete.
613 ///
614 DocumentPage *
renderPage(gint pageNum)615 PDFDocument::renderPage (gint pageNum)
616 {
617 if ( NULL == m_Document )
618 {
619 return NULL;
620 }
621
622 // First create the document's page.
623 gdouble pageWidth;
624 gdouble pageHeight;
625 getPageSizeForPage (pageNum, &pageWidth, &pageHeight);
626 gint width = MAX((gint) ((pageWidth * getZoom ()) + 0.5), 1);
627 gint height = MAX((gint) ((pageHeight * getZoom ()) + 0.5), 1);
628 DocumentPage *renderedPage = new DocumentPage ();
629 renderedPage->newPage (width, height);
630
631 PopplerPage *page = poppler_document_get_page (m_Document, pageNum - 1);
632 if ( NULL != page )
633 {
634 #if defined (HAVE_POPPLER_0_17_0)
635 cairo_surface_t *surface = cairo_image_surface_create_for_data (
636 renderedPage->getData (),
637 CAIRO_FORMAT_RGB24, width, height,
638 renderedPage->getRowStride ());
639 cairo_t *context = cairo_create (surface);
640 cairo_save(context);
641 cairo_set_source_rgb (context, 1.0, 1.0, 1.0);
642 cairo_rectangle (context, 0, 0, width, height);
643 cairo_fill(context);
644 cairo_restore(context);
645 cairo_save(context);
646
647 switch(getRotation())
648 {
649 case 90:
650 cairo_translate(context, width, 0);
651 break;
652
653 case 180:
654 cairo_translate(context, width, height);
655 break;
656
657 case 270:
658 cairo_translate(context, 0, height);
659 break;
660
661 default:
662 cairo_translate(context, 0, 0);
663 break;
664 }
665
666 cairo_scale(context, getZoom(), getZoom());
667 cairo_rotate(context, getRotation() * G_PI / 180.0);
668 poppler_page_render (page, context);
669 cairo_destroy(context);
670 cairo_surface_destroy (surface);
671 convert_bgra_to_rgba(renderedPage->getData (), width, height);
672 #else // !HAVE_POPPLER_0_17_0
673 // Create the pixbuf from the data and render to it.
674 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data (renderedPage->getData (),
675 GDK_COLORSPACE_RGB,
676 FALSE,
677 PIXBUF_BITS_PER_SAMPLE,
678 width, height,
679 renderedPage->getRowStride (),
680 NULL, NULL);
681 poppler_page_render_to_pixbuf (page, 0, 0, width, height, getZoom (),
682 getRotation (), pixbuf);
683 g_object_unref (pixbuf);
684 #endif // HAVE_POPPLER_0_17_0
685 setLinks (renderedPage, page);
686 g_object_unref (G_OBJECT (page));
687 }
688
689 return (renderedPage);
690 }
691
692 ///
693 /// @brief Saves a document's copy to a file.
694 ///
695 /// Tries to save the document to file @a filename.
696 ///
697 /// @param filename The path, absolute or relative, to the file name
698 /// to save the copy to.
699 /// @param error Location to store any error that could happen or
700 /// set to NULL to ignore errors.
701 ///
702 /// @return TRUE if the file could be saved. FALSE otherwise.
703 ///
704 gboolean
saveFile(const gchar * filename,GError ** error)705 PDFDocument::saveFile (const gchar *filename, GError **error)
706 {
707 g_assert (NULL != m_Document && "No document loaded yet.");
708 g_assert (NULL != filename && "Tried to save to a NULL file name.");
709
710 gchar *absoluteFileName = getAbsoluteFileName (filename);
711 gchar *filename_uri = g_filename_to_uri (absoluteFileName, NULL, error);
712 g_free (absoluteFileName);
713 if ( NULL == filename_uri )
714 {
715 return FALSE;
716 }
717 // Try to save the PDF document.
718 #if defined (HAVE_POPPLER_0_8_0)
719 gboolean result = poppler_document_save_a_copy (m_Document, filename_uri, error);
720 #else // !HAVE_POPPLER_0_8_0
721 gboolean result = poppler_document_save (m_Document, filename_uri, error);
722 #endif // HAVE_POPPLER_0_8_0
723 g_free (filename_uri);
724
725 return result;
726 }
727
728 static void
repairEmpty(PopplerRectangle & rect)729 repairEmpty(PopplerRectangle& rect)
730 {
731 //rect must have be least 1x1
732 if(rect.y1 == rect.y2)
733 rect.y2++;
734 if(rect.x1 == rect.x2)
735 rect.x2++;
736 }
737
738 void
setTextSelection(DocumentRectangle * rect)739 PDFDocument::setTextSelection (DocumentRectangle *rect)
740 {
741 g_assert(rect);
742
743 PopplerPage *page = poppler_document_get_page (m_Document, getCurrentPageNum()-1);
744 if(!page)
745 return;
746
747 gdouble pageWidth, pageHeight;
748 poppler_page_get_size(page, &pageWidth, &pageHeight);
749
750 #if defined (HAVE_POPPLER_0_15_0)
751 PopplerRectangle textRect = { rect->getX1() / getZoom(),
752 rect->getY1() / getZoom(),
753 rect->getX2() / getZoom(),
754 rect->getY2() / getZoom()};
755 #else // !HAVE_POPPLER_0_15_0
756 //for get text we must exchange y coordinate, don't ask me where logic here.
757 PopplerRectangle textRect = { rect->getX1() / getZoom(),
758 (pageHeight - rect->getY2() / getZoom()),
759 rect->getX2() / getZoom(),
760 (pageHeight - rect->getY1() / getZoom())};
761 #endif // HAVE_POPPLER_0_15_0
762 repairEmpty(textRect);
763
764 #if defined (HAVE_POPPLER_0_15_0)
765 gchar *text = poppler_page_get_selected_text(page, POPPLER_SELECTION_GLYPH,
766 &textRect);
767 #elif defined (HAVE_POPPLER_0_6_0)
768 gchar *text = poppler_page_get_text(page, POPPLER_SELECTION_GLYPH,
769 &textRect);
770 #else // !HAVE_POPPLER_0_6_0
771 gchar *text = poppler_page_get_text(page, &textRect);
772 #endif // HAVE_POPPLER_0_6_0
773 if(!text)
774 goto cleanup;
775
776 for ( GList *obs = g_list_first (m_Observers) ;
777 NULL != obs ;
778 obs = g_list_next (obs) )
779 {
780 IDocumentObserver *observer = (IDocumentObserver*)obs->data;
781 observer->notifyTextSelected(text);
782 }
783
784 cleanup:
785 if(page)
786 g_object_unref(page);
787 if(text)
788 g_free(text);
789 }
790
791 GdkRegion*
getTextRegion(DocumentRectangle * r)792 PDFDocument::getTextRegion (DocumentRectangle *r)
793 {
794 GdkRegion *res = NULL;
795 PopplerPage *page = poppler_document_get_page (m_Document, getCurrentPageNum()-1);
796 if(!page)
797 return NULL;
798
799 //calulate rect
800 PopplerRectangle rect;
801 rect.x1 = r->getX1() / getZoom();
802 rect.y1 = r->getY1() / getZoom();
803 rect.x2 = r->getX2() / getZoom();
804 rect.y2 = r->getY2() / getZoom();
805 repairEmpty(rect);
806
807 //calc selection size
808 #if defined (HAVE_POPPLER_0_8_0)
809 GList *selections = poppler_page_get_selection_region(page, getZoom(),
810 POPPLER_SELECTION_GLYPH, &rect);
811 res = gdk_region_new();
812 for (GList *selection = g_list_first (selections) ;
813 NULL != selection ; selection = g_list_next (selection)) {
814 PopplerRectangle *rectangle = (PopplerRectangle *)selection->data;
815 GdkRectangle rect;
816
817 rect.x = (gint)rectangle->x1;
818 rect.y = (gint)rectangle->y1;
819 rect.width = (gint) (rectangle->x2 - rectangle->x1);
820 rect.height = (gint) (rectangle->y2 - rectangle->y1);
821
822 gdk_region_union_with_rect (res, &rect);
823 }
824 poppler_page_selection_region_free (selections);
825 #elif defined (HAVE_POPPLER_0_6_0)
826 res = poppler_page_get_selection_region(page, getZoom(),
827 POPPLER_SELECTION_GLYPH, &rect);
828 #else // !HAVE_POPPLER_0_6_0
829 res = poppler_page_get_selection_region(page, getZoom(), &rect);
830 #endif // HAVE_POPPLER_0_6_0
831
832 //free some local data
833 g_object_unref(page);
834
835 return res;
836 }
837
838
839 ///
840 /// @brief Gets the document's page layout from Poppler's page layout.
841 ///
842 /// @param pageLayout Is the page layout that Poppler's glib wrapper gives.
843 ///
844 /// @return The PageLayout based on @a pageLayout.
845 ///
846 PageLayout
convertPageLayout(gint pageLayout)847 convertPageLayout (gint pageLayout)
848 {
849 PageLayout layout = PageLayoutUnset;
850 switch (pageLayout)
851 {
852 case POPPLER_PAGE_LAYOUT_SINGLE_PAGE:
853 layout = PageLayoutSinglePage;
854 break;
855 case POPPLER_PAGE_LAYOUT_ONE_COLUMN:
856 layout = PageLayoutOneColumn;
857 break;
858 case POPPLER_PAGE_LAYOUT_TWO_COLUMN_LEFT:
859 layout = PageLayoutTwoColumnLeft;
860 break;
861 case POPPLER_PAGE_LAYOUT_TWO_COLUMN_RIGHT:
862 layout = PageLayoutTwoColumnRight;
863 break;
864 case POPPLER_PAGE_LAYOUT_TWO_PAGE_LEFT:
865 layout = PageLayoutTwoPageLeft;
866 break;
867 case POPPLER_PAGE_LAYOUT_TWO_PAGE_RIGHT:
868 layout = PageLayoutTwoPageRight;
869 break;
870 case POPPLER_PAGE_LAYOUT_UNSET:
871 default:
872 layout = PageLayoutUnset;
873 }
874
875 return layout;
876 }
877
878 ///
879 /// @brief Get the document's page mode.
880 ///
881 /// @param pageLayout Is the page mode that Poppler's catalog gives.
882 ///
883 /// @return The PageLayout based on @a pageMode.
884 ///
885 PageMode
convertPageMode(gint pageMode)886 convertPageMode (gint pageMode)
887 {
888 PageMode mode = PageModeUnset;
889 switch (pageMode)
890 {
891 case POPPLER_PAGE_MODE_USE_OUTLINES:
892 mode = PageModeOutlines;
893 break;
894 case POPPLER_PAGE_MODE_USE_THUMBS:
895 mode = PageModeThumbs;
896 break;
897 case POPPLER_PAGE_MODE_FULL_SCREEN:
898 mode = PageModeFullScreen;
899 break;
900 case POPPLER_PAGE_MODE_USE_OC:
901 mode = PageModeOC;
902 break;
903 case POPPLER_PAGE_MODE_USE_ATTACHMENTS:
904 mode = PageModeAttach;
905 break;
906 case POPPLER_PAGE_MODE_NONE:
907 case POPPLER_PAGE_MODE_UNSET:
908 default:
909 mode = PageModeUnset;
910 }
911
912 return mode;
913 }
914
915 ///
916 /// @brief Gets the absolute path of a filename.
917 ///
918 /// This function checks if the given @a fileName is an absolute path. If
919 /// it is then it returns a copy of it, otherwise it prepends the current
920 /// working directory to it.
921 ///
922 /// @param fileName The filename to get the absolute path from.
923 ///
924 /// @return A copy of the absolute path to the file name. This copy must be
925 /// freed when no longer needed.
926 ///
927 gchar *
getAbsoluteFileName(const gchar * fileName)928 getAbsoluteFileName (const gchar *fileName)
929 {
930 gchar *absoluteFileName = NULL;
931 if ( g_path_is_absolute (fileName) )
932 {
933 absoluteFileName = g_strdup (fileName);
934 }
935 else
936 {
937 gchar *currentDir = g_get_current_dir ();
938 absoluteFileName = g_build_filename (currentDir, fileName, NULL);
939 g_free (currentDir);
940 }
941
942 return absoluteFileName;
943 }
944