Commit ffb96ee17e9721bb6cf3982249be537cacc68814

Authored by Jay Berkenbilt
1 parent bf059a60

Add pdf-from-scratch example

ChangeLog
1 1 2012-06-22 Jay Berkenbilt <ejb@ql.org>
2 2  
  3 + * examples/pdf-create.cc: Provide an example of creating a PDF
  4 + from scratch. This simple PDF has a single page with some text
  5 + and an image.
  6 +
3 7 * Add empty QPDFObjectHandle factories for array and dictionary.
4 8 With PDF-from-scratch capability, it is useful to be able to
5 9 create empty arrays and dictionaries and add keys to them.
... ...
... ... @@ -42,8 +42,6 @@ Next
42 42 contents to make sure the file is really correct. We need to
43 43 test normal writing and linearization.
44 44  
45   - * Consider adding an example that uses the page APIs, or update the
46   - documentation to refer the user to the test suite.
47 45  
48 46 Soon
49 47 ====
... ...
examples/build.mk
... ... @@ -3,7 +3,8 @@ BINS_examples = \
3 3 pdf-mod-info \
4 4 pdf-npages \
5 5 pdf-double-page-size \
6   - pdf-invert-images
  6 + pdf-invert-images \
  7 + pdf-create
7 8 CBINS_examples = pdf-linearize
8 9  
9 10 TARGETS_examples = $(foreach B,$(BINS_examples) $(CBINS_examples),examples/$(OUTPUT_DIR)/$(call binname,$(B)))
... ...
examples/pdf-create.cc 0 โ†’ 100644
  1 +#include <qpdf/QPDF.hh>
  2 +#include <qpdf/QPDFWriter.hh>
  3 +#include <qpdf/QPDFObjectHandle.hh>
  4 +#include <qpdf/QUtil.hh>
  5 +#include <iostream>
  6 +#include <string.h>
  7 +#include <stdlib.h>
  8 +
  9 +static char const* whoami = 0;
  10 +
  11 +// This is a simple StreamDataProvider that writes image data for an
  12 +// orange square of the given width and height.
  13 +class ImageProvider: public QPDFObjectHandle::StreamDataProvider
  14 +{
  15 + public:
  16 + ImageProvider(int width, int height);
  17 + virtual ~ImageProvider();
  18 + virtual void provideStreamData(int objid, int generation,
  19 + Pipeline* pipeline);
  20 + size_t getLength() const;
  21 +
  22 + private:
  23 + int width;
  24 + int height;
  25 +};
  26 +
  27 +ImageProvider::ImageProvider(int width, int height) :
  28 + width(width),
  29 + height(height)
  30 +{
  31 +}
  32 +
  33 +ImageProvider::~ImageProvider()
  34 +{
  35 +}
  36 +
  37 +void
  38 +ImageProvider::provideStreamData(int objid, int generation,
  39 + Pipeline* pipeline)
  40 +{
  41 + for (int i = 0; i < width * height; ++i)
  42 + {
  43 + pipeline->write((unsigned char*)"\xff\x7f\x00", 3);
  44 + }
  45 + pipeline->finish();
  46 +}
  47 +
  48 +size_t
  49 +ImageProvider::getLength() const
  50 +{
  51 + return 3 * width * height;
  52 +}
  53 +
  54 +void usage()
  55 +{
  56 + std::cerr << "Usage: " << whoami << " filename" << std::endl
  57 + << "Creates a simple PDF and writes it to filename" << std::endl;
  58 + exit(2);
  59 +}
  60 +
  61 +static QPDFObjectHandle createPageContents(QPDF& pdf, std::string const& text)
  62 +{
  63 + // Create a stream that displays our image and the given text in
  64 + // our font.
  65 + std::string contents =
  66 + "BT /F1 24 Tf 72 720 Td (" + text + ") Tj ET\n"
  67 + "q 144 0 0 144 234 324 cm /Im1 Do Q\n";
  68 + PointerHolder<Buffer> b = new Buffer(contents.length());
  69 + unsigned char* bp = b->getBuffer();
  70 + memcpy(bp, (char*)contents.c_str(), contents.length());
  71 + return QPDFObjectHandle::newStream(&pdf, b);
  72 +}
  73 +
  74 +QPDFObjectHandle newName(std::string const& name)
  75 +{
  76 + return QPDFObjectHandle::newName(name);
  77 +}
  78 +
  79 +QPDFObjectHandle newInteger(int val)
  80 +{
  81 + return QPDFObjectHandle::newInteger(val);
  82 +}
  83 +
  84 +static void create_pdf(char const* filename)
  85 +{
  86 + QPDF pdf;
  87 +
  88 + // Start with an empty PDF that has no pages or non-required objects.
  89 + pdf.emptyPDF();
  90 +
  91 + // Add an indirect object to contain a font descriptor for the
  92 + // built-in Helvetica font.
  93 + QPDFObjectHandle font = pdf.makeIndirectObject(
  94 + QPDFObjectHandle::newDictionary());
  95 + font.replaceKey("/Type", newName("/Font"));
  96 + font.replaceKey("/Subtype", newName("/Type1"));
  97 + font.replaceKey("/Name", newName("/F1"));
  98 + font.replaceKey("/BaseFont", newName("/Helvetica"));
  99 + font.replaceKey("/Encoding", newName("/WinAnsiEncoding"));
  100 +
  101 + // Create a stream to encode our image. We don't have to set the
  102 + // length or filters. QPDFWriter will fill in the length and
  103 + // compress the stream data using FlateDecode by default.
  104 + QPDFObjectHandle image = QPDFObjectHandle::newStream(&pdf);
  105 + QPDFObjectHandle image_dict = image.getDict();
  106 + image_dict.replaceKey("/Type", newName("/XObject"));
  107 + image_dict.replaceKey("/Subtype", newName("/Image"));
  108 + image_dict.replaceKey("/ColorSpace", newName("/DeviceRGB"));
  109 + image_dict.replaceKey("/BitsPerComponent", newInteger(8));
  110 + image_dict.replaceKey("/Width", newInteger(100));
  111 + image_dict.replaceKey("/Height", newInteger(100));
  112 + // Provide the stream data.
  113 + ImageProvider* p = new ImageProvider(100, 100);
  114 + PointerHolder<QPDFObjectHandle::StreamDataProvider> provider(p);
  115 + image.replaceStreamData(provider,
  116 + QPDFObjectHandle::newNull(),
  117 + QPDFObjectHandle::newNull(),
  118 + p->getLength());
  119 +
  120 + // Create direct objects as needed by the page dictionary.
  121 + QPDFObjectHandle procset = QPDFObjectHandle::newArray();
  122 + procset.appendItem(newName("/PDF"));
  123 + procset.appendItem(newName("/Text"));
  124 + procset.appendItem(newName("/ImageC"));
  125 +
  126 + QPDFObjectHandle rfont = QPDFObjectHandle::newDictionary();
  127 + rfont.replaceKey("/F1", font);
  128 +
  129 + QPDFObjectHandle xobject = QPDFObjectHandle::newDictionary();
  130 + xobject.replaceKey("/Im1", image);
  131 +
  132 + QPDFObjectHandle resources = QPDFObjectHandle::newDictionary();
  133 + resources.replaceKey("/ProcSet", procset);
  134 + resources.replaceKey("/Font", rfont);
  135 + resources.replaceKey("/XObject", xobject);
  136 +
  137 + QPDFObjectHandle mediabox = QPDFObjectHandle::newArray();
  138 + mediabox.appendItem(newInteger(0));
  139 + mediabox.appendItem(newInteger(0));
  140 + mediabox.appendItem(newInteger(612));
  141 + mediabox.appendItem(newInteger(792));
  142 +
  143 + // Create the page content stream
  144 + QPDFObjectHandle contents = createPageContents(
  145 + pdf, "Look at the pretty, orange square!");
  146 +
  147 + // Create the page dictionary
  148 + QPDFObjectHandle page = pdf.makeIndirectObject(
  149 + QPDFObjectHandle::newDictionary());
  150 + page.replaceKey("/Type", newName("/Page"));
  151 + page.replaceKey("/MediaBox", mediabox);
  152 + page.replaceKey("/Contents", contents);
  153 + page.replaceKey("/Resources", resources);
  154 +
  155 + // Add the page to the PDF file
  156 + pdf.addPage(page, true);
  157 +
  158 + // Write the results. A real application would not call
  159 + // setStaticID here, but this example does it for the sake of its
  160 + // test suite.
  161 + QPDFWriter w(pdf, filename);
  162 + w.setStaticID(true); // for testing only
  163 + w.write();
  164 +}
  165 +
  166 +int main(int argc, char* argv[])
  167 +{
  168 + whoami = QUtil::getWhoami(argv[0]);
  169 +
  170 + // For libtool's sake....
  171 + if (strncmp(whoami, "lt-", 3) == 0)
  172 + {
  173 + whoami += 3;
  174 + }
  175 + if (argc != 2)
  176 + {
  177 + usage();
  178 + }
  179 + char const* filename = argv[1];
  180 +
  181 + try
  182 + {
  183 + create_pdf(filename);
  184 + }
  185 + catch (std::exception& e)
  186 + {
  187 + std::cerr << e.what() << std::endl;
  188 + exit(2);
  189 + }
  190 +
  191 + return 0;
  192 +}
... ...
examples/qtest/create.test 0 โ†’ 100644
  1 +#!/usr/bin/env perl
  2 +require 5.008;
  3 +use warnings;
  4 +use strict;
  5 +
  6 +chdir("create") or die "chdir testdir failed: $!\n";
  7 +
  8 +require TestDriver;
  9 +
  10 +cleanup();
  11 +
  12 +my $td = new TestDriver('create');
  13 +
  14 +$td->runtest("create a simple PDF",
  15 + {$td->COMMAND => "pdf-create a.pdf"},
  16 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  17 +
  18 +$td->runtest("check",
  19 + {$td->FILE => "a.pdf"},
  20 + {$td->FILE => "orange-square.pdf"});
  21 +
  22 +cleanup();
  23 +
  24 +$td->report(2);
  25 +
  26 +sub cleanup
  27 +{
  28 + unlink "a.pdf";
  29 +}
... ...
examples/qtest/create/orange-square.pdf 0 โ†’ 100644
No preview for this file type
include/qpdf/QPDF.hh
... ... @@ -75,7 +75,8 @@ class QPDF
75 75 // objects can be added to the file in the normal way, and the
76 76 // trailer and document catalog can be mutated. Calling this
77 77 // method is equivalent to calling processFile on an equivalent
78   - // PDF file.
  78 + // PDF file. See the pdf-create.cc example for a demonstration of
  79 + // how to use this method to create a PDF file from scratch.
79 80 QPDF_DLL
80 81 void emptyPDF();
81 82  
... ...