mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 10:58:58 +00:00
Allow regular C++ functions to interoperate with the C API
This commit is contained in:
parent
924ebf9f6a
commit
d7a364b882
@ -1,3 +1,10 @@
|
||||
2023-12-16 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* Add new C++ functions "qpdf_c_get_qpdf" and "qpdf_c_wrap" to
|
||||
qpdf-c.h that make it possible to write your own extern "C"
|
||||
functions in C++ that interoperate with the C API. See
|
||||
examples/extend-c-api for more information.
|
||||
|
||||
2023-12-10 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* 11.6.4: release
|
||||
|
@ -34,6 +34,11 @@ foreach(PROG ${EXAMPLE_C_PROGRAMS})
|
||||
endforeach()
|
||||
target_include_directories(pdf-create PRIVATE ${JPEG_INCLUDE})
|
||||
|
||||
# extend-c-api contains a mixture of C and C++ files.
|
||||
add_executable(extend-c-api extend-c-api-impl.cc extend-c-api.c)
|
||||
set_property(TARGET extend-c-api PROPERTY LINKER_LANGUAGE CXX)
|
||||
target_link_libraries(extend-c-api libqpdf)
|
||||
|
||||
add_test(
|
||||
NAME examples
|
||||
COMMAND ${RUN_QTEST}
|
||||
@ -47,7 +52,7 @@ add_test(
|
||||
--tc "${qpdf_SOURCE_DIR}/examples/*.cc"
|
||||
--tc "${qpdf_SOURCE_DIR}/examples/*.c")
|
||||
|
||||
file(GLOB EXAMPLES_SRC "*.c" "*.cc")
|
||||
file(GLOB EXAMPLES_SRC "*.c" "*.cc" "*.h")
|
||||
if(INSTALL_EXAMPLES)
|
||||
install(FILES ${EXAMPLES_SRC}
|
||||
DESTINATION ${CMAKE_INSTALL_DOCDIR}/examples
|
||||
|
29
examples/extend-c-api-impl.cc
Normal file
29
examples/extend-c-api-impl.cc
Normal file
@ -0,0 +1,29 @@
|
||||
// This is an example of how to write C++ functions and make them usable with the qpdf C API. It
|
||||
// consists of three files:
|
||||
// - extend-c-api.h -- a plain C header file
|
||||
// - extend-c-api.c -- a C program that calls the function
|
||||
// - extend-c-api.cc -- a C++ file that implements the function
|
||||
|
||||
#include "extend-c-api.h"
|
||||
|
||||
// Here, we add a function to get the number of pages in a PDF file and make it callable through the
|
||||
// C API.
|
||||
|
||||
// This is a normal C++ function that works with QPDF in a normal way. It doesn't do anything
|
||||
// special to be callable from C.
|
||||
int
|
||||
numPages(std::shared_ptr<QPDF> qpdf)
|
||||
{
|
||||
return qpdf->getRoot().getKey("/Pages").getKey("/Count").getIntValueAsInt();
|
||||
}
|
||||
|
||||
// Now we define the glue that makes our function callable using the C API.
|
||||
|
||||
// This is the C++ implementation of the C function.
|
||||
QPDF_ERROR_CODE
|
||||
num_pages(qpdf_data qc, int* npages)
|
||||
{
|
||||
// Call qpdf_c_wrap to convert any exception our function might through to a QPDF_ERROR_CODE
|
||||
// and attach it to the qpdf_data object in the same way as other functions in the C API.
|
||||
return qpdf_c_wrap(qc, [&qc, &npages]() { *npages = numPages(qpdf_c_get_qpdf(qc)); });
|
||||
}
|
67
examples/extend-c-api.c
Normal file
67
examples/extend-c-api.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This is an example of how to write C++ functions and make them usable with the qpdf C API. It
|
||||
* consists of three files:
|
||||
* - extend-c-api.h -- a plain C header file
|
||||
* - extend-c-api.c -- a C program that calls the function
|
||||
* - extend-c-api.cc -- a C++ file that implements the function
|
||||
*/
|
||||
|
||||
#include "extend-c-api.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static char const* whoami = 0;
|
||||
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr, "Usage: %s infile\n", whoami);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
char* infile = NULL;
|
||||
qpdf_data qpdf = qpdf_init();
|
||||
int warnings = 0;
|
||||
int errors = 0;
|
||||
char* p = NULL;
|
||||
|
||||
if ((p = strrchr(argv[0], '/')) != NULL) {
|
||||
whoami = p + 1;
|
||||
} else if ((p = strrchr(argv[0], '\\')) != NULL) {
|
||||
whoami = p + 1;
|
||||
} else {
|
||||
whoami = argv[0];
|
||||
}
|
||||
|
||||
if (argc != 2) {
|
||||
usage();
|
||||
}
|
||||
|
||||
infile = argv[1];
|
||||
|
||||
if ((qpdf_read(qpdf, infile, NULL) & QPDF_ERRORS) == 0) {
|
||||
int npages;
|
||||
if ((num_pages(qpdf, &npages) & QPDF_ERRORS) == 0) {
|
||||
printf("num pages = %d\n", npages);
|
||||
}
|
||||
}
|
||||
if (qpdf_more_warnings(qpdf)) {
|
||||
warnings = 1;
|
||||
}
|
||||
if (qpdf_has_error(qpdf)) {
|
||||
errors = 1;
|
||||
printf("error: %s\n", qpdf_get_error_full_text(qpdf, qpdf_get_error(qpdf)));
|
||||
}
|
||||
qpdf_cleanup(&qpdf);
|
||||
if (errors) {
|
||||
return 2;
|
||||
} else if (warnings) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
25
examples/extend-c-api.h
Normal file
25
examples/extend-c-api.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef EXAMPLE_C_EXTEND_H
|
||||
#define EXAMPLE_C_EXTEND_H
|
||||
|
||||
/*
|
||||
* This is an example of how to write C++ functions and make them usable with the qpdf C API. It
|
||||
* consists of three files:
|
||||
* - extend-c-api.h -- a plain C header file
|
||||
* - extend-c-api.c -- a C program that calls the function
|
||||
* - extend-c-api.cc -- a C++ file that implements the function
|
||||
*/
|
||||
#include <qpdf/qpdf-c.h>
|
||||
|
||||
/* Declare your custom function to return QPDF_ERROR_CODE and take qpdf_data and anything else you
|
||||
* need. Any errors are retrievable through the qpdf C APIs normal error-handling mechanism.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
QPDF_ERROR_CODE num_pages(qpdf_data qc, int* npages);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EXAMPLE_C_EXTEND_H */
|
30
examples/qtest/extend-c-api.test
Normal file
30
examples/qtest/extend-c-api.test
Normal file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env perl
|
||||
require 5.008;
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
chdir("extend-c-api") or die "chdir testdir failed: $!\n";
|
||||
|
||||
require TestDriver;
|
||||
|
||||
cleanup();
|
||||
|
||||
my $td = new TestDriver('extend-c-api');
|
||||
|
||||
$td->runtest("extend C API (good)",
|
||||
{$td->COMMAND => "extend-c-api good.pdf"},
|
||||
{$td->FILE => "good.out", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
$td->runtest("extend C API (bad)",
|
||||
{$td->COMMAND => "extend-c-api bad.pdf"},
|
||||
{$td->FILE => "bad.out", $td->EXIT_STATUS => 2},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
cleanup();
|
||||
|
||||
$td->report(2);
|
||||
|
||||
sub cleanup
|
||||
{
|
||||
unlink "a.pdf";
|
||||
}
|
5
examples/qtest/extend-c-api/bad.out
Normal file
5
examples/qtest/extend-c-api/bad.out
Normal file
@ -0,0 +1,5 @@
|
||||
WARNING: bad.pdf: can't find PDF header
|
||||
WARNING: bad.pdf: file is damaged
|
||||
WARNING: bad.pdf: can't find startxref
|
||||
WARNING: bad.pdf: Attempting to reconstruct cross-reference table
|
||||
error: bad.pdf: unable to find trailer dictionary while recovering damaged file
|
1
examples/qtest/extend-c-api/bad.pdf
Normal file
1
examples/qtest/extend-c-api/bad.pdf
Normal file
@ -0,0 +1 @@
|
||||
not even a pdf file
|
1
examples/qtest/extend-c-api/good.out
Normal file
1
examples/qtest/extend-c-api/good.out
Normal file
@ -0,0 +1 @@
|
||||
num pages = 1
|
64
examples/qtest/extend-c-api/good.pdf
Normal file
64
examples/qtest/extend-c-api/good.pdf
Normal file
@ -0,0 +1,64 @@
|
||||
%PDF-2.0
|
||||
1 0 obj
|
||||
<<
|
||||
/Pages 2 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/Count 1
|
||||
/Kids [
|
||||
3 0 R
|
||||
]
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/Contents 4 0 R
|
||||
/MediaBox [ 0 0 612 792 ]
|
||||
/Parent 2 0 R
|
||||
/Resources <<
|
||||
/Font << /F1 5 0 R >>
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/Length 44
|
||||
>>
|
||||
stream
|
||||
BT
|
||||
/F1 24 Tf
|
||||
72 720 Td
|
||||
(Potato) Tj
|
||||
ET
|
||||
endstream
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica
|
||||
/Encoding /WinAnsiEncoding
|
||||
/Subtype /Type1
|
||||
/Type /Font
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 6
|
||||
0000000000 65535 f
|
||||
0000000009 00000 n
|
||||
0000000062 00000 n
|
||||
0000000133 00000 n
|
||||
0000000277 00000 n
|
||||
0000000372 00000 n
|
||||
trailer <<
|
||||
/Root 1 0 R
|
||||
/Size 6
|
||||
/ID [<42841c13bbf709d79a200fa1691836f8><b1d8b5838eeafe16125317aa78e666aa>]
|
||||
>>
|
||||
startxref
|
||||
478
|
||||
%%EOF
|
@ -25,17 +25,19 @@
|
||||
* capabilities to make them accessible to callers who can't handle calling C++ functions or working
|
||||
* with C++ classes. This may be especially useful to Windows users who are accessing the qpdf DLL
|
||||
* directly or to other people programming in non-C/C++ languages that can call C code but not C++
|
||||
* code.
|
||||
* code. Starting with qpdf 11.7, it is possible to write your own `extern "C"` functions that
|
||||
* interoperate with the C API.
|
||||
*
|
||||
* There are several things to keep in mind when using the C API.
|
||||
*
|
||||
* Error handling is tricky because the underlying C++ API uses exception handling. See "ERROR
|
||||
* HANDLING" below for a detailed explanation.
|
||||
*
|
||||
* The C API is not as rich as the C++ API. For any operations that involve actually
|
||||
* manipulating PDF objects, you must use the C++ API. The C API is primarily useful for doing
|
||||
* basic transformations on PDF files similar to what you might do with the qpdf command-line
|
||||
* tool.
|
||||
* The C API is not as rich as the C++ API. For many operations, you must use the C++ API. The C
|
||||
* API is primarily useful for doing basic transformations on PDF files similar to what you
|
||||
* might do with the qpdf command-line tool. You can write your own `extern "C"` functions in
|
||||
* C++ that interoperate with the C API by using qpdf_c_get_qpdf and qpdf_c_wrap which were
|
||||
* introduced in qpdf 11.7.0.
|
||||
*
|
||||
* These functions store their state in a qpdf_data object. Individual instances of qpdf_data
|
||||
* are not thread-safe: although you may access different qpdf_data objects from different
|
||||
@ -990,6 +992,23 @@ extern "C" {
|
||||
QPDF_ERROR_CODE qpdf_remove_page(qpdf_data qpdf, qpdf_oh page);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
// These C++ functions make it easier to write C++ code that interoperates with the C API.
|
||||
// See examples/extend-c-api.
|
||||
|
||||
# include <functional>
|
||||
# include <memory>
|
||||
|
||||
# include <qpdf/QPDF.hh>
|
||||
|
||||
// Retrieve the real QPDF object attached to this qpdf_data.
|
||||
QPDF_DLL
|
||||
std::shared_ptr<QPDF> qpdf_c_get_qpdf(qpdf_data qpdf);
|
||||
|
||||
// Wrap a C++ function that may throw an exception to translate the exception for retrieval using
|
||||
// the normal QPDF C API methods.
|
||||
QPDF_DLL
|
||||
QPDF_ERROR_CODE qpdf_c_wrap(qpdf_data qpdf, std::function<void()> fn);
|
||||
#endif
|
||||
|
||||
#endif /* QPDF_C_H */
|
||||
|
@ -1949,3 +1949,15 @@ qpdf_write_json(
|
||||
});
|
||||
return status;
|
||||
}
|
||||
|
||||
std::shared_ptr<QPDF>
|
||||
qpdf_c_get_qpdf(qpdf_data qpdf)
|
||||
{
|
||||
return qpdf->qpdf;
|
||||
}
|
||||
|
||||
QPDF_ERROR_CODE
|
||||
qpdf_c_wrap(qpdf_data qpdf, std::function<void()> fn)
|
||||
{
|
||||
return trap_errors(qpdf, [&fn](qpdf_data) { fn(); });
|
||||
}
|
||||
|
@ -38,6 +38,13 @@ Planned changes for future 12.x (subject to change):
|
||||
|
||||
.. x.y.z: not yet released
|
||||
|
||||
11.7.0: not yet released
|
||||
- Library Enhancements:
|
||||
|
||||
- Add C++ functions ``qpdf_c_wrap`` and ``qpdf_c_get_qpdf`` to the
|
||||
C API to enable custom C++ code to interoperate more easily with
|
||||
the the C API. See ``examples/extend-c-api``.
|
||||
|
||||
11.6.4: December 10, 2023
|
||||
- Bug fixes:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user