summaryrefslogtreecommitdiff
path: root/pdf
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2011-11-25 19:42:34 +0000
committerRobin Watts <robin.watts@artifex.com>2011-11-25 19:42:34 +0000
commitbdf5c8848a2de071c8380fab86a1a49215ed5ee7 (patch)
tree8ff2e40bca4f337e5db112284992e6be67cea820 /pdf
parent5f6c8d94faecc0bd87113a138befe554ab2172b2 (diff)
parent6e14149d3e915f559f99276a525862e28d6f0478 (diff)
downloadmupdf-bdf5c8848a2de071c8380fab86a1a49215ed5ee7.tar.xz
Merge branch 'master' into context
Diffstat (limited to 'pdf')
-rw-r--r--pdf/mupdf.h19
-rw-r--r--pdf/pdf_crypt.c6
-rw-r--r--pdf/pdf_image.c12
-rw-r--r--pdf/pdf_interpret.c283
-rw-r--r--pdf/pdf_repair.c21
-rw-r--r--pdf/pdf_xref.c171
6 files changed, 474 insertions, 38 deletions
diff --git a/pdf/mupdf.h b/pdf/mupdf.h
index d3eb72e6..79560dab 100644
--- a/pdf/mupdf.h
+++ b/pdf/mupdf.h
@@ -45,6 +45,8 @@ char *pdf_from_ucs2(fz_context *ctx, unsigned short *str);
typedef struct pdf_xref_entry_s pdf_xref_entry;
typedef struct pdf_crypt_s pdf_crypt;
+typedef struct pdf_ocg_descriptor_s pdf_ocg_descriptor;
+typedef struct pdf_ocg_entry_s pdf_ocg_entry;
struct pdf_xref_entry_s
{
@@ -55,6 +57,20 @@ struct pdf_xref_entry_s
int type; /* 0=unset (f)ree i(n)use (o)bjstm */
};
+struct pdf_ocg_entry_s
+{
+ int num;
+ int gen;
+ int state;
+};
+
+struct pdf_ocg_descriptor_s
+{
+ int len;
+ pdf_ocg_entry *ocgs;
+ fz_obj *intent;
+};
+
struct pdf_xref_s
{
fz_context *ctx;
@@ -64,6 +80,7 @@ struct pdf_xref_s
int file_size;
pdf_crypt *crypt;
fz_obj *trailer;
+ pdf_ocg_descriptor *ocg;
int len;
pdf_xref_entry *table;
@@ -478,7 +495,7 @@ void pdf_free_page(fz_context *ctx, pdf_page *page);
* Content stream parsing
*/
-void pdf_run_page_with_usage(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm, char *target);
+void pdf_run_page_with_usage(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm, char *event);
void pdf_run_page(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm);
void pdf_run_glyph(pdf_xref *xref, fz_obj *resources, fz_buffer *contents, fz_device *dev, fz_matrix ctm);
diff --git a/pdf/pdf_crypt.c b/pdf/pdf_crypt.c
index 1ae16b37..98ad0159 100644
--- a/pdf/pdf_crypt.c
+++ b/pdf/pdf_crypt.c
@@ -722,7 +722,11 @@ pdf_crypt_obj_imp(fz_context *ctx, pdf_crypt *crypt, fz_obj *obj, unsigned char
if (crypt->strf.method == PDF_CRYPT_AESV2 || crypt->strf.method == PDF_CRYPT_AESV3)
{
- if (n & 15 || n < 32)
+ if (n == 0)
+ {
+ /* Empty strings are permissible */
+ }
+ else if (n & 15 || n < 32)
fz_warn(ctx, "invalid string length for aes encryption");
else
{
diff --git a/pdf/pdf_image.c b/pdf/pdf_image.c
index 438f4cef..849f703e 100644
--- a/pdf/pdf_image.c
+++ b/pdf/pdf_image.c
@@ -309,6 +309,18 @@ pdf_load_jpx_image(pdf_xref *xref, fz_obj *dict)
img->mask = pdf_load_image_imp(xref, NULL, obj, NULL, 1);
/* RJW: "cannot load image mask/softmask" */
}
+
+ obj = fz_dict_getsa(dict, "Decode", "D");
+ if (obj)
+ {
+ float decode[FZ_MAX_COLORS * 2];
+ int i;
+
+ for (i = 0; i < img->n * 2; i++)
+ decode[i] = fz_to_real(fz_array_get(obj, i));
+
+ fz_decode_tile(img, decode);
+ }
}
fz_catch(ctx)
{
diff --git a/pdf/pdf_interpret.c b/pdf/pdf_interpret.c
index 85901539..0720f782 100644
--- a/pdf/pdf_interpret.c
+++ b/pdf/pdf_interpret.c
@@ -67,7 +67,7 @@ struct pdf_csi_s
pdf_xref *xref;
/* usage mode for optional content groups */
- char *target; /* "View", "Print", "Export" */
+ char *event; /* "View", "Print", "Export" */
/* interpreter stack */
fz_obj *obj;
@@ -79,6 +79,7 @@ struct pdf_csi_s
int xbalance;
int in_text;
+ int in_hidden_ocg;
/* path object state */
fz_path *path;
@@ -102,24 +103,194 @@ static void pdf_run_buffer(pdf_csi *csi, fz_obj *rdb, fz_buffer *contents);
static void pdf_run_xobject(pdf_csi *csi, fz_obj *resources, pdf_xobject *xobj, fz_matrix transform);
static void pdf_show_pattern(pdf_csi *csi, pdf_pattern *pat, fz_rect area, int what);
+static int
+ocg_intents_include(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 == NULL)
+ return (strcmp(name, "View") == 0);
+
+ if (fz_is_name(desc->intent))
+ {
+ char *intent = fz_to_name(desc->intent);
+ if (strcmp(intent, "All") == 0)
+ return 1;
+ return (strcmp(intent, name) == 0);
+ }
+ if (!fz_is_array(desc->intent))
+ return 0;
+
+ len = fz_array_len(desc->intent);
+ for (i=0; i < len; i++)
+ {
+ char *intent = fz_to_name(fz_array_get(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, fz_obj *xobj, char *target)
+pdf_is_hidden_ocg(fz_obj *ocg, pdf_csi *csi, fz_obj *rdb)
{
- char target_state[16];
- fz_obj *obj;
+ char event_state[16];
+ fz_obj *obj, *obj2;
+ char *type;
+ pdf_ocg_descriptor *desc = csi->xref->ocg;
+
+ /* If no ocg descriptor, everything is visible */
+ if (desc == NULL)
+ return 0;
+
+ /* If we've been handed a name, look it up in the properties. */
+ if (fz_is_name(ocg))
+ {
+ ocg = fz_dict_gets(fz_dict_gets(rdb, "Properties"), fz_to_name(ocg));
+ }
+ /* If we haven't been given an ocg at all, then we're visible */
+ if (ocg == NULL)
+ return 0;
+
+ fz_strlcpy(event_state, csi->event, sizeof event_state);
+ fz_strlcat(event_state, "State", sizeof event_state);
+
+ type = fz_to_name(fz_dict_gets(ocg, "Type"));
+
+ if (strcmp(type, "OCG") == 0)
+ {
+ /* An Optional Content Group */
+ int num = fz_to_num(ocg);
+ int gen = fz_to_gen(ocg);
+ int len = desc->len;
+ int i;
+
+ for (i = 0; i < len; i++)
+ {
+ if (desc->ocgs[i].num == num && desc->ocgs[i].gen == gen)
+ {
+ if (desc->ocgs[i].state == 0)
+ return 1; /* If off, hidden */
+ break;
+ }
+ }
- fz_strlcpy(target_state, target, sizeof target_state);
- fz_strlcat(target_state, "State", sizeof target_state);
+ /* Check Intents; if our intent is not part of the set given
+ * by the current config, we should ignore it. */
+ obj = fz_dict_gets(ocg, "Intent");
+ if (fz_is_name(obj))
+ {
+ /* If it doesn't match, it's hidden */
+ if (ocg_intents_include(desc, fz_to_name(obj)) == 0)
+ return 1;
+ }
+ else if (fz_is_array(obj))
+ {
+ int match = 0;
+ len = fz_array_len(obj);
+ for (i=0; i<len; i++) {
+ match |= ocg_intents_include(desc, fz_to_name(fz_array_get(obj, i)));
+ if (match)
+ break;
+ }
+ /* If we don't match any, it's hidden */
+ if (match == 0)
+ return 1;
+ }
+ else
+ {
+ /* If it doesn't match, it's hidden */
+ if (ocg_intents_include(desc, "View") == 0)
+ return 1;
+ }
+
+ /* FIXME: Currently we do a very simple check whereby we look
+ * at the Usage object (an Optional Content Usage Dictionary)
+ * and check to see if the corresponding 'event' key is on
+ * or off.
+ *
+ * Really we should only look at Usage dictionaries that
+ * correspond to entries in the AS list in the OCG config.
+ * Given that we don't handle Zoom or User, or Language
+ * dicts, this is not really a problem. */
+ obj = fz_dict_gets(ocg, "Usage");
+ if (!fz_is_dict(obj))
+ return 0;
+ /* FIXME: Should look at Zoom (and return hidden if out of
+ * max/min range) */
+ /* FIXME: Could provide hooks to the caller to check if
+ * User is appropriate - if not return hidden. */
+ obj2 = fz_dict_gets(obj, csi->event);
+ if (strcmp(fz_to_name(fz_dict_gets(obj2, event_state)), "OFF") == 0)
+ {
+ return 1;
+ }
+ return 0;
+ }
+ else if (strcmp(type, "OCMD") == 0)
+ {
+ /* An Optional Content Membership Dictionary */
+ char *name;
+ int combine, on;
+
+ obj = fz_dict_gets(ocg, "VE");
+ if (fz_is_array(obj)) {
+ /* FIXME: Calculate visibility from array */
+ return 0;
+ }
+ name = fz_to_name(fz_dict_gets(ocg, "P"));
+ /* Set combine; Bit 0 set => AND, Bit 1 set => true means
+ * Off, otherwise true means On */
+ if (strcmp(name, "AllOn") == 0)
+ {
+ combine = 1;
+ }
+ else if (strcmp(name, "AnyOff") == 0)
+ {
+ combine = 2;
+ }
+ else if (strcmp(name, "AllOff") == 0)
+ {
+ combine = 3;
+ }
+ else /* Assume it's the default (AnyOn) */
+ {
+ combine = 0;
+ }
- obj = fz_dict_gets(xobj, "OC");
- obj = fz_dict_gets(obj, "OCGs");
- if (fz_is_array(obj))
- obj = fz_array_get(obj, 0);
- obj = fz_dict_gets(obj, "Usage");
- obj = fz_dict_gets(obj, target);
- obj = fz_dict_gets(obj, target_state);
- return !strcmp(fz_to_name(obj), "OFF");
+ obj = fz_dict_gets(ocg, "OCGs");
+ on = combine & 1;
+ if (fz_is_array(obj)) {
+ int i, len;
+ len = fz_array_len(obj);
+ for (i = 0; i < len; i++)
+ {
+ int hidden;
+ hidden = pdf_is_hidden_ocg(fz_array_get(obj, i), csi, rdb);
+ if ((combine & 1) == 0)
+ hidden = !hidden;
+ if (combine & 2)
+ on &= hidden;
+ else
+ on |= hidden;
+ }
+ }
+ else
+ {
+ on = pdf_is_hidden_ocg(obj, csi, rdb);
+ if ((combine & 1) == 0)
+ on = !on;
+ }
+ return !on;
+ }
+ /* No idea what sort of object this is - be visible */
+ return 0;
}
/*
@@ -172,6 +343,9 @@ pdf_show_shade(pdf_csi *csi, fz_shade *shd)
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_rect bbox;
+ if (csi->in_hidden_ocg > 0)
+ return;
+
bbox = fz_bound_shade(shd, gstate->ctm);
pdf_begin_group(csi, bbox);
@@ -187,6 +361,9 @@ pdf_show_image(pdf_csi *csi, fz_pixmap *image)
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_rect bbox;
+ if (csi->in_hidden_ocg > 0)
+ return;
+
bbox = fz_transform_rect(gstate->ctm, fz_unit_rect);
if (image->mask)
@@ -266,8 +443,12 @@ pdf_show_path(pdf_csi *csi, int doclose, int dofill, int dostroke, int even_odd)
{
gstate->clip_depth++;
fz_clip_path(csi->dev, path, NULL, csi->clip_even_odd, gstate->ctm);
+ csi->clip = 0;
}
+ if (csi->in_hidden_ocg > 0)
+ dostroke = dofill = 0;
+
if (dofill || dostroke)
pdf_begin_group(csi, bbox);
@@ -369,6 +550,9 @@ pdf_flush_text(pdf_csi *csi)
case 7: doclip = 1; break;
}
+ if (csi->in_hidden_ocg > 0)
+ dostroke = dofill = 0;
+
bbox = fz_bound_text(text, gstate->ctm);
pdf_begin_group(csi, bbox);
@@ -656,14 +840,14 @@ pdf_init_gstate(pdf_gstate *gs, fz_matrix ctm)
}
static pdf_csi *
-pdf_new_csi(pdf_xref *xref, fz_device *dev, fz_matrix ctm, char *target)
+pdf_new_csi(pdf_xref *xref, fz_device *dev, fz_matrix ctm, char *event)
{
pdf_csi *csi;
csi = fz_malloc(dev->ctx, sizeof(pdf_csi));
csi->xref = xref;
csi->dev = dev;
- csi->target = target;
+ csi->event = event;
csi->top = 0;
csi->obj = NULL;
@@ -673,6 +857,7 @@ pdf_new_csi(pdf_xref *xref, fz_device *dev, fz_matrix ctm, char *target)
csi->xbalance = 0;
csi->in_text = 0;
+ csi->in_hidden_ocg = 0;
csi->path = fz_new_path(xref->ctx);
csi->clip = 0;
@@ -1265,8 +1450,33 @@ pdf_run_extgstate(pdf_csi *csi, fz_obj *rdb, fz_obj *extgstate)
* Operators
*/
-static void pdf_run_BDC(pdf_csi *csi)
+static void pdf_run_BDC(pdf_csi *csi, fz_obj *rdb)
{
+ fz_obj *ocg;
+
+ /* If we are already in a hidden OCG, then we'll still be hidden -
+ * just increment the depth so we pop back to visibility when we've
+ * seen enough EDCs. */
+ if (csi->in_hidden_ocg > 0)
+ {
+ csi->in_hidden_ocg++;
+ return;
+ }
+
+ ocg = fz_dict_gets(fz_dict_gets(rdb, "Properties"), csi->name);
+ if (ocg == NULL)
+ {
+ /* No Properties array, or name not found in the properties
+ * means visible. */
+ return;
+ }
+ if (strcmp(fz_to_name(fz_dict_gets(ocg, "Type")), "OCG") != 0)
+ {
+ /* Wrong type of property */
+ return;
+ }
+ if (pdf_is_hidden_ocg(ocg, csi, rdb))
+ csi->in_hidden_ocg++;
}
static void pdf_run_BI(pdf_csi *csi, fz_obj *rdb, fz_stream *file)
@@ -1311,6 +1521,13 @@ static void pdf_run_B(pdf_csi *csi)
static void pdf_run_BMC(pdf_csi *csi)
{
+ /* If we are already in a hidden OCG, then we'll still be hidden -
+ * just increment the depth so we pop back to visibility when we've
+ * seen enough EDCs. */
+ if (csi->in_hidden_ocg > 0)
+ {
+ csi->in_hidden_ocg++;
+ }
}
static void pdf_run_BT(pdf_csi *csi)
@@ -1401,7 +1618,7 @@ static void pdf_run_Do(pdf_csi *csi, fz_obj *rdb)
if (!fz_is_name(subtype))
fz_throw(ctx, "no XObject subtype specified");
- if (pdf_is_hidden_ocg(ctx, obj, csi->target))
+ if (pdf_is_hidden_ocg(fz_dict_gets(obj, "OC"), csi, rdb))
return;
if (!strcmp(fz_to_name(subtype), "Form") && fz_dict_gets(obj, "Subtype2"))
@@ -1449,6 +1666,8 @@ static void pdf_run_Do(pdf_csi *csi, fz_obj *rdb)
static void pdf_run_EMC(pdf_csi *csi)
{
+ if (csi->in_hidden_ocg > 0)
+ csi->in_hidden_ocg--;
}
static void pdf_run_ET(pdf_csi *csi)
@@ -1999,7 +2218,7 @@ pdf_run_keyword(pdf_csi *csi, fz_obj *rdb, fz_stream *file, char *buf)
case A('\''): pdf_run_squote(csi); break;
case A('B'): pdf_run_B(csi); break;
case B('B','*'): pdf_run_Bstar(csi); break;
- case C('B','D','C'): pdf_run_BDC(csi); break;
+ case C('B','D','C'): pdf_run_BDC(csi, rdb); break;
case B('B','I'):
pdf_run_BI(csi, rdb, file);
/* RJW: "cannot draw inline image" */
@@ -2252,7 +2471,7 @@ pdf_run_buffer(pdf_csi *csi, fz_obj *rdb, fz_buffer *contents)
}
void
-pdf_run_page_with_usage(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm, char *target)
+pdf_run_page_with_usage(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm, char *event)
{
pdf_csi *csi;
pdf_annot *annot;
@@ -2262,7 +2481,7 @@ pdf_run_page_with_usage(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matri
if (page->transparency)
fz_begin_group(dev, fz_transform_rect(ctm, page->mediabox), 1, 0, 0, 1);
- csi = pdf_new_csi(xref, dev, ctm, target);
+ csi = pdf_new_csi(xref, dev, ctm, event);
fz_try(ctx)
{
pdf_run_buffer(csi, page->resources, page->contents);
@@ -2286,18 +2505,18 @@ pdf_run_page_with_usage(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matri
if (flags & (1 << 5)) /* NoView */
continue;
- if (pdf_is_hidden_ocg(ctx, annot->obj, target))
- continue;
-
- csi = pdf_new_csi(xref, dev, ctm, target);
- fz_try(ctx)
- {
- pdf_run_xobject(csi, page->resources, annot->ap, annot->matrix);
- }
- fz_catch(ctx)
+ csi = pdf_new_csi(xref, dev, ctm, event);
+ if (!pdf_is_hidden_ocg(fz_dict_gets(annot->obj, "OC"), csi, page->resources))
{
- pdf_free_csi(csi);
- fz_throw(ctx, "cannot parse annotation appearance stream");
+ fz_try(ctx)
+ {
+ pdf_run_xobject(csi, page->resources, annot->ap, annot->matrix);
+ }
+ fz_catch(ctx)
+ {
+ pdf_free_csi(csi);
+ fz_throw(ctx, "cannot parse annotation appearance stream");
+ }
}
pdf_free_csi(csi);
}
diff --git a/pdf/pdf_repair.c b/pdf/pdf_repair.c
index 0a30f83b..abc443cc 100644
--- a/pdf/pdf_repair.c
+++ b/pdf/pdf_repair.c
@@ -33,8 +33,15 @@ pdf_repair_obj(fz_stream *file, char *buf, int cap, int *stmofsp, int *stmlenp,
fz_obj *dict, *obj;
/* Send NULL xref so we don't try to resolve references */
- dict = pdf_parse_dict(NULL, file, buf, cap);
- /* RJW: "cannot parse object" */
+ fz_try(ctx)
+ {
+ dict = pdf_parse_dict(NULL, file, buf, cap);
+ }
+ fz_catch(ctx)
+ {
+ /* Silently swallow the error */
+ dict = fz_new_dict(ctx, 2);
+ }
obj = fz_dict_gets(dict, "Type");
if (fz_is_name(obj) && !strcmp(fz_to_name(obj), "XRef"))
@@ -66,13 +73,19 @@ pdf_repair_obj(fz_stream *file, char *buf, int cap, int *stmofsp, int *stmlenp,
while ( tok != PDF_TOK_STREAM &&
tok != PDF_TOK_ENDOBJ &&
tok != PDF_TOK_ERROR &&
- tok != PDF_TOK_EOF )
+ tok != PDF_TOK_EOF &&
+ tok != PDF_TOK_INT )
{
tok = pdf_lex(file, buf, cap, &len);
/* RJW: "cannot scan for endobj or stream token" */
}
- if (tok == PDF_TOK_STREAM)
+ if (tok == PDF_TOK_INT)
+ {
+ while (len-- > 0)
+ fz_unread_byte(file);
+ }
+ else if (tok == PDF_TOK_STREAM)
{
int c = fz_read_byte(file);
if (c == '\r') {
diff --git a/pdf/pdf_xref.c b/pdf/pdf_xref.c
index cb538ae3..501db660 100644
--- a/pdf/pdf_xref.c
+++ b/pdf/pdf_xref.c
@@ -469,6 +469,165 @@ pdf_load_xref(pdf_xref *xref, char *buf, int bufsize)
}
}
+void
+pdf_ocg_set_config(pdf_xref *xref, int config)
+{
+ int i, j, len, len2;
+ pdf_ocg_descriptor *desc = xref->ocg;
+ fz_obj *obj, *cobj;
+ char *name;
+
+ obj = fz_dict_gets(fz_dict_gets(xref->trailer, "Root"), "OCProperties");
+ if (obj == NULL)
+ {
+ if (config == 0)
+ return;
+ else
+ fz_throw(xref->ctx, "Unknown OCG config (None known!)");
+ }
+ if (config == 0)
+ {
+ cobj = fz_dict_gets(obj, "D");
+ if (cobj == NULL)
+ fz_throw(xref->ctx, "No default OCG config");
+ }
+ else
+ {
+ cobj = fz_array_get(fz_dict_gets(obj, "Configs"), config);
+ if (cobj == NULL)
+ fz_throw(xref->ctx, "Illegal OCG config");
+ }
+
+ if (desc->intent != NULL)
+ fz_drop_obj(desc->intent);
+ desc->intent = fz_dict_gets(cobj, "Intent");
+ if (desc->intent != NULL)
+ fz_keep_obj(desc->intent);
+
+ len = desc->len;
+ name = fz_to_name(fz_dict_gets(cobj, "BaseState"));
+ if (strcmp(name, "Unchanged") == 0)
+ {
+ /* Do nothing */
+ }
+ else if (strcmp(name, "OFF") == 0)
+ {
+ for (i = 0; i < len; i++)
+ {
+ desc->ocgs[i].state = 0;
+ }
+ }
+ else /* Default to ON */
+ {
+ for (i = 0; i < len; i++)
+ {
+ desc->ocgs[i].state = 1;
+ }
+ }
+
+ obj = fz_dict_gets(cobj, "ON");
+ len2 = fz_array_len(obj);
+ for (i = 0; i < len2; i++)
+ {
+ fz_obj *o = fz_array_get(obj, i);
+ int n = fz_to_num(o);
+ int g = fz_to_gen(o);
+ for (j=0; j < len; j++)
+ {
+ if (desc->ocgs[j].num == n && desc->ocgs[j].gen == g)
+ {
+ desc->ocgs[j].state = 1;
+ break;
+ }
+ }
+ }
+
+ obj = fz_dict_gets(cobj, "OFF");
+ len2 = fz_array_len(obj);
+ for (i = 0; i < len2; i++)
+ {
+ fz_obj *o = fz_array_get(obj, i);
+ int n = fz_to_num(o);
+ int g = fz_to_gen(o);
+ for (j=0; j < len; j++)
+ {
+ if (desc->ocgs[j].num == n && desc->ocgs[j].gen == g)
+ {
+ desc->ocgs[j].state = 0;
+ break;
+ }
+ }
+ }
+
+ /* FIXME: Should make 'num configs' available in the descriptor. */
+ /* FIXME: Should copy out 'Intent' here into the descriptor, and remove
+ * csi->intent in favour of that. */
+ /* FIXME: Should copy 'AS' into the descriptor, and visibility
+ * decisions should respect it. */
+ /* FIXME: Make 'Order' available via the descriptor (when we have an
+ * app that needs it) */
+ /* FIXME: Make 'ListMode' available via the descriptor (when we have
+ * an app that needs it) */
+ /* FIXME: Make 'RBGroups' available via the descriptor (when we have
+ * an app that needs it) */
+ /* FIXME: Make 'Locked' available via the descriptor (when we have
+ * an app that needs it) */
+}
+
+static void
+pdf_read_ocg(pdf_xref *xref)
+{
+ fz_obj *obj, *ocg;
+ int len, i;
+ pdf_ocg_descriptor * volatile desc;
+ fz_context *ctx = xref->ctx;
+
+ obj = fz_dict_gets(fz_dict_gets(xref->trailer, "Root"), "OCProperties");
+ if (obj == NULL)
+ return;
+ ocg = fz_dict_gets(obj, "OCGs");
+ if (ocg == NULL || !fz_is_array(ocg))
+ /* Not ever supposed to happen, but live with it. */
+ return;
+ len = fz_array_len(ocg);
+ fz_try(ctx)
+ {
+ desc = fz_calloc(ctx, 1, sizeof(*desc));
+ desc->len = len;
+ desc->ocgs = fz_calloc(ctx, len, sizeof(*desc->ocgs));
+ desc->intent = NULL;
+ for (i=0; i < len; i++)
+ {
+ fz_obj *o = fz_array_get(ocg, i);
+ desc->ocgs[i].num = fz_to_num(o);
+ desc->ocgs[i].gen = fz_to_gen(o);
+ desc->ocgs[i].state = 0;
+ }
+ xref->ocg = desc;
+ }
+ fz_catch(ctx)
+ {
+ if (desc != NULL)
+ fz_free(ctx, desc->ocgs);
+ fz_free(ctx, desc);
+ fz_rethrow(ctx);
+ }
+
+ pdf_ocg_set_config(xref, 0);
+}
+
+static void
+pdf_free_ocg(fz_context *ctx, pdf_ocg_descriptor *desc)
+{
+ if (desc == NULL)
+ return;
+
+ if (desc->intent)
+ fz_drop_obj(desc->intent);
+ fz_free(ctx, desc->ocgs);
+ fz_free(ctx, desc);
+}
+
/*
* Initialize and load xref tables.
* If password is not null, try to decrypt.
@@ -591,6 +750,16 @@ pdf_open_xref_with_stream(fz_stream *file, char *password)
fz_throw(ctx, "cannot open document");
}
+ fz_try(ctx)
+ {
+ pdf_read_ocg(xref);
+ }
+ fz_catch(ctx)
+ {
+ pdf_free_xref(xref);
+ fz_throw(ctx, "Broken Optional Content");
+ }
+
return xref;
}
@@ -637,6 +806,8 @@ pdf_free_xref(pdf_xref *xref)
if (xref->crypt)
pdf_free_crypt(ctx, xref->crypt);
+ pdf_free_ocg(ctx, xref->ocg);
+
fz_free(ctx, xref);
}