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>
|
2023-12-10 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
* 11.6.4: release
|
* 11.6.4: release
|
||||||
|
@ -34,6 +34,11 @@ foreach(PROG ${EXAMPLE_C_PROGRAMS})
|
|||||||
endforeach()
|
endforeach()
|
||||||
target_include_directories(pdf-create PRIVATE ${JPEG_INCLUDE})
|
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(
|
add_test(
|
||||||
NAME examples
|
NAME examples
|
||||||
COMMAND ${RUN_QTEST}
|
COMMAND ${RUN_QTEST}
|
||||||
@ -47,7 +52,7 @@ add_test(
|
|||||||
--tc "${qpdf_SOURCE_DIR}/examples/*.cc"
|
--tc "${qpdf_SOURCE_DIR}/examples/*.cc"
|
||||||
--tc "${qpdf_SOURCE_DIR}/examples/*.c")
|
--tc "${qpdf_SOURCE_DIR}/examples/*.c")
|
||||||
|
|
||||||
file(GLOB EXAMPLES_SRC "*.c" "*.cc")
|
file(GLOB EXAMPLES_SRC "*.c" "*.cc" "*.h")
|
||||||
if(INSTALL_EXAMPLES)
|
if(INSTALL_EXAMPLES)
|
||||||
install(FILES ${EXAMPLES_SRC}
|
install(FILES ${EXAMPLES_SRC}
|
||||||
DESTINATION ${CMAKE_INSTALL_DOCDIR}/examples
|
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
|
* 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
|
* 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++
|
* 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.
|
* 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
|
* Error handling is tricky because the underlying C++ API uses exception handling. See "ERROR
|
||||||
* HANDLING" below for a detailed explanation.
|
* HANDLING" below for a detailed explanation.
|
||||||
*
|
*
|
||||||
* The C API is not as rich as the C++ API. For any operations that involve actually
|
* The C API is not as rich as the C++ API. For many operations, you must use the C++ API. The C
|
||||||
* manipulating PDF objects, you must use the C++ API. The C API is primarily useful for doing
|
* API is primarily useful for doing basic transformations on PDF files similar to what you
|
||||||
* basic transformations on PDF files similar to what you might do with the qpdf command-line
|
* might do with the qpdf command-line tool. You can write your own `extern "C"` functions in
|
||||||
* tool.
|
* 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
|
* 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
|
* 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);
|
QPDF_ERROR_CODE qpdf_remove_page(qpdf_data qpdf, qpdf_oh page);
|
||||||
#ifdef __cplusplus
|
#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
|
||||||
|
|
||||||
#endif /* QPDF_C_H */
|
#endif /* QPDF_C_H */
|
||||||
|
@ -1949,3 +1949,15 @@ qpdf_write_json(
|
|||||||
});
|
});
|
||||||
return status;
|
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
|
.. 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
|
11.6.4: December 10, 2023
|
||||||
- Bug fixes:
|
- Bug fixes:
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user