*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include <config.h>
#include "djvu-document.h"
-#include "djvu-text.h"
+#include "djvu-text-page.h"
#include "djvu-links.h"
#include "djvu-document-private.h"
-#include "ev-document-thumbnails.h"
#include "ev-file-exporter.h"
#include "ev-document-misc.h"
#include "ev-document-find.h"
#include "ev-selection.h"
#include "ev-file-helpers.h"
-#include <gdk-pixbuf/gdk-pixbuf-core.h>
-#include <glib/gi18n.h>
-#include <glib/gunicode.h>
+#include <glib.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <glib/gi18n-lib.h>
#include <string.h>
#define SCALE_FACTOR 0.2
struct _DjvuDocumentClass
{
- GObjectClass parent_class;
+ EvDocumentClass parent_class;
};
typedef struct _DjvuDocumentClass DjvuDocumentClass;
-static void djvu_document_document_iface_init (EvDocumentIface *iface);
-static void djvu_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
-static void djvu_document_file_exporter_iface_init (EvFileExporterIface *iface);
-static void djvu_document_find_iface_init (EvDocumentFindIface *iface);
-static void djvu_document_document_links_iface_init (EvDocumentLinksIface *iface);
-static void djvu_selection_iface_init (EvSelectionIface *iface);
+static void djvu_document_file_exporter_iface_init (EvFileExporterInterface *iface);
+static void djvu_document_find_iface_init (EvDocumentFindInterface *iface);
+static void djvu_document_document_links_iface_init (EvDocumentLinksInterface *iface);
+static void djvu_selection_iface_init (EvSelectionInterface *iface);
EV_BACKEND_REGISTER_WITH_CODE (DjvuDocument, djvu_document,
{
- G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, djvu_document_document_thumbnails_iface_init);
- G_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER, djvu_document_file_exporter_iface_init);
- G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND, djvu_document_find_iface_init);
- G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS, djvu_document_document_links_iface_init);
- G_IMPLEMENT_INTERFACE (EV_TYPE_SELECTION, djvu_selection_iface_init);
+ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER, djvu_document_file_exporter_iface_init);
+ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND, djvu_document_find_iface_init);
+ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS, djvu_document_document_links_iface_init);
+ EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_SELECTION, djvu_selection_iface_init);
});
-void
-djvu_handle_events (DjvuDocument *djvu_document, int wait)
+#define EV_DJVU_ERROR ev_djvu_error_quark ()
+
+static GQuark
+ev_djvu_error_quark (void)
+{
+ static GQuark q = 0;
+ if (q == 0)
+ q = g_quark_from_string ("ev-djvu-quark");
+
+ return q;
+}
+
+static void
+handle_message (const ddjvu_message_t *msg, GError **error)
+{
+ switch (msg->m_any.tag) {
+ case DDJVU_ERROR: {
+ gchar *error_str;
+
+ if (msg->m_error.filename) {
+ error_str = g_strdup_printf ("DjvuLibre error: %s:%d",
+ msg->m_error.filename,
+ msg->m_error.lineno);
+ } else {
+ error_str = g_strdup_printf ("DjvuLibre error: %s",
+ msg->m_error.message);
+ }
+
+ if (error) {
+ g_set_error_literal (error, EV_DJVU_ERROR, 0, error_str);
+ } else {
+ g_warning ("%s", error_str);
+ }
+
+ g_free (error_str);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void
+djvu_handle_events (DjvuDocument *djvu_document, int wait, GError **error)
{
ddjvu_context_t *ctx = djvu_document->d_context;
const ddjvu_message_t *msg;
return;
if (wait)
- msg = ddjvu_message_wait (ctx);
+ ddjvu_message_wait (ctx);
while ((msg = ddjvu_message_peek (ctx))) {
- switch (msg->m_any.tag) {
- case DDJVU_ERROR:
- g_warning ("DjvuLibre error: %s",
- msg->m_error.message);
- if (msg->m_error.filename)
- g_warning ("DjvuLibre error: %s:%d",
- msg->m_error.filename,
- msg->m_error.lineno);
- break;
- default:
- break;
- }
+ handle_message (msg, error);
+ ddjvu_message_pop (ctx);
+ if (error && *error)
+ return;
+ }
+}
+
+static void
+djvu_wait_for_message (DjvuDocument *djvu_document, ddjvu_message_tag_t message, GError **error)
+{
+ ddjvu_context_t *ctx = djvu_document->d_context;
+ const ddjvu_message_t *msg;
+
+ ddjvu_message_wait (ctx);
+ while ((msg = ddjvu_message_peek (ctx)) && (msg->m_any.tag != message)) {
+ handle_message (msg, error);
ddjvu_message_pop (ctx);
+ if (error && *error)
+ return;
}
+ if (msg && msg->m_any.tag == message)
+ ddjvu_message_pop (ctx);
}
static gboolean
ddjvu_document_t *doc;
gchar *filename;
gboolean missing_files = FALSE;
+ GError *djvu_error = NULL;
/* FIXME: We could actually load uris */
filename = g_filename_from_uri (uri, NULL, error);
if (!doc) {
g_free (filename);
+ g_set_error_literal (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ _("DjVu document has incorrect format"));
return FALSE;
}
djvu_document->d_document = doc;
- while (!ddjvu_document_decoding_done (djvu_document->d_document))
- djvu_handle_events (djvu_document, TRUE);
+ djvu_wait_for_message (djvu_document, DDJVU_DOCINFO, &djvu_error);
+ if (djvu_error) {
+ g_set_error_literal (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ djvu_error->message);
+ g_error_free (djvu_error);
+ g_free (filename);
+ ddjvu_document_release (djvu_document->d_document);
+ djvu_document->d_document = NULL;
+
+ return FALSE;
+ }
+
+ if (ddjvu_document_decoding_error (djvu_document->d_document))
+ djvu_handle_events (djvu_document, TRUE, &djvu_error);
+
+ if (djvu_error) {
+ g_set_error_literal (error,
+ EV_DOCUMENT_ERROR,
+ EV_DOCUMENT_ERROR_INVALID,
+ djvu_error->message);
+ g_error_free (djvu_error);
+ g_free (filename);
+ ddjvu_document_release (djvu_document->d_document);
+ djvu_document->d_document = NULL;
+
+ return FALSE;
+ }
+
g_free (djvu_document->uri);
djvu_document->uri = g_strdup (uri);
g_free (filename);
if (missing_files) {
- g_set_error (error,
- G_FILE_ERROR,
- G_FILE_ERROR_EXIST,
- _("The document is composed by several files. "
- "One or more of such files cannot be accessed."));
+ g_set_error_literal (error,
+ G_FILE_ERROR,
+ G_FILE_ERROR_EXIST,
+ _("The document is composed of several files. "
+ "One or more of these files cannot be accessed."));
return FALSE;
}
}
static void
-djvu_document_get_page_size (EvDocument *document,
- int page,
- double *width,
- double *height)
+document_get_page_size (DjvuDocument *djvu_document,
+ gint page,
+ double *width,
+ double *height)
{
- DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
- ddjvu_pageinfo_t info;
+ ddjvu_pageinfo_t info;
ddjvu_status_t r;
- g_return_if_fail (djvu_document->d_document);
-
while ((r = ddjvu_document_get_pageinfo(djvu_document->d_document, page, &info)) < DDJVU_JOB_OK)
- djvu_handle_events(djvu_document, TRUE);
+ djvu_handle_events(djvu_document, TRUE, NULL);
if (r >= DDJVU_JOB_FAILED)
- djvu_handle_events(djvu_document, TRUE);
+ djvu_handle_events(djvu_document, TRUE, NULL);
*width = info.width * SCALE_FACTOR;
*height = info.height * SCALE_FACTOR;
}
+static void
+djvu_document_get_page_size (EvDocument *document,
+ EvPage *page,
+ double *width,
+ double *height)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+
+ g_return_if_fail (djvu_document->d_document);
+
+ document_get_page_size (djvu_document, page->index,
+ width, height);
+}
+
static cairo_surface_t *
djvu_document_render (EvDocument *document,
EvRenderContext *rc)
ddjvu_page_t *d_page;
ddjvu_page_rotation_t rotation;
double page_width, page_height, tmp;
- static const cairo_user_data_key_t key;
- d_page = ddjvu_page_create_by_pageno (djvu_document->d_document, rc->page);
+ d_page = ddjvu_page_create_by_pageno (djvu_document->d_document, rc->page->index);
while (!ddjvu_page_decoding_done (d_page))
- djvu_handle_events(djvu_document, TRUE);
+ djvu_handle_events(djvu_document, TRUE, NULL);
page_width = ddjvu_page_get_width (d_page) * rc->scale * SCALE_FACTOR + 0.5;
page_height = ddjvu_page_get_height (d_page) * rc->scale * SCALE_FACTOR + 0.5;
rotation = DDJVU_ROTATE_0;
}
- rowstride = page_width * 4;
- pixels = (gchar *) g_malloc (page_height * rowstride);
- surface = cairo_image_surface_create_for_data ((guchar *)pixels,
- CAIRO_FORMAT_RGB24,
- page_width,
- page_height,
- rowstride);
- cairo_surface_set_user_data (surface, &key,
- pixels, (cairo_destroy_func_t)g_free);
+ surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ page_width, page_height);
+ rowstride = cairo_image_surface_get_stride (surface);
+ pixels = (gchar *)cairo_image_surface_get_data (surface);
+
prect.x = 0;
prect.y = 0;
prect.w = page_width;
rowstride,
pixels);
+ cairo_surface_mark_dirty (surface);
+
return surface;
}
+static GdkPixbuf *
+djvu_document_get_thumbnail (EvDocument *document,
+ EvRenderContext *rc)
+{
+ DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+ GdkPixbuf *pixbuf, *rotated_pixbuf;
+ gdouble page_width, page_height;
+ gint thumb_width, thumb_height;
+ guchar *pixels;
+
+ g_return_val_if_fail (djvu_document->d_document, NULL);
+
+ djvu_document_get_page_size (EV_DOCUMENT(djvu_document), rc->page,
+ &page_width, &page_height);
+
+ thumb_width = (gint) (page_width * rc->scale);
+ thumb_height = (gint) (page_height * rc->scale);
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
+ thumb_width, thumb_height);
+ gdk_pixbuf_fill (pixbuf, 0xffffffff);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ while (ddjvu_thumbnail_status (djvu_document->d_document, rc->page->index, 1) < DDJVU_JOB_OK)
+ djvu_handle_events(djvu_document, TRUE, NULL);
+
+ ddjvu_thumbnail_render (djvu_document->d_document, rc->page->index,
+ &thumb_width, &thumb_height,
+ djvu_document->thumbs_format,
+ gdk_pixbuf_get_rowstride (pixbuf),
+ (gchar *)pixels);
+
+ rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation);
+ g_object_unref (pixbuf);
+
+ return rotated_pixbuf;
+}
+
static void
djvu_document_finalize (GObject *object)
{
DjvuDocument *djvu_document = DJVU_DOCUMENT (object);
- if (djvu_document->search)
- djvu_text_free (djvu_document->search);
-
if (djvu_document->d_document)
ddjvu_document_release (djvu_document->d_document);
static void
djvu_document_class_init (DjvuDocumentClass *klass)
{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass);
gobject_class->finalize = djvu_document_finalize;
+
+ ev_document_class->load = djvu_document_load;
+ ev_document_class->save = djvu_document_save;
+ ev_document_class->get_n_pages = djvu_document_get_n_pages;
+ ev_document_class->get_page_size = djvu_document_get_page_size;
+ ev_document_class->render = djvu_document_render;
+ ev_document_class->get_thumbnail = djvu_document_get_thumbnail;
}
-static EvDocumentInfo *
-djvu_document_get_info (EvDocument *document)
+static gchar *
+djvu_text_copy (DjvuDocument *djvu_document,
+ gint page,
+ EvRectangle *rectangle)
{
- EvDocumentInfo *info;
+ miniexp_t page_text;
+ gchar *text = NULL;
- info = g_new0 (EvDocumentInfo, 1);
+ while ((page_text =
+ ddjvu_document_get_pagetext (djvu_document->d_document,
+ page, "char")) == miniexp_dummy)
+ djvu_handle_events (djvu_document, TRUE, NULL);
- return info;
-}
+ if (page_text != miniexp_nil) {
+ DjvuTextPage *page = djvu_text_page_new (page_text);
+
+ text = djvu_text_page_copy (page, rectangle);
+ djvu_text_page_free (page);
+ ddjvu_miniexp_release (djvu_document->d_document, page_text);
+ }
-static void
-djvu_document_document_iface_init (EvDocumentIface *iface)
-{
- iface->load = djvu_document_load;
- iface->save = djvu_document_save;
- iface->get_n_pages = djvu_document_get_n_pages;
- iface->get_page_size = djvu_document_get_page_size;
- iface->render = djvu_document_render;
- iface->get_info = djvu_document_get_info;
+ return text;
}
static gchar *
djvu_selection_get_selected_text (EvSelection *selection,
- EvRenderContext *rc,
+ EvPage *page,
EvSelectionStyle style,
EvRectangle *points)
{
gchar *text;
djvu_document_get_page_size (EV_DOCUMENT (djvu_document),
- rc->page, &width, &height);
+ page, &width, &height);
rectangle.x1 = points->x1 / SCALE_FACTOR;
rectangle.y1 = (height - points->y2) / SCALE_FACTOR;
rectangle.x2 = points->x2 / SCALE_FACTOR;
rectangle.y2 = (height - points->y1) / SCALE_FACTOR;
- text = djvu_text_copy (djvu_document, rc->page, &rectangle);
+ text = djvu_text_copy (djvu_document, page->index, &rectangle);
if (text == NULL)
text = g_strdup ("");
}
static void
-djvu_selection_iface_init (EvSelectionIface *iface)
+djvu_selection_iface_init (EvSelectionInterface *iface)
{
iface->get_selected_text = djvu_selection_get_selected_text;
}
-static void
-djvu_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
- EvRenderContext *rc,
- gint *width,
- gint *height)
-{
- DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
- gdouble page_width, page_height;
-
- djvu_document_get_page_size (EV_DOCUMENT(djvu_document), rc->page,
- &page_width, &page_height);
-
- if (rc->rotation == 90 || rc->rotation == 270) {
- *width = (gint) (page_height * rc->scale);
- *height = (gint) (page_width * rc->scale);
- } else {
- *width = (gint) (page_width * rc->scale);
- *height = (gint) (page_height * rc->scale);
- }
-}
-
-static GdkPixbuf *
-djvu_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
- EvRenderContext *rc,
- gboolean border)
-{
- DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
- GdkPixbuf *pixbuf, *rotated_pixbuf;
- gdouble page_width, page_height;
- gint thumb_width, thumb_height;
- guchar *pixels;
-
- g_return_val_if_fail (djvu_document->d_document, NULL);
-
- djvu_document_get_page_size (EV_DOCUMENT(djvu_document), rc->page,
- &page_width, &page_height);
-
- thumb_width = (gint) (page_width * rc->scale);
- thumb_height = (gint) (page_height * rc->scale);
-
- pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
- thumb_width, thumb_height);
- gdk_pixbuf_fill (pixbuf, 0xffffffff);
- pixels = gdk_pixbuf_get_pixels (pixbuf);
-
- while (ddjvu_thumbnail_status (djvu_document->d_document, rc->page, 1) < DDJVU_JOB_OK)
- djvu_handle_events(djvu_document, TRUE);
-
- ddjvu_thumbnail_render (djvu_document->d_document, rc->page,
- &thumb_width, &thumb_height,
- djvu_document->thumbs_format,
- gdk_pixbuf_get_rowstride (pixbuf),
- (gchar *)pixels);
-
- rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation);
- g_object_unref (pixbuf);
-
- if (border) {
- GdkPixbuf *tmp_pixbuf = rotated_pixbuf;
-
- rotated_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
- g_object_unref (tmp_pixbuf);
- }
-
- return rotated_pixbuf;
-}
-
-static void
-djvu_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
-{
- iface->get_thumbnail = djvu_document_thumbnails_get_thumbnail;
- iface->get_dimensions = djvu_document_thumbnails_get_dimensions;
-}
-
/* EvFileExporterIface */
static void
djvu_document_file_exporter_begin (EvFileExporter *exporter,
{
DjvuDocument *djvu_document = DJVU_DOCUMENT (exporter);
- g_string_append_printf (djvu_document->opts, "%d,", (rc->page) + 1);
+ g_string_append_printf (djvu_document->opts, "%d,", (rc->page->index) + 1);
}
static void
ddjvu_job_t * job = ddjvu_document_print(djvu_document->d_document, fn, d_optc, d_optv);
while (!ddjvu_job_done(job)) {
- djvu_handle_events (djvu_document, TRUE);
+ djvu_handle_events (djvu_document, TRUE, NULL);
}
fclose(fn);
}
static void
-djvu_document_file_exporter_iface_init (EvFileExporterIface *iface)
+djvu_document_file_exporter_iface_init (EvFileExporterInterface *iface)
{
iface->begin = djvu_document_file_exporter_begin;
iface->do_page = djvu_document_file_exporter_do_page;
djvu_document->d_document = NULL;
}
-static void
-djvu_document_find_begin (EvDocumentFind *document,
- int page,
- const char *search_string,
- gboolean case_sensitive)
+static GList *
+djvu_document_find_find_text (EvDocumentFind *document,
+ EvPage *page,
+ const char *text,
+ gboolean case_sensitive)
{
DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
+ miniexp_t page_text;
+ gdouble width, height;
+ GList *matches = NULL, *l;
- if (djvu_document->search &&
- strcmp (search_string, djvu_text_get_text (djvu_document->search)) == 0)
- return;
-
- if (djvu_document->search)
- djvu_text_free (djvu_document->search);
+ g_return_val_if_fail (text != NULL, NULL);
- djvu_document->search = djvu_text_new (djvu_document,
- page,
- case_sensitive,
- search_string);
-}
+ while ((page_text = ddjvu_document_get_pagetext (djvu_document->d_document,
+ page->index,
+ "char")) == miniexp_dummy)
+ djvu_handle_events (djvu_document, TRUE, NULL);
-static int
-djvu_document_find_get_n_results (EvDocumentFind *document_find, int page)
-{
- DjvuText *search = DJVU_DOCUMENT (document_find)->search;
-
- if (search) {
- return djvu_text_n_results (search, page);
- } else {
- return 0;
+ if (page_text != miniexp_nil) {
+ DjvuTextPage *tpage = djvu_text_page_new (page_text);
+
+ djvu_text_page_prepare_search (tpage, case_sensitive);
+ if (tpage->links->len > 0) {
+ djvu_text_page_search (tpage, text);
+ matches = tpage->results;
+ }
+ djvu_text_page_free (tpage);
+ ddjvu_miniexp_release (djvu_document->d_document, page_text);
}
-}
-static gboolean
-djvu_document_find_get_result (EvDocumentFind *document_find,
- int page,
- int n_result,
- EvRectangle *rectangle)
-{
- DjvuDocument *djvu_document = DJVU_DOCUMENT (document_find);
- DjvuText *search = djvu_document->search;
- EvRectangle *r;
- double width, height;
-
- if (search == NULL)
- return FALSE;
+ if (!matches)
+ return NULL;
- r = djvu_text_get_result (search, page, n_result);
- if (r == NULL)
- return FALSE;
+ document_get_page_size (djvu_document, page->index, &width, &height);
+ for (l = matches; l && l->data; l = g_list_next (l)) {
+ EvRectangle *r = (EvRectangle *)l->data;
+ gdouble tmp;
- djvu_document_get_page_size (EV_DOCUMENT (djvu_document),
- page, &width, &height);
- rectangle->x1 = r->x1 * SCALE_FACTOR;
- rectangle->y1 = height - r->y2 * SCALE_FACTOR;
- rectangle->x2 = r->x2 * SCALE_FACTOR;
- rectangle->y2 = height - r->y1 * SCALE_FACTOR;
+ tmp = r->y1;
- return TRUE;
-}
-
-static int
-djvu_document_find_page_has_results (EvDocumentFind *document_find,
- int page)
-{
- DjvuText *search = DJVU_DOCUMENT (document_find)->search;
-
- return search && djvu_text_has_results (search, page);
-}
+ r->x1 *= SCALE_FACTOR;
+ r->x2 *= SCALE_FACTOR;
-static double
-djvu_document_find_get_progress (EvDocumentFind *document_find)
-{
- DjvuText *search = DJVU_DOCUMENT (document_find)->search;
-
- if (search == NULL) {
- return 0;
+ tmp = r->y1;
+ r->y1 = height - r->y2 * SCALE_FACTOR;
+ r->y2 = height - tmp * SCALE_FACTOR;
}
+
- return djvu_text_get_progress (search);
-}
-
-static void
-djvu_document_find_cancel (EvDocumentFind *document)
-{
- DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
-
- if (djvu_document->search) {
- djvu_text_free (djvu_document->search);
- djvu_document->search = NULL;
- }
+ return matches;
}
static void
-djvu_document_find_iface_init (EvDocumentFindIface *iface)
+djvu_document_find_iface_init (EvDocumentFindInterface *iface)
{
- iface->begin = djvu_document_find_begin;
- iface->get_n_results = djvu_document_find_get_n_results;
- iface->get_result = djvu_document_find_get_result;
- iface->page_has_results = djvu_document_find_page_has_results;
- iface->get_progress = djvu_document_find_get_progress;
- iface->cancel = djvu_document_find_cancel;
+ iface->find_text = djvu_document_find_find_text;
}
-static GList *
+static EvMappingList *
djvu_document_links_get_links (EvDocumentLinks *document_links,
- gint page)
+ EvPage *page)
{
- return djvu_links_get_links (document_links, page, SCALE_FACTOR);
+ return djvu_links_get_links (document_links, page->index, SCALE_FACTOR);
}
static void
-djvu_document_document_links_iface_init (EvDocumentLinksIface *iface)
+djvu_document_document_links_iface_init (EvDocumentLinksInterface *iface)
{
iface->has_document_links = djvu_links_has_document_links;
iface->get_links_model = djvu_links_get_links_model;
iface->get_links = djvu_document_links_get_links;
iface->find_link_dest = djvu_links_find_link_dest;
+ iface->find_link_page = djvu_links_find_link_page;
}