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
openSUSE Build Service is sponsored by