From bc4e2320e7dafea8b6d6b6150c808ed2a98d7d03 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Tue, 1 Feb 2022 08:55:18 -0500 Subject: Add qpdfjob-c.h -- simple C API around parts of QPDFJob --- include/qpdf/qpdfjob-c.h | 79 +++++++++++++++++++++++++++++++++ libqpdf/build.mk | 3 +- libqpdf/qpdfjob-c.cc | 50 +++++++++++++++++++++ manual/qpdf-job.rst | 4 +- qpdf/build.mk | 4 +- qpdf/qpdfjob-ctest.c | 73 ++++++++++++++++++++++++++++++ qpdf/qtest/qpdf.test | 30 ++++++++++++- qpdf/qtest/qpdf/qpdfjob-ctest-wide.pdf | Bin 0 -> 799 bytes qpdf/qtest/qpdf/qpdfjob-ctest.out | 7 +++ qpdf/qtest/qpdf/qpdfjob-ctest1.pdf | Bin 0 -> 799 bytes qpdf/qtest/qpdf/qpdfjob-ctest2.pdf | Bin 0 -> 3379 bytes qpdf/qtest/qpdf/qpdfjob-ctest3.pdf | Bin 0 -> 16504 bytes 12 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 include/qpdf/qpdfjob-c.h create mode 100644 libqpdf/qpdfjob-c.cc create mode 100644 qpdf/qpdfjob-ctest.c create mode 100644 qpdf/qtest/qpdf/qpdfjob-ctest-wide.pdf create mode 100644 qpdf/qtest/qpdf/qpdfjob-ctest.out create mode 100644 qpdf/qtest/qpdf/qpdfjob-ctest1.pdf create mode 100644 qpdf/qtest/qpdf/qpdfjob-ctest2.pdf create mode 100644 qpdf/qtest/qpdf/qpdfjob-ctest3.pdf diff --git a/include/qpdf/qpdfjob-c.h b/include/qpdf/qpdfjob-c.h new file mode 100644 index 00000000..59c42822 --- /dev/null +++ b/include/qpdf/qpdfjob-c.h @@ -0,0 +1,79 @@ +/* Copyright (c) 2005-2021 Jay Berkenbilt + * + * This file is part of qpdf. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Versions of qpdf prior to version 7 were released under the terms + * of version 2.0 of the Artistic License. At your option, you may + * continue to consider qpdf to be licensed under those terms. Please + * see the manual for additional information. + */ + +#ifndef QPDFJOB_C_H +#define QPDFJOB_C_H + +/* + * This file defines a basic "C" API for QPDFJob. See also qpdf-c.h, + * which defines an API that exposes more of the library's API. This + * API is primarily intended to make it simpler for programs in + * languages other than C++ to incorporate functionality that could be + * run directly from the command-line. + */ + +#include +#include +#ifndef QPDF_NO_WCHAR_T +# include +#endif + +/* + * This file provides a minimal wrapper around QPDFJob. See + * examples/qpdf-job.c for an example of its use. + */ + +#ifdef __cplusplus +extern "C" { +#endif + /* This function does the equivalent of running the qpdf + * command-line with the given arguments and returns the exit code + * that qpdf would use. Note that arguments must be UTF8-encoded. + * If calling this from wmain on Windows, use + * qpdfjob_run_from_wide_argv instead. + */ + QPDF_DLL + int qpdfjob_run_from_argv(int argc, char* argv[]); + +#ifndef QPDF_NO_WCHAR_T + /* This function is the same as qpdfjob_run_from_argv except argv + * is encoded with wide characters. This would suitable for + * calling from a Windows wmain function. + */ + QPDF_DLL + int qpdfjob_run_from_wide_argv(int argc, wchar_t* argv[]); +#endif /* QPDF_NO_WCHAR_T */ + + /* This function runs QPDFJob from a job JSON file. See the "QPDF + * Job" section of the manual for details. The JSON string must be + * UTF8-encoded. It returns the error code that qpdf would return + * with the equivalent command-line invocation. + */ + QPDF_DLL + int qpdfjob_run_from_json(char const* json); + +#ifdef __cplusplus +} +#endif + + +#endif /* QPDFJOB_C_H */ diff --git a/libqpdf/build.mk b/libqpdf/build.mk index e16ba5e1..3dedfc71 100644 --- a/libqpdf/build.mk +++ b/libqpdf/build.mk @@ -115,7 +115,8 @@ SRCS_libqpdf = \ libqpdf/SecureRandomDataProvider.cc \ libqpdf/SF_FlateLzwDecode.cc \ libqpdf/SparseOHArray.cc \ - libqpdf/qpdf-c.cc + libqpdf/qpdf-c.cc \ + libqpdf/qpdfjob-c.cc ifeq ($(USE_CRYPTO_NATIVE), 1) SRCS_libqpdf += $(CRYPTO_NATIVE) diff --git a/libqpdf/qpdfjob-c.cc b/libqpdf/qpdfjob-c.cc new file mode 100644 index 00000000..7b3f6b60 --- /dev/null +++ b/libqpdf/qpdfjob-c.cc @@ -0,0 +1,50 @@ +#include + +#include +#include +#include + +#include +#include + +int qpdfjob_run_from_argv(int argc, char* argv[]) +{ + auto whoami = QUtil::getWhoami(argv[0]); + QUtil::setLineBuf(stdout); + + QPDFJob j; + try + { + j.initializeFromArgv(argc, argv); + j.run(); + } + catch (std::exception& e) + { + std::cerr << whoami << ": " << e.what() << std::endl; + return QPDFJob::EXIT_ERROR; + } + return j.getExitCode(); +} + +#ifndef QPDF_NO_WCHAR_T +int qpdfjob_run_from_wide_argv(int argc, wchar_t* argv[]) +{ + return QUtil::call_main_from_wmain(argc, argv, qpdfjob_run_from_argv); +} +#endif // QPDF_NO_WCHAR_T + +int qpdfjob_run_from_json(char const* json) +{ + QPDFJob j; + try + { + j.initializeFromJson(json); + j.run(); + } + catch (std::exception& e) + { + std::cerr << "qpdfjob json: " << e.what() << std::endl; + return QPDFJob::EXIT_ERROR; + } + return j.getExitCode(); +} diff --git a/manual/qpdf-job.rst b/manual/qpdf-job.rst index 72e02305..5ee497b1 100644 --- a/manual/qpdf-job.rst +++ b/manual/qpdf-job.rst @@ -14,7 +14,7 @@ executable is available from inside the C++ library using the - Use from the C++ API with ``QPDFJob::initializeFromArgv`` - - Use from the C API with QXXXQ + - Use from the C API with ``qpdfjob_run_from_argv`` from :file:`qpdfjob-c.h` - The job JSON file format @@ -22,7 +22,7 @@ executable is available from inside the C++ library using the - Use from the C++ API with ``QPDFJob::initializeFromJson`` - - Use from the C API with QXXXQ + - Use from the C API with ``qpdfjob_run_from_json`` from :file:`qpdfjob-c.h` - The ``QPDFJob`` C++ API diff --git a/qpdf/build.mk b/qpdf/build.mk index bf1f52f8..36224090 100644 --- a/qpdf/build.mk +++ b/qpdf/build.mk @@ -12,7 +12,9 @@ BINS_qpdf = \ test_tokenizer \ test_unicode_filenames \ test_xref -CBINS_qpdf = qpdf-ctest +CBINS_qpdf = \ + qpdf-ctest \ + qpdfjob-ctest TARGETS_qpdf = $(foreach B,$(BINS_qpdf) $(CBINS_qpdf),qpdf/$(OUTPUT_DIR)/$(call binname,$(B))) diff --git a/qpdf/qpdfjob-ctest.c b/qpdf/qpdfjob-ctest.c new file mode 100644 index 00000000..3de2fa96 --- /dev/null +++ b/qpdf/qpdfjob-ctest.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include + +#ifndef QPDF_NO_WCHAR_T +static void wide_test() +{ + wchar_t* argv[5]; + argv[0] = (wchar_t*)(L"qpdfjob"); + argv[1] = (wchar_t*)(L"minimal.pdf"); + argv[2] = (wchar_t*)(L"a.pdf"); + argv[3] = (wchar_t*)(L"--static-id"); + argv[4] = NULL; + assert(qpdfjob_run_from_wide_argv(4, argv) == 0); + printf("wide test passed\n"); +} +#endif // QPDF_NO_WCHAR_T + +static void run_tests() +{ + /* Be sure to use a different output file for each test. */ + + char* argv[5]; + argv[0] = (char*)("qpdfjob"); + argv[1] = (char*)("minimal.pdf"); + argv[2] = (char*)("a.pdf"); + argv[3] = (char*)("--deterministic-id"); + argv[4] = NULL; + assert(qpdfjob_run_from_argv(4, argv) == 0); + printf("argv test passed\n"); + + assert(qpdfjob_run_from_json("{\n\ + \"inputFile\": \"20-pages.pdf\",\n\ + \"password\": \"user\",\n\ + \"outputFile\": \"b.pdf\",\n\ + \"staticId\": \"\",\n\ + \"decrypt\": \"\",\n\ + \"objectStreams\": \"generate\"\n\ +}") == 0); + printf("json test passed\n"); + + assert(qpdfjob_run_from_json("{\n\ + \"inputFile\": \"xref-with-short-size.pdf\",\n\ + \"outputFile\": \"c.pdf\",\n\ + \"staticId\": \"\",\n\ + \"decrypt\": \"\",\n\ + \"objectStreams\": \"generate\"\n\ +}") == 3); + printf("json warn test passed\n"); + + assert(qpdfjob_run_from_json("{\n\ + \"inputFile\": \"nothing-there.pdf\"\n\ +}") == 2); + printf("json error test passed\n"); +} + +int main(int argc, char* argv[]) +{ + if ((argc == 2) && (strcmp(argv[1], "wide") == 0)) + { +#ifndef QPDF_NO_WCHAR_T + wide_test(); +#else + printf("skipped wide\n"); +#endif // QPDF_NO_WCHAR_T + return 0; + } + + run_tests(); + return 0; +} diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 040567cf..dcc707a3 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -402,7 +402,7 @@ my @good_json = ( "underlay-overlay-password", "misc-options", ); -$n_tests += 4 + scalar(@bad_json) + (2 * scalar(@good_json)); +$n_tests += 10 + scalar(@bad_json) + (2 * scalar(@good_json)); foreach my $i (@bad_json) @@ -457,6 +457,34 @@ $td->runtest("json output from job", {$td->FILE => "job-json-output.out.json", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); +$td->runtest("C job API", + {$td->COMMAND => "qpdfjob-ctest"}, + {$td->FILE => "qpdfjob-ctest.out", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); +foreach my $i (['a.pdf', 1], ['b.pdf', 2], ['c.pdf', 3]) +{ + $td->runtest("check output", + {$td->FILE => $i->[0]}, + {$td->FILE => "qpdfjob-ctest$i->[1].pdf"}); +} +my $wide_out = `qpdfjob-ctest wide`; +$td->runtest("qpdfjob-ctest wide", + {$td->STRING => "$?: $wide_out"}, + {$td->REGEXP => "0: (wide test passed|skipped wide)\n"}, + $td->NORMALIZE_NEWLINES); +if ($wide_out =~ m/skipped/) +{ + $td->runtest("skipped wide", + {$td->STRING => "yes"}, + {$td->STRING => "yes"}); +} +else +{ + $td->runtest("check output", + {$td->FILE => "a.pdf"}, + {$td->FILE => "qpdfjob-ctest-wide.pdf"}); +} + show_ntests(); # ---------- $td->notify("--- Form Tests ---"); diff --git a/qpdf/qtest/qpdf/qpdfjob-ctest-wide.pdf b/qpdf/qtest/qpdf/qpdfjob-ctest-wide.pdf new file mode 100644 index 00000000..b8c692ed Binary files /dev/null and b/qpdf/qtest/qpdf/qpdfjob-ctest-wide.pdf differ diff --git a/qpdf/qtest/qpdf/qpdfjob-ctest.out b/qpdf/qtest/qpdf/qpdfjob-ctest.out new file mode 100644 index 00000000..1d7df741 --- /dev/null +++ b/qpdf/qtest/qpdf/qpdfjob-ctest.out @@ -0,0 +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 +json warn test passed +qpdfjob json: an output file name is required; use - for standard output +json error test passed diff --git a/qpdf/qtest/qpdf/qpdfjob-ctest1.pdf b/qpdf/qtest/qpdf/qpdfjob-ctest1.pdf new file mode 100644 index 00000000..b2c4c2c0 Binary files /dev/null and b/qpdf/qtest/qpdf/qpdfjob-ctest1.pdf differ diff --git a/qpdf/qtest/qpdf/qpdfjob-ctest2.pdf b/qpdf/qtest/qpdf/qpdfjob-ctest2.pdf new file mode 100644 index 00000000..a5b2d868 Binary files /dev/null and b/qpdf/qtest/qpdf/qpdfjob-ctest2.pdf differ diff --git a/qpdf/qtest/qpdf/qpdfjob-ctest3.pdf b/qpdf/qtest/qpdf/qpdfjob-ctest3.pdf new file mode 100644 index 00000000..0031d7c4 Binary files /dev/null and b/qpdf/qtest/qpdf/qpdfjob-ctest3.pdf differ -- cgit v1.2.3-54-g00ecf