Hi,
I'm working on movie support in poppler (both external and embedded movies).
You can find a patch in attachment to apply on poppler release 0.6.3 that is
a first step toward full support.
For now, movie infos are only exported to the glib frontend (no QT
integration). It also lacks support of different movie options (window size,
position, play/pause/resume, etc.).
The only exported informations are : movie filename, display rectangle and
access to a decompression function if it's an embedded movie.
I'm new to poppler development, so there surely are improvements to come.
You can also find informations (and a way to test movie support with
ePDFView, with samples) at
http://ten0k.free.fr/pdf
Comments and suggestions welcomed.
diff -bBdru poppler-0.6.3.orig/glib/poppler-action.cc poppler-0.6.3/glib/poppler-action.cc
--- poppler-0.6.3.orig/glib/poppler-action.cc 2007-11-05 00:11:01.000000000 +0100
+++ poppler-0.6.3/glib/poppler-action.cc 2008-01-06 18:35:08.000000000 +0100
@@ -18,6 +18,7 @@
#include "poppler.h"
#include "poppler-private.h"
+#include <Stream.h>
GType
poppler_dest_get_type (void)
@@ -118,7 +119,7 @@
g_free (action->named.named_dest);
break;
case POPPLER_ACTION_MOVIE:
- /* TODO */
+ g_free (action->movie.file_name);
break;
default:
break;
@@ -174,7 +175,8 @@
new_action->named.named_dest = g_strdup (action->named.named_dest);
break;
case POPPLER_ACTION_MOVIE:
- /* TODO */
+ if (action->movie.file_name)
+ new_action->movie.file_name = g_strdup(action->movie.file_name);
break;
default:
break;
@@ -373,13 +375,77 @@
static void
build_movie (PopplerAction *action,
- LinkAction *link)
+ PopplerDocument* document,
+ PopplerPage* page,
+ LinkMovie *link)
{
- /* FIXME: Write */
+
+ if (!page) {
+ g_error("Page == NULL, PROBLEM ! => Video in index ??");
+ return;
+ }
+
+ Object obj1;
+ Catalog* catalog;
+ XRef* xref;
+
+ catalog = document->doc->getCatalog();
+ xref = document->doc->getXRef();
+
+ // find the corresponding movie annotation
+
+ Annot* movieAnnot = NULL;
+
+ Annots *annots = new Annots(xref, catalog, page->page->getAnnots(&obj1));
+
+ int nAnnots = annots->getNumAnnots();
+ for (int i=0; i < nAnnots; i++) {
+ Annot* a = annots->getAnnot(i);
+
+ // ... search by ref
+ if (link->hasAnnotRef()) {
+ Ref* ref = link->getAnnotRef();
+ if (a->match(ref)) {
+ movieAnnot = a;
+ break;
+ }
+
+ }
+ // ... or by title
+ else {
+ GooString* linkTitle = link->getTitle();
+ GooString* aTitle = a->getMovieTitle();
+ if (linkTitle && (linkTitle->cmp(aTitle) == 0)) {
+ movieAnnot = a;
+ break;
+ }
+ }
+ }
+
+ if (movieAnnot) {
+ // filename
+ gchar* dn = g_path_get_dirname(document->doc->getFileName()->getCString());
+ char* movieFile = movieAnnot->getMovieFile()->getCString();
+ gchar* fileName = g_strconcat(dn, "/", movieFile, NULL);
+ g_free(dn);
+
+ action->movie.file_name = fileName;
+
+ // ID
+ action->movie.id = movieAnnot->getRef().num;
+
+ // Rect
+ movieAnnot->getRect(action->movie.left,
+ action->movie.top,
+ action->movie.right,
+ action->movie.bottom);
+ }
+ delete annots;
}
PopplerAction *
_poppler_action_new (PopplerDocument *document,
+ PopplerPage* page,
LinkAction *link,
const gchar *title)
{
@@ -418,7 +484,7 @@
break;
case actionMovie:
action->type = POPPLER_ACTION_MOVIE;
- build_movie (action, link);
+ build_movie (action, document, page, dynamic_cast <LinkMovie *> (link));
break;
case actionUnknown:
default:
@@ -435,3 +501,50 @@
{
return dest_new_goto (document, link_dest);
}
+
+void poppler_action_movie_extract_to_file(PopplerActionMovie *action, gchar* filename)
+{
+ if (action->is_embedded) {
+ Stream* void_stream = (Stream*)action->emb_stream;
+ Stream* stream;
+ // reconstruct the Stream from the (void*) pointer
+ // the object of derived type cannot be upcasted to Stream* directly
+ // !!! gruik !!!
+
+ switch (action->stream_kind) {
+ case strFile:
+ stream = reinterpret_cast<FileStream*>(void_stream);
+ break;
+ case strASCIIHex:
+ stream = reinterpret_cast<ASCIIHexStream*>(void_stream);
+ break;
+ case strASCII85:
+ stream = reinterpret_cast<ASCII85Stream*>(void_stream);
+ break;
+ case strLZW:
+ stream = reinterpret_cast<LZWStream*>(void_stream);
+ break;
+ case strRunLength:
+ stream = reinterpret_cast<RunLengthStream*>(void_stream);
+ break;
+ case strCCITTFax:
+ stream = reinterpret_cast<CCITTFaxStream*>(void_stream);
+ break;
+ case strFlate:
+ stream = reinterpret_cast<FlateStream*>(void_stream);
+ break;
+ }
+
+ FILE* fp = fopen(filename, "w+");
+ int c;
+ stream->reset();
+ while(1) {
+ c = stream->getChar();
+ if (c == EOF) {
+ break;
+ }
+ fwrite(&c, 1, 1, fp);
+ }
+ fclose(fp);
+ }
+}
diff -bBdru poppler-0.6.3.orig/glib/poppler-action.h poppler-0.6.3/glib/poppler-action.h
--- poppler-0.6.3.orig/glib/poppler-action.h 2007-11-05 00:11:01.000000000 +0100
+++ poppler-0.6.3/glib/poppler-action.h 2008-01-06 18:35:08.000000000 +0100
@@ -128,6 +128,19 @@
{
PopplerActionType type;
gchar *title;
+
+ gboolean is_embedded;
+ gchar* file_name;
+ double left;
+ double bottom;
+ double right;
+ double top;
+
+ gint id;
+
+ // private
+ void* emb_stream;
+ int stream_kind;
};
union _PopplerAction
@@ -150,6 +163,8 @@
void poppler_action_free (PopplerAction *action);
PopplerAction *poppler_action_copy (PopplerAction *action);
+void poppler_action_movie_extract_to_file(PopplerActionMovie *action, gchar* filename);
+
#define POPPLER_TYPE_DEST (poppler_dest_get_type ())
GType poppler_dest_get_type (void) G_GNUC_CONST;
diff -bBdru poppler-0.6.3.orig/glib/poppler-document.cc poppler-0.6.3/glib/poppler-document.cc
--- poppler-0.6.3.orig/glib/poppler-document.cc 2007-11-28 21:20:15.000000000 +0100
+++ poppler-0.6.3/glib/poppler-document.cc 2008-01-06 18:35:08.000000000 +0100
@@ -1015,7 +1015,7 @@
title = unicode_to_char (item->getTitle(),
item->getTitleLength ());
- action = _poppler_action_new (iter->document, link_action, title);
+ action = _poppler_action_new (iter->document, NULL, link_action, title);
g_free (title);
return action;
Seulement dans poppler-0.6.3/glib: poppler-document.cc.orig
diff -bBdru poppler-0.6.3.orig/glib/poppler-page.cc poppler-0.6.3/glib/poppler-page.cc
--- poppler-0.6.3.orig/glib/poppler-page.cc 2007-11-05 00:11:01.000000000 +0100
+++ poppler-0.6.3/glib/poppler-page.cc 2008-01-06 18:35:08.000000000 +0100
@@ -1226,6 +1226,7 @@
poppler_page_get_link_mapping (PopplerPage *page)
{
GList *map_list = NULL;
+ GList *movie_list = NULL;
gint i;
Links *links;
Object obj;
@@ -1233,6 +1234,14 @@
g_return_val_if_fail (POPPLER_IS_PAGE (page), NULL);
+
+ Catalog* catalog;
+ XRef* xref;
+
+ catalog = page->document->doc->getCatalog();
+ xref = page->document->doc->getXRef();
+
+
links = new Links (page->page->getAnnots (&obj),
page->document->doc->getCatalog ()->getBaseURI ());
obj.free ();
@@ -1254,7 +1263,12 @@
/* Create the mapping */
mapping = g_new (PopplerLinkMapping, 1);
- mapping->action = _poppler_action_new (page->document, link_action, NULL);
+ mapping->action = _poppler_action_new (page->document, page, link_action, NULL);
+
+ if (mapping->action->type == POPPLER_ACTION_MOVIE) {
+ // add the movie ID to the movie list
+ movie_list = g_list_append(movie_list, &mapping->action->movie.id);
+ }
link->getRect (&rect.x1, &rect.y1, &rect.x2, &rect.y2);
@@ -1298,6 +1312,118 @@
delete links;
+
+ // look for isolated movies (movie without link)
+ // (to conform to Adobe Reader behavior)
+ Annots* annots = new Annots(xref, catalog, page->page->getAnnots(&obj));
+ int nAnnots = annots->getNumAnnots();
+ for (int i=0; i < nAnnots; i++) {
+ Annot* a = annots->getAnnot(i);
+
+ if (a->hasMovie()) {
+ GList* movie_iter = g_list_first(movie_list);
+
+ int found = 0;
+ while (movie_iter) {
+ gint linked_id = *((gint*)movie_iter->data);
+ gint movie_id = a->getRef().num;
+
+ if (movie_id == linked_id) {
+ found = 1;
+ break;
+ }
+ g_list_next(movie_iter);
+ }
+
+ if (!found) { // isolated movie
+ /* Create the mapping */
+ PopplerLinkMapping *mapping;
+ mapping = g_new (PopplerLinkMapping, 1);
+
+ PopplerAction* action = g_new0 (PopplerAction, 1);
+ action->type = POPPLER_ACTION_MOVIE;
+
+ if (a->hasEmbeddedMovie()) {
+ action->movie.is_embedded = true;
+ Stream* str = a->getEmbeddedMovieStream();
+ action->movie.emb_stream = (void*)str;
+ action->movie.stream_kind = str->getKind();
+ } else {
+ // filename
+ char* movieFile = a->getMovieFile()->getCString();
+ gchar* fileName = (gchar*)movieFile;
+ if (!g_path_is_absolute(fileName)) {
+ gchar* dn = g_path_get_dirname(page->document->doc->getFileName()->getCString());
+ fileName = g_strconcat(dn, "/", movieFile, NULL);
+ g_free(dn);
+ }
+
+ action->movie.file_name = fileName;
+
+ }
+
+ // ID
+ action->movie.id = a->getRef().num;
+
+ // Rect
+ a->getRect(action->movie.left,
+ action->movie.top,
+ action->movie.right,
+ action->movie.bottom);
+
+ mapping->action = action;
+
+ PopplerRectangle rect;
+ rect.x1 = action->movie.left;
+ rect.x2 = action->movie.right;
+ rect.y1 = action->movie.top;
+ rect.y2 = action->movie.bottom;
+
+ switch (page->page->getRotate ())
+ {
+ case 90:
+ mapping->area.x1 = rect.y1;
+ mapping->area.y1 = height - rect.x2;
+ mapping->area.x2 = mapping->area.x1 + (rect.y2 - rect.y1);
+ mapping->area.y2 = mapping->area.y1 + (rect.x2 - rect.x1);
+
+ break;
+ case 180:
+ mapping->area.x1 = width - rect.x2;
+ mapping->area.y1 = height - rect.y2;
+ mapping->area.x2 = mapping->area.x1 + (rect.x2 - rect.x1);
+ mapping->area.y2 = mapping->area.y1 + (rect.y2 - rect.y1);
+
+ break;
+ case 270:
+ mapping->area.x1 = width - rect.y2;
+ mapping->area.y1 = rect.x1;
+ mapping->area.x2 = mapping->area.x1 + (rect.y2 - rect.y1);
+ mapping->area.y2 = mapping->area.y1 + (rect.x2 - rect.x1);
+
+ break;
+ default:
+ mapping->area.x1 = rect.x1;
+ mapping->area.y1 = rect.y1;
+ mapping->area.x2 = rect.x2;
+ mapping->area.y2 = rect.y2;
+ }
+
+ mapping->area.x1 -= page->page->getCropBox()->x1;
+ mapping->area.x2 -= page->page->getCropBox()->x1;
+ mapping->area.y1 -= page->page->getCropBox()->y1;
+ mapping->area.y2 -= page->page->getCropBox()->y1;
+
+ map_list = g_list_prepend (map_list, mapping);
+
+ }
+ }
+ }
+
+ g_list_free(movie_list);
+
+ delete annots;
+
return map_list;
}
diff -bBdru poppler-0.6.3.orig/glib/poppler-private.h poppler-0.6.3/glib/poppler-private.h
--- poppler-0.6.3.orig/glib/poppler-private.h 2007-11-05 00:11:01.000000000 +0100
+++ poppler-0.6.3/glib/poppler-private.h 2008-01-06 18:35:08.000000000 +0100
@@ -17,6 +17,12 @@
#include <SplashOutputDev.h>
#endif
+struct _PopplerEmbeddedVideo
+{
+ GooString* filename;
+ guint id;
+};
+
struct _PopplerDocument
{
GObject parent_instance;
@@ -75,6 +81,7 @@
Page *page,
int index);
PopplerAction *_poppler_action_new (PopplerDocument *document,
+ PopplerPage *page,
LinkAction *link,
const gchar *title);
PopplerDest *_poppler_dest_new_goto (PopplerDocument *document,
diff -bBdru poppler-0.6.3.orig/poppler/Annot.cc poppler-0.6.3/poppler/Annot.cc
--- poppler-0.6.3.orig/poppler/Annot.cc 2007-11-05 00:11:04.000000000 +0100
+++ poppler-0.6.3/poppler/Annot.cc 2008-01-06 18:35:08.000000000 +0100
@@ -27,6 +27,8 @@
#include "Form.h"
#include "Error.h"
+#include "Link.h"
+
#define annotFlagHidden 0x0002
#define annotFlagPrint 0x0004
#define annotFlagNoView 0x0020
@@ -114,6 +116,8 @@
widget = NULL;
borderStyle = NULL;
+ has_embedded_movie = false;
+
//----- get the FormWidget
if (hasRef) {
Form *form = catalog->getForm ();
@@ -128,6 +132,42 @@
}
obj1.free();
+
+
+ //----- parse the movie dictionary if present
+ movieFile = NULL;
+ movieTitle = NULL;
+
+ if (!type->cmp("Movie")) {
+ if (dict->lookup("T", &obj1)->isString()) {
+ movieTitle = obj1.getString();
+ }
+ if (dict->lookup("Movie", &obj1)->isDict()) {
+ if (obj1.dictLookup("F", &obj2)->isString()) {
+ movieFile = obj2.getString();
+ }
+ // optionally parse other movie options
+ }
+ }
+
+ if (!type->cmp("Screen")) {
+ if (dict->lookup("A", &obj1)->isDict()) {
+ LinkAction* action = LinkAction::parseAction(&obj1, NULL);
+ if (action->getKind() == actionRendition) {
+ LinkRendition *rendition = (LinkRendition*)action;
+ if (rendition->isEmbedded()) {
+ has_embedded_movie = true;
+ rendition->getEmbedded()->copy(&embedded);
+ } else {
+ movieFile = rendition->getMovieFileName()->copy();
+ }
+ } else {
+ printf("other action type %d\n", action->getKind());
+ }
+ delete action;
+ }
+ }
+
//----- parse the rectangle
if (dict->lookup("Rect", &obj1)->isArray() &&
diff -bBdru poppler-0.6.3.orig/poppler/Annot.h poppler-0.6.3/poppler/Annot.h
--- poppler-0.6.3.orig/poppler/Annot.h 2007-11-05 00:11:04.000000000 +0100
+++ poppler-0.6.3/poppler/Annot.h 2008-01-06 18:35:08.000000000 +0100
@@ -87,6 +87,22 @@
double getFontSize() { return fontSize; }
+
+ GBool hasMovie() { return ((movieFile!=NULL) || (has_embedded_movie)); }
+ GBool hasEmbeddedMovie() { return has_embedded_movie; }
+ Stream* getEmbeddedMovieStream() { return embedded.getStream(); }
+ GooString* getMovieFile() { return movieFile; }
+ GooString* getMovieTitle() { return movieTitle; }
+
+ void getRect(double& x1, double& y1, double& x2, double& y2)
+ {
+ x1 = xMin;
+ x2 = xMax;
+ y1 = yMin;
+ y2 = yMax;
+ }
+ Ref getRef() { return ref; }
+
private:
void setColor(Array *a, GBool fill, int adjust);
void drawText(GooString *text, GooString *da, GfxFontDict *fontDict,
@@ -124,6 +140,12 @@
GBool regen, isTextField;
GBool isMultiline, isListbox;
+ GBool has_embedded_movie;
+
+ GooString *movieFile;
+ GooString *movieTitle;
+ Object embedded;
+
bool hasRef;
bool hidden;
};
diff -bBdru poppler-0.6.3.orig/poppler/Dict.cc poppler-0.6.3/poppler/Dict.cc
--- poppler-0.6.3.orig/poppler/Dict.cc 2007-11-05 00:11:04.000000000 +0100
+++ poppler-0.6.3/poppler/Dict.cc 2008-01-06 18:35:08.000000000 +0100
@@ -58,7 +58,7 @@
int i;
for (i = 0; i < length; ++i) {
- if (!strcmp(key, entries[i].key))
+ if (!strcasecmp(key, entries[i].key))
return &entries[i];
}
return NULL;
diff -bBdru poppler-0.6.3.orig/poppler/Link.cc poppler-0.6.3/poppler/Link.cc
--- poppler-0.6.3.orig/poppler/Link.cc 2007-11-05 00:11:04.000000000 +0100
+++ poppler-0.6.3/poppler/Link.cc 2008-01-06 18:35:08.000000000 +0100
@@ -92,6 +92,10 @@
} else if (obj2.isName("Sound")) {
action = new LinkSound(obj);
+ // Rendition action
+ } else if (obj2.isName("Rendition")) {
+ action = new LinkRendition(obj);
+
// unknown action
} else if (obj2.isName()) {
action = new LinkUnknown(obj2.getName());
@@ -678,6 +682,87 @@
}
//------------------------------------------------------------------------
+// LinkRendition
+//------------------------------------------------------------------------
+
+LinkRendition::LinkRendition(Object *Obj) {
+ operation = -1;
+ fileName = NULL;
+ is_embedded = false;
+
+ if (Obj->isDict())
+ {
+ Object tmp, tmp2;
+
+ if (Obj->dictLookup("OP", &tmp)->isNull()) {
+ error(-1, "Rendition action : no /OP field defined");
+ tmp.free();
+ } else {
+
+ operation = tmp.getInt();
+
+ // retrieve rendition object
+ Obj->dictLookup("R", &renditionObj);
+
+ if (renditionObj.dictLookup("S", &tmp)->isName()) {
+ if (!strcmp(tmp.getName(), "MR")) { // a media rendition
+ if (renditionObj.dictLookup("C", &tmp2)->isDict()) {
+
+ tmp.free();
+ if (tmp2.dictLookup("S", &tmp)->isName()) {
+ if (!strcmp(tmp.getName(), "MCD")) { // media clip data
+ Object obj1, obj2;
+ if (tmp2.dictLookup("D", &obj1)->isDict()) {
+ if (obj1.dictLookup("F", &obj2)->isString()) {
+ fileName = obj2.getString()->copy();
+ }
+ obj2.free();
+
+ if (!obj1.dictLookup("EF", &obj2)->isNull()) {
+ tmp.free();
+ Object mref;
+ if (!obj2.dictLookupNF("F", &mref)->isNull()) {
+ is_embedded = true;
+ obj2.dictLookup("F", &embedded);
+ }
+ mref.free();
+ }
+ }
+ obj1.free();
+ obj2.free();
+
+ if (tmp2.dictLookup("CT", &obj1)->isString()) {
+ printf("Content-type: %s\n", obj1.getString()->getCString());
+ }
+ obj1.free();
+ }
+ }
+ }
+ tmp.free();
+ }
+ }
+
+
+ // screen annotation reference
+ Obj->dictLookupNF("AN", &tmp);
+ if (tmp.isRef()) {
+ screenRef = tmp.getRef();
+ }
+ tmp.free();
+ }
+ }
+}
+
+LinkRendition::~LinkRendition() {
+ delete fileName;
+
+ renditionObj.free();
+ embedded.free();
+}
+
+
+
+//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------
@@ -789,8 +874,12 @@
if (annots->isArray()) {
for (i = 0; i < annots->arrayGetLength(); ++i) {
- if (annots->arrayGet(i, &obj1)->isDict()) {
- if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
+ annots->arrayGet(i, &obj1);
+
+ if (obj1.isDict()) {
+ Object* subType = obj1.dictLookup("SubType", &obj2);
+
+ if (subType->isName("Link")) {
link = new Link(obj1.getDict(), baseURI);
if (link->isOk()) {
if (numLinks >= size) {
diff -bBdru poppler-0.6.3.orig/poppler/Link.h poppler-0.6.3/poppler/Link.h
--- poppler-0.6.3.orig/poppler/Link.h 2007-11-05 00:11:04.000000000 +0100
+++ poppler-0.6.3/poppler/Link.h 2008-01-06 18:35:08.000000000 +0100
@@ -32,6 +32,7 @@
actionNamed, // named action
actionMovie, // movie action
actionSound, // sound action
+ actionRendition, // rendition action
actionUnknown // anything else
};
@@ -276,6 +277,47 @@
GooString *title;
};
+
+//------------------------------------------------------------------------
+// LinkRendition
+//------------------------------------------------------------------------
+
+class LinkRendition: public LinkAction {
+public:
+
+ LinkRendition(Object *Obj);
+
+ virtual ~LinkRendition();
+
+ virtual GBool isOk() { return true; }
+
+ virtual LinkActionKind getKind() { return actionRendition; }
+
+ GBool hasRenditionObject() { return !renditionObj.isNull(); }
+ // GBool hasScreenAnnot() { return screenRef.num > 0; }
+
+ int getOperation() { return operation; }
+ Object* getRenditionObject() { return &renditionObj; }
+ Ref* getScreenAnnot() { return &screenRef; }
+ GooString* getMovieFileName() { return fileName; }
+
+ GBool isEmbedded() { return is_embedded; }
+
+ Object* getEmbedded() { return &embedded; }
+
+
+
+private:
+
+ Ref screenRef;
+ GooString* fileName;
+ Object renditionObj;
+ int operation;
+
+ GBool is_embedded;
+ Object embedded;
+};
+
//------------------------------------------------------------------------
// LinkSound
//------------------------------------------------------------------------
_______________________________________________
poppler mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/poppler