[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) 2007 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 544 2010-10-04 01:34:17Z philippe $ */
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 
00037 #include "klfblockprocess.h"
00038 #include "klfbackend.h"
00039 
00040 // write Qt 3/4 compatible code
00041 #include "klfqt34common.h"
00042 
00043 
00074 // some standard guess settings for system configurations
00075 
00076 #ifdef KLF_EXTRA_SEARCH_PATHS
00077 #  define EXTRA_PATHS_PRE       KLF_EXTRA_SEARCH_PATHS ,
00078 #  define EXTRA_PATHS           KLF_EXTRA_SEARCH_PATHS
00079 #else
00080 #  define EXTRA_PATHS_PRE
00081 #  define EXTRA_PATHS
00082 #endif
00083 
00084 
00085 #if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
00086 QStringList progLATEX = QStringList() << "latex.exe";
00087 QStringList progDVIPS = QStringList() << "dvips.exe";
00088 QStringList progGS = QStringList() << "gswin32c.exe" << "mgs.exe";
00089 QStringList progEPSTOPDF = QStringList() << "epstopdf.exe";
00090 static const char * standard_extra_paths[] = {
00091   EXTRA_PATHS_PRE
00092   "C:\\Program Files\\MiKTeX*\\miktex\\bin",
00093   "C:\\Program Files\\gs\\gs*\\bin",
00094   NULL
00095 };
00096 #elif defined(Q_WS_MAC)
00097 QStringList progLATEX = QStringList() << "latex";
00098 QStringList progDVIPS = QStringList() << "dvips";
00099 QStringList progGS = QStringList() << "gs";
00100 QStringList progEPSTOPDF = QStringList() << "epstopdf";
00101 static const char * standard_extra_paths[] = {
00102   EXTRA_PATHS_PRE
00103   "/usr/texbin:/usr/local/bin:/sw/bin:/sw/usr/bin",
00104   NULL
00105 };
00106 #else
00107 QStringList progLATEX = QStringList() << "latex";
00108 QStringList progDVIPS = QStringList() << "dvips";
00109 QStringList progGS = QStringList() << "gs";
00110 QStringList progEPSTOPDF = QStringList() << "epstopdf";
00111 static const char * standard_extra_paths[] = {
00112   EXTRA_PATHS_PRE
00113   NULL
00114 };
00115 #endif
00116 
00117 
00118 
00119 // ---------------------------------
00120 
00121 KLFBackend::KLFBackend()
00122 {
00123 }
00124 
00125 
00126 // Utility function
00127 QString progErrorMsg(QString progname, int exitstatus, QString stderrstr, QString stdoutstr)
00128 {
00129   QString stdouthtml = stdoutstr;
00130   QString stderrhtml = stderrstr;
00131   stdouthtml.replace("&", "&amp;");
00132   stdouthtml.replace("<", "&lt;");
00133   stdouthtml.replace(">", "&gt;");
00134   stderrhtml.replace("&", "&amp;");
00135   stderrhtml.replace("<", "&lt;");
00136   stderrhtml.replace(">", "&gt;");
00137 
00138   if (stderrstr.isEmpty() && stdoutstr.isEmpty())
00139     return QObject::tr("<p><b>%1</b> reported an error (exit status %2). No Output was generated.</p>",
00140                        "KLFBackend")
00141         .arg(progname).arg(exitstatus);
00142   if (stderrstr.isEmpty())
00143     return
00144       QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stdout output:</p>\n"
00145                   "<pre>\n%3</pre>", "KLFBackend")
00146       .arg(progname).arg(exitstatus).arg(stdouthtml);
00147   if (stdoutstr.isEmpty())
00148     return
00149       QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
00150                   "<pre>\n%3</pre>", "KLFBackend")
00151       .arg(progname).arg(exitstatus).arg(stderrhtml);
00152   
00153   return QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
00154                      "<pre>\n%3</pre><p>And here is full stdout output:</p><pre>\n%4</pre>", "KLFBackend")
00155     .arg(progname).arg(exitstatus).arg(stderrhtml).arg(stdouthtml);
00156 }
00157 
00158 
00159 /* * Internal.
00160  * \internal */
00161 struct cleanup_caller {
00162   QString tempfname;
00163   cleanup_caller(QString fn) : tempfname(fn) { }
00164   ~cleanup_caller() {
00165     KLFBackend::cleanup(tempfname);
00166   }
00167 };
00168 
00169 QString __klf_expand_env_vars(const QString& envexpr)
00170 {
00171   QString s = envexpr;
00172   QRegExp rx("\\$(?:(\\$|(?:[A-Za-z0-9_]+))|\\{([A-Za-z0-9_]+)\\})");
00173   int i = 0;
00174   while ( (i = rx.rx_indexin_i(s, i)) != -1 ) {
00175     // match found, replace it
00176     QString envvarname = rx.cap(1);
00177     if (envvarname.isEmpty() || envvarname == QLatin1String("$")) {
00178       // note: empty variable name expands to a literal '$'
00179       s.replace(i, rx.matchedLength(), QLatin1String("$"));
00180       i += 1;
00181       continue;
00182     }
00183     const char *svalue = getenv(qPrintable(envvarname));
00184     QString qsvalue = (svalue != NULL) ? QString::fromLocal8Bit(svalue) : QString();
00185     s.replace(i, rx.matchedLength(), qsvalue);
00186     i += qsvalue.length();
00187   }
00188   // replacements performed
00189   return s;
00190 }
00191 
00192 void __klf_append_replace_env_var(QStringList *list, const QString& var, const QString& line)
00193 {
00194   // search for declaration of var in list
00195   int k;
00196   for (k = 0; k < (int)list->size(); ++k) {
00197     if (list->operator[](k).startsWith(var+QString("="))) {
00198       list->operator[](k) = line;
00199       return;
00200     }
00201   }
00202   // declaration not found, just append
00203   list->append(line);
00204 }
00205 
00206 
00207 
00208 KLFBackend::klfOutput KLFBackend::getLatexFormula(const klfInput& in, const klfSettings& settings)
00209 {
00210   // ALLOW ONLY ONE RUNNING getLatexFormula() AT A TIME 
00211   QMutexLocker mutexlocker(&__mutex);
00212 
00213   int k;
00214 
00215   qDebug("%s: %s: KLFBackend::getLatexFormula() called. latex=%s", KLF_FUNC_NAME, KLF_SHORT_TIME,
00216          qPrintable(in.latex));
00217 
00218   // get full, expanded exec environment
00219   QStringList execenv = klf_cur_environ();
00220   for (k = 0; k < (int)settings.execenv.size(); ++k) {
00221     int eqpos = settings.execenv[k].s_indexOf(QChar('='));
00222     if (eqpos == -1) {
00223       qWarning("%s: badly formed environment definition in `environ': %s", KLF_FUNC_NAME,
00224                qPrintable(settings.execenv[k]));
00225       continue;
00226     }
00227     QString varname = settings.execenv[k].mid(0, eqpos);
00228     QString newenvdef = __klf_expand_env_vars(settings.execenv[k]);
00229     __klf_append_replace_env_var(&execenv, varname, newenvdef);
00230   }
00231 
00232   klfDbg("execution environment for sub-processes:\n"+execenv.join("\n")) ;
00233   
00234   klfOutput res;
00235   res.status = KLFERR_NOERROR;
00236   res.errorstr = QString();
00237   res.result = QImage();
00238   res.pngdata_raw = QByteArray();
00239   res.pngdata = QByteArray();
00240   res.epsdata = QByteArray();
00241   res.pdfdata = QByteArray();
00242   res.input = in;
00243   res.settings = settings;
00244 
00245   // PROCEDURE:
00246   // - generate LaTeX-file
00247   // - latex --> get DVI file
00248   // - dvips -E file.dvi it to get an EPS file
00249   // - expand BBox by editing EPS file (if applicable)
00250   // - outline fonts with gs (if applicable)
00251   // - Run gs:  gs -dNOPAUSE -dSAFER -dEPSCrop -r600 -dTextAlphaBits=4 -dGraphicsAlphaBits=4
00252   //               -sDEVICE=pngalpha|png16m -sOutputFile=xxxxxx.png -q -dBATCH xxxxxx.eps
00253   //   to eventually get PNG data
00254   // - if epstopdfexec is not empty, run epstopdf and get PDF file.
00255 
00256   QString tempfname = settings.tempdir + "/klatexformulatmp" KLF_VERSION_STRING "-"
00257     + QDateTime::currentDateTime().toString("hh-mm-ss");
00258 
00259   QString fnTex = tempfname + ".tex";
00260   QString fnDvi = tempfname + ".dvi";
00261   QString fnRawEps = tempfname + "-raw.eps";
00262   QString fnBBCorrEps = tempfname + "-bbcorr.eps";
00263   QString fnOutlFontsEps = tempfname + "-outlfonts.eps";
00264   QString fnFinalEps = settings.outlineFonts ? fnOutlFontsEps : fnBBCorrEps;
00265   QString fnPng = tempfname + ".png";
00266   QString fnPdf = tempfname + ".pdf";
00267 
00268   // upon destruction (on stack) of this object, cleanup() will be
00269   // automatically called as wanted
00270   cleanup_caller cleanupcallerinstance(tempfname);
00271 
00272 #ifdef KLFBACKEND_QT4
00273   QString latexsimplified = in.latex.s_trimmed();
00274 #else
00275   QString latexsimplified = in.latex.stripWhiteSpace();
00276 #endif
00277   if (latexsimplified.isEmpty()) {
00278     res.errorstr = QObject::tr("You must specify a LaTeX formula!", "KLFBackend");
00279     res.status = KLFERR_MISSINGLATEXFORMULA;
00280     return res;
00281   }
00282 
00283   QString latexin;
00284   if (!in.bypassTemplate) {
00285     if (in.mathmode.contains("...") == 0) {
00286       res.status = KLFERR_MISSINGMATHMODETHREEDOTS;
00287       res.errorstr = QObject::tr("The math mode string doesn't contain '...'!", "KLFBackend");
00288       return res;
00289     }
00290     latexin = in.mathmode;
00291     latexin.replace("...", in.latex);
00292   }
00293 
00294   {
00295     QFile file(fnTex);
00296     bool r = file.open(dev_WRITEONLY);
00297     if ( ! r ) {
00298       res.status = KLFERR_TEXWRITEFAIL;
00299       res.errorstr = QObject::tr("Can't open file for writing: '%1'!", "KLFBackend")
00300         .arg(fnTex);
00301       return res;
00302     }
00303     QTextStream stream(&file);
00304     if (!in.bypassTemplate) {
00305       stream << "\\documentclass{article}\n"
00306              << "\\usepackage[dvips]{color}\n"
00307              << in.preamble << "\n"
00308              << "\\begin{document}\n"
00309              << "\\thispagestyle{empty}\n"
00310              << QString("\\definecolor{klffgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.fg_color)/255.0)
00311         .arg(qGreen(in.fg_color)/255.0).arg(qBlue(in.fg_color)/255.0)
00312              << QString("\\definecolor{klfbgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.bg_color)/255.0)
00313         .arg(qGreen(in.bg_color)/255.0).arg(qBlue(in.bg_color)/255.0)
00314              << ( (qAlpha(in.bg_color)>0) ? "\\pagecolor{klfbgcolor}\n" : "" )
00315              << "{\\color{klffgcolor} " << latexin << " }\n"
00316              << "\\end{document}\n";
00317     } else {
00318       stream << in.latex;
00319     }
00320   }
00321 
00322   { // execute latex
00323 
00324     KLFBlockProcess proc;
00325     QStringList args;
00326 
00327     proc.setWorkingDirectory(settings.tempdir);
00328 
00329     args << settings.latexexec << dir_native_separators(fnTex);
00330 
00331     qDebug("%s: %s:  about to exec latex...", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00332     bool r = proc.startProcess(args, execenv);
00333     qDebug("%s: %s:  latex returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00334 
00335     if (!r) {
00336       res.status = KLFERR_NOLATEXPROG;
00337       res.errorstr = QObject::tr("Unable to start Latex program %1!", "KLFBackend")
00338         .arg(settings.latexexec);
00339       return res;
00340     }
00341     if (!proc.processNormalExit()) {
00342       res.status = KLFERR_LATEXNONORMALEXIT;
00343       res.errorstr = QObject::tr("Latex was killed!", "KLFBackend");
00344       return res;
00345     }
00346     if (proc.processExitStatus() != 0) {
00347       res.status = KLFERR_PROGERR_LATEX;
00348       res.errorstr = progErrorMsg("LaTeX", proc.processExitStatus(), proc.readStderrString(),
00349                                   proc.readStdoutString());
00350       return res;
00351     }
00352 
00353     if (!QFile::exists(fnDvi)) {
00354       res.status = KLFERR_NODVIFILE;
00355       res.errorstr = QObject::tr("DVI file didn't appear after having called Latex!", "KLFBackend");
00356       return res;
00357     }
00358 
00359   } // end of 'latex' block
00360 
00361   { // execute dvips -E
00362 
00363     KLFBlockProcess proc;
00364     QStringList args;
00365     args << settings.dvipsexec << "-E" << dir_native_separators(fnDvi)
00366          << "-o" << dir_native_separators(fnRawEps);
00367 
00368     qDebug("%s: %s:  about to dvips... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
00369     bool r = proc.startProcess(args, execenv);
00370     qDebug("%s: %s:  dvips returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00371 
00372     if ( ! r ) {
00373       res.status = KLFERR_NODVIPSPROG;
00374       res.errorstr = QObject::tr("Unable to start dvips!\n", "KLFBackend");
00375       return res;
00376     }
00377     if ( !proc.processNormalExit() ) {
00378       res.status = KLFERR_DVIPSNONORMALEXIT;
00379       res.errorstr = QObject::tr("Dvips was mercilessly killed!\n", "KLFBackend");
00380       return res;
00381     }
00382     if ( proc.processExitStatus() != 0) {
00383       res.status = KLFERR_PROGERR_DVIPS;
00384       res.errorstr = progErrorMsg("dvips", proc.processExitStatus(), proc.readStderrString(),
00385                                   proc.readStdoutString());
00386       return res;
00387     }
00388     if (!QFile::exists(fnRawEps)) {
00389       res.status = KLFERR_NOEPSFILE;
00390       res.errorstr = QObject::tr("EPS file didn't appear after dvips call!\n", "KLFBackend");
00391       return res;
00392     }
00393 
00394     // add some space on bounding-box to avoid some too tight bounding box bugs
00395     // read eps file
00396     QFile epsfile(fnRawEps);
00397     r = epsfile.open(dev_READONLY);
00398     if ( ! r ) {
00399       res.status = KLFERR_EPSREADFAIL;
00400       res.errorstr = QObject::tr("Can't read file '%1'!\n", "KLFBackend").arg(fnRawEps);
00401       return res;
00402     }
00405     QByteArray epscontent = epsfile.readAll();
00406 #ifdef KLFBACKEND_QT4
00407     QByteArray epscontent_s = epscontent;
00408     int i = epscontent_s.indexOf("%%BoundingBox: ");
00409 #else
00410     QCString epscontent_s(epscontent.data(), epscontent.size());
00411     int i = epscontent_s.find("%%BoundingBox: ");
00412 #endif
00413     // process file data and transform it
00414     if ( i == -1 ) {
00415       res.status = KLFERR_NOEPSBBOX;
00416       res.errorstr = QObject::tr("File '%1' does not contain line \"%%BoundingBox: ... \" !",
00417                                  "KLFBackend").arg(fnRawEps);
00418       return res;
00419     }
00420     int ax, ay, bx, by;
00421     char temp[250];
00422     const int k = i;
00423     i += strlen("%%BoundingBox:");
00424     int n = sscanf(epscontent_s.data()+i, "%d %d %d %d", &ax, &ay, &bx, &by);
00425     if ( n != 4 ) {
00426       res.status = KLFERR_BADEPSBBOX;
00427       res.errorstr = QObject::tr("file %1: Line %%BoundingBox: can't read values!\n", "KLFBackend")
00428         .arg(fnRawEps);
00429       return res;
00430     }
00431     // grow bbox by settings.Xborderoffset points
00432     // Don't forget: '%' in printf has special meaning (!) -> double percent signs '%'->'%%'
00433     sprintf(temp, "%%%%BoundingBox: %d %d %d %d",
00434             (int)(ax-settings.lborderoffset+0.5),
00435             (int)(ay-settings.bborderoffset+0.5),
00436             (int)(bx+settings.rborderoffset+0.5),
00437             (int)(by+settings.tborderoffset+0.5));
00438     QString chunk = QString::fromLocal8Bit(epscontent_s.data()+k);
00439     QRegExp rx("^%%BoundingBox: [0-9]+ [0-9]+ [0-9]+ [0-9]+");
00440     rx.rx_indexin(chunk);
00441     int l = rx.matchedLength();
00442     epscontent_s.replace(k, l, temp);
00443 
00444     // write content back to second file
00445     QFile epsgoodfile(fnBBCorrEps);
00446     r = epsgoodfile.open(dev_WRITEONLY);
00447     if ( ! r ) {
00448       res.status = KLFERR_EPSWRITEFAIL;
00449       res.errorstr = QObject::tr("Can't write to file '%1'!\n", "KLFBackend")
00450         .arg(fnBBCorrEps);
00451       return res;
00452     }
00453     epsgoodfile.dev_write(epscontent_s);
00454 
00455     if ( ! settings.outlineFonts ) {
00456       res.epsdata.ba_assign(epscontent_s);
00457     }
00458     // res.epsdata is now set.
00459 
00460     qDebug("%s: %s:  eps bbox set.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;    
00461 
00462   } // end of block "make EPS"
00463 
00464   if (settings.outlineFonts) {
00465     // run 'gs' to outline fonts
00466     KLFBlockProcess proc;
00467     QStringList args;
00468     args << settings.gsexec << "-dNOCACHE" << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop"
00469          << "-sDEVICE=pswrite" << "-sOutputFile="+dir_native_separators(fnOutlFontsEps)
00470          << "-q" << "-dBATCH" << dir_native_separators(fnBBCorrEps);
00471 
00472     qDebug("%s: %s: about to gs (for outline fonts)...\n%s", KLF_FUNC_NAME, KLF_SHORT_TIME,
00473            qPrintable(args.join(" ")));
00474     bool r = proc.startProcess(args, execenv);
00475     qDebug("%s: %s:  gs returned (for outline fonts).", KLF_FUNC_NAME, KLF_SHORT_TIME) ;    
00476   
00477     if ( ! r ) {
00478       res.status = KLFERR_NOGSPROG;
00479       res.errorstr = QObject::tr("Unable to start gs!\n", "KLFBackend");
00480       return res;
00481     }
00482     if ( !proc.processNormalExit() ) {
00483       res.status = KLFERR_GSNONORMALEXIT;
00484       res.errorstr = QObject::tr("gs died abnormally!\n", "KLFBackend");
00485       return res;
00486     }
00487     if ( proc.processExitStatus() != 0) {
00488       res.status = KLFERR_PROGERR_GS_OF;
00489       res.errorstr = progErrorMsg("gs", proc.processExitStatus(), proc.readStderrString(),
00490                                   proc.readStdoutString());
00491       return res;
00492     }
00493     if (!QFile::exists(fnOutlFontsEps)) {
00494       res.status = KLFERR_NOEPSFILE_OF;
00495       res.errorstr = QObject::tr("EPS file (with outlined fonts) didn't appear after call to gs!\n",
00496                                  "KLFBackend");
00497       return res;
00498     }
00499 
00500     // get and save outlined EPS to memory
00501     QFile ofepsfile(fnOutlFontsEps);
00502     r = ofepsfile.open(dev_READONLY);
00503     if ( ! r ) {
00504       res.status = KLFERR_EPSREADFAIL_OF;
00505       res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend")
00506         .arg(fnOutlFontsEps);
00507       return res;
00508     }
00509     res.epsdata = ofepsfile.readAll();
00510     ofepsfile.close();
00511     // res.epsdata is now set to the outlined-fonts version
00512   }
00513 
00514   { // run 'gs' to get png
00515     KLFBlockProcess proc;
00516     QStringList args;
00517     args << settings.gsexec << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop"
00518          << "-r"+QString::number(in.dpi) << "-dTextAlphaBits=4"
00519          << "-dGraphicsAlphaBits=4";
00520     if (qAlpha(in.bg_color) > 0) { // we're forcing a background color
00521       args << "-sDEVICE=png16m";
00522     } else {
00523       args << "-sDEVICE=pngalpha";
00524     }
00525     args << "-sOutputFile="+dir_native_separators(fnPng) << "-q" << "-dBATCH"
00526          << dir_native_separators(fnFinalEps);
00527 
00528     qDebug("%s: %s:  about to gs... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
00529     bool r = proc.startProcess(args, execenv);
00530     qDebug("%s: %s:  gs returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;    
00531   
00532     if ( ! r ) {
00533       res.status = KLFERR_NOGSPROG;
00534       res.errorstr = QObject::tr("Unable to start gs!\n", "KLFBackend");
00535       return res;
00536     }
00537     if ( !proc.processNormalExit() ) {
00538       res.status = KLFERR_GSNONORMALEXIT;
00539       res.errorstr = QObject::tr("gs died abnormally!\n", "KLFBackend");
00540       return res;
00541     }
00542     if ( proc.processExitStatus() != 0) {
00543       res.status = KLFERR_PROGERR_GS;
00544       res.errorstr = progErrorMsg("gs", proc.processExitStatus(), proc.readStderrString(),
00545                                   proc.readStdoutString());
00546       return res;
00547     }
00548     if (!QFile::exists(fnPng)) {
00549       res.status = KLFERR_NOPNGFILE;
00550       res.errorstr = QObject::tr("PNG file didn't appear after call to gs!\n", "KLFBackend");
00551       return res;
00552     }
00553 
00554     // get and save PNG to memory
00555     QFile pngfile(fnPng);
00556     r = pngfile.open(dev_READONLY);
00557     if ( ! r ) {
00558       res.status = KLFERR_PNGREADFAIL;
00559       res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend")
00560         .arg(fnPng);
00561       return res;
00562     }
00563     res.pngdata_raw = pngfile.readAll();
00564     pngfile.close();
00565     // res.pngdata_raw is now set.
00566     res.result.loadFromData(res.pngdata_raw, "PNG");
00567 
00568     // store some meta-information into result
00569     res.result.img_settext("AppVersion", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
00570     res.result.img_settext("Application",
00571                  QObject::tr("Created with KLatexFormula version %1", "KLFBackend::saveOutputToFile"));
00572     res.result.img_settext("Software", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
00573     res.result.img_settext("InputLatex", in.latex);
00574     res.result.img_settext("InputMathMode", in.mathmode);
00575     res.result.img_settext("InputPreamble", in.preamble);
00576     res.result.img_settext("InputFgColor", QString("rgb(%1, %2, %3)").arg(qRed(in.fg_color))
00577                    .arg(qGreen(in.fg_color)).arg(qBlue(in.fg_color)));
00578     res.result.img_settext("InputBgColor", QString("rgba(%1, %2, %3, %4)").arg(qRed(in.bg_color))
00579                    .arg(qGreen(in.bg_color)).arg(qBlue(in.bg_color))
00580                    .arg(qAlpha(in.bg_color)));
00581     res.result.img_settext("InputDPI", QString::number(in.dpi));
00582     res.result.img_settext("SettingsTBorderOffset", QString::number(settings.tborderoffset));
00583     res.result.img_settext("SettingsRBorderOffset", QString::number(settings.rborderoffset));
00584     res.result.img_settext("SettingsBBorderOffset", QString::number(settings.bborderoffset));
00585     res.result.img_settext("SettingsLBorderOffset", QString::number(settings.lborderoffset));
00586     res.result.img_settext("SettingsOutlineFonts", settings.outlineFonts?QString("true"):QString("false"));
00587   }
00588 
00589   { // create "final" PNG data
00590 #ifdef KLFBACKEND_QT4
00591     QBuffer buf(&res.pngdata);
00592 #else
00593     QBuffer buf(res.pngdata);
00594 #endif
00595     buf.open(dev_WRITEONLY);
00596     bool r = res.result.save(&buf, "PNG");
00597     if (!r) {
00598       qWarning("%s: Error: Can't save \"final\" PNG data.", KLF_FUNC_NAME);
00599       res.pngdata.ba_assign(res.pngdata_raw);
00600     }
00601   }
00602 
00603   if (!settings.epstopdfexec.isEmpty()) {
00604     // if we have epstopdf functionality, then we'll take advantage of it to generate pdf:
00605     KLFBlockProcess proc;
00606     QStringList args;
00607     args << settings.epstopdfexec << dir_native_separators(fnFinalEps)
00608          << ("--outfile="+dir_native_separators(fnPdf));
00609 
00610     qDebug("%s: %s:  about to epstopdf... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;    
00611     bool r = proc.startProcess(args, execenv);
00612     qDebug("%s: %s:  epstopdf returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;    
00613 
00614     if ( ! r ) {
00615       res.status = KLFERR_NOEPSTOPDFPROG;
00616       res.errorstr = QObject::tr("Unable to start epstopdf!\n", "KLFBackend");
00617       return res;
00618     }
00619     if ( !proc.processNormalExit() ) {
00620       res.status = KLFERR_EPSTOPDFNONORMALEXIT;
00621       res.errorstr = QObject::tr("epstopdf died nastily!\n", "KLFBackend");
00622       return res;
00623     }
00624     if ( proc.processExitStatus() != 0) {
00625       res.status = KLFERR_PROGERR_EPSTOPDF;
00626       res.errorstr = progErrorMsg("epstopdf", proc.processExitStatus(), proc.readStderrString(),
00627                                   proc.readStdoutString());
00628       return res;
00629     }
00630     if (!QFile::exists(fnPdf)) {
00631       qDebug("%s: %s: pdf file '%s' didn't appear after epstopdf!", KLF_FUNC_NAME, KLF_SHORT_TIME,
00632              qPrintable(fnPdf));
00633       res.status = KLFERR_NOPDFFILE;
00634       res.errorstr = QObject::tr("PDF file didn't appear after call to epstopdf!\n", "KLFBackend");
00635       return res;
00636     }
00637 
00638     // get and save PDF to memory
00639     QFile pdffile(fnPdf);
00640     r = pdffile.open(dev_READONLY);
00641     if ( ! r ) {
00642       res.status = KLFERR_PDFREADFAIL;
00643       res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend").arg(fnPdf);
00644       return res;
00645     }
00646     res.pdfdata = pdffile.readAll();
00647 
00648   }
00649 
00650   qDebug("%s: %s:  end of function.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;    
00651 
00652   return res;
00653 }
00654 
00655 
00656 void KLFBackend::cleanup(QString tempfname)
00657 {
00658   if (QFile::exists(tempfname+".tex")) QFile::remove(tempfname+".tex");
00659   if (QFile::exists(tempfname+".dvi")) QFile::remove(tempfname+".dvi");
00660   if (QFile::exists(tempfname+".aux")) QFile::remove(tempfname+".aux");
00661   if (QFile::exists(tempfname+".log")) QFile::remove(tempfname+".log");
00662   if (QFile::exists(tempfname+".toc")) QFile::remove(tempfname+".toc");
00663   if (QFile::exists(tempfname+".eps")) QFile::remove(tempfname+".eps");
00664   if (QFile::exists(tempfname+"-good.eps")) QFile::remove(tempfname+"-good.eps");
00665   if (QFile::exists(tempfname+"-raw.eps")) QFile::remove(tempfname+"-raw.eps");
00666   if (QFile::exists(tempfname+"-bbcorr.eps")) QFile::remove(tempfname+"-bbcorr.eps");
00667   if (QFile::exists(tempfname+"-outlfonts.eps")) QFile::remove(tempfname+"-outlfonts.eps");
00668   if (QFile::exists(tempfname+".png")) QFile::remove(tempfname+".png");
00669   if (QFile::exists(tempfname+".pdf")) QFile::remove(tempfname+".pdf");
00670 }
00671 
00672 // static private mutex object
00673 QMutex KLFBackend::__mutex;
00674 
00675 KLF_EXPORT bool operator==(const KLFBackend::klfInput& a, const KLFBackend::klfInput& b)
00676 {
00677   return a.latex == b.latex &&
00678     a.mathmode == b.mathmode &&
00679     a.preamble == b.preamble &&
00680     a.fg_color == b.fg_color &&
00681     a.bg_color == b.bg_color &&
00682     a.dpi == b.dpi;
00683 }
00684 
00685 
00686 bool KLFBackend::saveOutputToDevice(const klfOutput& klfoutput, QIODevice *device,
00687                                     const QString& fmt, QString *errorStringPtr)
00688 {
00689   QString format = fmt.s_trimmed().s_toUpper();
00690 
00691   // now choose correct data source and write to fout
00692   if (format == "EPS" || format == "PS") {
00693     device->dev_write(klfoutput.epsdata);
00694   } else if (format == "PNG") {
00695     device->dev_write(klfoutput.pngdata);
00696   } else if (format == "PDF") {
00697     if (klfoutput.pdfdata.isEmpty()) {
00698       QString error = QObject::tr("PDF format is not available!\n",
00699                                   "KLFBackend::saveOutputToFile");
00700       qWarning("%s", qPrintable(error));
00701       if (errorStringPtr != NULL)
00702         errorStringPtr->operator=(error);
00703       return false;
00704     }
00705     device->dev_write(klfoutput.pdfdata);
00706  } else {
00707     bool res = klfoutput.result.save(device, format.s_toLatin1());
00708     if ( ! res ) {
00709       QString errstr = QObject::tr("Unable to save image in format `%1'!",
00710                                    "KLFBackend::saveOutputToDevice").arg(format);
00711       qWarning("%s", qPrintable(errstr));
00712       if (errorStringPtr != NULL)
00713         *errorStringPtr = errstr;
00714       return false;
00715     }
00716   }
00717 
00718   return true;
00719 }
00720 
00721 bool KLFBackend::saveOutputToFile(const klfOutput& klfoutput, const QString& fileName,
00722                                   const QString& fmt, QString *errorStringPtr)
00723 {
00724   QString format = fmt;
00725   // determine format first
00726   if (format.isEmpty() && !fileName.isEmpty()) {
00727     QFileInfo fi(fileName);
00728     if ( ! fi.fi_suffix().isEmpty() )
00729       format = fi.fi_suffix();
00730   }
00731   if (format.isEmpty())
00732     format = QLatin1String("PNG");
00733   format = format.s_trimmed().s_toUpper();
00734   // got format. choose output now and prepare write
00735   QFile fout;
00736   if (fileName.isEmpty() || fileName == "-") {
00737     if ( ! fout.f_open_fp(stdout) ) {
00738       QString error = QObject::tr("Unable to open stderr for write! Error: %1\n",
00739                                   "KLFBackend::saveOutputToFile").arg(fout.f_error());
00740       qWarning("%s", qPrintable(error));
00741       if (errorStringPtr != NULL)
00742         *errorStringPtr = error;
00743       return false;
00744     }
00745   } else {
00746     fout.f_setFileName(fileName);
00747     if ( ! fout.open(dev_WRITEONLY) ) {
00748       QString error = QObject::tr("Unable to write to file `%1'! Error: %2\n",
00749                                   "KLFBackend::saveOutputToFile")
00750         .arg(fileName).arg(fout.f_error());
00751       qWarning("%s", qPrintable(error));
00752       if (errorStringPtr != NULL)
00753         *errorStringPtr = error;
00754       return false;
00755     }
00756   }
00757 
00758   return saveOutputToDevice(klfoutput, &fout, format, errorStringPtr);
00759 }
00760 
00761 
00762 bool KLFBackend::detectSettings(klfSettings *settings, const QString& extraPath)
00763 {
00764   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00765 
00766   QStringList stdextrapaths;
00767   int k, j;
00768   for (k = 0; standard_extra_paths[k] != NULL; ++k) {
00769     stdextrapaths.append(standard_extra_paths[k]);
00770   }
00771   QString extra_paths = stdextrapaths.join(QString("")+KLF_PATH_SEP);
00772   if (!extraPath.isEmpty())
00773     extra_paths += KLF_PATH_SEP + extraPath;
00774 
00775   // temp dir
00776 #ifdef KLFBACKEND_QT4
00777   settings->tempdir = QDir::fromNativeSeparators(QDir::tempPath());
00778 #else
00779 # if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_MACX)
00780   settings->tempdir = "/tmp";
00781 # elif defined(Q_OS_WIN32)
00782   settings->tempdir = getenv("TEMP");
00783 # else
00784   settings->tempdir = QString();
00785 # endif
00786 #endif
00787 
00788   // sensible defaults
00789   settings->lborderoffset = 1;
00790   settings->tborderoffset = 1;
00791   settings->rborderoffset = 1;
00792   settings->bborderoffset = 1;
00793   
00794   // find executables
00795   struct { QString * target_setting; QStringList prog_names; }  progs_to_find[] = {
00796     { & settings->latexexec, progLATEX },
00797     { & settings->dvipsexec, progDVIPS },
00798     { & settings->gsexec, progGS },
00799     { & settings->epstopdfexec, progEPSTOPDF },
00800     { NULL, QStringList() }
00801   };
00802   // replace @executable_path in extra_paths
00803   klfDbg(klfFmtCC("Our base extra paths are: %s", qPrintable(extra_paths))) ;
00804   QString ourextrapaths = extra_paths;
00805   ourextrapaths.replace("@executable_path", qApp->applicationDirPath());
00806   klfDbg(klfFmtCC("Our extra paths are: %s", qPrintable(ourextrapaths))) ;
00807   // and actually search for those executables
00808   for (k = 0; progs_to_find[k].target_setting != NULL; ++k) {
00809     klfDbg("Looking for "+progs_to_find[k].prog_names.join(" or ")) ;
00810     for (j = 0; j < (int)progs_to_find[k].prog_names.size(); ++j) {
00811       klfDbg("Testing `"+progs_to_find[k].prog_names[j]+"'") ;
00812       *progs_to_find[k].target_setting
00813         = klfSearchPath(progs_to_find[k].prog_names[j], ourextrapaths);
00814       if (!progs_to_find[k].target_setting->isEmpty()) {
00815         klfDbg("Found! at `"+ *progs_to_find[k].target_setting+"'") ;
00816         break; // found a program
00817       }
00818     }
00819   }
00820 
00821   klf_detect_execenv(settings);
00822 
00823   bool result_failure =
00824     settings->tempdir.isEmpty() || settings->latexexec.isEmpty() || settings->dvipsexec.isEmpty() ||
00825     settings->gsexec.isEmpty(); // NOTE:  settings->epstopdfexec.isEmpty() is NOT a failure
00826 
00827   return !result_failure;
00828 }
00829 
00830 
00847 KLF_EXPORT bool klf_detect_execenv(KLFBackend::klfSettings *settings)
00848 {
00849   // detect mgs.exe as ghostscript and setup its environment properly
00850   QFileInfo gsfi(settings->gsexec);
00851   if (gsfi.fileName() == "mgs.exe") {
00852     QString mgsenv = QString("MIKTEX_GS_LIB=")
00853       + dir_native_separators(gsfi.fi_absolutePath()+"/../../ghostscript/base")
00854       + ";"
00855       + dir_native_separators(gsfi.fi_absolutePath()+"/../../fonts");
00856     __klf_append_replace_env_var(& settings->execenv, "MIKTEX_GS_LIB", mgsenv);
00857     klfDbg("Adjusting environment for mgs.exe: `"+mgsenv+"'") ;
00858   }
00859 
00860 #ifdef Q_WS_MAC
00861   // make sure that epstopdf's path is in PATH because it wants to all gs
00862   // (eg fink distributions)
00863   if (!settings->epstopdfexec.isEmpty()) {
00864     QFileInfo epstopdf_fi(settings->epstopdfexec);
00865     QString execenvpath = QString("PATH=%1:$PATH").arg(epstopdf_fi.fi_absolutePath());
00866     __klf_append_replace_env_var(& settings->execenv, "PATH", execenvpath);
00867   }
00868 #endif
00869 
00870   return true;
00871 }

Generated by doxygen 1.7.3