aboutsummaryrefslogtreecommitdiff
path: root/xpdf/ImageOutputDev.cc
diff options
context:
space:
mode:
authorCalvin Morrison <calvin@pobox.com>2023-04-05 14:13:39 -0400
committerCalvin Morrison <calvin@pobox.com>2023-04-05 14:13:39 -0400
commit835e373b3eeaabcd0621ed6798ab500f37982fae (patch)
treedfa16b0e2e1b4956b38f693220eac4e607802133 /xpdf/ImageOutputDev.cc
xpdf-no-select-disableHEADmaster
Diffstat (limited to 'xpdf/ImageOutputDev.cc')
-rw-r--r--xpdf/ImageOutputDev.cc453
1 files changed, 453 insertions, 0 deletions
diff --git a/xpdf/ImageOutputDev.cc b/xpdf/ImageOutputDev.cc
new file mode 100644
index 0000000..5a694d7
--- /dev/null
+++ b/xpdf/ImageOutputDev.cc
@@ -0,0 +1,453 @@
+//========================================================================
+//
+// ImageOutputDev.cc
+//
+// Copyright 1998-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <math.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "config.h"
+#include "Error.h"
+#include "GfxState.h"
+#include "Object.h"
+#include "Stream.h"
+#include "ImageOutputDev.h"
+
+ImageOutputDev::ImageOutputDev(char *fileRootA, GBool dumpJPEGA,
+ GBool dumpRawA, GBool listA) {
+ fileRoot = copyString(fileRootA);
+ dumpJPEG = dumpJPEGA;
+ dumpRaw = dumpRawA;
+ list = listA;
+ imgNum = 0;
+ curPageNum = 0;
+ ok = gTrue;
+}
+
+ImageOutputDev::~ImageOutputDev() {
+ gfree(fileRoot);
+}
+
+void ImageOutputDev::startPage(int pageNum, GfxState *state) {
+ curPageNum = pageNum;
+}
+
+void ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
+ Object *strRef,
+ int paintType, int tilingType,
+ Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep) {
+ // do nothing -- this avoids the potentially slow loop in Gfx.cc
+}
+
+void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate) {
+ GString *fileName;
+ FILE *f;
+ char buf[4096];
+ int size, n, i;
+
+ // dump raw file
+ if (dumpRaw && !inlineImg) {
+
+ // open the image file
+ fileName = GString::format("{0:s}-{1:04d}.{2:s}",
+ fileRoot, imgNum, getRawFileExtension(str));
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+
+ // initialize stream
+ str = getRawStream(str);
+ str->reset();
+
+ // copy the stream
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
+
+ str->close();
+ fclose(f);
+
+ // dump JPEG file
+ } else if (dumpJPEG && str->getKind() == strDCT && !inlineImg) {
+
+ // open the image file
+ fileName = GString::format("{0:s}-{1:04d}.jpg", fileRoot, imgNum);
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+
+ // initialize stream
+ str = ((DCTStream *)str)->getRawStream();
+ str->reset();
+
+ // copy the stream
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
+
+ str->close();
+ fclose(f);
+
+ // dump PBM file
+ } else {
+
+ // open the image file and write the PBM header
+ fileName = GString::format("{0:s}-{1:04d}.pbm", fileRoot, imgNum);
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+ fprintf(f, "P4\n");
+ fprintf(f, "%d %d\n", width, height);
+
+ // initialize stream
+ str->reset();
+
+ // copy the stream
+ size = height * ((width + 7) / 8);
+ while (size > 0) {
+ i = size < (int)sizeof(buf) ? size : (int)sizeof(buf);
+ n = str->getBlock(buf, i);
+ fwrite(buf, 1, n, f);
+ if (n < i) {
+ break;
+ }
+ size -= n;
+ }
+
+ str->close();
+ fclose(f);
+ }
+
+ if (list) {
+ writeImageInfo(fileName, width, height, state, NULL);
+ }
+
+ delete fileName;
+}
+
+void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
+ GfxColorSpaceMode csMode;
+ GString *fileName;
+ FILE *f;
+ ImageStream *imgStr;
+ Guchar *p;
+ GfxRGB rgb;
+ GfxGray gray;
+ int x, y;
+ char buf[4096];
+ int size, n, i, j;
+
+ csMode = colorMap->getColorSpace()->getMode();
+ if (csMode == csIndexed) {
+ csMode = ((GfxIndexedColorSpace *)colorMap->getColorSpace())
+ ->getBase()->getMode();
+ }
+
+ // dump raw file
+ if (dumpRaw && !inlineImg) {
+
+ // open the image file
+ fileName = GString::format("{0:s}-{1:04d}.{2:s}",
+ fileRoot, imgNum, getRawFileExtension(str));
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+
+ // initialize stream
+ str = getRawStream(str);
+ str->reset();
+
+ // copy the stream
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
+
+ str->close();
+ fclose(f);
+
+ // dump JPEG file
+ } else if (dumpJPEG && str->getKind() == strDCT &&
+ (colorMap->getNumPixelComps() == 1 ||
+ colorMap->getNumPixelComps() == 3) &&
+ !inlineImg) {
+
+ // open the image file
+ fileName = GString::format("{0:s}-{1:04d}.jpg", fileRoot, imgNum);
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+
+ // initialize stream
+ str = ((DCTStream *)str)->getRawStream();
+ str->reset();
+
+ // copy the stream
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
+
+ str->close();
+ fclose(f);
+
+ // dump PBM file
+ } else if (colorMap->getNumPixelComps() == 1 &&
+ colorMap->getBits() == 1) {
+
+ // open the image file and write the PBM header
+ fileName = GString::format("{0:s}-{1:04d}.pbm", fileRoot, imgNum);
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+ fprintf(f, "P4\n");
+ fprintf(f, "%d %d\n", width, height);
+
+ // initialize stream
+ str->reset();
+
+ // copy the stream
+ size = height * ((width + 7) / 8);
+ while (size > 0) {
+ i = size < (int)sizeof(buf) ? size : (int)sizeof(buf);
+ n = str->getBlock(buf, i);
+ for (j = 0; j < n; ++j) {
+ buf[j] = (char)(buf[j] ^ 0xff);
+ }
+ fwrite(buf, 1, n, f);
+ if (n < i) {
+ break;
+ }
+ size -= n;
+ }
+
+ str->close();
+ fclose(f);
+
+ // dump PGM file
+ } else if (colorMap->getNumPixelComps() == 1 &&
+ (csMode == csDeviceGray || csMode == csCalGray)) {
+
+ // open the image file and write the PGM header
+ fileName = GString::format("{0:s}-{1:04d}.pgm", fileRoot, imgNum);
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+ fprintf(f, "P5\n");
+ fprintf(f, "%d %d\n", width, height);
+ fprintf(f, "255\n");
+
+ // initialize stream
+ imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgStr->reset();
+
+ // for each line...
+ for (y = 0; y < height; ++y) {
+
+ // write the line
+ if ((p = imgStr->getLine())) {
+ for (x = 0; x < width; ++x) {
+ colorMap->getGray(p, &gray, state->getRenderingIntent());
+ fputc(colToByte(gray), f);
+ ++p;
+ }
+ } else {
+ for (x = 0; x < width; ++x) {
+ fputc(0, f);
+ }
+ }
+ }
+
+ imgStr->close();
+ delete imgStr;
+
+ fclose(f);
+
+ // dump PPM file
+ } else {
+
+ // open the image file and write the PPM header
+ fileName = GString::format("{0:s}-{1:04d}.ppm", fileRoot, imgNum);
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+ fprintf(f, "P6\n");
+ fprintf(f, "%d %d\n", width, height);
+ fprintf(f, "255\n");
+
+ // initialize stream
+ imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgStr->reset();
+
+ // for each line...
+ for (y = 0; y < height; ++y) {
+
+ // write the line
+ if ((p = imgStr->getLine())) {
+ for (x = 0; x < width; ++x) {
+ colorMap->getRGB(p, &rgb, state->getRenderingIntent());
+ fputc(colToByte(rgb.r), f);
+ fputc(colToByte(rgb.g), f);
+ fputc(colToByte(rgb.b), f);
+ p += colorMap->getNumPixelComps();
+ }
+ } else {
+ for (x = 0; x < width; ++x) {
+ fputc(0, f);
+ fputc(0, f);
+ fputc(0, f);
+ }
+ }
+ }
+
+ imgStr->close();
+ delete imgStr;
+
+ fclose(f);
+ }
+
+ if (list) {
+ writeImageInfo(fileName, width, height, state, colorMap);
+ }
+
+ delete fileName;
+}
+
+void ImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GBool maskInvert, GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap,
+ NULL, gFalse, interpolate);
+ drawImageMask(state, maskRef, maskStr, maskWidth, maskHeight, maskInvert,
+ gFalse, interpolate);
+}
+
+void ImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
+ Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ double *matte, GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap,
+ NULL, gFalse, interpolate);
+ drawImage(state, maskRef, maskStr, maskWidth, maskHeight, maskColorMap,
+ NULL, gFalse, interpolate);
+}
+
+Stream *ImageOutputDev::getRawStream(Stream *str) {
+ switch (str->getKind()) {
+ case strLZW:
+ case strRunLength:
+ case strCCITTFax:
+ case strDCT:
+ case strFlate:
+ case strJBIG2:
+ case strJPX:
+ return ((FilterStream *)str)->getNextStream();
+ default:
+ return str;
+ }
+}
+
+const char *ImageOutputDev::getRawFileExtension(Stream *str) {
+ switch (str->getKind()) {
+ case strLZW: return "lzw";
+ case strRunLength: return "rle";
+ case strCCITTFax: return "fax";
+ case strDCT: return "jpg";
+ case strFlate: return "flate";
+ case strJBIG2: return "jb2";
+ case strJPX: return "jpx";
+ default: return "unknown";
+ }
+}
+
+void ImageOutputDev::writeImageInfo(GString *fileName,
+ int width, int height, GfxState *state,
+ GfxImageColorMap *colorMap) {
+ const char *mode;
+ double hdpi, vdpi, x0, y0, x1, y1;
+ int bpc;
+
+ // this works for 0/90/180/270-degree rotations, along with
+ // horizontal/vertical flips
+ state->transformDelta(1, 0, &x0, &y0);
+ state->transformDelta(0, 1, &x1, &y1);
+ x0 = fabs(x0);
+ y0 = fabs(y0);
+ x1 = fabs(x1);
+ y1 = fabs(y1);
+ if (x0 > y0) {
+ hdpi = (72 * width) / x0;
+ vdpi = (72 * height) / y1;
+ } else {
+ hdpi = (72 * height) / x1;
+ vdpi = (72 * width) / y0;
+ }
+
+ if (colorMap) {
+ mode = GfxColorSpace::getColorSpaceModeName(
+ colorMap->getColorSpace()->getMode());
+ bpc = colorMap->getBits();
+ } else {
+ mode = NULL;
+ bpc = 1;
+ }
+
+ printf("%s: page=%d width=%d height=%d hdpi=%.2f vdpi=%.2f %s%s bpc=%d\n",
+ fileName->getCString(), curPageNum, width, height, hdpi, vdpi,
+ mode ? "colorspace=" : "mask",
+ mode ? mode : "",
+ bpc);
+}