Commit 7363c25e authored by Carlos Garcia Campos's avatar Carlos Garcia Campos

[glib] Add Optional Content support

parent c674566f
......@@ -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
......
......@@ -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
{
......
......@@ -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))
......
/* poppler-layer.cc: glib interface to poppler
*
* Copyright (C) 2008 Carlos Garcia Campos <carlosgc@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);
}
/* poppler-layer.h: glib interface to poppler
*
* Copyright (C) 2008 Carlos Garcia Campos <carlosgc@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__ */
......@@ -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,
......
......@@ -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