>
> Not commenging in glib code as we have better people to that.
>
> Minor comments:
> * Use poppler own types in poppler core code (yeah i know it sucks)
> * Use poppler's Gulong instead of long in poppler core core
> * Use poppler's gFalse and gTrue in poppler core core
> * Use GBool on Movie.h
> * You killed "Sound" from the if on createAnnot, is that a mistake?
>
> Important things:
> * The Movie ownernship is not clear to me, AnnotMovie creates one but
> never
> deletes it, that makes an instant leak. Same for LinkRendition. I think
> your
> idea is that people needs to call getMovie and then take care of deleting
> themselves. That's unaccetable because not everyone might want to care
> about
> Annots and because if there are two calls to getMovie it creates a "who
> has
> to delete dilemma". Probably the easiest solution is making the getMovie
> functions return a copy of movie the caller has to care to delete.
>
> Critical things:
> * Movie.h is GPLv3+ which makes it impossible for us to use it, it needs
> to
> be GPLv2+
>
>
Hi,
Here is another patch (against the last git commit) that fixes these issues.
Ownership of members has been clarified.
I only send a patch that adds movie support to the "core" poppler lib. Movie
access in the glib part has still to be rewritten to conform to recent
changes proposed by Iñigo (PopplerAnnotMapping).
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0aff96d..6d8c27d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -169,6 +169,7 @@ set(poppler_SRCS
poppler/SecurityHandler.cc
poppler/Sound.cc
poppler/XpdfPluginAPI.cc
+ poppler/Movie.cc
)
set(poppler_LIBS ${FREETYPE_LIBRARIES} ${FONTCONFIG_LIBRARIES})
if(ENABLE_SPLASH)
@@ -296,6 +297,7 @@ if(ENABLE_XPDF_HEADERS)
poppler/UTF8.h
poppler/XpdfPluginAPI.h
poppler/Sound.h
+ poppler/Movie.h
${CMAKE_CURRENT_BINARY_DIR}/poppler/poppler-config.h
DESTINATION include/poppler)
if(ENABLE_SPLASH)
diff --git a/poppler/Annot.cc b/poppler/Annot.cc
index 2f7d8fc..1d95a8b 100644
--- a/poppler/Annot.cc
+++ b/poppler/Annot.cc
@@ -29,6 +29,7 @@
#include "Error.h"
#include "Page.h"
#include "XRef.h"
+#include "Movie.h"
#define annotFlagHidden 0x0002
#define annotFlagPrint 0x0004
@@ -2992,6 +2993,309 @@ void AnnotWidget::draw(Gfx *gfx, GBool printing) {
obj.free();
}
+
+//------------------------------------------------------------------------
+// AnnotMovie
+//------------------------------------------------------------------------
+
+AnnotMovie::AnnotMovie(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
+ Annot(xrefA, dict, catalog, obj) {
+ type = typeMovie;
+ initialize(xrefA, catalog, dict);
+
+ movie = new Movie();
+ movie->parseAnnotMovie(this);
+}
+
+AnnotMovie::~AnnotMovie() {
+ if (title)
+ delete title;
+ if (fileName)
+ delete fileName;
+ delete movie;
+
+ if (posterStream && (!posterStream->decRef())) {
+ delete posterStream;
+ }
+}
+
+void AnnotMovie::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
+ Object obj1;
+
+ if (dict->lookup("T", &obj1)->isString()) {
+ title = obj1.getString()->copy();
+ } else {
+ title = NULL;
+ }
+ obj1.free();
+
+ Object movieDict;
+ Object aDict;
+
+ // default values
+ fileName = NULL;
+ width = 0;
+ height = 0;
+ rotationAngle = 0;
+ rate = 1.0;
+ volume = 1.0;
+ showControls = false;
+ repeatMode = repeatModeOnce;
+ synchronousPlay = false;
+
+ hasFloatingWindow = false;
+ isFullscreen = false;
+ FWScaleNum = 1;
+ FWScaleDenum = 1;
+ FWPosX = 0.5;
+ FWPosY = 0.5;
+
+ if (dict->lookup("Movie", &movieDict)->isDict()) {
+ if (movieDict.dictLookup("F", &obj1)->isString()) {
+ fileName = obj1.getString()->copy();
+ }
+ obj1.free();
+
+ if (movieDict.dictLookup("Aspect", &obj1)->isArray()) {
+ Array* aspect = obj1.getArray();
+ if (aspect->getLength() >= 2) {
+ Object tmp;
+ width = aspect->get(0, &tmp)->getInt();
+ tmp.free();
+ height = aspect->get(1, &tmp)->getInt();
+ tmp.free();
+ }
+ }
+ obj1.free();
+
+ if (movieDict.dictLookup("Rotate", &obj1)->isInt()) {
+ // round up to 90°
+ rotationAngle = (((obj1.getInt() + 360) % 360) % 90) * 90;
+ }
+ obj1.free();
+
+ //
+ // movie poster
+ //
+ posterType = posterTypeNone;
+ posterStream = NULL;
+ if (!movieDict.dictLookup("Poster", &obj1)->isNone()) {
+ if (obj1.isBool()) {
+ GBool v = obj1.getBool();
+ if (v)
+ posterType = posterTypeFromMovie;
+ }
+
+ if (obj1.isStream()) {
+ posterType = posterTypeStream;
+
+ // "copy" stream
+ posterStream = obj1.getStream();
+ posterStream->incRef();
+ }
+
+ obj1.free();
+ }
+
+ }
+ movieDict.free();
+
+
+ // activation dictionary parsing ...
+
+ if (dict->lookup("A", &aDict)->isDict()) {
+ if (!aDict.dictLookup("Start", &obj1)->isNone()) {
+ if (obj1.isInt()) {
+ // If it is representable as an integer (subject to the implementation limit for
+ // integers, as described in Appendix C), it should be specified as such.
+
+ start.units = obj1.getInt();
+ }
+ if (obj1.isString()) {
+ // If it is not representable as an integer, it should be specified as an 8-byte
+ // string representing a 64-bit twos-complement integer, most significant
+ // byte first.
+
+ // UNSUPPORTED
+ }
+
+ if (obj1.isArray()) {
+ Array* a = obj1.getArray();
+
+ Object tmp;
+ a->get(0, &tmp);
+ if (tmp.isInt()) {
+ start.units = tmp.getInt();
+ }
+ if (tmp.isString()) {
+ // UNSUPPORTED
+ }
+ tmp.free();
+
+ a->get(1, &tmp);
+ if (tmp.isInt()) {
+ start.units_per_second = tmp.getInt();
+ }
+ tmp.free();
+ }
+ }
+ obj1.free();
+
+ if (!aDict.dictLookup("Duration", &obj1)->isNone()) {
+ if (obj1.isInt()) {
+ duration.units = obj1.getInt();
+ }
+ if (obj1.isString()) {
+ // UNSUPPORTED
+ }
+
+ if (obj1.isArray()) {
+ Array* a = obj1.getArray();
+
+ Object tmp;
+ a->get(0, &tmp);
+ if (tmp.isInt()) {
+ duration.units = tmp.getInt();
+ }
+ if (tmp.isString()) {
+ // UNSUPPORTED
+ }
+ tmp.free();
+
+ a->get(1, &tmp);
+ if (tmp.isInt()) {
+ duration.units_per_second = tmp.getInt();
+ }
+ tmp.free();
+ }
+ }
+ obj1.free();
+
+ if (aDict.dictLookup("Rate", &obj1)->isNum()) {
+ rate = obj1.getNum();
+ }
+ obj1.free();
+
+ if (aDict.dictLookup("Volume", &obj1)->isNum()) {
+ volume = obj1.getNum();
+ }
+ obj1.free();
+
+ if (aDict.dictLookup("ShowControls", &obj1)->isBool()) {
+ showControls = obj1.getBool();
+ }
+ obj1.free();
+
+ if (aDict.dictLookup("Synchronous", &obj1)->isBool()) {
+ synchronousPlay = obj1.getBool();
+ }
+ obj1.free();
+
+ if (aDict.dictLookup("Mode", &obj1)->isName()) {
+ char* name = obj1.getName();
+ if (!strcmp(name, "Once"))
+ repeatMode = repeatModeOnce;
+ if (!strcmp(name, "Open"))
+ repeatMode = repeatModeOpen;
+ if (!strcmp(name, "Repeat"))
+ repeatMode = repeatModeRepeat;
+ if (!strcmp(name,"Palindrome"))
+ repeatMode = repeatModePalindrome;
+ }
+ obj1.free();
+
+ if (aDict.dictLookup("FWScale", &obj1)->isArray()) {
+ // the presence of that entry implies that the movie is to be played
+ // in a floating window
+ hasFloatingWindow = true;
+
+ Array* scale = obj1.getArray();
+ if (scale->getLength() >= 2) {
+ Object tmp;
+ if (scale->get(0, &tmp)->isInt()) {
+ FWScaleNum = tmp.getInt();
+ }
+ tmp.free();
+ if (scale->get(1, &tmp)->isInt()) {
+ FWScaleDenum = tmp.getInt();
+ }
+ tmp.free();
+ }
+
+ // detect fullscreen mode
+ if ((FWScaleNum == 999) && (FWScaleDenum == 1)) {
+ isFullscreen = true;
+ }
+ }
+ obj1.free();
+
+ if (aDict.dictLookup("FWPosition", &obj1)->isArray()) {
+ Array* pos = obj1.getArray();
+ if (pos->getLength() >= 2) {
+ Object tmp;
+ if (pos->get(0, &tmp)->isNum()) {
+ FWPosX = tmp.getNum();
+ }
+ tmp.free();
+ if (pos->get(1, &tmp)->isNum()) {
+ FWPosY = tmp.getNum();
+ }
+ tmp.free();
+ }
+ }
+ }
+ aDict.free();
+}
+
+void AnnotMovie::getMovieSize(int& width, int& height) {
+ width = this->width;
+ height = this->height;
+}
+
+void AnnotMovie::getZoomFactor(int& num, int& denum) {
+ num = FWScaleNum;
+ denum = FWScaleDenum;
+}
+
+
+//------------------------------------------------------------------------
+// AnnotScreen
+//------------------------------------------------------------------------
+
+AnnotScreen::AnnotScreen(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj) :
+ Annot(xrefA, dict, catalog, obj) {
+ type = typeScreen;
+ initialize(xrefA, catalog, dict);
+}
+
+AnnotScreen::~AnnotScreen() {
+ if (title)
+ delete title;
+ if (appearCharacs)
+ delete appearCharacs;
+}
+
+void AnnotScreen::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
+ Object obj1;
+
+ title = NULL;
+ if (dict->lookup("T", &obj1)->isString()) {
+ title = obj1.getString()->copy();
+ }
+ obj1.free();
+
+ dict->lookup("A", &action);
+
+ dict->lookup("AA", &additionAction);
+
+ appearCharacs = NULL;
+ if(dict->lookup("MK", &obj1)->isDict()) {
+ appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
+ }
+ obj1.free();
+
+}
+
//------------------------------------------------------------------------
// Annots
//------------------------------------------------------------------------
@@ -3072,13 +3376,13 @@ Annot *Annots::createAnnot(XRef *xref, Dict* dict, Catalog *catalog, Object *obj
annot = new Annot(xref, dict, catalog, obj);
} else if (!typeName->cmp("Sound")) {
annot = new Annot(xref, dict, catalog, obj);
- } else if (!typeName->cmp("Movie")) {
- annot = new Annot(xref, dict, catalog, obj);
- } else if (!typeName->cmp("Widget")) {
- annot = new AnnotWidget(xref, dict, catalog, obj);
- } else if (!typeName->cmp("Screen")) {
+ } else if(!typeName->cmp("Movie")) {
+ annot = new AnnotMovie(xref, dict, catalog, obj);
+ } else if(!typeName->cmp("Widget")) {
annot = new Annot(xref, dict, catalog, obj);
- } else if (!typeName->cmp("PrinterMark")) {
+ } else if(!typeName->cmp("Screen")) {
+ annot = new AnnotScreen(xref, dict, catalog, obj);
+ } else if(!typeName->cmp("PrinterMark")) {
annot = new Annot(xref, dict, catalog, obj);
} else if (!typeName->cmp("TrapNet")) {
annot = new Annot(xref, dict, catalog, obj);
diff --git a/poppler/Annot.h b/poppler/Annot.h
index 3098502..ef71ae0 100644
--- a/poppler/Annot.h
+++ b/poppler/Annot.h
@@ -22,6 +22,7 @@ class GfxFontDict;
class Form;
class FormWidget;
class PDFRectangle;
+class Movie;
enum AnnotLineEndingStyle {
annotLineEndingSquare, // Square
@@ -470,12 +471,15 @@ public:
int getTreeKey() const { return treeKey; }
Dict *getOptionalContent() const { return optionalContent; }
+ int getId() { return ref.num; }
+
private:
void readArrayNum(Object *pdfArray, int key, double *value);
// write vStr[i:j[ in appearBuf
void initialize (XRef *xrefA, Dict *dict, Catalog *catalog);
+
protected:
void setColor(Array *a, GBool fill, int adjust);
void drawCircle(double cx, double cy, double r, GBool fill);
@@ -624,6 +628,127 @@ private:
};
//------------------------------------------------------------------------
+// AnnotMovie
+//------------------------------------------------------------------------
+
+
+
+class AnnotMovie: public Annot {
+ public:
+ enum PosterType {
+ posterTypeNone,
+ posterTypeStream,
+ posterTypeFromMovie
+ };
+
+ enum RepeatMode {
+ repeatModeOnce,
+ repeatModeOpen,
+ repeatModeRepeat,
+ repeatModePalindrome
+ };
+
+ struct Time {
+ Time() { units_per_second = 0; }
+ Gulong units;
+ int units_per_second; // 0 : defined by movie
+ };
+
+ AnnotMovie(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj);
+ ~AnnotMovie();
+
+ GooString* getTitle() { return title; }
+ GooString* getFileName() { return fileName; }
+ int getRotationAngle() { return rotationAngle; }
+
+ PosterType getPosterType() { return posterType; }
+ Stream* getPosterStream() { return posterStream; }
+
+ Time getStart() { return start; }
+ Time getDuration() { return duration; }
+ double getRate() { return rate; }
+ double getVolume() { return volume; }
+
+ GBool getShowControls() { return showControls; }
+ RepeatMode getRepeatMode() { return repeatMode; }
+ GBool getSynchronousPlay() { return synchronousPlay; }
+
+ GBool needFloatingWindow() { return hasFloatingWindow; }
+ GBool needFullscreen() { return isFullscreen; }
+
+
+ void getMovieSize(int& width, int& height);
+ void getZoomFactor(int& num, int& denum);
+ void getWindowPosition(double& x, double& y) { x = FWPosX; y = FWPosY; }
+
+ Movie* getMovie() { return movie; }
+
+ private:
+ void initialize(XRef *xrefA, Catalog *catalog, Dict *dict);
+
+ GooString* title; // T
+ GooString* fileName; // Movie/F
+
+ int width; // Movie/Aspect
+ int height; // Movie/Aspect
+ int rotationAngle; // Movie/Rotate
+
+ PosterType posterType; // Movie/Poster
+ Stream* posterStream;
+
+ Time start; // A/Start
+ Time duration; // A/Duration
+ double rate; // A/Rate
+ double volume; // A/Volume
+
+ GBool showControls; // A/ShowControls
+
+ RepeatMode repeatMode; // A/Mode
+
+ GBool synchronousPlay; // A/Synchronous
+
+ // floating window
+ GBool hasFloatingWindow;
+ unsigned short FWScaleNum; // A/FWScale
+ unsigned short FWScaleDenum;
+ GBool isFullscreen;
+
+ double FWPosX; // A/FWPosition
+ double FWPosY;
+
+ Movie* movie;
+};
+
+
+//------------------------------------------------------------------------
+// AnnotScreen
+//------------------------------------------------------------------------
+
+class AnnotScreen: public Annot {
+ public:
+
+ AnnotScreen(XRef *xrefA, Dict *dict, Catalog *catalog, Object *obj);
+ ~AnnotScreen();
+
+ GooString* getTitle() { return title; }
+
+ AnnotAppearanceCharacs *getAppearCharacs() { return appearCharacs; }
+ Object* getAction() { return &action; }
+ Object* getAdditionActions() { return &additionAction; }
+
+ private:
+ void initialize(XRef *xrefA, Catalog *catalog, Dict *dict);
+
+
+ GooString* title; // T
+
+ AnnotAppearanceCharacs* appearCharacs; // MK
+
+ Object action; // A
+ Object additionAction; // AA
+};
+
+//------------------------------------------------------------------------
// AnnotLink
//------------------------------------------------------------------------
diff --git a/poppler/Link.cc b/poppler/Link.cc
index 887132f..0eb6822 100644
--- a/poppler/Link.cc
+++ b/poppler/Link.cc
@@ -22,6 +22,7 @@
#include "Dict.h"
#include "Link.h"
#include "Sound.h"
+#include "Movie.h"
//------------------------------------------------------------------------
// LinkAction
@@ -82,11 +83,11 @@ LinkAction *LinkAction::parseAction(Object *obj, GooString *baseURI) {
// Movie action
} else if (obj2.isName("Movie")) {
- obj->dictLookupNF("Annot", &obj3);
- obj->dictLookup("T", &obj4);
- action = new LinkMovie(&obj3, &obj4);
- obj3.free();
- obj4.free();
+ action = new LinkMovie(obj);
+
+ // Rendition action
+ } else if (obj2.isName("Rendition")) {
+ action = new LinkRendition(obj);
// Sound action
} else if (obj2.isName("Sound")) {
@@ -631,21 +632,47 @@ LinkNamed::~LinkNamed() {
// LinkMovie
//------------------------------------------------------------------------
-LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
+LinkMovie::LinkMovie(Object *obj) {
annotRef.num = -1;
- title = NULL;
- if (annotObj->isRef()) {
- annotRef = annotObj->getRef();
- } else if (titleObj->isString()) {
- title = titleObj->getString()->copy();
- } else {
+ annotTitle = NULL;
+
+ Object tmp;
+ if (obj->dictLookupNF("Annotation", &tmp)->isRef()) {
+ annotRef = tmp.getRef();
+ }
+ tmp.free();
+
+ if (obj->dictLookup("T", &tmp)->isString()) {
+ annotTitle = tmp.getString()->copy();
+ }
+ tmp.free();
+
+ if ((annotTitle == NULL) && (annotRef.num == -1)) {
error(-1, "Movie action is missing both the Annot and T keys");
}
+
+ if (obj->dictLookup("Operation", &tmp)->isName()) {
+ char *name = tmp.getName();
+
+ if (!strcmp(name, "Play")) {
+ operation = operationTypePlay;
+ }
+ else if (!strcmp(name, "Stop")) {
+ operation = operationTypeStop;
+ }
+ else if (!strcmp(name, "Pause")) {
+ operation = operationTypePause;
+ }
+ else if (!strcmp(name, "Resume")) {
+ operation = operationTypeResume;
+ }
+ }
+ tmp.free();
}
LinkMovie::~LinkMovie() {
- if (title) {
- delete title;
+ if (annotTitle) {
+ delete annotTitle;
}
}
@@ -698,6 +725,59 @@ LinkSound::~LinkSound() {
}
//------------------------------------------------------------------------
+// LinkRendition
+//------------------------------------------------------------------------
+
+LinkRendition::LinkRendition(Object *Obj) {
+ operation = -1;
+ movie = NULL;
+ screenRef.num = -1;
+
+ if (Obj->isDict())
+ {
+ Object tmp;
+
+ if (Obj->dictLookup("OP", &tmp)->isNull()) {
+ error(-1, "Rendition action : no /OP field defined");
+ tmp.free();
+ } else {
+
+ operation = tmp.getInt();
+ tmp.free();
+
+ // screen annotation reference
+ Obj->dictLookupNF("AN", &tmp);
+ if (tmp.isRef()) {
+ screenRef = tmp.getRef();
+ }
+ tmp.free();
+
+ // retrieve rendition object
+ Obj->dictLookup("R", &renditionObj);
+ if (renditionObj.isDict()) {
+
+ movie = new Movie();
+ movie->parseMediaRendition(&renditionObj);
+
+ if (screenRef.num == -1) {
+ error(-1, "Action Rendition : Rendition without Screen Annotation !");
+ }
+ }
+
+ }
+ }
+
+}
+
+LinkRendition::~LinkRendition() {
+ renditionObj.free();
+
+ if (movie)
+ delete movie;
+}
+
+
+//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------
diff --git a/poppler/Link.h b/poppler/Link.h
index 26f7702..64e8e8e 100644
--- a/poppler/Link.h
+++ b/poppler/Link.h
@@ -19,6 +19,7 @@ class GooString;
class Array;
class Dict;
class Sound;
+class Movie;
//------------------------------------------------------------------------
// LinkAction
@@ -31,6 +32,7 @@ enum LinkActionKind {
actionURI, // URI
actionNamed, // named action
actionMovie, // movie action
+ actionRendition,
actionSound, // sound action
actionUnknown // anything else
};
@@ -252,6 +254,7 @@ private:
GooString *name;
};
+
//------------------------------------------------------------------------
// LinkMovie
//------------------------------------------------------------------------
@@ -259,21 +262,70 @@ private:
class LinkMovie: public LinkAction {
public:
- LinkMovie(Object *annotObj, Object *titleObj);
+ enum OperationType {
+ operationTypePlay,
+ operationTypePause,
+ operationTypeResume,
+ operationTypeStop
+ };
+ LinkMovie(Object *obj);
virtual ~LinkMovie();
- virtual GBool isOk() { return annotRef.num >= 0 || title != NULL; }
-
+ virtual GBool isOk() { return annotRef.num >= 0 || annotTitle != NULL; }
virtual LinkActionKind getKind() { return actionMovie; }
+
+ // a movie action stores either an indirect reference to a movie annotation
+ // or the movie annotation title
+
GBool hasAnnotRef() { return annotRef.num >= 0; }
+ GBool hasAnnotTitle() { return annotTitle != NULL; }
Ref *getAnnotRef() { return &annotRef; }
- GooString *getTitle() { return title; }
+ GooString *getAnnotTitle() { return annotTitle; }
+
+ OperationType getOperation() { return operation; }
+
+private:
+
+ Ref annotRef; // Annotation
+ GooString *annotTitle; // T
+
+ OperationType operation; // Operation
+};
+
+
+//------------------------------------------------------------------------
+// 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(); }
+ Object* getRenditionObject() { return &renditionObj; }
+
+ GBool hasScreenAnnot() { return screenRef.num > 0; }
+ Ref* getScreenAnnot() { return &screenRef; }
+
+ int getOperation() { return operation; }
+
+ Movie* getMovie() { return movie; }
private:
- Ref annotRef;
- GooString *title;
+ Ref screenRef;
+ Object renditionObj;
+ int operation;
+
+ Movie* movie;
};
//------------------------------------------------------------------------
diff --git a/poppler/Makefile.am b/poppler/Makefile.am
index d98466b..a768414 100644
--- a/poppler/Makefile.am
+++ b/poppler/Makefile.am
@@ -145,6 +145,7 @@ poppler_include_HEADERS = \
JPXStream.h \
Lexer.h \
Link.h \
+ Movie.h \
NameToCharCode.h \
Object.h \
OptionalContent.h \
@@ -211,6 +212,7 @@ libpoppler_la_SOURCES = \
JPXStream.cc \
Lexer.cc \
Link.cc \
+ Movie.cc \
NameToCharCode.cc \
Object.cc \
OptionalContent.cc \
diff --git a/poppler/Movie.cc b/poppler/Movie.cc
new file mode 100644
index 0000000..683c88b
--- /dev/null
+++ b/poppler/Movie.cc
@@ -0,0 +1,443 @@
+//*********************************************************************************
+// Movie.cc
+//---------------------------------------------------------------------------------
+// Hugo Mercier <hmercier31[at]gmail.com> (c) 2008
+//
+// GNU GENERAL PUBLIC LICENSE - See COPYING.
+//*********************************************************************************
+
+#include "Movie.h"
+
+#include <GooList.h>
+
+MovieWindowParameters::MovieWindowParameters() {
+ // default values
+ type = movieWindowEmbedded;
+ width = -1;
+ height = -1;
+ relativeTo = windowRelativeToDocument;
+ XPosition = 0.5;
+ YPosition = 0.5;
+ hasTitleBar = gTrue;
+ hasCloseButton = gTrue;
+ isResizeable = gTrue;
+}
+
+MovieWindowParameters::~MovieWindowParameters() {
+}
+
+void MovieWindowParameters::parseFWParams(Object* obj) {
+ Object tmp;
+
+ if (obj->dictLookup("D", &tmp)->isArray()) {
+ Array * dim = tmp.getArray();
+
+ if (dim->getLength() >= 2) {
+ Object dd;
+ if (dim->get(0, &dd)->isInt()) {
+ width = dd.getInt();
+ }
+ dd.free();
+ if (dim->get(1, &dd)->isInt()) {
+ height = dd.getInt();
+ }
+ dd.free();
+ }
+ }
+ tmp.free();
+
+ if (obj->dictLookup("RT", &tmp)->isInt()) {
+ int t = tmp.getInt();
+ switch(t) {
+ case 0: relativeTo = windowRelativeToDocument; break;
+ case 1: relativeTo = windowRelativeToApplication; break;
+ case 2: relativeTo = windowRelativeToDesktop; break;
+ }
+ }
+ tmp.free();
+
+ if (obj->dictLookup("P",&tmp)->isInt()) {
+ int t = tmp.getInt();
+
+ switch(t) {
+ case 0: // Upper left
+ XPosition = 0.0;
+ YPosition = 0.0;
+ break;
+ case 1: // Upper Center
+ XPosition = 0.5;
+ YPosition = 0.0;
+ break;
+ case 2: // Upper Right
+ XPosition = 1.0;
+ YPosition = 0.0;
+ break;
+ case 3: // Center Left
+ XPosition = 0.0;
+ YPosition = 0.5;
+ break;
+ case 4: // Center
+ XPosition = 0.5;
+ YPosition = 0.5;
+ break;
+ case 5: // Center Right
+ XPosition = 1.0;
+ YPosition = 0.5;
+ break;
+ case 6: // Lower Left
+ XPosition = 0.0;
+ YPosition = 1.0;
+ break;
+ case 7: // Lower Center
+ XPosition = 0.5;
+ YPosition = 1.0;
+ break;
+ case 8: // Lower Right
+ XPosition = 1.0;
+ YPosition = 1.0;
+ break;
+ }
+ }
+ tmp.free();
+
+ if (obj->dictLookup("T", &tmp)->isBool()) {
+ hasTitleBar = tmp.getBool();
+ }
+ tmp.free();
+ if (obj->dictLookup("UC", &tmp)->isBool()) {
+ hasCloseButton = tmp.getBool();
+ }
+ tmp.free();
+ if (obj->dictLookup("R", &tmp)->isInt()) {
+ isResizeable = (tmp.getInt() != 0);
+ }
+ tmp.free();
+
+}
+
+MovieParameters::MovieParameters() {
+ // instanciate to default values
+
+ rotationAngle = 0;
+ rate = 1.0;
+ volume = 100;
+ fittingPolicy = fittingUndefined;
+ autoPlay = gTrue;
+ repeatCount = 1.0;
+ opacity = 1.0;
+ showControls = gFalse;
+
+
+ start.units = 0;
+ duration.units = 0;
+}
+
+MovieParameters::~MovieParameters() {
+}
+
+void MovieParameters::parseAnnotMovie(AnnotMovie* annot) {
+ windowParams.relativeTo = MovieWindowParameters::windowRelativeToDesktop;
+
+ if (annot->needFloatingWindow()) {
+ windowParams.type = MovieWindowParameters::movieWindowFloating;
+ }
+ if (annot->needFullscreen()) {
+ windowParams.type = MovieWindowParameters::movieWindowFullscreen;
+ }
+
+ int w, h;
+ int znum, zdenum;
+ annot->getMovieSize(w, h);
+ annot->getZoomFactor(znum, zdenum);
+ windowParams.width = int(w * double(znum) / zdenum);
+ windowParams.height = int(h * double(znum) / zdenum);
+
+ double x,y;
+ annot->getWindowPosition(x,y);
+ windowParams.XPosition = x;
+ windowParams.YPosition = y;
+
+ rate = annot->getRate();
+ // convert volume to [0 100]
+ volume = int((annot->getVolume() + 1.0) * 50);
+
+ AnnotMovie::RepeatMode mode = annot->getRepeatMode();
+ if (mode == AnnotMovie::repeatModeRepeat)
+ repeatCount = 0.0;
+
+ showControls = annot->getShowControls();
+
+ AnnotMovie::Time tStart = annot->getStart();
+ AnnotMovie::Time tDuration = annot->getDuration();
+
+ start.units = tStart.units;
+ start.units_per_second = tStart.units_per_second;
+
+ duration.units = tDuration.units;
+ duration.units_per_second = tDuration.units_per_second;
+}
+
+void MovieParameters::parseMediaPlayParameters(Object* obj) {
+
+ Object tmp;
+
+ if (obj->dictLookup("V", &tmp)->isInt()) {
+ volume = tmp.getInt();
+ }
+ tmp.free();
+
+ if (obj->dictLookup("C", &tmp)->isBool()) {
+ showControls = tmp.getBool();
+ }
+ tmp.free();
+
+ if (obj->dictLookup("F", &tmp)->isInt()) {
+ int t = tmp.getInt();
+
+ switch(t) {
+ case 0: fittingPolicy = fittingMeet; break;
+ case 1: fittingPolicy = fittingSlice; break;
+ case 2: fittingPolicy = fittingFill; break;
+ case 3: fittingPolicy = fittingScroll; break;
+ case 4: fittingPolicy = fittingHidden; break;
+ case 5: fittingPolicy = fittingUndefined; break;
+ }
+ }
+ tmp.free();
+
+ // duration parsing
+ // duration's default value is set to 0, which means : intrinsinc media duration
+ if (obj->dictLookup("D", &tmp)->isDict()) {
+ Object oname, ddict, tmp2;
+ if (tmp.dictLookup("S", &oname)->isName()) {
+ char* name = oname.getName();
+ if (!strcmp(name, "F"))
+ duration.units = -1; // infinity
+ else if (!strcmp(name, "T")) {
+ if (tmp.dictLookup("T", &ddict)->isDict()) {
+ if (ddict.dictLookup("V", &tmp2)->isNum()) {
+ duration.units = Gulong(tmp2.getNum());
+ }
+ tmp2.free();
+ }
+ ddict.free();
+ }
+ }
+ oname.free();
+ }
+ tmp.free();
+
+
+ if (obj->dictLookup("A", &tmp)->isBool()) {
+ autoPlay = tmp.getBool();
+ }
+ tmp.free();
+
+ if (obj->dictLookup("RC", &tmp)->isNum()) {
+ repeatCount = tmp.getNum();
+ }
+ tmp.free();
+
+}
+
+void MovieParameters::parseMediaScreenParameters(Object* obj) {
+ Object tmp;
+
+ if (obj->dictLookup("W", &tmp)->isInt()) {
+ int t = tmp.getInt();
+
+ switch(t) {
+ case 0: windowParams.type = MovieWindowParameters::movieWindowFloating; break;
+ case 1: windowParams.type = MovieWindowParameters::movieWindowFullscreen; break;
+ case 2: windowParams.type = MovieWindowParameters::movieWindowHidden; break;
+ case 3: windowParams.type = MovieWindowParameters::movieWindowEmbedded; break;
+ }
+ }
+ tmp.free();
+
+ // background color
+ if (obj->dictLookup("B", &tmp)->isArray()) {
+ Array* color = tmp.getArray();
+
+ Object component;
+
+ color->get(0, &component);
+ bgColor.r = component.getNum();
+ component.free();
+
+ color->get(1, &component);
+ bgColor.g = component.getNum();
+ component.free();
+
+ color->get(2, &component);
+ bgColor.b = component.getNum();
+ component.free();
+ }
+ tmp.free();
+
+
+ // opacity
+ if (obj->dictLookup("O", &tmp)->isNum()) {
+ opacity = tmp.getNum();
+ }
+ tmp.free();
+
+ if (windowParams.type == MovieWindowParameters::movieWindowFloating) {
+ Object winDict;
+ if (obj->dictLookup("F",&winDict)->isDict()) {
+ windowParams.parseFWParams(&winDict);
+ }
+ }
+}
+
+Movie::Movie() {
+ fileName = NULL;
+ contentType = NULL;
+ isEmbedded = gFalse;
+ embeddedStream = NULL;
+}
+
+Movie::~Movie() {
+ if (fileName)
+ delete fileName;
+ if (contentType)
+ delete contentType;
+
+ if (embeddedStream && (!embeddedStream->decRef())) {
+ delete embeddedStream;
+ }
+ if (posterStream && (!posterStream->decRef())) {
+ delete posterStream;
+ }
+}
+
+void Movie::parseAnnotMovie(AnnotMovie* annot) {
+ // AnnotMovie is not embedded
+ isEmbedded = gFalse;
+
+ fileName = annot->getFileName()->copy();
+
+ if (annot->getPosterStream()) {
+ posterStream = annot->getPosterStream();
+ posterStream->incRef();
+ }
+
+ MH.parseAnnotMovie(annot);
+ // deep copy of MH to BE
+ // (no distinction is made with AnnotMovie)
+ memcpy(&BE, &MH, sizeof(MH));
+}
+
+void Movie::parseMediaRendition(Object* obj) {
+
+ Object tmp, tmp2;
+ if (obj->dictLookup("S", &tmp)->isName()) {
+ if (!strcmp(tmp.getName(), "MR")) { // it's a media rendition
+
+ //
+ // parse Media Play Parameters
+ if (obj->dictLookup("P", &tmp2)->isDict()) { // media play parameters
+ Object params;
+ if (tmp2.dictLookup("MH", ¶ms)->isDict()) {
+ MH.parseMediaPlayParameters(¶ms);
+ }
+ params.free();
+ if (tmp2.dictLookup("BE", ¶ms)->isDict()) {
+ BE.parseMediaPlayParameters(¶ms);
+ }
+ params.free();
+ }
+ tmp2.free();
+ //
+ // parse Media Screen Parameters
+ if (obj->dictLookup("SP", &tmp2)->isDict()) { // media screen parameters
+ Object params;
+ if (tmp2.dictLookup("MH", ¶ms)->isDict()) {
+ MH.parseMediaScreenParameters(¶ms);
+ }
+ params.free();
+ if (tmp2.dictLookup("BE", ¶ms)->isDict()) {
+ BE.parseMediaScreenParameters(¶ms);
+ }
+ params.free();
+ }
+ tmp2.free();
+
+ //
+ // Parse media clip data
+ //
+ if (obj->dictLookup("C", &tmp2)->isDict()) { // media clip
+
+ 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()) {
+ isEmbedded = gTrue;
+ Object embedded;
+ obj2.dictLookup("F", &embedded);
+ if (embedded.isStream()) {
+ embeddedStream = embedded.getStream();
+ // "copy" stream
+ embeddedStream->incRef();
+ }
+ embedded.free();
+ }
+ mref.free();
+ }
+ obj2.free();
+ }
+ obj1.free();
+
+ if (tmp2.dictLookup("CT", &obj1)->isString()) {
+ contentType = obj1.getString()->copy();
+ }
+ obj1.free();
+ }
+ }
+ tmp.free();
+ }
+ tmp2.free();
+ }
+ }
+}
+
+
+void Movie::outputToFile(FILE* fp) {
+ embeddedStream->reset();
+
+ while (1) {
+ int c = embeddedStream->getChar();
+ if (c == EOF)
+ break;
+
+ fwrite(&c, 1, 1, fp);
+ }
+
+}
+
+Movie* Movie::copy() {
+
+ // call default copy constructor
+ Movie* new_movie = new Movie(*this);
+
+ new_movie->contentType = contentType->copy();
+ new_movie->fileName = fileName->copy();
+
+ new_movie->embeddedStream = embeddedStream;
+ new_movie->embeddedStream->incRef();
+
+ new_movie->posterStream = posterStream;
+ new_movie->posterStream->incRef();
+
+ return new_movie;
+}
diff --git a/poppler/Movie.h b/poppler/Movie.h
new file mode 100644
index 0000000..16584ad
--- /dev/null
+++ b/poppler/Movie.h
@@ -0,0 +1,177 @@
+//*********************************************************************************
+// Movie.h
+//---------------------------------------------------------------------------------
+//
+//---------------------------------------------------------------------------------
+// Hugo Mercier <hmercier31[at]gmail.com> (c) 2008
+//
+// 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//*********************************************************************************
+
+#ifndef _MOVIE_H_
+#define _MOVIE_H_
+
+#include "Object.h"
+#include "Annot.h"
+
+class GooList;
+
+
+struct MovieWindowParameters {
+
+ MovieWindowParameters();
+ ~MovieWindowParameters();
+
+ // parse from a floating window parameters dictionary
+ void parseFWParams(Object* obj);
+
+ enum MovieWindowType {
+ movieWindowFloating = 0,
+ movieWindowFullscreen,
+ movieWindowHidden, // ?
+ movieWindowEmbedded
+ };
+
+ enum MovieWindowRelativeTo {
+ windowRelativeToDocument = 0,
+ windowRelativeToApplication,
+ windowRelativeToDesktop
+ };
+
+
+ // DEFAULT VALUE
+
+ MovieWindowType type; // movieWindowEmbedded
+
+
+ int width; // -1
+ int height; // -1
+
+ // floating window position
+ MovieWindowRelativeTo relativeTo; // windowRelativeToDocument (or to desktop)
+ double XPosition; // 0.5
+ double YPosition; // 0.5
+
+ GBool hasTitleBar; // true
+ GBool hasCloseButton; // true
+ GBool isResizeable; // true
+};
+
+
+struct MovieParameters {
+
+ MovieParameters();
+ ~MovieParameters();
+
+ // parse from a "Media Play Parameters" dictionary
+ void parseMediaPlayParameters(Object* playObj);
+ // parse from a "Media Screen Parameters" dictionary
+ void parseMediaScreenParameters(Object* screenObj);
+ // parse from a AnnotMovie object
+ void parseAnnotMovie(AnnotMovie* annot);
+
+ enum MovieFittingPolicy {
+ fittingMeet = 0,
+ fittingSlice,
+ fittingFill,
+ fittingScroll,
+ fittingHidden,
+ fittingUndefined
+ };
+
+ struct MovieTime {
+ MovieTime() { units_per_second = 0; }
+ Gulong units;
+ int units_per_second; // 0 : defined by movie
+ };
+
+ struct Color {
+ double r, g, b;
+ };
+
+ Gushort rotationAngle; // 0
+
+ MovieTime start; // 0
+ MovieTime duration; // 0
+
+ double rate; // 1.0
+
+ int volume; // 100
+
+ // defined in media play parameters, p 770
+ // correspond to 'fit' SMIL's attribute
+ MovieFittingPolicy fittingPolicy; // fittingUndefined
+
+ GBool autoPlay; // true
+
+ // repeat count, can be real values, 0 means forever
+ double repeatCount; // 1.0
+
+ // background color // black = (0.0 0.0 0.0)
+ Color bgColor;
+
+ // opacity in [0.0 1.0]
+ double opacity; // 1.0
+
+
+ GBool showControls; // false
+
+ MovieWindowParameters windowParams;
+};
+
+class Movie {
+ public:
+ Movie();
+ ~Movie();
+
+ void parseAnnotMovie(AnnotMovie* annot);
+ void parseMediaRendition(Object* obj);
+
+ MovieParameters* getMHParameters() { return &MH; }
+ MovieParameters* getBEParameters() { return &BE; }
+
+ GooString* getContentType() { return contentType; }
+ GooString* getFileName() { return fileName; }
+
+ GBool getIsEmbedded() { return isEmbedded; }
+ Stream* getEmbbededStream() { return embeddedStream; }
+ // write embedded stream to file
+ void outputToFile(FILE*);
+
+ Stream* getPosterStream() { return posterStream; }
+
+ Movie* copy();
+
+ private:
+ // "Must Honor" parameters
+ MovieParameters MH;
+ // "Best Effort" parameters
+ MovieParameters BE;
+
+ Stream* posterStream;
+
+ GBool isEmbedded;
+
+ GooString* contentType;
+
+ // if it's embedded
+ Stream* embeddedStream;
+
+ // if it's not embedded
+ GooString* fileName;
+};
+
+#endif
+
_______________________________________________
poppler mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/poppler