ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git), ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
87 lines
2.6 KiB
C
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);
|
|
}
|