mirror of
https://github.com/qpdf/qpdf.git
synced 2024-06-04 19:30:53 +00:00
Add pdf-from-scratch example
This commit is contained in:
parent
bf059a6001
commit
ffb96ee17e
|
@ -1,5 +1,9 @@
|
||||||
2012-06-22 Jay Berkenbilt <ejb@ql.org>
|
2012-06-22 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
* examples/pdf-create.cc: Provide an example of creating a PDF
|
||||||
|
from scratch. This simple PDF has a single page with some text
|
||||||
|
and an image.
|
||||||
|
|
||||||
* Add empty QPDFObjectHandle factories for array and dictionary.
|
* Add empty QPDFObjectHandle factories for array and dictionary.
|
||||||
With PDF-from-scratch capability, it is useful to be able to
|
With PDF-from-scratch capability, it is useful to be able to
|
||||||
create empty arrays and dictionaries and add keys to them.
|
create empty arrays and dictionaries and add keys to them.
|
||||||
|
|
2
TODO
2
TODO
|
@ -42,8 +42,6 @@ Next
|
||||||
contents to make sure the file is really correct. We need to
|
contents to make sure the file is really correct. We need to
|
||||||
test normal writing and linearization.
|
test normal writing and linearization.
|
||||||
|
|
||||||
* Consider adding an example that uses the page APIs, or update the
|
|
||||||
documentation to refer the user to the test suite.
|
|
||||||
|
|
||||||
Soon
|
Soon
|
||||||
====
|
====
|
||||||
|
|
|
@ -3,7 +3,8 @@ BINS_examples = \
|
||||||
pdf-mod-info \
|
pdf-mod-info \
|
||||||
pdf-npages \
|
pdf-npages \
|
||||||
pdf-double-page-size \
|
pdf-double-page-size \
|
||||||
pdf-invert-images
|
pdf-invert-images \
|
||||||
|
pdf-create
|
||||||
CBINS_examples = pdf-linearize
|
CBINS_examples = pdf-linearize
|
||||||
|
|
||||||
TARGETS_examples = $(foreach B,$(BINS_examples) $(CBINS_examples),examples/$(OUTPUT_DIR)/$(call binname,$(B)))
|
TARGETS_examples = $(foreach B,$(BINS_examples) $(CBINS_examples),examples/$(OUTPUT_DIR)/$(call binname,$(B)))
|
||||||
|
|
192
examples/pdf-create.cc
Normal file
192
examples/pdf-create.cc
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
#include <qpdf/QPDF.hh>
|
||||||
|
#include <qpdf/QPDFWriter.hh>
|
||||||
|
#include <qpdf/QPDFObjectHandle.hh>
|
||||||
|
#include <qpdf/QUtil.hh>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
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<Buffer> 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<QPDFObjectHandle::StreamDataProvider> 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;
|
||||||
|
}
|
29
examples/qtest/create.test
Normal file
29
examples/qtest/create.test
Normal file
|
@ -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";
|
||||||
|
}
|
BIN
examples/qtest/create/orange-square.pdf
Normal file
BIN
examples/qtest/create/orange-square.pdf
Normal file
Binary file not shown.
|
@ -75,7 +75,8 @@ class QPDF
|
||||||
// objects can be added to the file in the normal way, and the
|
// objects can be added to the file in the normal way, and the
|
||||||
// trailer and document catalog can be mutated. Calling this
|
// trailer and document catalog can be mutated. Calling this
|
||||||
// method is equivalent to calling processFile on an equivalent
|
// method is equivalent to calling processFile on an equivalent
|
||||||
// PDF file.
|
// PDF file. See the pdf-create.cc example for a demonstration of
|
||||||
|
// how to use this method to create a PDF file from scratch.
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void emptyPDF();
|
void emptyPDF();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user