/* drvPDF.c : This file is part of pstoedit Backend for PDF(TM) format Copyright (C) 1993,1994,1995,1996 Wolfgang Glunz, Wolfgang.Glunz@zfe.siemens.de 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. */ /* The PDF format and the corresponding operators are copyrighted by Adobe Systems, Inc. */ #include "drvpdf.h" #include #include #include #include #include #include #include // for sin and cos #include static float rnd(const float f,const float roundnumber) { return ((long int) ( (f * roundnumber) + 0.5 )) / roundnumber; } static inline float RND3(const float f) { return rnd(f,1000);} const char *PDFFonts[] = { // predefined Fonts (see page 64 PDF Ref. Manual ) "Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique", "Helvetica", "Helvetica-Bold", "Helvetica-Oblique", "Helvetica-BoldOblique", "Symbol", "Times-Roman", "Times-Bold", "Times-Italic", "Times-BoldItalic", "ZapfDingbats" }; const unsigned int numberOfFonts = sizeof(PDFFonts)/(sizeof(char *)) ; unsigned int drvPDF::newobject() { currentobject++; if (currentobject >= maxobjects) { cerr << "Sorry, too many objects in this file" << endl; exit(1); } startPosition[currentobject] = outf.tellp() ; outf << currentobject << " 0 obj" << endl; return currentobject; } void drvPDF::endobject() { outf << "endobj" << endl; } static streampos newlinebytes = 1; // how many bytes are a newline (1 or 2) drvPDF::drvPDF(ostream & theoutStream): currentobject(0), pagenr(0), drvbase(theoutStream,1,1,0), buffer(tempFile.asOutput()) { const char * header = "%PDF-1.1"; outf << header << endl; newlinebytes = outf.tellp() - strlen(header); } drvPDF::~drvPDF() { // print trailer unsigned int outlines = newobject(); outf << "<<" << endl; outf << "/Type /Outlines" << endl; outf << "/Count 0" << endl; outf << ">>" << endl; endobject(); unsigned int firstFontObject = currentobject + 1; // Now define all the 14 standard fonts for (unsigned int f = 0 ; f < numberOfFonts ; f++ ) { unsigned int font = newobject(); outf << "<<" << endl; outf << "/Type /Font" << endl; outf << "/Subtype /Type1" << endl; outf << "/Name /F" << f << endl; outf << "/BaseFont /" << PDFFonts[f] << endl; outf << ">>" << endl; } unsigned int catalog = newobject(); unsigned int pages = currentobject + 1; // will be next; outf << "<<" << endl; outf << "/Type /Catalog" << endl; outf << "/Pages " << pages << " 0 R" << endl; outf << "/Outlines " << outlines << " 0 R" << endl; outf << ">>" << endl; endobject(); unsigned int nrOfPages = pagenr; pages = newobject(); unsigned int resources = currentobject + 1; // will be next; outf << "<<" << endl; outf << "/Type /Pages" << endl; outf << "/Count " << nrOfPages << endl; outf << "/Kids [ "; for (unsigned int i = 1 ; i <= nrOfPages; i++ ) { outf << i + pages << " 0 R " ; } outf << " ] " << endl; outf << "/MediaBox [0 0 612 792 ]" << endl; outf << "/Resources " ; #ifdef viareference outf << resources << " 0 R"<< endl; outf << ">>" << endl; endobject(); resources = newobject(); #endif outf << "<<" << endl; outf << "/ProcSet [ /PDF /Text ]" << endl; outf << "/Font <<" << endl; for (unsigned int f2 = 0 ; f2 < numberOfFonts; f2++ ) { outf << "/F" << f2 << " " << f2 + firstFontObject << " 0 R" << endl; } outf << ">>" << endl; // closing /Font outf << ">>" << endl; // closing /Resources dictionary #ifndef viareference outf << ">>" << endl; #endif endobject(); // Now write the Page parts for each page for (unsigned int j = 1 ; j <= nrOfPages; j++ ) { unsigned int pageobject = newobject(); outf << "<<" << endl; outf << "/Type /Page" << endl; outf << "/Parent " << pages << " 0 R" << endl; outf << "/Contents " << j << " 0 R" << endl; outf << ">>" << endl; endobject(); } unsigned int infoobject = newobject(); outf << "<<" << endl; time_t t = time(0); struct tm *localt = localtime(&t); outf << "/CreationDate (D:" << setw(4) << localt->tm_year + 1900 << setw(2) << setfill('0') << localt->tm_mon + 1 << setw(2) << setfill('0') << localt->tm_mday << setw(2) << setfill('0') << localt->tm_hour << setw(2) << setfill('0') << localt->tm_min << setw(2) << setfill('0') << localt->tm_sec << ")" << endl; outf << "/Producer (pstoedit by Wolfgang.Glunz@zfe.siemens.de)" << endl; outf << ">>" << endl; endobject(); streampos xrefbegin = outf.tellp() ; outf << "xref" << endl; outf << "0 " << currentobject+1 << endl; outf << "0000000000 65535 f" ; if (newlinebytes == 1) { outf << " "; } outf << endl; for (unsigned int x = 1; x <= currentobject ; x++ ) { outf.width(10); outf.fill('0'); outf << startPosition[x] << " 00000 n"; if (newlinebytes == 1) { outf << " "; } outf << endl; } outf << "trailer" << endl; outf << "<<" << endl; outf << "/Size " << currentobject+1 << endl; outf << "/Info " << infoobject << " 0 R" << endl; outf << "/Root " << catalog << " 0 R" << endl; outf << ">>" << endl; outf << "startxref" << endl; outf << xrefbegin << endl; outf << "%%EOF" << endl; } void drvPDF::print_coords() { // buffer.precision(3); // buffer.setf(ios::fixed); // buffer.width(0); // to force minimal width // buffer.unsetf(ios::showpoint); for (unsigned int n = 0; n < numberOfElementsInPath(); n++) { const basedrawingelement & elem = pathElement(n); switch (elem.getType()) { case moveto: { const Point & p = elem.getPoint(0); buffer << RND3(p.x_ + x_offset) << " " << RND3(p.y_ + y_offset) << " " ; buffer << "m " << endl; } break; case lineto: { const Point & p = elem.getPoint(0); buffer << RND3(p.x_ + x_offset) << " " << RND3(p.y_ + y_offset) << " " ; buffer << "l " << endl; } break; case closepath: buffer << "h " << endl; break; case curveto:{ for (unsigned int cp = 0 ; cp < 3; cp++ ) { const Point & p = elem.getPoint(cp); buffer << RND3(p.x_ + x_offset) << " " << RND3(p.y_ + y_offset) << " " ; } buffer << "c " << endl; } break; default: cerr << "Fatal: unexpected case in drvpdf " << endl; abort(); break; } } } void drvPDF::open_page() { unsigned int currentpage = newobject(); pagenr++; // provide a temp stream tempFile.asOutput(); buffer << "BT" << endl; } void drvPDF::close_page() { buffer << "ET" << endl; streampos endpos = buffer.tellp(); outf << "<<" << endl; outf << "/Length " << endpos << endl; outf << ">>" << endl; outf << "stream" << endl; ifstream & instream = tempFile.asInput(); copy_file(instream,outf); // int ret = 0; // while ( (ret = instream.get()) != EOF ) { // outf << (char) ret ; // } outf << "endstream" << endl; endobject(); } static int getFontNumber(const char * const fontname) { unsigned int fntlength = strlen(fontname); for (unsigned int i=0; i < numberOfFonts; i++) { unsigned int pdfFntLengh = strlen(PDFFonts[i]); if (fntlength == pdfFntLengh ) { if (strncmp(fontname,PDFFonts[i],fntlength) == 0) { return i; } } } return -1; } static int getSubStringFontNumber(const char * const fontname) { // searches for a font name which is the longest substring of the current font name int index = -1; int longest = -1; int fntlength = strlen(fontname); for (unsigned int i=0; i < numberOfFonts; i++) { int pdfFntLength = strlen(PDFFonts[i]); if (fntlength >= pdfFntLength ) { if (strncmp(fontname,PDFFonts[i],pdfFntLength) == 0) { if (pdfFntLength > longest) { longest = pdfFntLength; index = i; } } } } return index; } void drvPDF::show_text(const TextInfo & textinfo) { const float toRadians = 3.14159265359 / 180.0; const float angleInRadians = textinfo.currentFontAngle * toRadians; int PDFFontNum = getFontNumber(textinfo.currentFontName); if (PDFFontNum == -1) { PDFFontNum = getSubStringFontNumber(textinfo.currentFontName); if (PDFFontNum == -1) { cerr << "Warning, unsupported font " << textinfo.currentFontName << ", using Courier instead" << endl; PDFFontNum = 0; // Courier } else { cerr << "Warning, unsupported font " << textinfo.currentFontName << ", using " << PDFFonts[PDFFontNum] << " instead" << endl; } } buffer << "/F" << PDFFontNum << " 1 Tf" << endl; // use size 1 and scale via Tm const float cosphi = cos(angleInRadians); const float sinphi = sin(angleInRadians); const float Sx = textinfo.currentFontSize; const float Sy = textinfo.currentFontSize; // OK, we could get the real transformation matrix from the interpreter, // but this approximation should do it in most cases. // buffer.precision(3); // buffer.setf(ios::fixed); // buffer.width(0); // to force minimal width // buffer.unsetf(ios::showpoint); buffer << RND3(Sx * cosphi) << " " << RND3(Sx * sinphi) << " " << RND3(-Sy * sinphi) << " " << RND3(Sy * cosphi) << " " << RND3(textinfo.x + x_offset) << " " << RND3(textinfo.y + y_offset) << " Tm" << endl; buffer << RND3(textinfo.currentR) << " " << RND3(textinfo.currentG) << " " << RND3(textinfo.currentB) << " rg" << endl; buffer << "(" ; const char * start_of_text = textinfo.thetext; while (*start_of_text) { if ((*start_of_text == '(') || (*start_of_text == ')')) { buffer << '\\'; } buffer << *start_of_text; start_of_text++; } buffer << ") Tj" << endl; } void drvPDF::show_path() { // is done in drvbase !! add_to_page(); char * setrgbcolor; char * drawingop; switch (currentShowType() ) { case drvbase::stroke : // it's a stroke drawingop = "S"; setrgbcolor = "RG"; break; case drvbase::fill : drawingop = "f"; setrgbcolor = "rg"; break; case drvbase::eofill : drawingop = "f*"; setrgbcolor = "rg"; break; default: // cannot happen cerr << "unexpected ShowType " << (int) currentShowType() << endl; exit(1); break; } // buffer.precision(3); // buffer.setf(ios::fixed); // buffer.width(0); // to force minimal width // buffer.unsetf(ios::showpoint); buffer << "% path " << pathnumber << endl; buffer << RND3(currentR()) << " " << RND3(currentG()) << " " << RND3(currentB()) << " " << setrgbcolor << endl; buffer << currentLineWidth() << " w" << endl; print_coords(); buffer << drawingop << endl; } void drvPDF::show_rectangle( const float llx, const float lly, const float urx, const float ury) { // just do show_polyline for a first guess show_path(); }