Files
ElyOffice/crates/ely-pdf-engine/shim/ely_alt.c
T
ZacharyZhang-NY b7fca0146d Ely Office: six offline-first desktop apps in Rust and GPUI
ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git),
ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
2026-10-08 21:20:08 -04:00

87 lines
2.6 KiB
C

/* Alternate text dropped from redacted pages (PDF-09). */
#include "ely_shim.h"
typedef void (bdc_fn)(fz_context *ctx, pdf_processor *proc, const char *tag, pdf_obj *raw, pdf_obj *cooked);
static void drop_said(fz_context *ctx, pdf_obj *dict)
{
if (!pdf_is_dict(ctx, dict))
return;
pdf_dict_del(ctx, dict, PDF_NAME(ActualText));
pdf_dict_del(ctx, dict, PDF_NAME(Alt));
pdf_dict_del(ctx, dict, PDF_NAME(E));
}
static void quiet_bdc(fz_context *ctx, pdf_processor *proc, const char *tag, pdf_obj *raw, pdf_obj *cooked)
{
bdc_fn **out = fz_user_context(ctx);
drop_said(ctx, raw);
drop_said(ctx, cooked);
(*out)(ctx, proc, tag, raw, cooked);
}
/* Each output stream writes marked content through `quiet_bdc`. */
static pdf_processor *quiet_factory(fz_context *ctx, pdf_document *doc, pdf_processor *chain, int struct_parents,
fz_matrix transform, pdf_filter_options *options, void *sopts)
{
bdc_fn **out = fz_user_context(ctx);
if (chain->op_BDC != quiet_bdc)
{
*out = chain->op_BDC;
chain->op_BDC = quiet_bdc;
}
return pdf_new_sanitize_filter(ctx, doc, chain, struct_parents, transform, options, sopts);
}
/* Strips elements on `page`, by own, inherited or kids' `Pg`. */
static int quiet_elem(fz_context *ctx, pdf_obj *elem, int page, int inherited)
{
pdf_obj *kids;
int pg, i, on = 0;
if (!pdf_is_dict(ctx, elem) || pdf_mark_obj(ctx, elem))
return 0;
fz_try(ctx)
{
pg = pdf_dict_gets(ctx, elem, "Pg") ? pdf_to_num(ctx, pdf_dict_gets(ctx, elem, "Pg")) : inherited;
on = pg == page;
kids = pdf_dict_get(ctx, elem, PDF_NAME(K));
if (pdf_is_array(ctx, kids))
for (i = 0; i < pdf_array_len(ctx, kids); i++)
on |= quiet_elem(ctx, pdf_array_get(ctx, kids, i), page, pg);
else
on |= quiet_elem(ctx, kids, page, pg);
if (on)
{
drop_said(ctx, elem);
pdf_dict_del(ctx, elem, PDF_NAME(T));
}
}
fz_always(ctx)
pdf_unmark_obj(ctx, elem);
fz_catch(ctx)
fz_rethrow(ctx);
return on;
}
/* Drops what marked content says on `page`, forms too. */
void ely_drop_alt(fz_context *ctx, pdf_document *pdf, pdf_page *page)
{
pdf_sanitize_filter_options sopts = { 0 };
pdf_filter_factory list[2] = { { quiet_factory, NULL }, { NULL, NULL } };
pdf_filter_options opts = { 0 };
void *before = fz_user_context(ctx);
bdc_fn *out = NULL;
list[0].options = &sopts;
opts.recurse = 1;
opts.filters = list;
fz_set_user_context(ctx, &out);
fz_try(ctx)
pdf_filter_page_contents(ctx, pdf, page, &opts);
fz_always(ctx)
fz_set_user_context(ctx, before);
fz_catch(ctx)
fz_rethrow(ctx);
quiet_elem(ctx, pdf_dict_getp(ctx, pdf_trailer(ctx, pdf), "Root/StructTreeRoot"), pdf_to_num(ctx, page->obj), 0);
}