[poppler] 8 commits - glib/demo glib/Makefile.am glib/poppler-document.cc glib/poppler-document.h glib/poppler.h glib/poppler-layer.cc glib/poppler-layer.h glib/poppler-private.h glib/test-poppler-glib.cc poppler/Annot.cc poppler/Annot.h poppler/Catalog.cc poppler/Gfx.cc poppler/Gfx.h poppler/OptionalContent.cc poppler/OptionalContent.h qt4/src qt4/tests

Carlos Garcia Campos carlosgc at kemper.freedesktop.org
Sat Nov 8 12:01:09 PST 2008


 glib/Makefile.am               |    2 
 glib/demo/Makefile.am          |    2 
 glib/demo/layers.c             |  511 +++++++++++++++++++++++++++++++++++++++++
 glib/demo/layers.h             |   31 ++
 glib/demo/main.c               |    4 
 glib/poppler-document.cc       |  377 ++++++++++++++++++++++++++++++
 glib/poppler-document.h        |   12 
 glib/poppler-layer.cc          |  201 ++++++++++++++++
 glib/poppler-layer.h           |   43 +++
 glib/poppler-private.h         |   21 +
 glib/poppler.h                 |    3 
 glib/test-poppler-glib.cc      |   63 ++++-
 poppler/Annot.cc               |   20 -
 poppler/Annot.h                |    4 
 poppler/Catalog.cc             |    4 
 poppler/Gfx.cc                 |  113 ++++++---
 poppler/Gfx.h                  |    7 
 poppler/OptionalContent.cc     |   38 +--
 poppler/OptionalContent.h      |   23 +
 qt4/src/poppler-optcontent.cc  |    6 
 qt4/tests/check_optcontent.cpp |    8 
 21 files changed, 1405 insertions(+), 88 deletions(-)

New commits:
commit 471255c5a3850984997d91c7850759eb0c7e8a9c
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Sat Nov 8 20:58:57 2008 +0100

    [glib-demo] Add Optional Content demo

diff --git a/glib/demo/Makefile.am b/glib/demo/Makefile.am
index a79dbec..781c974 100644
--- a/glib/demo/Makefile.am
+++ b/glib/demo/Makefile.am
@@ -24,6 +24,8 @@ poppler_glib_demo_SOURCES = 			\
 	images.c				\
 	info.h					\
 	info.cc					\
+	layers.h				\
+	layers.c				\
 	links.h					\
 	links.c					\
 	outline.h				\
diff --git a/glib/demo/layers.c b/glib/demo/layers.c
new file mode 100644
index 0000000..ac93376
--- /dev/null
+++ b/glib/demo/layers.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2008 Carlos Garcia Campos  <carlosgc at gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "layers.h"
+
+enum {
+	LAYERS_TITLE_COLUMN,
+	LAYERS_VISIBILITY_COLUMN,
+	LAYERS_ENABLE_COLUMN,
+	LAYERS_SHOWTOGGLE_COLUMN,
+	LAYERS_RB_GROUP_COLUMN,
+	LAYERS_LAYER_COLUMN,
+	N_COLUMNS
+};
+
+typedef struct {
+	PopplerDocument *doc;
+	guint            page;
+	GtkWidget       *treeview;
+	GtkWidget       *darea;
+	
+#if defined (HAVE_CAIRO)
+	cairo_surface_t *surface;
+#else
+	GdkPixbuf       *pixbuf;
+#endif
+} PgdLayersDemo;
+
+static void
+pgd_layers_free (PgdLayersDemo *demo)
+{
+	if (!demo)
+		return;
+
+	if (demo->doc) {
+		g_object_unref (demo->doc);
+		demo->doc = NULL;
+	}
+	
+#if defined (HAVE_CAIRO)
+	if (demo->surface) {
+		cairo_surface_destroy (demo->surface);
+		demo->surface = NULL;
+	}
+#else
+	if (demo->pixbuf) {
+		g_object_unref (demo->pixbuf);
+		demo->pixbuf = NULL;
+	}
+#endif
+
+	g_free (demo);
+}
+
+static void
+build_tree (PopplerDocument   *document,
+	    GtkTreeModel      *model,
+	    GtkTreeIter       *parent,
+	    PopplerLayersIter *iter)
+{
+
+	do {
+		GtkTreeIter        tree_iter;
+		PopplerLayersIter *child;
+		PopplerLayer      *layer;
+		gboolean           visible;
+		gchar             *markup;
+		gint               rb_group = 0;
+
+		layer = poppler_layers_iter_get_layer (iter);
+		if (layer) {
+			markup = g_markup_escape_text (poppler_layer_get_title (layer), -1);
+			visible = poppler_layer_is_visible (layer);
+			rb_group = poppler_layer_get_radio_button_group_id (layer);
+		} else {
+			gchar *title;
+
+			title = poppler_layers_iter_get_title (iter);
+			markup = g_markup_escape_text (title, -1);
+			g_free (title);
+
+			visible = FALSE;
+			layer = NULL;
+		}
+
+		gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent);
+		gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter,
+				    LAYERS_TITLE_COLUMN, markup,
+				    LAYERS_VISIBILITY_COLUMN, visible,
+				    LAYERS_ENABLE_COLUMN, TRUE, /* FIXME */
+				    LAYERS_SHOWTOGGLE_COLUMN, (layer != NULL),
+				    LAYERS_RB_GROUP_COLUMN, rb_group,
+				    LAYERS_LAYER_COLUMN, layer,
+				    -1);
+		if (layer)
+			g_object_unref (layer);
+		g_free (markup);
+
+		child = poppler_layers_iter_get_child (iter);
+		if (child)
+			build_tree (document, model, &tree_iter, child);
+		poppler_layers_iter_free (child);
+	} while (poppler_layers_iter_next (iter));
+}
+
+GtkTreeModel *
+pgd_layers_create_model (PopplerDocument *document)
+{
+	GtkTreeModel     *model;
+	PopplerLayersIter *iter;
+
+	iter = poppler_layers_iter_new (document);
+	if (iter) {
+		model = GTK_TREE_MODEL (
+			gtk_tree_store_new (N_COLUMNS,
+					    G_TYPE_STRING, 
+					    G_TYPE_BOOLEAN,
+					    G_TYPE_BOOLEAN,
+					    G_TYPE_BOOLEAN,
+					    G_TYPE_INT,
+					    G_TYPE_OBJECT));
+		build_tree (document, model, NULL, iter);
+		poppler_layers_iter_free (iter);
+	} else {
+		GtkTreeIter tree_iter;
+		gchar      *markup;
+		
+		model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
+		gtk_list_store_append (GTK_LIST_STORE (model), &tree_iter);
+		markup = g_strdup_printf ("<span size=\"larger\" style=\"italic\">%s</span>",
+					  "The document doesn't contain layers");
+		gtk_list_store_set (GTK_LIST_STORE (model), &tree_iter,
+				    0, markup, -1);
+		g_free (markup);
+	}
+
+	return model;
+}
+
+#if defined (HAVE_CAIRO)
+static cairo_surface_t *
+pgd_layers_render_page (PgdLayersDemo *demo)
+{
+	cairo_t *cr;
+	PopplerPage *page;
+	gdouble width, height;
+	cairo_surface_t *surface = NULL;
+
+	page = poppler_document_get_page (demo->doc, demo->page);
+	if (!page)
+		return NULL;
+
+	poppler_page_get_size (page, &width, &height);
+	gtk_widget_set_size_request (demo->darea, width, height);
+	
+	surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+					      width, height);
+	cr = cairo_create (surface);
+
+	cairo_save (cr);
+	cairo_set_source_rgb (cr, 1, 1, 1);
+	cairo_rectangle (cr, 0, 0, width, height);
+	cairo_fill (cr);
+	cairo_restore (cr);
+
+	cairo_save (cr);
+	poppler_page_render (page, cr);
+	cairo_restore (cr);
+	
+	cairo_destroy (cr);
+	g_object_unref (page);
+
+	return surface;
+}
+#else
+static GdkPixbuf *
+pgd_layers_render_page (PgdLayersDemo *demo)
+{
+	PopplerPage *page;
+	gdouble width, height;
+	GdkPixbuf *pixbuf = NULL;
+
+	page = poppler_document_get_page (demo->doc, demo->page);
+	if (!page)
+		return NULL;
+
+	poppler_page_get_size (page, &width, &height);
+	gtk_widget_set_size_request (demo->darea, width, height);
+	
+	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+				 FALSE, 8, width, height);
+	gdk_pixbuf_fill (pixbuf, 0xffffff);
+	poppler_page_render_to_pixbuf (page, 0, 0, 
+				       width, height,
+				       1.0, 0, pixbuf);
+	g_object_unref (page);
+
+	return pixbuf;
+}
+#endif
+
+static gboolean
+pgd_layers_viewer_drawing_area_expose (GtkWidget      *area,
+				       GdkEventExpose *event,
+				       PgdLayersDemo  *demo)
+{
+#if defined (HAVE_CAIRO)
+	cairo_t *cr;
+	
+	if (!demo->surface) {
+		demo->surface = pgd_layers_render_page (demo);
+		if (!demo->surface)
+			return FALSE;
+	}
+#else
+	
+	if (!demo->pixbuf) {
+		demo->pixbuf = pgd_layers_render_page (demo);
+		if (!demo->pixbuf)
+			return FALSE;
+	}
+#endif
+
+	gdk_window_clear (area->window);
+
+#if defined (HAVE_CAIRO)
+	cr = gdk_cairo_create (area->window);
+	cairo_set_source_surface (cr, demo->surface, 0, 0);
+	cairo_paint (cr);
+	cairo_destroy (cr);
+#else
+	gdk_draw_pixbuf (area->window,
+			 area->style->fg_gc[GTK_STATE_NORMAL],
+			 demo->pixbuf,
+			 0, 0,
+			 0, 0,
+			 gdk_pixbuf_get_width (demo->pixbuf),
+			 gdk_pixbuf_get_height (demo->pixbuf),
+			 GDK_RGB_DITHER_NORMAL,
+			 0, 0);
+#endif
+	
+	return TRUE;
+}
+
+static gboolean
+pgd_layers_viewer_redraw (PgdLayersDemo *demo)
+{
+#if defined (HAVE_CAIRO)
+	cairo_surface_destroy (demo->surface);
+	demo->surface = NULL;
+#else
+	g_object_unref (demo->pixbuf);
+	demo->pixbuf = NULL;
+#endif
+	gtk_widget_queue_draw (demo->darea);
+
+	return FALSE;
+}
+
+static void
+pgd_layers_viewer_queue_redraw (PgdLayersDemo *demo)
+{
+	g_idle_add ((GSourceFunc)pgd_layers_viewer_redraw, demo);
+}
+
+static void
+pgd_layers_page_selector_value_changed (GtkSpinButton *spinbutton,
+					PgdLayersDemo *demo)
+{
+	demo->page = (gint)gtk_spin_button_get_value (spinbutton) - 1;
+	pgd_layers_viewer_queue_redraw (demo);
+}
+
+static GtkWidget *
+pgd_layers_create_viewer (PgdLayersDemo *demo)
+{
+	GtkWidget *vbox, *hbox;
+	GtkWidget *label;
+	GtkWidget *swindow;
+	GtkWidget *page_selector;
+	guint      n_pages;
+	gchar     *str;
+
+	vbox = gtk_vbox_new (FALSE, 6);
+
+	hbox = gtk_hbox_new (FALSE, 6);
+
+	label = gtk_label_new ("Page:");
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+	gtk_widget_show (label);
+
+	n_pages = poppler_document_get_n_pages (demo->doc);
+	page_selector = gtk_spin_button_new_with_range (1, n_pages, 1);
+	g_signal_connect (G_OBJECT (page_selector), "value-changed",
+			  G_CALLBACK (pgd_layers_page_selector_value_changed),
+			  (gpointer)demo);
+	gtk_box_pack_start (GTK_BOX (hbox), page_selector, FALSE, TRUE, 0);
+	gtk_widget_show (page_selector);
+
+	str = g_strdup_printf ("of %d", n_pages);
+	label = gtk_label_new (str);
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+	gtk_widget_show (label);
+	g_free (str);
+
+	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+	gtk_widget_show (hbox);
+
+	demo->darea = gtk_drawing_area_new ();
+	g_signal_connect (G_OBJECT (demo->darea), "expose_event",
+			  G_CALLBACK (pgd_layers_viewer_drawing_area_expose),
+			  (gpointer)demo);
+
+	swindow = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
+					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (swindow), demo->darea);
+	gtk_widget_show (demo->darea);
+
+	gtk_box_pack_start (GTK_BOX (vbox), swindow, TRUE, TRUE, 0);
+	gtk_widget_show (swindow);
+
+	return vbox;
+}
+
+static gboolean
+update_kids (GtkTreeModel *model,
+	     GtkTreePath  *path,
+	     GtkTreeIter  *iter,
+	     GtkTreeIter  *parent)
+{
+	if (gtk_tree_store_is_ancestor (GTK_TREE_STORE (model), parent, iter)) {
+		gboolean visible;
+
+		gtk_tree_model_get (model, parent,
+				    LAYERS_VISIBILITY_COLUMN, &visible,
+				    -1);
+		gtk_tree_store_set (GTK_TREE_STORE (model), iter,
+				    LAYERS_ENABLE_COLUMN, visible,
+				    -1);
+	}
+
+	return FALSE;
+}
+
+static gboolean
+clear_rb_group (GtkTreeModel *model,
+		GtkTreePath  *path,
+		GtkTreeIter  *iter,
+		gint         *rb_group)
+{
+	gint group;
+	
+	gtk_tree_model_get (model, iter,
+			    LAYERS_RB_GROUP_COLUMN, &group,
+			    -1);
+	
+	if (group == *rb_group) {
+		gtk_tree_store_set (GTK_TREE_STORE (model), iter,
+				    LAYERS_VISIBILITY_COLUMN, FALSE,
+				    -1);
+	}
+	
+	return FALSE;
+}
+
+static void
+pgd_layers_visibility_changed (GtkCellRendererToggle *cell,
+			       gchar                 *path_str,
+			       PgdLayersDemo         *demo)
+{
+	GtkTreeModel *model;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	gboolean visible;
+	PopplerLayer *layer;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (demo->treeview));
+
+	path = gtk_tree_path_new_from_string (path_str);
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_tree_model_get (model, &iter,
+			    LAYERS_VISIBILITY_COLUMN, &visible,
+			    LAYERS_LAYER_COLUMN, &layer,
+			    -1);
+
+	visible = !visible;
+	visible ? poppler_layer_show (layer) : poppler_layer_hide (layer);
+
+	if (visible) {
+		gint rb_group;
+		
+		rb_group = poppler_layer_get_radio_button_group_id (layer);
+		if (rb_group) {
+			gtk_tree_model_foreach (model,
+						(GtkTreeModelForeachFunc)clear_rb_group,
+						&rb_group);
+		}
+	}
+	
+	gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
+			    LAYERS_VISIBILITY_COLUMN, visible,
+			    -1);
+	
+	if (poppler_layer_is_parent (layer)) {
+		gtk_tree_model_foreach (model,
+					(GtkTreeModelForeachFunc)update_kids,
+					&iter);
+	}
+
+	pgd_layers_viewer_queue_redraw (demo);
+
+	gtk_tree_path_free (path);
+	g_object_unref (layer);
+}
+
+GtkWidget *
+pgd_layers_create_widget (PopplerDocument *document)
+{
+	PgdLayersDemo    *demo;
+	GtkWidget        *swindow;
+	GtkWidget        *treeview;
+	GtkTreeModel     *model;
+	GtkCellRenderer  *renderer;
+	GtkTreeSelection *selection;
+	GtkWidget        *hpaned, *viewer;
+
+	demo = g_new0 (PgdLayersDemo, 1);
+	demo->doc = g_object_ref (document);
+	
+	hpaned = gtk_hpaned_new ();
+
+	viewer = pgd_layers_create_viewer (demo);
+	
+	swindow = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
+					GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
+
+	model = pgd_layers_create_model (document);
+	treeview = gtk_tree_view_new_with_model (model);
+	demo->treeview = treeview;
+	g_object_unref (model);
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
+						     0, "Layer",
+						     renderer,
+						     "markup", LAYERS_TITLE_COLUMN,
+						     NULL);
+	g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+	g_object_set (G_OBJECT (gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), 0)),
+		      "expand", TRUE, NULL);
+
+	if (GTK_IS_TREE_STORE (model)) {
+		renderer = gtk_cell_renderer_toggle_new ();
+		gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
+							     1, "Show/Hide",
+							     renderer,
+							     "active", LAYERS_VISIBILITY_COLUMN,
+							     "activatable", LAYERS_ENABLE_COLUMN,
+							     "visible", LAYERS_SHOWTOGGLE_COLUMN,
+							     NULL);
+
+		g_signal_connect (renderer, "toggled",
+				  G_CALLBACK (pgd_layers_visibility_changed),
+				  (gpointer)demo);
+		gtk_tree_view_column_set_clickable (gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), 1),
+						    TRUE);
+	}
+
+	gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)),
+				     GTK_SELECTION_NONE);
+
+	gtk_container_add (GTK_CONTAINER (swindow), treeview);
+	gtk_widget_show (treeview);
+
+	gtk_paned_add1 (GTK_PANED (hpaned), swindow);
+	gtk_widget_show (swindow);
+
+	gtk_paned_add2 (GTK_PANED (hpaned), viewer);
+	gtk_widget_show (viewer);
+
+	gtk_paned_set_position (GTK_PANED (hpaned), 150);
+
+	g_object_weak_ref (G_OBJECT (hpaned),
+			   (GWeakNotify)pgd_layers_free,
+			   (gpointer)demo);
+
+	return hpaned;
+}
diff --git a/glib/demo/layers.h b/glib/demo/layers.h
new file mode 100644
index 0000000..88b43c3
--- /dev/null
+++ b/glib/demo/layers.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2008 Carlos Garcia Campos  <carlosgc at gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <poppler.h>
+
+#ifndef _LAYERS_H_
+#define _LAYERS_H_
+
+G_BEGIN_DECLS
+
+GtkWidget *pgd_layers_create_widget (PopplerDocument *document);
+
+G_END_DECLS
+
+#endif /* _LAYERS_H_ */ 
diff --git a/glib/demo/main.c b/glib/demo/main.c
index 3c26e61..ebbb595 100644
--- a/glib/demo/main.c
+++ b/glib/demo/main.c
@@ -31,6 +31,7 @@
 #include "images.h"
 #include "annots.h"
 #include "attachments.h"
+#include "layers.h"
 
 enum {
 	PGD_TITLE_COLUMN,
@@ -55,7 +56,8 @@ static const PopplerGlibDemo demo_list[] = {
 	{ "Page Transitions", pgd_transitions_create_widget },
 	{ "Images",           pgd_images_create_widget },
 	{ "Annots",           pgd_annots_create_widget },
-	{ "Attachments",      pgd_attachments_create_widget }
+	{ "Attachments",      pgd_attachments_create_widget },
+	{ "Layers",           pgd_layers_create_widget }
 };
 
 static void
commit 7363c25e1d83332932d9b4fe16d7fb4e364da628
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Sun Oct 26 19:47:35 2008 +0100

    [glib] Add Optional Content support

diff --git a/glib/Makefile.am b/glib/Makefile.am
index 187caed..806a37a 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -67,6 +67,7 @@ poppler_glib_public_headers =			\
 	poppler-attachment.h			\
 	poppler-form-field.h			\
 	poppler-annot.h				\
+	poppler-layer.h				\
 	poppler.h
 
 poppler_glib_includedir = $(includedir)/poppler/glib
@@ -85,6 +86,7 @@ libpoppler_glib_la_SOURCES =			\
 	poppler-attachment.cc			\
 	poppler-form-field.cc			\
 	poppler-annot.cc			\
+	poppler-layer.cc			\
 	poppler.cc				\
 	poppler-private.h
 
diff --git a/glib/poppler-document.cc b/glib/poppler-document.cc
index ef5deb0..352149b 100644
--- a/glib/poppler-document.cc
+++ b/glib/poppler-document.cc
@@ -29,6 +29,7 @@
 #include <FontInfo.h>
 #include <PDFDocEncoding.h>
 #include <DateInfo.h>
+#include <OptionalContent.h>
 
 #include "poppler.h"
 #include "poppler-private.h"
@@ -53,6 +54,8 @@ enum {
 	PROP_METADATA
 };
 
+static void poppler_document_layers_free (PopplerDocument *document);
+
 typedef struct _PopplerDocumentClass PopplerDocumentClass;
 struct _PopplerDocumentClass
 {
@@ -294,6 +297,7 @@ poppler_document_finalize (GObject *object)
 {
   PopplerDocument *document = POPPLER_DOCUMENT (object);
 
+  poppler_document_layers_free (document);
   delete document->output_dev;
   delete document->doc;
 }
@@ -1353,6 +1357,379 @@ poppler_font_info_free (PopplerFontInfo *font_info)
 }
 
 
+/* Optional content (layers) */
+static Layer *
+layer_new (OptionalContentGroup *oc)
+{
+  Layer *layer;
+
+  layer = g_new0 (Layer, 1);
+  layer->oc = oc;
+
+  return layer;
+}
+
+static void
+layer_free (Layer *layer)
+{
+  if (!layer)
+    return;
+
+  if (layer->kids) {
+	  g_list_foreach (layer->kids, (GFunc)layer_free, NULL);
+	  g_list_free (layer->kids);
+  }
+
+  if (layer->label) {
+	  g_free (layer->label);
+  }
+
+  g_free (layer);
+}
+
+static GList *
+get_optional_content_rbgroups (OCGs *ocg)
+{
+  Array *rb;
+  GList *groups = NULL;
+  
+  rb = ocg->getRBGroupsArray ();
+  
+  if (rb) {
+    int i, j;
+
+    for (i = 0; i < rb->getLength (); ++i) {
+      Object obj;
+      Array *rb_array;
+      GList *group = NULL;
+
+      rb->get (i, &obj);
+      if (!obj.isArray ()) {
+        obj.free ();
+	continue;
+      }
+
+      rb_array = obj.getArray ();
+      for (j = 0; j < rb_array->getLength (); ++j) {
+        Object ref;
+	OptionalContentGroup *oc;
+
+	rb_array->getNF (j, &ref);
+	if (!ref.isRef ()) {
+	  ref.free ();
+	  continue;
+	}
+
+	oc = ocg->findOcgByRef (ref.getRef ());
+	group = g_list_prepend (group, oc);
+	ref.free ();
+      }
+      obj.free ();
+
+      groups = g_list_prepend (groups, group);
+    }
+  }
+
+  return groups;
+}
+
+static GList *
+poppler_document_get_layer_rbgroup (PopplerDocument *document,
+				    Layer           *layer)
+{
+  GList *l;
+
+  for (l = document->layers_rbgroups; l && l->data; l = g_list_next (l)) {
+    GList *group = (GList *)l->data;
+
+    if (g_list_find (group, layer->oc))
+      return group;
+  }
+
+  return NULL;
+}
+
+static GList *
+get_optional_content_items_sorted (OCGs *ocg, Layer *parent, Array *order)
+{
+  GList *items = NULL;
+  Layer *last_item = parent;
+  int i;
+
+  for (i = 0; i < order->getLength (); ++i) {
+    Object orderItem;
+      
+    order->get (i, &orderItem);
+
+    if (orderItem.isDict ()) {
+      Object ref;
+      
+      order->getNF (i, &ref);
+      if (ref.isRef ()) {
+        OptionalContentGroup *oc = ocg->findOcgByRef (ref.getRef ());
+	Layer *layer = layer_new (oc);
+
+	items = g_list_prepend (items, layer);
+	last_item = layer;
+      }
+      ref.free ();
+    } else if (orderItem.isArray () && orderItem.arrayGetLength () > 0) {
+      if (!last_item) {
+        last_item = layer_new (NULL);
+	items = g_list_prepend (items, last_item);
+      }
+      last_item->kids = get_optional_content_items_sorted (ocg, last_item, orderItem.getArray ());
+    } else if (orderItem.isString ()) {
+      last_item->label = _poppler_goo_string_to_utf8 (orderItem.getString ());
+    }
+    orderItem.free ();
+  }
+  
+  return g_list_reverse (items);
+}
+		
+static GList *
+get_optional_content_items (OCGs *ocg)
+{
+  Array *order;
+  GList *items = NULL;
+  
+  order = ocg->getOrderArray ();
+
+  if (order) {
+    items = get_optional_content_items_sorted (ocg, NULL, order);
+  } else {
+    GooList *ocgs;
+    int i;
+
+    ocgs = ocg->getOCGs ();
+    
+    for (i = 0; i < ocgs->getLength (); ++i) {
+      OptionalContentGroup *oc = (OptionalContentGroup *) ocgs->get (i);
+      Layer *layer = layer_new (oc);
+      
+      items = g_list_prepend (items, layer);
+    }
+    
+    items = g_list_reverse (items);
+  }
+
+  return items;
+}
+
+static GList *
+poppler_document_get_layers (PopplerDocument *document)
+{
+  if (!document->layers) {
+    Catalog *catalog = document->doc->getCatalog ();
+    OCGs *ocg = catalog->getOptContentConfig ();
+
+    if (!ocg)
+      return NULL;
+    
+    document->layers = get_optional_content_items (ocg);
+    document->layers_rbgroups = get_optional_content_rbgroups (ocg);
+  }
+
+  return document->layers;
+}
+
+static void
+poppler_document_layers_free (PopplerDocument *document)
+{
+  if (!document->layers)
+	  return;
+
+  g_list_foreach (document->layers, (GFunc)layer_free, NULL);
+  g_list_free (document->layers);
+
+  g_list_foreach (document->layers_rbgroups, (GFunc)g_list_free, NULL);
+  g_list_free (document->layers_rbgroups);
+
+  document->layers = NULL;
+  document->layers_rbgroups = NULL;
+}
+
+/* PopplerLayersIter */
+struct _PopplerLayersIter {
+  PopplerDocument *document;
+  GList *items;
+  int index;
+};
+
+GType
+poppler_layers_iter_get_type (void)
+{
+  static GType our_type = 0;
+
+  if (our_type == 0)
+    our_type = g_boxed_type_register_static ("PopplerLayersIter",
+					     (GBoxedCopyFunc) poppler_layers_iter_copy,
+					     (GBoxedFreeFunc) poppler_layers_iter_free);
+  return our_type;
+}
+
+/**
+ * poppler_layers_iter_copy:
+ * @iter: a #PopplerLayersIter
+ * 
+ * Creates a new #PopplerLayersIter as a copy of @iter.  This must be freed with
+ * poppler_layers_iter_free().
+ * 
+ * Return value: a new #PopplerLayersIter
+ **/
+PopplerLayersIter *
+poppler_layers_iter_copy (PopplerLayersIter *iter)
+{
+  PopplerLayersIter *new_iter;
+
+  g_return_val_if_fail (iter != NULL, NULL);
+  
+  new_iter = g_new0 (PopplerLayersIter, 1);
+  *new_iter = *iter;
+  new_iter->document = (PopplerDocument *) g_object_ref (new_iter->document);
+  
+  return new_iter;
+}
+
+/**
+ * poppler_layers_iter_free:
+ * @iter: a #PopplerLayersIter
+ * 
+ * Frees @iter.
+ **/
+void
+poppler_layers_iter_free (PopplerLayersIter *iter)
+{
+  if (iter == NULL)
+    return;
+
+  g_object_unref (iter->document);
+  g_free (iter);
+}
+
+/**
+ * poppler_layers_iter_new:
+ **/
+PopplerLayersIter *
+poppler_layers_iter_new (PopplerDocument *document)
+{
+  PopplerLayersIter *iter;
+  GList *items;
+
+  items = poppler_document_get_layers (document);
+
+  if (!items)
+    return NULL;
+
+  iter = g_new0 (PopplerLayersIter, 1);
+  iter->document = (PopplerDocument *)g_object_ref (document);
+  iter->items = items;
+
+  return iter;
+}
+
+/**
+ * poppler_layers_iter_get_child:
+ * @parent: a #PopplerLayersIter
+ * 
+ * Returns a newly created child of @parent, or %NULL if the iter has no child.
+ * See poppler_layers_iter_new() for more information on this function.
+ * 
+ * Return value: a new #PopplerLayersIter, or %NULL
+ **/
+PopplerLayersIter *
+poppler_layers_iter_get_child (PopplerLayersIter *parent)
+{
+  PopplerLayersIter *child;
+  Layer *layer;
+
+  g_return_val_if_fail (parent != NULL, NULL);
+	
+  layer = (Layer *) g_list_nth_data (parent->items, parent->index);
+  if (!layer || !layer->kids)
+    return NULL;
+
+  child = g_new0 (PopplerLayersIter, 1);
+  child->document = (PopplerDocument *)g_object_ref (parent->document);
+  child->items = layer->kids;
+
+  g_assert (child->items);
+
+  return child;
+}
+
+/**
+ * poppler_layers_iter_get_title:
+ * @iter: a #PopplerLayersIter
+ * 
+ * Returns the title associated with @iter.  It must be freed with
+ * g_free().
+ * 
+ * Return value: a new string containing the @iter's title or %NULL if @iter doesn't have a title.
+ * The returned string should be freed with g_free() when no longer needed. 
+ **/
+gchar *
+poppler_layers_iter_get_title (PopplerLayersIter *iter)
+{
+  Layer *layer;
+  
+  g_return_val_if_fail (iter != NULL, NULL);
+  
+  layer = (Layer *)g_list_nth_data (iter->items, iter->index);
+
+  return layer->label ? g_strdup (layer->label) : NULL;
+}
+
+/**
+ * poppler_layers_iter_get_layer:
+ * @iter: a #PopplerLayersIter
+ * 
+ * Returns the #PopplerLayer associated with @iter.  It must be freed with
+ * poppler_layer_free().
+ * 
+ * Return value: a new #PopplerLayer, or %NULL if there isn't any layer associated with @iter
+ **/
+PopplerLayer *
+poppler_layers_iter_get_layer (PopplerLayersIter *iter)
+{
+  Layer *layer;
+  PopplerLayer *poppler_layer = NULL;
+  
+  g_return_val_if_fail (iter != NULL, NULL);
+  
+  layer = (Layer *)g_list_nth_data (iter->items, iter->index);
+  if (layer->oc) {
+    GList *rb_group = NULL;
+
+    rb_group = poppler_document_get_layer_rbgroup (iter->document, layer);
+    poppler_layer = _poppler_layer_new (iter->document, layer, rb_group);
+  }
+  
+  return poppler_layer;
+}
+
+/**
+ * poppler_layers_iter_next:
+ * @iter: a #PopplerLayersIter
+ * 
+ * Sets @iter to point to the next action at the current level, if valid.  See
+ * poppler_layers_iter_new() for more information.
+ * 
+ * Return value: %TRUE, if @iter was set to the next action
+ **/
+gboolean
+poppler_layers_iter_next (PopplerLayersIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, FALSE);
+
+  iter->index++;
+  if (iter->index >= (gint)g_list_length (iter->items))
+    return FALSE;
+  
+  return TRUE;
+}
+
 typedef struct _PopplerPSFileClass PopplerPSFileClass;
 struct _PopplerPSFileClass
 {
diff --git a/glib/poppler-document.h b/glib/poppler-document.h
index 4585425..2f4f50a 100644
--- a/glib/poppler-document.h
+++ b/glib/poppler-document.h
@@ -157,6 +157,18 @@ gboolean	  poppler_fonts_iter_is_embedded   (PopplerFontsIter  *iter);
 gboolean	  poppler_fonts_iter_is_subset     (PopplerFontsIter  *iter);
 gboolean          poppler_fonts_iter_next          (PopplerFontsIter  *iter);
 
+/* Interface for getting the Layers of a poppler_document */
+#define POPPLER_TYPE_LAYERS_ITER                   (poppler_layers_iter_get_type ())
+GType              poppler_layers_iter_get_type    (void) G_GNUC_CONST;
+PopplerLayersIter *poppler_layers_iter_new         (PopplerDocument   *document);
+PopplerLayersIter *poppler_layers_iter_copy        (PopplerLayersIter *iter);
+void               poppler_layers_iter_free        (PopplerLayersIter *iter);
+
+PopplerLayersIter *poppler_layers_iter_get_child   (PopplerLayersIter *parent);
+gchar             *poppler_layers_iter_get_title   (PopplerLayersIter *iter);
+PopplerLayer      *poppler_layers_iter_get_layer   (PopplerLayersIter *iter);
+gboolean           poppler_layers_iter_next        (PopplerLayersIter *iter);
+
 /* Export to ps */
 #define POPPLER_TYPE_PS_FILE             (poppler_ps_file_get_type ())
 #define POPPLER_PS_FILE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), POPPLER_TYPE_PS_FILE, PopplerPSFile))
diff --git a/glib/poppler-layer.cc b/glib/poppler-layer.cc
new file mode 100644
index 0000000..2049dc7
--- /dev/null
+++ b/glib/poppler-layer.cc
@@ -0,0 +1,201 @@
+/* poppler-layer.cc: glib interface to poppler
+ *
+ * Copyright (C) 2008 Carlos Garcia Campos <carlosgc at gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "poppler-layer.h"
+#include "poppler-private.h"
+
+typedef struct _PopplerLayerClass PopplerLayerClass;
+struct _PopplerLayerClass
+{
+  GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE (PopplerLayer, poppler_layer, G_TYPE_OBJECT)
+
+static void
+poppler_layer_finalize (GObject *object)
+{
+  PopplerLayer *poppler_layer = POPPLER_LAYER (object);
+
+  if (poppler_layer->document)
+    {
+      g_object_unref (poppler_layer->document);
+      poppler_layer->document = NULL;
+    }
+
+  if (poppler_layer->title)
+    {
+      g_free (poppler_layer->title);
+      poppler_layer->title = NULL;
+    }
+  poppler_layer->layer = NULL;
+  poppler_layer->rbgroup = NULL;
+
+  G_OBJECT_CLASS (poppler_layer_parent_class)->finalize (object);
+}
+
+static void
+poppler_layer_init (PopplerLayer *layer)
+{
+}
+
+static void
+poppler_layer_class_init (PopplerLayerClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = poppler_layer_finalize;
+}
+
+PopplerLayer *
+_poppler_layer_new (PopplerDocument *document,
+		    Layer           *layer,
+		    GList           *rbgroup)
+{
+  PopplerLayer *poppler_layer;
+
+  g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), NULL);
+  g_return_val_if_fail (layer != NULL, NULL);
+
+  poppler_layer = POPPLER_LAYER (g_object_new (POPPLER_TYPE_LAYER, NULL));
+
+  poppler_layer->document = (PopplerDocument *)g_object_ref (document);
+  poppler_layer->layer = layer;
+  poppler_layer->rbgroup = rbgroup;
+  poppler_layer->title = _poppler_goo_string_to_utf8 (layer->oc->getName ());
+  
+  return poppler_layer;
+}
+
+/**
+ * poppler_layer_get_title
+ * @layer: a #PopplerLayer
+ *
+ * Returns the name of the layer suitable for
+ * presentation as a title in a viewer's GUI
+ *
+ * Return value: a string containing the title of the layer
+ **/
+const gchar *
+poppler_layer_get_title (PopplerLayer *poppler_layer)
+{
+  g_return_val_if_fail (POPPLER_IS_LAYER (poppler_layer), NULL);
+
+  return poppler_layer->title;
+}
+
+/**
+ * poppler_layer_is_visible
+ * @layer: a #PopplerLayer
+ *
+ * Returns whether @layer is visible
+ *
+ * Return value: %TRUE if @layer is visible
+ **/
+gboolean
+poppler_layer_is_visible (PopplerLayer *poppler_layer)
+{
+  g_return_val_if_fail (POPPLER_IS_LAYER (poppler_layer), FALSE);
+
+  return poppler_layer->layer->oc->getState () == OptionalContentGroup::On;
+}
+
+/**
+ * poppler_layer_show
+ * @layer: a #PopplerLayer
+ *
+ * Shows @layer
+ **/
+void
+poppler_layer_show (PopplerLayer *poppler_layer)
+{
+  GList *l;
+  Layer *layer;
+  
+  g_return_if_fail (POPPLER_IS_LAYER (poppler_layer));
+
+  layer = poppler_layer->layer;
+
+  if (layer->oc->getState () == OptionalContentGroup::On)
+    return;
+  
+  layer->oc->setState (OptionalContentGroup::On);
+  
+  for (l = poppler_layer->rbgroup; l && l->data; l = g_list_next (l)) {
+    OptionalContentGroup *oc = (OptionalContentGroup *)l->data;
+
+    if (oc != layer->oc)
+      oc->setState (OptionalContentGroup::Off);
+  }
+}
+
+/**
+ * poppler_layer_hide
+ * @layer: a #PopplerLayer
+ *
+ * Hides @layer. If @layer is the parent of other nested layers,
+ * such layers will be also hidden and will be blocked until @layer
+ * is shown again
+ **/
+void
+poppler_layer_hide (PopplerLayer *poppler_layer)
+{
+  Layer *layer;
+  
+  g_return_if_fail (POPPLER_IS_LAYER (poppler_layer));
+
+  layer = poppler_layer->layer;
+  
+  if (layer->oc->getState () == OptionalContentGroup::Off)
+    return;
+  
+  layer->oc->setState (OptionalContentGroup::Off);
+}
+
+
+/**
+ * poppler_layer_is_parent
+ *
+ * Returns whether @layer is parent of other nested layers.
+ *
+ * Return value: %TRUE if @layer is a parent layer
+ **/
+gboolean
+poppler_layer_is_parent (PopplerLayer *poppler_layer)
+{
+  g_return_val_if_fail (POPPLER_IS_LAYER (poppler_layer), FALSE);
+
+  return poppler_layer->layer->kids != NULL;
+}
+
+/**
+ * poppler_layer_get_radio_button_group_id
+ *
+ * Returns the numeric ID the radio button group associated with @layer.
+ *
+ * Return value: the ID of the radio button group associated with @layer,
+ * or 0 if the layer is not associated to any radio button group
+ **/
+gint 
+poppler_layer_get_radio_button_group_id (PopplerLayer *poppler_layer)
+{
+  g_return_val_if_fail (POPPLER_IS_LAYER (poppler_layer), FALSE);
+
+  return GPOINTER_TO_INT (poppler_layer->rbgroup);
+}
diff --git a/glib/poppler-layer.h b/glib/poppler-layer.h
new file mode 100644
index 0000000..2434e27
--- /dev/null
+++ b/glib/poppler-layer.h
@@ -0,0 +1,43 @@
+/* poppler-layer.h: glib interface to poppler
+ *
+ * Copyright (C) 2008 Carlos Garcia Campos <carlosgc at gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __POPPLER_LAYER_H__
+#define __POPPLER_LAYER_H__
+
+#include <glib-object.h>
+#include "poppler.h"
+
+G_BEGIN_DECLS
+
+#define POPPLER_TYPE_LAYER    (poppler_layer_get_type ())
+#define POPPLER_LAYER(obj)    (G_TYPE_CHECK_INSTANCE_CAST ((obj), POPPLER_TYPE_LAYER, PopplerLayer))
+#define POPPLER_IS_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POPPLER_TYPE_LAYER))
+
+GType        poppler_layer_get_type                  (void) G_GNUC_CONST;
+
+const gchar *poppler_layer_get_title                 (PopplerLayer *layer);
+gboolean     poppler_layer_is_visible                (PopplerLayer *layer);
+void         poppler_layer_show                      (PopplerLayer *layer);
+void         poppler_layer_hide                      (PopplerLayer *layer);
+gboolean     poppler_layer_is_parent                 (PopplerLayer *layer);
+gint         poppler_layer_get_radio_button_group_id (PopplerLayer *layer);
+
+G_END_DECLS
+
+#endif /* __POPPLER_LAYER_H__ */
diff --git a/glib/poppler-private.h b/glib/poppler-private.h
index 539714c..663b8e8 100644
--- a/glib/poppler-private.h
+++ b/glib/poppler-private.h
@@ -10,6 +10,7 @@
 #include <FontInfo.h>
 #include <TextOutputDev.h>
 #include <Catalog.h>
+#include <OptionalContent.h>
 
 #if defined (HAVE_CAIRO)
 #include <CairoOutputDev.h>
@@ -22,6 +23,8 @@ struct _PopplerDocument
   GObject parent_instance;
   PDFDoc *doc;
 
+  GList *layers;
+  GList *layers_rbgroups;
 #if defined (HAVE_CAIRO)
   CairoOutputDev *output_dev;
 #elif defined (HAVE_SPLASH)
@@ -68,12 +71,30 @@ struct _PopplerFormField
   FormWidget *widget;
 };
 
+typedef struct _Layer {
+  GList *kids;
+  gchar *label;
+  OptionalContentGroup *oc;
+} Layer;
+
+struct _PopplerLayer
+{
+  GObject parent_instance;
+  PopplerDocument *document;
+  Layer *layer;
+  GList *rbgroup;
+  gchar *title;
+};
+
 PopplerPage   *_poppler_page_new   (PopplerDocument *document,
 				    Page            *page,
 				    int              index);
 PopplerAction *_poppler_action_new (PopplerDocument *document,
 				    LinkAction      *link,
 				    const gchar     *title);
+PopplerLayer  *_poppler_layer_new (PopplerDocument  *document,
+				   Layer            *layer,
+				   GList            *rbgroup);
 PopplerDest   *_poppler_dest_new_goto (PopplerDocument *document,
 				       LinkDest        *link_dest);
 PopplerFormField *_poppler_form_field_new (PopplerDocument *document,
diff --git a/glib/poppler.h b/glib/poppler.h
index 88a330c..d554093 100644
--- a/glib/poppler.h
+++ b/glib/poppler.h
@@ -82,6 +82,7 @@ typedef enum
 typedef struct _PopplerDocument         PopplerDocument;
 typedef struct _PopplerIndexIter        PopplerIndexIter;
 typedef struct _PopplerFontsIter        PopplerFontsIter;
+typedef struct _PopplerLayersIter       PopplerLayersIter;
 typedef struct _PopplerRectangle        PopplerRectangle;
 typedef struct _PopplerColor            PopplerColor;
 typedef struct _PopplerLinkMapping      PopplerLinkMapping;
@@ -91,6 +92,7 @@ typedef struct _PopplerFormFieldMapping PopplerFormFieldMapping;
 typedef struct _PopplerAnnotMapping     PopplerAnnotMapping;
 typedef struct _PopplerPage             PopplerPage;
 typedef struct _PopplerFontInfo         PopplerFontInfo;
+typedef struct _PopplerLayer            PopplerLayer;
 typedef struct _PopplerPSFile           PopplerPSFile;
 typedef union  _PopplerAction           PopplerAction;
 typedef struct _PopplerDest             PopplerDest;
@@ -117,6 +119,7 @@ G_END_DECLS
 #include "poppler-features.h"
 #include "poppler-document.h"
 #include "poppler-page.h"
+#include "poppler-layer.h"
 #include "poppler-action.h"
 #include "poppler-form-field.h"
 #include "poppler-enums.h"
diff --git a/glib/test-poppler-glib.cc b/glib/test-poppler-glib.cc
index 592a7c7..bd33f91 100644
--- a/glib/test-poppler-glib.cc
+++ b/glib/test-poppler-glib.cc
@@ -46,13 +46,52 @@ print_index (PopplerIndexIter *iter, gint deph)
       poppler_action_free (action);
       child = poppler_index_iter_get_child (iter);
       if (child)
-	      print_index (child, deph + 1);
+        print_index (child, deph + 1);
       poppler_index_iter_free (child);
     }
   while (poppler_index_iter_next (iter));
 }
 
 static void
+print_layers (PopplerLayersIter *iter, gint deph)
+{
+  do
+    {
+      PopplerLayersIter *child;
+      PopplerLayer      *layer;
+      gint               i;
+
+      for (i = 0; i < deph; i++)
+        g_print (" ");
+
+      layer = poppler_layers_iter_get_layer (iter);
+      if (layer)
+        {
+	  g_print ("+ %s (%s)\n", poppler_layer_get_title (layer),
+		   poppler_layer_is_visible (layer) ?
+		   "Visible" : "Hidden");
+	  g_object_unref (layer);
+	}
+      
+      child = poppler_layers_iter_get_child (iter);
+      if (child)
+        {
+	  gchar *title;
+
+	  title = poppler_layers_iter_get_title (iter);
+	  if (title)
+	    {
+	      g_print ("+ %s\n", title);
+	      g_free (title);
+	    }
+	  print_layers (child, deph + 1);
+	}
+      poppler_layers_iter_free (child);
+    }
+  while (poppler_layers_iter_next (iter));
+}
+ 
+static void
 print_document_info (PopplerDocument *document)
 {
   gchar *title, *format, *author, *subject, *keywords, *creator, *producer, *linearized;
@@ -344,6 +383,7 @@ int main (int argc, char *argv[])
   gint num_images;
   gint num_forms;
   gint form_id = 0;
+  PopplerLayersIter *layers_iter;
 
   if (argc != 3)
     FAIL ("usage: test-poppler-glib file://FILE PAGE");
@@ -540,6 +580,16 @@ int main (int argc, char *argv[])
   else
     g_print ("\tNo attachments found\n");
 
+  layers_iter = poppler_layers_iter_new (document);
+  if (layers_iter)
+    {
+      g_print ("\tLayers:\n");
+      print_layers (layers_iter, 0);
+      poppler_layers_iter_free (layers_iter);
+    }
+  else
+    g_print ("\tNo layers found\n");
+
   g_object_unref (G_OBJECT (page));
   g_object_unref (G_OBJECT (document));
 
commit c674566f458b54097f21aae0d4bf8637146565c5
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Sun Oct 26 19:42:53 2008 +0100

    Fix memory leaks in OptionalContent

diff --git a/poppler/OptionalContent.cc b/poppler/OptionalContent.cc
index 4a3d4a9..f59b16a 100644
--- a/poppler/OptionalContent.cc
+++ b/poppler/OptionalContent.cc
@@ -28,7 +28,7 @@
 //------------------------------------------------------------------------
 
 OCGs::OCGs(Object *ocgObject, XRef *xref) :
-  m_orderArray(0), m_rBGroupsArray(), m_xref(xref)
+  m_xref(xref)
 {
   // we need to parse the dictionary here, and build optionalContentGroups
   ok = gTrue;
@@ -126,17 +126,8 @@ OCGs::OCGs(Object *ocgObject, XRef *xref) :
   }
   off.free();
 
-  Object order;
   defaultOcgConfig.dictLookup("Order", &order);
-  if ( (order.isArray()) && (order.arrayGetLength() > 0) ) {
-    m_orderArray = order.getArray();
-  }
-
-  Object rbgroups;
   defaultOcgConfig.dictLookup("RBGroups", &rbgroups);
-  if ( (rbgroups.isArray()) && (rbgroups.arrayGetLength() > 0) ) {
-    m_rBGroupsArray = rbgroups.getArray();
-  }
 
   ocgList.free();
   defaultOcgConfig.free();
@@ -145,6 +136,8 @@ OCGs::OCGs(Object *ocgObject, XRef *xref) :
 OCGs::~OCGs()
 {
   deleteGooList(optionalContentGroups, OptionalContentGroup);
+  order.free();
+  rbgroups.free();
 }
 
 
diff --git a/poppler/OptionalContent.h b/poppler/OptionalContent.h
index b6136c3..90fded2 100644
--- a/poppler/OptionalContent.h
+++ b/poppler/OptionalContent.h
@@ -40,8 +40,10 @@ public:
 
   OptionalContentGroup* findOcgByRef( const Ref &ref);
 
-  Array* getOrderArray() const { return m_orderArray; }
-  Array* getRBGroupsArray() const { return m_rBGroupsArray; }
+  Array* getOrderArray() 
+    { return (order.isArray() && order.arrayGetLength() > 0) ? order.getArray() : NULL; }
+  Array* getRBGroupsArray() 
+    { return (rbgroups.isArray() && rbgroups.arrayGetLength()) ? rbgroups.getArray() : NULL; }
 
   bool optContentIsVisible( Object *dictRef );
 
@@ -55,8 +57,8 @@ private:
 
   GooList *optionalContentGroups;
 
-  Array *m_orderArray;
-  Array *m_rBGroupsArray;
+  Object order;
+  Object rbgroups;
   XRef *m_xref;
 };
 
commit 06ca313b8ecb8abb8dec3b418d118525b7bb0fdf
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Sun Oct 26 19:11:45 2008 +0100

    Check annotation optional content properties before drawing it

diff --git a/poppler/Annot.cc b/poppler/Annot.cc
index 6e35b2a..76a7cb1 100644
--- a/poppler/Annot.cc
+++ b/poppler/Annot.cc
@@ -957,13 +957,12 @@ void Annot::initialize(XRef *xrefA, Dict *dict, Catalog *catalog) {
     treeKey = 0;
   }
   obj1.free();
-  
-  if (dict->lookup("OC", &obj1)->isDict()) {
-    optionalContent = new OCGs(&obj1, xrefA);
-  } else {
-    optionalContent = NULL;
+
+  optContentConfig = catalog->getOptContentConfig();
+  dict->lookupNF("OC", &oc);
+  if (!oc.isRef() && !oc.isNull()) {
+    error (-1, "Annotation OC value not null or dict: %i", oc.getType());
   }
-  obj1.free();
 }
 
 double Annot::getXMin() {
@@ -1013,8 +1012,7 @@ Annot::~Annot() {
   if (color)
     delete color;
 
-  if (optionalContent)
-    delete optionalContent;
+  oc.free();
 }
 
 // Set the current fill or stroke color, based on <a> (which should
@@ -1149,6 +1147,12 @@ void Annot::draw(Gfx *gfx, GBool printing) {
     return;
   }
 
+  // check the OC
+  if (optContentConfig && oc.isRef()) {
+    if (! optContentConfig->optContentIsVisible(&oc))
+      return;
+  }
+
   // draw the appearance stream
   appearance.fetch(xref, &obj);
   gfx->drawAnnot(&obj, (type == typeLink) ? border : (AnnotBorder *)NULL, color,
diff --git a/poppler/Annot.h b/poppler/Annot.h
index ebd3e74..6dd7850 100644
--- a/poppler/Annot.h
+++ b/poppler/Annot.h
@@ -509,7 +509,6 @@ public:
   AnnotBorder *getBorder() const { return border; }
   AnnotColor *getColor() const { return color; }
   int getTreeKey() const { return treeKey; }
-  OCGs *getOptionalContent() const { return optionalContent; }
 
   int getId() { return ref.num; }
 
@@ -542,7 +541,8 @@ protected:
                          //   for the normal appearance
   GooString *appearState;           // AS
   int treeKey;                      // Struct Parent;
-  OCGs *optionalContent;            // OC
+  OCGs *optContentConfig;           // Optional content config
+  Object oc;                        // OC
 
   XRef *xref;			// the xref table for this PDF file
   Ref ref;                      // object ref identifying this annotation
commit a6d58927b048aa043cb6b6ed3ee9aeb213578924
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Sat Nov 8 20:50:40 2008 +0100

    Do not show hidden optional content
    
    Before any draw operation we first check whether we are inside an
    optional marked content element that is currently hidden.

diff --git a/poppler/Gfx.cc b/poppler/Gfx.cc
index be2f19a..871feb3 100644
--- a/poppler/Gfx.cc
+++ b/poppler/Gfx.cc
@@ -496,6 +496,7 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, Catalog *cata
   subPage = gFalse;
   printCommands = globalParams->getPrintCommands();
   profileCommands = globalParams->getProfileCommands();
+  mcStack = NULL;
 
   // start the resource stack
   res = new GfxResources(xref, resDict, NULL);
@@ -539,6 +540,7 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict, Catalog *catalogA,
   catalog = catalogA;
   subPage = gTrue;
   printCommands = globalParams->getPrintCommands();
+  mcStack = NULL;
 
   // start the resource stack
   res = new GfxResources(xref, resDict, NULL);
@@ -582,6 +584,9 @@ Gfx::~Gfx() {
   if (state) {
     delete state;
   }
+  while (mcStack) {
+    popMarkedContent();
+  }
 }
 
 void Gfx::display(Object *obj, GBool topLevel) {
@@ -1562,7 +1567,7 @@ void Gfx::opStroke(Object args[], int numArgs) {
     //error(getPos(), "No path in stroke");
     return;
   }
-  if (state->isPath()) {
+  if (state->isPath() && !contentIsHidden()) {
     if (state->getStrokeColorSpace()->getMode() == csPattern) {
       doPatternStroke();
     } else {
@@ -1578,7 +1583,7 @@ void Gfx::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) {
     return;
   }
   state->closePath();
-  if (state->isPath()) {
+  if (state->isPath() && !contentIsHidden()) {
     if (state->getStrokeColorSpace()->getMode() == csPattern) {
       doPatternStroke();
     } else {
@@ -1593,7 +1598,7 @@ void Gfx::opFill(Object args[], int numArgs) {
     //error(getPos(), "No path in fill");
     return;
   }
-  if (state->isPath()) {
+  if (state->isPath() && !contentIsHidden()) {
     if (state->getFillColorSpace()->getMode() == csPattern) {
       doPatternFill(gFalse);
     } else {
@@ -1608,7 +1613,7 @@ void Gfx::opEOFill(Object args[], int numArgs) {
     //error(getPos(), "No path in eofill");
     return;
   }
-  if (state->isPath()) {
+  if (state->isPath() && !contentIsHidden()) {
     if (state->getFillColorSpace()->getMode() == csPattern) {
       doPatternFill(gTrue);
     } else {
@@ -1623,7 +1628,7 @@ void Gfx::opFillStroke(Object args[], int numArgs) {
     //error(getPos(), "No path in fill/stroke");
     return;
   }
-  if (state->isPath()) {
+  if (state->isPath() && !contentIsHidden()) {
     if (state->getFillColorSpace()->getMode() == csPattern) {
       doPatternFill(gFalse);
     } else {
@@ -1643,7 +1648,7 @@ void Gfx::opCloseFillStroke(Object args[], int numArgs) {
     //error(getPos(), "No path in closepath/fill/stroke");
     return;
   }
-  if (state->isPath()) {
+  if (state->isPath() && !contentIsHidden()) {
     state->closePath();
     if (state->getFillColorSpace()->getMode() == csPattern) {
       doPatternFill(gFalse);
@@ -1664,7 +1669,7 @@ void Gfx::opEOFillStroke(Object args[], int numArgs) {
     //error(getPos(), "No path in eofill/stroke");
     return;
   }
-  if (state->isPath()) {
+  if (state->isPath() && !contentIsHidden()) {
     if (state->getFillColorSpace()->getMode() == csPattern) {
       doPatternFill(gTrue);
     } else {
@@ -1684,7 +1689,7 @@ void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
     //error(getPos(), "No path in closepath/eofill/stroke");
     return;
   }
-  if (state->isPath()) {
+  if (state->isPath() && !contentIsHidden()) {
     state->closePath();
     if (state->getFillColorSpace()->getMode() == csPattern) {
       doPatternFill(gTrue);
@@ -1911,10 +1916,12 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
   if (out->useTilingPatternFill()) {
     m1[4] = m[4];
     m1[5] = m[5];
-    out->tilingPatternFill(state, tPat->getContentStream(),
-			   tPat->getPaintType(), tPat->getResDict(),
-			   m1, tPat->getBBox(),
-			   xi0, yi0, xi1, yi1, xstep, ystep);
+    if (!contentIsHidden()) {
+      out->tilingPatternFill(state, tPat->getContentStream(),
+			     tPat->getPaintType(), tPat->getResDict(),
+			     m1, tPat->getBBox(),
+			     xi0, yi0, xi1, yi1, xstep, ystep);
+    }
   } else {
     for (yi = yi0; yi < yi1; ++yi) {
       for (xi = xi0; xi < xi1; ++xi) {
@@ -1983,7 +1990,8 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
   if (shading->getHasBackground()) {
     state->setFillColor(shading->getBackground());
     out->updateFillColor(state);
-    out->fill(state);
+    if (!contentIsHidden())
+      out->fill(state);
   }
   state->clearPath();
 
@@ -2196,7 +2204,8 @@ void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
     state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
 		  x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
     state->closePath();
-    out->fill(state);
+    if (!contentIsHidden())
+      out->fill(state);
     state->clearPath();
 
   // the four corner colors are not close enough -- subdivide the
@@ -2485,7 +2494,8 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
     state->lineTo(vx1, vy1);
     state->lineTo(ux1, uy1);
     state->closePath();
-    out->fill(state);
+    if (!contentIsHidden())
+      out->fill(state);
     state->clearPath();
 
     // set up for next region
@@ -2761,7 +2771,8 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
     }
 
     // fill the path
-    out->fill(state);
+    if (!contentIsHidden())
+      out->fill(state);
     state->clearPath();
 
     // step to the next value of t
@@ -2798,7 +2809,8 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
 	state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
       }
       state->closePath();
-      out->fill(state);
+      if (!contentIsHidden())
+        out->fill(state);
       state->clearPath();
     }
 
@@ -2830,7 +2842,8 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
 	state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
       }
       state->closePath();
-      out->fill(state);
+      if (!contentIsHidden())
+        out->fill(state);
       state->clearPath();
     }
   }
@@ -2871,7 +2884,8 @@ void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
     state->lineTo(x1, y1);
     state->lineTo(x2, y2);
     state->closePath();
-    out->fill(state);
+    if (!contentIsHidden())
+      out->fill(state);
     state->clearPath();
   } else {
     x01 = 0.5 * (x0 + x1);
@@ -2951,7 +2965,8 @@ void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
 		   patch->x[1][0], patch->y[1][0],
 		   patch->x[0][0], patch->y[0][0]);
     state->closePath();
-    out->fill(state);
+    if (!contentIsHidden())
+      out->fill(state);
     state->clearPath();
   } else {
     for (i = 0; i < 4; ++i) {
@@ -3411,8 +3426,10 @@ void Gfx::doShowText(GooString *s) {
       originX *= state->getFontSize();
       originY *= state->getFontSize();
       state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
-      out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
-		    tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
+      if (!contentIsHidden()) {
+        out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
+		      tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
+      }
       state->shift(tdx, tdy);
       p += n;
       len -= n;
@@ -3449,7 +3466,8 @@ void Gfx::doShowText(GooString *s) {
       dy *= state->getFontSize();
     }
     state->textTransformDelta(dx, dy, &tdx, &tdy);
-    out->drawString(state, s);
+    if (!contentIsHidden())
+      out->drawString(state, s);
     state->shift(tdx, tdy);
   }
 
@@ -3643,7 +3661,8 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
     obj1.free();
 
     // draw it
-    out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
+    if (!contentIsHidden())
+      out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
 
   } else {
 
@@ -3833,13 +3852,15 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
 
     // draw it
     if (haveSoftMask) {
-      out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
-			       maskStr, maskWidth, maskHeight, maskColorMap);
+      if (!contentIsHidden()) {
+        out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
+				 maskStr, maskWidth, maskHeight, maskColorMap);
+      }
       delete maskColorMap;
-    } else if (haveExplicitMask) {
+    } else if (haveExplicitMask && !contentIsHidden ()) {
       out->drawMaskedImage(state, ref, str, width, height, colorMap,
 			   maskStr, maskWidth, maskHeight, maskInvert);
-    } else {
+    } else if (!contentIsHidden()) {
       out->drawImage(state, ref, str, width, height, colorMap,
 		     haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
     }
@@ -4153,10 +4174,33 @@ void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
 // marked content operators
 //------------------------------------------------------------------------
 
+struct MarkedContentStack {
+  GBool ocSuppressed;       // are we ignoring content based on OptionalContent?
+  MarkedContentStack *next; // next object on stack
+};
+
+void Gfx::popMarkedContent() {
+  MarkedContentStack *mc = mcStack;
+  mcStack = mc->next;
+  delete mc;
+}
+
+void Gfx::pushMarkedContent() {
+  MarkedContentStack *mc = new MarkedContentStack();
+  mc->ocSuppressed = gFalse;
+  mc->next = mcStack;
+  mcStack = mc;
+}
+
+GBool Gfx::contentIsHidden() {
+  return mcStack && mcStack->ocSuppressed;
+}
+
 void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
-  // TODO: we really need to be adding this to the markedContentStack
+  // push a new stack entry
+  pushMarkedContent();
+  
   OCGs *contentConfig = catalog->getOptContentConfig();
-	
   char* name0 = args[0].getName();
   if ( strncmp( name0, "OC", 2) == 0 && contentConfig) {
     if ( numArgs >= 2 ) {
@@ -4168,7 +4212,8 @@ void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
       if ( res->lookupMarkedContentNF( name1, &markedContent ) ) {
 	if ( markedContent.isRef() ) {
 	  bool visible = contentConfig->optContentIsVisible( &markedContent );
-	  ocSuppressed = !(visible);
+	  MarkedContentStack *mc = mcStack;
+	  mc->ocSuppressed = !(visible);
        }
       } else {
 	error(getPos(), "DID NOT find %s", name1);
@@ -4178,7 +4223,6 @@ void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
     }
   }
 
-
   if (printCommands) {
     printf("  marked content: %s ", args[0].getName());
     if (numArgs == 2)
@@ -4195,8 +4239,9 @@ void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
 }
 
 void Gfx::opEndMarkedContent(Object args[], int numArgs) {
-  // TODO: we should turn this off based on the markedContentStack
-  ocSuppressed = false;
+  // pop the stack
+  if (mcStack)
+    popMarkedContent();
   out->endMarkedContent(state);
 }
 
diff --git a/poppler/Gfx.h b/poppler/Gfx.h
index 2dc9315..b14c5a2 100644
--- a/poppler/Gfx.h
+++ b/poppler/Gfx.h
@@ -61,6 +61,7 @@ class PDFRectangle;
 class AnnotBorder;
 class AnnotColor;
 class Catalog;
+struct MarkedContentStack;
 
 //------------------------------------------------------------------------
 
@@ -171,7 +172,6 @@ private:
   GBool profileCommands;	// profile the drawing commands (for debugging)
   GfxResources *res;		// resource stack
   int updateLevel;
-  GBool ocSuppressed;		// are we ignoring content based on OptionalContent?
 
   GfxState *state;		// current graphics state
   GBool fontChanged;		// set if font or text matrix has changed
@@ -181,7 +181,7 @@ private:
 				//   page/form/pattern
   int formDepth;
 
-  GooList *markedContentStack;	// current BMC/EMC stack
+  MarkedContentStack *mcStack;	// current BMC/EMC stack
 
   Parser *parser;		// parser for page content stream(s)
 
@@ -329,6 +329,9 @@ private:
   void opBeginMarkedContent(Object args[], int numArgs);
   void opEndMarkedContent(Object args[], int numArgs);
   void opMarkPoint(Object args[], int numArgs);
+  GBool contentIsHidden();
+  void pushMarkedContent();
+  void popMarkedContent();
 
   void pushResources(Dict *resDict);
   void popResources();
commit 5f168f991477e291290350a28f4a60a565e187b9
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Mon Oct 20 17:56:11 2008 +0200

    [glib] Show action titles when printing the index in test program

diff --git a/glib/test-poppler-glib.cc b/glib/test-poppler-glib.cc
index 1e4f1cd..592a7c7 100644
--- a/glib/test-poppler-glib.cc
+++ b/glib/test-poppler-glib.cc
@@ -31,19 +31,22 @@ poppler_format_date (GTime utime)
 }
 
 static void
-print_index (PopplerIndexIter *iter)
+print_index (PopplerIndexIter *iter, gint deph)
 {
   do
     {
-      PopplerAction *action;
+      PopplerAction    *action;
       PopplerIndexIter *child;
+      int               i;
 
       action = poppler_index_iter_get_action (iter);
-      g_print ("Action: %d\n", action->type);
+      for (i = 0; i < deph; i++)
+        g_print (" ");
+      g_print ("+ %s\n", action->any.title);
       poppler_action_free (action);
       child = poppler_index_iter_get_child (iter);
       if (child)
-	print_index (child);
+	      print_index (child, deph + 1);
       poppler_index_iter_free (child);
     }
   while (poppler_index_iter_next (iter));
@@ -127,7 +130,7 @@ print_document_info (PopplerDocument *document)
   if (index_iter)
     {
       g_print ("\tindex:\n");
-      print_index (index_iter);
+      print_index (index_iter, 0);
       poppler_index_iter_free (index_iter);
     }
 
commit a906d12e16748d9a40c0db4043a576fd3d004341
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Sat Nov 8 20:20:00 2008 +0100

    Minor code cleanup and consistency issues

diff --git a/poppler/OptionalContent.cc b/poppler/OptionalContent.cc
index ffffd6f..4a3d4a9 100644
--- a/poppler/OptionalContent.cc
+++ b/poppler/OptionalContent.cc
@@ -51,7 +51,7 @@ OCGs::OCGs(Object *ocgObject, XRef *xref) :
       ocg.free();
       break;
     }
-    OptionalContentGroup *thisOptionalContentGroup = new OptionalContentGroup(ocg.getDict(), xref);
+    OptionalContentGroup *thisOptionalContentGroup = new OptionalContentGroup(ocg.getDict());
     ocg.free();
     ocgList.arrayGetNF(i, &ocg);
     // TODO: we should create a lookup map from Ref to the OptionalContentGroup
@@ -159,7 +159,7 @@ OptionalContentGroup* OCGs::findOcgByRef( const Ref &ref)
   OptionalContentGroup *ocg = NULL;
   for (int i=0; i < optionalContentGroups->getLength(); ++i) {
     ocg = (OptionalContentGroup*)optionalContentGroups->get(i);
-    if ( (ocg->ref().num == ref.num) && (ocg->ref().gen == ref.gen) ) {
+    if ( (ocg->getRef().num == ref.num) && (ocg->getRef().gen == ref.gen) ) {
       return ocg;
     }
   }
@@ -206,7 +206,7 @@ bool OCGs::optContentIsVisible( Object *dictRef )
       }
     } else if (ocg.isRef()) {
       OptionalContentGroup* oc = findOcgByRef( ocg.getRef() );      
-      if ( !oc || oc->state() == OptionalContentGroup::Off ) {
+      if ( !oc || oc->getState() == OptionalContentGroup::Off ) {
 	result = false;
       } else {
 	result = true ;
@@ -216,7 +216,7 @@ bool OCGs::optContentIsVisible( Object *dictRef )
     policy.free();
   } else if ( dictType.isName("OCG") ) {
     OptionalContentGroup* oc = findOcgByRef( dictRef->getRef() );
-    if ( !oc || oc->state() == OptionalContentGroup::Off ) {
+    if ( !oc || oc->getState() == OptionalContentGroup::Off ) {
       result=false;
     }
   }
@@ -233,7 +233,7 @@ bool OCGs::allOn( Array *ocgArray )
     ocgArray->getNF(i, &ocgItem);
     if (ocgItem.isRef()) {
       OptionalContentGroup* oc = findOcgByRef( ocgItem.getRef() );      
-      if ( oc && oc->state() == OptionalContentGroup::Off ) {
+      if ( oc && oc->getState() == OptionalContentGroup::Off ) {
 	return false;
       }
     }
@@ -248,7 +248,7 @@ bool OCGs::allOff( Array *ocgArray )
     ocgArray->getNF(i, &ocgItem);
     if (ocgItem.isRef()) {
       OptionalContentGroup* oc = findOcgByRef( ocgItem.getRef() );      
-      if ( oc && oc->state() == OptionalContentGroup::On ) {
+      if ( oc && oc->getState() == OptionalContentGroup::On ) {
 	return false;
       }
     }
@@ -263,7 +263,7 @@ bool OCGs::anyOn( Array *ocgArray )
     ocgArray->getNF(i, &ocgItem);
     if (ocgItem.isRef()) {
       OptionalContentGroup* oc = findOcgByRef( ocgItem.getRef() );      
-      if ( oc && oc->state() == OptionalContentGroup::On ) {
+      if ( oc && oc->getState() == OptionalContentGroup::On ) {
 	return true;
       }
     }
@@ -278,7 +278,7 @@ bool OCGs::anyOff( Array *ocgArray )
     ocgArray->getNF(i, &ocgItem);
     if (ocgItem.isRef()) {
       OptionalContentGroup* oc = findOcgByRef( ocgItem.getRef() );      
-      if ( oc && oc->state() == OptionalContentGroup::Off ) {
+      if ( oc && oc->getState() == OptionalContentGroup::Off ) {
 	return true;
       }
     }
@@ -288,7 +288,7 @@ bool OCGs::anyOff( Array *ocgArray )
 
 //------------------------------------------------------------------------
 
-OptionalContentGroup::OptionalContentGroup(Dict *ocgDict, XRef *xrefA) : m_name(NULL)
+OptionalContentGroup::OptionalContentGroup(Dict *ocgDict) : m_name(NULL)
 {
   Object ocgName;
   ocgDict->lookup("Name", &ocgName);
@@ -306,7 +306,7 @@ OptionalContentGroup::OptionalContentGroup(GooString *label)
   m_state = On;
 }
 
-GooString* OptionalContentGroup::name() const
+GooString* OptionalContentGroup::getName() const
 {
   return m_name;
 }
@@ -316,7 +316,7 @@ void OptionalContentGroup::setRef(const Ref ref)
   m_ref = ref;
 }
 
-Ref OptionalContentGroup::ref() const
+Ref OptionalContentGroup::getRef() const
 {
   return m_ref;
 }
diff --git a/poppler/OptionalContent.h b/poppler/OptionalContent.h
index 0ffbc44..b6136c3 100644
--- a/poppler/OptionalContent.h
+++ b/poppler/OptionalContent.h
@@ -66,18 +66,18 @@ class OptionalContentGroup {
 public:
   enum State { On, Off };
 
-  OptionalContentGroup(Dict *dict, XRef *xrefA);
+  OptionalContentGroup(Dict *dict);
 
   OptionalContentGroup(GooString *label);
 
   ~OptionalContentGroup();
 
-  GooString* name() const;
+  GooString* getName() const;
 
-  Ref ref() const;
+  Ref getRef() const;
   void setRef(const Ref ref);
 
-  State state() { return m_state; };
+  State getState() { return m_state; };
   void setState(State state) { m_state = state; };
 
 private:
diff --git a/qt4/src/poppler-optcontent.cc b/qt4/src/poppler-optcontent.cc
index af916ab..ecbc23a 100644
--- a/qt4/src/poppler-optcontent.cc
+++ b/qt4/src/poppler-optcontent.cc
@@ -73,8 +73,8 @@ namespace Poppler
   {
     m_group = group;
     m_parent = 0;
-    m_name = UnicodeParsedString( group->name() );
-    if ( group->state() == OptionalContentGroup::On ) {
+    m_name = UnicodeParsedString( group->getName() );
+    if ( group->getState() == OptionalContentGroup::On ) {
       m_state = OptContentItem::On;
     } else {
       m_state = OptContentItem::Off;
@@ -162,7 +162,7 @@ namespace Poppler
     for (int i = 0; i < ocgs->getLength(); ++i) {
       OptionalContentGroup *ocg = static_cast<OptionalContentGroup*>(ocgs->get(i));
       OptContentItem *node = new OptContentItem( ocg );
-      m_optContentItems.insert( QString::number(ocg->ref().num), node);
+      m_optContentItems.insert( QString::number(ocg->getRef().num), node);
     }
 
     if ( optContent->getOrderArray() == 0 ) {
diff --git a/qt4/tests/check_optcontent.cpp b/qt4/tests/check_optcontent.cpp
index 247c67b..ec988b8 100644
--- a/qt4/tests/check_optcontent.cpp
+++ b/qt4/tests/check_optcontent.cpp
@@ -184,8 +184,8 @@ void TestOptionalContent::checkVisibilitySetting()
     OptionalContentGroup *ocgA = ocgs->findOcgByRef( ref21 );
     QVERIFY( ocgA );
 
-    QVERIFY( (ocgA->name()->cmp("A")) == 0 );
-    QCOMPARE( ocgA->state(), OptionalContentGroup::On );
+    QVERIFY( (ocgA->getName()->cmp("A")) == 0 );
+    QCOMPARE( ocgA->getState(), OptionalContentGroup::On );
 
     Object ref28obj;
     ref28obj.initRef( 28, 0 );
@@ -193,8 +193,8 @@ void TestOptionalContent::checkVisibilitySetting()
     OptionalContentGroup *ocgB = ocgs->findOcgByRef( ref28 );
     QVERIFY( ocgB );
 
-    QVERIFY( (ocgB->name()->cmp("B")) == 0 );
-    QCOMPARE( ocgB->state(), OptionalContentGroup::On );
+    QVERIFY( (ocgB->getName()->cmp("B")) == 0 );
+    QCOMPARE( ocgB->getState(), OptionalContentGroup::On );
 
     // turn one Off
     ocgA->setState( OptionalContentGroup::Off );
commit e73c2ce906b7e1f06d641f7e0733aad6336b4091
Author: Carlos Garcia Campos <carlosgc at gnome.org>
Date:   Sun Oct 19 12:44:13 2008 +0200

    Delete the optContent object if it's invalid

diff --git a/poppler/Catalog.cc b/poppler/Catalog.cc
index c5d473f..95476b5 100644
--- a/poppler/Catalog.cc
+++ b/poppler/Catalog.cc
@@ -202,6 +202,10 @@ Catalog::Catalog(XRef *xrefA) {
   // get the Optional Content dictionary
   if (catDict.dictLookup("OCProperties", &optContentProps)->isDict()) {
     optContent = new OCGs(&optContentProps, xref);
+    if (!optContent->isOk ()) {
+      delete optContent;
+      optContent = NULL;
+    }
   }
   optContentProps.free();
 
diff --git a/poppler/OptionalContent.cc b/poppler/OptionalContent.cc
index 204872b..ffffd6f 100644
--- a/poppler/OptionalContent.cc
+++ b/poppler/OptionalContent.cc
@@ -31,6 +31,7 @@ OCGs::OCGs(Object *ocgObject, XRef *xref) :
   m_orderArray(0), m_rBGroupsArray(), m_xref(xref)
 {
   // we need to parse the dictionary here, and build optionalContentGroups
+  ok = gTrue;
   optionalContentGroups = new GooList();
 
   Object ocgList;
@@ -38,6 +39,7 @@ OCGs::OCGs(Object *ocgObject, XRef *xref) :
   if (!ocgList.isArray()) {
     error(-1, "Expected the optional content group list, but wasn't able to find it, or it isn't an Array");
     ocgList.free();
+    ok = gFalse;
     return;
   }
 
@@ -66,6 +68,7 @@ OCGs::OCGs(Object *ocgObject, XRef *xref) :
     error(-1, "Expected the default config, but wasn't able to find it, or it isn't a Dictionary");
     defaultOcgConfig.free();
     ocgList.free();
+    ok = gFalse;
     return;
   }
 #if 0
diff --git a/poppler/OptionalContent.h b/poppler/OptionalContent.h
index 6afa023..0ffbc44 100644
--- a/poppler/OptionalContent.h
+++ b/poppler/OptionalContent.h
@@ -32,6 +32,9 @@ public:
   OCGs(Object *ocgObject, XRef *xref);
   ~OCGs();
 
+  // Is OCGS valid?
+  GBool isOk() { return ok; }
+  
   bool hasOCGs();
   GooList *getOCGs() const { return optionalContentGroups; }
 
@@ -43,6 +46,8 @@ public:
   bool optContentIsVisible( Object *dictRef );
 
 private:
+  GBool ok;
+  
   bool allOn( Array *ocgArray );
   bool allOff( Array *ocgArray );
   bool anyOn( Array *ocgArray );


More information about the poppler mailing list