Spaces:
Running
Running
//======================================================================== | |
// | |
// pdftoppm.cc | |
// | |
// Copyright 2003 Glyph & Cog, LLC | |
// | |
//======================================================================== | |
//======================================================================== | |
// | |
// Modified under the Poppler project - http://poppler.freedesktop.org | |
// | |
// All changes made under the Poppler project to this file are licensed | |
// under GPL version 2 or later | |
// | |
// Copyright (C) 2007 Ilmari Heikkinen <[email protected]> | |
// Copyright (C) 2008 Richard Airlie <[email protected]> | |
// Copyright (C) 2009 Michael K. Johnson <[email protected]> | |
// Copyright (C) 2009 Shen Liang <[email protected]> | |
// Copyright (C) 2009 Stefan Thomas <[email protected]> | |
// Copyright (C) 2009-2011, 2015, 2018-2022 Albert Astals Cid <[email protected]> | |
// Copyright (C) 2010, 2012, 2017 Adrian Johnson <[email protected]> | |
// Copyright (C) 2010 Hib Eris <[email protected]> | |
// Copyright (C) 2010 Jonathan Liu <[email protected]> | |
// Copyright (C) 2010 William Bader <[email protected]> | |
// Copyright (C) 2011-2013 Thomas Freitag <[email protected]> | |
// Copyright (C) 2013, 2015, 2018 Adam Reichold <[email protected]> | |
// Copyright (C) 2013 Suzuki Toshiya <[email protected]> | |
// Copyright (C) 2015 William Bader <[email protected]> | |
// Copyright (C) 2018 Martin Packman <[email protected]> | |
// Copyright (C) 2019 Yves-Gaël Chény <[email protected]> | |
// Copyright (C) 2019-2021 Oliver Sander <[email protected]> | |
// Copyright (C) 2019 <[email protected]> | |
// Copyright (C) 2019 Kris Jurka <[email protected]> | |
// Copyright (C) 2019 Sébastien Berthier <[email protected]> | |
// Copyright (C) 2020 Stéfan van der Walt <[email protected]> | |
// Copyright (C) 2020 Philipp Knechtges <[email protected]> | |
// Copyright (C) 2021 Diogo Kollross <[email protected]> | |
// Copyright (C) 2021 Peter Williams <[email protected]> | |
// Copyright (C) 2022 James Cloos <[email protected]> | |
// | |
// 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 | |
// | |
//======================================================================== | |
// Uncomment to build pdftoppm with pthreads | |
// You may also have to change the buildsystem to | |
// link pdftoppm to pthread library | |
// This is here for developer testing not user ready | |
// #define UTILS_USE_PTHREADS 1 | |
static int firstPage = 1; | |
static int lastPage = 0; | |
static bool printOnlyOdd = false; | |
static bool printOnlyEven = false; | |
static bool singleFile = false; | |
static bool scaleDimensionBeforeRotation = false; | |
static double resolution = 0.0; | |
static double x_resolution = 150.0; | |
static double y_resolution = 150.0; | |
static int scaleTo = 0; | |
static int x_scaleTo = 0; | |
static int y_scaleTo = 0; | |
static int param_x = 0; | |
static int param_y = 0; | |
static int param_w = 0; | |
static int param_h = 0; | |
static int sz = 0; | |
static bool hideAnnotations = false; | |
static bool useCropBox = false; | |
static bool mono = false; | |
static bool gray = false; | |
static GooString displayprofilename; | |
static GfxLCMSProfilePtr displayprofile; | |
static GooString defaultgrayprofilename; | |
static GfxLCMSProfilePtr defaultgrayprofile; | |
static GooString defaultrgbprofilename; | |
static GfxLCMSProfilePtr defaultrgbprofile; | |
static GooString defaultcmykprofilename; | |
static GfxLCMSProfilePtr defaultcmykprofile; | |
static char sep[2] = "-"; | |
static bool forceNum = false; | |
static bool png = false; | |
static bool jpeg = false; | |
static bool jpegcmyk = false; | |
static bool tiff = false; | |
static GooString jpegOpt; | |
static int jpegQuality = -1; | |
static bool jpegProgressive = false; | |
static bool jpegOptimize = false; | |
static bool overprint = false; | |
static bool splashOverprintPreview = false; | |
static char enableFreeTypeStr[16] = ""; | |
static bool enableFreeType = true; | |
static char antialiasStr[16] = ""; | |
static char vectorAntialiasStr[16] = ""; | |
static bool fontAntialias = true; | |
static bool vectorAntialias = true; | |
static char ownerPassword[33] = ""; | |
static char userPassword[33] = ""; | |
static char TiffCompressionStr[16] = ""; | |
static char thinLineModeStr[8] = ""; | |
static SplashThinLineMode thinLineMode = splashThinLineDefault; | |
static int numberOfJobs = 1; | |
static bool quiet = false; | |
static bool progress = false; | |
static bool printVersion = false; | |
static bool printHelp = false; | |
static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to print" }, | |
{ "-l", argInt, &lastPage, 0, "last page to print" }, | |
{ "-o", argFlag, &printOnlyOdd, 0, "print only odd pages" }, | |
{ "-e", argFlag, &printOnlyEven, 0, "print only even pages" }, | |
{ "-singlefile", argFlag, &singleFile, 0, "write only the first page and do not add digits" }, | |
{ "-scale-dimension-before-rotation", argFlag, &scaleDimensionBeforeRotation, 0, "for rotated pdf, resize dimensions before the rotation" }, | |
{ "-r", argFP, &resolution, 0, "resolution, in DPI (default is 150)" }, | |
{ "-rx", argFP, &x_resolution, 0, "X resolution, in DPI (default is 150)" }, | |
{ "-ry", argFP, &y_resolution, 0, "Y resolution, in DPI (default is 150)" }, | |
{ "-scale-to", argInt, &scaleTo, 0, "scales each page to fit within scale-to*scale-to pixel box" }, | |
{ "-scale-to-x", argInt, &x_scaleTo, 0, "scales each page horizontally to fit in scale-to-x pixels" }, | |
{ "-scale-to-y", argInt, &y_scaleTo, 0, "scales each page vertically to fit in scale-to-y pixels" }, | |
{ "-x", argInt, ¶m_x, 0, "x-coordinate of the crop area top left corner" }, | |
{ "-y", argInt, ¶m_y, 0, "y-coordinate of the crop area top left corner" }, | |
{ "-W", argInt, ¶m_w, 0, "width of crop area in pixels (default is 0)" }, | |
{ "-H", argInt, ¶m_h, 0, "height of crop area in pixels (default is 0)" }, | |
{ "-sz", argInt, &sz, 0, "size of crop square in pixels (sets W and H)" }, | |
{ "-cropbox", argFlag, &useCropBox, 0, "use the crop box rather than media box" }, | |
{ "-hide-annotations", argFlag, &hideAnnotations, 0, "do not show annotations" }, | |
{ "-mono", argFlag, &mono, 0, "generate a monochrome PBM file" }, | |
{ "-gray", argFlag, &gray, 0, "generate a grayscale PGM file" }, | |
{ "-displayprofile", argGooString, &displayprofilename, 0, "ICC color profile to use as the display profile" }, | |
{ "-defaultgrayprofile", argGooString, &defaultgrayprofilename, 0, "ICC color profile to use as the DefaultGray color space" }, | |
{ "-defaultrgbprofile", argGooString, &defaultrgbprofilename, 0, "ICC color profile to use as the DefaultRGB color space" }, | |
{ "-defaultcmykprofile", argGooString, &defaultcmykprofilename, 0, "ICC color profile to use as the DefaultCMYK color space" }, | |
{ "-sep", argString, sep, sizeof(sep), "single character separator between name and page number, default - " }, | |
{ "-forcenum", argFlag, &forceNum, 0, "force page number even if there is only one page " }, | |
{ "-png", argFlag, &png, 0, "generate a PNG file" }, | |
{ "-jpeg", argFlag, &jpeg, 0, "generate a JPEG file" }, | |
{ "-jpegcmyk", argFlag, &jpegcmyk, 0, "generate a CMYK JPEG file" }, | |
{ "-jpegopt", argGooString, &jpegOpt, 0, "jpeg options, with format <opt1>=<val1>[,<optN>=<valN>]*" }, | |
{ "-overprint", argFlag, &overprint, 0, "enable overprint" }, | |
{ "-tiff", argFlag, &tiff, 0, "generate a TIFF file" }, | |
{ "-tiffcompression", argString, TiffCompressionStr, sizeof(TiffCompressionStr), "set TIFF compression: none, packbits, jpeg, lzw, deflate" }, | |
{ "-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr), "enable FreeType font rasterizer: yes, no" }, | |
{ "-thinlinemode", argString, thinLineModeStr, sizeof(thinLineModeStr), "set thin line mode: none, solid, shape. Default: none" }, | |
{ "-aa", argString, antialiasStr, sizeof(antialiasStr), "enable font anti-aliasing: yes, no" }, | |
{ "-aaVector", argString, vectorAntialiasStr, sizeof(vectorAntialiasStr), "enable vector anti-aliasing: yes, no" }, | |
{ "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, | |
{ "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, | |
{ "-j", argInt, &numberOfJobs, 0, "number of jobs to run concurrently" }, | |
{ "-q", argFlag, &quiet, 0, "don't print any messages or errors" }, | |
{ "-progress", argFlag, &progress, 0, "print progress info" }, | |
{ "-v", argFlag, &printVersion, 0, "print copyright and version info" }, | |
{ "-h", argFlag, &printHelp, 0, "print usage information" }, | |
{ "-help", argFlag, &printHelp, 0, "print usage information" }, | |
{ "--help", argFlag, &printHelp, 0, "print usage information" }, | |
{ "-?", argFlag, &printHelp, 0, "print usage information" }, | |
{} }; | |
static constexpr int kOtherError = 99; | |
static bool needToRotate(int angle) | |
{ | |
return (angle == 90) || (angle == 270); | |
} | |
static bool parseJpegOptions() | |
{ | |
// jpegOpt format is: <opt1>=<val1>,<opt2>=<val2>,... | |
const char *nextOpt = jpegOpt.c_str(); | |
while (nextOpt && *nextOpt) { | |
const char *comma = strchr(nextOpt, ','); | |
GooString opt; | |
if (comma) { | |
opt.Set(nextOpt, static_cast<int>(comma - nextOpt)); | |
nextOpt = comma + 1; | |
} else { | |
opt.Set(nextOpt); | |
nextOpt = nullptr; | |
} | |
// here opt is "<optN>=<valN> " | |
const char *equal = strchr(opt.c_str(), '='); | |
if (!equal) { | |
fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); | |
return false; | |
} | |
const int iequal = static_cast<int>(equal - opt.c_str()); | |
GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1); | |
opt.del(iequal, opt.getLength() - iequal); | |
// here opt is "<optN>" and value is "<valN>" | |
if (opt.cmp("quality") == 0) { | |
if (!isInt(value.c_str())) { | |
fprintf(stderr, "Invalid jpeg quality\n"); | |
return false; | |
} | |
jpegQuality = atoi(value.c_str()); | |
if (jpegQuality < 0 || jpegQuality > 100) { | |
fprintf(stderr, "jpeg quality must be between 0 and 100\n"); | |
return false; | |
} | |
} else if (opt.cmp("progressive") == 0) { | |
jpegProgressive = false; | |
if (value.cmp("y") == 0) { | |
jpegProgressive = true; | |
} else if (value.cmp("n") != 0) { | |
fprintf(stderr, "jpeg progressive option must be \"y\" or \"n\"\n"); | |
return false; | |
} | |
} else if (opt.cmp("optimize") == 0 || opt.cmp("optimise") == 0) { | |
jpegOptimize = false; | |
if (value.cmp("y") == 0) { | |
jpegOptimize = true; | |
} else if (value.cmp("n") != 0) { | |
fprintf(stderr, "jpeg optimize option must be \"y\" or \"n\"\n"); | |
return false; | |
} | |
} else { | |
fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); | |
return false; | |
} | |
} | |
return true; | |
} | |
static auto annotDisplayDecideCbk = [](Annot *annot, void *user_data) { return !hideAnnotations; }; | |
static void savePageSlice(PDFDoc *doc, SplashOutputDev *splashOut, int pg, int x, int y, int w, int h, double pg_w, double pg_h, char *ppmFile) | |
{ | |
if (w == 0) { | |
w = (int)ceil(pg_w); | |
} | |
if (h == 0) { | |
h = (int)ceil(pg_h); | |
} | |
w = (x + w > pg_w ? (int)ceil(pg_w - x) : w); | |
h = (y + h > pg_h ? (int)ceil(pg_h - y) : h); | |
doc->displayPageSlice(splashOut, pg, x_resolution, y_resolution, 0, !useCropBox, false, false, x, y, w, h, nullptr, nullptr, annotDisplayDecideCbk, nullptr); | |
SplashBitmap *bitmap = splashOut->getBitmap(); | |
SplashBitmap::WriteImgParams params; | |
params.jpegQuality = jpegQuality; | |
params.jpegProgressive = jpegProgressive; | |
params.jpegOptimize = jpegOptimize; | |
params.tiffCompression = TiffCompressionStr; | |
if (ppmFile != nullptr) { | |
SplashError e; | |
if (png) { | |
e = bitmap->writeImgFile(splashFormatPng, ppmFile, x_resolution, y_resolution); | |
} else if (jpeg) { | |
e = bitmap->writeImgFile(splashFormatJpeg, ppmFile, x_resolution, y_resolution, ¶ms); | |
} else if (jpegcmyk) { | |
e = bitmap->writeImgFile(splashFormatJpegCMYK, ppmFile, x_resolution, y_resolution, ¶ms); | |
} else if (tiff) { | |
e = bitmap->writeImgFile(splashFormatTiff, ppmFile, x_resolution, y_resolution, ¶ms); | |
} else { | |
e = bitmap->writePNMFile(ppmFile); | |
} | |
if (e != splashOk) { | |
fprintf(stderr, "Could not write image to %s; exiting\n", ppmFile); | |
exit(EXIT_FAILURE); | |
} | |
} else { | |
_setmode(fileno(stdout), O_BINARY); | |
if (png) { | |
bitmap->writeImgFile(splashFormatPng, stdout, x_resolution, y_resolution); | |
} else if (jpeg) { | |
bitmap->writeImgFile(splashFormatJpeg, stdout, x_resolution, y_resolution, ¶ms); | |
} else if (tiff) { | |
bitmap->writeImgFile(splashFormatTiff, stdout, x_resolution, y_resolution, ¶ms); | |
} else { | |
bitmap->writePNMFile(stdout); | |
} | |
} | |
if (progress) { | |
fprintf(stderr, "%d %d %s\n", pg, lastPage, ppmFile != nullptr ? ppmFile : ""); | |
} | |
} | |
struct PageJob | |
{ | |
PDFDoc *doc; | |
int pg; | |
double pg_w, pg_h; | |
SplashColor *paperColor; | |
char *ppmFile; | |
}; | |
static std::deque<PageJob> pageJobQueue; | |
static pthread_mutex_t pageJobMutex = PTHREAD_MUTEX_INITIALIZER; | |
static void processPageJobs() | |
{ | |
while (true) { | |
// pop the next job or exit if queue is empty | |
pthread_mutex_lock(&pageJobMutex); | |
if (pageJobQueue.empty()) { | |
pthread_mutex_unlock(&pageJobMutex); | |
return; | |
} | |
PageJob pageJob = pageJobQueue.front(); | |
pageJobQueue.pop_front(); | |
pthread_mutex_unlock(&pageJobMutex); | |
// process the job | |
SplashOutputDev *splashOut = new SplashOutputDev(mono ? splashModeMono1 | |
: gray ? splashModeMono8 | |
: (jpegcmyk || overprint) ? splashModeDeviceN8 | |
: splashModeRGB8, | |
4, false, *pageJob.paperColor, true, thinLineMode, splashOverprintPreview); | |
splashOut->setFontAntialias(fontAntialias); | |
splashOut->setVectorAntialias(vectorAntialias); | |
splashOut->setEnableFreeType(enableFreeType); | |
splashOut->setDisplayProfile(displayprofile); | |
splashOut->setDefaultGrayProfile(defaultgrayprofile); | |
splashOut->setDefaultRGBProfile(defaultrgbprofile); | |
splashOut->setDefaultCMYKProfile(defaultcmykprofile); | |
splashOut->startDoc(pageJob.doc); | |
savePageSlice(pageJob.doc, splashOut, pageJob.pg, param_x, param_y, param_w, param_h, pageJob.pg_w, pageJob.pg_h, pageJob.ppmFile); | |
delete splashOut; | |
delete[] pageJob.ppmFile; | |
} | |
} | |
int main(int argc, char *argv[]) | |
{ | |
GooString *fileName = nullptr; | |
char *ppmRoot = nullptr; | |
char *ppmFile; | |
std::optional<GooString> ownerPW, userPW; | |
SplashColor paperColor; | |
SplashOutputDev *splashOut; | |
pthread_t *jobs; | |
bool ok; | |
int pg, pg_num_len; | |
double pg_w, pg_h; | |
cmsColorSpaceSignature profilecolorspace; | |
Win32Console win32Console(&argc, &argv); | |
// parse args | |
ok = parseArgs(argDesc, &argc, argv); | |
if (mono && gray) { | |
ok = false; | |
} | |
if (resolution != 0.0 && (x_resolution == 150.0 || y_resolution == 150.0)) { | |
x_resolution = resolution; | |
y_resolution = resolution; | |
} | |
if (!ok || argc > 3 || printVersion || printHelp) { | |
fprintf(stderr, "pdftoppm version %s\n", PACKAGE_VERSION); | |
fprintf(stderr, "%s\n", popplerCopyright); | |
fprintf(stderr, "%s\n", xpdfCopyright); | |
if (!printVersion) { | |
printUsage("pdftoppm", "[PDF-file [PPM-file-prefix]]", argDesc); | |
} | |
if (printVersion || printHelp) { | |
return 0; | |
} else { | |
return kOtherError; | |
} | |
} | |
if (argc > 1) { | |
fileName = new GooString(argv[1]); | |
} | |
if (argc == 3) { | |
ppmRoot = argv[2]; | |
} | |
if (antialiasStr[0]) { | |
if (!GlobalParams::parseYesNo2(antialiasStr, &fontAntialias)) { | |
fprintf(stderr, "Bad '-aa' value on command line\n"); | |
} | |
} | |
if (vectorAntialiasStr[0]) { | |
if (!GlobalParams::parseYesNo2(vectorAntialiasStr, &vectorAntialias)) { | |
fprintf(stderr, "Bad '-aaVector' value on command line\n"); | |
} | |
} | |
if (jpegOpt.getLength() > 0) { | |
if (!jpeg) { | |
fprintf(stderr, "Warning: -jpegopt only valid with jpeg output.\n"); | |
} | |
parseJpegOptions(); | |
} | |
// read config file | |
globalParams = std::make_unique<GlobalParams>(); | |
if (enableFreeTypeStr[0]) { | |
if (!GlobalParams::parseYesNo2(enableFreeTypeStr, &enableFreeType)) { | |
fprintf(stderr, "Bad '-freetype' value on command line\n"); | |
} | |
} | |
if (thinLineModeStr[0]) { | |
if (strcmp(thinLineModeStr, "solid") == 0) { | |
thinLineMode = splashThinLineSolid; | |
} else if (strcmp(thinLineModeStr, "shape") == 0) { | |
thinLineMode = splashThinLineShape; | |
} else if (strcmp(thinLineModeStr, "none") != 0) { | |
fprintf(stderr, "Bad '-thinlinemode' value on command line\n"); | |
} | |
} | |
if (quiet) { | |
globalParams->setErrQuiet(quiet); | |
} | |
// open PDF file | |
if (ownerPassword[0]) { | |
ownerPW = GooString(ownerPassword); | |
} | |
if (userPassword[0]) { | |
userPW = GooString(userPassword); | |
} | |
if (fileName == nullptr) { | |
fileName = new GooString("fd://0"); | |
} | |
if (fileName->cmp("-") == 0) { | |
delete fileName; | |
fileName = new GooString("fd://0"); | |
} | |
std::unique_ptr<PDFDoc> doc(PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW)); | |
delete fileName; | |
if (!doc->isOk()) { | |
return 1; | |
} | |
// get page range | |
if (firstPage < 1) { | |
firstPage = 1; | |
} | |
if (singleFile && lastPage < 1) { | |
lastPage = firstPage; | |
} | |
if (lastPage < 1 || lastPage > doc->getNumPages()) { | |
lastPage = doc->getNumPages(); | |
} | |
if (lastPage < firstPage) { | |
fprintf(stderr, "Wrong page range given: the first page (%d) can not be after the last page (%d).\n", firstPage, lastPage); | |
return kOtherError; | |
} | |
// If our page range selection and document size indicate we're only | |
// outputting a single page, ensure that even/odd page selection doesn't | |
// filter out that single page. | |
if (firstPage == lastPage && ((printOnlyEven && firstPage % 2 == 1) || (printOnlyOdd && firstPage % 2 == 0))) { | |
fprintf(stderr, "Invalid even/odd page selection, no pages match criteria.\n"); | |
return kOtherError; | |
} | |
if (singleFile && firstPage < lastPage) { | |
if (!quiet) { | |
fprintf(stderr, "Warning: Single file will write only the first of the %d pages.\n", lastPage + 1 - firstPage); | |
} | |
lastPage = firstPage; | |
} | |
// write PPM files | |
if (jpegcmyk || overprint) { | |
splashOverprintPreview = true; | |
splashClearColor(paperColor); | |
} else { | |
paperColor[0] = 255; | |
paperColor[1] = 255; | |
paperColor[2] = 255; | |
} | |
if (!displayprofilename.toStr().empty()) { | |
displayprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(displayprofilename.c_str(), "r")); | |
if (!displayprofile) { | |
fprintf(stderr, "Could not open the ICC profile \"%s\".\n", displayprofilename.c_str()); | |
return kOtherError; | |
} | |
if (!cmsIsIntentSupported(displayprofile.get(), INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT) && !cmsIsIntentSupported(displayprofile.get(), INTENT_ABSOLUTE_COLORIMETRIC, LCMS_USED_AS_OUTPUT) | |
&& !cmsIsIntentSupported(displayprofile.get(), INTENT_SATURATION, LCMS_USED_AS_OUTPUT) && !cmsIsIntentSupported(displayprofile.get(), INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT)) { | |
fprintf(stderr, "ICC profile \"%s\" is not an output profile.\n", displayprofilename.c_str()); | |
return kOtherError; | |
} | |
profilecolorspace = cmsGetColorSpace(displayprofile.get()); | |
// Note: In contrast to pdftops we do not fail if a non-matching ICC profile is supplied. | |
// Doing so would be pretentious, since SplashOutputDev by default assumes sRGB, even for | |
// the CMYK and Mono cases. | |
if (jpegcmyk || overprint) { | |
if (profilecolorspace != cmsSigCmykData) { | |
fprintf(stderr, "Warning: Supplied ICC profile \"%s\" is not a CMYK profile.\n", displayprofilename.c_str()); | |
} | |
} else if (mono || gray) { | |
if (profilecolorspace != cmsSigGrayData) { | |
fprintf(stderr, "Warning: Supplied ICC profile \"%s\" is not a monochrome profile.\n", displayprofilename.c_str()); | |
} | |
} else { | |
if (profilecolorspace != cmsSigRgbData) { | |
fprintf(stderr, "Warning: Supplied ICC profile \"%s\" is not a RGB profile.\n", displayprofilename.c_str()); | |
} | |
} | |
} | |
if (!defaultgrayprofilename.toStr().empty()) { | |
defaultgrayprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(defaultgrayprofilename.c_str(), "r")); | |
if (!checkICCProfile(defaultgrayprofile, defaultgrayprofilename.c_str(), LCMS_USED_AS_INPUT, cmsSigGrayData)) { | |
return kOtherError; | |
} | |
} | |
if (!defaultrgbprofilename.toStr().empty()) { | |
defaultrgbprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(defaultrgbprofilename.c_str(), "r")); | |
if (!checkICCProfile(defaultrgbprofile, defaultrgbprofilename.c_str(), LCMS_USED_AS_INPUT, cmsSigRgbData)) { | |
return kOtherError; | |
} | |
} | |
if (!defaultcmykprofilename.toStr().empty()) { | |
defaultcmykprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(defaultcmykprofilename.c_str(), "r")); | |
if (!checkICCProfile(defaultcmykprofile, defaultcmykprofilename.c_str(), LCMS_USED_AS_INPUT, cmsSigCmykData)) { | |
return kOtherError; | |
} | |
} | |
splashOut = new SplashOutputDev(mono ? splashModeMono1 : gray ? splashModeMono8 : (jpegcmyk || overprint) ? splashModeDeviceN8 : splashModeRGB8, 4, false, paperColor, true, thinLineMode, splashOverprintPreview); | |
splashOut->setFontAntialias(fontAntialias); | |
splashOut->setVectorAntialias(vectorAntialias); | |
splashOut->setEnableFreeType(enableFreeType); | |
splashOut->setDisplayProfile(displayprofile); | |
splashOut->setDefaultGrayProfile(defaultgrayprofile); | |
splashOut->setDefaultRGBProfile(defaultrgbprofile); | |
splashOut->setDefaultCMYKProfile(defaultcmykprofile); | |
splashOut->startDoc(doc.get()); | |
if (sz != 0) { | |
param_w = param_h = sz; | |
} | |
pg_num_len = numberOfCharacters(doc->getNumPages()); | |
for (pg = firstPage; pg <= lastPage; ++pg) { | |
if (printOnlyEven && pg % 2 == 1) { | |
continue; | |
} | |
if (printOnlyOdd && pg % 2 == 0) { | |
continue; | |
} | |
if (useCropBox) { | |
pg_w = doc->getPageCropWidth(pg); | |
pg_h = doc->getPageCropHeight(pg); | |
} else { | |
pg_w = doc->getPageMediaWidth(pg); | |
pg_h = doc->getPageMediaHeight(pg); | |
} | |
if (scaleDimensionBeforeRotation && needToRotate(doc->getPageRotate(pg))) { | |
std::swap(pg_w, pg_h); | |
} | |
// Handle requests for specific image size | |
if (scaleTo != 0) { | |
if (pg_w > pg_h) { | |
resolution = (72.0 * scaleTo) / pg_w; | |
pg_w = scaleTo; | |
pg_h = pg_h * (resolution / 72.0); | |
} else { | |
resolution = (72.0 * scaleTo) / pg_h; | |
pg_h = scaleTo; | |
pg_w = pg_w * (resolution / 72.0); | |
} | |
x_resolution = y_resolution = resolution; | |
} else { | |
if (x_scaleTo > 0) { | |
x_resolution = (72.0 * x_scaleTo) / pg_w; | |
pg_w = x_scaleTo; | |
if (y_scaleTo == -1) { | |
y_resolution = x_resolution; | |
} | |
} | |
if (y_scaleTo > 0) { | |
y_resolution = (72.0 * y_scaleTo) / pg_h; | |
pg_h = y_scaleTo; | |
if (x_scaleTo == -1) { | |
x_resolution = y_resolution; | |
} | |
} | |
// No specific image size requested---compute the size from the resolution | |
if (x_scaleTo <= 0) { | |
pg_w = pg_w * x_resolution / 72.0; | |
} | |
if (y_scaleTo <= 0) { | |
pg_h = pg_h * y_resolution / 72.0; | |
} | |
} | |
if (!scaleDimensionBeforeRotation && needToRotate(doc->getPageRotate(pg))) { | |
std::swap(pg_w, pg_h); | |
} | |
if (ppmRoot != nullptr) { | |
const char *ext = png ? "png" : (jpeg || jpegcmyk) ? "jpg" : tiff ? "tif" : mono ? "pbm" : gray ? "pgm" : "ppm"; | |
if (singleFile && !forceNum) { | |
ppmFile = new char[strlen(ppmRoot) + 1 + strlen(ext) + 1]; | |
sprintf(ppmFile, "%s.%s", ppmRoot, ext); | |
} else { | |
ppmFile = new char[strlen(ppmRoot) + 1 + pg_num_len + 1 + strlen(ext) + 1]; | |
sprintf(ppmFile, "%s%s%0*d.%s", ppmRoot, sep, pg_num_len, pg, ext); | |
} | |
} else { | |
ppmFile = nullptr; | |
} | |
// process job in main thread | |
savePageSlice(doc.get(), splashOut, pg, param_x, param_y, param_w, param_h, pg_w, pg_h, ppmFile); | |
delete[] ppmFile; | |
// queue job for worker threads | |
PageJob pageJob = { .doc = doc.get(), | |
.pg = pg, | |
.pg_w = pg_w, | |
.pg_h = pg_h, | |
.paperColor = &paperColor, | |
.ppmFile = ppmFile }; | |
pageJobQueue.push_back(pageJob); | |
} | |
delete splashOut; | |
// spawn worker threads and wait on them | |
jobs = (pthread_t *)malloc(numberOfJobs * sizeof(pthread_t)); | |
for (int i = 0; i < numberOfJobs; ++i) { | |
if (pthread_create(&jobs[i], NULL, (void *(*)(void *))processPageJobs, NULL) != 0) { | |
fprintf(stderr, "pthread_create() failed with errno: %d\n", errno); | |
exit(EXIT_FAILURE); | |
} | |
} | |
for (int i = 0; i < numberOfJobs; ++i) { | |
if (pthread_join(jobs[i], NULL) != 0) { | |
fprintf(stderr, "pthread_join() failed with errno: %d\n", errno); | |
exit(EXIT_FAILURE); | |
} | |
} | |
free(jobs); | |
return 0; | |
} | |