[KLF Application][KLF Tools][KLF Backend][KLF Home]
KLatexFormula Project

src/klfbackend/klfbackend.cpp

00001 /***************************************************************************
00002  *   file klfbackend.cpp
00003  *   This file is part of the KLatexFormula Project.
00004  *   Copyright (C) 2011 by Philippe Faist
00005  *   philippe.faist at bluewin.ch
00006  *                                                                         *
00007  *   This program is free software; you can redistribute it and/or modify  *
00008  *   it under the terms of the GNU General Public License as published by  *
00009  *   the Free Software Foundation; either version 2 of the License, or     *
00010  *   (at your option) any later version.                                   *
00011  *                                                                         *
00012  *   This program is distributed in the hope that it will be useful,       *
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015  *   GNU General Public License for more details.                          *
00016  *                                                                         *
00017  *   You should have received a copy of the GNU General Public License     *
00018  *   along with this program; if not, write to the                         *
00019  *   Free Software Foundation, Inc.,                                       *
00020  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00021  ***************************************************************************/
00022 /* $Id: klfbackend.cpp 863 2013-11-23 13:14:34Z phfaist $ */
00023 
00024 #include <stdio.h>
00025 #include <stdlib.h>
00026 #include <sys/time.h>
00027 
00028 #include <qapplication.h>
00029 #include <qregexp.h>
00030 #include <qfile.h>
00031 #include <qdatetime.h>
00032 #include <qtextstream.h>
00033 #include <qbuffer.h>
00034 #include <qdir.h>
00035 
00036 #include "klfblockprocess.h"
00037 #include "klfbackend.h"
00038 
00039 // write Qt 3/4 compatible code
00040 #include "klfqt34common.h"
00041 
00042 
00073 // some standard guess settings for system configurations
00074 
00075 #ifdef KLF_EXTRA_SEARCH_PATHS
00076 #  define EXTRA_PATHS_PRE       KLF_EXTRA_SEARCH_PATHS ,
00077 #  define EXTRA_PATHS           KLF_EXTRA_SEARCH_PATHS
00078 #else
00079 #  define EXTRA_PATHS_PRE
00080 #  define EXTRA_PATHS
00081 #endif
00082 
00083 
00084 #if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
00085 QStringList progLATEX = QStringList() << "latex.exe";
00086 QStringList progDVIPS = QStringList() << "dvips.exe";
00087 QStringList progGS = QStringList() << "gswin32c.exe" << "mgs.exe";
00088 QStringList progEPSTOPDF = QStringList() << "epstopdf.exe";
00089 static const char * standard_extra_paths[] = {
00090   EXTRA_PATHS_PRE
00091   "C:\\Program Files\\MiKTeX*\\miktex\\bin",
00092   "C:\\Program Files\\gs\\gs*\\bin",
00093   NULL
00094 };
00095 #elif defined(Q_WS_MAC)
00096 QStringList progLATEX = QStringList() << "latex";
00097 QStringList progDVIPS = QStringList() << "dvips";
00098 QStringList progGS = QStringList() << "gs";
00099 QStringList progEPSTOPDF = QStringList() << "epstopdf";
00100 static const char * standard_extra_paths[] = {
00101   EXTRA_PATHS_PRE
00102   "/usr/texbin:/usr/local/bin:/sw/bin:/sw/usr/bin",
00103   NULL
00104 };
00105 #else
00106 QStringList progLATEX = QStringList() << "latex";
00107 QStringList progDVIPS = QStringList() << "dvips";
00108 QStringList progGS = QStringList() << "gs";
00109 QStringList progEPSTOPDF = QStringList() << "epstopdf";
00110 static const char * standard_extra_paths[] = {
00111   EXTRA_PATHS_PRE
00112   NULL
00113 };
00114 #endif
00115 
00116 
00117 
00118 // ---------------------------------
00119 
00120 KLFBackend::KLFBackend()
00121 {
00122 }
00123 
00124 
00125 // Utility function
00126 QString progErrorMsg(QString progname, int exitstatus, QString stderrstr, QString stdoutstr)
00127 {
00128   QString stdouthtml = stdoutstr;
00129   QString stderrhtml = stderrstr;
00130   stdouthtml.replace("&", "&amp;");
00131   stdouthtml.replace("<", "&lt;");
00132   stdouthtml.replace(">", "&gt;");
00133   stderrhtml.replace("&", "&amp;");
00134   stderrhtml.replace("<", "&lt;");
00135   stderrhtml.replace(">", "&gt;");
00136 
00137   if (stderrstr.isEmpty() && stdoutstr.isEmpty())
00138     return QObject::tr("<p><b>%1</b> reported an error (exit status %2). No Output was generated.</p>",
00139                        "KLFBackend")
00140         .arg(progname).arg(exitstatus);
00141   if (stderrstr.isEmpty())
00142     return
00143       QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stdout output:</p>\n"
00144                   "<pre>\n%3</pre>", "KLFBackend")
00145       .arg(progname).arg(exitstatus).arg(stdouthtml);
00146   if (stdoutstr.isEmpty())
00147     return
00148       QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
00149                   "<pre>\n%3</pre>", "KLFBackend")
00150       .arg(progname).arg(exitstatus).arg(stderrhtml);
00151   
00152   return QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
00153                      "<pre>\n%3</pre><p>And here is full stdout output:</p><pre>\n%4</pre>", "KLFBackend")
00154     .arg(progname).arg(exitstatus).arg(stderrhtml).arg(stdouthtml);
00155 }
00156 
00157 
00158 /* * Internal.
00159  * \internal */
00160 struct cleanup_caller {
00161   QString tempfname;
00162   cleanup_caller(QString fn) : tempfname(fn) { }
00163   ~cleanup_caller() {
00164     KLFBackend::cleanup(tempfname);
00165   }
00166 };
00167 
00168 QString __klf_expand_env_vars(const QString& envexpr)
00169 {
00170   QString s = envexpr;
00171   QRegExp rx("\\$(?:(\\$|(?:[A-Za-z0-9_]+))|\\{([A-Za-z0-9_]+)\\})");
00172   int i = 0;
00173   while ( (i = rx.rx_indexin_i(s, i)) != -1 ) {
00174     // match found, replace it
00175     QString envvarname = rx.cap(1);
00176     if (envvarname.isEmpty() || envvarname == QLatin1String("$")) {
00177       // note: empty variable name expands to a literal '$'
00178       s.replace(i, rx.matchedLength(), QLatin1String("$"));
00179       i += 1;
00180       continue;
00181     }
00182     const char *svalue = getenv(qPrintable(envvarname));
00183     QString qsvalue = (svalue != NULL) ? QString::fromLocal8Bit(svalue) : QString();
00184     s.replace(i, rx.matchedLength(), qsvalue);
00185     i += qsvalue.length();
00186   }
00187   // replacements performed
00188   return s;
00189 }
00190 
00191 void __klf_append_replace_env_var(QStringList *list, const QString& var, const QString& line)
00192 {
00193   // search for declaration of var in list
00194   int k;
00195   for (k = 0; k < (int)list->size(); ++k) {
00196     if (list->operator[](k).startsWith(var+QString("="))) {
00197       list->operator[](k) = line;
00198       return;
00199     }
00200   }
00201   // declaration not found, just append
00202   list->append(line);
00203 }
00204 
00205 
00206 
00207 // utilities for dealing with bounding boxes in EPS file
00208 // (backported from 3.3)
00209 
00210 // A Bounding Box
00211 struct klfbbox {
00212   double x1, x2, y1, y2;
00213 };
00214 static bool read_eps_bbox(const QByteArray& epsdata, klfbbox *bbox, KLFBackend::klfOutput * resError);
00215 static void correct_eps_bbox(const QByteArray& epsdata, const klfbbox& bbox_corrected, const klfbbox& bbox_orig,
00216                              double vectorscale, QByteArray * epsdatacorrected);
00217 
00218 
00219 
00220 
00221 
00222 
00223 KLFBackend::klfOutput KLFBackend::getLatexFormula(const klfInput& in, const klfSettings& settings)
00224 {
00225   // ALLOW ONLY ONE RUNNING getLatexFormula() AT A TIME 
00226   QMutexLocker mutexlocker(&__mutex);
00227 
00228   int k;
00229 
00230   qDebug("%s: %s: KLFBackend::getLatexFormula() called. latex=%s", KLF_FUNC_NAME, KLF_SHORT_TIME,
00231          qPrintable(in.latex));
00232 
00233   // get full, expanded exec environment
00234   QStringList execenv = klf_cur_environ();
00235   for (k = 0; k < (int)settings.execenv.size(); ++k) {
00236     int eqpos = settings.execenv[k].s_indexOf(QChar('='));
00237     if (eqpos == -1) {
00238       qWarning("%s: badly formed environment definition in `environ': %s", KLF_FUNC_NAME,
00239                qPrintable(settings.execenv[k]));
00240       continue;
00241     }
00242     QString varname = settings.execenv[k].mid(0, eqpos);
00243     QString newenvdef = __klf_expand_env_vars(settings.execenv[k]);
00244     __klf_append_replace_env_var(&execenv, varname, newenvdef);
00245   }
00246 
00247   klfDbg("execution environment for sub-processes:\n"+execenv.join("\n")) ;
00248   
00249   klfOutput res;
00250   res.status = KLFERR_NOERROR;
00251   res.errorstr = QString();
00252   res.result = QImage();
00253   res.pngdata_raw = QByteArray();
00254   res.pngdata = QByteArray();
00255   res.epsdata = QByteArray();
00256   res.pdfdata = QByteArray();
00257   res.input = in;
00258   res.settings = settings;
00259 
00260   // PROCEDURE:
00261   // - generate LaTeX-file
00262   // - latex --> get DVI file
00263   // - dvips -E file.dvi it to get an EPS file
00264   // - expand BBox by editing EPS file (if applicable)
00265   // - outline fonts with gs (if applicable)
00266   // - Run gs:  gs -dNOPAUSE -dSAFER -dEPSCrop -r600 -dTextAlphaBits=4 -dGraphicsAlphaBits=4
00267   //               -sDEVICE=pngalpha|png16m -sOutputFile=xxxxxx.png -q -dBATCH xxxxxx.eps
00268   //   to eventually get PNG data
00269   // - if epstopdfexec is not empty, run epstopdf and get PDF file.
00270 
00271   QString tempfname = settings.tempdir + "/klatexformulatmp" KLF_VERSION_STRING "-"
00272     + QDateTime::currentDateTime().toString("hh-mm-ss")
00273 #ifdef KLFBACKEND_QT4
00274     + "-p"+ QString("%1").arg(QApplication::applicationPid(), 0, 26)
00275 #else
00276     + "-p" + QString("%1").arg(rand()%100000, 0, 26)
00277 #endif
00278     ;
00279 
00280   QString fnTex = tempfname + ".tex";
00281   QString fnDvi = tempfname + ".dvi";
00282   QString fnRawEps = tempfname + "-raw.eps";
00283   QString fnBBCorrEps = tempfname + "-bbcorr.eps";
00284   QString fnOutlFontsEps = tempfname + "-outlfonts.eps";
00285   QString fnFinalEps = settings.outlineFonts ? fnOutlFontsEps : fnBBCorrEps;
00286   QString fnPng = tempfname + ".png";
00287   QString fnPdf = tempfname + ".pdf";
00288 
00289   // upon destruction (on stack) of this object, cleanup() will be
00290   // automatically called as wanted
00291   cleanup_caller cleanupcallerinstance(tempfname);
00292 
00293 #ifdef KLFBACKEND_QT4
00294   QString latexsimplified = in.latex.s_trimmed();
00295 #else
00296   QString latexsimplified = in.latex.stripWhiteSpace();
00297 #endif
00298   if (latexsimplified.isEmpty()) {
00299     res.errorstr = QObject::tr("You must specify a LaTeX formula!", "KLFBackend");
00300     res.status = KLFERR_MISSINGLATEXFORMULA;
00301     return res;
00302   }
00303 
00304   QString latexin;
00305   if (!in.bypassTemplate) {
00306     if (in.mathmode.contains("...") == 0) {
00307       res.status = KLFERR_MISSINGMATHMODETHREEDOTS;
00308       res.errorstr = QObject::tr("The math mode string doesn't contain '...'!", "KLFBackend");
00309       return res;
00310     }
00311     latexin = in.mathmode;
00312     latexin.replace("...", in.latex);
00313   }
00314 
00315   {
00316     QFile file(fnTex);
00317     bool r = file.open(dev_WRITEONLY);
00318     if ( ! r ) {
00319       res.status = KLFERR_TEXWRITEFAIL;
00320       res.errorstr = QObject::tr("Can't open file for writing: '%1'!", "KLFBackend")
00321         .arg(fnTex);
00322       return res;
00323     }
00324     QTextStream stream(&file);
00325     if (!in.bypassTemplate) {
00326       stream << "\\documentclass{article}\n"
00327              << "\\usepackage[dvips]{color}\n"
00328              << in.preamble << "\n"
00329              << "\\begin{document}\n"
00330              << "\\thispagestyle{empty}\n"
00331              << QString("\\definecolor{klffgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.fg_color)/255.0)
00332         .arg(qGreen(in.fg_color)/255.0).arg(qBlue(in.fg_color)/255.0)
00333              << QString("\\definecolor{klfbgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.bg_color)/255.0)
00334         .arg(qGreen(in.bg_color)/255.0).arg(qBlue(in.bg_color)/255.0)
00335              << ( (qAlpha(in.bg_color)>0) ? "\\pagecolor{klfbgcolor}\n" : "" )
00336              << "{\\color{klffgcolor} " << latexin << " }\n"
00337              << "\\end{document}\n";
00338     } else {
00339       stream << in.latex;
00340     }
00341   }
00342 
00343   { // execute latex
00344 
00345     KLFBlockProcess proc;
00346     QStringList args;
00347 
00348     proc.setWorkingDirectory(settings.tempdir);
00349 
00350     args << settings.latexexec << dir_native_separators(fnTex);
00351 
00352     qDebug("%s: %s:  about to exec latex...", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00353     bool r = proc.startProcess(args, execenv);
00354     qDebug("%s: %s:  latex returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00355 
00356     if (!r) {
00357       res.status = KLFERR_NOLATEXPROG;
00358       res.errorstr = QObject::tr("Unable to start Latex program %1!", "KLFBackend")
00359         .arg(settings.latexexec);
00360       return res;
00361     }
00362     if (!proc.processNormalExit()) {
00363       res.status = KLFERR_LATEXNONORMALEXIT;
00364       res.errorstr = QObject::tr("Latex was killed!", "KLFBackend");
00365       return res;
00366     }
00367     if (proc.processExitStatus() != 0) {
00368       res.status = KLFERR_PROGERR_LATEX;
00369       res.errorstr = progErrorMsg("LaTeX", proc.processExitStatus(), proc.readStderrString(),
00370                                   proc.readStdoutString());
00371       return res;
00372     }
00373 
00374     if (!QFile::exists(fnDvi)) {
00375       res.status = KLFERR_NODVIFILE;
00376       res.errorstr = QObject::tr("DVI file didn't appear after having called Latex!", "KLFBackend");
00377       return res;
00378     }
00379 
00380   } // end of 'latex' block
00381 
00382   { // execute dvips -E
00383 
00384     KLFBlockProcess proc;
00385     QStringList args;
00386     args << settings.dvipsexec << "-E" << dir_native_separators(fnDvi)
00387          << "-o" << dir_native_separators(fnRawEps);
00388 
00389     qDebug("%s: %s:  about to dvips... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
00390     bool r = proc.startProcess(args, execenv);
00391     qDebug("%s: %s:  dvips returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00392 
00393     if ( ! r ) {
00394       res.status = KLFERR_NODVIPSPROG;
00395       res.errorstr = QObject::tr("Unable to start dvips!\n", "KLFBackend");
00396       return res;
00397     }
00398     if ( !proc.processNormalExit() ) {
00399       res.status = KLFERR_DVIPSNONORMALEXIT;
00400       res.errorstr = QObject::tr("Dvips was mercilessly killed!\n", "KLFBackend");
00401       return res;
00402     }
00403     if ( proc.processExitStatus() != 0) {
00404       res.status = KLFERR_PROGERR_DVIPS;
00405       res.errorstr = progErrorMsg("dvips", proc.processExitStatus(), proc.readStderrString(),
00406                                   proc.readStdoutString());
00407       return res;
00408     }
00409     if (!QFile::exists(fnRawEps)) {
00410       res.status = KLFERR_NOEPSFILE;
00411       res.errorstr = QObject::tr("EPS file didn't appear after dvips call!\n", "KLFBackend");
00412       return res;
00413     }
00414 
00415 //     { // DEAL WITH BOUNDING BOX
00416 
00417 //       // add some space on bounding-box to avoid some too tight bounding box bugs
00418 //       // read eps file
00419 //       QFile epsfile(fnRawEps);
00420 //       r = epsfile.open(dev_READONLY);
00421 //       if ( ! r ) {
00422 //         res.status = KLFERR_EPSREADFAIL;
00423 //         res.errorstr = QObject::tr("Can't read file '%1'!\n", "KLFBackend").arg(fnRawEps);
00424 //         return res;
00425 //       }
00426 //       /** \todo Hi-Res bounding box adjustment. Shouldn't be too hard to do, but needs tests to see
00427 //        * how this works... [ Currently: only integer-valued BoundingBox: is adjusted. ] */
00428 //       QByteArray epscontent = epsfile.readAll();
00429 // #ifdef KLFBACKEND_QT4
00430 //       QByteArray epscontent_s = epscontent;
00431 //       int i = epscontent_s.indexOf("%%BoundingBox: ");
00432 // #else
00433 //       QCString epscontent_s(epscontent.data(), epscontent.size());
00434 //       int i = epscontent_s.find("%%BoundingBox: ");
00435 // #endif
00436 //       // process file data and transform it
00437 //       if ( i == -1 ) {
00438 //         res.status = KLFERR_NOEPSBBOX;
00439 //         res.errorstr = QObject::tr("File '%1' does not contain line \"%%BoundingBox: ... \" !",
00440 //                                    "KLFBackend").arg(fnRawEps);
00441 //         return res;
00442 //       }
00443 //       int ax, ay, bx, by;
00444 //       char temp[250];
00445 //       const int k = i;
00446 //       i += strlen("%%BoundingBox:");
00447 //       int n = sscanf(epscontent_s.data()+i, "%d %d %d %d", &ax, &ay, &bx, &by);
00448 //       if ( n != 4 ) {
00449 //         res.status = KLFERR_BADEPSBBOX;
00450 //         res.errorstr = QObject::tr("file %1: Line %%BoundingBox: can't read values!\n", "KLFBackend")
00451 //           .arg(fnRawEps);
00452 //         return res;
00453 //       }
00454 //       // grow bbox by settings.Xborderoffset points
00455 //       // Don't forget: '%' in printf has special meaning (!) -> double percent signs '%'->'%%'
00456 //       sprintf(temp, "%%%%BoundingBox: %d %d %d %d",
00457 //               (int)(ax-settings.lborderoffset+0.5),
00458 //               (int)(ay-settings.bborderoffset+0.5),
00459 //               (int)(bx+settings.rborderoffset+0.5),
00460 //               (int)(by+settings.tborderoffset+0.5));
00461 //       QString chunk = QString::fromLocal8Bit(epscontent_s.data()+k);
00462 //       QRegExp rx("^%%BoundingBox: [0-9]+ [0-9]+ [0-9]+ [0-9]+");
00463 //       rx.rx_indexin(chunk);
00464 //       int l = rx.matchedLength();
00465 //       epscontent_s.replace(k, l, temp);
00466       
00467 //       // write content back to second file
00468 //       QFile epsgoodfile(fnBBCorrEps);
00469 //       r = epsgoodfile.open(dev_WRITEONLY);
00470 //       if ( ! r ) {
00471 //         res.status = KLFERR_EPSWRITEFAIL;
00472 //         res.errorstr = QObject::tr("Can't write to file '%1'!\n", "KLFBackend")
00473 //           .arg(fnBBCorrEps);
00474 //         return res;
00475 //       }
00476 //       epsgoodfile.dev_write(epscontent_s);
00477       
00478 //       if ( ! settings.outlineFonts ) {
00479 //         res.epsdata.ba_assign(epscontent_s);
00480 //       }
00481 //       // res.epsdata is now set.
00482 
00483 //     }
00484 
00485     { // DEAL WITH BBOX: BACKPORT FROM 3.3
00486 
00487       // read eps file
00488       QFile epsfile(fnRawEps);
00489       r = epsfile.open(dev_READONLY);
00490       if ( ! r ) {
00491         res.status = KLFERR_EPSREADFAIL;
00492         res.errorstr = QObject::tr("Can't read file '%1'!\n", "KLFBackend").arg(fnRawEps);
00493         return res;
00494       }
00495       QByteArray rawepsdata = epsfile.readAll();
00496 
00497       klfbbox bbox, bbox_corrected;
00498       bool ok = read_eps_bbox(rawepsdata, &bbox, &res);
00499       if (!ok)
00500         return res; // res was set by the function
00501 
00502       bbox.x1 -= settings.lborderoffset;
00503       bbox.y1 -= settings.bborderoffset;
00504       bbox.x2 += settings.rborderoffset;
00505       bbox.y2 += settings.tborderoffset;
00506 
00507       int width_pt = bbox.x2 - bbox.x1;
00508       int height_pt = bbox.y2 - bbox.y1;
00509 
00510       // now correct the bbox to (0,0,width,height)
00511 
00512       bbox_corrected.x1 = 0;
00513       bbox_corrected.y1 = 0;
00514       bbox_corrected.x2 = width_pt;
00515       bbox_corrected.y2 = height_pt;
00516 
00517       // and generate corrected raw EPS
00518       correct_eps_bbox(rawepsdata, bbox_corrected, bbox, 1.0,
00519                        &res.epsdata);
00520 
00521       QFile epsgoodfile(fnBBCorrEps);
00522       r = epsgoodfile.open(dev_WRITEONLY);
00523       if ( ! r ) {
00524         res.status = KLFERR_EPSWRITEFAIL;
00525         res.errorstr = QObject::tr("Can't write to file '%1'!\n", "KLFBackend")
00526           .arg(fnBBCorrEps);
00527         return res;
00528       }
00529       epsgoodfile.dev_write(res.epsdata);
00530     }
00531 
00532     qDebug("%s: %s:  eps bbox set.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;    
00533 
00534   } // end of block "make EPS"
00535 
00536   if (settings.outlineFonts) {
00537     // run 'gs' to outline fonts
00538     KLFBlockProcess proc;
00539 
00540     // Very bad joke from ghostscript's guys: they deprecate pswrite device, which worked very well, and
00541     // so I had to adapt the code so that it works with the new ps2write device. The bounding boxes were
00542     // going like hell. Hopefully a backport of the new system in 3.3 seemed to fix the issue.
00543 
00544     // So now we have to make sure we use ps2write on newer systems but make sure we still use pswrite on
00545     // old systems which don't support ps2write. THANKS A TON GS GUYS :(
00546 
00547     // In 3.2 we don't query gs version so we have no idea. So just let the user define an environment
00548     // variable in case. KLFBACKEND_GS_PS_DEVICE="pswrite" or "epswrite" or "ps2write" (note: with epswrite
00549     // you can't expand the bbox)
00550 
00551     QStringList try_ps_devices;
00552     const char *env_gs_device = getenv("KLFBACKEND_GS_PS_DEVICE");
00553     if (env_gs_device != NULL) {
00554       try_ps_devices << QString::fromLatin1(env_gs_device);
00555     } else {
00556       try_ps_devices << QLatin1String("pswrite") << QLatin1String("ps2write");
00557     }
00558 
00559     bool r = false;
00560     int try_ps_dev_i = 0;
00561     for (try_ps_dev_i = 0; try_ps_dev_i < try_ps_devices.size(); try_ps_dev_i++) {
00562       QString psdev = try_ps_devices[try_ps_dev_i];
00563       qDebug("trying with gs device %s ...", qPrintable(psdev));
00564 
00565       QStringList args;
00566       args << settings.gsexec << "-dNOCACHE" << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop"
00567            << QString("-sDEVICE=%1").arg(psdev)
00568            << "-sOutputFile="+dir_native_separators(fnOutlFontsEps)
00569            << "-q" << "-dBATCH" << dir_native_separators(fnBBCorrEps);
00570 
00571       qDebug("%s: %s: about to gs (for outline fonts)...\n%s", KLF_FUNC_NAME, KLF_SHORT_TIME,
00572              qPrintable(args.join(" ")));
00573       r = proc.startProcess(args, execenv);
00574       qDebug("%s: %s:  gs returned (for outline fonts).", KLF_FUNC_NAME, KLF_SHORT_TIME) ;    
00575 
00576       if (r && proc.processNormalExit() && proc.processExitStatus() == 0) {
00577         // successful run
00578         break;
00579       }
00580     }
00581   
00582     if ( ! r ) {
00583       res.status = KLFERR_NOGSPROG;
00584       res.errorstr = QObject::tr("Unable to start gs!\n", "KLFBackend");
00585       return res;
00586     }
00587     if ( !proc.processNormalExit() ) {
00588       res.status = KLFERR_GSNONORMALEXIT;
00589       res.errorstr = QObject::tr("gs died abnormally!\n", "KLFBackend");
00590       return res;
00591     }
00592     if ( proc.processExitStatus() != 0) {
00593       res.status = KLFERR_PROGERR_GS_OF;
00594       res.errorstr = progErrorMsg("gs", proc.processExitStatus(), proc.readStderrString(),
00595                                   proc.readStdoutString());
00596       return res;
00597     }
00598     if (!QFile::exists(fnOutlFontsEps)) {
00599       res.status = KLFERR_NOEPSFILE_OF;
00600       res.errorstr = QObject::tr("EPS file (with outlined fonts) didn't appear after call to gs!\n",
00601                                  "KLFBackend");
00602       return res;
00603     }
00604 
00605     // get and save outlined EPS to memory
00606     QFile ofepsfile(fnOutlFontsEps);
00607     r = ofepsfile.open(dev_READONLY);
00608     if ( ! r ) {
00609       res.status = KLFERR_EPSREADFAIL_OF;
00610       res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend")
00611         .arg(fnOutlFontsEps);
00612       return res;
00613     }
00614     res.epsdata = ofepsfile.readAll();
00615     ofepsfile.close();
00616     // res.epsdata is now set to the outlined-fonts version
00617   }
00618 
00619   { // run 'gs' to get png
00620     KLFBlockProcess proc;
00621     QStringList args;
00622     args << settings.gsexec << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop"
00623          << "-r"+QString::number(in.dpi) << "-dTextAlphaBits=4"
00624          << "-dGraphicsAlphaBits=4";
00625     if (qAlpha(in.bg_color) > 0) { // we're forcing a background color
00626       args << "-sDEVICE=png16m";
00627     } else {
00628       args << "-sDEVICE=pngalpha";
00629     }
00630     args << "-sOutputFile="+dir_native_separators(fnPng) << "-q" << "-dBATCH"
00631          << dir_native_separators(fnFinalEps);
00632 
00633     qDebug("%s: %s:  about to gs... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
00634     bool r = proc.startProcess(args, execenv);
00635     qDebug("%s: %s:  gs returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;    
00636   
00637     if ( ! r ) {
00638       res.status = KLFERR_NOGSPROG;
00639       res.errorstr = QObject::tr("Unable to start gs!\n", "KLFBackend");
00640       return res;
00641     }
00642     if ( !proc.processNormalExit() ) {
00643       res.status = KLFERR_GSNONORMALEXIT;
00644       res.errorstr = QObject::tr("gs died abnormally!\n", "KLFBackend");
00645       return res;
00646     }
00647     if ( proc.processExitStatus() != 0) {
00648       res.status = KLFERR_PROGERR_GS;
00649       res.errorstr = progErrorMsg("gs", proc.processExitStatus(), proc.readStderrString(),
00650                                   proc.readStdoutString());
00651       return res;
00652     }
00653     if (!QFile::exists(fnPng)) {
00654       res.status = KLFERR_NOPNGFILE;
00655       res.errorstr = QObject::tr("PNG file didn't appear after call to gs!\n", "KLFBackend");
00656       return res;
00657     }
00658 
00659     // get and save PNG to memory
00660     QFile pngfile(fnPng);
00661     r = pngfile.open(dev_READONLY);
00662     if ( ! r ) {
00663       res.status = KLFERR_PNGREADFAIL;
00664       res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend")
00665         .arg(fnPng);
00666       return res;
00667     }
00668     res.pngdata_raw = pngfile.readAll();
00669     pngfile.close();
00670     // res.pngdata_raw is now set.
00671     res.result.loadFromData(res.pngdata_raw, "PNG");
00672 
00673     // store some meta-information into result
00674     res.result.img_settext("AppVersion", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
00675     res.result.img_settext("Application",
00676                  QObject::tr("Created with KLatexFormula version %1", "KLFBackend::saveOutputToFile"));
00677     res.result.img_settext("Software", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
00678     res.result.img_settext("InputLatex", in.latex);
00679     res.result.img_settext("InputMathMode", in.mathmode);
00680     res.result.img_settext("InputPreamble", in.preamble);
00681     res.result.img_settext("InputFgColor", QString("rgb(%1, %2, %3)").arg(qRed(in.fg_color))
00682                    .arg(qGreen(in.fg_color)).arg(qBlue(in.fg_color)));
00683     res.result.img_settext("InputBgColor", QString("rgba(%1, %2, %3, %4)").arg(qRed(in.bg_color))
00684                    .arg(qGreen(in.bg_color)).arg(qBlue(in.bg_color))
00685                    .arg(qAlpha(in.bg_color)));
00686     res.result.img_settext("InputDPI", QString::number(in.dpi));
00687     res.result.img_settext("SettingsTBorderOffset", QString::number(settings.tborderoffset));
00688     res.result.img_settext("SettingsRBorderOffset", QString::number(settings.rborderoffset));
00689     res.result.img_settext("SettingsBBorderOffset", QString::number(settings.bborderoffset));
00690     res.result.img_settext("SettingsLBorderOffset", QString::number(settings.lborderoffset));
00691     res.result.img_settext("SettingsOutlineFonts", settings.outlineFonts?QString("true"):QString("false"));
00692   }
00693 
00694   { // create "final" PNG data
00695 #ifdef KLFBACKEND_QT4
00696     QBuffer buf(&res.pngdata);
00697 #else
00698     QBuffer buf(res.pngdata);
00699 #endif
00700     buf.open(dev_WRITEONLY);
00701     bool r = res.result.save(&buf, "PNG");
00702     if (!r) {
00703       qWarning("%s: Error: Can't save \"final\" PNG data.", KLF_FUNC_NAME);
00704       res.pngdata.ba_assign(res.pngdata_raw);
00705     }
00706   }
00707 
00708   if (!settings.epstopdfexec.isEmpty()) {
00709     // if we have epstopdf functionality, then we'll take advantage of it to generate pdf:
00710     KLFBlockProcess proc;
00711     QStringList args;
00712     args << settings.epstopdfexec << dir_native_separators(fnFinalEps)
00713          << ("--outfile="+dir_native_separators(fnPdf));
00714 
00715     qDebug("%s: %s:  about to epstopdf... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;    
00716     bool r = proc.startProcess(args, execenv);
00717     qDebug("%s: %s:  epstopdf returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;    
00718 
00719     if ( ! r ) {
00720       res.status = KLFERR_NOEPSTOPDFPROG;
00721       res.errorstr = QObject::tr("Unable to start epstopdf!\n", "KLFBackend");
00722       return res;
00723     }
00724     if ( !proc.processNormalExit() ) {
00725       res.status = KLFERR_EPSTOPDFNONORMALEXIT;
00726       res.errorstr = QObject::tr("epstopdf died nastily!\n", "KLFBackend");
00727       return res;
00728     }
00729     if ( proc.processExitStatus() != 0) {
00730       res.status = KLFERR_PROGERR_EPSTOPDF;
00731       res.errorstr = progErrorMsg("epstopdf", proc.processExitStatus(), proc.readStderrString(),
00732                                   proc.readStdoutString());
00733       return res;
00734     }
00735     if (!QFile::exists(fnPdf)) {
00736       qDebug("%s: %s: pdf file '%s' didn't appear after epstopdf!", KLF_FUNC_NAME, KLF_SHORT_TIME,
00737              qPrintable(fnPdf));
00738       res.status = KLFERR_NOPDFFILE;
00739       res.errorstr = QObject::tr("PDF file didn't appear after call to epstopdf!\n", "KLFBackend");
00740       return res;
00741     }
00742 
00743     // get and save PDF to memory
00744     QFile pdffile(fnPdf);
00745     r = pdffile.open(dev_READONLY);
00746     if ( ! r ) {
00747       res.status = KLFERR_PDFREADFAIL;
00748       res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend").arg(fnPdf);
00749       return res;
00750     }
00751     res.pdfdata = pdffile.readAll();
00752 
00753   }
00754 
00755   qDebug("%s: %s:  end of function.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;    
00756 
00757   return res;
00758 }
00759 
00760 
00761 
00762 static bool s_starts_with(const char * x, int len_x, const char *test, int len_test)
00763 {
00764   if (len_x < len_test)
00765     return false;
00766   return !strncmp(x, test, len_test);
00767 }
00768 
00769 #define D_RX "([0-9eE.-]+)"
00770 
00771 static bool parse_bbox_values(const QString& str, klfbbox *bbox)
00772 {
00773   // parse bbox values
00774   QRegExp rx_bbvalues("" D_RX "\\s+" D_RX "\\s+" D_RX "\\s+" D_RX "");
00775   int i = rx_bbvalues.rx_indexin(str);
00776   if (i < 0) {
00777     return false;
00778   }
00779   bbox->x1 = rx_bbvalues.cap(1).toDouble();
00780   bbox->y1 = rx_bbvalues.cap(2).toDouble();
00781   bbox->x2 = rx_bbvalues.cap(3).toDouble();
00782   bbox->y2 = rx_bbvalues.cap(4).toDouble();
00783   return true;
00784 }
00785 
00786 static bool read_eps_bbox(const QByteArray& epsdata, klfbbox *bbox, KLFBackend::klfOutput * resError)
00787 {
00788   static const char * hibboxtag = "%%HiResBoundingBox:";
00789   static const char * bboxtag = "%%BoundingBox:";
00790   static const int hibboxtaglen = strlen(hibboxtag);
00791   static const int bboxtaglen = strlen(bboxtag);
00792 
00793   // Read dvips' bounding box.
00794   QBuffer buf;
00795   buf_setdata(buf, epsdata);
00796   bool r = buf.open(dev_READONLY);
00797   if (!r) {
00798     qWarning("What's going on!!?! can't open buffer for reading? Will Fail!!!") ;
00799   }
00800 
00801   QString nobboxerrstr =
00802     QObject::tr("DVIPS did not provide parsable %%BoundingBox: in its output!", "KLFBackend");
00803 
00804   char linebuffer[512];
00805   int n;
00806   bool gotepsbbox = false;
00807   int still_look_for_hiresbbox_lines = 5;
00808   while ((n = buf.readLine(linebuffer, sizeof(linebuffer)-1)) > 0) {
00809     if (gotepsbbox && still_look_for_hiresbbox_lines-- < 0) {
00810       // if we already got the %BoundingBox, and we've been looking at more than a certian number of lines
00811       // after that, abort because usually %BoundingBox and %HiResBoundingBox are together...
00812       klfDbg("stopped looking for hires-bbox.") ;
00813       break;
00814     }
00815     if (s_starts_with(linebuffer, n-1, hibboxtag, hibboxtaglen)) {
00816       // got hi-res bounding-box
00817       bool ok = parse_bbox_values(QString::fromLatin1(linebuffer+hibboxtaglen), bbox);
00818       if (!ok) {
00819         resError->status = KLFERR_BADEPSBBOX;
00820         resError->errorstr = nobboxerrstr;
00821         return false;
00822       }
00823       klfDbg("got hires-bbox.") ;
00824       // all ok, got hi-res bbox
00825       return true;
00826     }
00827     if (s_starts_with(linebuffer, n-1, bboxtag, bboxtaglen)) {
00828       // got bounding-box.
00829       bool ok = parse_bbox_values(QString::fromLatin1(linebuffer+bboxtaglen), bbox);
00830       if (!ok) {
00831         continue;
00832       }
00833       // stand by, continue in case we have a hi-res bbox.
00834       gotepsbbox = true;
00835       klfDbg("got normal bbox.") ;
00836       continue;
00837     }
00838   }
00839 
00840   // didn't get a hi-res bbox. see if we still got a regular %BoundingBox: and return that.
00841   if (gotepsbbox) {
00842     // bbox pointer is already set
00843     return true;
00844   }
00845 
00846   resError->status = KLFERR_BADEPSBBOX;
00847   resError->errorstr = nobboxerrstr;
00848   return false;
00849 }
00850 
00851 // static int find_ba_in_ba(const QByteArray& haystack, const QByteArray& needle)
00852 // {
00853 // #ifdef KLFBACKEND_QT4
00854 //   return haystack.indexOf(needle);
00855 // #else
00856 //   int k, j;
00857 //   for (k = 0; k < haystack.length()-needle.length(); ++k) {
00858 //     // locally compare haystack and needle
00859 //     for (j = 0; j < needle.length(); ++j) {
00860 //       if (haystack[k+j] != needle[j])
00861 //         break; // nope they're not the same
00862 //     }
00863 //     if (j == needle.length())
00864 //       // found the needle
00865 //       return k;
00866 //   }
00867 //   return -1;
00868 // #endif
00869 // }
00870 
00871 static void correct_eps_bbox(const QByteArray& rawepsdata, const klfbbox& bbox_corrected,
00872                              const klfbbox& bbox_orig, double vectorscale,
00873                              QByteArray * epsdatacorrected)
00874 {
00875   static const char * bboxdecl = "%%BoundingBox:";
00876   static int bboxdecl_len = strlen(bboxdecl);
00877 
00878   double offx = bbox_corrected.x1 - bbox_orig.x1;
00879   double offy = bbox_corrected.y1 - bbox_orig.y1;
00880 
00881   // in raw EPS data, find '%%BoundingBox:' and length of the full BoundingBox instruction
00882   int i, len;
00883   char nl[] = "\0\0\0";
00884 #ifdef KLFBACKEND_QT4
00885   i = rawepsdata.indexOf(bboxdecl);
00886 #else
00887   QCString rawepsdata_s(rawepsdata.data(), rawepsdata.size());
00888   i = rawepsdata_s.find(bboxdecl);
00889 #endif
00890   if (i < 0) {
00891     i = 0;
00892     len = 0;
00893   } else {
00894     int j = i+bboxdecl_len;
00895     while (j < (int)rawepsdata.size() && rawepsdata[j] != '\r' && rawepsdata[j] != '\n')
00896       ++j;
00897     len = j-i;
00898     // also determine what the newline is (\n, \r, \r\n?)
00899     if (rawepsdata[j] == '\r' && j < (int)rawepsdata.size()-1 && rawepsdata[j+1] == '\n') {
00900       nl[0] = '\r', nl[1] = '\n';
00901     } else {
00902       nl[0] = rawepsdata[j];
00903     }
00904   }
00905 
00906   double dwi = bbox_corrected.x2 * vectorscale;
00907   double dhi = bbox_corrected.y2 * vectorscale;
00908   int wi = (int)(dwi + 0.99999) ;
00909   int hi = (int)(dhi + 0.99999) ;
00910   char buffer[1024];
00911   int buffer_len;
00912   // recall that '%%' in printf is replaced by a single '%'...
00913   snprintf(buffer, sizeof(buffer)-1,
00914            "%%%%BoundingBox: 0 0 %d %d%s"
00915            "%%%%HiResBoundingBox: 0 0 %.6g %.6g%s",
00916            wi, hi, nl,
00917            dwi, dhi, nl);
00918   buffer_len = strlen(buffer);
00919 
00920   /*
00921   char backgroundfillps[1024] = "";
00922   if (qAlpha(bgcolor) > 0) {
00923     sprintf(backgroundfillps,
00924             // draw the background color, if any
00925             "newpath "
00926             "-2 -2 moveto "
00927             "%s -2 lineto "
00928             "%s %s lineto "
00929             "-2 %s lineto "
00930             "closepath "
00931             "gsave "
00932             "%s %s %s setrgbcolor "
00933             "fill "
00934             "grestore %s",
00935             klfFmtDoubleCC(dwi+1, 'g', 6),
00936             klfFmtDoubleCC(dwi+1, 'g', 6), klfFmtDoubleCC(dhi+1, 'g', 6),
00937             klfFmtDoubleCC(dhi+1, 'g', 6),
00938             // and the color, in RGB components:
00939             klfFmtDoubleCC(qRed(bgcolor)/255.0, 'f', 6),
00940             klfFmtDoubleCC(qGreen(bgcolor)/255.0, 'f', 6),
00941             klfFmtDoubleCC(qBlue(bgcolor)/255.0, 'f', 6),
00942             nl
00943           );
00944   }
00945   */
00946 
00947   char buffer2[1024];
00948   int buffer2_len;
00949   snprintf(buffer2, sizeof(buffer2)-1,
00950            "%s"
00951            "%%%%Page 1 1%s"
00952            "%%%%PageBoundingBox 0 0 %d %d%s"
00953            "<< /PageSize [%d %d] >> setpagedevice%s"
00954            //"%s"
00955            "%f %f scale%s"
00956            "%f %f translate%s"
00957            ,
00958            nl,
00959            nl,
00960            wi, hi, nl,
00961            wi, hi, nl,
00962            //backgroundfillps,
00963            vectorscale, vectorscale, nl,
00964            offx, offy, nl);
00965   buffer2_len = strlen(buffer2);
00966 
00967   //    char buffer2[128];
00968   //    snprintf(buffer2, 127, "%sgrestore%s", nl, nl);
00969 
00970   //klfDbg("buffer is `"<<buffer<<"', length="<<buffer_len) ;
00971   //klfDbg("rawepsdata has length="<<rawepsdata.size()) ;
00972 
00973   // and modify the raw EPS data, to replace "%%BoundingBox:" instruction by our stuff...
00974 #ifdef KLFBACKEND_QT4
00975   QByteArray neweps;
00976   neweps = rawepsdata;
00977 #else
00978   QCString neweps(rawepsdata.data(), rawepsdata.size()); // makes deep copy
00979 #endif
00980   neweps.replace(i, len, buffer);
00981 
00982   const char * endsetupstr = "%%EndSetup";
00983   int i2 = neweps.s_indexOf(endsetupstr);
00984   if (i2 < 0)
00985     i2 = i + buffer_len; // add our info after modified %%BoundingBox'es instructions if %%EndSetup not found
00986   else
00987     i2 +=  strlen(endsetupstr);
00988 
00989   neweps.replace(i2, 0, buffer2);
00990   
00991   qDebug("neweps has now length=%d",neweps.size());
00992   qDebug("New eps bbox is [0 0 %.6g %.6g] with translate [%.6g %.6g] and scale %.6g.",
00993              dwi, dhi, offx, offy, vectorscale);
00994 
00995   epsdatacorrected->ba_assign(neweps);
00996 }
00997 
00998 
00999 
01000 
01001 
01002 
01003 
01004 
01005 
01006 
01007 
01008 void KLFBackend::cleanup(QString tempfname)
01009 {
01010   const char *skipcleanup = getenv("KLFBACKEND_LEAVE_TEMP_FILES");
01011   if (skipcleanup != NULL && (*skipcleanup == '1' || *skipcleanup == 't' || *skipcleanup == 'T' ||
01012                               *skipcleanup == 'y' || *skipcleanup == 'Y'))
01013     return; // skip cleaning up temp files
01014 
01015   if (QFile::exists(tempfname+".tex")) QFile::remove(tempfname+".tex");
01016   if (QFile::exists(tempfname+".dvi")) QFile::remove(tempfname+".dvi");
01017   if (QFile::exists(tempfname+".aux")) QFile::remove(tempfname+".aux");
01018   if (QFile::exists(tempfname+".log")) QFile::remove(tempfname+".log");
01019   if (QFile::exists(tempfname+".toc")) QFile::remove(tempfname+".toc");
01020   if (QFile::exists(tempfname+".eps")) QFile::remove(tempfname+".eps");
01021   if (QFile::exists(tempfname+"-good.eps")) QFile::remove(tempfname+"-good.eps");
01022   if (QFile::exists(tempfname+"-raw.eps")) QFile::remove(tempfname+"-raw.eps");
01023   if (QFile::exists(tempfname+"-bbcorr.eps")) QFile::remove(tempfname+"-bbcorr.eps");
01024   if (QFile::exists(tempfname+"-outlfonts.eps")) QFile::remove(tempfname+"-outlfonts.eps");
01025   if (QFile::exists(tempfname+".png")) QFile::remove(tempfname+".png");
01026   if (QFile::exists(tempfname+".pdf")) QFile::remove(tempfname+".pdf");
01027 }
01028 
01029 
01030 
01031 
01032 // static private mutex object
01033 QMutex KLFBackend::__mutex;
01034 
01035 
01036 
01037 KLF_EXPORT bool operator==(const KLFBackend::klfInput& a, const KLFBackend::klfInput& b)
01038 {
01039   return a.latex == b.latex &&
01040     a.mathmode == b.mathmode &&
01041     a.preamble == b.preamble &&
01042     a.fg_color == b.fg_color &&
01043     a.bg_color == b.bg_color &&
01044     a.dpi == b.dpi;
01045 }
01046 
01047 
01048 bool KLFBackend::saveOutputToDevice(const klfOutput& klfoutput, QIODevice *device,
01049                                     const QString& fmt, QString *errorStringPtr)
01050 {
01051   QString format = fmt.s_trimmed().s_toUpper();
01052 
01053   // now choose correct data source and write to fout
01054   if (format == "EPS" || format == "PS") {
01055     device->dev_write(klfoutput.epsdata);
01056   } else if (format == "PNG") {
01057     device->dev_write(klfoutput.pngdata);
01058   } else if (format == "PDF") {
01059     if (klfoutput.pdfdata.isEmpty()) {
01060       QString error = QObject::tr("PDF format is not available!\n",
01061                                   "KLFBackend::saveOutputToFile");
01062       qWarning("%s", qPrintable(error));
01063       if (errorStringPtr != NULL)
01064         errorStringPtr->operator=(error);
01065       return false;
01066     }
01067     device->dev_write(klfoutput.pdfdata);
01068  } else {
01069     bool res = klfoutput.result.save(device, format.s_toLatin1());
01070     if ( ! res ) {
01071       QString errstr = QObject::tr("Unable to save image in format `%1'!",
01072                                    "KLFBackend::saveOutputToDevice").arg(format);
01073       qWarning("%s", qPrintable(errstr));
01074       if (errorStringPtr != NULL)
01075         *errorStringPtr = errstr;
01076       return false;
01077     }
01078   }
01079 
01080   return true;
01081 }
01082 
01083 bool KLFBackend::saveOutputToFile(const klfOutput& klfoutput, const QString& fileName,
01084                                   const QString& fmt, QString *errorStringPtr)
01085 {
01086   QString format = fmt;
01087   // determine format first
01088   if (format.isEmpty() && !fileName.isEmpty()) {
01089     QFileInfo fi(fileName);
01090     if ( ! fi.fi_suffix().isEmpty() )
01091       format = fi.fi_suffix();
01092   }
01093   if (format.isEmpty())
01094     format = QLatin1String("PNG");
01095   format = format.s_trimmed().s_toUpper();
01096   // got format. choose output now and prepare write
01097   QFile fout;
01098   if (fileName.isEmpty() || fileName == "-") {
01099     if ( ! fout.f_open_fp(stdout) ) {
01100       QString error = QObject::tr("Unable to open stderr for write! Error: %1\n",
01101                                   "KLFBackend::saveOutputToFile").arg(fout.f_error());
01102       qWarning("%s", qPrintable(error));
01103       if (errorStringPtr != NULL)
01104         *errorStringPtr = error;
01105       return false;
01106     }
01107   } else {
01108     fout.f_setFileName(fileName);
01109     if ( ! fout.open(dev_WRITEONLY) ) {
01110       QString error = QObject::tr("Unable to write to file `%1'! Error: %2\n",
01111                                   "KLFBackend::saveOutputToFile")
01112         .arg(fileName).arg(fout.f_error());
01113       qWarning("%s", qPrintable(error));
01114       if (errorStringPtr != NULL)
01115         *errorStringPtr = error;
01116       return false;
01117     }
01118   }
01119 
01120   return saveOutputToDevice(klfoutput, &fout, format, errorStringPtr);
01121 }
01122 
01123 
01124 bool KLFBackend::detectSettings(klfSettings *settings, const QString& extraPath)
01125 {
01126   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01127 
01128   QStringList stdextrapaths;
01129   int k, j;
01130   for (k = 0; standard_extra_paths[k] != NULL; ++k) {
01131     stdextrapaths.append(standard_extra_paths[k]);
01132   }
01133   QString extra_paths = stdextrapaths.join(QString("")+KLF_PATH_SEP);
01134   if (!extraPath.isEmpty())
01135     extra_paths += KLF_PATH_SEP + extraPath;
01136 
01137   // temp dir
01138 #ifdef KLFBACKEND_QT4
01139   settings->tempdir = QDir::fromNativeSeparators(QDir::tempPath());
01140 #else
01141 # if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_MACX)
01142   settings->tempdir = "/tmp";
01143 # elif defined(Q_OS_WIN32)
01144   settings->tempdir = getenv("TEMP");
01145 # else
01146   settings->tempdir = QString();
01147 # endif
01148 #endif
01149 
01150   // sensible defaults
01151   settings->lborderoffset = 1;
01152   settings->tborderoffset = 1;
01153   settings->rborderoffset = 1;
01154   settings->bborderoffset = 1;
01155   
01156   // find executables
01157   struct { QString * target_setting; QStringList prog_names; }  progs_to_find[] = {
01158     { & settings->latexexec, progLATEX },
01159     { & settings->dvipsexec, progDVIPS },
01160     { & settings->gsexec, progGS },
01161     { & settings->epstopdfexec, progEPSTOPDF },
01162     { NULL, QStringList() }
01163   };
01164   // replace @executable_path in extra_paths
01165   klfDbg(klfFmtCC("Our base extra paths are: %s", qPrintable(extra_paths))) ;
01166   QString ourextrapaths = extra_paths;
01167   ourextrapaths.replace("@executable_path", qApp->applicationDirPath());
01168   klfDbg(klfFmtCC("Our extra paths are: %s", qPrintable(ourextrapaths))) ;
01169   // and actually search for those executables
01170   for (k = 0; progs_to_find[k].target_setting != NULL; ++k) {
01171     klfDbg("Looking for "+progs_to_find[k].prog_names.join(" or ")) ;
01172     for (j = 0; j < (int)progs_to_find[k].prog_names.size(); ++j) {
01173       klfDbg("Testing `"+progs_to_find[k].prog_names[j]+"'") ;
01174       *progs_to_find[k].target_setting
01175         = klfSearchPath(progs_to_find[k].prog_names[j], ourextrapaths);
01176       if (!progs_to_find[k].target_setting->isEmpty()) {
01177         klfDbg("Found! at `"+ *progs_to_find[k].target_setting+"'") ;
01178         break; // found a program
01179       }
01180     }
01181   }
01182 
01183   klf_detect_execenv(settings);
01184 
01185   bool result_failure =
01186     settings->tempdir.isEmpty() || settings->latexexec.isEmpty() || settings->dvipsexec.isEmpty() ||
01187     settings->gsexec.isEmpty(); // NOTE:  settings->epstopdfexec.isEmpty() is NOT a failure
01188 
01189   return !result_failure;
01190 }
01191 
01192 
01209 KLF_EXPORT bool klf_detect_execenv(KLFBackend::klfSettings *settings)
01210 {
01211   // detect mgs.exe as ghostscript and setup its environment properly
01212   QFileInfo gsfi(settings->gsexec);
01213   if (gsfi.fileName() == "mgs.exe") {
01214     QString mgsenv = QString("MIKTEX_GS_LIB=")
01215       + dir_native_separators(gsfi.fi_absolutePath()+"/../../ghostscript/base")
01216       + ";"
01217       + dir_native_separators(gsfi.fi_absolutePath()+"/../../fonts");
01218     __klf_append_replace_env_var(& settings->execenv, "MIKTEX_GS_LIB", mgsenv);
01219     klfDbg("Adjusting environment for mgs.exe: `"+mgsenv+"'") ;
01220   }
01221 
01222 #ifdef Q_WS_MAC
01223   // make sure that epstopdf's path is in PATH because it wants to all gs
01224   // (eg fink distributions)
01225   if (!settings->epstopdfexec.isEmpty()) {
01226     QFileInfo epstopdf_fi(settings->epstopdfexec);
01227     QString execenvpath = QString("PATH=%1:$PATH").arg(epstopdf_fi.fi_absolutePath());
01228     __klf_append_replace_env_var(& settings->execenv, "PATH", execenvpath);
01229   }
01230 #endif
01231 
01232   return true;
01233 }

Generated by doxygen 1.7.3