Commit 8e361d98f0bb23d58cbc773367ba76dffced7bdb

Authored by Jay Berkenbilt
1 parent 8130d50e

Add examples for output capture (fixes #691)

... ... @@ -9,12 +9,12 @@ Before Release:
9 9 * Release qtest with updates to qtest-driver and copy back into qpdf
10 10  
11 11 Next:
12   -* output capture
13 12 * QPDFPagesTree -- avoid ever flattening the pages tree.
14 13 * JSON v2 fixes
15 14  
16 15 Pending changes:
17 16  
  17 +* Allow users to supply a custom progress reporter for QPDFJob
18 18 * Check about runpath in the linux-bin distribution. I think the
19 19 appimage build specifically is setting the runpath, which is
20 20 actually desirable in this case. Make sure to understand and
... ... @@ -42,21 +42,6 @@ Pending changes:
42 42  
43 43 Soon: Break ground on "Document-level work"
44 44  
45   -Output Capture + QPDFJob
46   -========================
47   -
48   -QPDFJob:
49   -
50   -* Allow users to supply a custom progress reporter for QPDFJob
51   -
52   -Output Capture
53   -
54   -See https://github.com/qpdf/qpdf/issues/691
55   -
56   -There needs to be a C API to QPDFLogger. Use functions like this:
57   -
58   -void set_info((*f)(char* data, unsigned int len, void* udata), void* udata);
59   -
60 45  
61 46 QPDFPagesTree
62 47 =============
... ...
examples/CMakeLists.txt
... ... @@ -14,11 +14,13 @@ set(EXAMPLE_CXX_PROGRAMS
14 14 pdf-parse-content
15 15 pdf-set-form-values
16 16 pdf-split-pages
17   - qpdf-job)
  17 + qpdf-job
  18 + qpdfjob-save-attachment)
18 19 set(EXAMPLE_C_PROGRAMS
19 20 pdf-c-objects
20 21 pdf-linearize
21   - qpdfjob-c)
  22 + qpdfjob-c
  23 + qpdfjob-c-save-attachment)
22 24  
23 25 foreach(PROG ${EXAMPLE_CXX_PROGRAMS})
24 26 add_executable(${PROG} ${PROG}.cc)
... ...
examples/qpdfjob-c-save-attachment.c 0 โ†’ 100644
  1 +#include <qpdf/Constants.h>
  2 +#include <qpdf/qpdfjob-c.h>
  3 +#include <qpdf/qpdflogger-c.h>
  4 +
  5 +#include <stddef.h>
  6 +#include <stdio.h>
  7 +#include <stdlib.h>
  8 +#include <string.h>
  9 +
  10 +// This example demonstrates how we can redirect where saved output
  11 +// goes by calling the default logger's setSave method before running
  12 +// something with QPDFJob. See qpdfjob-c-save-attachment.c for an
  13 +// implementation that uses the C API.
  14 +
  15 +static void
  16 +save_to_file(char const* data, size_t len, void* udata)
  17 +{
  18 + FILE* f = (FILE*)udata;
  19 + fwrite(data, 1, len, f);
  20 +}
  21 +
  22 +static FILE*
  23 +do_fopen(char const* filename)
  24 +{
  25 + FILE* f = NULL;
  26 +#ifdef _MSC_VER
  27 + if (fopen_s(&f, filename, "wb") != 0) {
  28 + f = NULL;
  29 + }
  30 +#else
  31 + f = fopen(filename, "wb");
  32 +#endif
  33 + if (f == NULL) {
  34 + fprintf(stderr, "unable to open %s\n", filename);
  35 + exit(2);
  36 + }
  37 + return f;
  38 +}
  39 +
  40 +int
  41 +main(int argc, char* argv[])
  42 +{
  43 + char const* whoami = "qpdfjob-c-save-attachment";
  44 + char const* filename = NULL;
  45 + char const* key = NULL;
  46 + char const* outfilename = NULL;
  47 + char* attachment_arg = NULL;
  48 + char const* attachment_flag = "--show-attachment=";
  49 + size_t flag_len = 0;
  50 + FILE* outfile = NULL;
  51 + int status = 0;
  52 + qpdfjob_handle j = NULL;
  53 + qpdflogger_handle l = qpdflogger_default_logger();
  54 +
  55 + if (argc != 4) {
  56 + fprintf(stderr, "Usage: %s file attachment-key outfile\n", whoami);
  57 + exit(2);
  58 + }
  59 +
  60 + filename = argv[1];
  61 + key = argv[2];
  62 + outfilename = argv[3];
  63 +
  64 + flag_len = strlen(attachment_flag) + strlen(key) + 1;
  65 + attachment_arg = malloc(flag_len);
  66 +#ifdef _MSC_VER
  67 + strncpy_s(attachment_arg, flag_len, attachment_flag, flag_len);
  68 + strncat_s(attachment_arg, flag_len, key, flag_len - strlen(attachment_arg));
  69 +#else
  70 + strncpy(attachment_arg, attachment_flag, flag_len);
  71 + strncat(attachment_arg, key, flag_len - strlen(attachment_arg));
  72 +#endif
  73 +
  74 + char const* j_argv[5] = {
  75 + whoami,
  76 + filename,
  77 + attachment_arg,
  78 + "--",
  79 + NULL,
  80 + };
  81 + outfile = do_fopen(outfilename);
  82 +
  83 + /* Use qpdflogger_set_save with a callback function to redirect
  84 + * saved data. You can use other qpdf logger functions to capture
  85 + * informational output, warnings, and errors.
  86 + */
  87 + qpdflogger_set_save(
  88 + l, qpdf_log_dest_custom, save_to_file, (void*)outfile, 0);
  89 + qpdflogger_cleanup(&l);
  90 + j = qpdfjob_init();
  91 + status = (qpdfjob_initialize_from_argv(j, j_argv) ||
  92 + qpdfjob_run(j));
  93 + qpdfjob_cleanup(&j);
  94 + free(attachment_arg);
  95 + fclose(outfile);
  96 + if (status == qpdf_exit_success) {
  97 + printf("%s: wrote attachment to %s\n", whoami, outfilename);
  98 + }
  99 + return 0;
  100 +}
... ...
examples/qpdfjob-c.c
... ... @@ -52,6 +52,10 @@ main(int argc, char* argv[])
52 52 * To use that from C just like the argv one, call
53 53 * qpdfjob_run_from_json instead and pass the json string as a
54 54 * single char const* argument.
  55 + *
  56 + * See qpdfjob-c-save-attachment.c for an example of using the
  57 + * full form of the qpdfjob interface with init and cleanup
  58 + * functions.
55 59 */
56 60 r = qpdfjob_run_from_argv(new_argv);
57 61 return r;
... ...
examples/qpdfjob-save-attachment.cc 0 โ†’ 100644
  1 +#include <qpdf/Pl_StdioFile.hh>
  2 +#include <qpdf/QPDFJob.hh>
  3 +#include <qpdf/QPDFLogger.hh>
  4 +#include <qpdf/QUtil.hh>
  5 +
  6 +// This example demonstrates how we can redirect where saved output
  7 +// goes by calling the default logger's setSave method before running
  8 +// something with QPDFJob. See qpdfjob-c-save-attachment.c for an
  9 +// implementation that uses the C API.
  10 +
  11 +int
  12 +main(int argc, char* argv[])
  13 +{
  14 + auto whoami = QUtil::getWhoami(argv[0]);
  15 +
  16 + if (argc != 4) {
  17 + std::cerr << "Usage: " << whoami << " file attachment-key outfile"
  18 + << std::endl;
  19 + exit(2);
  20 + }
  21 +
  22 + char const* filename = argv[1];
  23 + char const* key = argv[2];
  24 + char const* outfilename = argv[3];
  25 + std::string attachment_arg = "--show-attachment=";
  26 + attachment_arg += key;
  27 +
  28 + char const* j_argv[] = {
  29 + whoami,
  30 + filename,
  31 + attachment_arg.c_str(),
  32 + "--",
  33 + nullptr,
  34 + };
  35 +
  36 + QUtil::FileCloser fc(QUtil::safe_fopen(outfilename, "wb"));
  37 + auto save = std::make_shared<Pl_StdioFile>("capture", fc.f);
  38 + QPDFLogger::defaultLogger()->setSave(save, false);
  39 +
  40 + try {
  41 + QPDFJob j;
  42 + j.initializeFromArgv(j_argv);
  43 + j.run();
  44 + } catch (std::exception& e) {
  45 + std::cerr << whoami << ": " << e.what() << std::endl;
  46 + exit(2);
  47 + }
  48 +
  49 + std::cout << whoami << ": wrote attachment to " << outfilename << std::endl;
  50 + return 0;
  51 +}
... ...
examples/qtest/qpdfjob-attachment/attach.pdf 0 โ†’ 100644
No preview for this file type
examples/qtest/save-attachment.test 0 โ†’ 100644
  1 +#!/usr/bin/env perl
  2 +require 5.008;
  3 +use warnings;
  4 +use strict;
  5 +
  6 +chdir("qpdfjob-attachment") or die "chdir testdir failed: $!\n";
  7 +
  8 +require TestDriver;
  9 +
  10 +cleanup();
  11 +
  12 +my $td = new TestDriver('qpdfjob-attachment');
  13 +
  14 +$td->runtest("save attachment (C)",
  15 + {$td->COMMAND => "qpdfjob-c-save-attachment attach.pdf a a"},
  16 + {$td->STRING =>
  17 + "qpdfjob-c-save-attachment: wrote attachment to a\n",
  18 + $td->EXIT_STATUS => 0},
  19 + $td->NORMALIZE_NEWLINES);
  20 +$td->runtest("check output",
  21 + {$td->FILE => "a"},
  22 + {$td->STRING => "quack\n"},
  23 + $td->NORMALIZE_NEWLINES);
  24 +$td->runtest("save attachment (C++)",
  25 + {$td->COMMAND => "qpdfjob-save-attachment attach.pdf a b"},
  26 + {$td->STRING =>
  27 + "qpdfjob-save-attachment: wrote attachment to b\n",
  28 + $td->EXIT_STATUS => 0},
  29 + $td->NORMALIZE_NEWLINES);
  30 +$td->runtest("check output",
  31 + {$td->FILE => "b"},
  32 + {$td->STRING => "quack\n"},
  33 + $td->NORMALIZE_NEWLINES);
  34 +
  35 +cleanup();
  36 +
  37 +$td->report(4);
  38 +
  39 +sub cleanup
  40 +{
  41 + unlink "a", "b";
  42 +}
... ...
include/qpdf/QPDFLogger.hh
... ... @@ -56,6 +56,10 @@ class QPDFLogger
56 56 // error -- standard error
57 57 // save -- undefined unless set
58 58 //
  59 + // "info" is used for diagnostic messages, verbose messages, and
  60 + // progress messages. "warn" is used for warnings. "error" is used
  61 + // for errors. "save" is used for saving output -- see below.
  62 + //
59 63 // On deletion, finish() is called for the standard output and
60 64 // standard error pipelines, which flushes output. If you supply
61 65 // any custom pipelines, you must call finish() on them yourself.
... ... @@ -64,6 +68,13 @@ class QPDFLogger
64 68 //
65 69 // NOTES ABOUT THE SAVE PIPELINE
66 70 //
  71 + // The save pipeline is used by QPDFJob when some kind of binary
  72 + // output is being saved. This includes saving attachments and
  73 + // stream data and also includes when the output file is standard
  74 + // output. If you want to grab that output, you can call setSave.
  75 + // See examples/qpdfjob-save-attachment.cc and
  76 + // examples/qpdfjob-c-save-attachment.c.
  77 + //
67 78 // You should never set the save pipeline to the same destination
68 79 // as something else. Doing so will corrupt your save output. If
69 80 // you want to save to standard output, use the method
... ...
include/qpdf/qpdflogger-c.h
... ... @@ -25,7 +25,8 @@
25 25  
26 26 /*
27 27 * This file provides a C API for QPDFLogger. See QPDFLogger.hh for
28   - * information about the logger.
  28 + * information about the logger and
  29 + * examples/qpdfjob-c-save-attachment.c for an example.
29 30 */
30 31  
31 32 #include <qpdf/DLL.h>
... ...
manual/release-notes.rst
... ... @@ -160,6 +160,9 @@ For a detailed list of changes, please see the file
160 160  
161 161 - A C API is available in :file:`include/qpdf/qpdflogger-c.h`.
162 162  
  163 + - See examples :file:`examples/qpdfjob-save-attachment.cc` and
  164 + :file:`examples/qpdfjob-c-save-attachment.cc`.
  165 +
163 166 - New methods ``insertItemAndGet``, ``appendItemAndGet``,
164 167 ``eraseItemAndGet``, ``replaceKeyAndGet``, and
165 168 ``removeKeyAndGet`` return the newly added or removed object.
... ...