#ifndef PARDES_PDF_BRIDGE_H #define PARDES_PDF_BRIDGE_H #include #ifdef __cplusplus extern "C" { #endif /* * MuPDF reports failures with setjmp/longjmp through fz_try/fz_catch. Those * jumps must never cross Zig frames, so every operation that can throw lives * behind this deliberately small C boundary. */ typedef struct pardes_pdf_document pardes_pdf_document; typedef void *(*pardes_pdf_alloc_fn)(void *user, size_t size); typedef void *(*pardes_pdf_realloc_fn)( void *user, void *pointer, size_t size ); typedef void (*pardes_pdf_free_fn)(void *user, void *pointer); typedef struct pardes_pdf_allocator { void *user; pardes_pdf_alloc_fn alloc; pardes_pdf_realloc_fn realloc; pardes_pdf_free_fn free; } pardes_pdf_allocator; /* * Install one process-global allocator, or restore libc/default MuPDF * allocation by passing NULL. Changing it while a document is live fails. */ int pardes_pdf_set_allocator(const pardes_pdf_allocator *allocator); typedef struct pardes_pdf_raster_layout { int width; int height; int stride; size_t samples_len; } pardes_pdf_raster_layout; typedef struct pardes_pdf_page_size { float width; float height; } pardes_pdf_page_size; typedef struct pardes_pdf_text { void *handle; const unsigned char *data; size_t len; } pardes_pdf_text; /* * All geometry crossing this boundary is normalized to the cached page's * bounds. (0,0) is the page's top-left and (1,1) its bottom-right, regardless * of the DPI used for a particular render. Corner names retain MuPDF's quad * orientation; callers must not collapse them to an axis-aligned rectangle. */ typedef struct pardes_pdf_point { float x; float y; } pardes_pdf_point; typedef struct pardes_pdf_quad { pardes_pdf_point ul; pardes_pdf_point ur; pardes_pdf_point ll; pardes_pdf_point lr; } pardes_pdf_quad; typedef enum pardes_pdf_highlight_kind { PARDES_PDF_HIGHLIGHT_CUSTOM = 0, PARDES_PDF_HIGHLIGHT_SEARCH = 1, PARDES_PDF_HIGHLIGHT_SELECTION = 2 } pardes_pdf_highlight_kind; typedef struct pardes_pdf_highlight { pardes_pdf_quad quad; unsigned char rgba[4]; int kind; } pardes_pdf_highlight; typedef struct pardes_pdf_search_quad { pardes_pdf_quad quad; size_t hit; } pardes_pdf_search_quad; typedef struct pardes_pdf_search_result { void *handle; const pardes_pdf_search_quad *quads; size_t quad_count; size_t hit_count; } pardes_pdf_search_result; typedef struct pardes_pdf_selection { void *handle; const pardes_pdf_quad *quads; size_t quad_count; pardes_pdf_point start; pardes_pdf_point end; } pardes_pdf_selection; typedef struct pardes_pdf_owned_text { void *handle; const unsigned char *data; size_t len; } pardes_pdf_owned_text; typedef enum pardes_pdf_outline_destination_kind { PARDES_PDF_OUTLINE_DESTINATION_NONE = 0, PARDES_PDF_OUTLINE_DESTINATION_INTERNAL = 1, PARDES_PDF_OUTLINE_DESTINATION_EXTERNAL = 2 } pardes_pdf_outline_destination_kind; /* * One pre-order depth-first row. Offsets address the result's byte arena; * title_present distinguishes a missing title from a present empty title. * Internal page numbers are zero-based. has_x/has_y preserve MuPDF's NAN * sentinel for viewing coordinates that the outline destination omits. */ typedef struct pardes_pdf_outline_item { size_t title_offset; size_t title_len; size_t uri_offset; size_t uri_len; unsigned int depth; int page; float x; float y; unsigned char title_present; unsigned char has_x; unsigned char has_y; unsigned char is_open; unsigned char flags; unsigned char r; unsigned char g; unsigned char b; int destination_kind; } pardes_pdf_outline_item; typedef struct pardes_pdf_outline_result { void *handle; const pardes_pdf_outline_item *items; size_t item_count; const unsigned char *bytes; size_t bytes_len; } pardes_pdf_outline_result; /* URI borrows the current page's lazily loaded link list. Copy it before any * other document operation. A NULL URI means the point has no usable link. */ typedef struct pardes_pdf_link { const unsigned char *uri; size_t uri_len; pardes_pdf_quad quad; int destination_kind; int page; float x; float y; unsigned char has_x; unsigned char has_y; } pardes_pdf_link; enum { PARDES_PDF_OK = 0, PARDES_PDF_ERROR = -1, PARDES_PDF_LIMIT_EXCEEDED = -2, /* Per-document MuPDF eviction cache; render buffers are separate. */ PARDES_PDF_STORE_LIMIT_BYTES = 64 * 1024 * 1024, /* * Search grows geometrically up to this aggregate count and then fails * atomically with PARDES_PDF_LIMIT_EXCEEDED; it never returns a silently * truncated result. Selection uses one extra private probe slot so an * exact-at-the-limit result remains distinguishable from truncation. */ PARDES_PDF_MAX_RESULT_QUADS = 65536, /* Outline conversion is atomic: crossing any bound returns no rows. */ PARDES_PDF_MAX_OUTLINE_ITEMS = 8192, PARDES_PDF_MAX_OUTLINE_DEPTH = 64, PARDES_PDF_MAX_OUTLINE_BYTES = 4 * 1024 * 1024, PARDES_PDF_MAX_LINK_URI_BYTES = 64 * 1024, /* No page raster is taller than this, whatever it is shown at. */ PARDES_PDF_MAX_RASTER_ROWS = 1 << 20 }; pardes_pdf_document *pardes_pdf_open(const char *path, int *page_count); pardes_pdf_document *pardes_pdf_open_memory(const unsigned char *bytes, size_t length, int *page_count); void pardes_pdf_close(pardes_pdf_document *document); /* Page dimensions in PDF points after crop/rotation, without rasterizing. */ int pardes_pdf_get_page_size( pardes_pdf_document *document, int page, pardes_pdf_page_size *out ); /* * Start at dpi, raise the uniform scale until both optional minimum pixel * dimensions are met, then clamp the page's width to max_dimension, and its * height too unless that would draw it smaller than the optional display size * (the width or height it is shown at): a tall page is then as tall as it is * shown, and its owner renders it in bands. Every function below takes the * same request and computes the same raster from it. * Measuring may populate the page cache, but never advances adaptive display * list promotion; only a successful render_into does that. */ int pardes_pdf_measure_render( pardes_pdf_document *document, int page, int dpi, int minimum_width, int minimum_height, int max_dimension, int display_width, int display_height, pardes_pdf_raster_layout *out ); /* * Rasterize one horizontal BAND of a page — rows [band_y, band_y+band_height) * of the raster `pardes_pdf_measure_render` describes — directly into an * exactly-sized caller-owned packed RGBA buffer, `stride * band_height` bytes. * A full page is the band `(0, height)`. * * MuPDF wraps but never owns or frees samples. Every successful pixel is * opaque unless transparent_paper is nonzero. That mode preserves content * coverage and returns straight-alpha RGBA over an unpainted page. * The CTM does not depend on the band, so a band is a clip of the same * rendering -- but not a bit-exact one: its edge rows and resampled images can * differ from the whole page's (see pardes_pdf_render_into). */ int pardes_pdf_render_into( pardes_pdf_document *document, int page, int dpi, int minimum_width, int minimum_height, int max_dimension, int display_width, int display_height, const pardes_pdf_highlight *highlights, size_t highlight_count, unsigned char *samples, size_t samples_len, int width, int height, int stride, int band_y, int band_height, int transparent_paper ); /* * The highlight pass of `pardes_pdf_render_into` alone, over caller-supplied * rows of that page's opaque raster: render_into(h) == paint(render_into({}), h) * row for row, because a render draws its highlights over the finished page. */ int pardes_pdf_paint_highlights( pardes_pdf_document *document, int page, int dpi, int minimum_width, int minimum_height, int max_dimension, int display_width, int display_height, const pardes_pdf_highlight *highlights, size_t highlight_count, unsigned char *samples, size_t samples_len, int width, int height, int stride, int band_y, int band_height ); /* * The raster rows [*row_start, *row_end) that painting `highlights` can touch * under the same request, in the coordinates `pardes_pdf_render_into` bands * use. An empty set (or only transparent ones) yields start == end == 0. */ int pardes_pdf_highlight_rows( pardes_pdf_document *document, int page, int dpi, int minimum_width, int minimum_height, int max_dimension, int display_width, int display_height, const pardes_pdf_highlight *highlights, size_t highlight_count, int *row_start, int *row_end ); int pardes_pdf_page_text( pardes_pdf_document *document, int page, pardes_pdf_text *out ); void pardes_pdf_drop_text(pardes_pdf_document *document, void *buffer); /* Case-insensitive, single-page search using MuPDF 1.27's iterative API. */ int pardes_pdf_search_page( pardes_pdf_document *document, int page, const char *needle, pardes_pdf_search_result *out ); void pardes_pdf_drop_search_result(pardes_pdf_document *document, void *result); /* Word-snap two normalized points and return oriented highlight quads. */ int pardes_pdf_select( pardes_pdf_document *document, int page, pardes_pdf_point start, pardes_pdf_point end, pardes_pdf_selection *out ); void pardes_pdf_drop_selection(pardes_pdf_document *document, void *selection); /* Pure/no-throw oriented-quad hit test in normalized page space. */ int pardes_pdf_point_inside_quad( pardes_pdf_point point, pardes_pdf_quad quad ); /* The input endpoints are normally the snapped points returned above. */ int pardes_pdf_copy_selection( pardes_pdf_document *document, int page, pardes_pdf_point start, pardes_pdf_point end, pardes_pdf_owned_text *out ); void pardes_pdf_drop_owned_text(pardes_pdf_document *document, void *text); /* * Flatten fz_load_outline's tree in stable document-order DFS. The borrowed * table and byte arena remain valid until drop_outline_result. */ int pardes_pdf_load_outline( pardes_pdf_document *document, pardes_pdf_outline_result *out ); void pardes_pdf_drop_outline_result( pardes_pdf_document *document, void *outline ); int pardes_pdf_link_at( pardes_pdf_document *document, int page_number, pardes_pdf_point point, pardes_pdf_link *out ); int pardes_pdf_copy_rectangle( pardes_pdf_document *document, int page_number, pardes_pdf_quad quad, pardes_pdf_owned_text *out ); #ifdef __cplusplus } #endif #endif