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*) ®istry << 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