diff --git a/utils/HtmlOutputDev.cc b/utils/HtmlOutputDev.cc
index b38af4d..eae8853 100644
--- a/utils/HtmlOutputDev.cc
+++ b/utils/HtmlOutputDev.cc
@@ -58,6 +58,7 @@
#include "Page.h"
#include "Annot.h"
#include "PNGWriter.h"
+#include "JpegWriter.h"
#include "GlobalParams.h"
#include "HtmlOutputDev.h"
#include "HtmlFonts.h"
@@ -1274,33 +1275,248 @@ void HtmlOutputDev::drawChar(GfxState *state, double x, double y,
pages->addChar(state, x, y, dx, dy, originX, originY, u, uLen);
}
-void HtmlOutputDev::drawJpegImage(GfxState *state, Stream *str)
+// based on code from pdf2xml
+static void getImageFlip( GfxState *state, GBool &xFlip, GBool &yFlip )
+{
+ double xt, yt, wt, ht;
+ GBool rotate;
+
+ // get image position and size
+ //state->transform(0, 0, &xt, &yt);
+ state->transformDelta(1, 1, &wt, &ht);
+
+ state->transformDelta(1, 0, &xt, &yt);
+
+ rotate = fabs(xt) < fabs(yt);
+
+ if (rotate) {
+ xFlip = ht < 0;
+ yFlip = wt > 0;
+ } else {
+ xFlip = wt < 0;
+ yFlip = ht > 0;
+ }
+}
+
+
+static void imageFlipY( unsigned char **rowPoiners, int rowCount )
+{
+ for (int i = 0; igetNumPixelComps(),
+ colorMap->getBits());
+ imgStr->reset();
+
+ // For each line...
+ for (int y = 0; y < height; y++) {
+
+ // Convert into an RGB row
+ p = imgStr->getLine();
+ unsigned char *row = (unsigned char *) gmalloc(3 * width); // 3 bytes/pixel: RGB
+
+ for (int x = 0; x < width; x++) {
+ colorMap->getRGB(p, &rgb);
+ // Write the RGB pixels into the row
+ row[3*x]= colToByte(rgb.r);
+ row[3*x+1]= colToByte(rgb.g);
+ row[3*x+2]= colToByte(rgb.b);
+ p += colorMap->getNumPixelComps();
+ }
+
+ // Buffer the row
+ rows[y] = row;
+ }
+
+ imgStr->close();
+ delete imgStr;
+
+ return rows;
+}
+
+static unsigned char **monochromeImageToRows(Stream *str, int width, int height)
+{
+ unsigned char **rows;
+
+ rows = (unsigned char **)gmalloc( height*sizeof(void*) );
+ memset( rows, 0, height*sizeof(void *) );
+
+ int bytes_per_row = ((width + 7) / 8);
+ Guchar *bit_row = (Guchar *)gmalloc( bytes_per_row );
+
+ str->reset();
+ for (int ri = 0; ri < height; ++ri)
+ {
+ // read the row bits of the mask
+ for (int i = 0; i < bytes_per_row; ++i) {
+ if ((bit_row[i] = str->getChar()) != EOF) {
+ } else {
+ error(errIO, -1, "Image mask stream is corrupt for image" );
+ gfree(bit_row);
+ freeRows( rows, height );
+ return NULL;
+ }
+ }
+
+ Guchar *image_row = (Guchar *)gmalloc( width );
+
+ // convert bits into a bytes for PNG
+ for(int i = 0; i < width; i++)
+ image_row[i] = ((bit_row[i/8]) & (1<<(7-(i%8)))) ? 0x00 : 0xff ; // invert for PNG
+
+ rows[ri] = image_row;
+ }
+
+ gfree(bit_row);
+
+ return rows;
+}
+
+GooString *HtmlOutputDev::createImageName(const char *ext)
{
- FILE *f1;
- int c;
GooString *fName=new GooString(Docname);
fName->append("-");
GooString *pgNum= GooString::fromInt(pageNum);
GooString *imgnum= GooString::fromInt(pages->getNumImages()+1);
// open the image file
- fName->append(pgNum)->append("_")->append(imgnum)->append(".jpg");
+ fName->append(pgNum)->append("_")->append(imgnum)->append(".")->append(ext);
delete pgNum;
delete imgnum;
+ return fName;
+}
+
+void HtmlOutputDev::drawJpegImage(GfxState *state, Stream *str, int width, int height,
+ GfxImageColorMap *colorMap)
+{
+ FILE *f1;
+ int c;
+
+ GooString *fName=createImageName("jpg");
+
if (!(f1 = fopen(fName->getCString(), "wb"))) {
error(errIO, -1, "Couldn't open image file '%s'", fName->getCString());
delete fName;
return;
}
- // initialize stream
- str = str->getNextStream();
- str->reset();
+#ifdef ENABLE_LIBJPEG
+ GBool xFlip, yFlip;
+ getImageFlip( state, xFlip, yFlip );
+ GBool needFlip = xFlip || yFlip;
- // copy the stream
- while ((c = str->getChar()) != EOF)
- fputc(c, f1);
+ if (needFlip)
+ {
+ // wrt the non-default quality: we reencode the JPEGs, try to use
+ // quality higher than the default (80) to add less artifacts
+ JpegWriter *writer = new JpegWriter(90, false, colorMap ? JCS_RGB : JCS_GRAYSCALE);
+ //JCS_GRAYSCALE
+
+ if (!writer->init(f1, width, height, 72, 72)) {
+ error(errInternal, -1, "Can't init Jpeg for image '%s'", fName->getCString());
+ delete writer;
+ fclose(f1);
+ return;
+ }
+
+ unsigned char **rows;
+ if (colorMap)
+ rows = rgbImageToRows( str, width, height, colorMap );
+ else
+ rows = monochromeImageToRows( str, width, height );
+
+ // flip by Y
+ if (yFlip)
+ imageFlipY( rows, height );
+
+ // flip by X
+ if (xFlip)
+ for(int i=0; iwritePointers( rows, height ))
+ {
+ delete writer;
+ fclose(f1);
+ freeRows( rows, height );
+ return;
+ }
+
+ writer->close();
+ delete writer;
+ freeRows( rows, height );
+ }
+ else // dump image as is if no flip is needed
+#endif
+ do {
+ // initialize stream
+ str = str->getNextStream();
+ str->reset();
+
+ // copy the stream
+ while ((c = str->getChar()) != EOF)
+ fputc(c, f1);
+ } while (0);
fclose(f1);
@@ -1314,21 +1530,15 @@ void HtmlOutputDev::drawPngImage(GfxState *state, Stream *str, int width, int he
{
#ifdef ENABLE_LIBPNG
FILE *f1;
+ unsigned char **rows;
+ GBool xFlip, yFlip;
if (!colorMap && !isMask) {
error(errInternal, -1, "Can't have color image without a color map");
return;
}
- GooString *fName=new GooString(Docname);
- fName->append("-");
- GooString *pgNum= GooString::fromInt(pageNum);
- GooString *imgnum= GooString::fromInt(pages->getNumImages()+1);
-
- // open the image file
- fName->append(pgNum)->append("_")->append(imgnum)->append(".png");
- delete pgNum;
- delete imgnum;
+ GooString *fName=createImageName("png");
if (!(f1 = fopen(fName->getCString(), "wb"))) {
error(errIO, -1, "Couldn't open image file '%s'", fName->getCString());
@@ -1345,86 +1555,40 @@ void HtmlOutputDev::drawPngImage(GfxState *state, Stream *str, int width, int he
return;
}
- if (!isMask) {
- Guchar *p;
- GfxRGB rgb;
- png_byte *row = (png_byte *) gmalloc(3 * width); // 3 bytes/pixel: RGB
- png_bytep *row_pointer= &row;
-
- // Initialize the image stream
- ImageStream *imgStr = new ImageStream(str, width,
- colorMap->getNumPixelComps(), colorMap->getBits());
- imgStr->reset();
-
- // For each line...
- for (int y = 0; y < height; y++) {
-
- // Convert into a PNG row
- p = imgStr->getLine();
- for (int x = 0; x < width; x++) {
- colorMap->getRGB(p, &rgb);
- // Write the RGB pixels into the row
- row[3*x]= colToByte(rgb.r);
- row[3*x+1]= colToByte(rgb.g);
- row[3*x+2]= colToByte(rgb.b);
- p += colorMap->getNumPixelComps();
- }
+ getImageFlip( state, xFlip, yFlip );
- if (!writer->writeRow(row_pointer)) {
- delete writer;
- fclose(f1);
- return;
- }
- }
- gfree(row);
- imgStr->close();
- delete imgStr;
+ if (!isMask) {
+ rows = rgbImageToRows( str, width, height, colorMap );
+ }
+ else {
+ rows = monochromeImageToRows( str, width, height );
}
- else { // isMask == true
- int bytes_per_row = ((width + 7) / 8);
- Guchar *bit_row = (Guchar *)gmalloc( bytes_per_row );
- Guchar *png_row = (Guchar *)gmalloc( width );
+ str->close();
- str->reset();
- for (int ri = 0; ri < height; ++ri)
- {
- // read the row bits of the mask
- for (int i = 0; i < bytes_per_row; ++i) {
- if ((bit_row[i] = str->getChar()) != EOF) {
- } else {
- error(errIO, -1, "Image mask stream is corrupt for image '%s'", fName->getCString());
- delete writer;
- fclose(f1);
- gfree(bit_row);
- gfree(png_row);
- return;
- }
- }
+ // flip by Y
+ if (yFlip)
+ imageFlipY( rows, height );
- // convert bits into a bytes for PNG
- for(int i = 0; i < width; i++)
- png_row[i] = ((bit_row[i/8]) & (1<<(7-(i%8)))) ? 0x00 : 0xff ; // invert for PNG
+ // flip by X
+ if (xFlip)
+ for(int i=0; iwriteRow( &png_row ))
- {
- error(errIO, -1, "Failed to write into PNG '%s'", fName->getCString());
- delete writer;
- fclose(f1);
- gfree(bit_row);
- gfree(png_row);
- return;
- }
- }
- gfree(bit_row);
- gfree(png_row);
+ // finally write the rows into the PNG
+ if (!writer->writePointers( rows, height ))
+ {
+ delete writer;
+ fclose(f1);
+ freeRows( rows, height );
+ return;
}
- str->close();
-
writer->close();
delete writer;
fclose(f1);
+ freeRows( rows, height );
+
pages->addImage(fName, state);
#else
return;
@@ -1439,10 +1603,10 @@ void HtmlOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
OutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg);
return;
}
-
+
// dump JPEG file
- if (dumpJPEG && str->getKind() == strDCT) {
- drawJpegImage(state, str);
+ if (dumpJPEG && str->getKind() == strDCT) {
+ drawJpegImage(state, str, width, height);
}
else {
#ifdef ENABLE_LIBPNG
@@ -1462,12 +1626,12 @@ void HtmlOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
maskColors, inlineImg);
return;
}
-
+
/*if( !globalParams->getErrQuiet() )
printf("image stream of kind %d\n", str->getKind());*/
// dump JPEG file
if (dumpJPEG && str->getKind() == strDCT) {
- drawJpegImage(state, str);
+ drawJpegImage(state, str, width, height, colorMap);
}
else {
#ifdef ENABLE_LIBPNG
diff --git a/utils/HtmlOutputDev.h b/utils/HtmlOutputDev.h
index dd9c993..3e52f41 100644
--- a/utils/HtmlOutputDev.h
+++ b/utils/HtmlOutputDev.h
@@ -322,7 +322,9 @@ private:
#ifndef DISABLE_OUTLINE
int getOutlinePageNum(OutlineItem *item);
#endif
- void drawJpegImage(GfxState *state, Stream *str);
+ GooString *createImageName(const char *ext);
+ void drawJpegImage(GfxState *state, Stream *str, int width=0, int height=0,
+ GfxImageColorMap *colorMap=NULL);
void drawPngImage(GfxState *state, Stream *str, int width, int height,
GfxImageColorMap *colorMap, GBool isMask = gFalse);