Commit 8e361d98f0bb23d58cbc773367ba76dffced7bdb

Authored by Jay Berkenbilt
1 parent 8130d50e

Add examples for output capture (fixes #691)

@@ -9,12 +9,12 @@ Before Release: @@ -9,12 +9,12 @@ Before Release:
9 * Release qtest with updates to qtest-driver and copy back into qpdf 9 * Release qtest with updates to qtest-driver and copy back into qpdf
10 10
11 Next: 11 Next:
12 -* output capture  
13 * QPDFPagesTree -- avoid ever flattening the pages tree. 12 * QPDFPagesTree -- avoid ever flattening the pages tree.
14 * JSON v2 fixes 13 * JSON v2 fixes
15 14
16 Pending changes: 15 Pending changes:
17 16
  17 +* Allow users to supply a custom progress reporter for QPDFJob
18 * Check about runpath in the linux-bin distribution. I think the 18 * Check about runpath in the linux-bin distribution. I think the
19 appimage build specifically is setting the runpath, which is 19 appimage build specifically is setting the runpath, which is
20 actually desirable in this case. Make sure to understand and 20 actually desirable in this case. Make sure to understand and
@@ -42,21 +42,6 @@ Pending changes: @@ -42,21 +42,6 @@ Pending changes:
42 42
43 Soon: Break ground on "Document-level work" 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 QPDFPagesTree 46 QPDFPagesTree
62 ============= 47 =============
examples/CMakeLists.txt
@@ -14,11 +14,13 @@ set(EXAMPLE_CXX_PROGRAMS @@ -14,11 +14,13 @@ set(EXAMPLE_CXX_PROGRAMS
14 pdf-parse-content 14 pdf-parse-content
15 pdf-set-form-values 15 pdf-set-form-values
16 pdf-split-pages 16 pdf-split-pages
17 - qpdf-job) 17 + qpdf-job
  18 + qpdfjob-save-attachment)
18 set(EXAMPLE_C_PROGRAMS 19 set(EXAMPLE_C_PROGRAMS
19 pdf-c-objects 20 pdf-c-objects
20 pdf-linearize 21 pdf-linearize
21 - qpdfjob-c) 22 + qpdfjob-c
  23 + qpdfjob-c-save-attachment)
22 24
23 foreach(PROG ${EXAMPLE_CXX_PROGRAMS}) 25 foreach(PROG ${EXAMPLE_CXX_PROGRAMS})
24 add_executable(${PROG} ${PROG}.cc) 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,6 +52,10 @@ main(int argc, char* argv[])
52 * To use that from C just like the argv one, call 52 * To use that from C just like the argv one, call
53 * qpdfjob_run_from_json instead and pass the json string as a 53 * qpdfjob_run_from_json instead and pass the json string as a
54 * single char const* argument. 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 r = qpdfjob_run_from_argv(new_argv); 60 r = qpdfjob_run_from_argv(new_argv);
57 return r; 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,6 +56,10 @@ class QPDFLogger
56 // error -- standard error 56 // error -- standard error
57 // save -- undefined unless set 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 // On deletion, finish() is called for the standard output and 63 // On deletion, finish() is called for the standard output and
60 // standard error pipelines, which flushes output. If you supply 64 // standard error pipelines, which flushes output. If you supply
61 // any custom pipelines, you must call finish() on them yourself. 65 // any custom pipelines, you must call finish() on them yourself.
@@ -64,6 +68,13 @@ class QPDFLogger @@ -64,6 +68,13 @@ class QPDFLogger
64 // 68 //
65 // NOTES ABOUT THE SAVE PIPELINE 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 // You should never set the save pipeline to the same destination 78 // You should never set the save pipeline to the same destination
68 // as something else. Doing so will corrupt your save output. If 79 // as something else. Doing so will corrupt your save output. If
69 // you want to save to standard output, use the method 80 // you want to save to standard output, use the method
include/qpdf/qpdflogger-c.h
@@ -25,7 +25,8 @@ @@ -25,7 +25,8 @@
25 25
26 /* 26 /*
27 * This file provides a C API for QPDFLogger. See QPDFLogger.hh for 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 #include <qpdf/DLL.h> 32 #include <qpdf/DLL.h>
manual/release-notes.rst
@@ -160,6 +160,9 @@ For a detailed list of changes, please see the file @@ -160,6 +160,9 @@ For a detailed list of changes, please see the file
160 160
161 - A C API is available in :file:`include/qpdf/qpdflogger-c.h`. 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 - New methods ``insertItemAndGet``, ``appendItemAndGet``, 166 - New methods ``insertItemAndGet``, ``appendItemAndGet``,
164 ``eraseItemAndGet``, ``replaceKeyAndGet``, and 167 ``eraseItemAndGet``, ``replaceKeyAndGet``, and
165 ``removeKeyAndGet`` return the newly added or removed object. 168 ``removeKeyAndGet`` return the newly added or removed object.