[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