diff --git a/ChangeLog b/ChangeLog index f170a11b..97e8506c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2022-06-18 Jay Berkenbilt + * Add additional qpdfjob C API functions take a handle. + * Add qpdf_exit_code_e to Constants.h so that exit codes from QPDFJob are accessible to the C API. diff --git a/include/qpdf/QPDFJob.hh b/include/qpdf/QPDFJob.hh index 39c7a612..16b1ab74 100644 --- a/include/qpdf/QPDFJob.hh +++ b/include/qpdf/QPDFJob.hh @@ -108,6 +108,8 @@ class QPDFJob // and error streams on the caller's behalf. Defaults to "qpdf". QPDF_DLL void setMessagePrefix(std::string const&); + QPDF_DLL + std::string getMessagePrefix() const; // To capture or redirect output, configure the logger returned by // getLogger(). By default, all QPDF and QPDFJob objects share the diff --git a/include/qpdf/qpdfjob-c.h b/include/qpdf/qpdfjob-c.h index b9fe8ccb..e15c8fa5 100644 --- a/include/qpdf/qpdfjob-c.h +++ b/include/qpdf/qpdfjob-c.h @@ -45,6 +45,11 @@ #ifdef __cplusplus extern "C" { #endif + /* SHORT INTERFACE -- These functions are single calls that take + * care of the whole life cycle of QPDFJob. They can be used for + * one-shot ooperations where no additional configuration is + * needed. See FULL INTERFACE below. */ + /* This function does the equivalent of running the qpdf * command-line with the given arguments and returns the exit code * that qpdf would use. argv must be a null-terminated array of @@ -74,6 +79,52 @@ extern "C" { QPDF_DLL int qpdfjob_run_from_json(char const* json); + /* FULL INTERFACE -- new in qpdf11. Similar to the qpdf-c.h API, + * you must call qpdfjob_init to get a qpdfjob_handle and, when + * done, call qpdfjob_cleanup to free resources. Remaining methods + * take qpdfjob_handle as an argument. This interface requires + * more calls but also offers greater flexibility. + */ + typedef struct _qpdfjob_handle* qpdfjob_handle; + QPDF_DLL + qpdfjob_handle qpdfjob_init(); + + QPDF_DLL + void qpdfjob_cleanup(qpdfjob_handle* j); + + /* This function wraps QPDFJob::initializeFromArgv. The return + * value is the same as qpdfjob_run. If this returns an error, it + * is invalid to call any other functions this job handle. + */ + QPDF_DLL + int + qpdfjob_initialize_from_argv(qpdfjob_handle j, char const* const argv[]); + +#ifndef QPDF_NO_WCHAR_T + /* This function is the same as qpdfjob_initialize_from_argv + * except argv is encoded with wide characters. This would be + * suitable for calling from a Windows wmain function. + */ + QPDF_DLL + int qpdfjob_initialize_from_wide_argv( + qpdfjob_handle j, wchar_t const* const argv[]); +#endif /* QPDF_NO_WCHAR_T */ + + /* This function wraps QPDFJob::initializeFromJson. The return + * value is the same as qpdfjob_run. If this returns an error, it + * is invalid to call any other functions this job handle. + */ + QPDF_DLL + int qpdfjob_initialize_from_json(qpdfjob_handle j, char const* json); + + /* This function wraps QPDFJob::run. It returns the error code + * that qpdf would return with the equivalent command-line + * invocation. Exit code values are defined in Constants.h in the + * qpdf_exit_code_e type. + */ + QPDF_DLL + int qpdfjob_run(qpdfjob_handle j); + #ifdef __cplusplus } #endif diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc index 9f76bdf4..b25257a1 100644 --- a/libqpdf/QPDFJob.cc +++ b/libqpdf/QPDFJob.cc @@ -438,6 +438,12 @@ QPDFJob::setMessagePrefix(std::string const& message_prefix) this->m->message_prefix = message_prefix; } +std::string +QPDFJob::getMessagePrefix() const +{ + return this->m->message_prefix; +} + std::shared_ptr QPDFJob::getLogger() { diff --git a/libqpdf/qpdfjob-c.cc b/libqpdf/qpdfjob-c.cc index 4e6666be..81433661 100644 --- a/libqpdf/qpdfjob-c.cc +++ b/libqpdf/qpdfjob-c.cc @@ -8,51 +8,115 @@ #include #include -int -qpdfjob_run_from_argv(char const* const argv[]) +struct _qpdfjob_handle { - auto whoami_p = QUtil::make_unique_cstr(argv[0]); - auto whoami = QUtil::getWhoami(whoami_p.get()); - QUtil::setLineBuf(stdout); + _qpdfjob_handle() = default; + ~_qpdfjob_handle() = default; QPDFJob j; +}; + +qpdfjob_handle +qpdfjob_init() +{ + return new _qpdfjob_handle; +} + +void +qpdfjob_cleanup(qpdfjob_handle* j) +{ + delete *j; + *j = nullptr; +} + +static int +wrap_qpdfjob(qpdfjob_handle j, std::function fn) +{ try { - j.initializeFromArgv(argv); - j.run(); + return fn(j); } catch (std::exception& e) { - *QPDFLogger::defaultLogger()->getError() - << whoami << ": " << e.what() << "\n"; - return QPDFJob::EXIT_ERROR; + *j->j.getLogger()->getError() + << j->j.getMessagePrefix() << ": " << e.what() << "\n"; } - return j.getExitCode(); + return QPDFJob::EXIT_ERROR; +} + +int +qpdfjob_initialize_from_argv(qpdfjob_handle j, char const* const argv[]) +{ + return wrap_qpdfjob(j, [argv](qpdfjob_handle jh) { + jh->j.initializeFromArgv(argv); + return 0; + }); } #ifndef QPDF_NO_WCHAR_T int -qpdfjob_run_from_wide_argv(wchar_t const* const argv[]) +qpdfjob_initialize_from_wide_argv(qpdfjob_handle j, wchar_t const* const argv[]) { int argc = 0; for (auto k = argv; *k; ++k) { ++argc; } return QUtil::call_main_from_wmain( - argc, argv, [](int, char const* const new_argv[]) { - return qpdfjob_run_from_argv(new_argv); + argc, argv, [j](int, char const* const new_argv[]) { + return qpdfjob_initialize_from_argv(j, new_argv); }); } #endif // QPDF_NO_WCHAR_T int -qpdfjob_run_from_json(char const* json) +qpdfjob_initialize_from_json(qpdfjob_handle j, char const* json) { - QPDFJob j; - try { - j.initializeFromJson(json); - j.run(); - } catch (std::exception& e) { - *QPDFLogger::defaultLogger()->getError() - << "qpdfjob json: " << e.what() << "\n"; - return QPDFJob::EXIT_ERROR; - } - return j.getExitCode(); + return wrap_qpdfjob(j, [json](qpdfjob_handle jh) { + jh->j.setMessagePrefix("qpdfjob json"); + jh->j.initializeFromJson(json); + return 0; + }); } + +int +qpdfjob_run(qpdfjob_handle j) +{ + QUtil::setLineBuf(stdout); + return wrap_qpdfjob(j, [](qpdfjob_handle jh) { + jh->j.run(); + return jh->j.getExitCode(); + }); +} + +static int run_with_handle(std::function fn) +{ + auto j = qpdfjob_init(); + int status = fn(j); + if (status == 0) { + status = qpdfjob_run(j); + } + qpdfjob_cleanup(&j); + return status; +} + +int qpdfjob_run_from_argv(char const* const argv[]) +{ + return run_with_handle([argv](qpdfjob_handle j) { + return qpdfjob_initialize_from_argv(j, argv); + }); +} + +#ifndef QPDF_NO_WCHAR_T +int +qpdfjob_run_from_wide_argv(wchar_t const* const argv[]) +{ + return run_with_handle([argv](qpdfjob_handle j) { + return qpdfjob_initialize_from_wide_argv(j, argv); + }); +} +#endif /* QPDF_NO_WCHAR_T */ + +int qpdfjob_run_from_json(char const* json) +{ + return run_with_handle([json](qpdfjob_handle j) { + return qpdfjob_initialize_from_json(j, json); + }); +} + diff --git a/manual/release-notes.rst b/manual/release-notes.rst index 780f541f..f7d673e7 100644 --- a/manual/release-notes.rst +++ b/manual/release-notes.rst @@ -188,6 +188,13 @@ For a detailed list of changes, please see the file writing large amounts of data without having to keep everything in memory. + - Add new functions to the C API for qpdfjob that use a + ``qpdfjob_handle``. Like with the regular C API for qpdf, you + have to call ``qpdfjob_init`` first, pass the handle to the + functions, and call ``qpdfjob_cleanup`` at the end. This + interface offers more flexibility than the old interface, which + remains available. + - Other changes - In JSON v1 mode, the ``"objects"`` key now reflects the repaired diff --git a/qpdf/qtest/qpdf/qpdfjob-ctest.out b/qpdf/qtest/qpdf/qpdfjob-ctest.out index 1d7df741..ac2959f2 100644 --- a/qpdf/qtest/qpdf/qpdfjob-ctest.out +++ b/qpdf/qtest/qpdf/qpdfjob-ctest.out @@ -1,7 +1,7 @@ argv test passed json test passed WARNING: xref-with-short-size.pdf (xref stream, offset 16227): Cross-reference stream data has the wrong size; expected = 52; actual = 56 -qpdf: operation succeeded with warnings; resulting file may have some problems +qpdfjob json: operation succeeded with warnings; resulting file may have some problems json warn test passed qpdfjob json: an output file name is required; use - for standard output json error test passed