Commit 8130d50e3b5aa0235a133c3c5a3018ac01afb5e1

Authored by Jay Berkenbilt
1 parent daef4e8f

Add C API to QPDFLogger

ChangeLog
1 1 2022-06-18 Jay Berkenbilt <ejb@ql.org>
2 2  
  3 + * Add examples that show how to capture QPDFJob's output by
  4 + configuring the default logger (qpdfjob-save-attachment.cc,
  5 + qpdfjob-c-save-attachment.c). Fixes #691.
  6 +
  7 + * Add C API for QPDFLogger -- see qpdflogger-c.h
  8 +
3 9 * Add additional qpdfjob C API functions take a handle.
4 10  
5 11 * Add qpdf_exit_code_e to Constants.h so that exit codes from
... ...
include/qpdf/QPDFLogger.hh
... ... @@ -130,9 +130,9 @@ class QPDFLogger
130 130 void setError(std::shared_ptr<Pipeline>);
131 131 // See notes above about the save pipeline
132 132 QPDF_DLL
133   - void setSave(std::shared_ptr<Pipeline>);
  133 + void setSave(std::shared_ptr<Pipeline>, bool only_if_not_set);
134 134 QPDF_DLL
135   - void saveToStandardOutput();
  135 + void saveToStandardOutput(bool only_if_not_set);
136 136  
137 137 // Shortcut for logic to reset output to new output/error streams.
138 138 // out_stream is used for info, err_stream is used for error, and
... ...
include/qpdf/qpdflogger-c.h 0 → 100644
  1 +/* Copyright (c) 2005-2022 Jay Berkenbilt
  2 + *
  3 + * This file is part of qpdf.
  4 + *
  5 + * Licensed under the Apache License, Version 2.0 (the "License");
  6 + * you may not use this file except in compliance with the License.
  7 + * You may obtain a copy of the License at
  8 + *
  9 + * http://www.apache.org/licenses/LICENSE-2.0
  10 + *
  11 + * Unless required by applicable law or agreed to in writing, software
  12 + * distributed under the License is distributed on an "AS IS" BASIS,
  13 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + * See the License for the specific language governing permissions and
  15 + * limitations under the License.
  16 + *
  17 + * Versions of qpdf prior to version 7 were released under the terms
  18 + * of version 2.0 of the Artistic License. At your option, you may
  19 + * continue to consider qpdf to be licensed under those terms. Please
  20 + * see the manual for additional information.
  21 + */
  22 +
  23 +#ifndef QPDFLOGGER_H
  24 +#define QPDFLOGGER_H
  25 +
  26 +/*
  27 + * This file provides a C API for QPDFLogger. See QPDFLogger.hh for
  28 + * information about the logger.
  29 + */
  30 +
  31 +#include <qpdf/DLL.h>
  32 +#include <stddef.h>
  33 +
  34 +#ifdef __cplusplus
  35 +extern "C" {
  36 +#endif
  37 +
  38 + /* To operate on a logger, you need a handle to it. call
  39 + * qpdflogger_default_logger to get a handle for the default
  40 + * logger. The qpdf and qpdfjob functions may offer ways to get
  41 + * other logger handles. When you're done with the logger handler,
  42 + * call qpdflogger_cleanup. This does not destroy the underlying
  43 + * log object. It just cleans up the handle to it.
  44 + */
  45 +
  46 + typedef struct _qpdflogger_handle* qpdflogger_handle;
  47 + QPDF_DLL
  48 + qpdflogger_handle qpdflogger_default_logger();
  49 +
  50 + QPDF_DLL
  51 + void qpdflogger_cleanup(qpdflogger_handle* l);
  52 +
  53 + enum qpdf_log_dest_e {
  54 + qpdf_log_dest_default = 0,
  55 + qpdf_log_dest_stdout = 1,
  56 + qpdf_log_dest_stderr = 2,
  57 + qpdf_log_dest_discard = 3,
  58 + qpdf_log_dest_custom = 4,
  59 + };
  60 +
  61 + typedef void (*qpdf_log_fn_t)(char const* data, size_t len, void* udata);
  62 +
  63 + QPDF_DLL
  64 + void qpdflogger_set_info(
  65 + qpdflogger_handle l,
  66 + enum qpdf_log_dest_e dest,
  67 + qpdf_log_fn_t fn,
  68 + void* udata);
  69 + QPDF_DLL
  70 + void qpdflogger_set_warn(
  71 + qpdflogger_handle l,
  72 + enum qpdf_log_dest_e dest,
  73 + qpdf_log_fn_t fn,
  74 + void* udata);
  75 + QPDF_DLL
  76 + void qpdflogger_set_error(
  77 + qpdflogger_handle l,
  78 + enum qpdf_log_dest_e dest,
  79 + qpdf_log_fn_t fn,
  80 + void* udata);
  81 +
  82 + /* A non-zero value for only_if_not_set means that the save
  83 + * pipeline will only be changed if it is not already set.
  84 + */
  85 + QPDF_DLL
  86 + void qpdflogger_set_save(
  87 + qpdflogger_handle l,
  88 + enum qpdf_log_dest_e dest,
  89 + qpdf_log_fn_t fn,
  90 + void* udata,
  91 + int only_if_not_set);
  92 + QPDF_DLL
  93 + void qpdflogger_save_to_standard_output(
  94 + qpdflogger_handle l, int only_if_not_set);
  95 +
  96 +#ifdef __cplusplus
  97 +}
  98 +#endif
  99 +
  100 +#endif // QPDFLOGGER_H
... ...
libqpdf/CMakeLists.txt
... ... @@ -110,7 +110,8 @@ set(libqpdf_SOURCES
110 110 SF_FlateLzwDecode.cc
111 111 SparseOHArray.cc
112 112 qpdf-c.cc
113   - qpdfjob-c.cc)
  113 + qpdfjob-c.cc
  114 + qpdflogger-c.cc)
114 115  
115 116 include(FindPkgConfig)
116 117 include(CheckTypeSize)
... ...
libqpdf/QPDFJob.cc
... ... @@ -710,7 +710,7 @@ QPDFJob::checkConfiguration()
710 710 save_to_stdout = true;
711 711 }
712 712 if (save_to_stdout) {
713   - this->m->log->saveToStandardOutput();
  713 + this->m->log->saveToStandardOutput(true);
714 714 }
715 715 if ((!m->split_pages) &&
716 716 QUtil::same_file(m->infilename.get(), m->outfilename.get())) {
... ... @@ -925,7 +925,7 @@ QPDFJob::doShowObj(QPDF&amp; pdf)
925 925 } else {
926 926 // If anything has been written to standard output,
927 927 // this will fail.
928   - this->m->log->saveToStandardOutput();
  928 + this->m->log->saveToStandardOutput(true);
929 929 obj.pipeStreamData(
930 930 this->m->log->getSave().get(),
931 931 (filter && m->normalize) ? qpdf_ef_normalize : 0,
... ... @@ -1031,7 +1031,7 @@ QPDFJob::doShowAttachment(QPDF&amp; pdf)
1031 1031 auto efs = fs->getEmbeddedFileStream();
1032 1032 // saveToStandardOutput has already been called, but it's harmless
1033 1033 // to call it again, so do as defensive coding.
1034   - this->m->log->saveToStandardOutput();
  1034 + this->m->log->saveToStandardOutput(true);
1035 1035 efs.pipeStreamData(this->m->log->getSave().get(), 0, qpdf_dl_all);
1036 1036 }
1037 1037  
... ... @@ -3289,7 +3289,7 @@ QPDFJob::writeOutfile(QPDF&amp; pdf)
3289 3289 } else {
3290 3290 // saveToStandardOutput has already been called, but
3291 3291 // calling it again is defensive and harmless.
3292   - this->m->log->saveToStandardOutput();
  3292 + this->m->log->saveToStandardOutput(true);
3293 3293 w.setOutputPipeline(this->m->log->getSave().get());
3294 3294 }
3295 3295 setWriterOptions(pdf, w);
... ...
libqpdf/QPDFLogger.cc
... ... @@ -181,8 +181,11 @@ QPDFLogger::setError(std::shared_ptr&lt;Pipeline&gt; p)
181 181 }
182 182  
183 183 void
184   -QPDFLogger::setSave(std::shared_ptr<Pipeline> p)
  184 +QPDFLogger::setSave(std::shared_ptr<Pipeline> p, bool only_if_not_set)
185 185 {
  186 + if (only_if_not_set && (this->m->p_save != nullptr)) {
  187 + return;
  188 + }
186 189 if (this->m->p_save == p) {
187 190 return;
188 191 }
... ... @@ -202,9 +205,9 @@ QPDFLogger::setSave(std::shared_ptr&lt;Pipeline&gt; p)
202 205 }
203 206  
204 207 void
205   -QPDFLogger::saveToStandardOutput()
  208 +QPDFLogger::saveToStandardOutput(bool only_if_not_set)
206 209 {
207   - setSave(standardOutput());
  210 + setSave(standardOutput(), only_if_not_set);
208 211 }
209 212  
210 213 void
... ...
libqpdf/qpdflogger-c.cc 0 → 100644
  1 +#include <qpdf/qpdflogger-c.h>
  2 +
  3 +#include <qpdf/Pipeline.hh>
  4 +#include <qpdf/QIntC.hh>
  5 +#include <qpdf/QPDFLogger.hh>
  6 +#include <functional>
  7 +#include <memory>
  8 +
  9 +struct _qpdflogger_handle
  10 +{
  11 + _qpdflogger_handle(std::shared_ptr<QPDFLogger> l);
  12 + ~_qpdflogger_handle() = default;
  13 +
  14 + std::shared_ptr<QPDFLogger> l;
  15 +};
  16 +
  17 +namespace
  18 +{
  19 + class FunctionPipeline: public Pipeline
  20 + {
  21 + public:
  22 + FunctionPipeline(char const* identifier, qpdf_log_fn_t fn, void* udata);
  23 + virtual ~FunctionPipeline() = default;
  24 +
  25 + virtual void write(unsigned char const* buf, size_t len) override;
  26 + virtual void finish() override;
  27 +
  28 + private:
  29 + qpdf_log_fn_t fn;
  30 + void* udata;
  31 + };
  32 +}; // namespace
  33 +
  34 +FunctionPipeline::FunctionPipeline(
  35 + char const* identifier, qpdf_log_fn_t fn, void* udata) :
  36 + Pipeline(identifier, nullptr),
  37 + fn(fn),
  38 + udata(udata)
  39 +{
  40 +}
  41 +
  42 +void
  43 +FunctionPipeline::write(unsigned char const* buf, size_t len)
  44 +{
  45 + fn(reinterpret_cast<char const*>(buf), QIntC::to_ulong(len), udata);
  46 +}
  47 +
  48 +void
  49 +FunctionPipeline::finish()
  50 +{
  51 + // Nothing needed
  52 +}
  53 +
  54 +_qpdflogger_handle::_qpdflogger_handle(std::shared_ptr<QPDFLogger> l) :
  55 + l(l)
  56 +{
  57 +}
  58 +
  59 +qpdflogger_handle
  60 +qpdflogger_default_logger()
  61 +{
  62 + return new _qpdflogger_handle(QPDFLogger::defaultLogger());
  63 +}
  64 +
  65 +void
  66 +qpdflogger_cleanup(qpdflogger_handle* l)
  67 +{
  68 + delete *l;
  69 + *l = nullptr;
  70 +}
  71 +
  72 +static void
  73 +set_log_dest(
  74 + QPDFLogger* l,
  75 + std::function<void(std::shared_ptr<Pipeline>)> method,
  76 + qpdf_log_dest_e dest,
  77 + char const* identifier,
  78 + qpdf_log_fn_t fn,
  79 + void* udata)
  80 +{
  81 + switch (dest) {
  82 + case qpdf_log_dest_default:
  83 + method(nullptr);
  84 + break;
  85 + case qpdf_log_dest_stdout:
  86 + method(l->standardOutput());
  87 + break;
  88 + case qpdf_log_dest_stderr:
  89 + method(l->standardError());
  90 + break;
  91 + case qpdf_log_dest_discard:
  92 + method(l->discard());
  93 + break;
  94 + case qpdf_log_dest_custom:
  95 + method(std::make_shared<FunctionPipeline>(identifier, fn, udata));
  96 + break;
  97 + }
  98 +}
  99 +
  100 +static void
  101 +set_log_dest(
  102 + QPDFLogger* l,
  103 + void (QPDFLogger::*method)(std::shared_ptr<Pipeline>),
  104 + qpdf_log_dest_e dest,
  105 + char const* identifier,
  106 + qpdf_log_fn_t fn,
  107 + void* udata)
  108 +{
  109 + set_log_dest(
  110 + l,
  111 + std::bind(std::mem_fn(method), l, std::placeholders::_1),
  112 + dest,
  113 + identifier,
  114 + fn,
  115 + udata);
  116 +}
  117 +
  118 +void
  119 +qpdflogger_set_info(
  120 + qpdflogger_handle l, qpdf_log_dest_e dest, qpdf_log_fn_t fn, void* udata)
  121 +{
  122 + set_log_dest(
  123 + l->l.get(), &QPDFLogger::setInfo, dest, "info logger", fn, udata);
  124 +}
  125 +
  126 +void
  127 +qpdflogger_set_warn(
  128 + qpdflogger_handle l, qpdf_log_dest_e dest, qpdf_log_fn_t fn, void* udata)
  129 +{
  130 + set_log_dest(
  131 + l->l.get(), &QPDFLogger::setWarn, dest, "warn logger", fn, udata);
  132 +}
  133 +
  134 +void
  135 +qpdflogger_set_error(
  136 + qpdflogger_handle l, qpdf_log_dest_e dest, qpdf_log_fn_t fn, void* udata)
  137 +{
  138 + set_log_dest(
  139 + l->l.get(), &QPDFLogger::setError, dest, "error logger", fn, udata);
  140 +}
  141 +
  142 +void
  143 +qpdflogger_set_save(
  144 + qpdflogger_handle l,
  145 + qpdf_log_dest_e dest,
  146 + qpdf_log_fn_t fn,
  147 + void* udata,
  148 + int only_if_not_set)
  149 +{
  150 + auto method = std::bind(
  151 + std::mem_fn(&QPDFLogger::setSave),
  152 + l->l.get(),
  153 + std::placeholders::_1,
  154 + only_if_not_set);
  155 + set_log_dest(l->l.get(), method, dest, "save logger", fn, udata);
  156 +}
  157 +
  158 +void
  159 +qpdflogger_save_to_standard_output(qpdflogger_handle l, int only_if_not_set)
  160 +{
  161 + qpdflogger_set_save(
  162 + l, qpdf_log_dest_stdout, nullptr, nullptr, only_if_not_set);
  163 +}
... ...
libtests/CMakeLists.txt
... ... @@ -33,10 +33,18 @@ set(TEST_PROGRAMS
33 33 runlength
34 34 sha2
35 35 sparse_array)
  36 +set(TEST_C_PROGRAMS
  37 + logger_c)
  38 +
36 39 foreach(PROG ${TEST_PROGRAMS})
37 40 add_executable(${PROG} ${PROG}.cc)
38 41 target_link_libraries(${PROG} libqpdf_object)
39 42 endforeach()
  43 +foreach(PROG ${TEST_C_PROGRAMS})
  44 + add_executable(${PROG} ${PROG}.c)
  45 + target_link_libraries(${PROG} libqpdf_object)
  46 + set_property(TARGET ${PROG} PROPERTY LINKER_LANGUAGE CXX)
  47 +endforeach()
40 48  
41 49 # Since libtests link with the object library and don't use the DLL,
42 50 # we don't need to (and shouldn't) add the libqpdf target directory to
... ...
libtests/logger.cc
... ... @@ -22,7 +22,7 @@ test1()
22 22 *(logger->getInfo()) << "getSave exception: " << e.what() << "\n";
23 23 }
24 24 try {
25   - logger->saveToStandardOutput();
  25 + logger->saveToStandardOutput(true);
26 26 assert(false);
27 27 } catch (std::logic_error& e) {
28 28 *(logger->getInfo())
... ... @@ -40,12 +40,12 @@ test2()
40 40 // First call saveToStandardOutput. Then use info, which then to
41 41 // go stderr.
42 42 QPDFLogger l;
43   - l.saveToStandardOutput();
  43 + l.saveToStandardOutput(true);
44 44 l.info(std::string("info to stderr\n"));
45 45 *(l.getSave()) << "save to stdout\n";
46 46 l.setInfo(nullptr);
47 47 l.info("info still to stderr\n");
48   - l.setSave(nullptr);
  48 + l.setSave(nullptr, false);
49 49 l.setInfo(nullptr);
50 50 l.info("info back to stdout\n");
51 51 }
... ...
libtests/logger_c.c 0 → 100644
  1 +#include <qpdf/assert_test.h>
  2 +
  3 +#include <qpdf/qpdflogger-c.h>
  4 +
  5 +#include <qpdf/Constants.h>
  6 +#include <qpdf/qpdfjob-c.h>
  7 +
  8 +#include <stdio.h>
  9 +#include <stdlib.h>
  10 +
  11 +static void
  12 +fn(char const* data, size_t len, void* udata)
  13 +{
  14 + FILE* f = (FILE*)udata;
  15 + fwrite(data, 1, len, f);
  16 +}
  17 +
  18 +static void
  19 +do_run(char const* json, int exp_status)
  20 +{
  21 + int status = qpdfjob_run_from_json(json);
  22 + assert(status == exp_status);
  23 +}
  24 +
  25 +static FILE*
  26 +do_fopen(char const* filename)
  27 +{
  28 + FILE* f = NULL;
  29 +#ifdef _MSC_VER
  30 + if (fopen_s(&f, filename, "wb") != 0) {
  31 + f = NULL;
  32 + }
  33 +#else
  34 + f = fopen(filename, "wb");
  35 +#endif
  36 + if (f == NULL) {
  37 + fprintf(stderr, "unable to open %s\n", filename);
  38 + exit(2);
  39 + }
  40 + return f;
  41 +}
  42 +
  43 +int
  44 +main()
  45 +{
  46 + FILE* info = do_fopen("info");
  47 + FILE* warn = do_fopen("warn");
  48 + FILE* error = do_fopen("error");
  49 + FILE* save = do_fopen("save");
  50 + FILE* save2 = do_fopen("save2");
  51 + qpdflogger_handle l = qpdflogger_default_logger();
  52 +
  53 + qpdflogger_set_info(l, qpdf_log_dest_custom, fn, (void*)info);
  54 + qpdflogger_set_warn(l, qpdf_log_dest_custom, fn, (void*)warn);
  55 + qpdflogger_set_error(l, qpdf_log_dest_custom, fn, (void*)error);
  56 + qpdflogger_set_save(l, qpdf_log_dest_custom, fn, (void*)save, 0);
  57 +
  58 + do_run(
  59 + "{\"inputFile\": \"normal.pdf\", \"showNpages\": \"\"}",
  60 + qpdf_exit_success);
  61 + do_run(
  62 + "{\"inputFile\": \"warning.pdf\", \"showNpages\": \"\"}",
  63 + qpdf_exit_warning);
  64 + do_run(
  65 + "{\"inputFile\": \"missing.pdf\", \"showNpages\": \"\"}",
  66 + qpdf_exit_error);
  67 + do_run(
  68 + "{\"inputFile\": \"normal.pdf\","
  69 + " \"staticId\": \"\","
  70 + " \"outputFile\": \"-\"}",
  71 + qpdf_exit_success);
  72 +
  73 + fclose(info);
  74 + fclose(warn);
  75 + fclose(error);
  76 + fclose(save);
  77 +
  78 + qpdflogger_set_info(l, qpdf_log_dest_stderr, NULL, NULL);
  79 + qpdflogger_set_warn(l, qpdf_log_dest_stdout, NULL, NULL);
  80 + qpdflogger_set_error(l, qpdf_log_dest_default, NULL, NULL);
  81 + qpdflogger_set_save(l, qpdf_log_dest_custom, fn, (void*)save2, 0);
  82 +
  83 + do_run(
  84 + "{\"inputFile\": \"2pages.pdf\", \"showNpages\": \"\"}",
  85 + qpdf_exit_success);
  86 + do_run(
  87 + "{\"inputFile\": \"warning.pdf\", \"showNpages\": \"\"}",
  88 + qpdf_exit_warning);
  89 + do_run(
  90 +
  91 + "{\"inputFile\": \"missing.pdf\", \"showNpages\": \"\"}",
  92 + qpdf_exit_error);
  93 + do_run(
  94 + "{\"inputFile\": \"attach.pdf\","
  95 + " \"showAttachment\": \"a\"}",
  96 + qpdf_exit_success);
  97 +
  98 + /* This won't change save since it's already set */
  99 + qpdflogger_save_to_standard_output(l, 1);
  100 + do_run(
  101 + "{\"inputFile\": \"attach.pdf\","
  102 + " \"showAttachment\": \"a\"}",
  103 + qpdf_exit_success);
  104 +
  105 + qpdflogger_cleanup(&l);
  106 +
  107 + return 0;
  108 +}
... ...
libtests/qtest/logger.test
... ... @@ -24,10 +24,22 @@ $td-&gt;runtest(&quot;check stderr&quot;,
24 24 {$td->FILE => "exp-stderr"},
25 25 $td->NORMALIZE_NEWLINES);
26 26  
  27 +$td->runtest("logger C API",
  28 + {$td->COMMAND => "logger_c >stdout 2>stderr"},
  29 + {$td->STRING => "", $td->EXIT_STATUS => 0},
  30 + $td->NORMALIZE_NEWLINES);
  31 +foreach my $f (qw(stdout stderr info warn error save save2))
  32 +{
  33 + $td->runtest("check $f (C)",
  34 + {$td->FILE => "$f"},
  35 + {$td->FILE => "c-exp-$f"},
  36 + $td->NORMALIZE_NEWLINES);
  37 +}
  38 +
27 39 cleanup();
28   -$td->report(3);
  40 +$td->report(11);
29 41  
30 42 sub cleanup
31 43 {
32   - unlink "stdout", "stderr";
  44 + unlink "stdout", "stderr", "info", "warn", "error", "save", "save2";
33 45 }
... ...
libtests/qtest/logger/2pages.pdf 0 → 100644
No preview for this file type
libtests/qtest/logger/attach.pdf 0 → 100644
No preview for this file type
libtests/qtest/logger/c-exp-error 0 → 100644
  1 +qpdfjob json: open missing.pdf: No such file or directory
... ...
libtests/qtest/logger/c-exp-info 0 → 100644
  1 +1
  2 +1
... ...
libtests/qtest/logger/c-exp-save 0 → 100644
No preview for this file type
libtests/qtest/logger/c-exp-save2 0 → 100644
  1 +quack
  2 +quack
... ...
libtests/qtest/logger/c-exp-stderr 0 → 100644
  1 +2
  2 +1
  3 +qpdfjob json: open missing.pdf: No such file or directory
... ...
libtests/qtest/logger/c-exp-stdout 0 → 100644
  1 +WARNING: warning.pdf: file is damaged
  2 +WARNING: warning.pdf (offset 1556): xref not found
  3 +WARNING: warning.pdf: Attempting to reconstruct cross-reference table
  4 +qpdfjob json: operation succeeded with warnings
... ...
libtests/qtest/logger/c-exp-warn 0 → 100644
  1 +WARNING: warning.pdf: file is damaged
  2 +WARNING: warning.pdf (offset 1556): xref not found
  3 +WARNING: warning.pdf: Attempting to reconstruct cross-reference table
  4 +qpdfjob json: operation succeeded with warnings
... ...
libtests/qtest/logger/normal.pdf 0 → 100644
  1 +%PDF-1.3
  2 +1 0 obj
  3 +<<
  4 + /Type /Catalog
  5 + /Pages 2 0 R
  6 +>>
  7 +endobj
  8 +
  9 +2 0 obj
  10 +<<
  11 + /Type /Pages
  12 + /Kids [
  13 + 3 0 R
  14 + ]
  15 + /Count 1
  16 +>>
  17 +endobj
  18 +
  19 +3 0 obj
  20 +<<
  21 + /Type /Page
  22 + /Parent 2 0 R
  23 + /MediaBox [0 0 612 792]
  24 + /Contents 4 0 R
  25 + /Resources <<
  26 + /ProcSet 5 0 R
  27 + /Font <<
  28 + /F1 6 0 R
  29 + >>
  30 + >>
  31 +>>
  32 +endobj
  33 +
  34 +4 0 obj
  35 +<<
  36 + /Length 44
  37 +>>
  38 +stream
  39 +BT
  40 + /F1 24 Tf
  41 + 72 720 Td
  42 + (Potato) Tj
  43 +ET
  44 +endstream
  45 +endobj
  46 +
  47 +5 0 obj
  48 +[
  49 + /PDF
  50 + /Text
  51 +]
  52 +endobj
  53 +
  54 +6 0 obj
  55 +<<
  56 + /Type /Font
  57 + /Subtype /Type1
  58 + /Name /F1
  59 + /BaseFont /Helvetica
  60 + /Encoding /WinAnsiEncoding
  61 +>>
  62 +endobj
  63 +
  64 +xref
  65 +0 7
  66 +0000000000 65535 f
  67 +0000000009 00000 n
  68 +0000000063 00000 n
  69 +0000000135 00000 n
  70 +0000000307 00000 n
  71 +0000000403 00000 n
  72 +0000000438 00000 n
  73 +trailer <<
  74 + /Size 7
  75 + /Root 1 0 R
  76 +>>
  77 +startxref
  78 +556
  79 +%%EOF
... ...
libtests/qtest/logger/warning.pdf 0 → 100644
  1 +%PDF-1.3
  2 +1 0 obj
  3 +<<
  4 + /Type /Catalog
  5 + /Pages 2 0 R
  6 +>>
  7 +endobj
  8 +
  9 +2 0 obj
  10 +<<
  11 + /Type /Pages
  12 + /Kids [
  13 + 3 0 R
  14 + ]
  15 + /Count 1
  16 +>>
  17 +endobj
  18 +
  19 +3 0 obj
  20 +<<
  21 + /Type /Page
  22 + /Parent 2 0 R
  23 + /MediaBox [0 0 612 792]
  24 + /Contents 4 0 R
  25 + /Resources <<
  26 + /ProcSet 5 0 R
  27 + /Font <<
  28 + /F1 6 0 R
  29 + >>
  30 + >>
  31 +>>
  32 +endobj
  33 +
  34 +4 0 obj
  35 +<<
  36 + /Length 44
  37 +>>
  38 +stream
  39 +BT
  40 + /F1 24 Tf
  41 + 72 720 Td
  42 + (Potato) Tj
  43 +ET
  44 +endstream
  45 +endobj
  46 +
  47 +5 0 obj
  48 +[
  49 + /PDF
  50 + /Text
  51 +]
  52 +endobj
  53 +
  54 +6 0 obj
  55 +<<
  56 + /Type /Font
  57 + /Subtype /Type1
  58 + /Name /F1
  59 + /BaseFont /Helvetica
  60 + /Encoding /WinAnsiEncoding
  61 +>>
  62 +endobj
  63 +
  64 +xref
  65 +0 7
  66 +0000000000 65535 f
  67 +0000000009 00000 n
  68 +0000000063 00000 n
  69 +0000000135 00000 n
  70 +0000000307 00000 n
  71 +0000000403 00000 n
  72 +0000000438 00000 n
  73 +trailer <<
  74 + /Size 7
  75 + /Root 1 0 R
  76 +>>
  77 +startxref
  78 +1556
  79 +%%EOF
... ...
manual/release-notes.rst
... ... @@ -158,6 +158,8 @@ For a detailed list of changes, please see the file
158 158 output and errors that slipped through the cracks with
159 159 ``setOutputStreams``.
160 160  
  161 + - A C API is available in :file:`include/qpdf/qpdflogger-c.h`.
  162 +
161 163 - New methods ``insertItemAndGet``, ``appendItemAndGet``,
162 164 ``eraseItemAndGet``, ``replaceKeyAndGet``, and
163 165 ``removeKeyAndGet`` return the newly added or removed object.
... ...