[poppler] glib/poppler-annot.cc glib/poppler-annot.h poppler/Annot.cc poppler/Annot.h
Albert Astals Cid
aacid at kemper.freedesktop.org
Thu Mar 17 13:10:41 PDT 2011
glib/poppler-annot.cc | 33 ++++
glib/poppler-annot.h | 5
poppler/Annot.cc | 377 ++++++++++++++++++++++++++++++++++++++++++++++++++
poppler/Annot.h | 2
4 files changed, 417 insertions(+)
New commits:
commit 66575c990f379871e4b796befc899de178332670
Author: Srinivas Adicherla <srinivas.adicherla at gmail.com>
Date: Thu Mar 17 20:14:05 2011 +0000
Patch for embedding videos in to the pdf
diff --git a/glib/poppler-annot.cc b/glib/poppler-annot.cc
index 62a0879..c714288 100644
--- a/glib/poppler-annot.cc
+++ b/glib/poppler-annot.cc
@@ -332,6 +332,39 @@ _poppler_annot_screen_new (Annot *annot)
return poppler_annot;
}
+/**
+ * poppler_annot_screen_new:
+ * @doc: a #PopplerDocument
+ * @rect: a #PopplerRectangle
+ * @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
+ *
+ * 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
+ *
+ * Since: 0.18
+ */
+PopplerAnnot *
+poppler_annot_screen_new (PopplerDocument *doc,
+ PopplerRectangle *rect,
+ const char* video_file,
+ const char* mimetype,
+ const char* 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(video_file, mimetype, img_file))
+ return _poppler_annot_screen_new ((Annot*)annot);
+ else
+ return NULL;
+}
/* Public methods */
/**
diff --git a/glib/poppler-annot.h b/glib/poppler-annot.h
index 99cbb74..749d3a3 100644
--- a/glib/poppler-annot.h
+++ b/glib/poppler-annot.h
@@ -218,6 +218,11 @@ PopplerMovie *poppler_annot_movie_get_movie (
/* 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* img_file);
/* PopplerCalloutLine */
GType poppler_annot_callout_line_get_type (void) G_GNUC_CONST;
diff --git a/poppler/Annot.cc b/poppler/Annot.cc
index e73b3ab..ce864f7 100644
--- a/poppler/Annot.cc
+++ b/poppler/Annot.cc
@@ -23,6 +23,7 @@
// Copyright (C) 2008 Michael Vrable <mvrable at cs.ucsd.edu>
// Copyright (C) 2008 Hugo Mercier <hmercier31 at gmail.com>
// Copyright (C) 2009 Ilya Gorenbein <igorenbein at finjan.com>
+// Copyright (C) 2011 Srinivas Adicherla <srinivas.adicherla at gmail.com>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
@@ -62,6 +63,14 @@
#include "Link.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
@@ -4099,6 +4108,374 @@ void AnnotScreen::initialize(XRef *xrefA, Catalog *catalog, Dict* dict) {
}
+static MemStream *load_from_png (FILE *f, Object *imgXObj) {
+#if defined(ENABLE_LIBPNG)
+ png_byte header[8];
+ fread(header, 1, 8, f);
+
+ if (png_sig_cmp(header, 0, 8)) {
+ fclose(f);
+ 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))) {
+ png_destroy_read_struct (&read_png, (png_infopp)NULL, (png_infopp)NULL);
+ fclose(f);
+ error(-1, "png_create_info_struct");
+ return NULL;
+ }
+
+ if (setjmp(png_jmpbuf(read_png))) {
+ png_destroy_read_struct (&read_png, &info_png, (png_infopp)NULL);
+ 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);
+
+ if (png_get_color_type(read_png, info_png) & PNG_COLOR_MASK_ALPHA)
+ png_set_strip_alpha(read_png);
+
+ int width = png_get_image_width(read_png, info_png);
+ int height = png_get_image_height(read_png, info_png);
+ png_read_update_info(read_png, info_png);
+
+ // Read the file
+ if (setjmp(png_jmpbuf(read_png))) {
+ png_destroy_read_struct (&read_png, &info_png, (png_infopp)NULL);
+ fclose(f);
+ error(-1, "png_jmpbuf");
+ return NULL;
+ }
+
+ unsigned int rowbytes = png_get_rowbytes(read_png, info_png);
+ long numbytes = static_cast<long>(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 * rowbytes));
+ }
+
+ png_read_image(read_png, numrows);
+ png_read_end(read_png, NULL);
+
+ Object obj;
+
+ if (png_get_channels(read_png, info_png) == 3)
+ imgXObj->dictSet("ColorSpace", obj.initName("DeviceRGB"));
+ else
+ imgXObj->dictSet("ColorSpace", obj.initName("DeviceGray"));
+
+ imgXObj->dictSet("BitsPerComponent", obj.initInt(png_get_bit_depth(read_png, info_png)));
+ imgXObj->dictSet("Width", obj.initInt(width));
+ imgXObj->dictSet("Height", obj.initInt(height));
+ imgXObj->dictSet("Length", obj.initInt(numbytes));
+
+ MemStream *imgStream = new MemStream (img_buffer, 0, numbytes, imgXObj);
+ imgStream->setNeedFree(gTrue);
+
+ png_destroy_read_struct (&read_png, &info_png, (png_infopp)NULL);
+ free(numrows);
+
+ fclose(f);
+ return imgStream;
+#else
+ fclose(f);
+ error(-1, "Png support was not compiled in");
+ return NULL;
+#endif
+}
+
+static MemStream *load_from_jpeg (FILE *f, Object *imgXObj)
+{
+#if defined(ENABLE_LIBJPEG)
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ jmp_buf setjmp_buffer;
+ JSAMPROW row_pointer[1];
+ unsigned long location = 0;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_decompress(&cinfo);
+
+ if (setjmp(setjmp_buffer)) {
+ jpeg_destroy_decompress(&cinfo);
+ fclose(f);
+ error(-1, "setjmp_buffer");
+ return NULL;
+ }
+
+ jpeg_stdio_src(&cinfo, f);
+ jpeg_read_header(&cinfo, TRUE);
+ jpeg_start_decompress(&cinfo);
+
+ char *raw_image = (char*)malloc(cinfo.output_width*cinfo.output_height*cinfo.num_components);
+ row_pointer[0] = (unsigned char*)malloc(cinfo.output_width*cinfo.num_components);
+
+ while(cinfo.output_scanline < cinfo.image_height) {
+ jpeg_read_scanlines( &cinfo, row_pointer, 1 );
+ for(Guint i = 0; i < cinfo.image_width*cinfo.num_components; i++)
+ raw_image[location++] = row_pointer[0][i];
+ }
+
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+ free(row_pointer[0]);
+ fclose(f);
+
+ Object obj;
+
+ imgXObj->dictSet("Width", obj.initInt (cinfo.output_width));
+ imgXObj->dictSet("Height", obj.initInt(cinfo.output_height));
+ imgXObj->dictSet("BitsPerComponent", obj.initInt(8));
+
+ switch(cinfo.output_components) {
+ case 3:
+ imgXObj->dictSet("ColorSpace", obj.initName("DeviceRGB"));
+ break;
+ case 4:
+ imgXObj->dictSet("ColorSpace", obj.initName("DeviceCMYK"));
+ break;
+ default:
+ imgXObj->dictSet("ColorSpace", obj.initName("DeviceGray"));
+ break;
+ }
+
+ unsigned long length = cinfo.output_width*cinfo.output_height*cinfo.num_components;
+ imgXObj->dictSet("Length", obj.initInt(length));
+
+ MemStream *imgStream = new MemStream (raw_image, 0, length, imgXObj);
+ imgStream->setNeedFree(gTrue);
+ return imgStream;
+#else
+ fclose(f);
+ error(-1, "Jpeg support was not compiled in");
+ return NULL;
+#endif
+}
+
+GBool AnnotScreen::setAction(const char *video_file, const char *mimetype, const char *img_file) {
+
+ if (!video_file) {
+ error(-1, "Need to pass the video file");
+ 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);
+
+ // Extract the video name from the file uri
+ const char *video_name = strrchr(video_file, '/');
+ if (video_name) {
+ video_name++;
+ } else {
+ video_name = video_file;
+ }
+
+ Object obj1, obj2, obj3, obj4;
+
+ annotObj.dictSet("T", obj1.initString(new GooString(video_name))); // title
+ annotObj.dictSet("Contents", obj2.initString(new GooString(video_name))); // alternate text to be dispalyed for the annotation
+ annotObj.dictSet("F", obj3.initInt(4)); // No Zoom
+
+ Object formXObj;
+ formXObj.initDict(xref);
+
+ formXObj.dictSet("Type", obj1.initName("XObject"));
+ formXObj.dictSet("Subtype", obj2.initName("Form"));
+
+ Object bbox;
+ bbox.initArray(xref);
+ bbox.arrayAdd(obj1.initReal(0.0000));
+ bbox.arrayAdd(obj2.initReal(0.0000));
+ bbox.arrayAdd(obj3.initReal(1.0000));
+ bbox.arrayAdd(obj4.initReal(1.0000));
+ formXObj.dictSet("BBox", &bbox);
+
+ Ref imgRef;
+ Object imgXObj;
+ imgXObj.initDict(xref);
+ imgXObj.dictSet("Type", obj1.initName("XObject"));
+ imgXObj.dictSet("Subtype", obj2.initName("Image"));
+
+ if (img_file) { //show the poster, else do not show anything
+ 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.free();
+ obj2.free();
+ obj3.free();
+ obj4.free();
+ bbox.free();
+ imgXObj.free();
+ return gFalse;
+ }
+
+ obj1.initStream(imgStream);
+ imgRef = xref->addIndirectObject(&obj1);
+
+ obj2.initDict(xref); // Image XObject
+ obj2.dictSet("Im1", obj3.initRef(imgRef.num, imgRef.gen));
+ obj4.initDict(xref);
+ obj4.dictSet("XObject", &obj2);
+
+ formXObj.dictSet("Resources", &obj4);
+
+ GooString *newString = new GooString();
+ newString->append("/Im1 Do");
+ formXObj.dictSet("Length", obj1.initInt(newString->getLength()));
+
+ MemStream *fstream = new MemStream(copyString(newString->getCString()), 0, newString->getLength(), &formXObj);
+ fstream->setNeedFree(gTrue);
+ delete(newString);
+
+ obj1.initStream(fstream);
+ Ref appRef = xref->addIndirectObject(&obj1);
+
+ obj2.initDict(xref);
+ obj2.dictSet("N", obj3.initRef(appRef.num, appRef.gen));
+ obj2.dictSet("R", obj3.initRef(appRef.num, appRef.gen));
+ annotObj.dictSet("AP", &obj2);
+
+ formXObj.initDict(xref);
+ formXObj.dictSet("Type", obj3.initName("XObject"));
+ formXObj.dictSet("Subtype", obj4.initName("Form"));
+
+ bbox.initArray(xref);
+ bbox.arrayAdd(obj1.initReal(0.0000));
+ bbox.arrayAdd(obj2.initReal(0.0000));
+ bbox.arrayAdd(obj3.initReal(1.0000));
+ bbox.arrayAdd(obj4.initReal(1.0000));
+ formXObj.dictSet("BBox", &bbox);
+
+ obj2.initDict(xref);
+ obj2.dictSet("Im1", obj3.initRef(imgRef.num, imgRef.gen));
+ obj1.initDict(xref);
+ obj1.dictSet("XObject", &obj2);
+ formXObj.dictSet("Resources", &obj1);
+
+ newString = new GooString();
+ newString->append("/Im1 Do");
+ formXObj.dictSet("Length", obj1.initInt(newString->getLength()));
+
+ fstream = new MemStream(copyString(newString->getCString()), 0, newString->getLength(), &formXObj);
+ fstream->setNeedFree(gTrue);
+ delete(newString);
+
+ obj1.initStream(fstream);
+ appRef = xref->addIndirectObject(&obj1);
+
+ obj2.initDict(xref); // MK dictionary
+ obj2.dictSet("I", obj3.initRef(appRef.num, appRef.gen));
+ annotObj.dictSet("MK", &obj2);
+ }
+
+ // Rendition Action to be add
+ Object actionDict;
+ actionDict.initDict(xref);
+ actionDict.dictSet("Type", obj1.initName("Action"));
+ actionDict.dictSet("S", obj2.initName("Rendition"));
+ actionDict.dictSet("OP", obj3.initInt(0));
+
+ // Media Rendition
+ Object MRendition;
+ MRendition.initDict(xref);
+ MRendition.dictSet("S", obj1.initName("MR"));
+ MRendition.dictSet("N", obj2.initString(new GooString(video_name)));
+
+ // Media Clip Dictionary
+ Object MClipDict;
+ MClipDict.initDict(xref);
+ MClipDict.dictSet("S", obj1.initName("MCD"));
+ MClipDict.dictSet("N", obj2.initString(new GooString(video_name)));
+
+ if (mimetype) {
+ MClipDict.dictSet("CT", obj3.initString(new GooString(mimetype)));
+ }
+
+ obj4.initString(new GooString("TEMPACCESS"));
+ obj1.initDict(xref);
+ obj1.dictSet("TF", &obj4);
+ MClipDict.dictSet("P", &obj1);
+
+ // File Specification Dictionary
+ Object fsDict;
+ fsDict.initDict(xref);
+ fsDict.dictSet("Type", obj1.initName("Filespec"));
+ fsDict.dictSet("F", obj2.initName((char*)video_name));
+ fsDict.dictSet("UF", obj3.initName((char*)video_name));
+
+ obj3.initDict(xref); // file stream eictionary
+ obj3.dictSet("DL", obj1.initInt(size));
+ obj3.dictSet("Length", obj2.initInt(size));
+
+ FileStream *stream = new FileStream(fs, 0, 0, size, &obj3);
+ obj1.initStream(stream);
+
+ Ref newRef;
+ newRef = xref->addIndirectObject(&obj1);
+ obj2.initRef(newRef.num, newRef.gen);
+ obj1.initDict(xref);
+ obj1.dictSet("F", &obj2);
+ fsDict.dictSet("EF", &obj1);
+
+ newRef = xref->addIndirectObject(&fsDict);
+ obj1.initRef(newRef.num, newRef.gen);
+ MClipDict.dictSet("D", &obj1);
+
+ newRef = xref->addIndirectObject(&MClipDict);
+ obj1.initRef(newRef.num, newRef.gen);
+ MRendition.dictSet("C", &obj1);
+
+ newRef = xref->addIndirectObject(&MRendition);
+ obj1.initRef(newRef.num, newRef.gen);
+ actionDict.dictSet("R", &obj1);
+
+ newRef = xref->addIndirectObject(&actionDict);
+ obj1.initRef(newRef.num, newRef.gen);
+ annotObj.dictSet("A", &obj1);
+
+ return gTrue;
+}
+
//------------------------------------------------------------------------
// AnnotStamp
//------------------------------------------------------------------------
diff --git a/poppler/Annot.h b/poppler/Annot.h
index 93f82bf..e5c578e 100644
--- a/poppler/Annot.h
+++ b/poppler/Annot.h
@@ -22,6 +22,7 @@
// Copyright (C) 2008 Pino Toscano <pino at kde.org>
// Copyright (C) 2008 Tomas Are Haavet <tomasare at gmail.com>
// Copyright (C) 2009, 2010 Albert Astals Cid <aacid at kde.org>
+// Copyright (C) 2011 Srinivas Adicherla <srinivas.adicherla at gmail.com>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
@@ -722,6 +723,7 @@ class AnnotScreen: public Annot {
AnnotAppearanceCharacs *getAppearCharacs() { return appearCharacs; }
LinkAction* getAction() { return action; }
Object* getAdditionActions() { return &additionAction; }
+ GBool setAction(const char *video_file, const char *mimetype, const char *img_file);
private:
void initialize(XRef *xrefA, Catalog *catalog, Dict *dict);
More information about the poppler
mailing list