File pdf.cc of Package fax2pdf
// -*- mode: c++ -*-
//**********************************************************************
//
// Copyright (c) 2000 by Peter Stamfest <peter@stamfest.at>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//**********************************************************************
static char *cvsid = "@(#)$Id: pdf.cc,v 1.3 2002/06/18 04:26:35 stamfest Exp $";
static short int ___n = cvsid - (char*)___n; // avoid a warning for
// unused cvsid
#include <zlib.h>
#include "pdf.h"
//**********************************************************************
// class obj
//**********************************************************************
int obj::nextnr = 1;
int obj::objcnt = 0;
xref obj::XRef;
obj::obj() :
onr(0),
gennr(0)
{
objcnt++;
}
obj::~obj()
{
objcnt--;
}
unsigned long obj::getnr()
{
onr = nextnr++;
return onr;
}
void obj::assign_numbers()
{
if (onr == 0) getnr();
}
target &obj::out(target &o)
{
return id(o);
}
//**********************************************************************
// class xref
//**********************************************************************
void xref::add(const obj &ob, target &o) {
if (ob.onr >= entries.capacity())
entries.resize(2 *ob.onr, 0);
if (ob.onr > maxseen) maxseen = ob.onr;
xrefentry *x = new xrefentry(ob.onr, ob.gennr, o.report());
entries[ob.onr] = x;
}
target &xref::out(target &o)
{
char buf[80];
o.out("xref\n");
sprintf(buf, "0 %ld\n0000000000 65535 f \n", maxseen + 1);
o.out(buf);
int N = entries.capacity();
int i;
for (i = 0 ; i < N ; i++) {
xrefentry *x = (xrefentry*)(entries[i]);
if (!x) continue;
sprintf(buf, "%010ld %05ld n \n", x->offset, x->gennr);
o.out(buf);
}
return o;
}
xref::~xref()
{
int N = entries.capacity();
int i;
for (i = 0 ; i < N ; i++) {
xrefentry *x = (xrefentry*)(entries[i]);
if (!x) continue;
entries[i] = 0;
delete x;
}
}
//**********************************************************************
// class document
//**********************************************************************
target &document::head(target &o)
{
o.out((unsigned char*) "%PDF-1.2\n% \231\211\221\222\n");
return o;
}
target &document::out(target &o)
{
char buf[1000];
assign_numbers();
cat.out(o, thepages);
thepages.out(o);
theinfo.out(o);
unsigned long xrefpos = o.report();
obj::XRef.out(o);
// o.out("xref\n"
// "0 1\n"
// "0000000000 65535 f\n");
sprintf(buf, "trailer\n"
"<<\n"
"/Size %ld\n"
"/Root ",
obj::XRef.maxseen + 1);
o.out(buf);
cat.ind(o);
o.out("\n/Info ");
theinfo.ind(o);
o.out("\n"
">>\n"
"startxref\n");
sprintf(buf, "%ld\n%%%%EOF\n", xrefpos);
o.out(buf);
return o;
}
void document::assign_numbers()
{
cat.assign_numbers();
thepages.assign_numbers();
theinfo.assign_numbers();
}
// needed because of virtual functions
document::~document()
{
}
//**********************************************************************
// class image
//**********************************************************************
int image::cnt = 0;
image::image(int width, int height, int depth,
int xx, int yy, int ssx, int ssy, unsigned char *d, long l) :
w(width),
h(height),
bpp(depth),
x(xx),
y(yy),
sx(ssx),
sy(ssy),
data(d),
leng(l),
flate(true),
copied(false)
{
if (flate) {
data = new unsigned char[l+255];
// cerr << "l="<< l << endl;
z_stream Z;
Z.zalloc = Z_NULL;
Z.zfree = Z_NULL;
deflateInit(&Z, Z_DEFAULT_COMPRESSION);
Z.next_in = d;
Z.avail_in = leng;
Z.next_out = data;
Z.avail_out = l+255;
int status = deflate(&Z, Z_FINISH);
if (status != Z_STREAM_END) {
cerr << "flate encoding error" << endl;
}
// cerr << "in=" << Z.total_in << " out=" << Z.total_out << endl;
unsigned char *t = new unsigned char[Z.total_out];
memcpy(t, data, Z.total_out);
delete [] data;
data = t;
leng = Z.total_out;
deflateEnd(&Z);
copied = true;
}
}
target &image::xobject_resource(target &o) {
char buf[255];
sprintf(buf, "/%s ", getname().c_str());
o.out(buf);
return ind(o);
};
long image::stream_out(target &o) {
unsigned long l = o.report();
if (flate) {
o.out(data, leng);
o.out("\n");
} else {
char buf[1000];
char m[17] = "0123456789ABCDEF";
char *b;
long ll;
for (ll = 0 , b = buf ; ll < leng ; ll++) {
// printf("X %d %d\n", ll, data[ll]);
if (ll % 32 == 31) {
*b++ = (char) '\n';
*b++ = (char) 0;
o.out(buf);
b = buf;
}
*b++ = m[data[ll]/16];
*b++ = m[data[ll]%16];
}
if (b - buf) {
*b++ = '\n';
*b++ = 0;
o.out(buf);
}
}
return o.report() - l;
};
target &image::out(target &o) {
char buf[1000];
obj len;
len.assign_numbers();
id(o);
sprintf(buf, "<<\n"
"/Type /XObject\n"
"/Subtype /Image\n"
"/Name /%s\n"
"/Width %d\n"
"/Height %d\n"
"/BitsPerComponent %d\n"
"/Interpolate true\n"
"/ColorSpace /DeviceGray\n"
"/Filter %s\n"
"/Length %s >>\n"
"stream\n", getname().c_str(), w, h, bpp,
flate ? "/FlateDecode" : "/ASCIIHexDecode",
len.ind().c_str());
o.out(buf);
long n = stream_out(o);
o.out("endstream\nendobj\n");
len.id(o);
sprintf(buf, "%ld\nendobj\n", n);
o.out(buf);
return o;
}
image::~image()
{
if (data && copied) delete [] data;
data = 0;
leng = 0;
}
target &image::contents_out(target &o) {
char buf[255];
sprintf(buf,
"1 0 0 1 %d %d cm\n"
"%d 0 0 %d 0 0 cm\n"
"/%s Do\n", x, y, sx, sy, getname().c_str());
return o.out(buf);
}
//**********************************************************************
// class catalog
//**********************************************************************
target &catalog::out(target &o, pages &p)
{
id(o);
o.out("<<\n"
"/Type /Catalog\n"
"/Pages ");
p.ind(o);
o.out("\n>>\nendobj\n");
return o;
}
catalog::~catalog()
{
}
//**********************************************************************
// class pages
//**********************************************************************
target &pages::out(target &o)
{
id(o);
char buf[1000];
sprintf(buf, "<<\n"
"/Type /Pages\n"
"/Count %d\n"
"/Kids [", thepages.size());
o.out(buf);
vector<void*>::iterator I;
foreach(I, thepages) {
((page*)(*I))->ind(o);
}
o.out("]\n>>\nendobj\n");
foreach(I, thepages) {
((page*)(*I))->out(o, *this);
}
return o;
}
void pages::assign_numbers()
{
if (onr == 0) getnr();
vector<void*>::iterator I;
foreach(I, thepages) {
((pages*)(*I))->assign_numbers();
}
}
pages::~pages()
{
vector<void*>::iterator I;
foreach(I, thepages) {
delete ((pages*)(*I));
}
}
//**********************************************************************
// class mark
//**********************************************************************
mark::~mark()
{
}
//**********************************************************************
// class page
//**********************************************************************
page::page(media_type m = a4) : streamed(false)
{
set_media(m);
}
void page::set_media(media_type m)
{
switch(m) {
case a4:
mediax = 595;
mediay = 824;
cutx = 14;
cuty = 14;
break;
case letter:
mediax = 612;
mediay = 792;
cutx = 14;
cuty = 14;
break;
case legal:
mediax = 612;
mediay = 1008;
cutx = 14;
cuty = 14;
break;
}
}
target &page::out(target &o, pages &parent)
{
if (streamed) return o;
streamed = true;
char buf[1000];
o.out("% page::out\n");
vector<void*>::iterator I;
obj contents;
obj clen;
contents.assign_numbers();
clen.assign_numbers();
id(o);
sprintf(buf, "<<\n"
"/Type /Page\n"
"/MediaBox [0 0 %d %d]\n"
"/CropBox [%d %d %d %d]\n"
"/Parent ", mediax, mediay,
cutx - 1, cuty - 1, mediax - cutx + 1, mediay - cuty + 1);
o.out(buf);
parent.ind(o);
o.out("\n"
"/Resources\n"
"<<\n"
"/ProcSet [/PDF /ImageB]\n"
"/XObject\n"
"<<\n");
foreach(I, themarks) {
((mark*)(*I))->xobject_resource(o);
}
o.out("\n>>\n");
o.out(">>\n"
"/Contents ");
contents.ind(o);
o.out("\n>>\n"
"endobj\n");
contents.id(o);
o.out("<<\n"
"/Length ");
clen.ind(o);
o.out(">>\n"
"stream\n");
unsigned long l = o.report();
foreach(I, themarks) {
((mark*)(*I))->contents_out(o);
}
l = o.report() - l;
o.out("endstream\n"
"endobj\n");
clen.id(o);
sprintf(buf, "%ld\nendobj\n", l);
o.out(buf);
foreach(I, themarks) {
((mark*)(*I))->out(o);
}
// after streaming all the marks, we do not need them any more...
foreach(I, themarks) {
delete ((mark*)(*I));
}
themarks.erase(themarks.begin(), themarks.end());
return o;
}
void page::done(target &o, pages &p)
{
out(o, p);
vector<void*>::iterator I;
foreach(I, themarks) {
delete ((mark*)(*I));
}
themarks.erase(themarks.begin(), themarks.end());
}
void page::assign_numbers()
{
if (onr == 0) getnr();
vector<void*>::iterator I;
foreach(I, themarks) {
((mark*)(*I))->assign_numbers();
}
}
page::~page()
{
vector<void*>::iterator I;
foreach(I, themarks) {
delete ((mark*)(*I));
}
}
//**********************************************************************
// class info
//**********************************************************************
target &info::out(target &o)
{
id(o);
o.out("<<\n");
vector<void*>::iterator I;
foreach(I, theinfos) {
const kv &inf = *((kv*)(*I));
o.out(inf.k.c_str());
o.out(" ");
o.out(inf.v.c_str());
o.out("\n");
}
o.out(">>\nendobj\n");
return o;
}
void info::addinfo(const string &k, const string &v)
{
theinfos.push_back(new kv(k,v));
}
info::~info()
{
vector<void*>::iterator I;
foreach(I, theinfos) {
delete ((kv*)(*I));
}
}
#if 0
main()
{
document d;
target o(cout);
page *p = new page;
d.addpage(p);
unsigned char dd[] = {
0xc0, 0x40, 0x20, 0x10, 8, 4, 2, 1, 0
};
p->addmark(new image(9,8,1,100,100,200,200,dd,sizeof(dd)));
// p->addmark(new image(24,23,1,2,2,1,1));
d.out(o);
}
#endif