summaryrefslogtreecommitdiff
path: root/src/pdf_bridge.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pdf_bridge.c')
-rw-r--r--src/pdf_bridge.c228
1 files changed, 189 insertions, 39 deletions
diff --git a/src/pdf_bridge.c b/src/pdf_bridge.c
index 976ed838..2c723dbb 100644
--- a/src/pdf_bridge.c
+++ b/src/pdf_bridge.c
@@ -572,6 +572,55 @@ pardes_pdf_measure_render(
return PARDES_PDF_OK;
}
+/* The highlight pass of a render: fills over whatever the pixmap holds. */
+static void
+pardes_pdf_fill_highlights(fz_context *ctx, pardes_pdf_document *document,
+ fz_device *device, const pardes_pdf_highlight *highlights, size_t highlight_count)
+{
+ fz_path *path = NULL;
+ size_t i;
+
+ fz_var(path);
+ fz_try(ctx)
+ {
+ for (i = 0; i < highlight_count; ++i) {
+ fz_quad quad;
+ float color[3];
+ float alpha;
+
+ if (!pardes_pdf_valid_quad(highlights[i].quad))
+ fz_throw(ctx, FZ_ERROR_ARGUMENT,
+ "PDF highlight contains a non-finite point");
+ if (highlights[i].rgba[3] == 0)
+ continue;
+
+ /* Kind is semantic metadata; caller-supplied RGBA is final. */
+ (void)highlights[i].kind;
+ quad = pardes_pdf_quad_to_page(document, highlights[i].quad);
+ color[0] = highlights[i].rgba[0] / 255.0f;
+ color[1] = highlights[i].rgba[1] / 255.0f;
+ color[2] = highlights[i].rgba[2] / 255.0f;
+ alpha = highlights[i].rgba[3] / 255.0f;
+
+ path = fz_new_path(ctx);
+ fz_moveto(ctx, path, quad.ul.x, quad.ul.y);
+ fz_lineto(ctx, path, quad.ur.x, quad.ur.y);
+ fz_lineto(ctx, path, quad.lr.x, quad.lr.y);
+ fz_lineto(ctx, path, quad.ll.x, quad.ll.y);
+ fz_closepath(ctx, path);
+ fz_fill_path(ctx, device, path, 0, fz_identity,
+ fz_device_rgb(ctx), color, alpha,
+ fz_default_color_params);
+ fz_drop_path(ctx, path);
+ path = NULL;
+ }
+ }
+ fz_always(ctx)
+ fz_drop_path(ctx, path);
+ fz_catch(ctx)
+ fz_rethrow(ctx);
+}
+
int
pardes_pdf_render_into(
pardes_pdf_document *document,
@@ -594,10 +643,8 @@ pardes_pdf_render_into(
fz_context *ctx;
fz_pixmap *pixmap = NULL;
fz_device *device = NULL;
- fz_path *path = NULL;
fz_matrix ctm;
fz_irect bbox;
- size_t i;
if (document == NULL || page_number < 0 ||
page_number >= document->page_count || dpi < 1 ||
@@ -614,7 +661,6 @@ pardes_pdf_render_into(
ctx = document->ctx;
fz_var(pixmap);
fz_var(device);
- fz_var(path);
fz_try(ctx)
{
pardes_pdf_render_geometry(document, page_number, dpi,
@@ -627,10 +673,12 @@ pardes_pdf_render_into(
* The BAND: rows [band_y, band_y + band_height) of the page raster,
* and nothing else. A pixmap's bbox IS the draw device's clip, so the
* page runs under the same CTM it would for a full raster and MuPDF
- * discards everything outside these rows. Every row inside them is
- * therefore bit-identical to the same row of the full-page raster —
- * "band rows equal full-page rows" in pdf.zig proves it, because the
- * whole point of a band is that the reader cannot tell.
+ * discards everything outside these rows. The rows are NOT always
+ * bit-identical to the full page's: rows at the band's edges, and a
+ * resampled image anywhere in it, can differ (measured on
+ * docs/registry.pdf and docs/design.pdf). Good enough for a band that
+ * is only shown while flinging; never use a band to patch rows into a
+ * whole-page raster -- `pardes_pdf_paint_highlights` exists for that.
*/
bbox.y0 += band_y;
bbox.y1 = bbox.y0 + band_height;
@@ -674,43 +722,12 @@ pardes_pdf_render_into(
if (highlight_count != 0) {
device = fz_new_draw_device(ctx, ctm, pixmap);
- for (i = 0; i < highlight_count; ++i) {
- fz_quad quad;
- float color[3];
- float alpha;
-
- if (!pardes_pdf_valid_quad(highlights[i].quad))
- fz_throw(ctx, FZ_ERROR_ARGUMENT,
- "PDF highlight contains a non-finite point");
- if (highlights[i].rgba[3] == 0)
- continue;
-
- /* Kind is semantic metadata; caller-supplied RGBA is final. */
- (void)highlights[i].kind;
- quad = pardes_pdf_quad_to_page(document, highlights[i].quad);
- color[0] = highlights[i].rgba[0] / 255.0f;
- color[1] = highlights[i].rgba[1] / 255.0f;
- color[2] = highlights[i].rgba[2] / 255.0f;
- alpha = highlights[i].rgba[3] / 255.0f;
-
- path = fz_new_path(ctx);
- fz_moveto(ctx, path, quad.ul.x, quad.ul.y);
- fz_lineto(ctx, path, quad.ur.x, quad.ur.y);
- fz_lineto(ctx, path, quad.lr.x, quad.lr.y);
- fz_lineto(ctx, path, quad.ll.x, quad.ll.y);
- fz_closepath(ctx, path);
- fz_fill_path(ctx, device, path, 0, fz_identity,
- fz_device_rgb(ctx), color, alpha,
- fz_default_color_params);
- fz_drop_path(ctx, path);
- path = NULL;
- }
+ pardes_pdf_fill_highlights(ctx, document, device, highlights, highlight_count);
fz_close_device(ctx, device);
}
}
fz_always(ctx)
{
- fz_drop_path(ctx, path);
fz_drop_device(ctx, device);
/* Drops only the wrapper: caller-owned samples remain untouched. */
fz_drop_pixmap(ctx, pixmap);
@@ -743,6 +760,139 @@ pardes_pdf_render_into(
}
int
+pardes_pdf_paint_highlights(
+ pardes_pdf_document *document,
+ int page_number,
+ int dpi,
+ int minimum_width,
+ int minimum_height,
+ int max_dimension,
+ 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)
+{
+ fz_context *ctx;
+ fz_pixmap *pixmap = NULL;
+ fz_device *device = NULL;
+ fz_matrix ctm;
+ fz_irect bbox;
+
+ if (document == NULL || page_number < 0 ||
+ page_number >= document->page_count || dpi < 1 ||
+ minimum_width < 0 || minimum_height < 0 || max_dimension < 1 ||
+ (highlight_count != 0 && highlights == NULL) ||
+ highlight_count > PARDES_PDF_MAX_RESULT_QUADS || samples == NULL ||
+ width < 1 || height < 1 || width > INT_MAX / 4 || stride != width * 4 ||
+ band_y < 0 || band_height < 1 || band_y > height - band_height ||
+ (size_t)band_height > SIZE_MAX / (size_t)stride ||
+ samples_len != (size_t)stride * (size_t)band_height)
+ return PARDES_PDF_ERROR;
+ if (highlight_count == 0)
+ return PARDES_PDF_OK;
+
+ ctx = document->ctx;
+ fz_var(pixmap);
+ fz_var(device);
+ fz_try(ctx)
+ {
+ pardes_pdf_render_geometry(document, page_number, dpi,
+ minimum_width, minimum_height, max_dimension, &ctm, &bbox);
+ if (fz_irect_width(bbox) != width || fz_irect_height(bbox) != height)
+ fz_throw(ctx, FZ_ERROR_ARGUMENT,
+ "PDF raster layout changed between measure and paint");
+ bbox.y0 += band_y;
+ bbox.y1 = bbox.y0 + band_height;
+ pixmap = fz_new_pixmap_with_bbox_and_data(
+ ctx, fz_device_rgb(ctx), bbox, NULL, 1, samples);
+ device = fz_new_draw_device(ctx, ctm, pixmap);
+ pardes_pdf_fill_highlights(ctx, document, device, highlights, highlight_count);
+ fz_close_device(ctx, device);
+ }
+ fz_always(ctx)
+ {
+ fz_drop_device(ctx, device);
+ fz_drop_pixmap(ctx, pixmap);
+ }
+ fz_catch(ctx)
+ {
+ fz_report_error(ctx);
+ return PARDES_PDF_ERROR;
+ }
+ return PARDES_PDF_OK;
+}
+
+int
+pardes_pdf_highlight_rows(
+ pardes_pdf_document *document,
+ int page_number,
+ int dpi,
+ int minimum_width,
+ int minimum_height,
+ int max_dimension,
+ const pardes_pdf_highlight *highlights,
+ size_t highlight_count,
+ int *row_start,
+ int *row_end)
+{
+ fz_context *ctx;
+ fz_matrix ctm;
+ fz_irect bbox;
+ float y0 = 0, y1 = 0;
+ int any = 0;
+ size_t i;
+
+ if (row_start == NULL || row_end == NULL)
+ return PARDES_PDF_ERROR;
+ *row_start = *row_end = 0;
+ if (document == NULL || page_number < 0 ||
+ page_number >= document->page_count || dpi < 1 ||
+ minimum_width < 0 || minimum_height < 0 || max_dimension < 1 ||
+ (highlight_count != 0 && highlights == NULL))
+ return PARDES_PDF_ERROR;
+
+ ctx = document->ctx;
+ fz_try(ctx)
+ {
+ pardes_pdf_render_geometry(document, page_number, dpi,
+ minimum_width, minimum_height, max_dimension, &ctm, &bbox);
+ for (i = 0; i < highlight_count; ++i) {
+ fz_rect r;
+ if (highlights[i].rgba[3] == 0 ||
+ !pardes_pdf_valid_quad(highlights[i].quad))
+ continue;
+ r = fz_rect_from_quad(fz_transform_quad(
+ pardes_pdf_quad_to_page(document, highlights[i].quad), ctm));
+ if (!any || r.y0 < y0) y0 = r.y0;
+ if (!any || r.y1 > y1) y1 = r.y1;
+ any = 1;
+ }
+ }
+ fz_catch(ctx)
+ {
+ fz_report_error(ctx);
+ return PARDES_PDF_ERROR;
+ }
+ if (any) {
+ /*
+ * Antialiasing covers the pixel a fractional edge falls in, and the
+ * rasterizer paints row floor(y1) even for a zero-height quad.
+ */
+ int lo = (int)floorf(y0) - bbox.y0;
+ int hi = (int)floorf(y1) + 1 - bbox.y0;
+ int h = fz_irect_height(bbox);
+ *row_start = lo < 0 ? 0 : (lo > h ? h : lo);
+ *row_end = hi < *row_start ? *row_start : (hi > h ? h : hi);
+ }
+ return PARDES_PDF_OK;
+}
+
+int
pardes_pdf_page_text(
pardes_pdf_document *document,
int page_number,