Add examples for output capture (fixes #691)

This commit is contained in:
Jay Berkenbilt 2022-06-18 14:16:56 -04:00
parent 8130d50e3b
commit 8e361d98f0
10 changed files with 218 additions and 19 deletions

17
TODO
View File

@ -9,12 +9,12 @@ Before Release:
* Release qtest with updates to qtest-driver and copy back into qpdf
Next:
* output capture
* QPDFPagesTree -- avoid ever flattening the pages tree.
* JSON v2 fixes
Pending changes:
* Allow users to supply a custom progress reporter for QPDFJob
* Check about runpath in the linux-bin distribution. I think the
appimage build specifically is setting the runpath, which is
actually desirable in this case. Make sure to understand and
@ -42,21 +42,6 @@ Pending changes:
Soon: Break ground on "Document-level work"
Output Capture + QPDFJob
========================
QPDFJob:
* Allow users to supply a custom progress reporter for QPDFJob
Output Capture
See https://github.com/qpdf/qpdf/issues/691
There needs to be a C API to QPDFLogger. Use functions like this:
void set_info((*f)(char* data, unsigned int len, void* udata), void* udata);
QPDFPagesTree
=============

View File

@ -14,11 +14,13 @@ set(EXAMPLE_CXX_PROGRAMS
pdf-parse-content
pdf-set-form-values
pdf-split-pages
qpdf-job)
qpdf-job
qpdfjob-save-attachment)
set(EXAMPLE_C_PROGRAMS
pdf-c-objects
pdf-linearize
qpdfjob-c)
qpdfjob-c
qpdfjob-c-save-attachment)
foreach(PROG ${EXAMPLE_CXX_PROGRAMS})
add_executable(${PROG} ${PROG}.cc)

View File

@ -0,0 +1,100 @@
#include <qpdf/Constants.h>
#include <qpdf/qpdfjob-c.h>
#include <qpdf/qpdflogger-c.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// This example demonstrates how we can redirect where saved output
// goes by calling the default logger's setSave method before running
// something with QPDFJob. See qpdfjob-c-save-attachment.c for an
// implementation that uses the C API.
static void
save_to_file(char const* data, size_t len, void* udata)
{
FILE* f = (FILE*)udata;
fwrite(data, 1, len, f);
}
static FILE*
do_fopen(char const* filename)
{
FILE* f = NULL;
#ifdef _MSC_VER
if (fopen_s(&f, filename, "wb") != 0) {
f = NULL;
}
#else
f = fopen(filename, "wb");
#endif
if (f == NULL) {
fprintf(stderr, "unable to open %s\n", filename);
exit(2);
}
return f;
}
int
main(int argc, char* argv[])
{
char const* whoami = "qpdfjob-c-save-attachment";
char const* filename = NULL;
char const* key = NULL;
char const* outfilename = NULL;
char* attachment_arg = NULL;
char const* attachment_flag = "--show-attachment=";
size_t flag_len = 0;
FILE* outfile = NULL;
int status = 0;
qpdfjob_handle j = NULL;
qpdflogger_handle l = qpdflogger_default_logger();
if (argc != 4) {
fprintf(stderr, "Usage: %s file attachment-key outfile\n", whoami);
exit(2);
}
filename = argv[1];
key = argv[2];
outfilename = argv[3];
flag_len = strlen(attachment_flag) + strlen(key) + 1;
attachment_arg = malloc(flag_len);
#ifdef _MSC_VER
strncpy_s(attachment_arg, flag_len, attachment_flag, flag_len);
strncat_s(attachment_arg, flag_len, key, flag_len - strlen(attachment_arg));
#else
strncpy(attachment_arg, attachment_flag, flag_len);
strncat(attachment_arg, key, flag_len - strlen(attachment_arg));
#endif
char const* j_argv[5] = {
whoami,
filename,
attachment_arg,
"--",
NULL,
};
outfile = do_fopen(outfilename);
/* Use qpdflogger_set_save with a callback function to redirect
* saved data. You can use other qpdf logger functions to capture
* informational output, warnings, and errors.
*/
qpdflogger_set_save(
l, qpdf_log_dest_custom, save_to_file, (void*)outfile, 0);
qpdflogger_cleanup(&l);
j = qpdfjob_init();
status = (qpdfjob_initialize_from_argv(j, j_argv) ||
qpdfjob_run(j));
qpdfjob_cleanup(&j);
free(attachment_arg);
fclose(outfile);
if (status == qpdf_exit_success) {
printf("%s: wrote attachment to %s\n", whoami, outfilename);
}
return 0;
}

View File

@ -52,6 +52,10 @@ main(int argc, char* argv[])
* To use that from C just like the argv one, call
* qpdfjob_run_from_json instead and pass the json string as a
* single char const* argument.
*
* See qpdfjob-c-save-attachment.c for an example of using the
* full form of the qpdfjob interface with init and cleanup
* functions.
*/
r = qpdfjob_run_from_argv(new_argv);
return r;

View File

@ -0,0 +1,51 @@
#include <qpdf/Pl_StdioFile.hh>
#include <qpdf/QPDFJob.hh>
#include <qpdf/QPDFLogger.hh>
#include <qpdf/QUtil.hh>
// This example demonstrates how we can redirect where saved output
// goes by calling the default logger's setSave method before running
// something with QPDFJob. See qpdfjob-c-save-attachment.c for an
// implementation that uses the C API.
int
main(int argc, char* argv[])
{
auto whoami = QUtil::getWhoami(argv[0]);
if (argc != 4) {
std::cerr << "Usage: " << whoami << " file attachment-key outfile"
<< std::endl;
exit(2);
}
char const* filename = argv[1];
char const* key = argv[2];
char const* outfilename = argv[3];
std::string attachment_arg = "--show-attachment=";
attachment_arg += key;
char const* j_argv[] = {
whoami,
filename,
attachment_arg.c_str(),
"--",
nullptr,
};
QUtil::FileCloser fc(QUtil::safe_fopen(outfilename, "wb"));
auto save = std::make_shared<Pl_StdioFile>("capture", fc.f);
QPDFLogger::defaultLogger()->setSave(save, false);
try {
QPDFJob j;
j.initializeFromArgv(j_argv);
j.run();
} catch (std::exception& e) {
std::cerr << whoami << ": " << e.what() << std::endl;
exit(2);
}
std::cout << whoami << ": wrote attachment to " << outfilename << std::endl;
return 0;
}

Binary file not shown.

View File

@ -0,0 +1,42 @@
#!/usr/bin/env perl
require 5.008;
use warnings;
use strict;
chdir("qpdfjob-attachment") or die "chdir testdir failed: $!\n";
require TestDriver;
cleanup();
my $td = new TestDriver('qpdfjob-attachment');
$td->runtest("save attachment (C)",
{$td->COMMAND => "qpdfjob-c-save-attachment attach.pdf a a"},
{$td->STRING =>
"qpdfjob-c-save-attachment: wrote attachment to a\n",
$td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("check output",
{$td->FILE => "a"},
{$td->STRING => "quack\n"},
$td->NORMALIZE_NEWLINES);
$td->runtest("save attachment (C++)",
{$td->COMMAND => "qpdfjob-save-attachment attach.pdf a b"},
{$td->STRING =>
"qpdfjob-save-attachment: wrote attachment to b\n",
$td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("check output",
{$td->FILE => "b"},
{$td->STRING => "quack\n"},
$td->NORMALIZE_NEWLINES);
cleanup();
$td->report(4);
sub cleanup
{
unlink "a", "b";
}

View File

@ -56,6 +56,10 @@ class QPDFLogger
// error -- standard error
// save -- undefined unless set
//
// "info" is used for diagnostic messages, verbose messages, and
// progress messages. "warn" is used for warnings. "error" is used
// for errors. "save" is used for saving output -- see below.
//
// On deletion, finish() is called for the standard output and
// standard error pipelines, which flushes output. If you supply
// any custom pipelines, you must call finish() on them yourself.
@ -64,6 +68,13 @@ class QPDFLogger
//
// NOTES ABOUT THE SAVE PIPELINE
//
// The save pipeline is used by QPDFJob when some kind of binary
// output is being saved. This includes saving attachments and
// stream data and also includes when the output file is standard
// output. If you want to grab that output, you can call setSave.
// See examples/qpdfjob-save-attachment.cc and
// examples/qpdfjob-c-save-attachment.c.
//
// You should never set the save pipeline to the same destination
// as something else. Doing so will corrupt your save output. If
// you want to save to standard output, use the method

View File

@ -25,7 +25,8 @@
/*
* This file provides a C API for QPDFLogger. See QPDFLogger.hh for
* information about the logger.
* information about the logger and
* examples/qpdfjob-c-save-attachment.c for an example.
*/
#include <qpdf/DLL.h>

View File

@ -160,6 +160,9 @@ For a detailed list of changes, please see the file
- A C API is available in :file:`include/qpdf/qpdflogger-c.h`.
- See examples :file:`examples/qpdfjob-save-attachment.cc` and
:file:`examples/qpdfjob-c-save-attachment.cc`.
- New methods ``insertItemAndGet``, ``appendItemAndGet``,
``eraseItemAndGet``, ``replaceKeyAndGet``, and
``removeKeyAndGet`` return the newly added or removed object.