From 5cea5eb0365ac715463c4d6fc893ed4154821e42 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Thu, 23 Dec 2004 05:12:59 +0000 Subject: [PATCH] make search find stuff on other pages, sort of (only returns one result on 2004-12-22 Havoc Pennington * pdf/xpdf/pdf-document.cc (pdf_document_find_begin): make search find stuff on other pages, sort of (only returns one result on invisible pages, to show they have results; updates full results for a page when you view it). Currently repaints the current page every time a new result is found on any page, which isn't so nice. --- ChangeLog | 8 ++ pdf/xpdf/pdf-document.cc | 293 ++++++++++++++++++++++++++++++++++----- shell/ev-view.c | 48 +++++-- 3 files changed, 299 insertions(+), 50 deletions(-) diff --git a/ChangeLog b/ChangeLog index c92ff05b..974ea21f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2004-12-22 Havoc Pennington + + * pdf/xpdf/pdf-document.cc (pdf_document_find_begin): make search + find stuff on other pages, sort of (only returns one result on + invisible pages, to show they have results; updates full results + for a page when you view it). Currently repaints the current page + every time a new result is found on any page, which isn't so nice. + 2004-12-22 Havoc Pennington * shell/ev-window.c (ev_window_cmd_edit_find): display an error if diff --git a/pdf/xpdf/pdf-document.cc b/pdf/xpdf/pdf-document.cc index efee4625..64d592ca 100644 --- a/pdf/xpdf/pdf-document.cc +++ b/pdf/xpdf/pdf-document.cc @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */ /* pdfdocument.h: Implementation of EvDocument for PDF * Copyright (C) 2004, Red Hat, Inc. * @@ -28,6 +28,21 @@ #include "PDFDoc.h" #include "PSOutputDev.h" +typedef struct +{ + PdfDocument *document; + gunichar *ucs4; + glong ucs4_len; + guint idle; + /* full results are only possible for the rendered current page */ + int current_page; + GArray *current_page_results; + guchar *other_page_flags; /* length n_pages + 1, first element ignored */ + int start_page; /* skip this one as we iterate, since we did it first */ + int search_page; /* the page we're searching now */ + TextOutputDev *output_dev; +} PdfDocumentSearch; + typedef struct _PdfDocumentClass PdfDocumentClass; #define PDF_DOCUMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PDF_TYPE_DOCUMENT, PdfDocumentClass)) @@ -54,11 +69,15 @@ struct _PdfDocument PDFDoc *doc; gboolean page_valid; + + PdfDocumentSearch *search; }; static void pdf_document_document_iface_init (EvDocumentIface *iface); static void pdf_document_ps_exporter_iface_init (EvPSExporterIface *iface); static void pdf_document_find_iface_init (EvDocumentFindIface *iface); +static void pdf_document_search_free (PdfDocumentSearch *search); +static void pdf_document_search_page_changed (PdfDocumentSearch *search); G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT, { @@ -80,6 +99,12 @@ document_validate_page (PdfDocument *pdf_document) 0, gTrue, gTrue); pdf_document->page_valid = TRUE; + + /* Update the search results available to the app since + * we only provide full results on the current page + */ + if (pdf_document->search) + pdf_document_search_page_changed (pdf_document->search); } } @@ -285,38 +310,86 @@ pdf_document_render (EvDocument *document, } static void -pdf_document_find_begin (EvDocumentFind *document, - const char *search_string, - gboolean case_sensitive) +pdf_document_search_emit_found (PdfDocumentSearch *search) { - /* FIXME make this incremental (idle handler) and multi-page */ - /* Right now it's fully synchronous plus only does the current page */ - - PdfDocument *pdf_document = PDF_DOCUMENT (document); - gunichar *ucs4; - glong ucs4_len; - int xMin, yMin, xMax, yMax; - GArray *results; - EvFindResult result; + PdfDocument *pdf_document = search->document; + int n_pages; + double pages_done; + GArray *tmp_results; + int i; + + n_pages = ev_document_get_n_pages (EV_DOCUMENT (search->document)); + if (search->search_page > search->start_page) { + pages_done = search->search_page - search->start_page; + } else { + pages_done = n_pages - search->start_page + search->search_page; + } - /* FIXME case_sensitive (right now XPDF - * code is always case insensitive for ASCII - * and case sensitive for all other languaages) + tmp_results = g_array_new (FALSE, FALSE, sizeof (EvFindResult)); + g_array_append_vals (tmp_results, + search->current_page_results->data, + search->current_page_results->len); + + /* Now append a bogus element for each page that has a result in it, + * that is not the current page */ + i = 1; + while (i <= n_pages) { + if (i != pdf_document->page && + search->other_page_flags[i]) { + EvFindResult result; + + result.page_num = i; + + /* Use bogus coordinates, again we can't get coordinates + * until this is the current page because TextOutputDev + * isn't good enough + */ + result.highlight_area.x = -1; + result.highlight_area.y = -1; + result.highlight_area.width = 1; + result.highlight_area.height = 1; + + g_array_append_val (tmp_results, result); + } + + ++i; + } - g_assert (sizeof (gunichar) == sizeof (Unicode)); - ucs4 = g_utf8_to_ucs4_fast (search_string, -1, - &ucs4_len); + ev_document_find_found (EV_DOCUMENT_FIND (pdf_document), + (EvFindResult*) tmp_results->data, + tmp_results->len, + pages_done / (double) n_pages); + + g_array_free (tmp_results, TRUE); +} + +static void +pdf_document_search_page_changed (PdfDocumentSearch *search) +{ + PdfDocument *pdf_document = search->document; + int current_page; + EvFindResult result; + int xMin, yMin, xMax, yMax; - results = g_array_new (FALSE, - FALSE, - sizeof (EvFindResult)); + current_page = pdf_document->page; - if (pdf_document->out->findText (ucs4, ucs4_len, + if (!pdf_document->page_valid) { + /* we can't do anything until displayPage() */ + search->current_page = -1; + return; + } + + if (search->current_page == current_page) + return; + + /* We need to create current_page_results for the new current page */ + g_array_set_size (search->current_page_results, 0); + + if (pdf_document->out->findText (search->ucs4, search->ucs4_len, gTrue, gTrue, // startAtTop, stopAtBottom gFalse, gFalse, // startAtLast, stopAtLast &xMin, &yMin, &xMax, &yMax)) { - result.page_num = pdf_document->page; result.highlight_area.x = xMin; @@ -324,11 +397,11 @@ pdf_document_find_begin (EvDocumentFind *document, result.highlight_area.width = xMax - xMin; result.highlight_area.height = yMax - yMin; - g_array_append_val (results, result); + g_array_append_val (search->current_page_results, result); /* Now find further results */ - while (pdf_document->out->findText (ucs4, ucs4_len, + while (pdf_document->out->findText (search->ucs4, search->ucs4_len, gFalse, gTrue, gTrue, gFalse, &xMin, &yMin, &xMax, &yMax)) { @@ -340,26 +413,173 @@ pdf_document_find_begin (EvDocumentFind *document, result.highlight_area.width = xMax - xMin; result.highlight_area.height = yMax - yMin; - g_array_append_val (results, result); + g_array_append_val (search->current_page_results, result); + } + } + + /* needed for the initial current page since we don't search + * it in the idle + */ + search->other_page_flags[current_page] = + search->current_page_results->len > 0; + + pdf_document_search_emit_found (search); +} + +static gboolean +pdf_document_search_idle_callback (void *data) +{ + PdfDocumentSearch *search = (PdfDocumentSearch*) data; + PdfDocument *pdf_document = search->document; + int n_pages; + double xMin, yMin, xMax, yMax; + gboolean found; + + /* Note that PDF page count is 1 through n_pages INCLUSIVE + * like a real book. We are looking to add one result for each + * page with a match, because the coordinates are meaningless + * with TextOutputDev, so we just want to flag matching pages + * and then when the user switches to the current page, we + * will emit "found" again with the real results. + */ + n_pages = ev_document_get_n_pages (EV_DOCUMENT (search->document)); + + if (search->search_page == search->start_page) { + goto end_search; + } + + if (search->output_dev == 0) { + /* First time through here... */ + search->output_dev = new TextOutputDev (NULL, gTrue, gFalse, gFalse); + if (!search->output_dev->isOk()) { + goto end_search; } } + + pdf_document->doc->displayPage (search->output_dev, + search->search_page, + 72, 72, 0, gTrue, gFalse); + + if (search->output_dev->findText (search->ucs4, + search->ucs4_len, + gTrue, gTrue, // startAtTop, stopAtBottom + gFalse, gFalse, // startAtLast, stopAtLast + &xMin, &yMin, &xMax, &yMax)) { + /* This page has results */ + search->other_page_flags[search->search_page] = TRUE; + + pdf_document_search_emit_found (search); + } - ev_document_find_found (document, - (EvFindResult*) results->data, - results->len, - 1.0); + search->search_page += 1; + if (search->search_page > n_pages) { + /* wrap around */ + search->search_page = 1; + } + + return TRUE; - g_array_free (results, TRUE); + end_search: + /* We're done. */ + search->idle = 0; /* will return FALSE to remove */ + return FALSE; } static void -pdf_document_find_cancel (EvDocumentFind *document) +pdf_document_find_begin (EvDocumentFind *document, + const char *search_string, + gboolean case_sensitive) { PdfDocument *pdf_document = PDF_DOCUMENT (document); + PdfDocumentSearch *search; + int n_pages; + gunichar *ucs4; + glong ucs4_len; - /* FIXME this will do something once begin_find queues - * an incremental find + /* FIXME handle case_sensitive (right now XPDF + * code is always case insensitive for ASCII + * and case sensitive for all other languaages) */ + + g_assert (sizeof (gunichar) == sizeof (Unicode)); + ucs4 = g_utf8_to_ucs4_fast (search_string, -1, + &ucs4_len); + + if (pdf_document->search && + pdf_document->search->ucs4_len == ucs4_len && + memcmp (pdf_document->search->ucs4, + ucs4, + sizeof (gunichar) * ucs4_len) == 0) { + /* Search is unchanged */ + g_free (ucs4); + return; + } + + if (pdf_document->search) { + pdf_document_search_free (pdf_document->search); + pdf_document->search = NULL; + } + + search = g_new0 (PdfDocumentSearch, 1); + + search->ucs4 = ucs4; + search->ucs4_len = ucs4_len; + + search->current_page_results = g_array_new (FALSE, + FALSE, + sizeof (EvFindResult)); + n_pages = ev_document_get_n_pages (EV_DOCUMENT (document)); + + /* This is an array of bool; with the first value ignored + * so we can index by the based-at-1 page numbers + */ + search->other_page_flags = g_new0 (guchar, n_pages + 1); + + search->document = pdf_document; + + search->idle = g_idle_add (pdf_document_search_idle_callback, + search); + + search->output_dev = 0; + + search->start_page = pdf_document->page; + search->search_page = search->start_page + 1; + if (search->search_page > n_pages) + search->search_page = 1; + + search->current_page = -1; + + pdf_document->search = search; + + /* Update for the current page right away */ + pdf_document_search_page_changed (search); +} + +static void +pdf_document_find_cancel (EvDocumentFind *document) +{ + PdfDocument *pdf_document = PDF_DOCUMENT (document); + + if (pdf_document->search) { + pdf_document_search_free (pdf_document->search); + pdf_document->search = NULL; + } +} + +static void +pdf_document_search_free (PdfDocumentSearch *search) +{ + if (search->idle != 0) + g_source_remove (search->idle); + + if (search->output_dev) + delete search->output_dev; + + g_array_free (search->current_page_results, TRUE); + g_free (search->other_page_flags); + + g_free (search->ucs4); + g_free (search); } static void @@ -399,6 +619,9 @@ pdf_document_finalize (GObject *object) { PdfDocument *pdf_document = PDF_DOCUMENT (object); + if (pdf_document->search) + pdf_document_search_free (pdf_document->search); + if (pdf_document->target) g_object_unref (pdf_document->target); diff --git a/shell/ev-view.c b/shell/ev-view.c index 8f8f068f..d0950936 100644 --- a/shell/ev-view.c +++ b/shell/ev-view.c @@ -295,32 +295,36 @@ expose_bin_window (GtkWidget *widget, { EvView *view = EV_VIEW (widget); int i; + int current_page; const EvFindResult *results; - if (view->document) - ev_document_render (view->document, - event->area.x, event->area.y, - event->area.width, event->area.height); + if (view->document == NULL) + return; + + ev_document_render (view->document, + event->area.x, event->area.y, + event->area.width, event->area.height); results = (EvFindResult*) view->find_results->data; + current_page = ev_document_get_page (view->document); i = 0; while (i < view->find_results->len) { #if 0 - g_printerr ("highlighting result %d at %d,%d %dx%d\n", - i, + g_printerr ("highlighting result %d page %d at %d,%d %dx%d\n", + i, results[i].page_num, results[i].highlight_area.x, results[i].highlight_area.y, results[i].highlight_area.width, results[i].highlight_area.height); -#endif - // if (results[i].page_num == current_page) FIXME - gdk_draw_rectangle (view->bin_window, - widget->style->base_gc[GTK_STATE_SELECTED], - FALSE, - results[i].highlight_area.x, - results[i].highlight_area.y, - results[i].highlight_area.width, - results[i].highlight_area.height); +#endif + if (results[i].page_num == current_page) + gdk_draw_rectangle (view->bin_window, + widget->style->base_gc[GTK_STATE_SELECTED], + FALSE, + results[i].highlight_area.x, + results[i].highlight_area.y, + results[i].highlight_area.width, + results[i].highlight_area.height); ++i; } } @@ -485,6 +489,20 @@ found_results_callback (EvDocument *document, if (n_results > 0) g_array_append_vals (view->find_results, results, n_results); + +#if 0 + { + int i; + + g_printerr ("%d results: ", n_results); + i = 0; + while (i < n_results) { + g_printerr ("%d ", results[i].page_num); + ++i; + } + g_printerr ("\n"); + } +#endif gtk_widget_queue_draw (GTK_WIDGET (view)); } -- 2.43.5