blob: 809fa586b1c2cb491e689d9c15df745b08c42e79 [file] [log] [blame]
/* poppler-action.cc: glib wrapper for poppler -*- c-basic-offset: 8 -*-
* Copyright (C) 2005, Red Hat, Inc.
*
* 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.h"
#include "poppler-private.h"
/**
* SECTION:poppler-action
* @short_description: Action links
* @title: PopplerAction
*/
POPPLER_DEFINE_BOXED_TYPE (PopplerDest, poppler_dest, poppler_dest_copy, poppler_dest_free)
/**
* poppler_dest_copy:
* @dest: a #PopplerDest
*
* Copies @dest, creating an identical #PopplerDest.
*
* Return value: a new destination identical to @dest
**/
PopplerDest *
poppler_dest_copy (PopplerDest *dest)
{
PopplerDest *new_dest;
new_dest = g_slice_dup (PopplerDest, dest);
if (dest->named_dest)
new_dest->named_dest = g_strdup (dest->named_dest);
return new_dest;
}
/**
* poppler_dest_free:
* @dest: a #PopplerDest
*
* Frees @dest
**/
void
poppler_dest_free (PopplerDest *dest)
{
if (!dest)
return;
if (dest->named_dest)
g_free (dest->named_dest);
g_slice_free (PopplerDest, dest);
}
static void
poppler_action_layer_free (PopplerActionLayer *action_layer)
{
if (!action_layer)
return;
if (action_layer->layers) {
g_list_foreach (action_layer->layers, (GFunc)g_object_unref, NULL);
g_list_free (action_layer->layers);
action_layer->layers = NULL;
}
g_slice_free (PopplerActionLayer, action_layer);
}
static PopplerActionLayer *
poppler_action_layer_copy (PopplerActionLayer *action_layer)
{
PopplerActionLayer *retval = g_slice_dup (PopplerActionLayer, action_layer);
retval->layers = g_list_copy (action_layer->layers);
g_list_foreach (action_layer->layers, (GFunc)g_object_ref, NULL);
return retval;
}
POPPLER_DEFINE_BOXED_TYPE (PopplerAction, poppler_action, poppler_action_copy, poppler_action_free)
/**
* poppler_action_free:
* @action: a #PopplerAction
*
* Frees @action
**/
void
poppler_action_free (PopplerAction *action)
{
if (action == NULL)
return;
/* Action specific stuff */
switch (action->type) {
case POPPLER_ACTION_GOTO_DEST:
poppler_dest_free (action->goto_dest.dest);
break;
case POPPLER_ACTION_GOTO_REMOTE:
poppler_dest_free (action->goto_remote.dest);
g_free (action->goto_remote.file_name);
break;
case POPPLER_ACTION_URI:
g_free (action->uri.uri);
break;
case POPPLER_ACTION_LAUNCH:
g_free (action->launch.file_name);
g_free (action->launch.params);
break;
case POPPLER_ACTION_NAMED:
g_free (action->named.named_dest);
break;
case POPPLER_ACTION_MOVIE:
if (action->movie.movie)
g_object_unref (action->movie.movie);
break;
case POPPLER_ACTION_RENDITION:
if (action->rendition.media)
g_object_unref (action->rendition.media);
break;
case POPPLER_ACTION_OCG_STATE:
if (action->ocg_state.state_list) {
g_list_foreach (action->ocg_state.state_list, (GFunc)poppler_action_layer_free, NULL);
g_list_free (action->ocg_state.state_list);
}
break;
case POPPLER_ACTION_JAVASCRIPT:
if (action->javascript.script)
g_free (action->javascript.script);
break;
default:
break;
}
g_free (action->any.title);
g_slice_free (PopplerAction, action);
}
/**
* poppler_action_copy:
* @action: a #PopplerAction
*
* Copies @action, creating an identical #PopplerAction.
*
* Return value: a new action identical to @action
**/
PopplerAction *
poppler_action_copy (PopplerAction *action)
{
PopplerAction *new_action;
g_return_val_if_fail (action != NULL, NULL);
/* Do a straight copy of the memory */
new_action = g_slice_dup (PopplerAction, action);
if (action->any.title != NULL)
new_action->any.title = g_strdup (action->any.title);
switch (action->type) {
case POPPLER_ACTION_GOTO_DEST:
new_action->goto_dest.dest = poppler_dest_copy (action->goto_dest.dest);
break;
case POPPLER_ACTION_GOTO_REMOTE:
new_action->goto_remote.dest = poppler_dest_copy (action->goto_remote.dest);
if (action->goto_remote.file_name)
new_action->goto_remote.file_name = g_strdup (action->goto_remote.file_name);
break;
case POPPLER_ACTION_URI:
if (action->uri.uri)
new_action->uri.uri = g_strdup (action->uri.uri);
break;
case POPPLER_ACTION_LAUNCH:
if (action->launch.file_name)
new_action->launch.file_name = g_strdup (action->launch.file_name);
if (action->launch.params)
new_action->launch.params = g_strdup (action->launch.params);
break;
case POPPLER_ACTION_NAMED:
if (action->named.named_dest)
new_action->named.named_dest = g_strdup (action->named.named_dest);
break;
case POPPLER_ACTION_MOVIE:
if (action->movie.movie)
new_action->movie.movie = (PopplerMovie *)g_object_ref (action->movie.movie);
break;
case POPPLER_ACTION_RENDITION:
if (action->rendition.media)
new_action->rendition.media = (PopplerMedia *)g_object_ref (action->rendition.media);
break;
case POPPLER_ACTION_OCG_STATE:
if (action->ocg_state.state_list) {
GList *l;
GList *new_list = NULL;
for (l = action->ocg_state.state_list; l; l = g_list_next (l)) {
PopplerActionLayer *alayer = (PopplerActionLayer *)l->data;
new_list = g_list_prepend (new_list, poppler_action_layer_copy (alayer));
}
new_action->ocg_state.state_list = g_list_reverse (new_list);
}
break;
case POPPLER_ACTION_JAVASCRIPT:
if (action->javascript.script)
new_action->javascript.script = g_strdup (action->javascript.script);
break;
default:
break;
}
return new_action;
}
PopplerDest *
dest_new_goto (PopplerDocument *document,
LinkDest *link_dest)
{
PopplerDest *dest;
dest = g_slice_new0 (PopplerDest);
if (link_dest == NULL) {
dest->type = POPPLER_DEST_UNKNOWN;
return dest;
}
switch (link_dest->getKind ()) {
case destXYZ:
dest->type = POPPLER_DEST_XYZ;
break;
case destFit:
dest->type = POPPLER_DEST_FIT;
break;
case destFitH:
dest->type = POPPLER_DEST_FITH;
break;
case destFitV:
dest->type = POPPLER_DEST_FITV;
break;
case destFitR:
dest->type = POPPLER_DEST_FITR;
break;
case destFitB:
dest->type = POPPLER_DEST_FITB;
break;
case destFitBH:
dest->type = POPPLER_DEST_FITBH;
break;
case destFitBV:
dest->type = POPPLER_DEST_FITBV;
break;
default:
dest->type = POPPLER_DEST_UNKNOWN;
}
if (link_dest->isPageRef ()) {
if (document) {
Ref page_ref = link_dest->getPageRef ();
dest->page_num = document->doc->findPage (page_ref.num, page_ref.gen);
} else {
/* FIXME: We don't keep areound the page_ref for the
* remote doc, so we can't look this up. Guess that
* it's 0*/
dest->page_num = 0;
}
} else {
dest->page_num = link_dest->getPageNum ();
}
dest->left = link_dest->getLeft ();
dest->bottom = link_dest->getBottom ();
dest->right = link_dest->getRight ();
dest->top = link_dest->getTop ();
dest->zoom = link_dest->getZoom ();
dest->change_left = link_dest->getChangeLeft ();
dest->change_top = link_dest->getChangeTop ();
dest->change_zoom = link_dest->getChangeZoom ();
if (document && dest->page_num > 0) {
PopplerPage *page;
page = poppler_document_get_page (document, dest->page_num - 1);
if (page) {
dest->left -= page->page->getCropBox ()->x1;
dest->bottom -= page->page->getCropBox ()->x1;
dest->right -= page->page->getCropBox ()->y1;
dest->top -= page->page->getCropBox ()->y1;
g_object_unref (page);
} else {
g_warning ("Invalid page %d in Link Destination\n", dest->page_num);
dest->page_num = 0;
}
}
return dest;
}
static PopplerDest *
dest_new_named (GooString *named_dest)
{
PopplerDest *dest;
dest = g_slice_new0 (PopplerDest);
if (named_dest == NULL) {
dest->type = POPPLER_DEST_UNKNOWN;
return dest;
}
dest->type = POPPLER_DEST_NAMED;
dest->named_dest = g_strdup (named_dest->getCString ());
return dest;
}
static void
build_goto_dest (PopplerDocument *document,
PopplerAction *action,
LinkGoTo *link)
{
LinkDest *link_dest;
GooString *named_dest;
/* Return if it isn't OK */
if (! link->isOk ()) {
action->goto_dest.dest = dest_new_goto (NULL, NULL);
return;
}
link_dest = link->getDest ();
named_dest = link->getNamedDest ();
if (link_dest != NULL) {
action->goto_dest.dest = dest_new_goto (document, link_dest);
} else if (named_dest != NULL) {
action->goto_dest.dest = dest_new_named (named_dest);
} else {
action->goto_dest.dest = dest_new_goto (document, NULL);
}
}
static void
build_goto_remote (PopplerAction *action,
LinkGoToR *link)
{
LinkDest *link_dest;
GooString *named_dest;
/* Return if it isn't OK */
if (! link->isOk ()) {
action->goto_remote.dest = dest_new_goto (NULL, NULL);
return;
}
action->goto_remote.file_name = _poppler_goo_string_to_utf8 (link->getFileName());
link_dest = link->getDest ();
named_dest = link->getNamedDest ();
if (link_dest != NULL) {
action->goto_remote.dest = dest_new_goto (NULL, link_dest);
} else if (named_dest != NULL) {
action->goto_remote.dest = dest_new_named (named_dest);
} else {
action->goto_remote.dest = dest_new_goto (NULL, NULL);
}
}
static void
build_launch (PopplerAction *action,
LinkLaunch *link)
{
if (link->getFileName()) {
action->launch.file_name = g_strdup (link->getFileName()->getCString ());
}
if (link->getParams()) {
action->launch.params = g_strdup (link->getParams()->getCString ());
}
}
static void
build_uri (PopplerAction *action,
LinkURI *link)
{
gchar *uri;
uri = link->getURI()->getCString ();
if (uri != NULL)
action->uri.uri = g_strdup (uri);
}
static void
build_named (PopplerAction *action,
LinkNamed *link)
{
gchar *name;
name = link->getName ()->getCString ();
if (name != NULL)
action->named.named_dest = g_strdup (name);
}
static AnnotMovie *
find_annot_movie_for_action (PopplerDocument *document,
LinkMovie *link)
{
AnnotMovie *annot = NULL;
XRef *xref = document->doc->getXRef ();
Object annotObj;
if (link->hasAnnotRef ()) {
Ref *ref = link->getAnnotRef ();
xref->fetch (ref->num, ref->gen, &annotObj);
} else if (link->hasAnnotTitle ()) {
Object annots;
GooString *title = link->getAnnotTitle ();
int i;
for (i = 1; i <= document->doc->getNumPages (); ++i) {
Page *p = document->doc->getPage (i);
if (!p) continue;
if (p->getAnnots (&annots)->isArray ()) {
int j;
GBool found = gFalse;
for (j = 0; j < annots.arrayGetLength () && !found; ++j) {
if (annots.arrayGet(j, &annotObj)->isDict()) {
Object obj1;
if (!annotObj.dictLookup ("Subtype", &obj1)->isName ("Movie")) {
obj1.free ();
continue;
}
obj1.free ();
if (annotObj.dictLookup ("T", &obj1)->isString()) {
GooString *t = obj1.getString ();
if (title->cmp(t) == 0)
found = gTrue;
}
obj1.free ();
}
if (!found)
annotObj.free ();
}
if (found) {
annots.free ();
break;
} else {
annotObj.free ();
}
}
annots.free ();
}
}
if (annotObj.isDict ()) {
Object tmp;
annot = new AnnotMovie (document->doc, annotObj.getDict(), &tmp);
if (!annot->isOk ()) {
delete annot;
annot = NULL;
}
}
annotObj.free ();
return annot;
}
static void
build_movie (PopplerDocument *document,
PopplerAction *action,
LinkMovie *link)
{
AnnotMovie *annot;
switch (link->getOperation ()) {
case LinkMovie::operationTypePause:
action->movie.operation = POPPLER_ACTION_MOVIE_PAUSE;
break;
case LinkMovie::operationTypeResume:
action->movie.operation = POPPLER_ACTION_MOVIE_RESUME;
break;
case LinkMovie::operationTypeStop:
action->movie.operation = POPPLER_ACTION_MOVIE_STOP;
break;
default:
case LinkMovie::operationTypePlay:
action->movie.operation = POPPLER_ACTION_MOVIE_PLAY;
break;
}
annot = find_annot_movie_for_action (document, link);
if (annot) {
action->movie.movie = _poppler_movie_new (annot->getMovie());
delete annot;
}
}
static void
build_javascript (PopplerAction *action,
LinkJavaScript *link)
{
GooString *script;
script = link->getScript();
if (script)
action->javascript.script = _poppler_goo_string_to_utf8 (script);
}
static void
build_rendition (PopplerAction *action,
LinkRendition *link)
{
action->rendition.op = link->getOperation();
if (link->hasRenditionObject())
action->rendition.media = _poppler_media_new (link->getMedia());
// TODO: annotation reference
}
static PopplerLayer *
get_layer_for_ref (PopplerDocument *document,
GList *layers,
Ref *ref,
gboolean preserve_rb)
{
GList *l;
for (l = layers; l; l = g_list_next (l)) {
Layer *layer = (Layer *)l->data;
if (layer->oc) {
Ref ocgRef = layer->oc->getRef();
if (ref->num == ocgRef.num && ref->gen == ocgRef.gen) {
GList *rb_group = NULL;
if (preserve_rb)
rb_group = _poppler_document_get_layer_rbgroup (document, layer);
return _poppler_layer_new (document, layer, rb_group);
}
}
if (layer->kids) {
PopplerLayer *retval = get_layer_for_ref (document, layer->kids, ref, preserve_rb);
if (retval)
return retval;
}
}
return NULL;
}
static void
build_ocg_state (PopplerDocument *document,
PopplerAction *action,
LinkOCGState *ocg_state)
{
GooList *st_list = ocg_state->getStateList();
GBool preserve_rb = ocg_state->getPreserveRB();
gint i, j;
GList *layer_state = NULL;
if (!document->layers) {
if (!_poppler_document_get_layers (document))
return;
}
for (i = 0; i < st_list->getLength(); ++i) {
LinkOCGState::StateList *list = (LinkOCGState::StateList *)st_list->get(i);
PopplerActionLayer *action_layer = g_slice_new0 (PopplerActionLayer);
switch (list->st) {
case LinkOCGState::On:
action_layer->action = POPPLER_ACTION_LAYER_ON;
break;
case LinkOCGState::Off:
action_layer->action = POPPLER_ACTION_LAYER_OFF;
break;
case LinkOCGState::Toggle:
action_layer->action = POPPLER_ACTION_LAYER_TOGGLE;
break;
}
for (j = 0; j < list->list->getLength(); ++j) {
Ref *ref = (Ref *)list->list->get(j);
PopplerLayer *layer = get_layer_for_ref (document, document->layers, ref, preserve_rb);
action_layer->layers = g_list_prepend (action_layer->layers, layer);
}
layer_state = g_list_prepend (layer_state, action_layer);
}
action->ocg_state.state_list = g_list_reverse (layer_state);
}
PopplerAction *
_poppler_action_new (PopplerDocument *document,
LinkAction *link,
const gchar *title)
{
PopplerAction *action;
action = g_slice_new0 (PopplerAction);
if (title)
action->any.title = g_strdup (title);
if (link == NULL) {
action->type = POPPLER_ACTION_NONE;
return action;
}
switch (link->getKind ()) {
case actionGoTo:
action->type = POPPLER_ACTION_GOTO_DEST;
build_goto_dest (document, action, dynamic_cast <LinkGoTo *> (link));
break;
case actionGoToR:
action->type = POPPLER_ACTION_GOTO_REMOTE;
build_goto_remote (action, dynamic_cast <LinkGoToR *> (link));
break;
case actionLaunch:
action->type = POPPLER_ACTION_LAUNCH;
build_launch (action, dynamic_cast <LinkLaunch *> (link));
break;
case actionURI:
action->type = POPPLER_ACTION_URI;
build_uri (action, dynamic_cast <LinkURI *> (link));
break;
case actionNamed:
action->type = POPPLER_ACTION_NAMED;
build_named (action, dynamic_cast <LinkNamed *> (link));
break;
case actionMovie:
action->type = POPPLER_ACTION_MOVIE;
build_movie (document, action, dynamic_cast<LinkMovie*> (link));
break;
case actionRendition:
action->type = POPPLER_ACTION_RENDITION;
build_rendition (action, dynamic_cast<LinkRendition*> (link));
break;
case actionOCGState:
action->type = POPPLER_ACTION_OCG_STATE;
build_ocg_state (document, action, dynamic_cast<LinkOCGState*> (link));
break;
case actionJavaScript:
action->type = POPPLER_ACTION_JAVASCRIPT;
build_javascript (action, dynamic_cast<LinkJavaScript*> (link));
break;
case actionUnknown:
default:
action->type = POPPLER_ACTION_UNKNOWN;
break;
}
return action;
}
PopplerDest *
_poppler_dest_new_goto (PopplerDocument *document,
LinkDest *link_dest)
{
return dest_new_goto (document, link_dest);
}