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@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 
00023 #include <stdlib.h>
00024 
00025 //#include <iostream>
00026 
00027 #include <qregexp.h>
00028 #include <qfile.h>
00029 #include <qdatetime.h>
00030 #include <qtextstream.h>
00031 
00032 #include "klfblockprocess.h"
00033 #include "klfbackend.h"
00034 
00036 #define TRANSLATE(x) QString(x)
00037 
00038 
00039 KLFBackend::KLFBackend()
00040 {
00041 }
00042 
00043 
00044 // Utility function
00045 QString progErrorMsg(QString progname, int exitstatus, QString stderr, QString stdout)
00046 {
00047   if (stderr.isEmpty())
00048     return TRANSLATE("<p><b>%1</b> reported an error (exit status %2). Here is full stdout output:</p>\n"
00049              "<pre>\n%3</pre>")
00050       .arg(progname).arg(exitstatus).arg(stdout);
00051   if (stdout.isEmpty())
00052     return TRANSLATE("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
00053              "<pre>\n%3</pre>")
00054       .arg(progname).arg(exitstatus).arg(stderr);
00055 
00056   return TRANSLATE("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
00057            "<pre>\n%3</pre><p>And here is full stdout output:</p><pre>\n%4</pre>")
00058     .arg(progname).arg(exitstatus).arg(stderr).arg(stdout);
00059 }
00060 
00061 
00062 KLFBackend::klfOutput KLFBackend::getLatexFormula(const klfInput& in, const klfSettings& settings)
00063 {
00064   klfOutput res;
00065   res.status = 0;
00066   res.errorstr = QString();
00067   res.result = QImage();
00068   res.pngdata = QByteArray();
00069   res.epsdata = QByteArray();
00070   res.pdfdata = QByteArray();
00071 
00072   // PROCEDURE:
00073   // - generate LaTeX-file (documentclass klatexformula.cls)
00074   // - latex --> get DVI file
00075   // - dvips -E file.dvi it to get an EPS file
00076   // - Run gs:  gs -dNOPAUSE -dSAFER -dEPSCrop -r600 -dTextAlphaBits=4 -dGraphicsAlphaBits=4
00077   //              -sDEVICE=pngalpha|png16m -sOutputFile=xxxxxx.png -q -dBATCH xxxxxx.eps   to eventually get PNG data
00078   // - if epstopdfexec is not empty, run epstopdf and get PDF file.
00079 
00080   QString tempfname = settings.tempdir + "/klatexformulatmp2-"
00081     + QDateTime::currentDateTime().toString("hh-mm-ss");
00082 
00083 
00084 #ifdef KLFBACKEND_QT4
00085   QString latexsimplified = in.latex.trimmed();
00086 #else
00087   QString latexsimplified = in.latex.stripWhiteSpace();
00088 #endif
00089   if (latexsimplified.isEmpty()) {
00090     res.errorstr = TRANSLATE("You must specify a LaTeX formula!");
00091     res.status = -1;
00092     return res;
00093   }
00094 
00095   QString latexin;
00096   if (in.mathmode.contains("...") == 0) {
00097     res.status = -2;
00098     res.errorstr = TRANSLATE("The math mode string doesn't contain '...'!");
00099     return res;
00100   }
00101   latexin = in.mathmode;
00102   latexin.replace("...", in.latex);
00103 
00104   {
00105     QFile file(tempfname+".tex");
00106 #ifdef KLFBACKEND_QT4
00107     bool r = file.open(QIODevice::WriteOnly);
00108 #else
00109     bool r = file.open(IO_WriteOnly);
00110 #endif
00111     if ( ! r ) {
00112       res.status = -3;
00113       res.errorstr = TRANSLATE("Can't open file for writing: '%1'!").arg(tempfname+".tex");
00114       return res;
00115     }
00116     QTextStream stream(&file);
00117     stream << "\\documentclass{klatexformula}\n"
00118        << "\\usepackage[usenames]{color}\n"
00119        << in.preamble << "\n"
00120        << "\\begin{document}\n"
00121        << QString("\\definecolor{klffgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.fg_color)/255.0)
00122       .arg(qGreen(in.fg_color)/255.0).arg(qBlue(in.fg_color)/255.0)
00123        << QString("\\definecolor{klfbgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.bg_color)/255.0)
00124       .arg(qGreen(in.bg_color)/255.0).arg(qBlue(in.bg_color)/255.0)
00125        << ( (qAlpha(in.bg_color)>0) ? "\\pagecolor{klfbgcolor}\n" : "" )
00126        << "{\\color{klffgcolor} " << latexin << " }\n"
00127        << "\\end{document}\n";
00128   }
00129 
00130   { // execute latex
00131     // we need klatexformula.cls, which is in path settings.klfclspath
00132 
00133     KLFBlockProcess proc;
00134     QStringList args;
00135     QStringList env;
00136     extern char **environ;
00137 
00138     proc.setWorkingDirectory(settings.tempdir);
00139     bool texinputsset = false;
00140     for (int i = 0; environ && environ[i] != NULL; ++i) {
00141       if (!strncmp(environ[i], "TEXINPUTS=", strlen("TEXINPUTS="))) {
00142     QString s = environ[i];
00143     s.insert(strlen("TEXINPUTS="), settings.klfclspath+":");
00144     texinputsset = true;
00145     env << s;
00146       } else {
00147     env << environ[i];
00148       }
00149     }
00150     if (!texinputsset) {
00151       env << "TEXINPUTS="+settings.klfclspath+":.";
00152     }
00153     args << settings.latexexec << tempfname+".tex";
00154 
00155     bool r = proc.startProcess(args, env);
00156 
00157 
00158     if (!r) {
00159       res.status = -4;
00160       res.errorstr = TRANSLATE("Unable to start Latex!");
00161       return res;
00162     }
00163     if (!proc.normalExit()) {
00164       res.status = -5;
00165       res.errorstr = TRANSLATE("Latex was killed!");
00166       return res;
00167     }
00168     if (proc.exitStatus() != 0) {
00169       cleanup(tempfname);
00170       res.status = 1;
00171       res.errorstr = progErrorMsg("LaTeX", proc.exitStatus(), proc.readStderrString(), proc.readStdoutString());
00172       return res;
00173     }
00174 
00175     if (!QFile::exists(tempfname + ".dvi")) {
00176       res.status = -6;
00177       res.errorstr = TRANSLATE("DVI file didn't appear after having called Latex!");
00178       return res;
00179     }
00180 
00181   } // end of 'latex' block
00182 
00183   { // execute dvips -E
00184 
00185     KLFBlockProcess proc;
00186     QStringList args;
00187     args << settings.dvipsexec << "-E" << (tempfname+".dvi") << "-o" << (tempfname+".eps");
00188 
00189     bool r = proc.startProcess(args);
00190 
00191     if ( ! r ) {
00192       res.status = -7;
00193       res.errorstr = TRANSLATE("Unable to start dvips!\n");
00194       return res;
00195     }
00196     if ( !proc.normalExit() ) {
00197       res.status = -8;
00198       res.errorstr = TRANSLATE("Dvips was mercilessly killed!\n");
00199       return res;
00200     }
00201     if ( proc.exitStatus() != 0) {
00202       res.status = 2;
00203       res.errorstr = progErrorMsg("dvips", proc.exitStatus(), proc.readStderrString(), proc.readStdoutString());
00204       return res;
00205     }
00206     if (!QFile::exists(tempfname + ".eps")) {
00207       res.status = -9;
00208       res.errorstr = TRANSLATE("EPS file didn't appear after dvips call!\n");
00209       return res;
00210     }
00211 
00212     // add some space on bounding-box to avoid some too tight bounding box bugs
00213     // read eps file
00214     QFile epsfile(tempfname+".eps");
00215 #ifdef KLFBACKEND_QT4
00216     r = epsfile.open(QIODevice::ReadOnly);
00217 #else
00218     r = epsfile.open(IO_ReadOnly);
00219 #endif
00220     if ( ! r ) {
00221       res.status = -10;
00222       res.errorstr = TRANSLATE("Can't read file '%1'!\n").arg(tempfname+".eps");
00223       return res;
00224     }
00225     QByteArray epscontent = epsfile.readAll();
00226 #ifdef KLFBACKEND_QT4
00227     int i = epscontent.indexOf("%%BoundingBox: ");
00228     if ( i == -1 ) {
00229       res.status = -11;
00230       res.errorstr = TRANSLATE("File '%1' does not contain line \"%%BoundingBox: ... \" !").arg(tempfname+".eps");
00231     }
00232     int ax, ay, bx, by;
00233     char temp[250];
00234     int k = i;
00235     i += strlen("%%BoundingBox:");
00236     int n = sscanf(epscontent.constData()+i, "%d %d %d %d", &ax, &ay, &bx, &by);
00237     if ( n != 4 ) {
00238       res.status = -12;
00239       res.errorstr = TRANSLATE("file %1: Line %%BoundingBox: can't read values!\n").arg(tempfname+".eps");
00240       return res;
00241     }
00242     sprintf(temp, "%%%%BoundingBox: %d %d %d %d", ax-settings.lborderoffset, ay-settings.bborderoffset,
00243         bx+settings.rborderoffset, by+settings.tborderoffset); // grow bbox by settings.?borderoffset points
00244     QString chunk = QString::fromLocal8Bit(epscontent.constData()+k);
00245     QRegExp rx("^%%BoundingBox: [0-9]+ [0-9]+ [0-9]+ [0-9]+");
00246     (void) rx.indexIn(chunk);
00247     int l = rx.matchedLength();
00248     epscontent.replace(k, l, temp);
00249     res.epsdata = epscontent;
00250 #else
00251     QCString epscontent_s(epscontent.data(), epscontent.size());
00252     // process file data and transform it
00253     int i = epscontent_s.find("%%BoundingBox: ");
00254     if ( i == -1 ) {
00255       res.status = -11;
00256       res.errorstr = TRANSLATE("File '%1' does not contain line \"%%BoundingBox: ... \" !").arg(tempfname+".eps");
00257       return res;
00258     }
00259     int ax, ay, bx, by;
00260     char temp[250];
00261     i += strlen("%%BoundingBox:");
00262     int n = sscanf((const char*)epscontent_s+i, "%d %d %d %d", &ax, &ay, &bx, &by);
00263     if ( n != 4 ) {
00264       res.status = -12;
00265       res.errorstr = TRANSLATE("file %1: Line %%BoundingBox: can't read values!\n").arg(tempfname+".eps");
00266       return res;
00267     }
00268     sprintf(temp, "%%%%BoundingBox: %d %d %d %d", ax-settings.lborderoffset, ay-settings.bborderoffset,
00269         bx+settings.rborderoffset, by+settings.tborderoffset); // grow bbox by settings.?borderoffset points
00270     epscontent_s.replace(QRegExp("%%BoundingBox: [0-9]+ [0-9]+ [0-9]+ [0-9]+"), temp);
00271     res.epsdata.duplicate(epscontent_s.data(), epscontent_s.length());
00272 #endif
00273     // write content back to second file
00274     QFile epsgoodfile(tempfname+"-good.eps");
00275 #ifdef KLFBACKEND_QT4
00276     r = epsgoodfile.open(QIODevice::WriteOnly);
00277 #else
00278     r = epsgoodfile.open(IO_WriteOnly);
00279 #endif
00280     if ( ! r ) {
00281       res.status = -13;
00282       res.errorstr = TRANSLATE("Can't write to file '%1'!\n").arg(tempfname+"-good.eps");
00283       return res;
00284     }
00285 #ifdef KLFBACKEND_QT4
00286     epsgoodfile.write(res.epsdata);
00287 #else
00288     epsgoodfile.writeBlock(res.epsdata);
00289 #endif
00290     // res.epsdata is now set.
00291 
00292   } // end of block "make EPS"
00293 
00294   { // run 'gs' to get png
00295     KLFBlockProcess proc;
00296     QStringList args;
00297     args << settings.gsexec << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop" << "-r"+QString::number(in.dpi)
00298      << "-dTextAlphaBits=4" << "-dGraphicsAlphaBits=4";
00299     if (qAlpha(in.bg_color) > 0) { // we're forcing a background color
00300       args << "-sDEVICE=png16m";
00301     } else {
00302       args << "-sDEVICE=pngalpha";
00303     }
00304     args << "-sOutputFile="+tempfname+".png" << "-q" << "-dBATCH" << (tempfname+"-good.eps");
00305 
00306     bool r = proc.startProcess(args);
00307   
00308     if ( ! r ) {
00309       res.status = -7;
00310       res.errorstr = TRANSLATE("Unable to start gs!\n");
00311       return res;
00312     }
00313     if ( !proc.normalExit() ) {
00314       res.status = -8;
00315       res.errorstr = TRANSLATE("gs died abnormally!\n");
00316       return res;
00317     }
00318     if ( proc.exitStatus() != 0) {
00319       res.status = 3;
00320       res.errorstr = progErrorMsg("gs", proc.exitStatus(), proc.readStderrString(), proc.readStdoutString());
00321       return res;
00322     }
00323     if (!QFile::exists(tempfname + ".png")) {
00324       res.status = -9;
00325       res.errorstr = TRANSLATE("PNG file didn't appear after call to gs!\n");
00326       return res;
00327     }
00328 
00329     // get and save PNG to memory
00330     QFile pngfile(tempfname+".png");
00331 #ifdef KLFBACKEND_QT4
00332     r = pngfile.open(QIODevice::ReadOnly);
00333 #else
00334     r = pngfile.open(IO_ReadOnly);
00335 #endif
00336     if ( ! r ) {
00337       res.status = -10;
00338       res.errorstr = TRANSLATE("Unable to read file %1!\n").arg(tempfname+".png");
00339       return res;
00340     }
00341     res.pngdata = pngfile.readAll();
00342     pngfile.close();
00343     // res.pngdata is now set.
00344     res.result.loadFromData(res.pngdata, "PNG");
00345 
00346   }
00347 
00348   if (!settings.epstopdfexec.isEmpty()) {
00349     // if we have epstopdf functionality, then we'll take advantage of it to generate pdf:
00350     KLFBlockProcess proc;
00351     QStringList args;
00352     args << settings.epstopdfexec << (tempfname+"-good.eps") << "-o" << (tempfname+".pdf");
00353 
00354     bool r = proc.startProcess(args);
00355 
00356     if ( ! r ) {
00357       res.status = -11;
00358       res.errorstr = TRANSLATE("Unable to start epstopdf!\n");
00359       return res;
00360     }
00361     if ( !proc.normalExit() ) {
00362       res.status = -12;
00363       res.errorstr = TRANSLATE("epstopdf died nastily!\n");
00364       return res;
00365     }
00366     if ( proc.exitStatus() != 0) {
00367       res.status = 4;
00368       res.errorstr = progErrorMsg("epstopdf", proc.exitStatus(), proc.readStderrString(), proc.readStdoutString());
00369       return res;
00370     }
00371     if (!QFile::exists(tempfname + ".pdf")) {
00372       res.status = -13;
00373       res.errorstr = TRANSLATE("PDF file didn't appear after call to epstopdf!\n");
00374       return res;
00375     }
00376 
00377     // get and save PDF to memory
00378     QFile pdffile(tempfname+".pdf");
00379 #ifdef KLFBACKEND_QT4
00380     r = pdffile.open(QIODevice::ReadOnly);
00381 #else
00382     r = pdffile.open(IO_ReadOnly);
00383 #endif
00384     if ( ! r ) {
00385       res.status = -14;
00386       res.errorstr = TRANSLATE("Unable to read file %1!\n").arg(tempfname+".pdf");
00387       return res;
00388     }
00389     res.pdfdata = pdffile.readAll();
00390 
00391   }
00392 
00393   // clean up our mess
00394   cleanup(tempfname);
00395 
00396 
00397   return res;
00398 }
00399 
00400 
00401 void KLFBackend::cleanup(QString tempfname)
00402 {
00403   if (QFile::exists(tempfname+".tex")) QFile::remove(tempfname+".tex");
00404   if (QFile::exists(tempfname+".dvi")) QFile::remove(tempfname+".dvi");
00405   if (QFile::exists(tempfname+".aux")) QFile::remove(tempfname+".aux");
00406   if (QFile::exists(tempfname+".log")) QFile::remove(tempfname+".log");
00407   if (QFile::exists(tempfname+".toc")) QFile::remove(tempfname+".toc");
00408   if (QFile::exists(tempfname+".eps")) QFile::remove(tempfname+".eps");
00409   if (QFile::exists(tempfname+"-good.eps")) QFile::remove(tempfname+"-good.eps");
00410   if (QFile::exists(tempfname+".png")) QFile::remove(tempfname+".png");
00411   if (QFile::exists(tempfname+".pdf")) QFile::remove(tempfname+".pdf");
00412 }
00413 

Generated on Sat Apr 28 14:14:02 2007 for KLatexFormula-Library-Backend by  doxygen 1.4.6