| //======================================================================== |
| // |
| // 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 <ilmari.heikkinen@gmail.com> |
| // Copyright (C) 2008 Richard Airlie <richard.airlie@maglabs.net> |
| // Copyright (C) 2009 Michael K. Johnson <a1237@danlj.org> |
| // Copyright (C) 2009 Shen Liang <shenzhuxi@gmail.com> |
| // Copyright (C) 2009 Stefan Thomas <thomas@eload24.com> |
| // Copyright (C) 2009-2011, 2015, 2018 Albert Astals Cid <aacid@kde.org> |
| // Copyright (C) 2010, 2012, 2017 Adrian Johnson <ajohnson@redneon.com> |
| // Copyright (C) 2010 Hib Eris <hib@hiberis.nl> |
| // Copyright (C) 2010 Jonathan Liu <net147@gmail.com> |
| // Copyright (C) 2010 William Bader <williambader@hotmail.com> |
| // Copyright (C) 2011-2013 Thomas Freitag <Thomas.Freitag@alfa.de> |
| // Copyright (C) 2013, 2015, 2018 Adam Reichold <adamreichold@myopera.com> |
| // Copyright (C) 2013 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp> |
| // Copyright (C) 2015 William Bader <williambader@hotmail.com> |
| // Copyright (C) 2018 Martin Packman <gzlist@googlemail.com> |
| // Copyright (C) 2019 Yves-Gaël Chény <gitlab@r0b0t.fr> |
| // |
| // 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 |
| // |
| //======================================================================== |
| |
| #include "config.h" |
| #include <poppler-config.h> |
| #ifdef _WIN32 |
| #include <fcntl.h> // for O_BINARY |
| #include <io.h> // for setmode |
| #endif |
| #include <stdio.h> |
| #include <math.h> |
| #include "parseargs.h" |
| #include "goo/gmem.h" |
| #include "goo/GooString.h" |
| #include "GlobalParams.h" |
| #include "Object.h" |
| #include "PDFDoc.h" |
| #include "PDFDocFactory.h" |
| #include "splash/SplashBitmap.h" |
| #include "splash/Splash.h" |
| #include "SplashOutputDev.h" |
| #include "Win32Console.h" |
| #include "numberofcharacters.h" |
| |
| // 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 |
| |
| #ifdef UTILS_USE_PTHREADS |
| #include <errno.h> |
| #include <pthread.h> |
| #include <deque> |
| #endif // UTILS_USE_PTHREADS |
| |
| static int firstPage = 1; |
| static int lastPage = 0; |
| static bool printOnlyOdd = false; |
| static bool printOnlyEven = false; |
| static bool singleFile = 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 useCropBox = false; |
| static bool mono = false; |
| static bool gray = false; |
| 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 char enableFreeTypeStr[16] = ""; |
| 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; |
| #ifdef UTILS_USE_PTHREADS |
| static int numberOfJobs = 1; |
| #endif // UTILS_USE_PTHREADS |
| static bool quiet = 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"}, |
| |
| {"-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"}, |
| |
| {"-mono", argFlag, &mono, 0, |
| "generate a monochrome PBM file"}, |
| {"-gray", argFlag, &gray, 0, |
| "generate a grayscale PGM file"}, |
| {"-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 "}, |
| #ifdef ENABLE_LIBPNG |
| {"-png", argFlag, &png, 0, |
| "generate a PNG file"}, |
| #endif |
| #ifdef ENABLE_LIBJPEG |
| {"-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>]*"}, |
| #endif |
| {"-overprint",argFlag, &overprint, 0, |
| "enable overprint"}, |
| #ifdef ENABLE_LIBTIFF |
| {"-tiff", argFlag, &tiff, 0, |
| "generate a TIFF file"}, |
| {"-tiffcompression", argString, TiffCompressionStr, sizeof(TiffCompressionStr), |
| "set TIFF compression: none, packbits, jpeg, lzw, deflate"}, |
| #endif |
| {"-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)"}, |
| |
| #ifdef UTILS_USE_PTHREADS |
| {"-j", argInt, &numberOfJobs, 0, |
| "number of jobs to run concurrently"}, |
| #endif // UTILS_USE_PTHREADS |
| |
| {"-q", argFlag, &quiet, 0, |
| "don't print any messages or errors"}, |
| {"-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 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, 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; |
| } |
| int iequal = 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 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 |
| ); |
| |
| SplashBitmap *bitmap = splashOut->getBitmap(); |
| |
| SplashBitmap::WriteImgParams params; |
| params.jpegQuality = jpegQuality; |
| params.jpegProgressive = jpegProgressive; |
| params.jpegOptimize = jpegOptimize; |
| params.tiffCompression.Set(TiffCompressionStr); |
| |
| if (ppmFile != nullptr) { |
| if (png) { |
| bitmap->writeImgFile(splashFormatPng, ppmFile, x_resolution, y_resolution); |
| } else if (jpeg) { |
| bitmap->writeImgFile(splashFormatJpeg, ppmFile, x_resolution, y_resolution, ¶ms); |
| } else if (jpegcmyk) { |
| bitmap->writeImgFile(splashFormatJpegCMYK, ppmFile, x_resolution, y_resolution, ¶ms); |
| } else if (tiff) { |
| bitmap->writeImgFile(splashFormatTiff, ppmFile, x_resolution, y_resolution, ¶ms); |
| } else { |
| bitmap->writePNMFile(ppmFile); |
| } |
| } else { |
| #ifdef _WIN32 |
| setmode(fileno(stdout), O_BINARY); |
| #endif |
| |
| 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); |
| } |
| } |
| } |
| |
| #ifdef UTILS_USE_PTHREADS |
| |
| 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); |
| splashOut->setFontAntialias(fontAntialias); |
| splashOut->setVectorAntialias(vectorAntialias); |
| splashOut->startDoc(pageJob.doc); |
| |
| savePageSlice(pageJob.doc, splashOut, pageJob.pg, x, y, w, h, pageJob.pg_w, pageJob.pg_h, pageJob.ppmFile); |
| |
| delete splashOut; |
| delete[] pageJob.ppmFile; |
| } |
| } |
| |
| #endif // UTILS_USE_PTHREADS |
| |
| int main(int argc, char *argv[]) { |
| PDFDoc *doc; |
| GooString *fileName = nullptr; |
| char *ppmRoot = nullptr; |
| char *ppmFile; |
| GooString *ownerPW, *userPW; |
| SplashColor paperColor; |
| #ifndef UTILS_USE_PTHREADS |
| SplashOutputDev *splashOut; |
| #else |
| pthread_t* jobs; |
| #endif // UTILS_USE_PTHREADS |
| bool ok; |
| int exitCode; |
| int pg, pg_num_len; |
| double pg_w, pg_h, tmp; |
| |
| Win32Console win32Console(&argc, &argv); |
| exitCode = 99; |
| |
| // 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) |
| exitCode = 0; |
| goto err0; |
| } |
| 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 = new GlobalParams(); |
| if (enableFreeTypeStr[0]) { |
| if (!globalParams->setEnableFreeType(enableFreeTypeStr)) { |
| 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 = new GooString(ownerPassword); |
| } else { |
| ownerPW = nullptr; |
| } |
| if (userPassword[0]) { |
| userPW = new GooString(userPassword); |
| } else { |
| userPW = nullptr; |
| } |
| |
| if (fileName == nullptr) { |
| fileName = new GooString("fd://0"); |
| } |
| if (fileName->cmp("-") == 0) { |
| delete fileName; |
| fileName = new GooString("fd://0"); |
| } |
| doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); |
| delete fileName; |
| |
| if (userPW) { |
| delete userPW; |
| } |
| if (ownerPW) { |
| delete ownerPW; |
| } |
| if (!doc->isOk()) { |
| exitCode = 1; |
| goto err1; |
| } |
| |
| // 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); |
| goto err1; |
| } |
| |
| 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) { |
| globalParams->setOverprintPreview(true); |
| for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) |
| paperColor[cp] = 0; |
| } else { |
| paperColor[0] = 255; |
| paperColor[1] = 255; |
| paperColor[2] = 255; |
| } |
| |
| #ifndef UTILS_USE_PTHREADS |
| |
| splashOut = new SplashOutputDev(mono ? splashModeMono1 : |
| gray ? splashModeMono8 : |
| (jpegcmyk || overprint) ? splashModeDeviceN8 : |
| splashModeRGB8, 4, |
| false, paperColor, true, thinLineMode); |
| |
| |
| |
| splashOut->setFontAntialias(fontAntialias); |
| splashOut->setVectorAntialias(vectorAntialias); |
| splashOut->startDoc(doc); |
| |
| #endif // UTILS_USE_PTHREADS |
| |
| if (sz != 0) param_w = param_h = sz; |
| pg_num_len = numberOfCharacters(doc->getNumPages()); |
| for (pg = firstPage; pg <= lastPage; ++pg) { |
| if (printOnlyEven && pg % 2 == 0) continue; |
| if (printOnlyOdd && pg % 2 == 1) 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 (scaleTo != 0) { |
| resolution = (72.0 * scaleTo) / (pg_w > pg_h ? pg_w : pg_h); |
| x_resolution = y_resolution = resolution; |
| } else { |
| if (x_scaleTo > 0) { |
| x_resolution = (72.0 * x_scaleTo) / pg_w; |
| if (y_scaleTo == -1) |
| y_resolution = x_resolution; |
| } |
| if (y_scaleTo > 0) { |
| y_resolution = (72.0 * y_scaleTo) / pg_h; |
| if (x_scaleTo == -1) |
| x_resolution = y_resolution; |
| } |
| } |
| pg_w = pg_w * (x_resolution / 72.0); |
| pg_h = pg_h * (y_resolution / 72.0); |
| if ((doc->getPageRotate(pg) == 90) || (doc->getPageRotate(pg) == 270)) { |
| tmp = pg_w; |
| pg_w = pg_h; |
| pg_h = tmp; |
| } |
| 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; |
| } |
| #ifndef UTILS_USE_PTHREADS |
| // process job in main thread |
| savePageSlice(doc, splashOut, pg, param_x, param_y, param_w, param_h, pg_w, pg_h, ppmFile); |
| |
| delete[] ppmFile; |
| #else |
| |
| // queue job for worker threads |
| PageJob pageJob = { |
| .doc = doc, |
| .pg = pg, |
| |
| .pg_w = pg_w, .pg_h = pg_h, |
| |
| .paperColor = &paperColor, |
| |
| .ppmFile = ppmFile |
| }; |
| |
| pageJobQueue.push_back(pageJob); |
| |
| #endif // UTILS_USE_PTHREADS |
| } |
| #ifndef UTILS_USE_PTHREADS |
| delete splashOut; |
| #else |
| |
| // 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); |
| |
| #endif // UTILS_USE_PTHREADS |
| |
| exitCode = 0; |
| |
| // clean up |
| err1: |
| delete doc; |
| delete globalParams; |
| err0: |
| |
| return exitCode; |
| } |