diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/pdf-attach-file.cc | 22 | ||||
-rw-r--r-- | examples/pdf-bookmarks.cc | 15 | ||||
-rw-r--r-- | examples/pdf-c-objects.c | 11 | ||||
-rw-r--r-- | examples/pdf-count-strings.cc | 20 | ||||
-rw-r--r-- | examples/pdf-create.cc | 59 | ||||
-rw-r--r-- | examples/pdf-custom-filter.cc | 327 | ||||
-rw-r--r-- | examples/pdf-double-page-size.cc | 10 | ||||
-rw-r--r-- | examples/pdf-filter-tokens.cc | 74 | ||||
-rw-r--r-- | examples/pdf-invert-images.cc | 65 | ||||
-rw-r--r-- | examples/pdf-linearize.c | 3 | ||||
-rw-r--r-- | examples/pdf-name-number-tree.cc | 53 | ||||
-rw-r--r-- | examples/pdf-overlay-page.cc | 22 | ||||
-rw-r--r-- | examples/pdf-parse-content.cc | 4 | ||||
-rw-r--r-- | examples/pdf-set-form-values.cc | 42 | ||||
-rw-r--r-- | examples/pdf-split-pages.cc | 8 | ||||
-rw-r--r-- | examples/qpdf-job.cc | 10 | ||||
-rw-r--r-- | examples/qpdfjob-c-save-attachment.c | 12 | ||||
-rw-r--r-- | examples/qpdfjob-c.c | 17 | ||||
-rw-r--r-- | examples/qpdfjob-remove-annotations.cc | 7 | ||||
-rw-r--r-- | examples/qpdfjob-save-attachment.cc | 7 |
20 files changed, 324 insertions, 464 deletions
diff --git a/examples/pdf-attach-file.cc b/examples/pdf-attach-file.cc index b5a1c64a..9ae3b247 100644 --- a/examples/pdf-attach-file.cc +++ b/examples/pdf-attach-file.cc @@ -8,11 +8,10 @@ #include <iostream> // -// This example attaches a file to an input file, adds a page to the -// beginning of the file that includes a file attachment annotation, -// and writes the result to an output file. It also illustrates a -// number of new API calls that were added in qpdf 10.2 as well as -// the use of the qpdf literal syntax introduced in qpdf 10.6. +// This example attaches a file to an input file, adds a page to the beginning of the file that +// includes a file attachment annotation, and writes the result to an output file. It also +// illustrates a number of new API calls that were added in qpdf 10.2 as well as the use of the qpdf +// literal syntax introduced in qpdf 10.6. // static char const* whoami = nullptr; @@ -43,8 +42,8 @@ process( QPDF q; q.processFile(infilename, password); - // Create an indirect object for the built-in Helvetica font. This - // uses the qpdf literal syntax introduced in qpdf 10.6. + // Create an indirect object for the built-in Helvetica font. This uses the qpdf literal syntax + // introduced in qpdf 10.6. auto f1 = q.makeIndirectObject( // force line-break "<<" @@ -55,9 +54,8 @@ process( " /Encoding /WinAnsiEncoding" ">>"_qpdf); - // Create a resources dictionary with fonts. This uses the new - // parse introduced in qpdf 10.2 that takes a QPDF* and allows - // indirect object references. + // Create a resources dictionary with fonts. This uses the new parse introduced in qpdf 10.2 + // that takes a QPDF* and allows indirect object references. auto resources = q.makeIndirectObject( // line-break QPDFObjectHandle::parse( @@ -97,8 +95,8 @@ process( "S\n"); auto apdict = ap.getDict(); - // The following four lines demonstrate the use of the qpdf literal syntax - // introduced in qpdf 10.6. They could have been written as: + // The following four lines demonstrate the use of the qpdf literal syntax introduced in + // qpdf 10.6. They could have been written as: // apdict.replaceKey("/Resources", QPDFObjectHandle::newDictionary()); // apdict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject")); // apdict.replaceKey("/Subtype", QPDFObjectHandle::newName("/Form")); diff --git a/examples/pdf-bookmarks.cc b/examples/pdf-bookmarks.cc index d40ded6a..9581a135 100644 --- a/examples/pdf-bookmarks.cc +++ b/examples/pdf-bookmarks.cc @@ -8,9 +8,9 @@ #include <cstring> #include <iostream> -// This program demonstrates extraction of bookmarks using the qpdf -// outlines API. Note that all the information shown by this program -// can also be obtained from a PDF file using qpdf's --json option. +// This program demonstrates extraction of bookmarks using the qpdf outlines API. Note that all the +// information shown by this program can also be obtained from a PDF file using qpdf's --json +// option. // // Ignore calls to QTC::TC - they are for qpdf CI testing only. @@ -118,11 +118,10 @@ show_bookmark_details(QPDFOutlineObjectHelper outline, std::vector<int> numbers) void extract_bookmarks(std::vector<QPDFOutlineObjectHelper> outlines, std::vector<int>& numbers) { - // For style == st_numbers, numbers.at(n) contains the numerical - // label for the outline, so we count up from 1. - // For style == st_lines, numbers.at(n) == 0 indicates the last - // outline at level n, and we don't otherwise care what the value - // is, so we count up to zero. + // For style == st_numbers, numbers.at(n) contains the numerical label for the outline, so we + // count up from 1. + // For style == st_lines, numbers.at(n) == 0 indicates the last outline at level n, and we don't + // otherwise care what the value is, so we count up to zero. numbers.push_back((style == st_lines) ? -QIntC::to_int(outlines.size()) : 0); for (auto& outline: outlines) { ++(numbers.back()); diff --git a/examples/pdf-c-objects.c b/examples/pdf-c-objects.c index f4b872a7..171a9b66 100644 --- a/examples/pdf-c-objects.c +++ b/examples/pdf-c-objects.c @@ -1,7 +1,4 @@ -/* - * This is an example program to demonstrate use of object handle - * functions in the C API. - */ +/* This is an example program to demonstrate use of object handle functions in the C API. */ #include <qpdf/qpdf-c.h> #include <stdio.h> @@ -20,8 +17,7 @@ usage() QPDF_BOOL modify_file(qpdf_data qpdf) { - /* This small example performs the following operation on the - * document catalog (a.k.a. root): + /* This small example performs the following operation on the document catalog (a.k.a. root): * - Remove PageLayout * - Remove OpenAction * - If there are outlines, set PageMode to UseOutlines; otherwise, @@ -72,8 +68,7 @@ main(int argc, char* argv[]) if (((qpdf_read(qpdf, infile, password) & QPDF_ERRORS) == 0) && modify_file(qpdf) && ((qpdf_init_write(qpdf, outfile) & QPDF_ERRORS) == 0)) { - /* Use static ID for testing only. For production, a - * non-static ID is used. See also + /* Use static ID for testing only. For production, a non-static ID is used. See also * qpdf_set_deterministic_ID. */ qpdf_set_static_ID(qpdf, QPDF_TRUE); /* for testing only */ qpdf_write(qpdf); diff --git a/examples/pdf-count-strings.cc b/examples/pdf-count-strings.cc index c70e183e..2061a499 100644 --- a/examples/pdf-count-strings.cc +++ b/examples/pdf-count-strings.cc @@ -1,7 +1,7 @@ // -// This example illustrates the use of QPDFObjectHandle::TokenFilter -// with filterContents. See also pdf-filter-tokens.cc for an example -// that uses QPDFObjectHandle::TokenFilter with addContentTokenFilter. +// This example illustrates the use of QPDFObjectHandle::TokenFilter with filterContents. See also +// pdf-filter-tokens.cc for an example that uses QPDFObjectHandle::TokenFilter with +// addContentTokenFilter. // #include <cstdlib> @@ -46,16 +46,15 @@ StringCounter::handleToken(QPDFTokenizer::Token const& token) if (token.getType() == QPDFTokenizer::tt_string) { ++this->count; } - // Preserve input verbatim by passing each token to any specified - // downstream filter. + // Preserve input verbatim by passing each token to any specified downstream filter. writeToken(token); } void StringCounter::handleEOF() { - // Write a comment at the end of the stream just to show how we - // can enhance the output if we want. + // Write a comment at the end of the stream just to show how we can enhance the output if we + // want. write("\n% strings found: "); write(std::to_string(this->count)); } @@ -82,10 +81,9 @@ main(int argc, char* argv[]) int pageno = 0; for (auto& page: QPDFPageDocumentHelper(pdf).getAllPages()) { ++pageno; - // Pass the contents of a page through our string counter. - // If it's an even page, capture the output. This - // illustrates that you may capture any output generated - // by the filter, or you may ignore it. + // Pass the contents of a page through our string counter. If it's an even page, capture + // the output. This illustrates that you may capture any output generated by the filter, + // or you may ignore it. StringCounter counter; if (pageno % 2) { // Ignore output for odd pages. diff --git a/examples/pdf-create.cc b/examples/pdf-create.cc index e0fa93b1..c793b5f8 100644 --- a/examples/pdf-create.cc +++ b/examples/pdf-create.cc @@ -1,7 +1,6 @@ // -// This is an example of creating a PDF file from scratch. It -// illustrates use of several QPDF operations for creating objects and -// streams. It also serves as an illustration of how to use +// This is an example of creating a PDF file from scratch. It illustrates use of several QPDF +// operations for creating objects and streams. It also serves as an illustration of how to use // StreamDataProvider with different types of filters. // @@ -20,8 +19,8 @@ static char const* whoami = nullptr; -// This is a simple StreamDataProvider that writes image data for an -// orange square of the given width and height. +// This is a simple StreamDataProvider that writes image data for an orange square of the given +// width and height. class ImageProvider: public QPDFObjectHandle::StreamDataProvider { public: @@ -130,8 +129,7 @@ usage() static QPDFObjectHandle createPageContents(QPDF& pdf, std::string const& text) { - // Create a stream that displays our image and the given text in - // our font. + // Create a stream that displays our image and the given text in our font. std::string contents = "BT /F1 24 Tf 72 320 Td (" + text + ") Tj ET\n" "q 244 0 0 144 184 100 cm /Im1 Do Q\n"; @@ -159,11 +157,9 @@ add_page( { QPDF& pdf(dh.getQPDF()); - // Create a stream to encode our image. QPDFWriter will fill in - // the length and will respect our filters based on stream data - // mode. Since we are not specifying, QPDFWriter will compress - // with /FlateDecode if we don't provide any other form of - // compression. + // Create a stream to encode our image. QPDFWriter will fill in the length and will respect our + // filters based on stream data mode. Since we are not specifying, QPDFWriter will compress with + // /FlateDecode if we don't provide any other form of compression. auto* p = new ImageProvider(color_space, filter); std::shared_ptr<QPDFObjectHandle::StreamDataProvider> provider(p); size_t width = p->getWidth(); @@ -219,20 +215,17 @@ check( std::vector<std::string> const& color_spaces, std::vector<std::string> const& filters) { - // Each stream is compressed the way it is supposed to be. We will - // add additional tests in qpdf.test to exercise QPDFWriter more - // fully. In this case, we want to make sure that we actually have - // RunLengthDecode and DCTDecode where we are supposed to and - // FlateDecode where we provided no filters. + // Each stream is compressed the way it is supposed to be. We will add additional tests in + // qpdf.test to exercise QPDFWriter more fully. In this case, we want to make sure that we + // actually have RunLengthDecode and DCTDecode where we are supposed to and FlateDecode where we + // provided no filters. - // Each image is correct. For non-lossy image compression, the - // uncompressed image data should exactly match what ImageProvider - // provided. For the DCTDecode data, allow for some fuzz to handle - // jpeg compression as well as its variance on different systems. + // Each image is correct. For non-lossy image compression, the uncompressed image data should + // exactly match what ImageProvider provided. For the DCTDecode data, allow for some fuzz to + // handle jpeg compression as well as its variance on different systems. - // These tests should use QPDFObjectHandle's stream data retrieval - // methods, but don't try to fully exercise them here. That is - // done elsewhere. + // These tests should use QPDFObjectHandle's stream data retrieval methods, but don't try to + // fully exercise them here. That is done elsewhere. size_t n_color_spaces = color_spaces.size(); size_t n_filters = filters.size(); @@ -254,8 +247,8 @@ check( // Check filter and color space. std::string desired_color_space = color_spaces[(pageno - 1) / n_color_spaces]; std::string desired_filter = filters[(pageno - 1) % n_filters]; - // In the default mode, QPDFWriter will compress with - // /FlateDecode if no filters are provided. + // In the default mode, QPDFWriter will compress with /FlateDecode if no filters are + // provided. if (desired_filter == "null") { desired_filter = "/FlateDecode"; } @@ -288,11 +281,9 @@ check( std::cout << "page " << pageno << ": image data length mismatch" << std::endl; this_errors = errors = true; } else { - // Compare bytes. For JPEG, allow a certain number of - // the bytes to be off desired by more than a given - // tolerance. Any of the samples may be a little off - // because of lossy compression, and around sharp - // edges, things can be quite off. For non-lossy + // Compare bytes. For JPEG, allow a certain number of the bytes to be off desired by + // more than a given tolerance. Any of the samples may be a little off because of + // lossy compression, and around sharp edges, things can be quite off. For non-lossy // compression, do not allow any tolerance. unsigned char const* actual_bytes = actual_data->getBuffer(); unsigned char const* desired_bytes = desired_data->getBuffer(); @@ -332,8 +323,7 @@ create_pdf(char const* filename) // Start with an empty PDF that has no pages or non-required objects. pdf.emptyPDF(); - // Add an indirect object to contain a font descriptor for the - // built-in Helvetica font. + // Add an indirect object to contain a font descriptor for the built-in Helvetica font. QPDFObjectHandle font = pdf.makeIndirectObject( // line-break "<<" @@ -362,8 +352,7 @@ create_pdf(char const* filename) QPDFWriter w(pdf, filename); w.write(); - // For test suite, verify that everything is the way it is - // supposed to be. + // For test suite, verify that everything is the way it is supposed to be. check(filename, color_spaces, filters); } diff --git a/examples/pdf-custom-filter.cc b/examples/pdf-custom-filter.cc index 57eedf74..e3cdf164 100644 --- a/examples/pdf-custom-filter.cc +++ b/examples/pdf-custom-filter.cc @@ -8,43 +8,35 @@ #include <iostream> #include <memory> -// This example shows you everything you need to know to implement a -// custom stream filter for encoding and decoding as well as a stream -// data provider that modifies the stream's dictionary. This example -// uses the pattern of having the stream data provider class use a -// second QPDF instance with copies of streams from the original QPDF -// so that the stream data provider can access the original stream -// data. This is implemented very efficiently inside the qpdf library as -// the second QPDF instance knows how to read the stream data from the -// original input file, so no extra copies of the original stream data -// are made. - -// This example creates an imaginary filter called /XORDecode. There -// is no such filter in PDF, so the streams created by the example -// would not be usable by any PDF reader. However, the techniques here -// would work if you were going to implement support for a filter that -// qpdf does not support natively. For example, using the techniques -// shown here, it would be possible to create an application that -// downsampled or re-encoded images or that re-compressed streams -// using a more efficient "deflate" implementation than zlib. - -// Comments appear throughout the code describing each piece of code -// and its purpose. You can read the file top to bottom, or you can -// start with main() and follow the flow. - -// Please also see the test suite, qtest/custom-filter.test, which -// contains additional comments describing how to observe the results -// of running this example on test files that are specifically crafted -// for it. +// This example shows you everything you need to know to implement a custom stream filter for +// encoding and decoding as well as a stream data provider that modifies the stream's dictionary. +// This example uses the pattern of having the stream data provider class use a second QPDF instance +// with copies of streams from the original QPDF so that the stream data provider can access the +// original stream data. This is implemented very efficiently inside the qpdf library as the second +// QPDF instance knows how to read the stream data from the original input file, so no extra copies +// of the original stream data are made. + +// This example creates an imaginary filter called /XORDecode. There is no such filter in PDF, so +// the streams created by the example would not be usable by any PDF reader. However, the techniques +// here would work if you were going to implement support for a filter that qpdf does not support +// natively. For example, using the techniques shown here, it would be possible to create an +// application that downsampled or re-encoded images or that re-compressed streams using a more +// efficient "deflate" implementation than zlib. + +// Comments appear throughout the code describing each piece of code and its purpose. You can read +// the file top to bottom, or you can start with main() and follow the flow. + +// Please also see the test suite, qtest/custom-filter.test, which contains additional comments +// describing how to observe the results of running this example on test files that are specifically +// crafted for it. static char const* whoami = nullptr; class Pl_XOR: public Pipeline { - // This class implements a Pipeline for the made-up XOR decoder. - // It is initialized with a single-byte "key" and just XORs each - // byte with that key. This makes it reversible, so there is no - // distinction between encoding and decoding. + // This class implements a Pipeline for the made-up XOR decoder. It is initialized with a + // single-byte "key" and just XORs each byte with that key. This makes it reversible, so there + // is no distinction between encoding and decoding. public: Pl_XOR(char const* identifier, Pipeline* next, unsigned char key); @@ -79,17 +71,14 @@ Pl_XOR::finish() class SF_XORDecode: public QPDFStreamFilter { - // This class implements a QPDFStreamFilter that knows how to - // validate and interpret decode parameters (/DecodeParms) for the - // made-up /XORDecode stream filter. Since this is not a real - // stream filter, no actual PDF reader would know how to interpret - // it. This is just to illustrate how to create a stream filter. - // In main(), we call QPDF::registerStreamFilter to tell the - // library about the filter. See comments in QPDFStreamFilter.hh - // for details on how to implement the methods. For purposes of - // example, we are calling this a "specialized" compression - // filter, which just means QPDF assumes that it should not - // "uncompress" the stream by default. + // This class implements a QPDFStreamFilter that knows how to validate and interpret decode + // parameters (/DecodeParms) for the made-up /XORDecode stream filter. Since this is not a real + // stream filter, no actual PDF reader would know how to interpret it. This is just to + // illustrate how to create a stream filter. In main(), we call QPDF::registerStreamFilter to + // tell the library about the filter. See comments in QPDFStreamFilter.hh for details on how to + // implement the methods. For purposes of example, we are calling this a "specialized" + // compression filter, which just means QPDF assumes that it should not "uncompress" the stream + // by default. public: ~SF_XORDecode() override = default; bool setDecodeParms(QPDFObjectHandle decode_parms) override; @@ -98,33 +87,28 @@ class SF_XORDecode: public QPDFStreamFilter private: unsigned char key; - // It is the responsibility of the QPDFStreamFilter implementation - // to ensure that the pipeline returned by getDecodePipeline() is - // deleted when the class is deleted. The easiest way to do this - // is to stash the pipeline in a std::shared_ptr, which enables us - // to use the default destructor implementation. + // It is the responsibility of the QPDFStreamFilter implementation to ensure that the pipeline + // returned by getDecodePipeline() is deleted when the class is deleted. The easiest way to do + // this is to stash the pipeline in a std::shared_ptr, which enables us to use the default + // destructor implementation. std::shared_ptr<Pl_XOR> pipeline; }; bool SF_XORDecode::setDecodeParms(QPDFObjectHandle decode_parms) { - // For purposes of example, we store the key in a separate stream. - // We could just as well store the key directly in /DecodeParms, - // but this example uses a stream to illustrate how one might do - // that. For example, if implementing /JBIG2Decode, one would need - // to handle the /JBIG2Globals key, which points to a stream. See - // comments in SF_XORDecode::registerStream for additional notes - // on this. + // For purposes of example, we store the key in a separate stream. We could just as well store + // the key directly in /DecodeParms, but this example uses a stream to illustrate how one might + // do that. For example, if implementing /JBIG2Decode, one would need to handle the + // /JBIG2Globals key, which points to a stream. See comments in SF_XORDecode::registerStream for + // additional notes on this. try { - // Expect /DecodeParms to be a dictionary with a /KeyStream - // key that points to a one-byte stream whose single byte is - // the key. If we are successful at retrieving the key, return - // true, indicating that we are able to process with the given - // decode parameters. Under any other circumstances, return - // false. For other examples of QPDFStreamFilter - // implementations, look at the classes whose names start with - // SF_ in the qpdf library implementation. + // Expect /DecodeParms to be a dictionary with a /KeyStream key that points to a one-byte + // stream whose single byte is the key. If we are successful at retrieving the key, return + // true, indicating that we are able to process with the given decode parameters. Under any + // other circumstances, return false. For other examples of QPDFStreamFilter + // implementations, look at the classes whose names start with SF_ in the qpdf library + // implementation. auto buf = decode_parms.getKey("/KeyStream").getStreamData(); if (buf->getSize() != 1) { return false; @@ -140,14 +124,12 @@ SF_XORDecode::setDecodeParms(QPDFObjectHandle decode_parms) Pipeline* SF_XORDecode::getDecodePipeline(Pipeline* next) { - // Return a pipeline that the qpdf library should pass the stream - // data through. The pipeline should receive encoded data and pass - // decoded data to "next". getDecodePipeline() can always count on - // setDecodeParms() having been called first. The setDecodeParms() - // method should store any parameters needed by the pipeline. To - // ensure that the pipeline we return disappears when the class - // disappears, stash it in a std::shared_ptr<Pl_XOR> and retrieve - // the raw pointer from there. + // Return a pipeline that the qpdf library should pass the stream data through. The pipeline + // should receive encoded data and pass decoded data to "next". getDecodePipeline() can always + // count on setDecodeParms() having been called first. The setDecodeParms() method should store + // any parameters needed by the pipeline. To ensure that the pipeline we return disappears when + // the class disappears, stash it in a std::shared_ptr<Pl_XOR> and retrieve the raw pointer from + // there. this->pipeline = std::make_shared<Pl_XOR>("xor", next, this->key); return this->pipeline.get(); } @@ -155,46 +137,37 @@ SF_XORDecode::getDecodePipeline(Pipeline* next) bool SF_XORDecode::isSpecializedCompression() { - // The default implementation of QPDFStreamFilter would return - // false, so if you want a specialized or lossy compression - // filter, override one of the methods as described in + // The default implementation of QPDFStreamFilter would return false, so if you want a + // specialized or lossy compression filter, override one of the methods as described in // QPDFStreamFilter.hh. return true; } class StreamReplacer: public QPDFObjectHandle::StreamDataProvider { - // This class implements a StreamDataProvider that, under specific - // conditions, replaces the stream data with data encoded with the - // made-up /XORDecode filter. + // This class implements a StreamDataProvider that, under specific conditions, replaces the + // stream data with data encoded with the made-up /XORDecode filter. // The flow for this class is as follows: // - // * The main application iterates through streams that should be - // replaced and calls registerStream. registerStream in turn - // calls maybeReplace passing nullptr to pipeline and the - // address of a valid QPDFObjectHandle to dict_updates. The - // stream passed in for this call is the stream for the original - // QPDF object. It has not yet been altered, so we have access - // to its original dictionary and data. As described in the - // method, the method when called in this way makes a - // determination as to whether the stream should be replaced. If - // so, registerStream makes whatever changes are required. We - // have to do this now because we can't modify the stream during - // the writing process. + // * The main application iterates through streams that should be replaced and calls + // registerStream. registerStream in turn calls maybeReplace passing nullptr to pipeline and + // the address of a valid QPDFObjectHandle to dict_updates. The stream passed in for this call + // is the stream for the original QPDF object. It has not yet been altered, so we have access + // to its original dictionary and data. As described in the method, the method when called in + // this way makes a determination as to whether the stream should be replaced. If so, + // registerStream makes whatever changes are required. We have to do this now because we can't + // modify the stream during the writing process. // - // * provideStreamData(), which is called by QPDFWriter during the - // write process, actually writes the modified stream data. It - // calls maybeReplace again, but this time it passes a valid - // pipeline and passes nullptr to dict_updates. In this mode, - // the stream dictionary has already been altered, and the - // original stream data is no longer directly accessible. Trying - // to retrieve the stream data would cause an infinite loop because - // it would just end up calling provideStreamData again. This is - // why maybeReplace uses a stashed copy of the original stream. - - // Additional explanation can be found in the method - // implementations. + // * provideStreamData(), which is called by QPDFWriter during the write process, actually + // writes the modified stream data. It calls maybeReplace again, but this time it passes a + // valid pipeline and passes nullptr to dict_updates. In this mode, the stream dictionary has + // already been altered, and the original stream data is no longer directly accessible. Trying + // to retrieve the stream data would cause an infinite loop because it would just end up + // calling provideStreamData again. This is why maybeReplace uses a stashed copy of the + // original stream. + + // Additional explanation can be found in the method implementations. public: StreamReplacer(QPDF* pdf); @@ -211,17 +184,16 @@ class StreamReplacer: public QPDFObjectHandle::StreamDataProvider Pipeline* pipeline, QPDFObjectHandle* dict_updates); - // Hang onto a reference to the QPDF object containing the streams - // we are replacing. We need this to create a new stream. + // Hang onto a reference to the QPDF object containing the streams we are replacing. We need + // this to create a new stream. QPDF* pdf; - // Map the object/generation in original file to the copied stream - // in "other". We use this to retrieve the original data. + // Map the object/generation in original file to the copied stream in "other". We use this to + // retrieve the original data. std::map<QPDFObjGen, QPDFObjectHandle> copied_streams; - // Each stream gets is own "key" for the XOR filter. We use a - // single instance of StreamReplacer for all streams, so stash all - // the keys here. + // Each stream gets is own "key" for the XOR filter. We use a single instance of StreamReplacer + // for all streams, so stash all the keys here. std::map<QPDFObjGen, unsigned char> keys; }; @@ -237,49 +209,38 @@ StreamReplacer::maybeReplace( Pipeline* pipeline, QPDFObjectHandle* dict_updates) { - // As described in the class comments, this method is called - // twice. Before writing has started pipeline is nullptr, and - // dict_updates is provided. In this mode, we figure out whether - // we should replace the stream and, if so, take care of the - // necessary setup. When we are actually ready to supply the data, - // this method is called again with pipeline populated and - // dict_updates as a nullptr. In this mode, we are not allowed to - // change anything, since writing is already in progress. We - // must simply provide the stream data. - - // The return value indicates whether or not we should replace the - // stream. If the first call returns false, there will be no - // second call. If the second call returns false, something went - // wrong since the method should always make the same decision for - // a given stream. - - // For this example, all the determination logic could have - // appeared inside the if (dict_updates) block rather than being - // duplicated, but in some cases, there may be a reason to - // duplicate things. For example, if you wanted to write code that - // re-encoded an image if the new encoding was more efficient, - // you'd have to actually try it out. Then you would either have - // to cache the result somewhere or just repeat the calculations, - // depending on space/time constraints, etc. - - // In our contrived example, we are replacing the data for all - // streams that have /DoXOR = true in the stream dictionary. If - // this were a more realistic application, our criteria would be - // more sensible. For example, an image downsampler might choose - // to replace a stream that represented an image with a high pixel - // density. + // As described in the class comments, this method is called twice. Before writing has started + // pipeline is nullptr, and dict_updates is provided. In this mode, we figure out whether we + // should replace the stream and, if so, take care of the necessary setup. When we are actually + // ready to supply the data, this method is called again with pipeline populated and + // dict_updates as a nullptr. In this mode, we are not allowed to change anything, since writing + // is already in progress. We must simply provide the stream data. + + // The return value indicates whether or not we should replace the stream. If the first call + // returns false, there will be no second call. If the second call returns false, something went + // wrong since the method should always make the same decision for a given stream. + + // For this example, all the determination logic could have appeared inside the if + // (dict_updates) block rather than being duplicated, but in some cases, there may be a reason + // to duplicate things. For example, if you wanted to write code that re-encoded an image if the + // new encoding was more efficient, you'd have to actually try it out. Then you would either + // have to cache the result somewhere or just repeat the calculations, depending on space/time + // constraints, etc. + + // In our contrived example, we are replacing the data for all streams that have /DoXOR = true + // in the stream dictionary. If this were a more realistic application, our criteria would be + // more sensible. For example, an image downsampler might choose to replace a stream that + // represented an image with a high pixel density. auto dict = stream.getDict(); auto mark = dict.getKey("/DoXOR"); if (!(mark.isBool() && mark.getBoolValue())) { return false; } - // We can't replace the stream data if we can't get the original - // stream data for any reason. A more realistic application may - // actually look at the data here as well, or it may be able to - // make all its decisions from the stream dictionary. However, - // it's a good idea to make sure we can retrieve the filtered data - // if we are going to need it later. + // We can't replace the stream data if we can't get the original stream data for any reason. A + // more realistic application may actually look at the data here as well, or it may be able to + // make all its decisions from the stream dictionary. However, it's a good idea to make sure we + // can retrieve the filtered data if we are going to need it later. std::shared_ptr<Buffer> out; try { out = stream.getStreamData(); @@ -288,19 +249,15 @@ StreamReplacer::maybeReplace( } if (dict_updates) { - // It's not safe to make any modifications to any objects - // during the writing process since the updated objects may - // have already been written. In this mode, when dict_updates - // is provided, we have not started writing. Store the - // modifications we intend to make to the stream dictionary - // here. We're just storing /OrigLength for purposes of - // example. Again, a realistic application would make other - // changes. For example, an image resampler might change the - // dimensions or other properties of the image. + // It's not safe to make any modifications to any objects during the writing process since + // the updated objects may have already been written. In this mode, when dict_updates is + // provided, we have not started writing. Store the modifications we intend to make to the + // stream dictionary here. We're just storing /OrigLength for purposes of example. Again, a + // realistic application would make other changes. For example, an image resampler might + // change the dimensions or other properties of the image. dict_updates->replaceKey( "/OrigLength", QPDFObjectHandle::newInteger(QIntC::to_longlong(out->getSize()))); - // We are also storing the "key" that we will access when - // writing the data. + // We are also storing the "key" that we will access when writing the data. this->keys[og] = QIntC::to_uchar((og.getObj() * QIntC::to_int(out->getSize())) & 0xff); } @@ -319,21 +276,18 @@ StreamReplacer::registerStream( { QPDFObjGen og(stream.getObjGen()); - // We don't need to process a stream more than once. In this - // example, we are just iterating through objects, but if we were - // doing something like iterating through images on pages, we + // We don't need to process a stream more than once. In this example, we are just iterating + // through objects, but if we were doing something like iterating through images on pages, we // might realistically encounter the same stream more than once. if (this->copied_streams.count(og) > 0) { return; } - // Store something in copied_streams so that we don't - // double-process even in the negative case. This gets replaced - // later if needed. + // Store something in copied_streams so that we don't double-process even in the negative case. + // This gets replaced later if needed. this->copied_streams[og] = QPDFObjectHandle::newNull(); - // Call maybeReplace with dict_updates. In this mode, it - // determines whether we should replace the stream data and, if - // so, supplies dictionary updates we should make. + // Call maybeReplace with dict_updates. In this mode, it determines whether we should replace + // the stream data and, if so, supplies dictionary updates we should make. bool should_replace = false; QPDFObjectHandle dict_updates = QPDFObjectHandle::newDictionary(); try { @@ -343,9 +297,8 @@ StreamReplacer::registerStream( } if (should_replace) { - // Copy the stream so we can get to the original data from the - // stream data provider. This doesn't actually copy any data, - // but the copy retains the original stream data after the + // Copy the stream so we can get to the original data from the stream data provider. This + // doesn't actually copy any data, but the copy retains the original stream data after the // original one is modified. this->copied_streams[og] = stream.copyStream(); // Update the stream dictionary with any changes. @@ -353,20 +306,17 @@ StreamReplacer::registerStream( for (auto const& k: dict_updates.getKeys()) { dict.replaceKey(k, dict_updates.getKey(k)); } - // Create the key stream that will be referenced from - // /DecodeParms. We have to do this now since you can't modify - // or create objects during write. + // Create the key stream that will be referenced from /DecodeParms. We have to do this now + // since you can't modify or create objects during write. char p[1] = {static_cast<char>(this->keys[og])}; std::string p_str(p, 1); QPDFObjectHandle dp_stream = this->pdf->newStream(p_str); - // Create /DecodeParms as expected by our fictitious - // /XORDecode filter. + // Create /DecodeParms as expected by our fictitious /XORDecode filter. QPDFObjectHandle decode_parms = QPDFObjectHandle::newDictionary({{"/KeyStream", dp_stream}}); stream.replaceStreamData(self, QPDFObjectHandle::newName("/XORDecode"), decode_parms); - // Further, if /ProtectXOR = true, we disable filtering on write - // so that QPDFWriter will not decode the stream even though we - // have registered a stream filter for /XORDecode. + // Further, if /ProtectXOR = true, we disable filtering on write so that QPDFWriter will not + // decode the stream even though we have registered a stream filter for /XORDecode. auto protect = dict.getKey("/ProtectXOR"); if (protect.isBool() && protect.getBoolValue()) { stream.setFilterOnWrite(false); @@ -378,14 +328,12 @@ void StreamReplacer::provideStreamData(QPDFObjGen const& og, Pipeline* pipeline) { QPDFObjectHandle orig = this->copied_streams[og]; - // call maybeReplace again, this time with the pipeline and no - // dict_updates. In this mode, maybeReplace doesn't make any - // changes. We have to hand it the original stream data, which we + // call maybeReplace again, this time with the pipeline and no dict_updates. In this mode, + // maybeReplace doesn't make any changes. We have to hand it the original stream data, which we // get from copied_streams. if (!maybeReplace(og, orig, pipeline, nullptr)) { - // Since this only gets called for streams we already - // determined we are replacing, a false return would indicate - // a logic error. + // Since this only gets called for streams we already determined we are replacing, a false + // return would indicate a logic error. throw std::logic_error("should_replace return false in provideStreamData"); } } @@ -396,17 +344,15 @@ process(char const* infilename, char const* outfilename, bool decode_specialized QPDF qpdf; qpdf.processFile(infilename); - // Create a single StreamReplacer instance. The interface requires - // a std::shared_ptr in various places, so allocate a StreamReplacer - // and stash it in a std::shared_ptr. + // Create a single StreamReplacer instance. The interface requires a std::shared_ptr in various + // places, so allocate a StreamReplacer and stash it in a std::shared_ptr. auto* replacer = new StreamReplacer(&qpdf); std::shared_ptr<QPDFObjectHandle::StreamDataProvider> p(replacer); for (auto& o: qpdf.getAllObjects()) { if (o.isStream()) { - // Call registerStream for every stream. Only ones that - // registerStream decides to replace will actually be - // replaced. + // Call registerStream for every stream. Only ones that registerStream decides to + // replace will actually be replaced. replacer->registerStream(o, p); } } @@ -454,9 +400,8 @@ main(int argc, char* argv[]) } try { - // Register our fictitious filter. This enables QPDFWriter to - // decode our streams. This is not a real filter, so no real - // PDF reading application would be able to interpret it. This + // Register our fictitious filter. This enables QPDFWriter to decode our streams. This is + // not a real filter, so no real PDF reading application would be able to interpret it. This // is just for illustrative purposes. QPDF::registerStreamFilter("/XORDecode", [] { return std::make_shared<SF_XORDecode>(); }); // Do the actual processing. diff --git a/examples/pdf-double-page-size.cc b/examples/pdf-double-page-size.cc index 6e1b4f8b..289c4d17 100644 --- a/examples/pdf-double-page-size.cc +++ b/examples/pdf-double-page-size.cc @@ -14,18 +14,16 @@ void usage() { std::cerr << "Usage: " << whoami << " infile.pdf outfile.pdf [in-password]" << std::endl - << "Double size of all pages in infile.pdf;" - << " write output to outfile.pdf" << std::endl; + << "Double size of all pages in infile.pdf; write output to outfile.pdf" << std::endl; exit(2); } -// If there is a box of name box_name, replace it with a new box whose -// elements are double the values of the original box. +// If there is a box of name box_name, replace it with a new box whose elements are double the +// values of the original box. static void doubleBoxSize(QPDFPageObjectHelper& page, char const* box_name) { - // We need to use getAttribute rather than getKey as some boxes could - // be inherited. + // We need to use getAttribute rather than getKey as some boxes could be inherited. auto box = page.getAttribute(box_name, true); if (box.isNull()) { return; diff --git a/examples/pdf-filter-tokens.cc b/examples/pdf-filter-tokens.cc index 68d6e149..4a06bcd2 100644 --- a/examples/pdf-filter-tokens.cc +++ b/examples/pdf-filter-tokens.cc @@ -1,7 +1,6 @@ // -// This example illustrates the use of QPDFObjectHandle::TokenFilter -// with addContentTokenFilter. Please see comments inline for details. -// See also pdf-count-strings.cc for a use of +// This example illustrates the use of QPDFObjectHandle::TokenFilter with addContentTokenFilter. +// Please see comments inline for details. See also pdf-count-strings.cc for a use of // QPDFObjectHandle::TokenFilter with filterContents. // @@ -26,9 +25,8 @@ usage() exit(2); } -// The StringReverser class is a trivial example of using a token -// filter. This class only overrides the pure virtual handleToken -// function and preserves the default handleEOF function. +// The StringReverser class is a trivial example of using a token filter. This class only overrides +// the pure virtual handleToken function and preserves the default handleEOF function. class StringReverser: public QPDFObjectHandle::TokenFilter { public: @@ -39,15 +37,12 @@ class StringReverser: public QPDFObjectHandle::TokenFilter void StringReverser::handleToken(QPDFTokenizer::Token const& token) { - // For string tokens, reverse the characters. For other tokens, - // just pass them through. Notice that we construct a new string - // token and write that, thus allowing the library to handle any - // subtleties about properly encoding unprintable characters. This - // function doesn't handle multibyte characters at all. It's not - // intended to be an example of the correct way to reverse - // strings. It's just intended to give a simple example of a - // pretty minimal filter and to show an example of writing a - // constructed token. + // For string tokens, reverse the characters. For other tokens, just pass them through. Notice + // that we construct a new string token and write that, thus allowing the library to handle any + // subtleties about properly encoding unprintable characters. This function doesn't handle + // multibyte characters at all. It's not intended to be an example of the correct way to reverse + // strings. It's just intended to give a simple example of a pretty minimal filter and to show + // an example of writing a constructed token. if (token.getType() == QPDFTokenizer::tt_string) { std::string value = token.getValue(); std::reverse(value.begin(), value.end()); @@ -57,9 +52,8 @@ StringReverser::handleToken(QPDFTokenizer::Token const& token) } } -// The ColorToGray filter finds all "rg" operators in the content -// stream and replaces them with "g" operators, thus mapping color to -// grayscale. Note that it only applies to content streams, not +// The ColorToGray filter finds all "rg" operators in the content stream and replaces them with "g" +// operators, thus mapping color to grayscale. Note that it only applies to content streams, not // images, so this will not replace color images with grayscale // images. class ColorToGray: public QPDFObjectHandle::TokenFilter @@ -99,29 +93,23 @@ ColorToGray::numericValue(QPDFTokenizer::Token const& token) void ColorToGray::handleToken(QPDFTokenizer::Token const& token) { - // Track the number of non-ignorable tokens we've seen. If we see - // an "rg" following three numbers, convert it to a grayscale - // value. Keep writing tokens to the output as we can. - - // There are several things to notice here. We keep two stacks: - // one of "meaningful" tokens, and one of all tokens. This way we - // can preserve whitespace or comments that we encounter in the - // stream and there preserve layout. As we receive tokens, we keep - // the last four meaningful tokens. If we see three numbers - // followed by rg, we use the three numbers to calculate a gray - // value that is perceptually similar to the color value and then - // write the "g" operator to the output, discarding any spaces or - // comments encountered embedded in the "rg" operator. - - // The stack and all_stack members are updated in such a way that - // they always contain exactly the same non-ignorable tokens. The - // stack member contains the tokens that would be left if you + // Track the number of non-ignorable tokens we've seen. If we see an "rg" following three + // numbers, convert it to a grayscale value. Keep writing tokens to the output as we can. + + // There are several things to notice here. We keep two stacks: one of "meaningful" tokens, and + // one of all tokens. This way we can preserve whitespace or comments that we encounter in the + // stream and there preserve layout. As we receive tokens, we keep the last four meaningful + // tokens. If we see three numbers followed by rg, we use the three numbers to calculate a gray + // value that is perceptually similar to the color value and then write the "g" operator to the + // output, discarding any spaces or comments encountered embedded in the "rg" operator. + + // The stack and all_stack members are updated in such a way that they always contain exactly + // the same non-ignorable tokens. The stack member contains the tokens that would be left if you // removed all space and comment tokens from all_stack. - // On each new token, flush out any space or comment tokens. Store - // the incoming token. If we just got an rg preceded by the right - // kinds of operands, replace the command. Flush any additional - // accumulated tokens to keep the stack only four tokens deep. + // On each new token, flush out any space or comment tokens. Store the incoming token. If we + // just got an rg preceded by the right kinds of operands, replace the command. Flush any + // additional accumulated tokens to keep the stack only four tokens deep. while ((!this->all_stack.empty()) && isIgnorable(this->all_stack.at(0).getType())) { writeToken(this->all_stack.at(0)); @@ -182,11 +170,9 @@ main(int argc, char* argv[]) QPDF pdf; pdf.processFile(infilename); for (auto& page: QPDFPageDocumentHelper(pdf).getAllPages()) { - // Attach two token filters to each page of this file. - // When the file is written, or when the pages' contents - // are retrieved in any other way, the filters will be - // applied. See comments on the filters for additional - // details. + // Attach two token filters to each page of this file. When the file is written, or when + // the pages' contents are retrieved in any other way, the filters will be applied. See + // comments on the filters for additional details. page.addContentTokenFilter( std::shared_ptr<QPDFObjectHandle::TokenFilter>(new StringReverser)); page.addContentTokenFilter( diff --git a/examples/pdf-invert-images.cc b/examples/pdf-invert-images.cc index 5692e7b2..4ec83b84 100644 --- a/examples/pdf-invert-images.cc +++ b/examples/pdf-invert-images.cc @@ -20,15 +20,12 @@ usage() exit(2); } -// Derive a class from StreamDataProvider to provide updated stream -// data. The main purpose of using this object is to avoid having to -// allocate memory up front for the objects. We want to replace the -// stream data with a function of the original stream data. In order -// to do this without actually holding all the images in memory, we -// create copies of the streams. Copying the streams doesn't actually -// copy the data. Internally, the qpdf library is holding onto the -// location of the original stream data, which makes it possible for -// the StreamDataProvider to access it when it needs it. +// Derive a class from StreamDataProvider to provide updated stream data. The main purpose of using +// this object is to avoid having to allocate memory up front for the objects. We want to replace +// the stream data with a function of the original stream data. In order to do this without actually +// holding all the images in memory, we create copies of the streams. Copying the streams doesn't +// actually copy the data. Internally, the qpdf library is holding onto the location of the original +// stream data, which makes it possible for the StreamDataProvider to access it when it needs it. class ImageInverter: public QPDFObjectHandle::StreamDataProvider { public: @@ -46,42 +43,35 @@ void ImageInverter::registerImage( QPDFObjectHandle image, std::shared_ptr<QPDFObjectHandle::StreamDataProvider> self) { - // replaceStreamData requires a pointer holder to the stream data - // provider, but there's no way for us to generate one ourselves, - // so we have to have it handed to us. Don't be tempted to have - // the class contain a std::shared_ptr to itself as a member. Doing - // this will prevent the class from ever being deleted since the - // reference count will never drop to zero (and std::shared_ptr - // doesn't have weak references). + // replaceStreamData requires a pointer holder to the stream data provider, but there's no way + // for us to generate one ourselves, so we have to have it handed to us. Don't be tempted to + // have the class contain a std::shared_ptr to itself as a member. Doing this will prevent the + // class from ever being deleted since the reference count will never drop to zero (and + // std::shared_ptr doesn't have weak references). QPDFObjGen og(image.getObjGen()); - // Store information about the images based on the object and - // generation number. Recall that a single image object may be - // used more than once, so no need to update the same stream - // multiple times. + // Store information about the images based on the object and generation number. Recall that a + // single image object may be used more than once, so no need to update the same stream multiple + // times. if (this->copied_images.count(og) > 0) { return; } this->copied_images[og] = image.copyStream(); - // Register our stream data provider for this stream. Future calls - // to getStreamData or pipeStreamData will use the new - // information. Provide null for both filter and decode - // parameters. Note that this does not mean the image data will be - // uncompressed when we write the file. By default, QPDFWriter - // will use /FlateDecode for anything that is uncompressed or - // filterable in the input QPDF object, so we don't have to deal - // with it explicitly here. We could explicitly use /DCTDecode and - // write through a DCT filter if we wanted. + // Register our stream data provider for this stream. Future calls to getStreamData or + // pipeStreamData will use the new information. Provide null for both filter and decode + // parameters. Note that this does not mean the image data will be uncompressed when we write + // the file. By default, QPDFWriter will use /FlateDecode for anything that is uncompressed or + // filterable in the input QPDF object, so we don't have to deal with it explicitly here. We + // could explicitly use /DCTDecode and write through a DCT filter if we wanted. image.replaceStreamData(self, QPDFObjectHandle::newNull(), QPDFObjectHandle::newNull()); } void ImageInverter::provideStreamData(QPDFObjGen const& og, Pipeline* pipeline) { - // Use the object and generation number supplied to look up the - // image data. Then invert the image data and write the inverted - // data to the pipeline. + // Use the object and generation number supplied to look up the image data. Then invert the + // image data and write the inverted data to the pipeline. std::shared_ptr<Buffer> data = this->copied_images[og].getStreamData(qpdf_dl_all); size_t size = data->getSize(); unsigned char* buf = data->getBuffer(); @@ -130,11 +120,9 @@ main(int argc, char* argv[]) QPDFObjectHandle color_space = image_dict.getKey("/ColorSpace"); QPDFObjectHandle bits_per_component = image_dict.getKey("/BitsPerComponent"); - // For our example, we can only work with images 8-bit - // grayscale images that we can fully decode. Use - // pipeStreamData with a null pipeline to determine - // whether the image is filterable. Directly inspect - // keys to determine the image type. + // For our example, we can only work with 8-bit grayscale images that we can fully + // decode. Use pipeStreamData with a null pipeline to determine whether the image + // is filterable. Directly inspect keys to determine the image type. if (image.pipeStreamData(nullptr, qpdf_ef_compress, qpdf_dl_all) && color_space.isNameAndEquals("/DeviceGray") && bits_per_component.isInteger() && (bits_per_component.getIntValue() == 8)) { @@ -146,8 +134,7 @@ main(int argc, char* argv[]) // Write out a new file QPDFWriter w(qpdf, outfilename); if (static_id) { - // For the test suite, uncompress streams and use static - // IDs. + // For the test suite, uncompress streams and use static IDs. w.setStaticID(true); // for testing only } w.write(); diff --git a/examples/pdf-linearize.c b/examples/pdf-linearize.c index 3a7a335d..20461593 100644 --- a/examples/pdf-linearize.c +++ b/examples/pdf-linearize.c @@ -51,8 +51,7 @@ main(int argc, char* argv[]) if (((qpdf_read(qpdf, infile, password) & QPDF_ERRORS) == 0) && ((qpdf_init_write(qpdf, outfile) & QPDF_ERRORS) == 0)) { - /* Use static ID for testing only. For production, a - * non-static ID is used. See also + /* Use static ID for testing only. For production, a non-static ID is used. See also * qpdf_set_deterministic_ID. */ qpdf_set_static_ID(qpdf, QPDF_TRUE); /* for testing only */ qpdf_set_linearization(qpdf, QPDF_TRUE); diff --git a/examples/pdf-name-number-tree.cc b/examples/pdf-name-number-tree.cc index a14ad126..7701e70e 100644 --- a/examples/pdf-name-number-tree.cc +++ b/examples/pdf-name-number-tree.cc @@ -29,24 +29,19 @@ main(int argc, char* argv[]) QPDF qpdf; qpdf.emptyPDF(); - // This example doesn't do anything particularly useful other than - // just illustrate how to use the APIs for name and number trees. - // It also demonstrates use of the iterators for dictionaries and - // arrays introduced at the same time with qpdf 10.2. - - // To use this example, compile it and run it. Study the output - // and compare it to what you expect. When done, look at the - // generated output file in a text editor to inspect the structure - // of the trees as left in the file. - - // We're just going to create some name and number trees, hang - // them off the document catalog (root), and write an empty PDF to - // a file. The PDF will have no pages and won't be viewable, but - // you can look at it in a text editor to see the resulting - // structure of the PDF. - - // Create a dictionary off the root where we will hang our name - // and number tree. + // This example doesn't do anything particularly useful other than just illustrate how to use + // the APIs for name and number trees. It also demonstrates use of the iterators for + // dictionaries and arrays introduced at the same time with qpdf 10.2. + + // To use this example, compile it and run it. Study the output and compare it to what you + // expect. When done, look at the generated output file in a text editor to inspect the + // structure of the trees as left in the file. + + // We're just going to create some name and number trees, hang them off the document catalog + // (root), and write an empty PDF to a file. The PDF will have no pages and won't be viewable, + // but you can look at it in a text editor to see the resulting structure of the PDF. + + // Create a dictionary off the root where we will hang our name and number trees. auto root = qpdf.getRoot(); auto example = QPDFObjectHandle::newDictionary(); root.replaceKey("/Example", example); @@ -75,8 +70,8 @@ main(int argc, char* argv[]) std::cout << " " << i.first << " -> " << i.second.unparse() << std::endl; } - // This is a small tree, so everything will be at the root. We can - // look at it using dictionary and array iterators. + // This is a small tree, so everything will be at the root. We can look at it using dictionary + // and array iterators. std::cout << "Keys in name tree object:" << std::endl; QPDFObjectHandle names; for (auto const& i: name_tree_oh.ditems()) { @@ -121,15 +116,12 @@ main(int argc, char* argv[]) << std::endl; std::cout << "Has K?: " << name_tree.hasName("K") << std::endl; - // Illustrate some more advanced usage using number trees. These - // calls work for name trees too. + // Illustrate some more advanced usage using number trees. These calls work for name trees too. - // The safe way to populate a tree is to call insert repeatedly as - // above, but if you know you are definitely inserting items in - // order, it is more efficient to insert them using insertAfter, - // which avoids doing a binary search through the tree for each - // insertion. Note that if you don't insert items in order using - // this method, you will create an invalid tree. + // The safe way to populate a tree is to call insert repeatedly as above, but if you know you + // are definitely inserting items in order, it is more efficient to insert them using + // insertAfter, which avoids doing a binary search through the tree for each insertion. Note + // that if you don't insert items in order using this method, you will create an invalid tree. auto number_tree = QPDFNumberTreeObjectHelper::newEmpty(qpdf); auto number_tree_oh = number_tree.getObjectHandle(); example.replaceKey("/NumberTree", number_tree_oh); @@ -149,9 +141,8 @@ main(int argc, char* argv[]) ++n; } - // When you remove an item with an iterator, the iterator - // advances. This makes it possible to filter while iterating. - // Remove all items that are multiples of 5. + // When you remove an item with an iterator, the iterator advances. This makes it possible to + // filter while iterating. Remove all items that are multiples of 5. iter2 = number_tree.begin(); while (iter2 != number_tree.end()) { if (iter2->first % 5 == 0) { diff --git a/examples/pdf-overlay-page.cc b/examples/pdf-overlay-page.cc index 6d1cd0b0..75577094 100644 --- a/examples/pdf-overlay-page.cc +++ b/examples/pdf-overlay-page.cc @@ -6,10 +6,9 @@ #include <cstdlib> #include <iostream> -// This program demonstrates use of form XObjects to overlay a page -// from one file onto all pages of another file. The qpdf program's -// --overlay and --underlay options provide a more general version of -// this capability. +// This program demonstrates use of form XObjects to overlay a page from one file onto all pages of +// another file. The qpdf program's --overlay and --underlay options provide a more general version +// of this capability. static char const* whoami = nullptr; @@ -44,24 +43,21 @@ stamp_page(char const* infile, char const* stampfile, char const* outfile) int min_suffix = 1; std::string name = resources.getUniqueResourceName("/Fx", min_suffix); - // Generate content to place the form XObject centered within - // destination page's trim box. + // Generate content to place the form XObject centered within destination page's trim box. QPDFMatrix m; std::string content = ph.placeFormXObject(stamp_fo, name, ph.getTrimBox().getArrayAsRectangle(), m); if (!content.empty()) { - // Append the content to the page's content. Surround the - // original content with q...Q to the new content from the - // page's original content. + // Append the content to the page's content. Surround the original content with q...Q to + // the new content from the page's original content. resources.mergeResources("<< /XObject << >> >>"_qpdf); resources.getKey("/XObject").replaceKey(name, stamp_fo); ph.addPageContents(inpdf.newStream("q\n"), true); ph.addPageContents(inpdf.newStream("\nQ\n" + content), false); } - // Copy the annotations and form fields from the original page - // to the new page. For more efficiency when copying multiple - // pages, we can create a QPDFAcroFormDocumentHelper and pass - // it in. See comments in QPDFPageObjectHelper.hh for details. + // Copy the annotations and form fields from the original page to the new page. For more + // efficiency when copying multiple pages, we can create a QPDFAcroFormDocumentHelper and + // pass it in. See comments in QPDFPageObjectHelper.hh for details. ph.copyAnnotations(stamp_page_1, m); } diff --git a/examples/pdf-parse-content.cc b/examples/pdf-parse-content.cc index 743986a1..b60693f0 100644 --- a/examples/pdf-parse-content.cc +++ b/examples/pdf-parse-content.cc @@ -13,8 +13,8 @@ void usage() { std::cerr << "Usage: " << whoami << " filename page-number" << std::endl - << "Prints a dump of the objects in the content streams" - << " of the given page." << std::endl + << "Prints a dump of the objects in the content streams of the given page." + << std::endl << "Pages are numbered from 1." << std::endl; exit(2); } diff --git a/examples/pdf-set-form-values.cc b/examples/pdf-set-form-values.cc index c2793142..810b9fa3 100644 --- a/examples/pdf-set-form-values.cc +++ b/examples/pdf-set-form-values.cc @@ -29,42 +29,34 @@ main(int argc, char* argv[]) char const* outfilename = argv[2]; char const* value = argv[3]; - // This is a contrived example that just goes through a file page - // by page and sets the value of any text fields it finds to a - // fixed value as given on the command line. The purpose here is - // to illustrate use of the helper classes around interactive - // forms. + // This is a contrived example that just goes through a file page by page and sets the value of + // any text fields it finds to a fixed value as given on the command line. The purpose here is + // to illustrate use of the helper classes around interactive forms. try { QPDF qpdf; qpdf.processFile(infilename); - // We will iterate through form fields by starting at the page - // level and looking at each field for each page. We could - // also called QPDFAcroFormDocumentHelper::getFormFields to - // iterate at the field level, but doing it as below - // illustrates how we can map from annotations to fields. + // We will iterate through form fields by starting at the page level and looking at each + // field for each page. We could also call QPDFAcroFormDocumentHelper::getFormFields to + // iterate at the field level, but doing it as below illustrates how we can map from + // annotations to fields. QPDFAcroFormDocumentHelper afdh(qpdf); for (auto const& page: QPDFPageDocumentHelper(qpdf).getAllPages()) { - // Get all widget annotations for each page. Widget - // annotations are the ones that contain the details of - // what's in a form field. + // Get all widget annotations for each page. Widget annotations are the ones that + // contain the details of what's in a form field. for (auto& annot: afdh.getWidgetAnnotationsForPage(page)) { - // For each annotation, find its associated field. If - // it's a text field, set its value. + // For each annotation, find its associated field. If it's a text field, set its + // value. QPDFFormFieldObjectHelper ffh = afdh.getFieldForAnnotation(annot); if (ffh.getFieldType() == "/Tx") { - // Set the value. Passing false as the second - // value prevents qpdf from setting - // /NeedAppearances to true (but will not turn it - // off if it's already on), so we call - // generateAppearance after setting the value. You - // may or may not want to do this depending on - // whether the appearance streams generated by - // qpdf are good enough for your purposes. For - // additional details, please see comments in - // QPDFFormFieldObjectHelper.hh for this method. + // Set the value. Passing false as the second parameter prevents qpdf from + // setting /NeedAppearances to true (but will not turn it off if it's already + // on), so we call generateAppearance after setting the value. You may or may + // not want to do this depending on whether the appearance streams generated by + // qpdf are good enough for your purposes. For additional details, please see + // comments in QPDFFormFieldObjectHelper.hh for this method. ffh.setV(value, false); ffh.generateAppearance(annot); } diff --git a/examples/pdf-split-pages.cc b/examples/pdf-split-pages.cc index 9890ccf4..fde4bc1b 100644 --- a/examples/pdf-split-pages.cc +++ b/examples/pdf-split-pages.cc @@ -1,7 +1,6 @@ // -// This is a stand-alone example of splitting a PDF into individual -// pages. It does essentially the same thing that qpdf --split-pages -// does. +// This is a stand-alone example of splitting a PDF into individual pages. It does essentially the +// same thing that qpdf --split-pages does. // #include <qpdf/QIntC.hh> @@ -32,8 +31,7 @@ process(char const* whoami, char const* infile, std::string outprefix) QPDFPageDocumentHelper(outpdf).addPage(page, false); QPDFWriter outpdfw(outpdf, outfile.c_str()); if (static_id) { - // For the test suite, uncompress streams and use static - // IDs. + // For the test suite, uncompress streams and use static IDs. outpdfw.setStaticID(true); // for testing only outpdfw.setStreamDataMode(qpdf_s_uncompress); } diff --git a/examples/qpdf-job.cc b/examples/qpdf-job.cc index c7131f77..be868a17 100644 --- a/examples/qpdf-job.cc +++ b/examples/qpdf-job.cc @@ -3,8 +3,7 @@ #include <iostream> -// This program is a simple demonstration of different ways to use the -// QPDFJob API. +// This program is a simple demonstration of different ways to use the QPDFJob API. static char const* whoami = nullptr; @@ -28,10 +27,9 @@ main(int argc, char* argv[]) usage(); } - // The examples below all catch std::exception. Note that - // QPDFUsage can be caught separately to report on errors in using - // the API itself. For CLI, this is command-line usage. For JSON - // or the API, it would be errors from the equivalent invocation. + // The examples below all catch std::exception. Note that QPDFUsage can be caught separately to + // report on errors in using the API itself. For CLI, this is command-line usage. For JSON or + // the API, it would be errors from the equivalent invocation. // Note that staticId is used for testing only. diff --git a/examples/qpdfjob-c-save-attachment.c b/examples/qpdfjob-c-save-attachment.c index c461f974..30e48127 100644 --- a/examples/qpdfjob-c-save-attachment.c +++ b/examples/qpdfjob-c-save-attachment.c @@ -6,10 +6,9 @@ #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. +// 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 int save_to_file(char const* data, size_t len, void* udata) @@ -79,9 +78,8 @@ main(int argc, char* argv[]) }; 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. + /* 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); diff --git a/examples/qpdfjob-c.c b/examples/qpdfjob-c.c index 452e689b..62528392 100644 --- a/examples/qpdfjob-c.c +++ b/examples/qpdfjob-c.c @@ -1,7 +1,4 @@ -/* - * This is an example program to linearize a PDF file using the C - * QPDFJob API. - */ +/* This is an example program to linearize a PDF file using the C QPDFJob API. */ #include <qpdf/qpdfjob-c.h> #include <stdio.h> @@ -48,14 +45,12 @@ main(int argc, char* argv[]) new_argv[4] = "--static-id"; /* for testing only */ new_argv[5] = NULL; - /* See qpdf-job.cc for a C++ example of using the json interface. - * 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 qpdf-job.cc for a C++ example of using the json interface. 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. + * 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; diff --git a/examples/qpdfjob-remove-annotations.cc b/examples/qpdfjob-remove-annotations.cc index b0afa2da..bc043d33 100644 --- a/examples/qpdfjob-remove-annotations.cc +++ b/examples/qpdfjob-remove-annotations.cc @@ -6,10 +6,9 @@ #include <cstdlib> #include <iostream> -// This example demonstrates how we can use the QPDFJob createQPDF and writeQPDF -// methods to add custom transformations to the output produced by QPDFJob runs. -// The example is a full copy of the qpdf program modified to allways remove all -// annotations from the final output. +// This example demonstrates how we can use the QPDFJob createQPDF and writeQPDF methods to add +// custom transformations to the output produced by QPDFJob runs. The example is a full copy of the +// qpdf program modified to allways remove all annotations from the final output. static char const* whoami = nullptr; diff --git a/examples/qpdfjob-save-attachment.cc b/examples/qpdfjob-save-attachment.cc index 045e38bf..790d808f 100644 --- a/examples/qpdfjob-save-attachment.cc +++ b/examples/qpdfjob-save-attachment.cc @@ -3,10 +3,9 @@ #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. +// 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[]) |