From da50c6f300e565c8eedf4ce252c44b5ddbb95cee Mon Sep 17 00:00:00 2001 From: Robin Watts Date: Wed, 9 Nov 2016 17:41:43 +0000 Subject: Add pdf_layer configuration API. Add API to: * allow enumeration of layer configs (OCCDs) within PDF files. * allow selection of layer configs. * allow enumeration of the "UI" (or "Human readable") form of layer configs. * allow selection/toggling of entries in the UI. --- include/mupdf/pdf/document.h | 125 ++++++- include/mupdf/pdf/interpret.h | 4 +- include/mupdf/pdf/page.h | 7 +- platform/win32/libmupdf.vcproj | 8 + resources/pdf/names.txt | 3 + source/pdf/pdf-imp.h | 9 + source/pdf/pdf-interpret.c | 226 +------------ source/pdf/pdf-layer.c | 715 +++++++++++++++++++++++++++++++++++++++++ source/pdf/pdf-op-run.c | 4 +- source/pdf/pdf-run.c | 20 +- source/pdf/pdf-xref.c | 161 +--------- 11 files changed, 879 insertions(+), 403 deletions(-) create mode 100644 source/pdf/pdf-imp.h create mode 100644 source/pdf/pdf-layer.c diff --git a/include/mupdf/pdf/document.h b/include/mupdf/pdf/document.h index 5b9632ff..4cb4c819 100644 --- a/include/mupdf/pdf/document.h +++ b/include/mupdf/pdf/document.h @@ -119,20 +119,125 @@ int pdf_lookup_metadata(fz_context *ctx, pdf_document *doc, const char *key, cha fz_outline *pdf_load_outline(fz_context *ctx, pdf_document *doc); -typedef struct pdf_ocg_entry_s pdf_ocg_entry; +/* + pdf_count_layer_configs: Get the number of layer + configurations defined in this document. + + doc: The document in question. +*/ +int pdf_count_layer_configs(fz_context *ctx, pdf_document *doc); -struct pdf_ocg_entry_s +typedef struct { - int num; - int state; -}; + const char *name; + const char *creator; +} pdf_layer_config; + +/* + pdf_layer_config_info: Fetch the name (and + optionally creator) of the given layer config. + + doc: The document in question. + + config_num: A value in the 0..n-1 range, where n is the + value returned from pdf_count_layer_configs. + + info: Pointer to structure to fill in. Pointers within + this structure may be set to NULL if no information is + available. +*/ +void pdf_layer_config_info(fz_context *ctx, pdf_document *doc, int config_num, pdf_layer_config *info); + +/* + pdf_select_layer_config: Set the current configuration. + This updates the visibility of the optional content groups + within the document. + + doc: The document in question. + + config_num: A value in the 0..n-1 range, where n is the + value returned from pdf_count_layer_configs. +*/ +void pdf_select_layer_config(fz_context *ctx, pdf_document *doc, int config_num); + +/* + pdf_count_layer_config_ui: Returns the number of entries in the + 'UI' for this layer configuration. + + doc: The document in question. +*/ +int pdf_count_layer_config_ui(fz_context *ctx, pdf_document *doc); + +/* + pdf_select_layer_ui: Select a checkbox/radiobox + within the 'UI' for this layer configuration. + + Selecting a UI entry that is a radiobox may disable + other UI entries. + + doc: The document in question. -struct pdf_ocg_descriptor_s + ui: A value in the 0..m-1 range, where m is the value + returned by pdf_count_layer_config_ui. +*/ +void pdf_select_layer_config_ui(fz_context *ctx, pdf_document *doc, int ui); + +/* + pdf_deselect_layer_ui: Select a checkbox/radiobox + within the 'UI' for this layer configuration. + + doc: The document in question. + + ui: A value in the 0..m-1 range, where m is the value + returned by pdf_count_layer_config_ui. +*/ +void pdf_deselect_layer_config_ui(fz_context *ctx, pdf_document *doc, int ui); + +/* + pdf_toggle_layer_config_ui: Toggle a checkbox/radiobox + within the 'UI' for this layer configuration. + + Toggling a UI entry that is a radiobox may disable + other UI entries. + + doc: The document in question. + + ui: A value in the 0..m-1 range, where m is the value + returned by pdf_count_layer_config_ui. +*/ +void pdf_toggle_layer_config_ui(fz_context *ctx, pdf_document *doc, int ui); + +typedef enum { - int len; - pdf_ocg_entry *ocgs; - pdf_obj *intent; -}; + PDF_LAYER_UI_LABEL = 0, + PDF_LAYER_UI_CHECKBOX = 1, + PDF_LAYER_UI_RADIOBOX = 2 +} pdf_layer_config_ui_type; + +typedef struct +{ + const char *text; + int depth; + pdf_layer_config_ui_type type; + int selected; + int locked; +} pdf_layer_config_ui; + +/* + pdf_layer_config_ui_info: Get the info for a given + entry in the layer config ui. + + doc: The document in question. + + ui: A value in the 0..m-1 range, where m is the value + returned by pdf_count_layer_config_ui. + + info: Pointer to a structure to fill in with information + about the requested ui entry. +*/ +void pdf_layer_config_ui_info(fz_context *ctx, pdf_document *doc, int ui, pdf_layer_config_ui *info); + +int pdf_is_hidden_ocg(fz_context *ctx, pdf_ocg_descriptor *desc, pdf_obj *rdb, const char *usage, pdf_obj *ocg); /* pdf_update_page: update a page for the sake of changes caused by a call diff --git a/include/mupdf/pdf/interpret.h b/include/mupdf/pdf/interpret.h index 07460221..b7b85fff 100644 --- a/include/mupdf/pdf/interpret.h +++ b/include/mupdf/pdf/interpret.h @@ -127,7 +127,7 @@ struct pdf_processor_s void (*op_END)(fz_context *ctx, pdf_processor *proc); /* interpreter state that persists across content streams */ - const char *event; + const char *usage; int hidden; }; @@ -155,7 +155,7 @@ struct pdf_csi_s }; /* Functions to set up pdf_process structures */ -pdf_processor *pdf_new_run_processor(fz_context *ctx, fz_device *dev, const fz_matrix *ctm, const char *event, pdf_gstate *gstate, int nested); +pdf_processor *pdf_new_run_processor(fz_context *ctx, fz_device *dev, const fz_matrix *ctm, const char *usage, pdf_gstate *gstate, int nested); pdf_processor *pdf_new_buffer_processor(fz_context *ctx, fz_buffer *buffer, int ahxencode); pdf_processor *pdf_new_filter_processor(fz_context *ctx, pdf_processor *chain, pdf_document *doc, pdf_obj *old_res, pdf_obj *new_res); diff --git a/include/mupdf/pdf/page.h b/include/mupdf/pdf/page.h index a5a60be8..407fcc09 100644 --- a/include/mupdf/pdf/page.h +++ b/include/mupdf/pdf/page.h @@ -65,7 +65,7 @@ fz_rect *pdf_bound_page(fz_context *ctx, pdf_page *page, fz_rect *); void pdf_run_page(fz_context *ctx, pdf_page *page, fz_device *dev, const fz_matrix *ctm, fz_cookie *cookie); /* - pdf_run_page: Interpret a loaded page and render it on a device. + pdf_run_page_with_usage: Interpret a loaded page and render it on a device. page: A page loaded by pdf_load_page. @@ -74,10 +74,13 @@ void pdf_run_page(fz_context *ctx, pdf_page *page, fz_device *dev, const fz_matr ctm: A transformation matrix applied to the objects on the page, e.g. to scale or rotate the page contents as desired. + usage: The 'usage' for displaying the file (typically + 'View', 'Print' or 'Export'). NULL means 'View'. + cookie: A pointer to an optional fz_cookie structure that can be used to track progress, collect errors etc. */ -void pdf_run_page_with_usage(fz_context *ctx, pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, char *event, fz_cookie *cookie); +void pdf_run_page_with_usage(fz_context *ctx, pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, const char *usage, fz_cookie *cookie); /* pdf_run_page_contents: Interpret a loaded page and render it on a device. diff --git a/platform/win32/libmupdf.vcproj b/platform/win32/libmupdf.vcproj index f7134220..69193a9a 100644 --- a/platform/win32/libmupdf.vcproj +++ b/platform/win32/libmupdf.vcproj @@ -2030,6 +2030,10 @@ RelativePath="..\..\source\pdf\pdf-image.c" > + + @@ -2046,6 +2050,10 @@ RelativePath="..\..\source\pdf\pdf-js.c" > + + diff --git a/resources/pdf/names.txt b/resources/pdf/names.txt index 57228c15..30f4f60d 100644 --- a/resources/pdf/names.txt +++ b/resources/pdf/names.txt @@ -214,6 +214,7 @@ Limits Line Linearized Link +Locked Luminosity M MK @@ -249,6 +250,7 @@ Of Off OpenType Opt +Order Ordering Outlines P @@ -275,6 +277,7 @@ Push Q QuadPoints R +RBGroups RGB RI RL diff --git a/source/pdf/pdf-imp.h b/source/pdf/pdf-imp.h new file mode 100644 index 00000000..37605c51 --- /dev/null +++ b/source/pdf/pdf-imp.h @@ -0,0 +1,9 @@ +#ifndef MUPDF_PDF_IMP_H +#define MUPDF_PDF_IMP_H + +#include "mupdf/pdf.h" + +void pdf_read_ocg(fz_context *ctx, pdf_document *doc); +void pdf_drop_ocg(fz_context *ctx, pdf_document *doc); + +#endif diff --git a/source/pdf/pdf-interpret.c b/source/pdf/pdf-interpret.c index 58999373..4a8a2a46 100644 --- a/source/pdf/pdf-interpret.c +++ b/source/pdf/pdf-interpret.c @@ -81,220 +81,6 @@ load_font_or_hail_mary(fz_context *ctx, pdf_document *doc, pdf_obj *rdb, pdf_obj return desc; } -static int -ocg_intents_include(fz_context *ctx, pdf_ocg_descriptor *desc, char *name) -{ - int i, len; - - if (strcmp(name, "All") == 0) - return 1; - - /* In the absence of a specified intent, it's 'View' */ - if (!desc->intent) - return (strcmp(name, "View") == 0); - - if (pdf_is_name(ctx, desc->intent)) - { - char *intent = pdf_to_name(ctx, desc->intent); - if (strcmp(intent, "All") == 0) - return 1; - return (strcmp(intent, name) == 0); - } - if (!pdf_is_array(ctx, desc->intent)) - return 0; - - len = pdf_array_len(ctx, desc->intent); - for (i=0; i < len; i++) - { - char *intent = pdf_to_name(ctx, pdf_array_get(ctx, desc->intent, i)); - if (strcmp(intent, "All") == 0) - return 1; - if (strcmp(intent, name) == 0) - return 1; - } - return 0; -} - -static int -pdf_is_hidden_ocg(fz_context *ctx, pdf_ocg_descriptor *desc, pdf_obj *rdb, const char *event, pdf_obj *ocg) -{ - char event_state[16]; - pdf_obj *obj, *obj2, *type; - - /* Avoid infinite recursions */ - if (pdf_obj_marked(ctx, ocg)) - return 0; - - /* If no event, everything is visible */ - if (!event) - return 0; - - /* If no ocg descriptor, everything is visible */ - if (!desc) - return 0; - - /* If we've been handed a name, look it up in the properties. */ - if (pdf_is_name(ctx, ocg)) - { - ocg = pdf_dict_get(ctx, pdf_dict_get(ctx, rdb, PDF_NAME_Properties), ocg); - } - /* If we haven't been given an ocg at all, then we're visible */ - if (!ocg) - return 0; - - fz_strlcpy(event_state, event, sizeof event_state); - fz_strlcat(event_state, "State", sizeof event_state); - - type = pdf_dict_get(ctx, ocg, PDF_NAME_Type); - - if (pdf_name_eq(ctx, type, PDF_NAME_OCG)) - { - /* An Optional Content Group */ - int default_value = 0; - int num = pdf_to_num(ctx, ocg); - int len = desc->len; - int i; - pdf_obj *es; - - /* by default an OCG is visible, unless it's explicitly hidden */ - for (i = 0; i < len; i++) - { - if (desc->ocgs[i].num == num) - { - default_value = desc->ocgs[i].state == 0; - break; - } - } - - /* Check Intents; if our intent is not part of the set given - * by the current config, we should ignore it. */ - obj = pdf_dict_get(ctx, ocg, PDF_NAME_Intent); - if (pdf_is_name(ctx, obj)) - { - /* If it doesn't match, it's hidden */ - if (ocg_intents_include(ctx, desc, pdf_to_name(ctx, obj)) == 0) - return 1; - } - else if (pdf_is_array(ctx, obj)) - { - int match = 0; - len = pdf_array_len(ctx, obj); - for (i=0; i