diff --git a/ChangeLog b/ChangeLog index 1592daaf..e5e2237a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2010-08-05 Jay Berkenbilt + * Add QPDFObjectHandle::addPageContents, a convenience routine for + appending or prepending new streams to a page's content streams. + The "double-page-size" example illustrates its use. + * Add new methods to QPDFObjectHandle: replaceStreamData and newStream. These methods allow users of the qpdf library to add new streams and to replace data of existing streams. Examples are diff --git a/TODO b/TODO index 55980081..70af8b9f 100644 --- a/TODO +++ b/TODO @@ -7,9 +7,9 @@ Next in August, 2009. He seems to like to send encrypted mail (key 01FCC336). Tell him about newStream and replaceStreamData. - * Tell stronghorse@tom.com about QPDFObjectHandle::addPageContents. - See message from stronghorse@tom.com ("Suggestion for qpdf") from - 2010-06-09 and my response. + * Tell stronghorse@tom.com about QPDFObjectHandle::addPageContents + and double-page-size example. See message from stronghorse@tom.com + ("Suggestion for qpdf") from 2010-06-09 and my response. 2.2 === @@ -18,8 +18,6 @@ Next image. Use QPDF::getAllPages and QPDFObjectHandle::getPageImages along with new stream data and dictionary manipulation. - * Add example program for addPageContents using suggestion from - stronghorse@tom.com's message above. General ======= diff --git a/examples/build.mk b/examples/build.mk index 5c2b24de..f2e0432d 100644 --- a/examples/build.mk +++ b/examples/build.mk @@ -1,4 +1,4 @@ -BINS_examples = pdf-bookmarks pdf-mod-info pdf-npages +BINS_examples = pdf-bookmarks pdf-mod-info pdf-npages double-page-size CBINS_examples = pdf-linearize TARGETS_examples = $(foreach B,$(BINS_examples) $(CBINS_examples),examples/$(OUTPUT_DIR)/$(call binname,$(B))) diff --git a/examples/double-page-size.cc b/examples/double-page-size.cc new file mode 100644 index 00000000..e933b5ac --- /dev/null +++ b/examples/double-page-size.cc @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include + +static char const* whoami = 0; + +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; + exit(2); +} + +static void doubleBoxSize(QPDFObjectHandle& page, char const* box_name) +{ + QPDFObjectHandle box = page.getKey(box_name); + if (box.isNull()) + { + return; + } + if (! (box.isArray() && (box.getArrayNItems() == 4))) + { + throw std::runtime_error(std::string("box ") + box_name + + " is not an array of four elements"); + } + std::vector doubled; + for (unsigned int i = 0; i < 4; ++i) + { + doubled.push_back( + QPDFObjectHandle::newReal( + QUtil::double_to_string( + box.getArrayItem(i).getNumericValue() * 2.0, 2))); + } + page.replaceKey(box_name, QPDFObjectHandle::newArray(doubled)); +} + +int main(int argc, char* argv[]) +{ + whoami = QUtil::getWhoami(argv[0]); + + // For libtool's sake.... + if (strncmp(whoami, "lt-", 3) == 0) + { + whoami += 3; + } + + if (! ((argc == 3) || (argc == 4))) + { + usage(); + } + + char const* infilename = argv[1]; + char const* outfilename = argv[2]; + char const* password = (argc == 4) ? argv[3] : ""; + + // Text to prepend to each page's contents + char const* content = "2 0 0 2 0 0 cm\n"; + + // Copy text into a buffer without the null terminator + PointerHolder b = new Buffer(strlen(content)); + unsigned char* bp = b.getPointer()->getBuffer(); + memcpy(bp, (unsigned char*)content, strlen(content)); + + try + { + QPDF qpdf; + qpdf.processFile(infilename, password); + + std::vector pages = qpdf.getAllPages(); + for (std::vector::iterator iter = pages.begin(); + iter != pages.end(); ++iter) + { + QPDFObjectHandle& page = *iter; + + // Prepend the buffer to the page's contents + page.addPageContents(QPDFObjectHandle::newStream(&qpdf, b), true); + + // Double the size of each of the content boxes + doubleBoxSize(page, "/MediaBox"); + doubleBoxSize(page, "/CropBox"); + doubleBoxSize(page, "/BleedBox"); + doubleBoxSize(page, "/TrimBox"); + doubleBoxSize(page, "/ArtBox"); + } + + // Write out a new file + QPDFWriter w(qpdf, outfilename); + if (QUtil::get_env("IN_TESTSUITE")) + { + // For the test suite, uncompress streams and use static + // IDs. + w.setStaticID(true); + w.setStreamDataMode(qpdf_s_uncompress); + } + w.write(); + std::cout << whoami << ": new file written to " << outfilename + << std::endl; + } + catch (std::exception &e) + { + std::cerr << whoami << " processing file " << infilename << ": " + << e.what() << std::endl; + exit(2); + } + + return 0; +} diff --git a/examples/qtest/double-page-size.test b/examples/qtest/double-page-size.test new file mode 100644 index 00000000..79e74862 --- /dev/null +++ b/examples/qtest/double-page-size.test @@ -0,0 +1,31 @@ +#!/usr/bin/env perl +require 5.008; +BEGIN { $^W = 1; } +use strict; + +chdir("double-page-size") or die "chdir testdir failed: $!\n"; + +require TestDriver; + +my $td = new TestDriver('double-page-size'); + +cleanup(); + +$td->runtest("double page size", + {$td->COMMAND => "double-page-size in.pdf a.pdf"}, + {$td->STRING => "double-page-size: new file written to a.pdf\n", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + +$td->runtest("check output", + {$td->FILE => "a.pdf"}, + {$td->FILE => "out.pdf"}); + +cleanup(); + +$td->report(2); + +sub cleanup +{ + unlink 'a.pdf'; +} diff --git a/examples/qtest/double-page-size/in.pdf b/examples/qtest/double-page-size/in.pdf new file mode 100644 index 00000000..84f252be --- /dev/null +++ b/examples/qtest/double-page-size/in.pdf @@ -0,0 +1,55 @@ +%PDF-1.3 +%¿÷¢þ +1 0 obj +<< /Pages 2 0 R /Type /Catalog >> +endobj +2 0 obj +<< /Count 2 /Kids [ 3 0 R 4 0 R ] /Type /Pages >> +endobj +3 0 obj +<< /Contents 5 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 6 0 R >> /ProcSet 7 0 R >> /Type /Page >> +endobj +4 0 obj +<< /Contents 8 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 6 0 R >> /ProcSet 7 0 R >> /Type /Page >> +endobj +5 0 obj +<< /Length 44 >> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj +6 0 obj +<< /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >> +endobj +7 0 obj +[ /PDF /Text ] +endobj +8 0 obj +<< /Length 43 >> +stream +BT + /F1 24 Tf + 72 720 Td + (Salad) Tj +ET +endstream +endobj +xref +0 9 +0000000000 65535 f +0000000015 00000 n +0000000064 00000 n +0000000129 00000 n +0000000272 00000 n +0000000415 00000 n +0000000508 00000 n +0000000615 00000 n +0000000645 00000 n +trailer << /Root 1 0 R /Size 9 /ID [<071a6499182be9cfc990b11d0c4e5bc0><90dbb2f2f000af76d1107187b0e00349>] >> +startxref +737 +%%EOF diff --git a/examples/qtest/double-page-size/out.pdf b/examples/qtest/double-page-size/out.pdf new file mode 100644 index 00000000..e27706ca --- /dev/null +++ b/examples/qtest/double-page-size/out.pdf @@ -0,0 +1,69 @@ +%PDF-1.3 +%¿÷¢þ +1 0 obj +<< /Pages 2 0 R /Type /Catalog >> +endobj +2 0 obj +<< /Count 2 /Kids [ 3 0 R 4 0 R ] /Type /Pages >> +endobj +3 0 obj +<< /Contents [ 5 0 R 6 0 R ] /MediaBox [ 0.00 0.00 1224.00 1584.00 ] /Parent 2 0 R /Resources << /Font << /F1 7 0 R >> /ProcSet 8 0 R >> /Type /Page >> +endobj +4 0 obj +<< /Contents [ 9 0 R 10 0 R ] /MediaBox [ 0.00 0.00 1224.00 1584.00 ] /Parent 2 0 R /Resources << /Font << /F1 7 0 R >> /ProcSet 8 0 R >> /Type /Page >> +endobj +5 0 obj +<< /Length 15 >> +stream +2 0 0 2 0 0 cm +endstream +endobj +6 0 obj +<< /Length 44 >> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj +7 0 obj +<< /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >> +endobj +8 0 obj +[ /PDF /Text ] +endobj +9 0 obj +<< /Length 15 >> +stream +2 0 0 2 0 0 cm +endstream +endobj +10 0 obj +<< /Length 43 >> +stream +BT + /F1 24 Tf + 72 720 Td + (Salad) Tj +ET +endstream +endobj +xref +0 11 +0000000000 65535 f +0000000015 00000 n +0000000064 00000 n +0000000129 00000 n +0000000296 00000 n +0000000464 00000 n +0000000528 00000 n +0000000621 00000 n +0000000728 00000 n +0000000758 00000 n +0000000822 00000 n +trailer << /Root 1 0 R /Size 11 /ID [<071a6499182be9cfc990b11d0c4e5bc0><31415926535897932384626433832795>] >> +startxref +915 +%%EOF