pdf-mini.h revision 3027d46e220d40848a2a56135aa0cbe2384c2ae4
/** \file
* PDF Mini library.
*/
/*
* Authors:
* Ulf Erikson <ulferikson@users.sf.net>
*
* Minimalistic library to support generation of PDF files.
*
* Copyright (C) 2006 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#ifndef PDF_MINI_H_SEEN
#define PDF_MINI_H_SEEN
class PdfFile;
class PdfXref;
class PdfObject;
enum pdf_resource_type { pdf_none, pdf_extgstate, pdf_colorspace, pdf_pattern, pdf_shading, pdf_xobject, pdf_font, pdf_properties };
char pdf_res_short[][5] = { "", "GS", "CS", "Pa", "Sh", "XObj", "F", "Prop" };
char pdf_res_long[][20] = { "", "ExtGState", "ColorSpace", "Pattern",
"Shading", "XObject", "Font", "Properties" };
class PdfFile {
public:
PdfFile(FILE *_stream);
~PdfFile();
long puts(const char *str);
long puts(std::string ss) { return puts(ss.c_str()); }
long puts(Inkscape::SVGOStringStream &os) { return puts(os.str().c_str()); }
long puts(PdfXref *table);
long puts(PdfObject *object);
long tell();
PdfObject *begin_document(double version = 1.2);
void end_document(PdfObject *doc_info);
PdfObject *begin_page(int x0, int y0, int x1, int y1);
PdfObject *begin_page(int w, int h) { return begin_page(0,0, w, h); }
void end_page(PdfObject *page);
PdfObject *begin_object();
void end_object(PdfObject *object);
PdfObject *begin_resource(enum pdf_resource_type);
void end_resource(PdfObject *resource);
protected:
FILE *fp;
long length;
long stream_pos;
PdfXref *xref;
PdfXref *pages;
PdfXref *resources;
PdfObject *obj_root;
PdfObject *obj_catalog;
PdfObject *obj_info;
PdfObject *obj_resources;
PdfObject *obj_contents;
PdfObject *obj_length;
};
class PdfXref {
public:
PdfXref() { size = 10; table = (long*) malloc(size*sizeof(long)); entries = 0; }
~PdfXref() { free(table); }
long get_size() { return entries; }
long get_entry(long id) { return table[id]; }
long alloc_id() {
entries++;
if (entries >= size) {
size = size*2;
table = (long *) realloc(table, size*sizeof(long));
}
return entries;
}
void update_entry(long id, long offset) {
table[id] = offset;
}
long add_entry(long offset = 0) {
long id = alloc_id();
update_entry(id, offset);
return id;
}
protected:
long *table;
long entries;
long size;
};
class PdfObject : public Inkscape::SVGOStringStream {
public:
PdfObject(long i) : next(NULL) { id = i; setf(std::ios::fixed); }
~PdfObject() {}
long get_id() { return id; }
const char *get_name() { return name.str().c_str(); }
long get_length() { return str().length(); }
void set_type(enum pdf_resource_type type) { name << "/" << pdf_res_short[type] << id; }
PdfObject *next;
protected:
long id;
Inkscape::SVGOStringStream name;
};
PdfFile::PdfFile(FILE *_stream) {
fp = _stream;
xref = new PdfXref();
}
PdfFile::~PdfFile() {
delete xref;
}
long PdfFile::puts(const char *str) {
long res = fprintf(fp, "%s", str);
if (res >= 0) {
length += res;
res = length;
}
return res;
}
long PdfFile::puts(PdfObject *object) {
xref->update_entry( object->get_id(), tell() );
return puts(object->str());
}
long PdfFile::puts(PdfXref *table) {
Inkscape::SVGOStringStream os;
char buffer[32];
int i;
int size = table->get_size() + 1;
os << "xref\n";
os << "0" << " " << size << "\n";
snprintf(buffer, sizeof(buffer), "%010d %05d %c \n", 0, 65535, 'f');
buffer[sizeof(buffer)-1] = 0;
os << buffer;
for (i = 1; i < size; i++) {
snprintf(buffer, sizeof(buffer), "%010d %05d %c \n", (int)table->get_entry(i), 0, 'n');
buffer[sizeof(buffer)-1] = 0;
os << buffer;
}
return puts(os);
}
long PdfFile::tell() {
return length;
}
PdfObject *PdfFile::begin_document(double version) {
Inkscape::SVGOStringStream os;
char bin[5] = {0x80|'B', 0x80|'i', 0x80|'n', 0x80|'!', 0};
length = 0;
pages = new PdfXref();;
os << "%PDF-" << version << "\n";
os << "%" << bin << "\n";
obj_info = begin_object();
*obj_info << "<<\n";
obj_catalog = begin_object();
obj_root = begin_object();
*obj_root << "<<\n";
*obj_root << " /Type /Catalog\n";
*obj_root << " /Pages " << obj_catalog->get_id() << " 0 R\n";
*obj_root << ">>\n";
end_object(obj_root);
puts(os);
puts(obj_root);
return obj_info;
}
void PdfFile::end_document(PdfObject *doc_info) {
Inkscape::SVGOStringStream os;
long startxref;
int i;
*doc_info << ">>\n";
end_object(doc_info);
puts(doc_info);
*obj_catalog << "<<\n"
<< " /Type /Pages\n"
<< " /Count " << pages->get_size() << "\n"
<< " /Kids [\n";
for (i=1; i<=pages->get_size(); i++) {
*obj_catalog << " " << pages->get_entry(i) << " 0 R\n";
}
*obj_catalog << " ]\n"
<< ">>\n";
end_object(obj_catalog);
puts(obj_catalog);
startxref = tell();
puts(xref);
os << "trailer\n"
<< "<<\n"
<< " /Size " << xref->get_size() << "\n"
<< " /Root " << obj_root->get_id() << " 0 R\n"
<< " /Info " << obj_info->get_id() << " 0 R\n"
<< ">>\n";
os << "startxref\n"
<< startxref << "\n";
os << "%%EOF\n";
puts(os);
delete pages;
delete obj_root;
delete obj_catalog;
delete obj_info;
}
PdfObject *PdfFile::begin_page(int x0, int y0, int x1, int y1) {
Inkscape::SVGOStringStream os;
resources = new PdfXref[11]();
PdfObject *obj_page = begin_object();
obj_resources = begin_object();
obj_contents = begin_object();
obj_length = begin_object();
*obj_page << "<<\n"
<< " /Type /Page\n"
<< " /Parent " << obj_catalog->get_id() << " 0 R\n"
<< " /MediaBox [ " << x0 << " " << y0 << " " << x1 << " " << y1 << " ]\n"
<< " /Resources " << obj_resources->get_id() << " 0 R\n"
<< " /Contents " << obj_contents->get_id() << " 0 R\n"
<< ">>\n";
end_object(obj_page);
puts(obj_page);
pages->add_entry( obj_page->get_id() );
*obj_contents << "<<\n"
<< " /Length " << obj_length->get_id() << " 0 R\n"
<< ">>\n"
<< "stream\n";
stream_pos = obj_contents->get_length();
return obj_contents;
}
void PdfFile::end_page(PdfObject *page) {
long stream_length = page->get_length() - stream_pos;
*page << "endstream\n";
end_object(page);
puts(page);
*obj_length << stream_length << "\n";
end_object(obj_length);
puts(obj_length);
*obj_resources << "<<\n";
*obj_resources << " /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n";
int res;
for (res=pdf_extgstate; res<=pdf_properties; res++) {
if (resources[res].get_size()) {
*obj_resources << " /" << pdf_res_long[res] << "\n";
*obj_resources << " <<\n";
int i;
for (i=1; i<=resources[res].get_size(); i++) {
int id = resources[res].get_entry(i);
*obj_resources << " /"
<< pdf_res_short[res] << id
<< " " << id << " 0 R\n";
}
*obj_resources << " >>\n";
}
}
*obj_resources << ">>\n";
end_object(obj_resources);
puts(obj_resources);
PdfObject *o = obj_resources->next;
while (o) {
PdfObject *t = o->next;
puts(o);
delete o;
o = t;
}
delete obj_resources;
delete obj_length;
delete obj_contents;
delete[] resources;
}
PdfObject *PdfFile::begin_object() {
long id = xref->add_entry();
PdfObject *obj = new PdfObject(id);
*obj << id << " 0 obj\n";
return obj;
}
void PdfFile::end_object(PdfObject *object) {
*object << "endobj\n";
}
PdfObject *PdfFile::begin_resource(enum pdf_resource_type res) {
PdfObject *obj = begin_object();
if (res != pdf_none) {
resources[res].add_entry(obj->get_id());
obj->set_type(res);
}
obj->next = obj_resources->next;
obj_resources->next = obj;
return obj;
}
void PdfFile::end_resource(PdfObject *resource) {
end_object(resource);
}
#endif /* !PDF_MINI_H_SEEN */