From ffb96ee17e9721bb6cf3982249be537cacc68814 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Fri, 22 Jun 2012 22:07:58 -0400 Subject: Add pdf-from-scratch example --- examples/build.mk | 3 +- examples/pdf-create.cc | 192 ++++++++++++++++++++++++++++++++ examples/qtest/create.test | 29 +++++ examples/qtest/create/orange-square.pdf | Bin 0 -> 1088 bytes 4 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 examples/pdf-create.cc create mode 100644 examples/qtest/create.test create mode 100644 examples/qtest/create/orange-square.pdf (limited to 'examples') diff --git a/examples/build.mk b/examples/build.mk index 38e4c3cc..12734b1b 100644 --- a/examples/build.mk +++ b/examples/build.mk @@ -3,7 +3,8 @@ BINS_examples = \ pdf-mod-info \ pdf-npages \ pdf-double-page-size \ - pdf-invert-images + pdf-invert-images \ + pdf-create CBINS_examples = pdf-linearize TARGETS_examples = $(foreach B,$(BINS_examples) $(CBINS_examples),examples/$(OUTPUT_DIR)/$(call binname,$(B))) diff --git a/examples/pdf-create.cc b/examples/pdf-create.cc new file mode 100644 index 00000000..e1a3ddd6 --- /dev/null +++ b/examples/pdf-create.cc @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include +#include +#include + +static char const* whoami = 0; + +// 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: + ImageProvider(int width, int height); + virtual ~ImageProvider(); + virtual void provideStreamData(int objid, int generation, + Pipeline* pipeline); + size_t getLength() const; + + private: + int width; + int height; +}; + +ImageProvider::ImageProvider(int width, int height) : + width(width), + height(height) +{ +} + +ImageProvider::~ImageProvider() +{ +} + +void +ImageProvider::provideStreamData(int objid, int generation, + Pipeline* pipeline) +{ + for (int i = 0; i < width * height; ++i) + { + pipeline->write((unsigned char*)"\xff\x7f\x00", 3); + } + pipeline->finish(); +} + +size_t +ImageProvider::getLength() const +{ + return 3 * width * height; +} + +void usage() +{ + std::cerr << "Usage: " << whoami << " filename" << std::endl + << "Creates a simple PDF and writes it to filename" << std::endl; + exit(2); +} + +static QPDFObjectHandle createPageContents(QPDF& pdf, std::string const& text) +{ + // Create a stream that displays our image and the given text in + // our font. + std::string contents = + "BT /F1 24 Tf 72 720 Td (" + text + ") Tj ET\n" + "q 144 0 0 144 234 324 cm /Im1 Do Q\n"; + PointerHolder b = new Buffer(contents.length()); + unsigned char* bp = b->getBuffer(); + memcpy(bp, (char*)contents.c_str(), contents.length()); + return QPDFObjectHandle::newStream(&pdf, b); +} + +QPDFObjectHandle newName(std::string const& name) +{ + return QPDFObjectHandle::newName(name); +} + +QPDFObjectHandle newInteger(int val) +{ + return QPDFObjectHandle::newInteger(val); +} + +static void create_pdf(char const* filename) +{ + QPDF pdf; + + // 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. + QPDFObjectHandle font = pdf.makeIndirectObject( + QPDFObjectHandle::newDictionary()); + font.replaceKey("/Type", newName("/Font")); + font.replaceKey("/Subtype", newName("/Type1")); + font.replaceKey("/Name", newName("/F1")); + font.replaceKey("/BaseFont", newName("/Helvetica")); + font.replaceKey("/Encoding", newName("/WinAnsiEncoding")); + + // Create a stream to encode our image. We don't have to set the + // length or filters. QPDFWriter will fill in the length and + // compress the stream data using FlateDecode by default. + QPDFObjectHandle image = QPDFObjectHandle::newStream(&pdf); + QPDFObjectHandle image_dict = image.getDict(); + image_dict.replaceKey("/Type", newName("/XObject")); + image_dict.replaceKey("/Subtype", newName("/Image")); + image_dict.replaceKey("/ColorSpace", newName("/DeviceRGB")); + image_dict.replaceKey("/BitsPerComponent", newInteger(8)); + image_dict.replaceKey("/Width", newInteger(100)); + image_dict.replaceKey("/Height", newInteger(100)); + // Provide the stream data. + ImageProvider* p = new ImageProvider(100, 100); + PointerHolder provider(p); + image.replaceStreamData(provider, + QPDFObjectHandle::newNull(), + QPDFObjectHandle::newNull(), + p->getLength()); + + // Create direct objects as needed by the page dictionary. + QPDFObjectHandle procset = QPDFObjectHandle::newArray(); + procset.appendItem(newName("/PDF")); + procset.appendItem(newName("/Text")); + procset.appendItem(newName("/ImageC")); + + QPDFObjectHandle rfont = QPDFObjectHandle::newDictionary(); + rfont.replaceKey("/F1", font); + + QPDFObjectHandle xobject = QPDFObjectHandle::newDictionary(); + xobject.replaceKey("/Im1", image); + + QPDFObjectHandle resources = QPDFObjectHandle::newDictionary(); + resources.replaceKey("/ProcSet", procset); + resources.replaceKey("/Font", rfont); + resources.replaceKey("/XObject", xobject); + + QPDFObjectHandle mediabox = QPDFObjectHandle::newArray(); + mediabox.appendItem(newInteger(0)); + mediabox.appendItem(newInteger(0)); + mediabox.appendItem(newInteger(612)); + mediabox.appendItem(newInteger(792)); + + // Create the page content stream + QPDFObjectHandle contents = createPageContents( + pdf, "Look at the pretty, orange square!"); + + // Create the page dictionary + QPDFObjectHandle page = pdf.makeIndirectObject( + QPDFObjectHandle::newDictionary()); + page.replaceKey("/Type", newName("/Page")); + page.replaceKey("/MediaBox", mediabox); + page.replaceKey("/Contents", contents); + page.replaceKey("/Resources", resources); + + // Add the page to the PDF file + pdf.addPage(page, true); + + // Write the results. A real application would not call + // setStaticID here, but this example does it for the sake of its + // test suite. + QPDFWriter w(pdf, filename); + w.setStaticID(true); // for testing only + w.write(); +} + +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 != 2) + { + usage(); + } + char const* filename = argv[1]; + + try + { + create_pdf(filename); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + exit(2); + } + + return 0; +} diff --git a/examples/qtest/create.test b/examples/qtest/create.test new file mode 100644 index 00000000..bcdac25c --- /dev/null +++ b/examples/qtest/create.test @@ -0,0 +1,29 @@ +#!/usr/bin/env perl +require 5.008; +use warnings; +use strict; + +chdir("create") or die "chdir testdir failed: $!\n"; + +require TestDriver; + +cleanup(); + +my $td = new TestDriver('create'); + +$td->runtest("create a simple PDF", + {$td->COMMAND => "pdf-create a.pdf"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}); + +$td->runtest("check", + {$td->FILE => "a.pdf"}, + {$td->FILE => "orange-square.pdf"}); + +cleanup(); + +$td->report(2); + +sub cleanup +{ + unlink "a.pdf"; +} diff --git a/examples/qtest/create/orange-square.pdf b/examples/qtest/create/orange-square.pdf new file mode 100644 index 00000000..28fb47cd Binary files /dev/null and b/examples/qtest/create/orange-square.pdf differ -- cgit v1.2.3-54-g00ecf