/*! ** ** \file pdfToolboxSampleThreadSafe.cpp ** ** \brief multithreaded sample program (OMP) ** ** \author © 2013 callas software GmbH, Berlin, Germany - www.callassoftware.com * */ //////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #ifndef _WIN32 # include # include # include # include # include # include # include #endif # include #if defined(_WIN32) || defined(_M_X64) # include #elif defined(MAC_ENV) # include "Carbon.h" #endif #include "ptb.h" #define EXIT_WITH_FAILURE 100 typedef std::basic_string TStringUTF8; #include // Locking Stuff // #define USE_OMP_NEST_LOCK 1 #ifdef USE_OMP_NEST_LOCK // note: omp_nest_lock does *not* work together with g++-4.3 on unix (SEGV) # define SAMPLE_LOCK_T omp_nest_lock_t # define SAMPLE_INIT_LOCK omp_init_nest_lock # define SAMPLE_SET_LOCK omp_set_nest_lock # define SAMPLE_UNSET_LOCK omp_unset_nest_lock # define SAMPLE_INIT_LOCK omp_init_nest_lock #else # define SAMPLE_LOCK_T omp_lock_t # define SAMPLE_INIT_LOCK omp_init_lock # define SAMPLE_SET_LOCK omp_set_lock # define SAMPLE_UNSET_LOCK omp_unset_lock # define SAMPLE_INIT_LOCK omp_init_lock #endif SAMPLE_LOCK_T console_lock; // synchronize access to console output SAMPLE_LOCK_T inputqueue_lock; // synchronize access to input queue struct lock_raii_t { lock_raii_t(SAMPLE_LOCK_T &lock_) : lock(lock_) { SAMPLE_SET_LOCK(&lock); } ~lock_raii_t() { SAMPLE_UNSET_LOCK(&lock); } SAMPLE_LOCK_T &lock; }; void init_sample_locks() { # ifdef USE_OMP_NEST_LOCK std::cout << std::endl; std::cout << "**********************************************************************************************************" << std::endl; std::cout << "be aware! omp_nest_lock does not work together with some g++ versions on unix (namely g++-4.3 causes SEGV)" << std::endl; std::cout << "**********************************************************************************************************" << std::endl; # endif SAMPLE_INIT_LOCK(&console_lock); SAMPLE_INIT_LOCK(&inputqueue_lock); } //////////////////////////////////////////////////////////////////////////////// struct CUserdata_t { CUserdata_t(PTB_size_t lastValue, const std::string &inputfile, const std::string &logfile) : lastValue(lastValue), inputfile(inputfile), logfile(logfile), hits(0), fixups(0), isPDFA(true), idResult(PTB_INVALID_ID) { logstream.open(logfile.c_str()); } PTB_size_t lastValue; const std::string inputfile; const std::string logfile; unsigned int hits; unsigned int fixups; bool isPDFA; PTB_ResultID idResult; std::ofstream logstream; ~CUserdata_t() { if (logstream.is_open()) logstream.close(); if (PTB_INVALID_ID != idResult) PTB_PreflightRelease( idResult, NULL ); } }; // Filesystem Stuff bool path_basename(const std::string &path, std::string &basename) { if (path.empty()) return false; unsigned slash_pos = path.find_last_of("/\\"); unsigned dot_pos = path.find_last_of('.'); basename=path.substr(slash_pos+1, dot_pos-slash_pos-1); if (basename.empty()) return false; return true; } std::string path_combine(const std::string &dir_path, const std::string &s) { #ifdef _WIN32 return dir_path + "\\" + s; #else return dir_path + "/" + s; #endif } std::list inputfiles; #ifdef _WIN32 bool path_exists(const std::string &filePath, bool want_directory=false) { DWORD fileAtt = GetFileAttributesA(filePath.c_str()); //If an error occurred it will equal to INVALID_FILE_ATTRIBUTES if(fileAtt == INVALID_FILE_ATTRIBUTES) return false; if (want_directory) { return fileAtt & FILE_ATTRIBUTE_DIRECTORY; } return ( fileAtt & FILE_ATTRIBUTE_DIRECTORY ) == 0; } void scan_inputfolder(const std::string &inputfolder) { WIN32_FIND_DATAA FileData; std::string filename(inputfolder); filename += "\\*"; HANDLE hSearch = FindFirstFileA(filename.c_str(), &FileData); if (INVALID_HANDLE_VALUE == hSearch) { std::cout << "failed to open inputfolder " << inputfolder << std::endl; return; } do { std::string path(path_combine(inputfolder, FileData.cFileName)); if (path_exists(path)) { std::cout << "queued " << path << std::endl; inputfiles.push_back(path.c_str()); } } while (FindNextFileA(hSearch, &FileData)); } #else bool path_exists(const std::string &filePath, bool want_directory=false) { struct stat sb; if (lstat(filePath.c_str(), &sb) == -1) { return false; } if (want_directory) { return (sb.st_mode & S_IFMT) == S_IFDIR; } return (sb.st_mode & S_IFMT) == S_IFREG; // regular file ? } void scan_inputfolder(const std::string &inputfolder) { DIR *dirHandle=opendir(inputfolder.c_str()); if (0==dirHandle) return; struct dirent * dirEntry=0; while(0 != (dirEntry = readdir(dirHandle))) { std::string path(path_combine(inputfolder, dirEntry->d_name)); if (path_exists(path)) { std::cout << "queued " << path << std::endl; inputfiles.push_back(path.c_str()); } } closedir(dirHandle); } #endif //////////////////////////////////////////////////////////////////////////////// /*! Write log statement to console */ template< typename T > void workerConsoleLog( const T & t ) { lock_raii_t lock_(console_lock); std::cout << " worker #" << omp_get_thread_num() << " - " << t << std::endl; } template< typename T, typename TArg > T stream_cast( const TArg& src ) { std::stringstream ss; ss << src; T tgt; ss >> tgt; return tgt; } //////////////////////////////////////////////////////////////////////////////// /*! convert a PTB_StringID to a std::string */ std::string getString( PTB_StringID idStr, bool bDispose = false ) { if( PTB_IsValidStringID( idStr ) ) { PTB_size_t size; PTB_EError err = PTB_StringLength( idStr, &size ); if( err != PTB_eerrNone ) { throw std::runtime_error( std::string("call to PTB_StringLength() failed") ); } std::vector< PTB_utf8_char_t > buf( size ); err=PTB_StringGet( idStr, &buf[0], &size ); if( err != PTB_eerrNone ) { throw std::runtime_error( std::string("call to PTB_StringGet() failed") ); } if( bDispose ) { PTB_StringRelease( idStr ); } //remove NULL byte buf.pop_back(); return std::string( buf.begin(), buf.end() ); } else { return ""; } } //////////////////////////////////////////////////////////////////////////////// /*! convert a PTB_EError to a std::string */ std::string getErrorString( PTB_EError err ) { PTB_StringID strid; PTB_EError err2 = PTB_GetErrorMessage( err, &strid ); if( err2 == PTB_eerrNone ) return getString( strid ); return ""; } //////////////////////////////////////////////////////////////////////////////// /*! Check for no error */ void require_noErr( PTB_EError err, bool bThrow = false, bool bUseDict = true ) { if (err == PTB_eerrNone) return; std::ostringstream oss; oss << "ERROR: 0x" << std::hex << err; if (err == PTB_eerrKeyCodeFail) oss << " (PTB_eerrKeyCodeFail)"; else if (err == PTB_eerrKeyCodeExpire) oss << " (PTB_eerrKeyCodeExpire)"; else if (bUseDict) { oss << " (" << getErrorString(err) << ")"; } if( bThrow ) throw std::runtime_error( oss.str() ); else workerConsoleLog(oss.str()); } void require_noErr2( PTB_EError err, CUserdata_t *userData, bool bThrow = false, bool bUseDict = true ) { if( err != PTB_eerrNone ) { std::ostringstream oss; if( bUseDict ) { oss << "ERROR: 0x" << std::hex << err; oss << " (" << getErrorString( err ) << ")" << std::endl; } else { oss << "ERROR: 0x" << std::hex << err << std::endl; } if (userData && userData->logstream.is_open()) { (userData->logstream) << oss.str() << std::endl; } if( bThrow ) throw std::runtime_error( oss.str() ); else workerConsoleLog(oss.str()); } } static const unsigned int pathBufSize=255; PTB_Path_t *make_ptb_path(const std::string &strSource, PTB_Path_t bufTarget[], unsigned int bufSize=pathBufSize) { require_noErr(PTB_ConvertSysToUTF8(strSource.c_str(), bufTarget, &bufSize)); return bufTarget; } //////////////////////////////////////////////////////////////////////////////// /*! convert a std::string to valid UTF8 string */ TStringUTF8 getUTF8( const std::string& source ) { if( source.empty() ) return TStringUTF8(); std::vector buffer( source.size()+1 ); PTB_size_t size = static_cast( buffer.size() ); const PTB_EError error = PTB_ConvertSysToUTF8( source.c_str(), &buffer[0], &size ); if( error == PTB_eerrStrBufferTooSmall ) { require_noErr( PTB_ConvertSysToUTF8( source.c_str(), &buffer[0], &size ) ); } return TStringUTF8( buffer.begin() , buffer.begin()+buffer.size()-1 ); } //////////////////////////////////////////////////////////////////////////////// /*! Progress callbacks */ PTB_bool_t progressCB( PTB_uint32_t max /*!< Progress value max */ , PTB_uint32_t current /*!< Current progress value */ , PTB_EPreflightPart part /*!< Current Preflight part in process */ , PTB_StringID idStr /*!< String data */ , void* userData /*!< User data */ ) { CUserdata_t *work = userData ? reinterpret_cast(userData) : 0; if( 0 == work ) return false; PTB_size_t &lastval = work->lastValue; int val( static_cast( static_cast( current) * 100.0 / static_cast( max))); if(lastval != val) { std::string msg(stream_cast(val)); msg += "%"; if (work->logstream.is_open()) { work->logstream << msg << std::endl; } // workerConsoleLog (msg); lastval = val; } return true; } PTB_bool_t progressPRCCB( PTB_uint32_t max /*!< Progress value max */ , PTB_uint32_t current /*!< Current progress value */ // , PTB_EPreflightPart part /*!< Current Preflight part in process */ , PTB_StringID idStr /*!< String data */ , void* userData /*!< User data */ ) { CUserdata_t *work = userData ? reinterpret_cast(userData) : 0; if( 0 == work ) return false; PTB_size_t &lastval = work->lastValue; int val( static_cast( static_cast( current) * 100.0 / static_cast( max))); if(lastval != val) { std::string msg(stream_cast(val)); msg += "%"; work->logstream << msg << std::endl; // workerConsoleLog (msg); lastval = val; } return true; } //////////////////////////////////////////////////////////////////////////////// // find first profile in engine // userData: PTB_PRCProfID * void FindFirstProfileCB( PTB_PRCEngineID idEng, PTB_PRCProfID idProf, void * userData ) { PTB_PRCProfID * firstProfile = reinterpret_cast< PTB_PRCProfID * >( userData ); if( firstProfile && !PTB_PRCIsValidProfID( *firstProfile ) ) { *firstProfile = idProf; } } //////////////////////////////////////////////////////////////////////////////// // log fixups as they occur // userData: NULL void fixupCB( PTB_PRCEngineID idEng /*!< PRC Engine ID */ , PTB_PRCFxupID idFixup /*!< PRC Fixup ID */ , PTB_EFixupState state /*!< Fixup state */ , PTB_StringID idReason /*!< Reason text */ , void* userData /*!< user data */ ) { CUserdata_t *work = userData ? reinterpret_cast(userData) : 0; if( 0 != work ) { work->fixups++; } PTB_StringID idFixupName; require_noErr2( PTB_PRCGetFixupData( idEng, idFixup, PTB_PRCEdtName, NULL, &idFixupName ), work, true); std::stringstream ss; ss << "Fixup \"" << getString( idFixupName, true ) << "\"" << std::endl; ss << " Status: "; switch( state ) { case PTB_efsFixupSuccess : ss << "Success" ; break; case PTB_efsFixupFailure : ss << "Failure" ; break; case PTB_efsFixupNotRequired: ss << "Not Required"; break; } ss << std::endl; ss << " Reason: " << getString( idReason ); if( 0 != work ) { work->logstream << ss.str() << std::endl; } } //////////////////////////////////////////////////////////////////////////////// // log hits as they occur // userData: NULL PTB_EHitCBResult hitContinueCB( PTB_PRCEngineID idEng /*!< PRC Engine ID */ , PTB_PRCProfID idProf /*!< PRC Profile ID */ , PTB_PRCRuleID idRule /*!< PRC Rule ID */ , PTB_ResultID idResult /*!< Result ID */ , PTB_HitID idHit /*!< Hit ID */ , PTB_PRCERuleCheckSeverity ruleSev /*!< PRC Rule severity */ , PTB_uint32_t pageNum /*!< Current page number */ , void* userData /*!< user data */ ) { CUserdata_t *work = userData ? reinterpret_cast(userData) : 0; if( 0 != work ) { work->isPDFA = false; work->hits++; } PTB_StringID idRuleName; require_noErr2( PTB_PRCGetRuleData( idEng, idRule, PTB_PRCEdtName, NULL, &idRuleName ), work, true); std::stringstream ss; ss << "Hit with severity "; switch( ruleSev ) { case PTB_PRCErcsInfo : ss << "Info"; break; case PTB_PRCErcsWarning: ss << "Warning"; break; case PTB_PRCErcsError : ss << "Error"; break; } if(PTB_DOCUMENT_HIT==pageNum) ss << " in document"; else ss << " on page " << pageNum; ss << ": " << getString( idRuleName ); PTB_StringRelease( idRuleName ); if (0 != work) work->logstream << ss.str() << std::endl; return PTB_ehcbrContinue; } PTB_EHitCBResult hitCancelCB( PTB_PRCEngineID /*idEng*/ /*!< PRC Engine ID */ , PTB_PRCProfID /*idProf*/ /*!< PRC Profile ID */ , PTB_PRCRuleID /*idRule*/ /*!< PRC Rule ID */ , PTB_ResultID /*idResult*/ /*!< Result ID */ , PTB_HitID /*idHit*/ /*!< Hit ID */ , PTB_PRCERuleCheckSeverity ruleSev /*!< PRC Rule severity */ , PTB_uint32_t /*pageNum*/ /*!< Current page number */ , void* userData /*!< user data */ ) { if( NULL != userData ) { CUserdata_t *work = reinterpret_cast(userData); work->isPDFA = false; work->hits++; } return (ruleSev == PTB_PRCErcsError ? PTB_ehcbrCancel : PTB_ehcbrContinue); } //////////////////////////////////////////////////////////////////////////////// /*! Save callback // userData: string -> result PDF */ PTB_Path_t* saveCB( PTB_ResultID idResult /*!< Result ID */ , PTB_ESaveAsReason reason /*!< */ , PTB_ESaveAsReasonPDFX reasonPDFX /*!< */ , PTB_EPDFXVersion versionPDFX /*!< */ , PTB_ESaveAsReasonPDFA reasonPDFA /*!< */ , PTB_EPDFAVersion versionPDFA /*!< */ , PTB_StringID currentPath /*!< current path of PDF document */ , void* userData /*!< User data */ ) { PTB_Path_t * resultPDF = reinterpret_cast< PTB_Path_t * >( userData ); std::stringstream ss; if( resultPDF != NULL ) { ss << "Saving file to " << resultPDF << std::endl; workerConsoleLog(ss.str()); return const_cast< PTB_Path_t* >( resultPDF ); } else if( PTB_IsValidStringID( currentPath)) { PTB_size_t strBufSize = 0; PTB_EError err = PTB_StringLength( currentPath, &strBufSize); if( err != PTB_eerrNone ) return NULL; std::vector strBuffer(strBufSize); err = PTB_StringGet( currentPath, &strBuffer[0], &strBufSize); if( err != PTB_eerrNone ) return NULL; ss << "Saving file to " << &strBuffer[0] << std::endl; workerConsoleLog(ss.str()); } return NULL; // NULL: use default file stored in 'currentPath' } //////////////////////////////////////////////////////////////////////////////// bool checkVersion() { bool ret=true; PTB_uint16_t pdfEngineVersion; PTB_uint16_t apiVersion; PTB_uint16_t apiIteration; PTB_uint16_t buildNumber; PTB_bool_t x64; PTB_bool_t threadSafety; PTB_EError err = PTB_LibAPIGetVersion2( &pdfEngineVersion , &apiVersion , &apiIteration , &buildNumber , &x64 , &threadSafety); require_noErr( err, false ); std::string arch_info(""); if (x64) arch_info += "x64"; if (threadSafety) { if (arch_info.size() > 0) arch_info += " "; arch_info += "threadSafe"; } std::cout << "callas pdfEngine SDK " << pdfEngineVersion << '.' << apiVersion << '.' << buildNumber; if (arch_info.size() > 0) { std::cout << " (" << arch_info << ")"; } std::cout << std::endl; if (false==threadSafety) { std::cout << "the used PdfEngine does not provide the required ThreadSafety feature" << std::endl; ret=false; } const PTB_uint16_t curPTBVers = dPTBSDK_pdfEngineVersion; const PTB_uint16_t curAPIVersion = dPTBSDK_APIMainVersion; const PTB_uint16_t curAPIIteration = dPTBSDK_APIVersionIteration; if ( curPTBVers != pdfEngineVersion || curAPIVersion != apiVersion //|| curapiIteration < apiIteration //later this should be sufficient. || curAPIIteration != apiIteration //the first prerelease iterations might be incompatible! ) { std::cout << "internal API version mismatch. Expected: " << curPTBVers << "." << curAPIVersion << "." << curAPIIteration << ". Found: " << pdfEngineVersion << "." << apiVersion << "." << apiIteration << std::endl; ret=false; } return ret; } //////////////////////////////////////////////////////////////////////////////// void createReports(CUserdata_t &userdata , const PTB_Path_t* xmlfile , const PTB_Path_t* htmlfile , const PTB_Path_t* mhtfile) { PTB_ResultID &idResult = userdata.idResult; unsigned int bufSize=0; PTB_EError err = PTB_eerrNone; if( xmlfile != NULL ) { std::string msg("Creating XML Report "); msg += reinterpret_cast(xmlfile); workerConsoleLog(msg); userdata.logstream << msg << std::endl; userdata.lastValue = 0; // reset progress counter err = PTB_Report( idResult , xmlfile , PTB_eXMLResults_V2 , PTB_eCR , PTB_eUTF8 , 0 , PTB_eAllDetails , NULL , progressCB , &userdata , 0 , PTB_MAX_END_PAGE , NULL ); require_noErr( err, false ); } if( htmlfile!= NULL ) { std::string msg("Creating HTML Report "); msg += reinterpret_cast(htmlfile); workerConsoleLog(msg); userdata.logstream << msg << std::endl; userdata.lastValue = 0; err = PTB_Report( idResult , htmlfile , PTB_eHTML , PTB_eCR , PTB_eUTF8 , PTB_eHTMLNoIcons , PTB_eAllDetails , NULL , progressCB , &userdata , 0 , PTB_MAX_END_PAGE , NULL ); require_noErr( err, false ); } if( mhtfile!= NULL ) { std::string msg("Creating MHT Report "); msg += reinterpret_cast(mhtfile); workerConsoleLog(msg); userdata.logstream << msg << std::endl; userdata.lastValue = 0; err = PTB_Report( idResult , mhtfile , PTB_eHTML , PTB_eCR , PTB_eUTF8 , PTB_eHTMLasMHT | PTB_eHTMLNoIcons , PTB_eAllDetails , NULL , progressCB , &userdata , 0 , PTB_MAX_END_PAGE , NULL); require_noErr( err, false ); } } PTB_EError convertFile(PTB_Path_t *inputfile, PTB_EPDFAVersion version , const PTB_Path_t* saveasfile , const PTB_Path_t* customprofile , const PTB_Path_t* fontfolder , const PTB_Path_t* fontcachefolder , const PTB_Path_t* substitutefile , const PTB_Path_t* outputintentfile , bool removexmpmetadata , const PTB_PRCEngineID &idEng , const PTB_PRCProfID &idProf , CUserdata_t &userdata) { PTB_ResultID &idResult = userdata.idResult; PTB_EError err = PTB_eerrNone; userdata.isPDFA = true; // reset old status PTB_uint32_t flags = PTB_ecpfNone; if( !removexmpmetadata ) flags |= PTB_ecpfNoXMPMetadataRemoval; PTB_PreflightRelease( idResult, NULL ); // release old results idResult = PTB_INVALID_ID; // reset old results userdata.isPDFA = true; // reset old status err = PTB_ConvertPDFA7( inputfile , version , fixupCB , &userdata , hitContinueCB , &userdata , progressCB , &userdata , saveCB , const_cast(saveasfile) , flags , fontfolder , fontcachefolder , substitutefile , outputintentfile , NULL, NULL, NULL, NULL , NULL , 0, 0, PTB_MAX_END_PAGE , idEng , idProf , &idResult , NULL ); if(err != PTB_eerrNone) { userdata.isPDFA=false; // assume that a failed conversion produced a non-PDFA result std::string msg("PDF/A Conversion failed!"); std::string saveasfile_s(reinterpret_cast(saveasfile)); if (path_exists(saveasfile_s)) { msg += " Assume a non-PDFA result for "; msg += saveasfile_s; } workerConsoleLog(msg); userdata.logstream << msg << std::endl; } return err; } int processFile(const std::string &inputfile, PTB_EPDFAVersion version , const PTB_Path_t* xmlfile , const PTB_Path_t* htmlfile , const PTB_Path_t* mhtfile , const PTB_Path_t* saveasfile , const PTB_Path_t* customprofile , const PTB_Path_t* fontfolder , const PTB_Path_t* fontcachefolder , const PTB_Path_t* substitutefile , const PTB_Path_t* outputintentfile , bool removexmpmetadata , const std::string &logfile , const PTB_PRCEngineID &idEng , const PTB_PRCProfID &idProf) { PTB_EError err = PTB_eerrNone; CUserdata_t userdata(0, inputfile, logfile); std::string msg; msg += "Checking "; msg += inputfile; msg += " ..."; workerConsoleLog(msg); userdata.logstream << msg << std::endl; PTB_Path_t filePath[255]; PTB_size_t bufSize = sizeof(filePath); make_ptb_path(inputfile, filePath); PTB_ResultID &idResult = userdata.idResult; PTB_HitCB hitCB = hitContinueCB; if( NULL != saveasfile ) { hitCB = hitCancelCB; } err = PTB_CheckPDFA( filePath , version , hitCB , &userdata , progressCB , &userdata , idEng , idProf , &idResult , NULL ); if(err != PTB_eerrNone) userdata.isPDFA=false; msg=inputfile; if(userdata.isPDFA) { msg += " is valid PDF/A no conversion needed."; } else { msg += " is not a valid PDF/A file"; } userdata.logstream << msg << std::endl; workerConsoleLog(msg); if( !userdata.isPDFA && (NULL != saveasfile)) { msg=inputfile; msg += " trying to convert to a valid PDF/A file ..."; userdata.logstream << msg << std::endl; workerConsoleLog(msg); err = convertFile(filePath, version, saveasfile, customprofile, fontfolder, fontcachefolder, substitutefile, outputintentfile, removexmpmetadata, idEng, idProf, userdata); require_noErr( err ); } if(PTB_eerrNone == err) { createReports(userdata, xmlfile, htmlfile, mhtfile); } return err; } //////////////////////////////////////////////////////////////////////////////// int usage() { std::stringstream ss; ss << "USAGE:" << std::endl; ss << " pdfaPilotSampleThreadSafe" << " " << " [" << " -o" << " [" << " -x -h -m -p" << " [" << " -f" << " -c" << " -s" << " -i" << " -d" << " ]" << " ]" << " ]" << std::endl; std::cout << ss.str(); return EXIT_WITH_FAILURE; } //////////////////////////////////////////////////////////////////////////////// typedef std::vector< std::string > TArgumentVec; //////////////////////////////////////////////////////////////////////////////// // main //////////////////////////////////////////////////////////////////////////////// struct common_config_t { common_config_t(const TArgumentVec &args) { // set defaults xmlfile_wanted=false; htmlfile_wanted=false; mhtfile_wanted=false; std::string level; check_only=true; profile=0; fontfolder=0; fontcachefolder=0; substitutefile=0; outputintentfile=0; removexmpmetadata = false; version = PTB_epdfa1b_2005; // evaluate args for ( int i = 0; i < args.size(); i++) { std::string arg( args.at(i) ); if (0==i) continue; // skip executable filename if (1==i) continue; // skip keycode if (2==i){ // get input folder inputfolder = arg; continue; } if (3==i){ // get output folder outputfolder = arg; continue; } unsigned int bufSize=0; if( arg[0] == '-' && arg.size() > 1) { switch(arg[1]) { case 'x': case 'X': xmlfile_wanted = true; break; case 'h': case 'H': htmlfile_wanted = true; break; case 'm': case 'M': mhtfile_wanted = true; break; case 'o': case 'O': check_only=false; break; case 'p': case 'P': profile=make_ptb_path(arg.substr(2), profile_buf); break; case 'f': case 'F': fontfolder=make_ptb_path(arg.substr(2), fontfolder_buf); break; case 'c': case 'C': fontcachefolder=make_ptb_path(arg.substr(2), fontcachefolder_buf); break; case 's': case 'S': substitutefile=make_ptb_path(arg.substr(2), substitutefile_buf); break; case 'i': case 'I': outputintentfile=make_ptb_path(arg.substr(2), outputintentfile_buf); break; case 'd': case 'D': removexmpmetadata = true; break; case 'l': case 'L': level = arg.substr(2); break; } } } if( !level.empty() ) { std::string compliance( level ); if( compliance == "1a" ) { version = PTB_epdfa1a_2005; } else if ( compliance == "1b" ) { version = PTB_epdfa1b_2005; } else if ( compliance == "2a" ) { version = PTB_epdfa2a; } else if ( compliance == "2b" ) { version = PTB_epdfa2b; } else if ( compliance == "2u" ) { version = PTB_epdfa2u; } else if ( compliance == "3a" ) { version = PTB_epdfa3a; } else if ( compliance == "3b" ) { version = PTB_epdfa3b; } else if ( compliance == "3u" ) { version = PTB_epdfa3u; } } } mutable std::string inputfolder; mutable std::string outputfolder; mutable PTB_EPDFAVersion version; mutable bool xmlfile_wanted; mutable bool htmlfile_wanted; mutable bool mhtfile_wanted; mutable bool check_only; mutable PTB_Path_t *profile; mutable PTB_Path_t *fontfolder; mutable PTB_Path_t *fontcachefolder; mutable PTB_Path_t *substitutefile; mutable PTB_Path_t *outputintentfile; mutable bool removexmpmetadata; private: PTB_Path_t profile_buf[pathBufSize]; PTB_Path_t fontfolder_buf[pathBufSize]; PTB_Path_t fontcachefolder_buf[pathBufSize]; PTB_Path_t substitutefile_buf[pathBufSize]; PTB_Path_t outputintentfile_buf[pathBufSize]; }; const std::string option_value(const std::string &option) { if (option.length() > 2) { return option.substr(2); } else { std::string msg("option "); msg += option; msg += " requires a value"; throw std::invalid_argument(msg); } return ""; } bool next_inputfile(std::string &inputfile) { lock_raii_t lock_(inputqueue_lock); if (inputfiles.empty()) return false; const std::string &tmp=inputfiles.front(); inputfile = tmp; inputfiles.pop_front(); return true; } void work(const common_config_t &cfg) { std::string inputfile; PTB_EError err = PTB_eerrNone; PTB_PRCEngineID idEng = PTB_INVALID_ID; PTB_PRCProfID idProf = PTB_INVALID_ID; if ((false==cfg.check_only) || (NULL != cfg.profile)) { err = PTB_PRCEngineCreate( &idEng); require_noErr( err ); } if (NULL != cfg.profile) { err = PTB_PRCEngineImportPackageFromFile( idEng, cfg.profile, NULL, NULL); require_noErr( err ); err = PTB_PRCEnumProfiles( idEng, FindFirstProfileCB, &idProf); require_noErr( err ); } while (next_inputfile(inputfile)) { std::string basename; path_basename(inputfile, basename); std::string output_without_ext = path_combine(cfg.outputfolder, basename); std::string logfile(output_without_ext + ".log"); PTB_Path_t xmlfile_buf[pathBufSize]; PTB_Path_t htmlfile_buf[pathBufSize]; PTB_Path_t mhtfile_buf[pathBufSize]; PTB_Path_t savasfile_buf[pathBufSize]; processFile( inputfile , cfg.version , cfg.xmlfile_wanted ? make_ptb_path(output_without_ext + ".xml", xmlfile_buf) : NULL , cfg.htmlfile_wanted ? make_ptb_path(output_without_ext + ".html", htmlfile_buf) : NULL , cfg.mhtfile_wanted ? make_ptb_path(output_without_ext + ".mht", mhtfile_buf) : NULL , cfg.check_only ? NULL : make_ptb_path(output_without_ext + ".pdf", savasfile_buf) , cfg.profile , cfg.fontfolder , cfg.fontcachefolder , cfg.substitutefile , cfg.outputintentfile , cfg.removexmpmetadata , logfile , idEng , idProf); } if (PTB_INVALID_ID != idEng) PTB_PRCEngineDispose( idEng); } int main(int argc, char* argv[]) { if( argc < 4 ) { return usage(); } init_sample_locks(); size_t nInputfiles=0; size_t nThreads=omp_get_num_procs(); if (nThreads < 4) nThreads=4; time_t start_time = time(0); workerConsoleLog( "" ); try { if (!checkVersion()) { return EXIT_WITH_FAILURE; } TArgumentVec args; for( std::size_t i = 0; i < argc; ++i) { args.push_back( argv[i] ); std::cout << "argv[" << i << "] = " << args.back() << std::endl; } const common_config_t cfg(args); if (!path_exists(cfg.inputfolder.c_str(), true)) { std::cout << "the given inputfolder does not exist : " << cfg.inputfolder << std::endl; return EXIT_WITH_FAILURE; } if (!path_exists(cfg.outputfolder.c_str(), true)) { std::cout << "the given outputfolder does not exist : " << cfg.outputfolder << std::endl; return EXIT_WITH_FAILURE; } // fill input queue scan_inputfolder(cfg.inputfolder); nInputfiles=inputfiles.size(); if (0 == nInputfiles) { std::cout << "input folder empty" << std::endl; return EXIT_WITH_FAILURE; } // "embracing" outer SDK init std::cout << "main: initializing SDK ..." << std::endl; require_noErr( PTB_LibInit( argv[1], NULL, 0) , true, false ); if (nInputfiles( argv[0] ) ); // inner SDK init work(cfg); workerConsoleLog( "releasing SDK ..." ); PTB_LibRelease(); // inner SDK de-init } catch( const std::exception & x ) { workerConsoleLog (std::string("exception catched: ") + x.what()); } } std::cout << "parallel part completed" << std::endl; // outer SDK de-init (aka "un-embracing") std::cout << "main: releasing SDK ..." << std::endl; PTB_LibRelease(); } catch( const std::out_of_range& ) { std::cout << "An error occured" << std::endl; std::cout << "Array index out of bounds" << std::endl; std::cout << "Maybe to few arguments?" << std::endl; return EXIT_WITH_FAILURE; } catch( const std::string & x ) { std::cout << "exception catched: " << x << std::endl; return EXIT_WITH_FAILURE; } catch( const std::exception & x ) { std::cout << "exception catched: " << x.what() << std::endl; return EXIT_WITH_FAILURE; } time_t duration = time(0) - start_time; std::cout << std::endl; std::cout << "processed " << nInputfiles << " file(s) in " << duration << " seconds" << std::endl; std::cout << std::endl; std::cout << "bye..." << std::endl; return EXIT_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // EOF