[poppler] Patch for embedding videos in to the pdf
Carlos Garcia Campos
carlosgc at gnome.org
Fri Mar 25 06:38:10 PDT 2011
Excerpts from srinivas adicherla's message of vie mar 25 12:42:22 +0100 2011:
> Hi
> Sorry I didn't attach the patch in previous mail.
> I attached with this mail.
>
> Thanks
This looks much better, but there are still some problems. First,
please, don't include generated files in the patch. See inline
comments below
> Index: poppler-0.16.2/poppler/Link.cc
> ===================================================================
> --- poppler-0.16.2/poppler/Link.cc (revision 12)
> +++ poppler-0.16.2/poppler/Link.cc (working copy)
> @@ -44,6 +44,7 @@
> #include "Sound.h"
> #include "FileSpec.h"
> #include "Rendition.h"
> +#include "XRef.h"
> //------------------------------------------------------------------------
> // LinkAction
> @@ -738,6 +739,30 @@
> }
> }
> +LinkRendition::LinkRendition(XRef *xrefA, MediaRendition *rendition, int operation, Annot *annot) {
> + Object obj;
> + Ref annotRef = annot->getRef();
> + renditionObj.initDict(xrefA);
> + renditionObj.dictSet("Type", obj.initName("Action"));
> + renditionObj.dictSet("S", obj.initName("Rendition"));
> + renditionObj.dictSet("OP", obj.initInt(operation));
> + renditionObj.dictSet("AN", obj.initRef(annotRef.num, annotRef.gen));
> +
> + // Media Rendition
> + obj = rendition->getMediaRendition();
> + Ref newRef = xrefA->addIndirectObject(&obj);
the media rendition object should be added to the xref in the
MediaRendition constructor, see the annot constructor:
annotObj.initDict (xrefA);
annotObj.dictSet ("Type", obj2.initName ("Annot"));
annotObj.dictSet ("Rect", &obj1);
ref = xrefA->addIndirectObject (&annotObj);
Here you should do the same with the rendition action object.
> + obj.initRef(newRef.num, newRef.gen);
> + renditionObj.dictSet("R", &obj);
> +}
> +
> +LinkRendition::LinkRendition(XRef *xrefA, Object *js) {
> + Object obj;
> + renditionObj.initDict(xrefA);
> + renditionObj.dictSet("Type", obj.initName("Action"));
> + renditionObj.dictSet("S", obj.initName("Rendition"));
> + renditionObj.dictSet("JS", js);
You should add the rendition action object to xref here too.
> +}
> +
> LinkRendition::~LinkRendition() {
> renditionObj.free();
> screenRef.free();
> Index: poppler-0.16.2/poppler/Annot.h
> ===================================================================
> --- poppler-0.16.2/poppler/Annot.h (revision 12)
> +++ poppler-0.16.2/poppler/Annot.h (working copy)
> @@ -492,7 +492,7 @@
> virtual void draw(Gfx *gfx, GBool printing);
> // Get appearance object.
> Object *getAppearance(Object *obj) { return appearance.fetch(xref, obj); }
> -
> + GBool setAppearanceFromFile(const char *img_file);
Use imgFile.
> GBool match(Ref *refA)
> { return ref.num == refA->num && ref.gen == refA->gen; }
> @@ -740,6 +740,8 @@
> AnnotAppearanceCharacs *getAppearCharacs() { return appearCharacs; }
> LinkAction* getAction() { return action; }
> Object* getAdditionActions() { return &additionAction; }
> + void setTitle(GooString *title);
> + GBool setAction(LinkAction *action);
> private:
> void initialize(XRef *xrefA, Catalog *catalog, Dict *dict);
> Index: poppler-0.16.2/poppler/Rendition.cc
> ===================================================================
> --- poppler-0.16.2/poppler/Rendition.cc (revision 12)
> +++ poppler-0.16.2/poppler/Rendition.cc (working copy)
> @@ -23,6 +23,7 @@
> //*********************************************************************************
> #include <math.h>
> +#include "XRef.h"
> #include "Rendition.h"
> #include "FileSpec.h"
> @@ -281,6 +282,7 @@
> isEmbedded = gFalse;
> embeddedStream = NULL;
> + obj->copy(&mediaRendition);
> //
> // Parse media clip data
> //
> @@ -366,6 +368,31 @@
> tmp2.free();
> }
> +MediaRendition::MediaRendition(XRef *xrefA, const char *mediafile, const char *mimetype) {
> + Object obj1, obj2;
> +
> + mediaRendition.initDict(xrefA);
> + mediaRendition.dictSet("S", obj1.initName("MR"));
> +
> + // Media Clip Dictionary
> + Object mclipDict;
> + mclipDict.initDict(xrefA);
> + mclipDict.dictSet("S", obj1.initName("MCD"));
> +
> + if (mimetype)
> + mclipDict.dictSet("CT", obj1.initString(new GooString(mimetype)));
> +
> + // File Specification Dictionary
> + Ref *ref = createFilespec(xrefA, mediafile);
> + obj2.initRef(ref->num, ref->gen);
> + mclipDict.dictSet("D", &obj2);
> +
> + Ref newRef;
> + newRef = xrefA->addIndirectObject(&mclipDict);
> + obj2.initRef(newRef.num, newRef.gen);
> + mediaRendition.dictSet("C", &obj2);
The media rendition object should be added to Xref here.
> +}
> +
> void MediaRendition::outputToFile(FILE* fp) {
> if (!isEmbedded)
> return;
> Index: poppler-0.16.2/poppler/Rendition.h
> ===================================================================
> --- poppler-0.16.2/poppler/Rendition.h (revision 12)
> +++ poppler-0.16.2/poppler/Rendition.h (working copy)
> @@ -25,6 +25,7 @@
> #define _RENDITION_H_
> #include "Object.h"
> +#include "Annot.h"
> struct MediaWindowParameters {
> @@ -118,6 +119,7 @@
> class MediaRendition {
> public:
> MediaRendition(Object *obj);
> + MediaRendition(XRef *xref, const char *mediafile, const char *mimetype);
> ~MediaRendition();
> GBool isOk () { return ok; }
> @@ -128,6 +130,7 @@
> GooString* getContentType() { return contentType; }
> GooString* getFileName() { return fileName; }
> + Object getMediaRendition() {return mediaRendition;}
Return a pointer to the object instead:
Object *getMediaRendition() { return &mediaRendition; }
> GBool getIsEmbedded() { return isEmbedded; }
> Stream* getEmbbededStream() { return embeddedStream; }
> // write embedded stream to file
> @@ -145,6 +148,7 @@
> GBool isEmbedded;
> + Object mediaRendition;
This object should be freed in the destructor (mediaRendition.free())
> GooString* contentType;
> // if it's embedded
> Index: poppler-0.16.2/poppler/FileSpec.cc
> ===================================================================
> --- poppler-0.16.2/poppler/FileSpec.cc (revision 12)
> +++ poppler-0.16.2/poppler/FileSpec.cc (working copy)
> @@ -23,8 +23,19 @@
> #include <config.h>
> +#include "XRef.h"
> #include "FileSpec.h"
> +#include "PDFDocEncoding.h"
> +const char* getFileNameFromPath (const char *filepath) {
> + const char *filename = strrchr(filepath, '/');
> + if (filename)
> + filename++;
> + else
> + filename = filepath;
> + return filename;
> +}
> +
> GBool getFileSpecName (Object *fileSpec, Object *fileName)
> {
> if (fileSpec->isString()) {
> @@ -138,3 +149,45 @@
> return gTrue;
> }
> +
> +Ref* createFilespec(XRef *xrefA, const char *filepath) {
> +
> + Object obj1, obj2, obj3;
> + Object fileSpec;
> + FILE *fs;
> + unsigned int size = 0;
> + if (!(fs = fopen(filepath, "rb"))) {
> + error(-1, "Couldn't open video file '%s'", filepath);
> + return gFalse;
> + }
> + fseek(fs, 0, SEEK_END);
> + size = ftell(fs);
> +
> + // Extract the video name from the file uri
> + const char *filename = getFileNameFromPath(filepath);
> + fileSpec.initDict(xrefA);
> + fileSpec.dictSet("Type", obj1.initName("Filespec"));
> + fileSpec.dictSet("F", obj2.initName(((char*)filename)));
F is a string, not a name.
> + GooString *videoname = new GooString(filename);
> + int length = videoname->getLength();
lenght is an output variable in pdfDocEncodingToUTF16, so you don't
need to initialize it.
> + filename = pdfDocEncodingToUTF16(videoname, &length);
> + fileSpec.dictSet("UF", obj3.initName((char*)filename));
UF is a text string, not a name, so it should be something like:
fileSpec.dictSet("UF", obj3.initString(new GooString(filenameUTF16, length)));
> + delete(filename);
filename is a const char *, it shouldn't be deleted, use another
variable and delete it with delete []
> + delete(videoname);
> +
> + obj3.initDict(xrefA); // file stream eictionary
> + obj3.dictSet("DL", obj1.initInt(size));
what's DL?
> + obj3.dictSet("Length", obj2.initInt(size));
> +
> + FileStream *stream = new FileStream(fs, 0, 0, size, &obj3);
> + obj1.initStream(stream);
> +
> + Ref newRef = xrefA->addIndirectObject(&obj1);
> + obj2.initRef(newRef.num, newRef.gen);
> + obj1.initDict(xrefA);
> + obj1.dictSet("F", &obj2);
> + fileSpec.dictSet("EF", &obj1);
> + Ref ref = xrefA->addIndirectObject(&fileSpec);
> + return &ref;
Does this work? Ref is a local variable in createFilespec(), you
should return its contents rather than its memory address.
> +}
> Index: poppler-0.16.2/poppler/FileSpec.h
> ===================================================================
> --- poppler-0.16.2/poppler/FileSpec.h (revision 12)
> +++ poppler-0.16.2/poppler/FileSpec.h (working copy)
> @@ -18,7 +18,8 @@
> #include "goo/gtypes.h"
> #include "Object.h"
> +const char* getFileNameFromPath (const char *filepath);
> GBool getFileSpecName (Object *fileSpec, Object *fileName);
> GBool getFileSpecNameForPlatform (Object *fileSpec, Object *fileName);
> -
> +Ref* createFilespec(XRef *xref, const char* filename);
> #endif /* FILE_SPEC_H */
> Index: poppler-0.16.2/poppler/Annot.cc
> ===================================================================
> --- poppler-0.16.2/poppler/Annot.cc (revision 12)
> +++ poppler-0.16.2/poppler/Annot.cc (working copy)
> @@ -60,8 +60,17 @@
> #include "FileSpec.h"
> #include "DateInfo.h"
> #include "Link.h"
> +#include "Rendition.h"
> #include <string.h>
> +#ifdef ENABLE_LIBJPEG
> +#include <jpeglib.h>
> +#endif
> +
> +#ifdef ENABLE_LIBPNG
> +#include <png.h>
> +#endif
> +
> #define fieldFlagReadOnly 0x00000001
> #define fieldFlagRequired 0x00000002
> #define fieldFlagNoExport 0x00000004
> @@ -1336,6 +1345,221 @@
> obj.free();
> }
> +GBool Annot::setAppearanceFromFile(const char *img_file) {
> + Ref imgRef;
> + Object obj1, obj2, obj3, obj4, imgXObj;
> + imgXObj.initDict(xref);
> + imgXObj.dictSet("Type", obj1.initName("XObject"));
> + imgXObj.dictSet("Subtype", obj1.initName("Image"));
> +
> + if (img_file) {
Use an early return, or even an assert, does it make sense to call
this method with a NULL image file?
> + FILE *imgfp;
> + if (!(imgfp = fopen(img_file, "rb"))) {
> + error(-1, "Couldn't open file: %s",img_file);
> + return gFalse;
> + }
> + unsigned char h[10];
> + fread(h, 1, 10, imgfp);
> + fseek(imgfp, 0, SEEK_SET);
> +
> + MemStream *imgStream = NULL;
> + // Load the stream from the png file
> + if (h[0] == 0x89 && h[1] == 0x50 && h[2] == 0x4E && h[3] == 0x47)
> + imgStream = load_from_png (imgfp, &imgXObj);
> + // Load the stream from the jpeg file
> + else if(h[0] == 0xFF && h[1] == 0xD8 && h[6] == 0x4A && h[7] == 0x46 && h[8] == 0x49 && h[9] == 0x46)
> + imgStream = load_from_jpeg (imgfp, &imgXObj);
> + else {
> + error(-1, "Image format cannot be supported, only png/jpeg\n");
> + return gFalse;
> + }
> +
> + if (imgStream) {
> + obj1.initStream(imgStream);
> + appearBuf = new GooString("/Im1 Do");
> + double bbox[4];
> + bbox[0] = bbox[1] = 0;
> + bbox[2] = bbox[3] = 1;
Why is bbox 0, 0, 1, 1? Shouldn't it be the annot rectangle?
> + createResourcesDict("Im1", &obj1, "GS0", 1.0, NULL, &obj2);
> + createForm(bbox, gFalse, &obj2, &obj1);
You should delete appearBuf here.
> +
> + Ref appRef = xref->addIndirectObject(&obj1);
> + obj2.initDict(xref);
> + obj2.dictSet("N", obj1.initRef(appRef.num, appRef.gen));
> + annotObj.dictSet("AP", &obj2);
Use Annot::update() mthod to modify the annot object. You should
update the appearance variable so that ::draw() will work.
> + obj2.initStream(imgStream);
> + createResourcesDict("Im1", &obj2, "GS0", 1.0, NULL, &obj1);
> + createForm(bbox, gFalse, &obj1, &obj2);
> + appRef = xref->addIndirectObject(&obj2);
> +
> + obj1.initDict(xref); // MK dictionary
> + obj1.dictSet("I", obj2.initRef(appRef.num, appRef.gen));
> + annotObj.dictSet("MK", &obj1);
Use Annot::update() mthod to modify the annot object.
> + }
> + }
> +}
> +
> //------------------------------------------------------------------------
> // AnnotPopup
> //------------------------------------------------------------------------
> @@ -4258,6 +4482,35 @@
> }
> +void AnnotScreen::setTitle(GooString *title) {
> + Object obj1;
> + if (title) {
> + title = new GooString(title);
> + if (!title->hasUnicodeMarker()) {
> + title->insert(0, 0xff);
> + title->insert(0, 0xfe);
> + }
> + }
> + else
> + title = new GooString();
> +
> + obj1.initString(title->copy());
> + update ("T", &obj1);
> + delete(title);
why do you delete the title here? Please, see Annot::setContents() as
an example of this method should work.
> +}
> +
> +GBool AnnotScreen::setAction(LinkAction *action) {
This should be void instead of GBool, you are always returning gTrue
after all.
> + Object obj;
> + if (action->getKind() == actionRendition) {
A screen annotation can be used to trigger any action type, not only
rendition actions. This method should be simply:
Ref ref = action->getRef();
obj.initRef(ref.num, ref.gen);
update("A", &obj);
> + LinkRendition *rendition = (LinkRendition*)action;
> + Object *renditionObj = rendition->getRenditionObject();
> + Ref newRef = xref->addIndirectObject(renditionObj);
> + obj.initRef(newRef.num, newRef.gen);
> + annotObj.dictSet("A", &obj);
> + }
> + return gTrue;
> +}
> +
> //------------------------------------------------------------------------
> // AnnotStamp
> //------------------------------------------------------------------------
> Index: poppler-0.16.2/glib/poppler-action.h
> ===================================================================
> --- poppler-0.16.2/glib/poppler-action.h (revision 12)
> +++ poppler-0.16.2/glib/poppler-action.h (working copy)
> @@ -291,8 +291,13 @@
> void poppler_action_free (PopplerAction *action);
> PopplerAction *poppler_action_copy (PopplerAction *action);
> +gboolean poppler_action_create_rendition_new (PopplerAnnot *annot,
> + const char* video_file,
> + const char* mimetype,
> + const char* video_title,
> + const char* img_file,
> + const char* js);
create and new in the same function name looks a bit redundant to
me. Use poppler_action_rendition_new()
> -
> #define POPPLER_TYPE_DEST (poppler_dest_get_type ())
> GType poppler_dest_get_type (void) G_GNUC_CONST;
> Index: poppler-0.16.2/glib/poppler-annot.cc
> ===================================================================
> --- poppler-0.16.2/glib/poppler-annot.cc (revision 12)
> +++ poppler-0.16.2/glib/poppler-annot.cc (working copy)
> @@ -332,7 +332,54 @@
> return poppler_annot;
> }
> +/**
> + * poppler_annot_screen_new:
> + * @doc: a #PopplerDocument
> + * @rect: a #PopplerRectangle
> + *
> + * Creates a new Screen annotation that will be
> + * located on @rect when added to a page. See
> + * poppler_page_add_annot()
> + *
> + * Return value: A newly created #PopplerAnnotScreen annotation
> + *
> + */
> +PopplerAnnot *
> +poppler_annot_screen_new (PopplerDocument *doc,
> + PopplerRectangle *rect)
> +{
> + Annot *annot;
> + PDFRectangle pdf_rect(rect->x1, rect->y1,
> + rect->x2, rect->y2);
> + annot = new AnnotScreen (doc->doc->getXRef(), &pdf_rect, doc->doc->getCatalog());
> + return _poppler_annot_screen_new (annot);
> +}
> +
> +/**
> + * poppler_annot_screen_set_action:
> + * @poppler_annot: a #PopplerAnnot
> + * @video_file: pass the path of the video to be embed
> + * @mimetype: pass the mimetype of the video to put in the Media Clip Dictionary
> + * @img_file: pass png/jpeg image file for poster
> + * @js: set the JS as this javascript stream in rendition action else if it is null set OP
> +
> + * Set the action for the screen annotation specified @poppler_annot
> + *
> +void
> +poppler_annot_screen_set_action (PopplerAnnot *poppler_annot,
> + const char* video_file,
> + const char* mimetype,
> + const char* img_file,
> + const char* js)
> +{
> + //PopplerAction *action = g_slice_new0 (PopplerAction);
> + //action->type = POPPLER_ACTION_RENDITION;
> + AnnotScreen *annot = (AnnotScreen*)poppler_annot->annot;
> + annot->setAction(video_file, mimetype, img_file, js);
I guess you forgot to change this.
> +}
> +
> + */
> /* Public methods */
> /**
> * poppler_annot_get_annot_type:
> Index: poppler-0.16.2/glib/poppler-action.cc
> ===================================================================
> --- poppler-0.16.2/glib/poppler-action.cc (revision 12)
> +++ poppler-0.16.2/glib/poppler-action.cc (working copy)
> @@ -665,3 +665,53 @@
> {
> return dest_new_goto (document, link_dest);
> }
> +
> +/*
> + * poppler_action_create_rendition_new:
> + * @poppler_annot: a #PopplerAnnot
> + * @video_file: pass the path of the video to be embed
> + * @mimetype: pass the mimetype of the video to put in the Media Clip Dictionary
> + * @video_title: Title of the video to set
> + * @img_file: pass png/jpeg image file for poster
> + * @js: set the JS as this javascript stream in rendition action else if it is null set OP
> +
> + * Set the action for the screen annotation specified @poppler_annot
> + *
> + */
> +gboolean
Why gboolean? this should return a new PopplerAction, no?
> +poppler_action_create_rendition_new (PopplerAnnot *poppler_annot,
> + const char* video_file,
> + const char* mimetype,
> + const char* video_title,
> + const char* img_file,
> + const char* js)
Use the same idea than the core, two different methods to create a
rendition action depending on whether it uses a js or not. It would be
something like:
PopplerAction *poppler_action_rendition_new (PopplerDocument *doc, PopplerMedia *media, guint operation, PopplerAnnot *annot);
PopplerAction *poppler_action_rendition_new_for_javascript (PopplerDocument *doc, const gchar *js);
> +{
> + if (!video_file) {
> + error(-1, "Need to pass the video file");
> + return FALSE;
> + }
Use g_return macros to make sure given values are correct.
> + // Extract the video name from the file uri
> + GooString *title = new GooString(video_title);
> + ((AnnotScreen *)poppler_annot->annot)->setTitle(title);
> + poppler_annot->annot->setContents(title);
> + delete(title);
These shouldn't be here, caller should use PopplerAnnot API if she
wants to set a title or contents. There's already
poppler_annot_set_contents(), you would need to add
poppler_annot_screen_set_title() too.
> + XRef *xref = poppler_annot->annot->getXRef();
> + MediaRendition *media_rendition = new MediaRendition (xref, video_file, mimetype);
> + LinkRendition *rendition;
> + if (js == NULL)
> + rendition = new LinkRendition(xref, media_rendition, 0, poppler_annot->annot);
> + else {
> + Object jsObj, obj1, obj2;
> + obj1.initDict(xref);
> + obj1.dictSet("Length", obj2.initInt(strlen(js)));
> + MemStream *jsstream = new MemStream(copyString((char*)js), 0, strlen(js), &obj1);
> + jsObj.initStream(jsstream);
> + rendition = new LinkRendition(xref, &jsObj);
> + }
>
> + ((AnnotScreen *)poppler_annot->annot)->setAction (rendition);
> + poppler_annot->annot->setAppearanceFromFile(img_file);
Same here, there should be a poppler_annot_set_appearance_from_file()
that the caller should use, but not here.
> + return TRUE;
> +}
> +
--
Carlos Garcia Campos
PGP key: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x523E6462
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/poppler/attachments/20110325/7d0919e9/attachment-0001.pgp>
More information about the poppler
mailing list