[poppler] [patch] a sketch of yflip for PNGWriter for pdftohtml

Ihar `Philips` Filipau thephilips at gmail.com
Sun Mar 11 09:44:02 PDT 2012


Hi All!

I had some spare time and source code at hand and tried to solve
another of my problems with the pdftohtml: vertically flipped images.
(Also a long standing bug:
https://bugs.freedesktop.org/show_bug.cgi?id=32340 )

An hour (w)hacking at the code produced that piece of frankenstein
code you might see below.

Short summary: implement very crude yflip in PNGWriter by buffering
rows and transpose/write them before closing the PNG image; disable
JPEGs and write always  PNGs. (The piece of code detecting flip was
lifted from pdf2xml project.)


Is there any official ideas on how to implement the image flip in the pdftohtml?

Are other flip possible too? rotations? To date I have seen only PDFs
with upside down images.

Have Fun.


 goo/PNGWriter.cc       |   89 ++++++++++++++++++++++++++++++++++++++++++++++++
 goo/PNGWriter.h        |   12 ++++++-
 utils/HtmlOutputDev.cc |   47 ++++++++++++++++++++++++-
 3 files changed, 145 insertions(+), 3 deletions(-)

diff --git a/goo/PNGWriter.cc b/goo/PNGWriter.cc
index fe8b79e..b60a540 100644
--- a/goo/PNGWriter.cc
+++ b/goo/PNGWriter.cc
@@ -30,6 +30,11 @@ PNGWriter::PNGWriter(Format formatA) : format(formatA)
 	icc_data_size = 0;
 	icc_name = NULL;
 	sRGB_profile = false;
+
+	y_flip = false;
+	row_buf = NULL;
+	row_buf_num = 0;
+	row_buf_cap = 0;
 }

 PNGWriter::~PNGWriter()
@@ -64,6 +69,9 @@ bool PNGWriter::init(FILE *f, int width, int height,
int hDPI, int vDPI)
         png_const_bytep icc_data_ptr = (png_const_bytep)icc_data;
 #endif

+	image_width = width;
+	image_height = height;
+
 	/* initialize stuff */
 	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 	if (!png_ptr) {
@@ -99,18 +107,22 @@ bool PNGWriter::init(FILE *f, int width, int
height, int hDPI, int vDPI)
 		case RGB:
 			bit_depth = 8;
 			color_type = PNG_COLOR_TYPE_RGB;
+			bytes_per_row = 3 * image_width;
 			break;
 		case RGBA:
 			bit_depth = 8;
 			color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+			bytes_per_row = 4 * image_width;
 			break;
 		case GRAY:
 			bit_depth = 8;
 			color_type = PNG_COLOR_TYPE_GRAY;
+			bytes_per_row = 1 * image_width;
 			break;
 		case MONOCHROME:
 			bit_depth = 1;
 			color_type = PNG_COLOR_TYPE_GRAY;
+			bytes_per_row = 1 * image_width;
 			break;
 	}
 	png_byte interlace_type = PNG_INTERLACE_NONE;
@@ -139,6 +151,17 @@ bool PNGWriter::init(FILE *f, int width, int
height, int hDPI, int vDPI)

 bool PNGWriter::writePointers(unsigned char **rowPointers, int rowCount)
 {
+	/* writes the image at once */
+	if (y_flip) {
+		if (rowCount == image_height) {
+			for (int i = 0; i<rowCount/2; i++) {
+				unsigned char *tmp = rowPointers[i];
+				int ri = rowCount-i-1;
+				rowPointers[i] = rowPointers[ri];
+				rowPointers[ri] = tmp;
+			}
+		}
+	}
 	png_write_image(png_ptr, rowPointers);
 	/* write bytes */
 	if (setjmp(png_jmpbuf(png_ptr))) {
@@ -152,6 +175,10 @@ bool PNGWriter::writePointers(unsigned char
**rowPointers, int rowCount)
 bool PNGWriter::writeRow(unsigned char **row)
 {
 	// Write the row to the file
+	// or just buffer if y_flip
+	if (y_flip) {
+		return bufRows( row, 1 );
+	}
 	png_write_rows(png_ptr, row, 1);
 	if (setjmp(png_jmpbuf(png_ptr))) {
 		error(errInternal, -1, "error during png row write");
@@ -163,6 +190,37 @@ bool PNGWriter::writeRow(unsigned char **row)

 bool PNGWriter::close()
 {
+	/* y_flip is implemented by buffering the rows - flush the rows here. */
+	if (y_flip && row_buf_num) {
+		/* transpose the row_buf to accomodate the y_flip */
+		for (int i = 0; i<row_buf_num/2; i++) {
+			unsigned char *tmp = row_buf[i];
+			int ri = row_buf_num-i-1;
+			row_buf[i] = row_buf[ri];
+			row_buf[ri] = tmp;
+		}
+		/* write it */
+		if (row_buf_num == image_height)
+		{
+			png_write_image(png_ptr, row_buf);
+			/* write bytes */
+			if (setjmp(png_jmpbuf(png_ptr))) {
+				error(errInternal, -1, "Error during writing bytes");
+				return false;
+			}
+		}
+		else
+		{
+			for (int i = 0; i<row_buf_num; i++) {
+				png_write_rows(png_ptr, &row_buf[i], 1);
+				if (setjmp(png_jmpbuf(png_ptr))) {
+					error(errInternal, -1, "error during png row write");
+					return false;
+				}
+			}
+		}
+	}
+
 	/* end write */
 	png_write_end(png_ptr, info_ptr);
 	if (setjmp(png_jmpbuf(png_ptr))) {
@@ -173,4 +231,35 @@ bool PNGWriter::close()
 	return true;
 }

+void PNGWriter::setFlipY(bool flip)
+{
+	y_flip = flip;
+}
+
+bool PNGWriter::bufRows( unsigned char **rows, int row_count )
+{
+	if (!row_count)
+		return true;
+
+	if (row_buf_num + row_count > row_buf_cap) {
+
+		if (row_buf_cap == 0)
+			row_buf_cap = 32;
+
+		while ( row_buf_num + row_count > row_buf_cap )
+			row_buf_cap = (row_buf_cap/4)*5;	/* increase capacity by 25% */
+
+		row_buf = (unsigned char **)grealloc( row_buf,
row_buf_cap*sizeof(unsigned char *) );
+	}
+
+	while (row_count > 0) {
+		row_buf[ row_buf_num ] = (unsigned char *)gmalloc( bytes_per_row );
+		memcpy( row_buf[ row_buf_num ], *rows, bytes_per_row );
+		row_buf_num++;
+		rows++;
+		row_count--;
+	}
+	return true;
+}
+
 #endif
diff --git a/goo/PNGWriter.h b/goo/PNGWriter.h
index f22495d..efc8f58 100644
--- a/goo/PNGWriter.h
+++ b/goo/PNGWriter.h
@@ -47,7 +47,8 @@ class PNGWriter : public ImgWriter
 		bool writeRow(unsigned char **row);
 		
 		bool close();
-	
+
+		void setFlipY(bool = true);
 	private:
 		Format format;
 		png_structp png_ptr;
@@ -56,6 +57,15 @@ class PNGWriter : public ImgWriter
 		int icc_data_size;
 		char *icc_name;
 		bool sRGB_profile;
+
+		int image_width, image_height;
+		int bytes_per_row;
+
+		bool y_flip;
+		unsigned char **row_buf;
+		int row_buf_num, row_buf_cap;
+
+		bool bufRows( unsigned char **rowPointers, int rowCount );
 };

 #endif
diff --git a/utils/HtmlOutputDev.cc b/utils/HtmlOutputDev.cc
index b38af4d..cd80f6d 100644
--- a/utils/HtmlOutputDev.cc
+++ b/utils/HtmlOutputDev.cc
@@ -1295,7 +1295,9 @@ void HtmlOutputDev::drawJpegImage(GfxState
*state, Stream *str)
   }

   // initialize stream
+  fprintf( stderr, "jpeg stream kind before: %d\n",
int(str->getKind()) ); // StreamKind
   str = str->getNextStream();
+  fprintf( stderr, "jpeg stream kind after: %d\n",
int(str->getKind()) ); // StreamKind
   str->reset();

   // copy the stream
@@ -1345,6 +1347,47 @@ void HtmlOutputDev::drawPngImage(GfxState
*state, Stream *str, int width, int he
     return;
   }

+  if (1) {
+        double x0, y0; // top left corner of image
+        double w0, h0, w1, h1; // size of image
+        double xt, yt, wt, ht;
+        GBool rotate, xFlip, yFlip;
+
+        // get image position and size
+        state->transform(0, 0, &xt, &yt);
+        state->transformDelta(1, 1, &wt, &ht);
+        if (wt > 0) {
+                x0 = (xt);
+                w0 = (wt);
+        } else {
+                x0 = (xt + wt);
+                w0 = (-wt);
+        }
+        if (ht > 0) {
+                y0 = (yt);
+                h0 = (ht);
+        } else {
+                y0 = (yt + ht);
+                h0 = (-ht);
+        }
+        state->transformDelta(1, 0, &xt, &yt);
+        rotate = fabs(xt) < fabs(yt);
+        if (rotate) {
+                w1 = h0;
+                h1 = w0;
+                xFlip = ht < 0;
+                yFlip = wt > 0;
+        } else {
+                w1 = w0;
+                h1 = h0;
+                xFlip = wt < 0;
+                yFlip = ht > 0;
+        }
+        //fprintf( stderr, "image xFlip:%d yFlip:%d\n", int(xFlip),
int(yFlip) ); // StreamKind
+
+        if (yFlip) writer->setFlipY();
+  }
+
   if (!isMask) {
     Guchar *p;
     GfxRGB rgb;
@@ -1441,7 +1484,7 @@ void HtmlOutputDev::drawImageMask(GfxState
*state, Object *ref, Stream *str,
   }

   // dump JPEG file
-  if (dumpJPEG  && str->getKind() == strDCT) {
+  if (0 && dumpJPEG  && str->getKind() == strDCT) {
     drawJpegImage(state, str);
   }
   else {
@@ -1466,7 +1509,7 @@ void HtmlOutputDev::drawImage(GfxState *state,
Object *ref, Stream *str,
   /*if( !globalParams->getErrQuiet() )
     printf("image stream of kind %d\n", str->getKind());*/
   // dump JPEG file
-  if (dumpJPEG && str->getKind() == strDCT) {
+  if (0 && dumpJPEG && str->getKind() == strDCT) {
     drawJpegImage(state, str);
   }
   else {


More information about the poppler mailing list