Add C API functions for using custom loggers

Expose functions to the C API to create new loggers and to setLogger
and getLogger for QPDF and QPDFJob.
This commit is contained in:
Jay Berkenbilt 2022-09-09 08:03:21 -04:00
parent 0ad4e190ff
commit 3dbab589e3
13 changed files with 163 additions and 24 deletions

View File

@ -1,3 +1,8 @@
2022-09-09 Jay Berkenbilt <ejb@ql.org>
* Expose ability to create custom loggers and to get and set the
logger for QPDF and QPDFJob through the C API.
2022-09-08 Jay Berkenbilt <ejb@ql.org>
* Added new functions to the C API to support qpdf JSON:

7
TODO
View File

@ -8,12 +8,7 @@ Always
Next
====
Pending changes:
* Consider also exposing a way to set a new logger and to get the
logger from QPDF and QPDFJob in the C API.
Soon: Break ground on "Document-level work"
* Break ground on "Document-level work"
Possible future JSON enhancements
=================================

View File

@ -140,6 +140,7 @@
#include <qpdf/Constants.h>
#include <qpdf/DLL.h>
#include <qpdf/Types.h>
#include <qpdf/qpdflogger-c.h>
#include <string.h>
#ifdef __cplusplus
@ -249,6 +250,20 @@ extern "C" {
QPDF_DLL
void qpdf_set_suppress_warnings(qpdf_data qpdf, QPDF_BOOL value);
/* LOG FUNCTIONS */
/* Set or get the current logger. You need to call
* qpdflogger_cleanup on the logger handles when you are done with
* the handles. The underlying logger is cleaned up automatically
* and persists if needed after the logger handle is destroyed.
* See comments in qpdflogger-c.h for details.
*/
QPDF_DLL
void qpdf_set_logger(qpdf_data qpdf, qpdflogger_handle logger);
QPDF_DLL
qpdflogger_handle qpdf_get_logger(qpdf_data qpdf);
/* CHECK FUNCTIONS */
/* Attempt to read the entire PDF file to see if there are any

View File

@ -32,6 +32,7 @@
*/
#include <qpdf/DLL.h>
#include <qpdf/qpdflogger-c.h>
#include <string.h>
#ifndef QPDF_NO_WCHAR_T
# include <wchar.h>
@ -92,6 +93,18 @@ extern "C" {
QPDF_DLL
void qpdfjob_cleanup(qpdfjob_handle* j);
/* Set or get the current logger. You need to call
* qpdflogger_cleanup on the logger handles when you are done with
* the handles. The underlying logger is cleaned up automatically
* and persists if needed after the logger handle is destroyed.
* See comments in qpdflogger-c.h for details.
*/
QPDF_DLL
void qpdfjob_set_logger(qpdfjob_handle j, qpdflogger_handle logger);
QPDF_DLL
qpdflogger_handle qpdfjob_get_logger(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.

View File

@ -38,16 +38,28 @@ extern "C" {
/* To operate on a logger, you need a handle to it. call
* qpdflogger_default_logger to get a handle for the default
* logger. The qpdf and qpdfjob functions may offer ways to get
* other logger handles. When you're done with the logger handler,
* call qpdflogger_cleanup. This does not destroy the underlying
* log object. It just cleans up the handle to it.
* logger. There are functions in qpdf-c.h and qpdfjob-c.h that
* also take or return logger handles. When you're done with the
* logger handler, call qpdflogger_cleanup. This cleans up the
* handle but leaves the underlying log object intact. (It uses a
* shared pointer and will be cleaned up automatically when it is
* no longer in use.) That means you can create a logger with
* qpdflogger_create(), pass the logger handle to a function in
* qpdf-c.h or qpdfjob-c.h, and then clean it up, subject to
* constraints imposed by the other function.
*/
typedef struct _qpdflogger_handle* qpdflogger_handle;
QPDF_DLL
qpdflogger_handle qpdflogger_default_logger();
/* Calling cleanup on the handle returned by qpdflogger_create
* destroys the handle but not the underlying logger. See comments
* above.
*/
QPDF_DLL
qpdflogger_handle qpdflogger_create();
QPDF_DLL
void qpdflogger_cleanup(qpdflogger_handle* l);
@ -95,6 +107,10 @@ extern "C" {
void qpdflogger_save_to_standard_output(
qpdflogger_handle l, int only_if_not_set);
/* For testing */
QPDF_DLL
int qpdflogger_equal(qpdflogger_handle l1, qpdflogger_handle l2);
#ifdef __cplusplus
}
#endif

View File

@ -12,6 +12,7 @@
#include <qpdf/QPDFWriter.hh>
#include <qpdf/QTC.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/qpdflogger-c_impl.hh>
#include <cstring>
#include <functional>
@ -279,6 +280,18 @@ qpdf_check_pdf(qpdf_data qpdf)
return status;
}
void
qpdf_set_logger(qpdf_data qpdf, qpdflogger_handle logger)
{
qpdf->qpdf->setLogger(logger->l);
}
qpdflogger_handle
qpdf_get_logger(qpdf_data qpdf)
{
return new _qpdflogger_handle(qpdf->qpdf->getLogger());
}
void
qpdf_set_suppress_warnings(qpdf_data qpdf, QPDF_BOOL value)
{

View File

@ -0,0 +1,11 @@
#include <qpdf/qpdflogger-c.h>
#include <qpdf/QPDFLogger.hh>
struct _qpdflogger_handle
{
_qpdflogger_handle(std::shared_ptr<QPDFLogger> l);
~_qpdflogger_handle() = default;
std::shared_ptr<QPDFLogger> l;
};

View File

@ -4,6 +4,7 @@
#include <qpdf/QPDFLogger.hh>
#include <qpdf/QPDFUsage.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/qpdflogger-c_impl.hh>
#include <cstdio>
#include <cstring>
@ -41,6 +42,18 @@ wrap_qpdfjob(qpdfjob_handle j, std::function<int(qpdfjob_handle j)> fn)
return QPDFJob::EXIT_ERROR;
}
void
qpdfjob_set_logger(qpdfjob_handle j, qpdflogger_handle logger)
{
j->j.setLogger(logger->l);
}
qpdflogger_handle
qpdfjob_get_logger(qpdfjob_handle j)
{
return new _qpdflogger_handle(j->j.getLogger());
}
int
qpdfjob_initialize_from_argv(qpdfjob_handle j, char const* const argv[])
{

View File

@ -1,5 +1,7 @@
#include <qpdf/qpdflogger-c.h>
#include <qpdf/qpdflogger-c_impl.hh>
#include <qpdf/Pipeline.hh>
#include <qpdf/Pl_Function.hh>
#include <qpdf/QIntC.hh>
@ -7,14 +9,6 @@
#include <functional>
#include <memory>
struct _qpdflogger_handle
{
_qpdflogger_handle(std::shared_ptr<QPDFLogger> l);
~_qpdflogger_handle() = default;
std::shared_ptr<QPDFLogger> l;
};
_qpdflogger_handle::_qpdflogger_handle(std::shared_ptr<QPDFLogger> l) :
l(l)
{
@ -26,6 +20,12 @@ qpdflogger_default_logger()
return new _qpdflogger_handle(QPDFLogger::defaultLogger());
}
qpdflogger_handle
qpdflogger_create()
{
return new _qpdflogger_handle(QPDFLogger::create());
}
void
qpdflogger_cleanup(qpdflogger_handle* l)
{
@ -125,3 +125,9 @@ qpdflogger_save_to_standard_output(qpdflogger_handle l, int only_if_not_set)
qpdflogger_set_save(
l, qpdf_log_dest_stdout, nullptr, nullptr, only_if_not_set);
}
int
qpdflogger_equal(qpdflogger_handle l1, qpdflogger_handle l2)
{
return l1->l.get() == l2->l.get();
}

View File

@ -140,6 +140,15 @@ write_to_file(char const* data, size_t size, void* udata)
return fwrite(data, 1, size, f) != size;
}
static int
custom_log(char const* data, size_t size, void* udata)
{
fprintf(stderr, "|custom|");
fwrite(data, 1, size, stderr);
fflush(stderr);
return 0;
}
static void
test01(
char const* infile,
@ -583,6 +592,20 @@ test23(
char const* outfile,
char const* xarg)
{
/* Test check and also exercise custom logger */
qpdflogger_handle l1 = qpdf_get_logger(qpdf);
qpdflogger_handle l2 = qpdflogger_default_logger();
assert(qpdflogger_equal(l1, l2));
qpdflogger_cleanup(&l1);
qpdflogger_cleanup(&l2);
qpdflogger_handle l = qpdflogger_create();
qpdflogger_set_warn(l, qpdf_log_dest_custom, custom_log, NULL);
qpdf_set_logger(qpdf, l);
qpdflogger_handle l3 = qpdf_get_logger(qpdf);
assert(qpdflogger_equal(l, l3));
qpdflogger_cleanup(&l);
qpdflogger_cleanup(&l3);
QPDF_ERROR_CODE status = 0;
qpdf_read(qpdf, infile, password);
status = qpdf_check_pdf(qpdf);

View File

@ -26,6 +26,15 @@ custom_progress(int progress, void* data)
printf("%s: write progress: %d%%\n", (char const*)data, progress);
}
static int
custom_log(char const* data, size_t size, void* udata)
{
fprintf(stderr, "|custom|");
fwrite(data, 1, size, stderr);
fflush(stderr);
return 0;
}
static void
run_tests()
{
@ -55,6 +64,7 @@ run_tests()
\"objectStreams\": \"generate\"\n\
}") == 0);
printf("json test passed\n");
fflush(stdout);
assert(qpdfjob_run_from_json("{\n\
\"inputFile\": \"xref-with-short-size.pdf\",\n\
@ -64,10 +74,28 @@ run_tests()
\"objectStreams\": \"generate\"\n\
}") == 3);
printf("json warn test passed\n");
fflush(stdout);
assert(qpdfjob_run_from_json("{\n\
/* Also exercise custom logger */
j = qpdfjob_init();
qpdflogger_handle l1 = qpdfjob_get_logger(j);
qpdflogger_handle l2 = qpdflogger_default_logger();
assert(qpdflogger_equal(l1, l2));
qpdflogger_cleanup(&l1);
qpdflogger_cleanup(&l2);
qpdflogger_handle l = qpdflogger_create();
qpdflogger_set_error(l, qpdf_log_dest_custom, custom_log, NULL);
qpdfjob_set_logger(j, l);
qpdflogger_handle l3 = qpdfjob_get_logger(j);
assert(qpdflogger_equal(l, l3));
qpdflogger_cleanup(&l);
qpdflogger_cleanup(&l3);
qpdfjob_initialize_from_json(j, "{\n\
\"inputFile\": \"nothing-there.pdf\"\n\
}") == 2);
}");
assert(qpdfjob_run(j) == 2);
qpdfjob_cleanup(&j);
printf("json error test passed\n");
}

View File

@ -1,6 +1,6 @@
WARNING: c-check-warn-in.pdf: file is damaged
WARNING: c-check-warn-in.pdf (offset 1556): xref not found
WARNING: c-check-warn-in.pdf: Attempting to reconstruct cross-reference table
|custom|WARNING: |custom|c-check-warn-in.pdf: file is damaged|custom|
|custom|WARNING: |custom|c-check-warn-in.pdf (offset 1556): xref not found|custom|
|custom|WARNING: |custom|c-check-warn-in.pdf: Attempting to reconstruct cross-reference table|custom|
status: 1
warning: c-check-warn-in.pdf: file is damaged
code: 5

View File

@ -6,5 +6,6 @@ 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
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
|custom|qpdfjob json|custom|: |custom|an output file name is required; use - for standard output|custom|
|custom|qpdfjob json|custom|: |custom|an output file name is required; use - for standard output|custom|
json error test passed