summaryrefslogtreecommitdiff
path: root/src/pdf_bridge.c
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-15 13:46:50 -0300
committerGabriel Schneider <[email protected]>2026-09-15 17:24:42 -0300
commit6d7b506c8a380e4dd97f337590ca079d810a360a (patch)
tree71075c8c4383df25c6ecd5140ef75063a897b444 /src/pdf_bridge.c
parent13d0d107a06d7ac5fb5f8e86c747e54603f51b4e (diff)
downloadpardes-6d7b506c8a380e4dd97f337590ca079d810a360a.tar.gz
pardes-6d7b506c8a380e4dd97f337590ca079d810a360a.zip
Follow embedded PDF links through Look
Diffstat (limited to 'src/pdf_bridge.c')
-rw-r--r--src/pdf_bridge.c121
1 files changed, 121 insertions, 0 deletions
diff --git a/src/pdf_bridge.c b/src/pdf_bridge.c
index 8e433fde..bdc7b891 100644
--- a/src/pdf_bridge.c
+++ b/src/pdf_bridge.c
@@ -27,6 +27,8 @@ struct pardes_pdf_document {
fz_stext_page *cached_text;
fz_display_list *cached_display_list;
fz_rect cached_bounds;
+ fz_link *cached_links;
+ int cached_links_loaded;
/* Promote only a page which is rendered twice without changing pages. */
int display_list_candidate_page_number;
@@ -126,12 +128,15 @@ pardes_pdf_free_document(pardes_pdf_document *document)
static void
pardes_pdf_drop_cached_page(pardes_pdf_document *document)
{
+ fz_drop_link(document->ctx, document->cached_links);
fz_drop_display_list(document->ctx, document->cached_display_list);
fz_drop_stext_page(document->ctx, document->cached_text);
fz_drop_page(document->ctx, document->cached_page);
document->cached_display_list = NULL;
document->cached_text = NULL;
document->cached_page = NULL;
+ document->cached_links = NULL;
+ document->cached_links_loaded = 0;
document->cached_page_number = -1;
document->display_list_candidate_page_number = -1;
}
@@ -994,6 +999,85 @@ pardes_pdf_point_inside_quad(pardes_pdf_point point, pardes_pdf_quad quad)
}
int
+pardes_pdf_link_at(
+ pardes_pdf_document *document,
+ int page_number,
+ pardes_pdf_point point,
+ pardes_pdf_link *out)
+{
+ fz_context *ctx;
+
+ if (out == NULL)
+ return PARDES_PDF_ERROR;
+ memset(out, 0, sizeof(*out));
+ out->page = -1;
+ if (document == NULL || page_number < 0 ||
+ page_number >= document->page_count)
+ return PARDES_PDF_ERROR;
+ if (!pardes_pdf_valid_point(point) || point.x < 0 || point.x > 1 ||
+ point.y < 0 || point.y > 1)
+ return PARDES_PDF_OK;
+
+ ctx = document->ctx;
+ fz_try(ctx)
+ {
+ fz_link *link;
+ fz_point page_point;
+ pardes_pdf_cache_page(document, page_number, 0);
+ if (!document->cached_links_loaded) {
+ document->cached_links = fz_load_links(ctx, document->cached_page);
+ document->cached_links_loaded = 1;
+ }
+ page_point = pardes_pdf_point_to_page(document, point);
+ for (link = document->cached_links; link != NULL; link = link->next) {
+ size_t len;
+ int destination_kind;
+ int destination_page = -1;
+ fz_link_dest destination = fz_make_link_dest_none();
+ if (!pardes_pdf_valid_bounds(link->rect) ||
+ !fz_is_point_inside_rect(page_point, link->rect) ||
+ link->uri == NULL || link->uri[0] == 0)
+ continue;
+ for (len = 0; len <= PARDES_PDF_MAX_LINK_URI_BYTES &&
+ link->uri[len] != 0; ++len)
+ ;
+ if (len > PARDES_PDF_MAX_LINK_URI_BYTES)
+ continue;
+ destination_kind = PARDES_PDF_OUTLINE_DESTINATION_EXTERNAL;
+ if (!fz_is_external_link(ctx, link->uri)) {
+ destination = fz_resolve_link_dest(ctx, document->doc, link->uri);
+ if (destination.loc.chapter >= 0 && destination.loc.page >= 0)
+ destination_page = fz_page_number_from_location(
+ ctx, document->doc, destination.loc);
+ if (destination_page >= 0 && destination_page < document->page_count)
+ destination_kind = PARDES_PDF_OUTLINE_DESTINATION_INTERNAL;
+ else if (link->uri[0] == '#')
+ continue;
+ }
+ out->quad = pardes_pdf_quad_from_page(
+ document, fz_quad_from_rect(link->rect));
+ out->destination_kind = destination_kind;
+ if (destination_kind == PARDES_PDF_OUTLINE_DESTINATION_INTERNAL) {
+ out->page = destination_page;
+ out->has_x = isfinite(destination.x);
+ out->has_y = isfinite(destination.y);
+ if (out->has_x) out->x = destination.x;
+ if (out->has_y) out->y = destination.y;
+ }
+ out->uri = (const unsigned char *)link->uri;
+ out->uri_len = len;
+ break;
+ }
+ }
+ fz_catch(ctx)
+ {
+ memset(out, 0, sizeof(*out));
+ return PARDES_PDF_ERROR;
+ }
+ return PARDES_PDF_OK;
+}
+
+int
pardes_pdf_copy_selection(
pardes_pdf_document *document,
int page_number,
@@ -1041,6 +1125,43 @@ pardes_pdf_drop_owned_text(pardes_pdf_document *document, void *text)
fz_free(document->ctx, text);
}
+int
+pardes_pdf_copy_rectangle(
+ pardes_pdf_document *document,
+ int page_number,
+ pardes_pdf_quad quad,
+ pardes_pdf_owned_text *out)
+{
+ fz_context *ctx;
+ char *text = NULL;
+ fz_quad native;
+ if (document == NULL || out == NULL || page_number < 0 ||
+ page_number >= document->page_count || !pardes_pdf_valid_quad(quad))
+ return PARDES_PDF_ERROR;
+ memset(out, 0, sizeof(*out));
+ ctx = document->ctx;
+ fz_var(text);
+ fz_try(ctx)
+ {
+ pardes_pdf_cache_page(document, page_number, 1);
+ native.ul = pardes_pdf_point_to_page(document, quad.ul);
+ native.ur = pardes_pdf_point_to_page(document, quad.ur);
+ native.ll = pardes_pdf_point_to_page(document, quad.ll);
+ native.lr = pardes_pdf_point_to_page(document, quad.lr);
+ text = fz_copy_rectangle(ctx, document->cached_text,
+ fz_rect_from_quad(native), 0);
+ }
+ fz_catch(ctx)
+ {
+ fz_free(ctx, text);
+ return PARDES_PDF_ERROR;
+ }
+ out->handle = text;
+ out->data = (const unsigned char *)text;
+ out->len = text == NULL ? 0 : strlen(text);
+ return PARDES_PDF_OK;
+}
+
typedef struct pardes_pdf_outline_measurement {
size_t item_count;
size_t bytes_len;