1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /* pdfdocument.h: Implementation of EvDocument for PDF
3 * Copyright (C) 2004, Red Hat, Inc.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #include "gpdf-g-switch.h"
21 #include "pdf-document.h"
22 #include "ev-ps-exporter.h"
23 #include "ev-document-find.h"
24 #include "gpdf-g-switch.h"
26 #include "GlobalParams.h"
27 #include "GDKSplashOutputDev.h"
29 #include "PSOutputDev.h"
33 PdfDocument *document;
37 /* full results are only possible for the rendered current page */
39 GArray *current_page_results;
40 guchar *other_page_flags; /* length n_pages + 1, first element ignored */
41 int start_page; /* skip this one as we iterate, since we did it first */
42 int search_page; /* the page we're searching now */
43 TextOutputDev *output_dev;
46 typedef struct _PdfDocumentClass PdfDocumentClass;
48 #define PDF_DOCUMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PDF_TYPE_DOCUMENT, PdfDocumentClass))
49 #define PDF_IS_DOCUMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PDF_TYPE_DOCUMENT))
50 #define PDF_DOCUMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PDF_TYPE_DOCUMENT, PdfDocumentClass))
52 struct _PdfDocumentClass
54 GObjectClass parent_class;
59 GObject parent_instance;
67 GDKSplashOutputDev *out;
73 PdfDocumentSearch *search;
76 static void pdf_document_document_iface_init (EvDocumentIface *iface);
77 static void pdf_document_ps_exporter_iface_init (EvPSExporterIface *iface);
78 static void pdf_document_find_iface_init (EvDocumentFindIface *iface);
79 static void pdf_document_search_free (PdfDocumentSearch *search);
80 static void pdf_document_search_page_changed (PdfDocumentSearch *search);
82 G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT,
84 G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
85 pdf_document_document_iface_init);
86 G_IMPLEMENT_INTERFACE (EV_TYPE_PS_EXPORTER,
87 pdf_document_ps_exporter_iface_init);
88 G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND,
89 pdf_document_find_iface_init);
93 document_validate_page (PdfDocument *pdf_document)
95 if (!pdf_document->page_valid) {
96 pdf_document->doc->displayPage (pdf_document->out, pdf_document->page,
97 72 * pdf_document->scale,
98 72 * pdf_document->scale,
101 pdf_document->page_valid = TRUE;
103 /* Update the search results available to the app since
104 * we only provide full results on the current page
106 if (pdf_document->search)
107 pdf_document_search_page_changed (pdf_document->search);
112 pdf_document_load (EvDocument *document,
116 PdfDocument *pdf_document = PDF_DOCUMENT (document);
123 globalParams = new GlobalParams("/etc/xpdfrc");
124 globalParams->setupBaseFontsFc(NULL);
127 filename = g_filename_from_uri (uri, NULL, error);
131 filename_g = new GString (filename);
134 // open the PDF file, assumes ownership of filename_g
135 newDoc = new PDFDoc(filename_g, 0, 0);
137 if (!newDoc->isOk()) {
138 err = newDoc->getErrorCode();
141 /* FIXME: Add a real error enum to EvDocument */
142 g_set_error (error, G_FILE_ERROR,
144 "Failed to load document (error %d) '%s'\n",
151 if (pdf_document->doc)
152 delete pdf_document->doc;
153 pdf_document->doc = newDoc;
155 pdf_document->page = 1;
157 if (pdf_document->out)
158 pdf_document->out->startDoc(pdf_document->doc->getXRef());
160 pdf_document->page_valid = FALSE;
166 pdf_document_get_n_pages (EvDocument *document)
168 PdfDocument *pdf_document = PDF_DOCUMENT (document);
170 if (pdf_document->doc)
171 return pdf_document->doc->getNumPages();
177 pdf_document_set_page (EvDocument *document,
180 PdfDocument *pdf_document = PDF_DOCUMENT (document);
182 page = CLAMP (page, 1, ev_document_get_n_pages (document));
184 if (page != pdf_document->page) {
185 pdf_document->page = page;
186 pdf_document->page_valid = FALSE;
192 pdf_document_get_page (EvDocument *document)
194 PdfDocument *pdf_document = PDF_DOCUMENT (document);
196 return pdf_document->page;
200 redraw_callback (void *data)
202 /* Need to hook up through a EvDocument callback? */
206 pdf_document_set_target (EvDocument *document,
209 PdfDocument *pdf_document = PDF_DOCUMENT (document);
211 if (pdf_document->target != target) {
212 if (pdf_document->target)
213 g_object_unref (pdf_document->target);
215 pdf_document->target = target;
217 if (pdf_document->target)
218 g_object_ref (pdf_document->target);
220 if (pdf_document->out) {
221 delete pdf_document->out;
222 pdf_document->out = NULL;
225 if (pdf_document->target) {
226 pdf_document->out = new GDKSplashOutputDev (gdk_drawable_get_screen (pdf_document->target),
227 redraw_callback, (void*) document);
229 if (pdf_document->doc)
230 pdf_document->out->startDoc(pdf_document->doc->getXRef());
234 pdf_document->page_valid = FALSE;
239 pdf_document_set_scale (EvDocument *document,
242 PdfDocument *pdf_document = PDF_DOCUMENT (document);
244 if (pdf_document->scale != scale) {
245 pdf_document->scale = scale;
246 pdf_document->page_valid = FALSE;
251 pdf_document_set_page_offset (EvDocument *document,
255 PdfDocument *pdf_document = PDF_DOCUMENT (document);
257 pdf_document->page_x_offset = x;
258 pdf_document->page_y_offset = y;
262 pdf_document_get_page_size (EvDocument *document,
266 PdfDocument *pdf_document = PDF_DOCUMENT (document);
268 if (document_validate_page (pdf_document)) {
270 *width = pdf_document->out->getBitmapWidth();
272 *height = pdf_document->out->getBitmapHeight();
282 pdf_document_render (EvDocument *document,
288 PdfDocument *pdf_document = PDF_DOCUMENT (document);
292 if (!document_validate_page (pdf_document) || !pdf_document->target)
295 page.x = pdf_document->page_x_offset;
296 page.y = pdf_document->page_y_offset;
297 page.width = pdf_document->out->getBitmapWidth();
298 page.height = pdf_document->out->getBitmapHeight();
302 draw.width = clip_width;
303 draw.height = clip_height;
305 if (gdk_rectangle_intersect (&page, &draw, &draw))
306 pdf_document->out->redraw (draw.x - page.x, draw.y - page.y,
307 pdf_document->target,
309 draw.width, draw.height);
313 pdf_document_search_emit_found (PdfDocumentSearch *search)
315 PdfDocument *pdf_document = search->document;
321 n_pages = ev_document_get_n_pages (EV_DOCUMENT (search->document));
322 if (search->search_page > search->start_page) {
323 pages_done = search->search_page - search->start_page;
325 pages_done = n_pages - search->start_page + search->search_page;
328 tmp_results = g_array_new (FALSE, FALSE, sizeof (EvFindResult));
329 g_array_append_vals (tmp_results,
330 search->current_page_results->data,
331 search->current_page_results->len);
333 /* Now append a bogus element for each page that has a result in it,
334 * that is not the current page
337 while (i <= n_pages) {
338 if (i != pdf_document->page &&
339 search->other_page_flags[i]) {
344 /* Use bogus coordinates, again we can't get coordinates
345 * until this is the current page because TextOutputDev
348 result.highlight_area.x = -1;
349 result.highlight_area.y = -1;
350 result.highlight_area.width = 1;
351 result.highlight_area.height = 1;
353 g_array_append_val (tmp_results, result);
359 ev_document_find_found (EV_DOCUMENT_FIND (pdf_document),
360 (EvFindResult*) tmp_results->data,
362 pages_done / (double) n_pages);
364 g_array_free (tmp_results, TRUE);
368 pdf_document_search_page_changed (PdfDocumentSearch *search)
370 PdfDocument *pdf_document = search->document;
373 int xMin, yMin, xMax, yMax;
375 current_page = pdf_document->page;
377 if (!pdf_document->page_valid) {
378 /* we can't do anything until displayPage() */
379 search->current_page = -1;
383 if (search->current_page == current_page)
386 /* We need to create current_page_results for the new current page */
387 g_array_set_size (search->current_page_results, 0);
389 if (pdf_document->out->findText (search->ucs4, search->ucs4_len,
390 gTrue, gTrue, // startAtTop, stopAtBottom
391 gFalse, gFalse, // startAtLast, stopAtLast
392 &xMin, &yMin, &xMax, &yMax)) {
393 result.page_num = pdf_document->page;
395 result.highlight_area.x = xMin;
396 result.highlight_area.y = yMin;
397 result.highlight_area.width = xMax - xMin;
398 result.highlight_area.height = yMax - yMin;
400 g_array_append_val (search->current_page_results, result);
402 /* Now find further results */
404 while (pdf_document->out->findText (search->ucs4, search->ucs4_len,
407 &xMin, &yMin, &xMax, &yMax)) {
409 result.page_num = pdf_document->page;
411 result.highlight_area.x = xMin;
412 result.highlight_area.y = yMin;
413 result.highlight_area.width = xMax - xMin;
414 result.highlight_area.height = yMax - yMin;
416 g_array_append_val (search->current_page_results, result);
420 /* needed for the initial current page since we don't search
423 search->other_page_flags[current_page] =
424 search->current_page_results->len > 0;
426 pdf_document_search_emit_found (search);
430 pdf_document_search_idle_callback (void *data)
432 PdfDocumentSearch *search = (PdfDocumentSearch*) data;
433 PdfDocument *pdf_document = search->document;
435 double xMin, yMin, xMax, yMax;
438 /* Note that PDF page count is 1 through n_pages INCLUSIVE
439 * like a real book. We are looking to add one result for each
440 * page with a match, because the coordinates are meaningless
441 * with TextOutputDev, so we just want to flag matching pages
442 * and then when the user switches to the current page, we
443 * will emit "found" again with the real results.
445 n_pages = ev_document_get_n_pages (EV_DOCUMENT (search->document));
447 if (search->search_page == search->start_page) {
451 if (search->output_dev == 0) {
452 /* First time through here... */
453 search->output_dev = new TextOutputDev (NULL, gTrue, gFalse, gFalse);
454 if (!search->output_dev->isOk()) {
459 pdf_document->doc->displayPage (search->output_dev,
461 72, 72, 0, gTrue, gFalse);
463 if (search->output_dev->findText (search->ucs4,
465 gTrue, gTrue, // startAtTop, stopAtBottom
466 gFalse, gFalse, // startAtLast, stopAtLast
467 &xMin, &yMin, &xMax, &yMax)) {
468 /* This page has results */
469 search->other_page_flags[search->search_page] = TRUE;
471 pdf_document_search_emit_found (search);
474 search->search_page += 1;
475 if (search->search_page > n_pages) {
477 search->search_page = 1;
484 search->idle = 0; /* will return FALSE to remove */
489 pdf_document_find_begin (EvDocumentFind *document,
490 const char *search_string,
491 gboolean case_sensitive)
493 PdfDocument *pdf_document = PDF_DOCUMENT (document);
494 PdfDocumentSearch *search;
499 /* FIXME handle case_sensitive (right now XPDF
500 * code is always case insensitive for ASCII
501 * and case sensitive for all other languaages)
504 g_assert (sizeof (gunichar) == sizeof (Unicode));
505 ucs4 = g_utf8_to_ucs4_fast (search_string, -1,
508 if (pdf_document->search &&
509 pdf_document->search->ucs4_len == ucs4_len &&
510 memcmp (pdf_document->search->ucs4,
512 sizeof (gunichar) * ucs4_len) == 0) {
513 /* Search is unchanged */
518 if (pdf_document->search) {
519 pdf_document_search_free (pdf_document->search);
520 pdf_document->search = NULL;
523 search = g_new0 (PdfDocumentSearch, 1);
526 search->ucs4_len = ucs4_len;
528 search->current_page_results = g_array_new (FALSE,
530 sizeof (EvFindResult));
531 n_pages = ev_document_get_n_pages (EV_DOCUMENT (document));
533 /* This is an array of bool; with the first value ignored
534 * so we can index by the based-at-1 page numbers
536 search->other_page_flags = g_new0 (guchar, n_pages + 1);
538 search->document = pdf_document;
540 search->idle = g_idle_add (pdf_document_search_idle_callback,
543 search->output_dev = 0;
545 search->start_page = pdf_document->page;
546 search->search_page = search->start_page + 1;
547 if (search->search_page > n_pages)
548 search->search_page = 1;
550 search->current_page = -1;
552 pdf_document->search = search;
554 /* Update for the current page right away */
555 pdf_document_search_page_changed (search);
559 pdf_document_find_cancel (EvDocumentFind *document)
561 PdfDocument *pdf_document = PDF_DOCUMENT (document);
563 if (pdf_document->search) {
564 pdf_document_search_free (pdf_document->search);
565 pdf_document->search = NULL;
570 pdf_document_search_free (PdfDocumentSearch *search)
572 if (search->idle != 0)
573 g_source_remove (search->idle);
575 if (search->output_dev)
576 delete search->output_dev;
578 g_array_free (search->current_page_results, TRUE);
579 g_free (search->other_page_flags);
581 g_free (search->ucs4);
586 pdf_document_ps_export_begin (EvPSExporter *exporter, const char *filename)
588 PdfDocument *document = PDF_DOCUMENT (exporter);
590 if (document->ps_out)
591 delete document->ps_out;
593 document->ps_out = new PSOutputDev ((char *)filename, document->doc->getXRef(),
594 document->doc->getCatalog(), 1,
595 ev_document_get_n_pages (EV_DOCUMENT (document)),
600 pdf_document_ps_export_do_page (EvPSExporter *exporter, int page)
602 PdfDocument *document = PDF_DOCUMENT (exporter);
604 document->doc->displayPage (document->ps_out, page,
605 72.0, 72.0, 0, gTrue, gFalse);
609 pdf_document_ps_export_end (EvPSExporter *exporter)
611 PdfDocument *document = PDF_DOCUMENT (exporter);
613 delete document->ps_out;
614 document->ps_out = NULL;
618 pdf_document_finalize (GObject *object)
620 PdfDocument *pdf_document = PDF_DOCUMENT (object);
622 if (pdf_document->search)
623 pdf_document_search_free (pdf_document->search);
625 if (pdf_document->target)
626 g_object_unref (pdf_document->target);
628 if (pdf_document->out)
629 delete pdf_document->out;
630 if (pdf_document->ps_out)
631 delete pdf_document->ps_out;
632 if (pdf_document->doc)
633 delete pdf_document->doc;
638 pdf_document_class_init (PdfDocumentClass *klass)
640 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
642 gobject_class->finalize = pdf_document_finalize;
646 pdf_document_document_iface_init (EvDocumentIface *iface)
648 iface->load = pdf_document_load;
649 iface->get_n_pages = pdf_document_get_n_pages;
650 iface->set_page = pdf_document_set_page;
651 iface->get_page = pdf_document_get_page;
652 iface->set_scale = pdf_document_set_scale;
653 iface->set_target = pdf_document_set_target;
654 iface->set_page_offset = pdf_document_set_page_offset;
655 iface->get_page_size = pdf_document_get_page_size;
656 iface->render = pdf_document_render;
660 pdf_document_ps_exporter_iface_init (EvPSExporterIface *iface)
662 iface->begin = pdf_document_ps_export_begin;
663 iface->do_page = pdf_document_ps_export_do_page;
664 iface->end = pdf_document_ps_export_end;
669 pdf_document_find_iface_init (EvDocumentFindIface *iface)
671 iface->begin = pdf_document_find_begin;
672 iface->cancel = pdf_document_find_cancel;
676 pdf_document_init (PdfDocument *pdf_document)
678 pdf_document->page = 1;
679 pdf_document->page_x_offset = 0;
680 pdf_document->page_y_offset = 0;
681 pdf_document->scale = 1.;
683 pdf_document->page_valid = FALSE;