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