"Fossies" - the Fresh Open Source Software Archive

Member "pstoedit-3.78/src/drvbase.cpp" (5 Sep 2021, 50934 Bytes) of package /linux/misc/pstoedit-3.78.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "drvbase.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.77_vs_3.78.

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