home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 5 Edit
/
05-Edit.zip
/
pstoedit.zip
/
source.zip
/
pstoedit.2.50
/
src
/
drvbase.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1996-11-22
|
18KB
|
659 lines
/*
drvbase.cpp : This file is part of pstoedit
Basic, driver independent output routines
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.
*/
#include <stdlib.h>
#include <iostream.h>
#include <string.h>
#if !defined(unix) && !defined(__unix__) && !defined(__unix)
#include <strstrea.h>
#else
#include <strstream.h>
#endif
#include "drvbase.h"
drvbase::drvbase(const char * driveroptions_p,ostream & theoutStream,
ostream & theerrStream,
bool backendSupportsSubPathes_p,
bool backendSupportsCurveto_p,
bool backendSupportsMerging_p)
: // constructor
backendSupportsSubPathes(backendSupportsSubPathes_p),
backendSupportsCurveto(backendSupportsCurveto_p),
backendSupportsMerging(backendSupportsMerging_p),
d_argc (0),
d_argv (0),
page_empty (1),
outf (theoutStream),
errf (theerrStream),
scale (1.0f),
// set some common defaults
currentDeviceHeight (792.0f * scale),
x_offset (0.0f),
y_offset (0.0f),
currentPageNumber (0),
pathnumber (0),
verbose (false),
domerge (false),
driveroptions (0)
{
// init segment info for first segment
// all others will be updated with each newsegment
verbose = (getenv("PSTOEDITVERBOSE") != 0);
if (verbose) {
errf << "verbose mode turned on\n" << endl;
}
numbers = new float[maxpoints]; // The number stack
nextFreeNumber = 0;
// preparse driveroptions and build d_argc and d_argv
if (driveroptions_p) {
driveroptions = strdup(driveroptions_p);
istrstream optstream(driveroptions,strlen(driveroptions));
const long startOfStream = optstream.tellg();
char currentarg[100];
// first cound number of arguments
while(!optstream.eof()) {
optstream.width(sizeof(currentarg));
optstream >> currentarg;
d_argc++;
}
d_argv = new const char *[d_argc+1];
// now fill d_args array;
optstream.seekg(startOfStream); // reposition to start
optstream.clear();
d_argc=0;
while(!optstream.eof()) {
optstream >> currentarg;
d_argv[d_argc] = strdup(currentarg);
d_argc++;
}
d_argv[d_argc] = 0;
if (verbose) {
errf << "got " << d_argc << " driver arguments" << endl;
for (unsigned int i = 0; i < d_argc ; i++ ) {
errf << "Driver option " << i << ":" << d_argv[i] << endl;
}
}
}
currentPath = &p1;
lastPath = &p2;
outputPath = currentPath;
if ((numbers == 0) || (p1.path == 0) || (p2.path == 0) ) {
errf << "new failed in drvbase::drvbase " << endl;
exit(1);
}
setCurrentFontName("Courier",1);
setCurrentFontFamilyName("Courier");
setCurrentFontWeight("Regular");
setCurrentFontFullName("Courier");
setCurrentFontSize(10.0f);
}
drvbase::~drvbase() {
delete [] numbers;
currentPath = 0;
lastPath = 0;
outputPath = 0;
for (unsigned int i = 0; i < d_argc ; i++ ) {
free((char *)d_argv[i]); // use free because of strdup uses malloc
}
delete [] d_argv;
if (driveroptions) {
free((char *) driveroptions);
}
yylexcleanup();
}
void drvbase::run(bool merge)
{
domerge = false; // default
if (merge) {
if ( backendSupportsMerging ) {
domerge = true;
} else {
errf << "the selected backend does not support merging, -merge ignored" << endl;
}
}
yylex();
outputPath->clear(); // define past the end path as empty
dumpPath(); // dump last path
// close page (if no explicit showpage was done)
if (!page_empty) {
close_page();
}
page_empty = 1;
}
bool drvbase::pathsCanBeMerged(const PathInfo & path1, const PathInfo & path2)
{
// return 0;
// two paths can be merged if one of them is a stroke and the
// other a fill AND
// all pathelements are the same
// errf << path1.numberOfElementsInPath << " " << path2.numberOfElementsInPath << endl;
if (((path1.currentShowType == stroke && path2.currentShowType == fill) ||
(path2.currentShowType == stroke && path1.currentShowType == fill) )
&& (path1.numberOfElementsInPath == path2.numberOfElementsInPath) ) {
// errf << "Pathes seem to be mergeable" << endl;
for (unsigned int i = 0; i < path1.numberOfElementsInPath ; i++) {
const basedrawingelement *bd1 = path1.path[i];
const basedrawingelement *bd2 = path2.path[i];
// if (! *(path1.path[i]) == *(path2.path[i]) ) return 0;
if (! (*bd1 == *bd2) ) return 0;
}
// errf << "Pathes are mergeable" << endl;
return 1;
} else {
return 0;
}
}
void drvbase::addNumber(float co)
{
// printf("Adding %f\n",co);
if (nextFreeNumber < maxpoints) {
numbers[nextFreeNumber++] = co;
// nextFreeNumber always points to the next free number
} else {
errf << "Too many numbers on stack. Please increase maxpoints in drvbase.h \n";
exit(1);
}
}
const basedrawingelement & drvbase::pathElement(unsigned int index) const
{
return *(outputPath->path[index]);
}
bool operator==(const basedrawingelement & bd1, const basedrawingelement & bd2)
{
if (bd1.getType() != bd2.getType() ) {
return 0;
} else {
for (unsigned int i = 0; i < bd1.size; i++ ) {
if (! (bd1.getPoint(i) == bd2.getPoint(i)) ) return 0;
}
}
return 1;
}
void drvbase::dumpText(const char * const thetext)
{
if (strlen(thetext) > 0) {
dumpPath(); // dump last path to avoid wrong sequence of text and graphics
add_to_page();
float y = pop();
float x = pop();
textInfo_.x = x;
textInfo_.y = y;
textInfo_.thetext = thetext;
show_text(textInfo_);
}
}
float drvbase::pop()
{
if (nextFreeNumber > 0) {
nextFreeNumber--;
return numbers[nextFreeNumber]; // the value we just popped
} else {
errf << "Fatal error in drvbase::pop : nextFreeNumber would get < 0" << endl;
exit(1);
}
return 0.0f; // never reached, just to make compiler quiet
}
void drvbase::setCurrentWidthParams(float ax,float ay,int Char,float cx,float cy)
{
textInfo_.ax = ax;
textInfo_.ay = ay;
textInfo_.Char = Char;
textInfo_.cx = cx;
textInfo_.cy = cy;
}
void drvbase::setCurrentFontName(const char * const Name,bool is_non_standard_font)
{
textInfo_.currentFontName.copy(Name);
textInfo_.is_non_standard_font = is_non_standard_font;
}
void drvbase::setCurrentFontFamilyName(const char * const Name)
{ textInfo_.currentFontFamilyName.copy(Name); }
void drvbase::setCurrentFontFullName(const char * const Name)
{ textInfo_.currentFontFullName.copy(Name); }
void drvbase::setCurrentFontWeight(const char * const Name)
{ textInfo_.currentFontWeight.copy(Name); }
void drvbase::setCurrentFontSize(const float Size)
{ /* errf << "setting Size to " << Size << endl; */ textInfo_.currentFontSize = Size ; }
void drvbase::setCurrentFontAngle(float value)
{ textInfo_.currentFontAngle = value; }
bool drvbase::is_a_rectangle()
{
#if 0
// TODO, Not yet implemented
int dir; /* 1 - up/down (same x) 0 - left/right (same y) */
if (numbersInCurrentSegment() != 10)
return 0;
/* it might be a rectangle */
if (pNumber(0) == pNumber(2)) {
/* same x */
dir = 0;
} else if (pNumber(1) == pNumber(3)) {
/* same y */
dir = 1;
} else {
/* no rectangle */
return 0;
}
for (int i = 0; i < 8; i++, i++) {
if (dir == 1) {
if (!(pNumber(i + 1) == pNumber(i + 1 + 2)))
return 0;
dir = 0;
} else {
if (!(pNumber(i) == pNumber(i + 2)))
return 0;
dir = 1;
}
}
return 1;
#else
return 0;
#endif
}
void drvbase::add_to_page()
{
if (page_empty) {
page_empty = 0;
currentPageNumber++;
open_page();
}
}
void drvbase::guess_linetype()
{
const char * pattern = dashPattern();
// first count number of ' ' in pattern to determine number of entries
// we normally have one less than number of blanks
// line looks like: " [ 2.25 6.75 ] 0.0 setdash"
drvbase::linetype curtype = solid;
int nr_of_entries = -1;
while ((*pattern) && (*pattern != ']' ) ) {
if (*pattern == ' ') nr_of_entries++;
pattern++;
}
pattern = dashPattern();
// errf << nr_of_entries << " entries found in " << pattern << endl;
if (nr_of_entries > 0) {
// now get the numbers
// repeat the numbers, if number of entries is odd
int rep = nr_of_entries % 2; // rep is 1 for odd numbers 0 for even
float *d_numbers = new float[nr_of_entries *(rep + 1)];
int cur = 0;
#ifndef USEISTREAM
for (int i = 0; i <= rep ; i++ ) {
pattern = dashPattern();
while ((*pattern) && (*pattern != ']' ) ) {
if (*pattern == ' ' && ( *(pattern+1) != ']' ) ) {
float f = (float) atof(pattern);
d_numbers[cur] = f;
// errf << d_numbers[cur] << endl;
cur++;
}
pattern++;
}
}
#else
for (int i = 0; i <= rep ; i++ ) {
// on some systems istrstreams expects a non const char *
// so we need to make a copy
char * localpattern = new char[strlen(pattern+1) + 1];
strcpy(localpattern,pattern+1); // skip leading [
istrstream instream(localpattern);
while (!instream.fail()) {
float f;
instream >> f;
if (!instream.fail() ) {
d_numbers[cur] = f;
// errf << d_numbers[cur] << endl;
cur++;
}
}
delete [] localpattern;
}
#endif
// now guess a pattern from
// solid, dashed, dotted, dashdot, dashdotdot ; // corresponding to the CGM patterns
switch ( nr_of_entries *(rep + 1) ) {
case 2:
if (d_numbers[0] < 2.0f) {
// if on is < 2 then always dotted
// ok we miss '. . .'
curtype = drvbase::dotted;
} else {
curtype = drvbase::dashed;
}
break;
case 4:
if ( (d_numbers[0] < 2.0f) || (d_numbers[2] < 2.0f) ) {
curtype = drvbase::dashdot;
} else {
curtype = drvbase::dashed;
}
break;
case 6:
if ( (d_numbers[0] < 2.0f) || (d_numbers[2] < 2.0f) || (d_numbers[2] < 2.0f) ) {
curtype = drvbase::dashdotdot;
} else {
curtype = drvbase::dashed;
}
break;
default:
curtype = drvbase::dashed;
break;
}
delete [] d_numbers;
} else {
// no entry
curtype = drvbase::solid;
}
// errf << "linetype from " << dashPattern() << " is " << curtype << endl;
setCurrentLineType(curtype);
}
void drvbase::dumpPath()
{
guess_linetype(); // needs to be done here, because we must write to currentpath
if (currentPath->numberOfElementsInPath == 2) {
// a polygon with two points is drawn as a line
currentPath->isPolygon=false;
currentPath->currentShowType=drvbase::stroke;
}
if (currentPath->currentShowType!=drvbase::stroke)
{
/* don't show border with fill */
setCurrentLineWidth(0.0f);
}
if (domerge && pathsCanBeMerged(p1,p2)) {
// make p1 the outputPath and clear p2
if (verbose) {
errf << "Path " << p1.nr << " type " << (int) p1.currentShowType << endl;
errf << p1.fillR << " " << p1.fillG << " " << p1.fillB << endl;
errf << p1.edgeR << " " << p1.edgeG << " " << p1.edgeB << endl;
errf << p1.currentLineWidth << endl;
errf << "Path " << p2.nr << " type " << (int) p2.currentShowType << endl;
errf << p2.fillR << " " << p2.fillG << " " << p2.fillB << endl;
errf << p2.edgeR << " " << p2.edgeG << " " << p2.edgeB << endl;
errf << p2.currentLineWidth << endl;
errf << " have been merged\n";
}
// merge p2 into p1
if (p1.currentShowType == stroke) {
// p2 is the fill
p1.currentShowType = p2.currentShowType;
p1.fillR = p2.fillR;
p1.fillG = p2.fillG;
p1.fillB = p2.fillB;
} else {
// p1 is the fill, so copy the line parameters from p2
p1.currentLineWidth = p2.currentLineWidth;
p1.edgeR = p2.edgeR;
p1.edgeG = p2.edgeG;
p1.edgeB = p2.edgeB;
}
if (verbose) {
errf << " result is \n";
errf << "Path " << p1.nr << " type " << (int) p1.currentShowType << endl;
errf << p1.fillR << " " << p1.fillG << " " << p1.fillB << endl;
errf << p1.edgeR << " " << p1.edgeG << " " << p1.edgeB << endl;
errf << p1.currentLineWidth << endl;
}
outputPath = &p1;
p2.clear();
} else {
outputPath = lastPath;
}
if (numberOfElementsInPath() > 0 ) {
// nothing to do for empty pathes
// pathes may be empty due to a merge operation
if (verbose) {
errf << "working on";
switch ( currentShowType() ) {
case drvbase::stroke:
errf << " stroked ";
break;
case drvbase::fill:
errf << " filled ";
break;
case drvbase::eofill:
errf << " eofilled ";
break;
default:
break;
}
errf << "path " << currentNr() << " with " <<
numberOfElementsInPath() << " elements" << endl;
}
if (numberOfElementsInPath() > 1) {
// cannot draw single points
add_to_page();
if (isPolygon()) { /* PolyGon */
if (is_a_rectangle()) {
{
#if 0
float llx,lly,urx,ury;
// TODO, Not yet implemented
llx = min( min(pNumber(0),pNumber(2)), min(pNumber(4),pNumber(6)));
urx = max( max(pNumber(0),pNumber(2)), max(pNumber(4),pNumber(6)));
lly = min( min(pNumber(1),pNumber(3)), min(pNumber(5),pNumber(7)));
ury = max( max(pNumber(1),pNumber(3)), max(pNumber(5),pNumber(7)));
show_rectangle(llx,lly,urx,ury);
#endif
}
} else {
show_path();
}
} else { /* PolyLine */
show_path();
};
}
// cleanup
outputPath->clear();
}
// swap current and last pointers
PathInfo *help = currentPath;
currentPath = lastPath;
lastPath = help;
currentPath->copyInfo(*help); // initialize next path with state of last path
// currentPath is the path filled next by lexer
outputPath = currentPath;
}
void drvbase::addtopath(basedrawingelement * newelement)
{
if (newelement) {
if (currentPath->numberOfElementsInPath < maxElements) {
currentPath->path[currentPath->numberOfElementsInPath] = newelement;
#ifdef DEBUG
cout << "pathelement " << currentPath->numberOfElementsInPath << " added "
<< *newelement << endl;
#endif
currentPath->numberOfElementsInPath++;
} else {
errf << "Fatal: number of path elements exceeded. Increase maxElements in drvbase.h" << endl;
exit(1);
}
} else {
errf << "Fatal: newelement is NULL in addtopath " << endl;
exit(1);
}
}
void drvbase::PathInfo::clear() {
for (unsigned int i = 0 ; i < numberOfElementsInPath; i++ ) {
delete path[i];
path[i] = 0;
}
numberOfElementsInPath = 0;
}
void drvbase::PathInfo::copyInfo(const PathInfo & p){
// copies the whole path state except the path array
currentShowType = p.currentShowType;
nr = p.nr;
// Path is not copied path(0),
isPolygon = p.isPolygon;
// numberOfElementsInPath = p.numberOfElementsInPath;
currentLineWidth = p.currentLineWidth;
edgeR = p.edgeR;
edgeG = p.edgeG;
edgeB = p.edgeB;
fillR = p.fillR;
fillG = p.fillG;
fillB = p.fillB;
}
ostream & operator<<(ostream & out,const basedrawingelement &elem)
{
out << "type: " << (int) elem.getType() << " params: " ;
for (unsigned int i = 0 ; i < elem.size ; i++ ) {
out << elem.getPoint(i).x_ << " "
<< elem.getPoint(i).y_ << " ";
}
out << endl;
return out;
}
ColorTable::ColorTable(const char * const * defaultColors,
const unsigned int numberOfDefaultColors,
makeColorNameType makeColorName) :
defaultColors_(defaultColors),
numberOfDefaultColors_(numberOfDefaultColors),
makeColorName_(makeColorName)
{
for (unsigned int i = 0 ; i< maxcolors; i++) newColors[i] = 0;
}
ColorTable::~ColorTable()
{
unsigned int current= 0;
while (newColors[current] != 0) {
delete [] newColors[current];
current++;
}
}
unsigned int ColorTable::getColorIndex(float r, float g, float b)
{
// registers a possibly new color and returns the index
// under which the color was registered
const char * cmp = makeColorName_(r,g,b);
for (unsigned int i = 0; i < numberOfDefaultColors_ ; i++ )
{
if (strcmp(cmp,defaultColors_[i]) == 0) {
return i;
}
}
// look in new colors
unsigned int j = 0;
for (j = 0; ((j < maxcolors) && (newColors[j] != 0)) ; j++ )
{
if (strcmp(cmp,newColors[j]) == 0) {
return j+numberOfDefaultColors_;
}
}
// not found so far
// j is either maxcolors or the index of the next free entry
// add a copy to newColors
if (j < maxcolors) {
newColors[j] = new char[strlen(cmp) +1];
strcpy(newColors[j],cmp);
return j+numberOfDefaultColors_;
} else {
// cerr << "running out of colors" << endl;
return 0;
}
}
const char * const ColorTable::getColorString(float r, float g, float b)
{
return getColorString(getColorIndex(r,g,b));
}
bool ColorTable::isKnownColor(float r, float g, float b) const
{
// Possible improvements:
// could return the next free entry as negative number in case
// the color is not found. This would make it possible to
// use this function in getColorEntry as well, or (better)
// make a pure registercolor(index,.....) instead of
// getColorEntry.
const char * cmp = makeColorName_(r,g,b);
for (unsigned int i = 0; i < numberOfDefaultColors_ ; i++ )
{
if (strcmp(cmp,defaultColors_[i]) == 0) {
return true;
}
}
// look in new colors
unsigned int j = 0;
for (j = 0; ((j < maxcolors) && (newColors[j] != 0)) ; j++ )
{
if (strcmp(cmp,newColors[j]) == 0) {
return true; // j+numberOfDefaultColors_;
}
}
// not found so far
return false;
}
const char * const ColorTable::getColorString(unsigned int index) const
{
return (index < numberOfDefaultColors_) ? defaultColors_[index] :
newColors[index - numberOfDefaultColors_];
}