1 /*
2    drvbase.cpp : This file is part of pstoedit
3    Basic, driver independent output routines
4 
5    Copyright (C) 1993 - 2014 Wolfgang Glunz, wglunz35_AT_pstoedit.net
6 
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
23 #include "drvbase.h"
24 
25 #include I_stdlib
26 #include I_iostream
27 //#include I_iomanip
28 
29 #include I_string_h
30 
31 #include I_strstream
32 
33 #include <math.h>
34 
35 #ifndef miscutil_h
36 #include "miscutil.h"
37 #endif
38 
splitFullFileName(const char * const fullName,RSString & pathName,RSString & baseName,RSString & fileExt)39 static void splitFullFileName(const char *const fullName,
40 							  RSString& pathName,
41 							  RSString& baseName,
42 							  RSString& fileExt)
43 {
44 	if (fullName == NIL)
45 		return;
46 
47 	char *fullName_T = cppstrdup(fullName);
48 	char *baseName_T = 0;
49 
50 #if defined(unix) || defined(__unix__) || defined(_unix) || defined(__unix) || defined(__EMX__) || defined (NetBSD)
51 	// coverity[uninit_use_in_call]
52 	char *c = strrchr(fullName_T, '/');
53 #else
54 	char *c = strrchr(fullName_T, '\\');
55 #endif
56 	if (c != NIL) {
57 		baseName_T = cppstrdup(c + 1);
58 		*(c + 1) = 0;
59 		pathName = fullName_T;
60 	} else {
61 		baseName_T = cppstrdup(fullName_T);
62 		pathName = "";
63 	}
64 
65 	// coverity[uninit_use_in_call]
66 	c = strrchr(baseName_T, '.');
67 	if (c != NIL) {
68 		fileExt =  (c + 1);
69 		*c = 0;
70 		baseName = baseName_T;
71 	} else {
72 		fileExt = "";
73 		baseName = baseName_T;
74 	}
75 	delete[]baseName_T;
76 	delete[]fullName_T;
77 }
78 
79 
80 
81 
drvbase(const char * driveroptions_p,ostream & theoutStream,ostream & theerrStream,const char * nameOfInputFile_p,const char * nameOfOutputFile_p,PsToEditOptions & globaloptions_p,const DriverDescription & driverdesc_p)82 drvbase::drvbase(const char *driveroptions_p, ostream & theoutStream,
83 				 ostream & theerrStream,
84 				 const char *nameOfInputFile_p,
85 				 const char *nameOfOutputFile_p,
86 				 PsToEditOptions & globaloptions_p,
87 				 const DriverDescription & driverdesc_p)
88 :								// constructor
89 driverdesc(driverdesc_p),
90 DOptions_ptr(driverdesc_p.createDriverOptions()),
91 //  totalNumberOfPages(0),
92 //  bboxes(0),
93 	outf(theoutStream),
94 errf(theerrStream),
95 inFileName(nameOfInputFile_p ? nameOfInputFile_p : ""),
96 outFileName(nameOfOutputFile_p ? nameOfOutputFile_p : ""),
97 outDirName(""), outBaseName(""), d_argc(0), d_argv(0), globaloptions(globaloptions_p),
98 	// set some common defaults
99 currentDeviceHeight(792.0f ),  // US Letter
100 currentDeviceWidth(612.0f ),   // US Letter
101 x_offset(0.0f),
102 y_offset(0.0f),
103 currentPageNumber(0),
104 domerge(false),
105 defaultFontName(0),
106 ctorOK(true),
107 saveRestoreInfo(NIL), currentSaveLevel(&saveRestoreInfo), page_empty(true), driveroptions(0),
108 	// default for PI1 and PI2 and clippath
109 	currentPath(0), last_currentPath(0), outputPath(0), lastPath(0)
110 	// default for textInfo_ and lasttextInfo_
111 {
112 
113 
114 
115 	// verbose = (getenv("PSTOEDITVERBOSE") != 0);
116 
117 	if (verbose) {
118 		errf << "verbose mode turned on\n" << endl;
119 	}
120 
121 	if (nameOfOutputFile_p) {
122 		RSString extension; // not needed
123 		splitFullFileName(nameOfOutputFile_p, outDirName, outBaseName, extension );
124 		if (verbose) {
125 			errf << "nameofOutputFile: '" << nameOfOutputFile_p;
126 			errf << "' outDirName: '" << outDirName;
127 			errf << "' outBaseName: '" << outBaseName;
128 			errf << "'" << endl;
129 		}
130 	}
131 	// preparse driveroptions and build d_argc and d_argv
132 	if (driveroptions_p) {
133 #if 1
134 		Argv driverargs;
135 		(void) driverargs.parseFromString(driveroptions_p);
136 		d_argc = driverargs.argc;
137 		d_argv = new const char *[d_argc + 2];  // 1 more for the argv[0]
138 		d_argv[0] = cppstrdup(driverdesc_p.symbolicname);
139 		d_argc = 1;
140 		for (unsigned int a = 0; a < driverargs.argc; a++) {
141 			d_argv[d_argc] = cppstrdup(driverargs.argv[a]);
142 			d_argc++;
143 		}
144 		d_argv[d_argc] = 0;
145 #else
146 		driveroptions = cppstrdup(driveroptions_p);
147 		//C_istrstream optstream(driveroptions, strlen(driveroptions));
148 		C_istrstream optstream(driveroptions); //, strlen(driveroptions));
149 		const long startOfStream = optstream.tellg();
150 		char currentarg[100];
151 		// first count number of arguments
152 		while (!optstream.eof()) {
153 			(void) optstream.width(sizeof(currentarg));
154 			optstream >> currentarg;
155 			d_argc++;
156 		}
157 		d_argv = new const char *[d_argc + 2];  // 1 more for the argv[0]
158 		// now fill d_args array;
159 		(void) optstream.seekg(startOfStream);	// reposition to start
160 		optstream.clear();
161 		// fill argv[0] with driver name (to be similar with Unix)
162 		d_argv[0] = cppstrdup(driverdesc_p.symbolicname);
163 		d_argc = 1;
164 		while (!optstream.eof()) {
165 			optstream >> currentarg;
166 			if (strlen(currentarg) > 0) {
167 				d_argv[d_argc] = cppstrdup(currentarg);
168 				d_argc++;
169 			}
170 		}
171 		d_argv[d_argc] = 0;
172 #endif
173 		if (verbose) {
174 			errf << "got " << d_argc << " driver argument(s)" << endl;
175 			for (unsigned int i = 0; i < d_argc; i++) {
176 				errf << "Driver option " << i << ":" << d_argv[i] << endl;
177 			}
178 		}
179 	}
180 
181 // now call the driver specific option parser.
182 	if (d_argc>0) {
183 		if (DOptions_ptr) {
184 			//debug errf << "DOptions_ptr: " << (void*) DOptions_ptr << endl;
185 			const unsigned int remaining = DOptions_ptr->parseoptions(errf,d_argc,d_argv);
186 			if ((remaining > 0) && !DOptions_ptr->expectUnhandled) {
187 				errf << "the following " << remaining  << " options could not be handled by the driver: " << endl;
188 				for (unsigned int i = 0; i < remaining; i++) {
189 					errf << DOptions_ptr->unhandledOptions[i] << endl;
190 				}
191 			}
192 		} else {
193 			cerr << "DOptions_ptr is NIL - program flow error - contact author." << endl;
194 		}
195 	}
196 
197 //  bboxes = new BBox[maxPages];
198 
199 	// init segment info for first segment
200 	// all others will be updated with each newsegment
201 
202 
203 	currentPath = &PI1;
204 	lastPath = &PI2;
205 	outputPath = currentPath;
206 
207 #if defined(HAVE_STL) && !defined(USE_FIXED_ARRAY)
208 #else
209 	if ((PI1.path == 0) || (PI2.path == 0) || (clippath.path == 0)) {
210 		errf << "new failed in drvbase::drvbase " << endl;
211 		exit(1);
212 	}
213 #endif
214 
215 	textInfo_.thetext.assign("");
216 	setCurrentFontName("Courier", true);
217 	setCurrentFontFamilyName("Courier");
218 	setCurrentFontWeight("Regular");
219 	setCurrentFontFullName("Courier");
220 	setCurrentFontSize(10.0f);
221 	mergedTextInfo = textInfo_; // initial value - empty buffer
222 	lastTextInfo_ = textInfo_;
223 	lastTextInfo_.currentFontSize = -textInfo_.currentFontSize;	// to force a new font the first time.
224 	lastTextInfo_.currentR = textInfo_.currentR + 1;	// to force new color
225 }
226 
~drvbase()227 drvbase::~drvbase()
228 {
229 	currentPath = 0;
230 	lastPath = 0;
231 	outputPath = 0;
232 	if (d_argv) {
233 		for (unsigned int i = 0; i < d_argc; i++) {
234 			delete[](d_argv[i]);
235 			d_argv[i] = 0;
236 		}
237 		delete[]d_argv;
238 		d_argv = NIL;
239 	}
240 	if (driveroptions) {
241 		delete[]driveroptions;
242 		driveroptions = NIL;
243 	}
244 //  delete[] bboxes; bboxes = NIL;
245 //	delete[]outDirName;
246 //	outDirName = NIL;
247 //	delete[]outBaseName;
248 //	outBaseName = NIL;
249 //	Pdriverdesc = NIL;
250 
251 	delete DOptions_ptr;
252 	DOptions_ptr = NIL;
253 
254 	if (currentSaveLevel->previous != NIL) {
255 		while (currentSaveLevel->previous != NIL) {
256 			currentSaveLevel = currentSaveLevel->previous;
257 			delete currentSaveLevel->next;
258 		}
259 	}
260 	currentSaveLevel = 0;
261 	defaultFontName = NIL;
262 	last_currentPath = NIL;
263 }
264 
getPageSize() const265 const RSString & drvbase::getPageSize() const { return globaloptions.outputPageSize(); }
266 
getCurrentBBox() const267 const BBox & drvbase::getCurrentBBox() const
268 {
269 	if ( verbose )
270 		cout << " get getCurrentBBox for page: " << currentPageNumber <<
271 			" of " << totalNumberOfPages() << endl;
272 	if ((totalNumberOfPages() > 0)
273 		&& (currentPageNumber <= totalNumberOfPages())) {
274 		// page numbers start from 1.
275 		return bboxes()[currentPageNumber > 0 ? (currentPageNumber - 1) : 0];
276 	} else {
277 		static BBox dummy;
278 		return dummy;
279 	}
280 }
281 
startup(bool mergelines)282 void drvbase::startup(bool mergelines)
283 {
284 	domerge = false;			// default
285 	if (mergelines) {
286 		if (driverdesc.backendSupportsMerging) {
287 			domerge = true;
288 		} else {
289 			errf << "the selected backend does not support merging, -mergelines ignored" << endl;
290 		}
291 	}
292 }
293 
finalize()294 void drvbase::finalize()
295 {
296 // needed because base destructor is called after derived destructor
297 	outputPath->clear();		// define past the end path as empty
298 	// close page (if no explicit showpage was done)
299 	showpage();
300 }
301 
showpage()302 void drvbase::showpage()
303 {
304 	flushOutStanding();					// dump last path
305 	if (!page_empty) {
306 		close_page();
307 	}
308 	page_empty = true;
309 }
310 
pathsCanBeMerged(const PathInfo & path1,const PathInfo & path2) const311 bool drvbase::pathsCanBeMerged(const PathInfo & path1, const PathInfo & path2) const
312 {
313 	//
314 	// two paths can be merged if one of them is a stroke and the
315 	// other a fill or eofill AND
316 	// all pathelements are the same
317 	//
318 	// This is a default implementation which allows only solid edges since
319 	// most backends support only such edges.
320 	// If a backend allows more, it can overwrite this function
321 	//
322 	if (((path1.currentShowType == stroke && path1.currentLineType == solid
323 		  && ((path2.currentShowType == fill)
324 			  || (path2.currentShowType == eofill)))
325 		 || (path2.currentShowType == stroke
326 			 && path2.currentLineType == solid && ((path1.currentShowType == fill)
327 												   || (path1.currentShowType == eofill))))
328 		&& (path1.numberOfElementsInPath == path2.numberOfElementsInPath)) {
329 		//errf << "Pathes seem to be mergeable" << endl;
330 		for (unsigned int i = 0; i < path1.numberOfElementsInPath; i++) {
331 			const basedrawingelement *bd1 = path1.path[i];
332 			const basedrawingelement *bd2 = path2.path[i];
333 //          if (! *(path1.path[i]) == *(path2.path[i]) ) return 0;
334 			//errf << "comparing " << *bd1 << " with " << *bd2 << endl;
335 			const bool result = (*bd1 == *bd2);
336 			if (verbose)
337 				errf << "comparing " << *bd1 << " with " << *bd2 <<	" results in " << (int) result << endl;
338 			if (!result)
339 				return false;
340 		}
341 		if (verbose)
342 			errf << "Pathes are mergeable" << endl;
343 		return true;
344 	} else {
345 		if (verbose)
346 			errf << "Pathes are not mergable:" <<
347 				" PI1 st " << (int) path1.currentShowType <<
348 				" PI1 lt " << (int) path1.currentLineType <<
349 				" PI1 el " << path1.numberOfElementsInPath <<
350 				" PI2 st " << (int) path2.currentShowType <<
351 				" PI2 lt " << (int) path2.currentLineType <<
352 				" PI2 el " << path2.numberOfElementsInPath << endl;
353 		return false;
354 	}
355 }
356 
357 
358 
pathElement(unsigned int index) const359 const basedrawingelement & drvbase::pathElement(unsigned int index) const
360 {
361 	return *(outputPath->path[index + outputPath->subpathoffset]);
362 }
363 
operator ==(const basedrawingelement & bd2) const364 bool basedrawingelement::operator == (const basedrawingelement & bd2) const
365 {
366 	if (this->getType() != bd2.getType()) {
367 		return false;
368 	} else {
369 		for (unsigned int i = 0; i < this->getNrOfPoints(); i++) {
370 			if (!(this->getPoint(i) == bd2.getPoint(i)))
371 				return false;
372 		}
373 	}
374 	return true;
375 }
376 
textIsWorthToPrint(const RSString & thetext) const377 bool drvbase::textIsWorthToPrint(const RSString& thetext) const
378 {
379 	// check whether it contains just blanks. This makes
380 	// problems, e.g. with the xfig backend.
381   const char *cp = thetext.c_str();
382   for (size_t i = thetext.length(); i>0; i--)
383     if (*cp++ != ' ')
384       return true;
385   return false;
386 }
387 
textCanBeMerged(const TextInfo & text1,const TextInfo & text2) const388 bool drvbase::textCanBeMerged(const TextInfo & text1, const TextInfo & text2) const
389 {
390 	return (
391 				(text1.currentFontName == text2.currentFontName)
392 			 && (text1.currentFontFamilyName  == text2.currentFontFamilyName)
393 			 && (text1.currentFontFullName  == text2.currentFontFullName)
394 			 && (text1.currentFontWeight  == text2.currentFontWeight)
395 			 && (text1.currentFontSize  == text2.currentFontSize)
396 			 && (text1.currentFontAngle  == text2.currentFontAngle)
397 			 && (text1.currentR  == text2.currentR)
398 			 && (text1.currentG  == text2.currentG)
399 			 && (text1.currentB  == text2.currentB)
400 
401 			 && (fabs(text1.x - text2.x_end) < text1.currentFontSize / 10)
402 			 && (fabs(text1.y - text2.y_end) < text1.currentFontSize / 10)
403 
404 			);
405 
406 	// text matrix is ignored for the moment
407 }
408 
show_text(const TextInfo & textinfo)409 void drvbase::show_text(const TextInfo & textinfo)
410 {
411 		unused(&textinfo);
412 		if (driverdesc.backendSupportsText) {
413 			errf << " Backends that support text need to define a show_text method " <<endl;
414 		}
415 		// in case backendSupportsText is false, the frontend already flattens text (usually)
416 		// Must use the -dt flag for this, since RenderMan doesn't support text
417 }
418 
show_rectangle(const float llx,const float lly,const float urx,const float ury)419 void drvbase::show_rectangle(
420 				       const float llx,
421 				       const float lly,
422 				       const float urx,
423 				       const float ury)
424 	// writes a rectangle at points (llx,lly) (urx,ury)
425 {
426 	// outf << "Rectangle ( " << llx << "," << lly << ") (" << urx << "," << ury << ")" << endl;
427 	// just do show_path for a first guess
428 
429 	if (globaloptions.convertFilledRectToStroke && (currentShowType() == drvbase::fill || currentShowType() == drvbase::eofill)) {
430 // if possible and wished - convert a filled rectangle to a single stroked line
431 
432 		const float dx = urx - llx;
433 		const float dy = ury - lly;
434 		const float lw = currentLineWidth();
435 		const float lwhalf = lw/2.0f;
436 
437 		PathInfo * savepath = currentPath;
438 		currentPath = outputPath; // in order to be able to use the add.. functions
439 		// we have to use outputPath-> instead of currentpath
440 
441 		setCurrentShowType(drvbase::stroke);
442 		setCurrentLineCap(0); // 0 means "butt", i.e. no overlap
443 		setCurrentLineType(drvbase::solid);
444 
445 		if (dx > dy) {
446 			// horizontal line
447 			const float mid = (ury+lly)/2.0f;
448 	 		currentPath->clear();
449 	 		addtopath(new Moveto(llx-lwhalf,mid));
450 	 		addtopath(new Lineto(urx+lwhalf,mid));
451 	 		setCurrentLineWidth( dy+lw );
452 			// debug cout << "rect -> horizontal line " << endl;
453 		} else {
454 			// vertical line
455 			const float mid = (urx+llx)/2.0f;
456 			currentPath->clear();
457 			addtopath(new Moveto(mid,lly+lwhalf));
458 			addtopath(new Lineto(mid,ury+lwhalf));
459 			setCurrentLineWidth( dx+lw );
460 			// debug cout << "rect -> vertical line " << endl;
461 		}
462 		currentPath = savepath;
463 	} else {
464 		// default - just write the rect as an ordinary polygon
465 
466 		// debug cout << "rect as path " << endl;
467 	}
468 
469 	show_or_convert_path();
470 }
471 
472 
show_or_convert_path()473 void drvbase::show_or_convert_path() {
474 	if (globaloptions.simulateFill &&
475 		!(currentShowType() == stroke)) {
476 		// handle fill and eofill
477 		simulate_fill();
478 	} else {
479 		show_path();
480 	}
481 }
482 
483 
flushTextBuffer(bool useMergeBuffer)484 void drvbase::flushTextBuffer(bool useMergeBuffer)
485 {
486 	if (useMergeBuffer) textInfo_ = mergedTextInfo; // this is ugly, I know, but to be consistent
487 								// with other functions that use textInfo_ directly
488 								// this is needed.
489 	const TextInfo* textToBeFlushed = useMergeBuffer ? &mergedTextInfo : &textInfo_;
490 	add_to_page();
491 	show_text(*textToBeFlushed);
492 	lastTextInfo_ = *textToBeFlushed;	// save for font and color comparison
493 }
494 
showOrMergeText()495 void drvbase::showOrMergeText()
496 {
497 	flushOutStanding(flushpath); // dump last path to avoid wrong sequence of text and graphics
498 	// this flushing needs to be done in any case, even if the text is not written immediately
499 	// but instead buffered first. But otherwise, the order gets corrupted
500 
501 	if (globaloptions.mergetext) {
502 		if (mergedTextInfo.thetext == "") {
503 			mergedTextInfo = textInfo_;
504 			// there was nothing in the buffer so far, so just place it there.
505 			// for this we need a final flush somewhere
506 		} else if (textCanBeMerged(textInfo_,mergedTextInfo)) {
507 		   // text can be merged.
508 			if (verbose) {
509 				errf << "Info: merging text '" << mergedTextInfo.thetext
510 					<< "' and '"
511 					<< textInfo_.thetext << "'" << endl;
512 			}
513 			mergedTextInfo.thetext += textInfo_.thetext;
514 			static const RSString space(" ");
515 			(mergedTextInfo.glyphnames += space ) += textInfo_.glyphnames;
516 			mergedTextInfo.x_end = textInfo_.x_end;
517 			mergedTextInfo.y_end = textInfo_.y_end;
518 		} else {
519 			// cannot be merged, so dump text collected so far and place the new
520 			// one in the buffer for later
521 			if (textIsWorthToPrint(mergedTextInfo.thetext)) {
522 				TextInfo temp = textInfo_;	// save "new" text in temp
523 				flushTextBuffer(true); // true -> use merge buffer
524 				mergedTextInfo = temp;		// set the merge buffer to the "new" text
525 			} else {
526 				// the merge buffer was not worth to be printed so forget it and
527 				// start over with new text
528 				mergedTextInfo = textInfo_;
529 			}
530 		}
531 	} else {
532 		// always just "pass through" if it is worth to be printed
533 		if (textIsWorthToPrint(textInfo_.thetext)) {
534 			flushTextBuffer(false); // false -> use textinfo_
535 		}
536 	}
537 }
538 
pushText(const size_t len,const char * const thetext,const float x,const float y,const char * const glyphnames)539 void drvbase::pushText(const size_t len, const char *const thetext, const float x, const float y, const char * const glyphnames)
540 {
541 		textInfo_.x = x;
542 		textInfo_.y = y;
543 		textInfo_.thetext.assign(thetext, len);
544 		textInfo_.glyphnames.assign(glyphnames ? glyphnames:"");
545 		textInfo_.currentFontUnmappedName = textInfo_.currentFontName;
546 		textInfo_.remappedfont= false;
547 		const char *remappedFontName = drvbase::theFontMapper().mapFont(textInfo_.currentFontName);
548 		// errf << " Mapping of " << textInfo_.currentFontName << " returned " << (remappedFontName ? remappedFontName:" ") << endl;
549 		if (remappedFontName) {
550 			if (verbose) {
551 				errf << "Font remapped from '" << textInfo_.
552 					currentFontName << "' to '" << remappedFontName << "'" << endl;
553 			}
554 			textInfo_.currentFontName.assign(remappedFontName);
555 			textInfo_.remappedfont= true;
556 		}
557 
558 		showOrMergeText();
559 
560 #if 0
561 		if ((lasttextInfo_.y == textInfo_.y)
562 			&& (lasttextInfo_.x_end >= textInfo_.x)
563 			&& (lasttextInfo_.x < textInfo_.x)
564 			&& lasttextInfo_.samefont(textInfo_)) {
565 			if (verbose) {
566 				errf << "Text overlap ! '" << lasttextInfo_.thetext.
567 					c_str() << "' and '" << textInfo_.thetext.c_str() << endl;
568 			}
569 		}
570 #endif
571 
572 }
573 
574 
hexdecode(char high,char low)575 static unsigned short hexdecode( char high, char low) {
576 	return 16*hextoint(high) + hextoint(low);
577 }
578 
pushHEXText(const char * const thetext,const float x,const float y,const char * const glyphnames)579 void drvbase::pushHEXText(const char *const thetext, const float x, const float y, const char * const glyphnames)
580 {
581 	const size_t textlen = strlen(thetext);
582 	if (textlen) {
583 		char * decodedText = new char[ (textlen / 2 ) + 1 ];
584 		for (unsigned int i = 0, j = 0; i < (textlen/2); i++) {
585 			decodedText[i] = (char) hexdecode(thetext[j], thetext[j+1]);
586 			j++;j++;
587 		}
588 		decodedText[textlen/2] = '\0';
589 		pushText(textlen/2,decodedText,x,y,glyphnames);
590 		delete [] decodedText;
591 	}
592 }
593 
setCurrentWidthParams(const float ax,const float ay,const int Char,const float cx,const float cy,const float x_end,const float y_end)594 void drvbase::setCurrentWidthParams(const float ax,
595 									const float ay,
596 									const int Char,
597 									const float cx,
598 									const float cy, const float x_end, const float y_end)
599 {
600 	textInfo_.ax = ax;
601 	textInfo_.ay = ay;
602 	textInfo_.Char = Char;
603 	textInfo_.cx = cx;
604 	textInfo_.x_end = x_end;
605 	textInfo_.y_end = y_end;
606 	textInfo_.cy = cy;
607 }
608 
setCurrentFontName(const char * const Name,bool is_non_standard_font)609 void drvbase::setCurrentFontName(const char *const Name, bool is_non_standard_font)
610 {
611 	textInfo_.currentFontName.assign(Name);
612 	textInfo_.is_non_standard_font = is_non_standard_font;
613 }
614 
setCurrentFontFamilyName(const char * const Name)615 void drvbase::setCurrentFontFamilyName(const char *const Name)
616 {
617 	textInfo_.currentFontFamilyName.assign(Name);
618 }
619 
setCurrentFontFullName(const char * const Name)620 void drvbase::setCurrentFontFullName(const char *const Name)
621 {
622 	textInfo_.currentFontFullName.assign(Name);
623 }
624 
setCurrentFontWeight(const char * const Name)625 void drvbase::setCurrentFontWeight(const char *const Name)
626 {
627 	textInfo_.currentFontWeight.assign(Name);
628 }
629 
setCurrentFontSize(const float Size)630 void drvbase::setCurrentFontSize(const float Size)
631 {								/* errf << "setting Size to " << Size << endl; */
632 	textInfo_.currentFontSize = Size;
633 }
634 
setCurrentFontAngle(float value)635 void drvbase::setCurrentFontAngle(float value)
636 {
637 	textInfo_.currentFontAngle = value;
638 }
639 
is_a_rectangle() const640 bool drvbase::is_a_rectangle() const
641 {
642 /* Detects the following sequences
643                 moveto
644                 lineto
645                 lineto
646                 lineto
647                 closepath
648 or
649                 moveto
650                 lineto
651                 lineto
652                 lineto
653                 lineto
654 				(if last lineto goes to same coord as first moveto
655 */
656 	// cout << "Testing path " << currentNr() <<endl;
657 // there have to be 5 elements
658 	if (numberOfElementsInPath() != 5)       return false;
659 	if (pathElement(0).getType() != moveto ) return false;
660 	if (pathElement(1).getType() != lineto ) return false;
661 	if (pathElement(2).getType() != lineto ) return false;
662 	if (pathElement(3).getType() != lineto ) return false;
663 
664 	Point points[5];
665 	{
666 	//	cout << "before normalization " <<  "Path # " << currentNr() <<endl;
667 	for (int i = 0; i< 4; i++) {
668 		points[i] = pathElement(i).getPoint(0) ;
669 		// cout << "p " << i << " " << points[i].x_ << " " <<  points[i].y_ << endl;
670 	}
671 	// cout << "####" << endl;
672 	}
673 	// the 5th depend on the last element
674 
675 	if (pathElement(4).getType() == lineto ) {
676 			// check for first == last
677 		if (pathElement(0).getPoint(0) != pathElement(4).getPoint(0)) return false;
678 	} else {
679 		if (pathElement(4).getType() != closepath ) return false; // 4th element is neither lineto nor closepath
680 	}
681 
682 	// now we are sure we either have a closepath or a final line to the initial moveto so we can set the last point to the first one.
683 	points[4] = pathElement(0).getPoint(0); // use the point of the first moveto.
684 
685 
686 // so far all OK - now check the points.
687 
688 	unsigned int start_horic_test;
689 	unsigned int start_vert_test;
690 
691 	if (points[0].x_ == points[1].x_) {
692 		start_horic_test = 0;
693 		start_vert_test = 1;
694 	} else {
695 		start_horic_test = 1;
696 		start_vert_test = 0;
697 	}
698 
699 	{
700 		for (unsigned int i = start_horic_test; i < 4; i++, i++)
701 			if (points[i].x_ != points[(i + 1) % 4].x_) {
702 				// cout << "F1" << endl;
703 				return false;
704 			}
705 	}
706 
707 	{
708 		for (unsigned int i = start_vert_test; i < 4; i++, i++)
709 			if (points[i].y_ != points[(i + 1) % 4].y_) {
710 				// cout << "F2" << endl;
711 				return false;
712 			}
713 	}
714 	// cout << "IS RECT" << endl;
715 	return true;
716 }
717 
add_to_page()718 void drvbase::add_to_page()
719 {
720 	if (page_empty) {
721 		page_empty = false;
722 		currentPageNumber++;
723 		open_page();
724 	}
725 }
726 
727 
DashPattern(const char * patternAsSetDashString)728 DashPattern::DashPattern(const char
729 						 *patternAsSetDashString):dashString(patternAsSetDashString),
730 nrOfEntries(-1), numbers(0), offset(0)
731 {
732 	const char *pattern = patternAsSetDashString;
733 	// first count number of ' ' in pattern to determine number of entries
734 	// we normally have one less than number of blanks
735 	// line looks like: " [ 2.25 6.75 ] 0.0 setdash"
736 
737 	while ((*pattern) && (*pattern != ']')) {
738 		if (*pattern == ' ')
739 			nrOfEntries++;
740 		pattern++;
741 	}
742 
743 	// errf << nr_of_entries << " entries found in " << pattern << endl;
744 	if (nrOfEntries > 0) {
745 		pattern = patternAsSetDashString;
746 		// now get the numbers
747 		// repeat the numbers, if number of entries is odd
748 		unsigned int rep = nrOfEntries % 2;	// rep is 1 for odd numbers 0 for even
749 		numbers = new float[nrOfEntries * (rep + 1)];
750 		unsigned int cur = 0;
751 #if 1
752 		for (unsigned int i = 0; i <= rep; i++) {
753 			pattern = patternAsSetDashString;
754 			while ((*pattern) && (*pattern != ']')) {
755 				if (*pattern == ' ' && (*(pattern + 1) != ']')) {
756 					float f = (float) atof(pattern);
757 					numbers[cur] = f;
758 					// errf << d_numbers[cur] << endl;
759 					cur++;
760 				}
761 				pattern++;
762 			}
763 		}
764 //      if ( *(pattern+1) == ']' ) {
765 //          offset = (float) atof(pattern +2);
766 //      }
767 		if (*(pattern) == ']') {	// DMB // fixed by david butterfield
768 			offset = (float) atof(pattern + 1);	// DMB
769 		}
770 #else
771 		// this is the "C++" version. But this doesn't work with the GNU library under Linux
772 		for (unsigned int i = 0; i <= rep; i++) {
773 			// on some systems istrstreams expects a non const char *
774 			// so we need to make a copy
775 			char *localpattern = new char[strlen(pattern + 1) + 1];
776 			strcpy(localpattern, pattern + 1);	// skip leading [
777 			istrstream instream(localpattern);
778 			while (!instream.fail()) {
779 				float f;
780 				instream >> f;
781 				if (!instream.fail()) {
782 					d_numbers[cur] = f;
783 					// errf << d_numbers[cur] << endl;
784 					cur++;
785 				}
786 			}
787 			delete[]localpattern;
788 		}
789 #endif
790 	}
791 }
792 
~DashPattern()793 DashPattern::~DashPattern()
794 {
795 	delete[]numbers;
796 	numbers = 0;
797 	nrOfEntries = 0;
798 }
799 
800 
guess_linetype()801 void drvbase::guess_linetype()
802 {
803 	DashPattern dp(dashPattern());
804 	const float *const d_numbers = dp.numbers;
805 	const int nr_of_entries = dp.nrOfEntries;
806 
807 	drvbase::linetype curtype = solid;
808 	if (nr_of_entries > 0) {
809 		int rep = nr_of_entries % 2;	// rep is 1 for odd numbers 0 for even
810 		// now guess a pattern from
811 		// solid, dashed, dotted, dashdot, dashdotdot ; // corresponding to the CGM patterns
812 		switch (nr_of_entries * (rep + 1)) {
813 		case 2:
814 			if (d_numbers[1] == 0.0f) {
815 				curtype = drvbase::solid;	// if off is 0 -> solid
816 			} else if ((d_numbers[0] / d_numbers[1]) > 100) {
817 				curtype = drvbase::solid;	// if on/off > 100 -> use solid
818 			} else if (d_numbers[0] < 2.0f) {
819 				// if on is < 2 then always dotted
820 				// ok we miss '.             .             .'
821 				curtype = drvbase::dotted;
822 			} else {
823 				curtype = drvbase::dashed;
824 			}
825 			break;
826 		case 4:
827 			if ((d_numbers[1] == 0.0f) && (d_numbers[3] == 0.0f)) {
828 				curtype = drvbase::solid;	// if off is 0 -> solid
829 			} else if ((d_numbers[0] < 2.0f) || (d_numbers[2] < 2.0f)) {
830 				curtype = drvbase::dashdot;
831 			} else {
832 				curtype = drvbase::dashed;
833 			}
834 			break;
835 		case 6:
836 			if ((d_numbers[1] == 0.0f) && (d_numbers[3] == 0.0f)
837 				&& (d_numbers[5] == 0.0f)) {
838 				curtype = drvbase::solid;	// if off is 0 -> solid
839 			} else if ((d_numbers[0] < 2.0f) ||
840                                    (d_numbers[2] < 2.0f) ||
841                                    (d_numbers[4] < 2.0f)) {
842 				curtype = drvbase::dashdotdot;
843 			} else {
844 				curtype = drvbase::dashed;
845 			}
846 			break;
847 		default:
848 			curtype = drvbase::dashed;
849 			break;
850 		}
851 	} else {
852 		// no entry
853 		curtype = drvbase::solid;
854 	}
855 //   errf << "linetype from " << dashPattern() << " is " << curtype << endl;
856 	setCurrentLineType(curtype);
857 }
858 
dumpImage()859 void drvbase::dumpImage()
860 {
861 	flushOutStanding();					// dump last path to avoid wrong sequence of text and graphics
862 	add_to_page();
863 	imageInfo.calculateBoundingBox();
864 	show_image(imageInfo);
865 	delete[]imageInfo.data;
866 	imageInfo.nextfreedataitem = 0;
867 	imageInfo.data = 0;
868 }
869 
nrOfSubpaths() const870 unsigned int drvbase::nrOfSubpaths() const
871 {
872 	unsigned int nr = 0;
873 	for (unsigned int n = 0; n + 1 < numberOfElementsInPath(); n++) {
874 		const basedrawingelement & elem = pathElement(n);
875 		if (elem.getType() == moveto)
876 			nr++;
877 	}
878 	return nr;
879 }
880 
881 
dumpRearrangedPathes()882 void drvbase::dumpRearrangedPathes()
883 {
884 	// Count the subpaths
885 	unsigned int numpaths = nrOfSubpaths();
886 	if (verbose)
887 		errf << "numpaths: " << numpaths << endl;
888 	// Rearrange the path if necessary
889 	if ((numpaths > 1) && (currentLineWidth() == 0.0) && (currentShowType() != drvbase::stroke)) {
890 		if (verbose)
891 			errf << "Starting rearrangment of subpaths" << endl;
892 		outputPath->rearrange();
893 		numpaths = nrOfSubpaths();
894 	}
895 	if (!numpaths)
896 		numpaths = 1;
897 
898 	const unsigned int origCount = numberOfElementsInPath();
899 	unsigned int starti = 0;
900 	for (unsigned int i = 0; i < numpaths; i++) {
901 		unsigned int endi = starti;
902 		outputPath->subpathoffset = 0;
903 		for ( ; ; ) { // while true but without compiler warning
904 			// Find the next end index
905 			endi++;
906 			if (endi >= origCount)
907 				break;
908 			else if (pathElement(endi).getType() == moveto)
909 				break;
910 		}
911 		if (endi <= origCount) {
912 			if (verbose)
913 				errf << "dumping subpath from " << starti << " to " << endi << endl;
914 			outputPath->subpathoffset = starti;
915 			outputPath->numberOfElementsInPath = endi - starti;
916 			show_or_convert_path();		// from start to end
917 		}
918 		starti = endi;
919 	}
920 	outputPath->numberOfElementsInPath = origCount;
921 	outputPath->subpathoffset = 0;
922 }
923 
close_output_file_and_reopen_in_binary_mode()924 bool drvbase::close_output_file_and_reopen_in_binary_mode()
925 {
926 	if (Verbose()) cerr << "begin close_output_file_and_reopen_in_binary_mode" << endl;
927 
928 	if (outFileName.length() || (&outf != &cout) )
929 		// output is to a file, and outf is not cout
930 	{
931 	 	ofstream *outputFilePtr = (ofstream *) (& outf);
932 //		ofstream *outputFilePtr = dynamic_cast<ofstream *> (& outf);
933 
934 		//dbg cerr << "outputfileptr = " << (void*) outputFilePtr << " outf " << (void*) (&outf)<< endl;
935 
936 		outputFilePtr->close();
937 		if (Verbose()) cerr << "after close " << endl;
938 #if (defined(unix) || defined(__unix__) || defined(_unix) || defined(__unix) || defined(__EMX__) || defined (NetBSD)  ) && !defined(DJGPP)
939 		// binary is not available on UNIX, only on PC
940 		outputFilePtr->open(outFileName.c_str(), ios::out);
941 #else
942 		// use redundant ios::out because of bug in djgpp
943 		outputFilePtr->open(outFileName.c_str(), ios::out | ios::binary);
944 
945 #endif
946 		if (Verbose()) cerr << "after open " << endl;
947 		return true;
948 	} else {
949 		cerr << "Error: This driver cannot write to stdout since it writes binary data " << endl;
950 		return false;
951 	}
952 //	return 0; // not reached - but to make some compilers happy
953 }
954 
955 
956 
beginClipPath()957 void drvbase::beginClipPath()
958 {
959 	// now we start a clippath, so we need to dump
960 	// all previous pathes
961 	flushOutStanding();
962 	last_currentPath = currentPath;
963 	currentPath = &clippath;
964 	outputPath = currentPath;
965 	setCurrentShowType(drvbase::stroke);
966 }
967 
endClipPath(cliptype clipmode)968 void drvbase::endClipPath(cliptype clipmode)
969 {
970 	add_to_page();
971 	ClipPath(clipmode);
972 	clippath.clear();
973 	currentPath = last_currentPath;
974 	outputPath = currentPath;
975 }
976 
977 // default versions
978 //  virtual
ClipPath(cliptype)979 void drvbase::ClipPath(cliptype /* clipmode */ )
980 {
981 }
982 
983 //  virtual
Save()984 void drvbase::Save()
985 {
986 }
987 
988 //  virtual
Restore()989 void drvbase::Restore()
990 {
991 }
992 
flushOutStanding(flushmode_t flushmode)993 void drvbase::flushOutStanding( flushmode_t flushmode )
994 {
995 	switch ( flushmode ) {
996 		case  flushall:
997 			// this needs to be fixed concerning the ordering (which was first  - the text or the path)
998 			flushOutStanding(flushpath);
999 			flushOutStanding(flushtext);
1000 			break;
1001 		case flushtext:
1002 			if (textIsWorthToPrint(mergedTextInfo.thetext.c_str())) {
1003 				flushTextBuffer(true);
1004 				mergedTextInfo.thetext="";		// clear the merge buffer
1005 			}
1006 			break;
1007 		case flushpath:
1008 			dumpPath(false); // false -> no flush text
1009 			break;
1010 		default:
1011 			break;
1012 	}
1013 }
1014 
dumpPath(bool doFlushText)1015 void drvbase::dumpPath(bool doFlushText)
1016 {
1017 	if (doFlushText) flushOutStanding(flushtext); // flush text, so merge is not supported in case of
1018 								 // text path text sequence
1019 
1020 	guess_linetype();			 // needs to be done here, because we must write to currentpath
1021 
1022 #ifdef fixlater
1023 	// this does not work as it is at the moment since
1024 	// * it changes the showtype also for subsequent segments which might have
1025 	//   more than 2 points AND
1026 	// * it is not valid, if the only element (besides moveto) is a curveto.
1027 
1028 	if (currentPath->numberOfElementsInPath == 2) {
1029 		// a polygon with two points is drawn as a line
1030 
1031 		// PROBLEM ! This resetting has an impact on the subsequent segments
1032 		// if subpathes are not supported by the backend !!!!
1033 		currentPath->isPolygon = false;
1034 		currentPath->currentShowType = drvbase::stroke;
1035 	}
1036 #endif
1037 
1038 	if (currentPath->currentShowType != drvbase::stroke) {
1039 		/* don't show border with fill */
1040 		setCurrentLineWidth(0.0f);
1041 	}
1042 
1043 	if (domerge && pathsCanBeMerged(PI1, PI2)) {
1044 		// make PI1 the outputPath and clear PI2
1045 		if (verbose) {
1046 			errf << "Path " << PI1.nr << " type " << (int) PI1.currentShowType << endl;
1047 			errf << PI1.fillR << " " << PI1.fillG << " " << PI1.fillB << endl;
1048 			errf << PI1.edgeR << " " << PI1.edgeG << " " << PI1.edgeB << endl;
1049 			errf << PI1.currentLineWidth << endl;
1050 
1051 			errf << "Path " << PI2.nr << " type " << (int) PI2.currentShowType << endl;
1052 			errf << PI2.fillR << " " << PI2.fillG << " " << PI2.fillB << endl;
1053 			errf << PI2.edgeR << " " << PI2.edgeG << " " << PI2.edgeB << endl;
1054 			errf << PI2.currentLineWidth << endl;
1055 			errf << " have been merged\n";
1056 		}
1057 		// merge PI2 into PI1
1058 		if (PI1.currentShowType == stroke) {
1059 			// PI2 is the fill
1060 			PI1.currentShowType = PI2.currentShowType;
1061 			PI1.fillR = PI2.fillR;
1062 			PI1.fillG = PI2.fillG;
1063 			PI1.fillB = PI2.fillB;
1064 		} else {
1065 			// PI1 is the fill, so copy the line parameters from PI2
1066 			PI1.currentLineWidth = PI2.currentLineWidth;
1067 			PI1.edgeR = PI2.edgeR;
1068 			PI1.edgeG = PI2.edgeG;
1069 			PI1.edgeB = PI2.edgeB;
1070 		}
1071 		if (verbose) {
1072 			errf << " result is \n";
1073 			errf << "Path " << PI1.nr << " type " << (int) PI1.currentShowType << endl;
1074 			errf << PI1.fillR << " " << PI1.fillG << " " << PI1.fillB << endl;
1075 			errf << PI1.edgeR << " " << PI1.edgeG << " " << PI1.edgeB << endl;
1076 			errf << PI1.currentLineWidth << endl;
1077 		}
1078 		outputPath = &PI1;
1079 		PI1.pathWasMerged = true;
1080 		PI2.clear();
1081 	} else {
1082 		outputPath = lastPath;
1083 	}
1084 	if (numberOfElementsInPath() > 0) {
1085 
1086 		// nothing to do for empty pathes
1087 		// pathes may be empty due to a merge operation
1088 
1089 		if (verbose) {
1090 			errf << "working on";
1091 			switch (currentShowType()) {
1092 			case drvbase::stroke:
1093 				errf << " stroked ";
1094 				break;
1095 			case drvbase::fill:
1096 				errf << " filled ";
1097 				break;
1098 			case drvbase::eofill:
1099 				errf << " eofilled ";
1100 				break;
1101 			default:
1102 				break;
1103 			}
1104 			errf << "path " << currentNr() << " with " <<
1105 				numberOfElementsInPath() << " elements" << endl;
1106 		}
1107 
1108 		if (numberOfElementsInPath() > 1) {
1109 			// cannot draw single points
1110 			add_to_page();
1111 			if (isPolygon()) {	/* PolyGon */
1112 				if (is_a_rectangle()) {
1113 					const float llx =
1114 						min(min
1115 							(pathElement(0).getPoint(0).x_,
1116 							 pathElement(1).getPoint(0).x_),
1117 							min(pathElement(2).getPoint(0).x_, pathElement(3).getPoint(0).x_));
1118 					const float urx =
1119 						max(max
1120 							(pathElement(0).getPoint(0).x_,
1121 							 pathElement(1).getPoint(0).x_),
1122 							max(pathElement(2).getPoint(0).x_, pathElement(3).getPoint(0).x_));
1123 					const float lly =
1124 						min(min
1125 							(pathElement(0).getPoint(0).y_,
1126 							 pathElement(1).getPoint(0).y_),
1127 							min(pathElement(2).getPoint(0).y_, pathElement(3).getPoint(0).y_));
1128 					const float ury =
1129 						max(max
1130 							(pathElement(0).getPoint(0).y_,
1131 							 pathElement(1).getPoint(0).y_),
1132 							max(pathElement(2).getPoint(0).y_, pathElement(3).getPoint(0).y_));
1133 
1134 					show_rectangle(llx, lly, urx, ury);
1135 				} else {
1136 					if (globaloptions.simulateSubPaths)
1137 						dumpRearrangedPathes();
1138 					else
1139 						show_or_convert_path();
1140 				}
1141 			} else {			/* PolyLine */
1142 				if (globaloptions.simulateSubPaths)
1143 					dumpRearrangedPathes();
1144 				else
1145 					show_or_convert_path();
1146 			}
1147 		}
1148 		// cleanup
1149 		outputPath->clear();
1150 	}
1151 	// swap current and last pointers
1152 	PathInfo *help = currentPath;
1153 	currentPath = lastPath; // currentPath will be filled next be Lexer
1154 	lastPath = help;
1155 
1156 	currentPath->copyInfo(*help);	// initialize next path with state of last path
1157 	// currentPath is the path filled next by lexer
1158 
1159 	outputPath = currentPath;
1160 }
1161 
removeFromElementFromPath()1162 void drvbase::removeFromElementFromPath()
1163 {
1164 	currentPath->numberOfElementsInPath--;
1165 }
1166 
addtopath(basedrawingelement * newelement)1167 void drvbase::addtopath(basedrawingelement * newelement) {
1168 	if (newelement) {
1169 	   currentPath->addtopath(newelement, errf);
1170 	} else {
1171 		errf << "Fatal: newelement is NIL in addtopath " << endl;
1172 		exit(1);
1173 	}
1174 }
addtopath(basedrawingelement * newelement,ostream & errf)1175 void drvbase::PathInfo::addtopath(basedrawingelement * newelement,
1176 	                              ostream & errf)
1177 {
1178 #if defined(HAVE_STL) && !defined(USE_FIXED_ARRAY)
1179         if (numberOfElementsInPath < path.size()) {
1180 	      path[numberOfElementsInPath] = newelement;
1181         } else {
1182           path.push_back(newelement);
1183         }
1184 		numberOfElementsInPath++;
1185 		unused(&errf);
1186 #else
1187 		if (numberOfElementsInPath < maxElements) {
1188 			path[numberOfElementsInPath] = newelement;
1189 #ifdef DEBUG
1190 			cout << "pathelement " <<
1191 				numberOfElementsInPath << " added " << *newelement << endl;
1192 #endif
1193 			numberOfElementsInPath++;
1194 		} else {
1195 			errf <<
1196 				"Fatal: number of path elements exceeded. Increase maxElements in drvbase.h"
1197 				<< endl;
1198 			exit(1);
1199 		}
1200 #endif
1201 
1202 }
1203 
clear()1204 void drvbase::PathInfo::clear()
1205 {
1206 	for (unsigned int i = 0; i < numberOfElementsInPath; i++) {
1207 		// delete path[i];
1208 		path[i]->deleteyourself(); // see note in drvbase.h
1209 		path[i] = 0;
1210 	}
1211 	numberOfElementsInPath = 0;
1212 	pathWasMerged = false;
1213 }
1214 
copyInfo(const PathInfo & p)1215 void drvbase::PathInfo::copyInfo(const PathInfo & p)
1216 {
1217 	// copies the whole path state except the path array
1218 	currentShowType = p.currentShowType;
1219 	currentLineType = p.currentLineType;
1220 	currentLineCap = p.currentLineCap;
1221 	currentLineJoin = p.currentLineJoin;
1222 	currentMiterLimit = p.currentMiterLimit;
1223 	nr = p.nr;
1224 	// Path is not copied path(0),
1225 	isPolygon = p.isPolygon;
1226 	// numberOfElementsInPath = p.numberOfElementsInPath;
1227 	currentLineWidth = p.currentLineWidth;
1228 	edgeR = p.edgeR;
1229 	edgeG = p.edgeG;
1230 	edgeB = p.edgeB;
1231 	fillR = p.fillR;
1232 	fillG = p.fillG;
1233 	fillB = p.fillB;
1234 	colorName = p.colorName;
1235 	dashPattern = p.dashPattern;
1236 }
1237 
operator <<(ostream & out,const basedrawingelement & elem)1238 ostream & operator << (ostream & out, const basedrawingelement & elem)
1239 {
1240 	out << "type: " << (int) elem.getType() << " params: ";
1241 	for (unsigned int i = 0; i < elem.getNrOfPoints(); i++) {
1242 		out << elem.getPoint(i).x_ << " " << elem.getPoint(i).y_ << " ";
1243 	}
1244 	out << endl;
1245 	return out;
1246 }
1247 
ColorTable(const char * const * defaultColors,const unsigned int numberOfDefaultColors,makeColorNameType makeColorName)1248 ColorTable::ColorTable(const char *const *defaultColors, const unsigned int numberOfDefaultColors, makeColorNameType makeColorName):
1249 defaultColors_(defaultColors),
1250 numberOfDefaultColors_(numberOfDefaultColors), makeColorName_(makeColorName)
1251 {
1252 //dbg   cerr << " Constructing a color table with " << numberOfDefaultColors << " default colors" << endl;
1253 	for (unsigned int i = 0; i < maxcolors; i++)
1254 		newColors[i] = 0;
1255 //dbg   cerr << 1/(1/numberOfDefaultColors) << endl;
1256 }
1257 
~ColorTable()1258 ColorTable::~ColorTable()
1259 {
1260 	unsigned int current = 0;
1261 	while (newColors[current] != 0) {
1262 		delete[] newColors[current];
1263 		newColors[current] = NIL;
1264 		current++;
1265 	}
1266 	// cannot assign since it is const - defaultColors_ = NIL;
1267 	//lint -esym(1540,ColorTable::defaultColors_)
1268 }
1269 
1270 
getColorIndex(float r,float g,float b)1271 unsigned int ColorTable::getColorIndex(float r, float g, float b)
1272 {
1273 // registers a possibly new color and returns the index
1274 // under which the color was registered
1275 	const char *cmp = makeColorName_(r, g, b);
1276 	for (unsigned int i = 0; i < numberOfDefaultColors_; i++) {
1277 		if (strcmp(cmp, defaultColors_[i]) == 0) {
1278 			return i;
1279 		}
1280 	}
1281 // look in new colors
1282 	unsigned int j ;
1283 	for (j = 0; ((j < maxcolors) && (newColors[j] != 0)); j++) {
1284 		if (strcmp(cmp, newColors[j]) == 0) {
1285 			return j + numberOfDefaultColors_;
1286 		}
1287 	}
1288 // not found so far
1289 // j is either maxcolors or the index of the next free entry
1290 // add a copy to newColors
1291 	if (j < maxcolors) {
1292 		const size_t size = strlen(cmp) + 1;
1293 		newColors[j] = new char[size];
1294 		strcpy_s(newColors[j], size, cmp);
1295 		return j + numberOfDefaultColors_;
1296 	} else {
1297 //      cerr << "running out of colors" << endl;
1298 		return 0;
1299 	}
1300 
1301 }
1302 
getColorString(float r,float g,float b)1303 const char *  ColorTable::getColorString(float r, float g, float b)	// non const
1304 {
1305 	return getColorString(getColorIndex(r, g, b));
1306 }
1307 
isKnownColor(float r,float g,float b) const1308 bool ColorTable::isKnownColor(float r, float g, float b) const
1309 {
1310 // Possible improvements:
1311 // could return the next free entry as negative number in case
1312 // the color is not found. This would make it possible to
1313 // use this function in getColorEntry as well, or (better)
1314 // make a pure registercolor(index,.....) instead of
1315 // getColorEntry.
1316 	const char *cmp = makeColorName_(r, g, b);
1317 	for (unsigned int i = 0; i < numberOfDefaultColors_; i++) {
1318 		if (strcmp(cmp, defaultColors_[i]) == 0) {
1319 			return true;
1320 		}
1321 	}
1322 	// look in new colors
1323 	for (unsigned int j = 0; ((j < maxcolors) && (newColors[j] != 0)); j++) {
1324 		if (strcmp(cmp, newColors[j]) == 0) {
1325 			return true;		// j+numberOfDefaultColors_;
1326 		}
1327 	}
1328 	// not found so far
1329 	return false;
1330 }
1331 
getColorString(unsigned int index) const1332 const char *  ColorTable::getColorString(unsigned int index) const
1333 {
1334 	return (index < numberOfDefaultColors_) ? defaultColors_[index] :
1335 		newColors[index - numberOfDefaultColors_];
1336 }
1337 
1338 
1339 
getDriverDescForName(const char * drivername) const1340 const DriverDescription *DescriptionRegister:: getDriverDescForName(const char *drivername) const
1341 {
1342 	unsigned int i = 0;
1343 	while (rp[i] != 0) {
1344 		if ((strcmp(drivername, rp[i]->symbolicname) == 0)) {
1345 			return rp[i];
1346 		}
1347 		i++;
1348 	}
1349 	return 0;
1350 }
1351 
getDriverDescForSuffix(const char * suffix) const1352 const DriverDescription *DescriptionRegister:: getDriverDescForSuffix(const char *suffix) const
1353 {
1354 	unsigned int i = 0;
1355 	const DriverDescription * founditem = 0;
1356 	while (rp[i] != 0) {
1357 		if ((STRICMP(suffix, rp[i]->suffix) == 0)) {
1358 			if (founditem) {
1359 				// already found an entry for this suffix - so it is not unique -> return 0
1360 				return 0;
1361 			} else {
1362 				founditem = rp[i]; // first chance - but loop throug all items
1363 			}
1364 		}
1365 		i++;
1366 	}
1367 	return founditem;
1368 }
1369 
listdrivers(ostream & out) const1370 void DescriptionRegister::listdrivers(ostream &out) const
1371 {
1372 	unsigned int i = 0;
1373 	while (rp[i] != 0) {
1374 		out << rp[i]->symbolicname << ",";
1375 		out << rp[i]->suffix << ",";
1376 		out << rp[i]->short_explanation << "," << rp[i]->additionalInfo();
1377 		out << "\t(" << rp[i]->filename << ")" << endl;
1378 		i++;
1379 	}
1380 }
explainformats(ostream & out,bool withdetails) const1381 void DescriptionRegister::explainformats(ostream & out, bool withdetails) const
1382 {
1383 	if (withdetails) {
1384 		// out << "\\subsection{Available formats and their specific options}" << endl;
1385 	} else {
1386 		out << "Available formats :\n";
1387 	}
1388 	unsigned int i = 0;
1389 	while (rp[i] != 0) {
1390 		if (withdetails) {
1391 			out << "\\subsubsection{" << rp[i]->symbolicname << " - " << rp[i]->short_explanation <<"}" << endl;
1392 			if (strlen(rp[i]->long_explanation)>0) { out << rp[i]->long_explanation << endl << endl; }
1393 		} else {
1394 			out << '\t' << rp[i]->symbolicname << ":\t";
1395 			if (strlen(rp[i]->symbolicname) < 7) {
1396 				out << '\t';
1397 			}
1398 			out << "\t." << rp[i]->suffix << ":\t";
1399 			out << rp[i]->short_explanation << " " << rp[i]->additionalInfo();
1400 		}
1401 
1402 		if (!withdetails && rp[i]->checkfunc) {
1403 			if (!(rp[i]->checkfunc())) {
1404 				out << " (no valid key found)";
1405 			}
1406 		}
1407 
1408 		if (!withdetails) out << "\t(" << rp[i]->filename << ")" << endl;
1409 
1410 		ProgramOptions* dummy = rp[i]->createDriverOptions();
1411 		if (!withdetails && dummy->numberOfOptions() ) {
1412 			out << "This driver supports the following additional options: (specify using -f \"format:-option1 -option2\")" << endl;
1413 		}
1414 		dummy->showhelp(out,withdetails,withdetails);
1415 		delete dummy;
1416 
1417 		if (withdetails) {
1418 			out << "%%// end of options" << endl;
1419 		} else {
1420 			out << "-------------------------------------------" << endl;
1421 		}
1422 
1423 		i++;
1424 	}
1425 }
mergeRegister(ostream & out,const DescriptionRegister & src,const char * filename)1426 void DescriptionRegister::mergeRegister(ostream & out,
1427 										const DescriptionRegister & src, const char *filename)
1428 {
1429 	int i = 0;
1430 	while (src.rp[i]) {
1431 		const unsigned int srcversion = src.rp[i]->getdrvbaseVersion();
1432 		if (srcversion != 0) {
1433 			if (srcversion == drvbaseVersion) {
1434 				src.rp[i]->filename = filename;
1435 				registerDriver(src.rp[i]);
1436 			} else {
1437 				out << src.rp[i]->short_explanation << "(" << filename << ")" <<
1438 					" - backend has other version than expected by pstoedit core "
1439 					<< srcversion << " <> " << drvbaseVersion << endl;
1440 				out <<
1441 					"The pstoedit.dll (core) and the additional DLLs (plugins.dll or importps.dll) must have the same version number."
1442 					<< endl;
1443 				out <<
1444 					"Please get a consistent set of pstoedit.dll (plugins.dll and or importps.dll) from www.pstoedit.net/pstoedit/ "
1445 					<< endl;
1446 
1447 			}
1448 		}
1449 		i++;
1450 	}
1451 }
registerDriver(DriverDescription * xp)1452 void DescriptionRegister::registerDriver(DriverDescription * xp)
1453 {
1454 	//  cout << " registering " << (void *) xp << endl;
1455 	// check for duplicate:
1456 	for (int i = 0; i < ind; i++) {
1457 		if (strcmp(rp[i]->symbolicname, xp->symbolicname) == 0) {
1458 			// duplicate found
1459 			if (xp->checkfunc && xp->checkfunc() && !(rp[i]->checkfunc())) {
1460 				// the new one has a license, so use this instead
1461 				rp[i] = xp;
1462 			}
1463 			return; // just use the first version - except for the above case.
1464 		}
1465 	}
1466 	rp[ind] = xp;
1467 	ind++;
1468 }
1469 
1470 // int Rinit::ref = 0;
1471 DLLEXPORT DescriptionRegister *globalRp = 0;
1472 
getglobalRp()1473 extern "C" DLLEXPORT DescriptionRegister * getglobalRp()
1474 {
1475 	return &DescriptionRegister::getInstance();
1476 }
1477 
1478 #ifdef  BUGGYGPP
transform(const float * matrix) const1479 Point Point::transform(const float * matrix) const
1480 #else
1481 Point Point::transform(const float matrix[6]) const
1482 #endif
1483 {
1484 	const float tx = matrix[0] * x_ + matrix[2] * y_ + matrix[4];
1485 	const float ty = matrix[1] * x_ + matrix[3] * y_ + matrix[5];
1486 	return  Point(tx, ty);
1487 }
1488 
1489 
1490 
1491 const char * DriverDescription::currentfilename = "built-in";
DriverDescription(const char * const s_name,const char * const short_expl,const char * const long_expl,const char * const suffix_p,const bool backendSupportsSubPathes_p,const bool backendSupportsCurveto_p,const bool backendSupportsMerging_p,const bool backendSupportsText_p,const imageformat backendDesiredImageFormat_p,const opentype backendFileOpenType_p,const bool backendSupportsMultiplePages_p,const bool backendSupportsClipping_p,const bool nativedriver_p,checkfuncptr checkfunc_p)1492 DriverDescription::DriverDescription(	const char *const s_name,
1493 										const char *const short_expl,
1494 										const char *const long_expl,
1495 										const char *const suffix_p,
1496 										const bool backendSupportsSubPathes_p,
1497 										const bool backendSupportsCurveto_p,
1498 										const bool backendSupportsMerging_p,	// merge a separate outline and filling of a polygon -> 1. element
1499 										const bool backendSupportsText_p,
1500 										const imageformat backendDesiredImageFormat_p,
1501 										const opentype backendFileOpenType_p,
1502 										const bool backendSupportsMultiplePages_p,
1503 										const bool backendSupportsClipping_p,
1504 										const bool nativedriver_p,
1505 										checkfuncptr checkfunc_p):
1506 	symbolicname(s_name),
1507 	short_explanation(short_expl),
1508 	long_explanation(long_expl),
1509 	suffix(suffix_p),
1510 	backendSupportsSubPathes(backendSupportsSubPathes_p),
1511 	backendSupportsCurveto(backendSupportsCurveto_p),
1512 	backendSupportsMerging(backendSupportsMerging_p),	// merge a separate outline and filling of a polygon -> 1. element
1513 	backendSupportsText(backendSupportsText_p),
1514 	backendDesiredImageFormat(backendDesiredImageFormat_p),
1515 	backendFileOpenType(backendFileOpenType_p),
1516 	backendSupportsMultiplePages(backendSupportsMultiplePages_p),
1517 	backendSupportsClipping(backendSupportsClipping_p),
1518 	nativedriver(nativedriver_p),
1519 	filename(DriverDescription::currentfilename),
1520 	checkfunc(checkfunc_p)
1521 {
1522 	DescriptionRegister & registry = DescriptionRegister::getInstance();
1523     //dbg	cout << "registering driver " << s_name << "\t at registry " << (void*) &registry << endl;
1524 	registry.registerDriver(this);
1525 }
1526 
additionalInfo() const1527 const char * DriverDescription::additionalInfo() const {
1528 	return ((checkfunc != 0) ? (checkfunc()? "" : "(license key needed, see pstoedit manual)") : "");
1529 }
1530 
1531 #if 0
1532 // not needed - pure virtual
1533 drvbase *DriverDescription::
1534 CreateBackend(const char *const driveroptions_P, ostream & theoutStream,
1535 			  ostream & theerrStream, const char *const nameOfInputFile,
1536 			  const char *const nameOfOutputFile,
1537 			  const PsToEditOptions & globaloptions) const
1538 {
1539 	unused(driveroptions_P);
1540 	unused(&theoutStream);
1541 	unused(&theerrStream);
1542 	unused(nameOfInputFile);
1543 	unused(nameOfOutputFile);
1544 	unused(&globaloptions);
1545 	return 0;
1546 }
1547 #endif
1548 
1549 #if 0
1550 // def BUGGYGPP
1551 //
1552 // GNU g++ causes a seg fault, when the singleton instance is destroyed triggered by a dlclose()
1553 // so we have to live with this small memory leak due to the allocation on the heap
1554 //
1555 // This problem is now solved by the removal of the dtor for this class.
1556 //
1557 DescriptionRegister & DescriptionRegister::getInstance()
1558 {
1559 	static DescriptionRegister * theSingleInstance = new DescriptionRegister;
1560 	globalRp = theSingleInstance;
1561 	return *theSingleInstance;
1562 }
1563 #else
getInstance()1564 DescriptionRegister & DescriptionRegister::getInstance()
1565 {
1566 	static DescriptionRegister theSingleInstance;
1567 	globalRp = &theSingleInstance;
1568 	return theSingleInstance;
1569 }
1570 #endif
1571 
1572 //
1573 // SINGLETONSONHEAP might be useful if problems occur during the unloading of libpstoedit.so
1574 //
1575 
1576 // Implementation of SingleTon "Objects".
1577 
bboxes()1578 BBox * drvbase::bboxes() {	// array of bboxes - maxpages long
1579 #ifdef SINGLETONSONHEAP
1580 	static BBox * dummy = new BBox[maxPages];	return dummy;
1581 #else
1582 	static BBox dummy[maxPages]; return &(dummy[0]);
1583 #endif
1584 }
1585 
totalNumberOfPages()1586 unsigned int &drvbase::totalNumberOfPages() {
1587 	// using the singleton pattern for easier linkage
1588 	static unsigned int nrOfPages = 0;
1589 	return nrOfPages;
1590 }
1591 
pstoeditHomeDir()1592 RSString& drvbase::pstoeditHomeDir(){// usually the place where the binary is installed
1593 #ifdef SINGLETONSONHEAP
1594 	static RSString *dummy = new RSString(""); 	return *dummy;
1595 #else
1596 	static RSString dummy(""); 	return dummy;
1597 #endif
1598 }
pstoeditDataDir()1599 RSString& drvbase::pstoeditDataDir() {// where the fmp and other data files are stored
1600 #ifdef SINGLETONSONHEAP
1601 	static RSString *dummy = new RSString(""); 	return *dummy;
1602 #else
1603 	static RSString dummy(""); 	return dummy;
1604 #endif
1605 }
1606 
1607 // the global static FontMapper
theFontMapper()1608 FontMapper& drvbase::theFontMapper() {
1609 #ifdef SINGLETONSONHEAP
1610 	static FontMapper *dummy = new FontMapper;	return *dummy;
1611 #else
1612 	static FontMapper dummy;	return dummy;
1613 #endif
1614 }
1615 
1616 bool drvbase::verbose = false; // offensichtlich kann man keine initialisierten Daten DLLEXPORTieren
Verbose()1617 bool drvbase::Verbose() { return verbose; }
SetVerbose(bool param)1618 void drvbase::SetVerbose(bool param) { verbose = param; }
1619 
1620