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);