Hi all,
I made a patch for embedding videos into the pdf file. I created a
Screen annotation, and add a Rendition action to it. For appearance I took
the image from the user (only accepting png), later I will do for others
also. I tested opening the embedded pdf files using Adobe and Foxit Reader
it was successfully opened for me. I tried for swf and mpg files.
I am asking the user to specify the image because there are lot of video
formats, so its time consuming for creating an image from the video frame
for all types. ( May be in the future i add that or anybody can help me in
that).
I added the same in bugzilla.
Please give me suggestions to make this successful.
Thanks in Advance.
A Srinivas
PopplerDocument *document;
PopplerPage *page;
// pass the fileuri to create a document
document = poppler_document_new_from_file (fileuri, NULL, NULL);
page = poppler_document_get_page (document, page_number);
// pass video file uri and its mimetype and pass some png image for appearance
PopplerAnnot *annot = poppler_annot_screen_new (document, area, video_file_uri,
mimetype, "pngimage.png");
poppler_page_add_annot (page, annot);
//save it into the new file
poppler_document_save (document, newfileuri, NULL);
Index: poppler/Annot.h
===================================================================
--- poppler/Annot.h (revision 4)
+++ poppler/Annot.h (working copy)
@@ -740,6 +740,7 @@
AnnotAppearanceCharacs *getAppearCharacs() { return appearCharacs; }
LinkAction* getAction() { return action; }
Object* getAdditionActions() { return &additionAction; }
+ GBool SetAction(XRef *xrefA, Catalog *catalogA, const char* video_file, const char* mimetype, const char* img_file);
private:
void initialize(XRef *xrefA, Catalog *catalog, Dict *dict);
Index: poppler/Annot.cc
===================================================================
--- poppler/Annot.cc (revision 4)
+++ poppler/Annot.cc (working copy)
@@ -61,6 +61,7 @@
#include "DateInfo.h"
#include "Link.h"
#include <string.h>
+#include <png.h>
#define fieldFlagReadOnly 0x00000001
#define fieldFlagRequired 0x00000002
@@ -4258,6 +4259,276 @@
}
+static MemStream* load_from_png (const char* img_file, Object *imgXObj, XRef *xrefA) {
+ FILE *f;;
+
+ if (!(f = fopen(img_file, "rb"))) {
+ error(-1, "Couldn't open file '%s''", img_file);
+ return NULL;
+ }
+
+ png_byte header[8];
+ fread(header, 1, 8, f);
+
+ if (png_sig_cmp(header, 0, 8)) {
+ error(-1, "The file could not be recognized as a PNG file.");
+ return NULL;
+ }
+
+ png_structp read_png;;
+ if (!(read_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))) {
+ fclose(f);
+ error(-1, "png_create_read_struct");
+ return NULL;
+ }
+
+ png_infop info_png;
+ if (!(info_png = png_create_info_struct(read_png))) {
+ fclose(f);
+ error(-1, "png_create_info_struct");
+ return NULL;
+ }
+
+ if (setjmp(png_jmpbuf(read_png))) {
+ fclose(f);
+ error(-1, "png_jmpbuf");
+ return NULL;
+ }
+
+ png_init_io(read_png, f);
+ png_set_sig_bytes(read_png, 8);
+ png_read_info(read_png, info_png);
+
+ int width = info_png->width;
+ int height = info_png->height;
+ png_read_update_info(read_png, info_png);
+
+ // Read the file
+ if (setjmp(png_jmpbuf(read_png))) {
+ fclose(f);
+ error(-1, "png_jmpbuf");
+ return NULL;
+ }
+
+ long numbytes = static_cast<long>(info_png->rowbytes * height);
+ char *img_buffer = static_cast<char*>(malloc(sizeof(char) * numbytes));
+ png_bytepp numrows = static_cast<png_bytepp>(malloc(sizeof(png_bytep)*height));
+
+ for(int i=0; i<height; i++) {
+ numrows[i] = reinterpret_cast<png_bytep>(img_buffer + (i * info_png->rowbytes));
+ }
+
+ png_read_image(read_png, numrows);
+ fclose(f);
+
+ Object obj;
+ imgXObj->dictSet("BitsPerComponent", obj.initInt(info_png->bit_depth));
+
+ char *temp_img_buffer = NULL;
+ if (info_png->channels == 3)
+ imgXObj->dictSet("ColorSpace", obj.initName("DeviceRGB"));
+ else if(info_png->channels == 4) {
+ imgXObj->dictSet("ColorSpace", obj.initName("DeviceRGB"));
+ // This is a hack that png image doesn't have CMYK, so I am removing the ALPHA from the image
+ // I am taking only RGB pixel values and displaying it.
+ int i=0;
+ temp_img_buffer = (char*)malloc(3*width*height*sizeof(char));
+
+ for(int j=0; j<(info_png->rowbytes*height); j++) {
+ if((j+1)%4 != 0) {
+ temp_img_buffer[i++] = img_buffer[j];
+ }
+ }
+ free(img_buffer);
+ }
+ else
+ imgXObj->dictSet("ColorSpace", obj.initName("DeviceGray"));
+
+ imgXObj->dictSet("Width", obj.initInt(width));
+ imgXObj->dictSet("Height", obj.initInt(height));
+
+ GooString *imgbuf = new GooString();
+
+ if (temp_img_buffer)
+ imgbuf->append(temp_img_buffer, 3*width*height);
+ else
+ imgbuf->append(img_buffer, info_png->rowbytes * height);
+
+ imgXObj->dictSet("Length", obj.initInt(imgbuf->getLength()));
+ MemStream *imgStream = new MemStream (imgbuf->getCString(), 0, imgbuf->getLength(), imgXObj);
+ return imgStream;
+}
+
+GBool AnnotScreen::SetAction(XRef *xrefA, Catalog *catalogA, const char* video_file, const char* mimetype, const char* img_file) {
+ if (!video_file) {
+ error(-1, "Need to pass the video file uri");
+ return gFalse;
+ }
+
+ if (!img_file) {
+ error(-1, "Need to pass the image for appearance");
+ return gFalse;
+ }
+
+ FILE *fs;
+ unsigned int size = 0;
+ if (!(fs = fopen(video_file, "rb"))) {
+ error(-1, "Couldn't open video file '%s'", video_file);
+ return gFalse;
+ }
+ fseek(fs, 0, SEEK_END);
+ size = ftell(fs);
+ fseek(fs, 0, SEEK_CUR);
+
+ // Extract the video name from the file uri
+ const char *video_name = strrchr(video_file, '/');
+ video_name++;
+
+ Object obj, obj1, obj2;
+ Ref newRef;
+
+ annotObj.dictSet("T", obj.initString(new GooString(video_name))); // title
+ annotObj.dictSet("Contents", obj.initString(new GooString(video_name))); // alternate text to be dispalyed for the annotation
+ annotObj.dictSet("F", obj.initInt(4)); // No Zoom
+
+ Object formXObj;
+ formXObj.initDict(xrefA);
+ formXObj.dictSet("Type", obj.initName("XObject"));
+ formXObj.dictSet("Subtype", obj.initName("Form"));
+ obj.initArray(xrefA);
+ obj.arrayAdd(obj1.initReal(0.0000));
+ obj.arrayAdd(obj1.initReal(0.0000));
+ obj.arrayAdd(obj1.initReal(1.0000));
+ obj.arrayAdd(obj1.initReal(1.0000));
+ formXObj.dictSet("BBox", &obj);
+
+ Object imgXObj;
+ imgXObj.initDict(xrefA);
+ imgXObj.dictSet("Type", obj.initName("XObject"));
+ imgXObj.dictSet("Subtype", obj.initName("Image"));
+
+ // Load the stream from the png file
+ MemStream *imgStream = load_from_png (img_file, &imgXObj, xrefA);
+ if (!imgStream)
+ return gFalse;
+
+ obj.initStream(imgStream);
+ Ref imgRef = xrefA->addIndirectObject(&obj);
+
+ obj1.initDict(xrefA); // Image XObject
+ obj1.dictSet("Im1", obj2.initRef(imgRef.num, imgRef.gen));
+ obj.initDict(xrefA);
+ obj.dictSet("XObject", &obj1);
+
+ formXObj.dictSet("Resources", &obj);
+
+ GooString *newString = new GooString();
+ newString->append("/Im1 Do");
+ formXObj.dictSet("Length", obj.initInt(newString->getLength()));
+
+ MemStream *fstream = new MemStream(copyString(newString->getCString()), 0, newString->getLength(), &formXObj);
+ Object objStream;
+ objStream.initStream(fstream);
+
+ Ref appRef = xrefA->addIndirectObject(&objStream);
+ obj.initRef(appRef.num, appRef.gen);
+ obj1.initDict(xrefA);
+ obj1.dictSet("N", &obj);
+ annotObj.dictSet("AP", &obj1);
+
+ formXObj.initDict(xrefA);
+ formXObj.dictSet("Type", obj1.initName("XObject"));
+ formXObj.dictSet("Subtype", obj1.initName("Form"));
+ obj1.initArray(xrefA);
+ obj1.arrayAdd(obj.initReal(0.0000));
+ obj1.arrayAdd(obj.initReal(0.0000));
+ obj1.arrayAdd(obj.initReal(1.0000));
+ obj1.arrayAdd(obj.initReal(1.0000));
+ formXObj.dictSet("BBox", &obj1);
+
+ obj1.initDict(xrefA);
+ obj1.dictSet("Im1", obj2.initRef(imgRef.num, imgRef.gen));
+ obj.initDict(xrefA);
+ obj.dictSet("XObject", &obj1);
+ formXObj.dictSet("Resources", &obj);
+
+ newString = new GooString();
+ newString->append("/Im1 Do");
+ formXObj.dictSet("Length", obj.initInt(newString->getLength()));
+
+ fstream = new MemStream(copyString(newString->getCString()), 0, newString->getLength(), &formXObj);
+ objStream.initStream(fstream);
+ appRef = xrefA->addIndirectObject(&objStream);
+ obj.initRef(appRef.num, appRef.gen);
+
+ obj1.initDict(xrefA); // MK dictionary
+ obj1.dictSet("I", &obj);
+ annotObj.dictSet("MK", &obj1);
+
+ // Rendition Action to be add
+ Object actionDict;
+ actionDict.initDict(xrefA);
+ actionDict.dictSet("Type", obj1.initName("Action"));
+ actionDict.dictSet("S", obj1.initName("Rendition"));
+ actionDict.dictSet("OP", obj1.initInt(0));
+ actionDict.dictSet("AN", obj1.initRef(ref.num, ref.gen));
+
+ // Media Rendition
+ Object MRendition;
+ MRendition.initDict(xrefA);
+ MRendition.dictSet("S", obj1.initName("MR"));
+ MRendition.dictSet("N", obj1.initString(new GooString(video_name)));
+
+ // Media Clip Dictionary
+ Object MClipDict;
+ MClipDict.initDict(xrefA);
+ MClipDict.dictSet("S", obj1.initName("MCD"));
+ if (mimetype)
+ MClipDict.dictSet("CT", obj1.initString(new GooString(mimetype)));
+ MClipDict.dictSet("N", obj1.initString(new GooString(video_name)));
+ obj.initDict(xrefA);
+ obj.dictSet("TF", obj1.initString(new GooString("TEMPACCESS")));
+ MClipDict.dictSet("P", &obj);
+
+ // File Specification Dictionary
+ Object fsDict;
+ fsDict.initDict(xrefA);
+ fsDict.dictSet("Type",obj1.initName("Filespec"));
+ fsDict.dictSet("F", obj1.initName((char*)video_name));
+ fsDict.dictSet("UF", obj1.initName((char*)video_name));
+
+ obj2.initDict(xrefA); // file stream dictionary
+ obj2.dictSet("DL", obj1.initInt(size));
+ obj2.dictSet("Length", obj1.initInt(size));
+
+ FileStream *stream = new FileStream(fs, 0, 0, size, &obj2);
+ obj.initStream(stream);
+
+ newRef = xrefA->addIndirectObject(&obj);
+ obj1.initRef(newRef.num, newRef.gen);
+ obj.initDict(xrefA);
+ obj.dictSet("F", &obj1);
+ fsDict.dictSet("EF", &obj);
+
+ newRef = xrefA->addIndirectObject(&fsDict);
+ obj.initRef(newRef.num, newRef.gen);
+ MClipDict.dictSet("D", &obj);
+
+ newRef = xrefA->addIndirectObject(&MClipDict);
+ obj.initRef(newRef.num, newRef.gen);
+ MRendition.dictSet("C", &obj);
+
+ newRef = xrefA->addIndirectObject(&MRendition);
+ obj.initRef(newRef.num, newRef.gen);
+ actionDict.dictSet("R", &obj);
+
+ newRef = xrefA->addIndirectObject(&actionDict);
+ obj.initRef(newRef.num, newRef.gen);
+ annotObj.dictSet("A", &obj);
+
+ return gTrue;
+}
+
//------------------------------------------------------------------------
// AnnotStamp
//------------------------------------------------------------------------
Index: glib/poppler-annot.cc
===================================================================
--- glib/poppler-annot.cc (revision 4)
+++ glib/poppler-annot.cc (working copy)
@@ -332,7 +332,37 @@
return poppler_annot;
}
+/**
+ * poppler_annot_screen_new:
+ * @doc: a #PopplerDocument
+ * @rect: a #PopplerRectangle
+ * @vide_file: path to the video file
+ * @mimetype: mimetype of the video file
+ * @png_img_file: pass the png image file
+ * 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,
+ const char* video_file,
+ const char* mimetype,
+ const char* png_img_file)
+{
+ AnnotScreen *annot;
+ PDFRectangle pdf_rect(rect->x1, rect->y1,
+ rect->x2, rect->y2);
+ annot = new AnnotScreen (doc->doc->getXRef(), &pdf_rect, doc->doc->getCatalog());
+ if (annot->SetAction(doc->doc->getXRef(), doc->doc->getCatalog(), video_file, mimetype, png_img_file))
+ return _poppler_annot_screen_new ((Annot*)annot);
+ else
+ return NULL;
+}
/* Public methods */
/**
* poppler_annot_get_annot_type:
Index: glib/poppler-annot.h
===================================================================
--- glib/poppler-annot.h (revision 4)
+++ glib/poppler-annot.h (working copy)
@@ -218,6 +218,11 @@
/* PopplerAnnotScreen */
GType poppler_annot_screen_get_type (void) G_GNUC_CONST;
PopplerAction *poppler_annot_screen_get_action (PopplerAnnotScreen *poppler_annot);
+PopplerAnnot *poppler_annot_screen_new (PopplerDocument *doc,
+ PopplerRectangle *rect,
+ const char* video_file,
+ const char* mimetype,
+ const char* png_img_file);
/* PopplerCalloutLine */
GType poppler_annot_callout_line_get_type (void) G_GNUC_CONST;
_______________________________________________
poppler mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/poppler