[KLF Application][KLF Tools][KLF Backend][KLF Home]
KLatexFormula Project
klfbackend.cpp
1 /***************************************************************************
2  * file klfbackend.cpp
3  * This file is part of the KLatexFormula Project.
4  * Copyright (C) 2011 by Philippe Faist
5  * philippe.faist at bluewin.ch
6  * *
7  * This program is free software; you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation; either version 2 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program; if not, write to the *
19  * Free Software Foundation, Inc., *
20  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
21  ***************************************************************************/
22 /* $Id: klfbackend.cpp 748 2012-01-01 15:06:40Z phfaist $ */
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/time.h>
27 
28 #include <qapplication.h>
29 #include <qregexp.h>
30 #include <qfile.h>
31 #include <qdatetime.h>
32 #include <qtextstream.h>
33 #include <qbuffer.h>
34 #include <qdir.h>
35 
36 
37 #include "klfblockprocess.h"
38 #include "klfbackend.h"
39 
40 // write Qt 3/4 compatible code
41 #include "klfqt34common.h"
42 
43 
74 // some standard guess settings for system configurations
75 
76 #ifdef KLF_EXTRA_SEARCH_PATHS
77 # define EXTRA_PATHS_PRE KLF_EXTRA_SEARCH_PATHS ,
78 # define EXTRA_PATHS KLF_EXTRA_SEARCH_PATHS
79 #else
80 # define EXTRA_PATHS_PRE
81 # define EXTRA_PATHS
82 #endif
83 
84 
85 #if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
86 QStringList progLATEX = QStringList() << "latex.exe";
87 QStringList progDVIPS = QStringList() << "dvips.exe";
88 QStringList progGS = QStringList() << "gswin32c.exe" << "mgs.exe";
89 QStringList progEPSTOPDF = QStringList() << "epstopdf.exe";
90 static const char * standard_extra_paths[] = {
91  EXTRA_PATHS_PRE
92  "C:\\Program Files\\MiKTeX*\\miktex\\bin",
93  "C:\\Program Files\\gs\\gs*\\bin",
94  NULL
95 };
96 #elif defined(Q_WS_MAC)
97 QStringList progLATEX = QStringList() << "latex";
98 QStringList progDVIPS = QStringList() << "dvips";
99 QStringList progGS = QStringList() << "gs";
100 QStringList progEPSTOPDF = QStringList() << "epstopdf";
101 static const char * standard_extra_paths[] = {
102  EXTRA_PATHS_PRE
103  "/usr/texbin:/usr/local/bin:/sw/bin:/sw/usr/bin",
104  NULL
105 };
106 #else
107 QStringList progLATEX = QStringList() << "latex";
108 QStringList progDVIPS = QStringList() << "dvips";
109 QStringList progGS = QStringList() << "gs";
110 QStringList progEPSTOPDF = QStringList() << "epstopdf";
111 static const char * standard_extra_paths[] = {
112  EXTRA_PATHS_PRE
113  NULL
114 };
115 #endif
116 
117 
118 
119 // ---------------------------------
120 
121 KLFBackend::KLFBackend()
122 {
123 }
124 
125 
126 // Utility function
127 QString progErrorMsg(QString progname, int exitstatus, QString stderrstr, QString stdoutstr)
128 {
129  QString stdouthtml = stdoutstr;
130  QString stderrhtml = stderrstr;
131  stdouthtml.replace("&", "&amp;");
132  stdouthtml.replace("<", "&lt;");
133  stdouthtml.replace(">", "&gt;");
134  stderrhtml.replace("&", "&amp;");
135  stderrhtml.replace("<", "&lt;");
136  stderrhtml.replace(">", "&gt;");
137 
138  if (stderrstr.isEmpty() && stdoutstr.isEmpty())
139  return QObject::tr("<p><b>%1</b> reported an error (exit status %2). No Output was generated.</p>",
140  "KLFBackend")
141  .arg(progname).arg(exitstatus);
142  if (stderrstr.isEmpty())
143  return
144  QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stdout output:</p>\n"
145  "<pre>\n%3</pre>", "KLFBackend")
146  .arg(progname).arg(exitstatus).arg(stdouthtml);
147  if (stdoutstr.isEmpty())
148  return
149  QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
150  "<pre>\n%3</pre>", "KLFBackend")
151  .arg(progname).arg(exitstatus).arg(stderrhtml);
152 
153  return QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
154  "<pre>\n%3</pre><p>And here is full stdout output:</p><pre>\n%4</pre>", "KLFBackend")
155  .arg(progname).arg(exitstatus).arg(stderrhtml).arg(stdouthtml);
156 }
157 
158 
159 /* * Internal.
160  * \internal */
161 struct cleanup_caller {
162  QString tempfname;
163  cleanup_caller(QString fn) : tempfname(fn) { }
164  ~cleanup_caller() {
165  KLFBackend::cleanup(tempfname);
166  }
167 };
168 
169 QString __klf_expand_env_vars(const QString& envexpr)
170 {
171  QString s = envexpr;
172  QRegExp rx("\\$(?:(\\$|(?:[A-Za-z0-9_]+))|\\{([A-Za-z0-9_]+)\\})");
173  int i = 0;
174  while ( (i = rx.rx_indexin_i(s, i)) != -1 ) {
175  // match found, replace it
176  QString envvarname = rx.cap(1);
177  if (envvarname.isEmpty() || envvarname == QLatin1String("$")) {
178  // note: empty variable name expands to a literal '$'
179  s.replace(i, rx.matchedLength(), QLatin1String("$"));
180  i += 1;
181  continue;
182  }
183  const char *svalue = getenv(qPrintable(envvarname));
184  QString qsvalue = (svalue != NULL) ? QString::fromLocal8Bit(svalue) : QString();
185  s.replace(i, rx.matchedLength(), qsvalue);
186  i += qsvalue.length();
187  }
188  // replacements performed
189  return s;
190 }
191 
192 void __klf_append_replace_env_var(QStringList *list, const QString& var, const QString& line)
193 {
194  // search for declaration of var in list
195  int k;
196  for (k = 0; k < (int)list->size(); ++k) {
197  if (list->operator[](k).startsWith(var+QString("="))) {
198  list->operator[](k) = line;
199  return;
200  }
201  }
202  // declaration not found, just append
203  list->append(line);
204 }
205 
206 
207 
209 {
210  // ALLOW ONLY ONE RUNNING getLatexFormula() AT A TIME
211  QMutexLocker mutexlocker(&__mutex);
212 
213  int k;
214 
215  qDebug("%s: %s: KLFBackend::getLatexFormula() called. latex=%s", KLF_FUNC_NAME, KLF_SHORT_TIME,
216  qPrintable(in.latex));
217 
218  // get full, expanded exec environment
219  QStringList execenv = klf_cur_environ();
220  for (k = 0; k < (int)settings.execenv.size(); ++k) {
221  int eqpos = settings.execenv[k].s_indexOf(QChar('='));
222  if (eqpos == -1) {
223  qWarning("%s: badly formed environment definition in `environ': %s", KLF_FUNC_NAME,
224  qPrintable(settings.execenv[k]));
225  continue;
226  }
227  QString varname = settings.execenv[k].mid(0, eqpos);
228  QString newenvdef = __klf_expand_env_vars(settings.execenv[k]);
229  __klf_append_replace_env_var(&execenv, varname, newenvdef);
230  }
231 
232  klfDbg("execution environment for sub-processes:\n"+execenv.join("\n")) ;
233 
234  klfOutput res;
235  res.status = KLFERR_NOERROR;
236  res.errorstr = QString();
237  res.result = QImage();
238  res.pngdata_raw = QByteArray();
239  res.pngdata = QByteArray();
240  res.epsdata = QByteArray();
241  res.pdfdata = QByteArray();
242  res.input = in;
243  res.settings = settings;
244 
245  // PROCEDURE:
246  // - generate LaTeX-file
247  // - latex --> get DVI file
248  // - dvips -E file.dvi it to get an EPS file
249  // - expand BBox by editing EPS file (if applicable)
250  // - outline fonts with gs (if applicable)
251  // - Run gs: gs -dNOPAUSE -dSAFER -dEPSCrop -r600 -dTextAlphaBits=4 -dGraphicsAlphaBits=4
252  // -sDEVICE=pngalpha|png16m -sOutputFile=xxxxxx.png -q -dBATCH xxxxxx.eps
253  // to eventually get PNG data
254  // - if epstopdfexec is not empty, run epstopdf and get PDF file.
255 
256  QString tempfname = settings.tempdir + "/klatexformulatmp" KLF_VERSION_STRING "-"
257  + QDateTime::currentDateTime().toString("hh-mm-ss");
258 
259  QString fnTex = tempfname + ".tex";
260  QString fnDvi = tempfname + ".dvi";
261  QString fnRawEps = tempfname + "-raw.eps";
262  QString fnBBCorrEps = tempfname + "-bbcorr.eps";
263  QString fnOutlFontsEps = tempfname + "-outlfonts.eps";
264  QString fnFinalEps = settings.outlineFonts ? fnOutlFontsEps : fnBBCorrEps;
265  QString fnPng = tempfname + ".png";
266  QString fnPdf = tempfname + ".pdf";
267 
268  // upon destruction (on stack) of this object, cleanup() will be
269  // automatically called as wanted
270  cleanup_caller cleanupcallerinstance(tempfname);
271 
272 #ifdef KLFBACKEND_QT4
273  QString latexsimplified = in.latex.s_trimmed();
274 #else
275  QString latexsimplified = in.latex.stripWhiteSpace();
276 #endif
277  if (latexsimplified.isEmpty()) {
278  res.errorstr = QObject::tr("You must specify a LaTeX formula!", "KLFBackend");
279  res.status = KLFERR_MISSINGLATEXFORMULA;
280  return res;
281  }
282 
283  QString latexin;
284  if (!in.bypassTemplate) {
285  if (in.mathmode.contains("...") == 0) {
286  res.status = KLFERR_MISSINGMATHMODETHREEDOTS;
287  res.errorstr = QObject::tr("The math mode string doesn't contain '...'!", "KLFBackend");
288  return res;
289  }
290  latexin = in.mathmode;
291  latexin.replace("...", in.latex);
292  }
293 
294  {
295  QFile file(fnTex);
296  bool r = file.open(dev_WRITEONLY);
297  if ( ! r ) {
298  res.status = KLFERR_TEXWRITEFAIL;
299  res.errorstr = QObject::tr("Can't open file for writing: '%1'!", "KLFBackend")
300  .arg(fnTex);
301  return res;
302  }
303  QTextStream stream(&file);
304  if (!in.bypassTemplate) {
305  stream << "\\documentclass{article}\n"
306  << "\\usepackage[dvips]{color}\n"
307  << in.preamble << "\n"
308  << "\\begin{document}\n"
309  << "\\thispagestyle{empty}\n"
310  << QString("\\definecolor{klffgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.fg_color)/255.0)
311  .arg(qGreen(in.fg_color)/255.0).arg(qBlue(in.fg_color)/255.0)
312  << QString("\\definecolor{klfbgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.bg_color)/255.0)
313  .arg(qGreen(in.bg_color)/255.0).arg(qBlue(in.bg_color)/255.0)
314  << ( (qAlpha(in.bg_color)>0) ? "\\pagecolor{klfbgcolor}\n" : "" )
315  << "{\\color{klffgcolor} " << latexin << " }\n"
316  << "\\end{document}\n";
317  } else {
318  stream << in.latex;
319  }
320  }
321 
322  { // execute latex
323 
324  KLFBlockProcess proc;
325  QStringList args;
326 
327  proc.setWorkingDirectory(settings.tempdir);
328 
329  args << settings.latexexec << dir_native_separators(fnTex);
330 
331  qDebug("%s: %s: about to exec latex...", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
332  bool r = proc.startProcess(args, execenv);
333  qDebug("%s: %s: latex returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
334 
335  if (!r) {
336  res.status = KLFERR_NOLATEXPROG;
337  res.errorstr = QObject::tr("Unable to start Latex program %1!", "KLFBackend")
338  .arg(settings.latexexec);
339  return res;
340  }
341  if (!proc.processNormalExit()) {
342  res.status = KLFERR_LATEXNONORMALEXIT;
343  res.errorstr = QObject::tr("Latex was killed!", "KLFBackend");
344  return res;
345  }
346  if (proc.processExitStatus() != 0) {
347  res.status = KLFERR_PROGERR_LATEX;
348  res.errorstr = progErrorMsg("LaTeX", proc.processExitStatus(), proc.readStderrString(),
349  proc.readStdoutString());
350  return res;
351  }
352 
353  if (!QFile::exists(fnDvi)) {
354  res.status = KLFERR_NODVIFILE;
355  res.errorstr = QObject::tr("DVI file didn't appear after having called Latex!", "KLFBackend");
356  return res;
357  }
358 
359  } // end of 'latex' block
360 
361  { // execute dvips -E
362 
363  KLFBlockProcess proc;
364  QStringList args;
365  args << settings.dvipsexec << "-E" << dir_native_separators(fnDvi)
366  << "-o" << dir_native_separators(fnRawEps);
367 
368  qDebug("%s: %s: about to dvips... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
369  bool r = proc.startProcess(args, execenv);
370  qDebug("%s: %s: dvips returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
371 
372  if ( ! r ) {
373  res.status = KLFERR_NODVIPSPROG;
374  res.errorstr = QObject::tr("Unable to start dvips!\n", "KLFBackend");
375  return res;
376  }
377  if ( !proc.processNormalExit() ) {
378  res.status = KLFERR_DVIPSNONORMALEXIT;
379  res.errorstr = QObject::tr("Dvips was mercilessly killed!\n", "KLFBackend");
380  return res;
381  }
382  if ( proc.processExitStatus() != 0) {
383  res.status = KLFERR_PROGERR_DVIPS;
384  res.errorstr = progErrorMsg("dvips", proc.processExitStatus(), proc.readStderrString(),
385  proc.readStdoutString());
386  return res;
387  }
388  if (!QFile::exists(fnRawEps)) {
389  res.status = KLFERR_NOEPSFILE;
390  res.errorstr = QObject::tr("EPS file didn't appear after dvips call!\n", "KLFBackend");
391  return res;
392  }
393 
394  // add some space on bounding-box to avoid some too tight bounding box bugs
395  // read eps file
396  QFile epsfile(fnRawEps);
397  r = epsfile.open(dev_READONLY);
398  if ( ! r ) {
399  res.status = KLFERR_EPSREADFAIL;
400  res.errorstr = QObject::tr("Can't read file '%1'!\n", "KLFBackend").arg(fnRawEps);
401  return res;
402  }
405  QByteArray epscontent = epsfile.readAll();
406 #ifdef KLFBACKEND_QT4
407  QByteArray epscontent_s = epscontent;
408  int i = epscontent_s.indexOf("%%BoundingBox: ");
409 #else
410  QCString epscontent_s(epscontent.data(), epscontent.size());
411  int i = epscontent_s.find("%%BoundingBox: ");
412 #endif
413  // process file data and transform it
414  if ( i == -1 ) {
415  res.status = KLFERR_NOEPSBBOX;
416  res.errorstr = QObject::tr("File '%1' does not contain line \"%%BoundingBox: ... \" !",
417  "KLFBackend").arg(fnRawEps);
418  return res;
419  }
420  int ax, ay, bx, by;
421  char temp[250];
422  const int k = i;
423  i += strlen("%%BoundingBox:");
424  int n = sscanf(epscontent_s.data()+i, "%d %d %d %d", &ax, &ay, &bx, &by);
425  if ( n != 4 ) {
426  res.status = KLFERR_BADEPSBBOX;
427  res.errorstr = QObject::tr("file %1: Line %%BoundingBox: can't read values!\n", "KLFBackend")
428  .arg(fnRawEps);
429  return res;
430  }
431  // grow bbox by settings.Xborderoffset points
432  // Don't forget: '%' in printf has special meaning (!) -> double percent signs '%'->'%%'
433  sprintf(temp, "%%%%BoundingBox: %d %d %d %d",
434  (int)(ax-settings.lborderoffset+0.5),
435  (int)(ay-settings.bborderoffset+0.5),
436  (int)(bx+settings.rborderoffset+0.5),
437  (int)(by+settings.tborderoffset+0.5));
438  QString chunk = QString::fromLocal8Bit(epscontent_s.data()+k);
439  QRegExp rx("^%%BoundingBox: [0-9]+ [0-9]+ [0-9]+ [0-9]+");
440  rx.rx_indexin(chunk);
441  int l = rx.matchedLength();
442  epscontent_s.replace(k, l, temp);
443 
444  // write content back to second file
445  QFile epsgoodfile(fnBBCorrEps);
446  r = epsgoodfile.open(dev_WRITEONLY);
447  if ( ! r ) {
448  res.status = KLFERR_EPSWRITEFAIL;
449  res.errorstr = QObject::tr("Can't write to file '%1'!\n", "KLFBackend")
450  .arg(fnBBCorrEps);
451  return res;
452  }
453  epsgoodfile.dev_write(epscontent_s);
454 
455  if ( ! settings.outlineFonts ) {
456  res.epsdata.ba_assign(epscontent_s);
457  }
458  // res.epsdata is now set.
459 
460  qDebug("%s: %s: eps bbox set.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
461 
462  } // end of block "make EPS"
463 
464  if (settings.outlineFonts) {
465  // run 'gs' to outline fonts
466  KLFBlockProcess proc;
467  QStringList args;
468  args << settings.gsexec << "-dNOCACHE" << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop"
469  << "-sDEVICE=pswrite" << "-sOutputFile="+dir_native_separators(fnOutlFontsEps)
470  << "-q" << "-dBATCH" << dir_native_separators(fnBBCorrEps);
471 
472  qDebug("%s: %s: about to gs (for outline fonts)...\n%s", KLF_FUNC_NAME, KLF_SHORT_TIME,
473  qPrintable(args.join(" ")));
474  bool r = proc.startProcess(args, execenv);
475  qDebug("%s: %s: gs returned (for outline fonts).", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
476 
477  if ( ! r ) {
478  res.status = KLFERR_NOGSPROG;
479  res.errorstr = QObject::tr("Unable to start gs!\n", "KLFBackend");
480  return res;
481  }
482  if ( !proc.processNormalExit() ) {
483  res.status = KLFERR_GSNONORMALEXIT;
484  res.errorstr = QObject::tr("gs died abnormally!\n", "KLFBackend");
485  return res;
486  }
487  if ( proc.processExitStatus() != 0) {
488  res.status = KLFERR_PROGERR_GS_OF;
489  res.errorstr = progErrorMsg("gs", proc.processExitStatus(), proc.readStderrString(),
490  proc.readStdoutString());
491  return res;
492  }
493  if (!QFile::exists(fnOutlFontsEps)) {
494  res.status = KLFERR_NOEPSFILE_OF;
495  res.errorstr = QObject::tr("EPS file (with outlined fonts) didn't appear after call to gs!\n",
496  "KLFBackend");
497  return res;
498  }
499 
500  // get and save outlined EPS to memory
501  QFile ofepsfile(fnOutlFontsEps);
502  r = ofepsfile.open(dev_READONLY);
503  if ( ! r ) {
504  res.status = KLFERR_EPSREADFAIL_OF;
505  res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend")
506  .arg(fnOutlFontsEps);
507  return res;
508  }
509  res.epsdata = ofepsfile.readAll();
510  ofepsfile.close();
511  // res.epsdata is now set to the outlined-fonts version
512  }
513 
514  { // run 'gs' to get png
515  KLFBlockProcess proc;
516  QStringList args;
517  args << settings.gsexec << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop"
518  << "-r"+QString::number(in.dpi) << "-dTextAlphaBits=4"
519  << "-dGraphicsAlphaBits=4";
520  if (qAlpha(in.bg_color) > 0) { // we're forcing a background color
521  args << "-sDEVICE=png16m";
522  } else {
523  args << "-sDEVICE=pngalpha";
524  }
525  args << "-sOutputFile="+dir_native_separators(fnPng) << "-q" << "-dBATCH"
526  << dir_native_separators(fnFinalEps);
527 
528  qDebug("%s: %s: about to gs... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
529  bool r = proc.startProcess(args, execenv);
530  qDebug("%s: %s: gs returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
531 
532  if ( ! r ) {
533  res.status = KLFERR_NOGSPROG;
534  res.errorstr = QObject::tr("Unable to start gs!\n", "KLFBackend");
535  return res;
536  }
537  if ( !proc.processNormalExit() ) {
538  res.status = KLFERR_GSNONORMALEXIT;
539  res.errorstr = QObject::tr("gs died abnormally!\n", "KLFBackend");
540  return res;
541  }
542  if ( proc.processExitStatus() != 0) {
543  res.status = KLFERR_PROGERR_GS;
544  res.errorstr = progErrorMsg("gs", proc.processExitStatus(), proc.readStderrString(),
545  proc.readStdoutString());
546  return res;
547  }
548  if (!QFile::exists(fnPng)) {
549  res.status = KLFERR_NOPNGFILE;
550  res.errorstr = QObject::tr("PNG file didn't appear after call to gs!\n", "KLFBackend");
551  return res;
552  }
553 
554  // get and save PNG to memory
555  QFile pngfile(fnPng);
556  r = pngfile.open(dev_READONLY);
557  if ( ! r ) {
558  res.status = KLFERR_PNGREADFAIL;
559  res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend")
560  .arg(fnPng);
561  return res;
562  }
563  res.pngdata_raw = pngfile.readAll();
564  pngfile.close();
565  // res.pngdata_raw is now set.
566  res.result.loadFromData(res.pngdata_raw, "PNG");
567 
568  // store some meta-information into result
569  res.result.img_settext("AppVersion", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
570  res.result.img_settext("Application",
571  QObject::tr("Created with KLatexFormula version %1", "KLFBackend::saveOutputToFile"));
572  res.result.img_settext("Software", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
573  res.result.img_settext("InputLatex", in.latex);
574  res.result.img_settext("InputMathMode", in.mathmode);
575  res.result.img_settext("InputPreamble", in.preamble);
576  res.result.img_settext("InputFgColor", QString("rgb(%1, %2, %3)").arg(qRed(in.fg_color))
577  .arg(qGreen(in.fg_color)).arg(qBlue(in.fg_color)));
578  res.result.img_settext("InputBgColor", QString("rgba(%1, %2, %3, %4)").arg(qRed(in.bg_color))
579  .arg(qGreen(in.bg_color)).arg(qBlue(in.bg_color))
580  .arg(qAlpha(in.bg_color)));
581  res.result.img_settext("InputDPI", QString::number(in.dpi));
582  res.result.img_settext("SettingsTBorderOffset", QString::number(settings.tborderoffset));
583  res.result.img_settext("SettingsRBorderOffset", QString::number(settings.rborderoffset));
584  res.result.img_settext("SettingsBBorderOffset", QString::number(settings.bborderoffset));
585  res.result.img_settext("SettingsLBorderOffset", QString::number(settings.lborderoffset));
586  res.result.img_settext("SettingsOutlineFonts", settings.outlineFonts?QString("true"):QString("false"));
587  }
588 
589  { // create "final" PNG data
590 #ifdef KLFBACKEND_QT4
591  QBuffer buf(&res.pngdata);
592 #else
593  QBuffer buf(res.pngdata);
594 #endif
595  buf.open(dev_WRITEONLY);
596  bool r = res.result.save(&buf, "PNG");
597  if (!r) {
598  qWarning("%s: Error: Can't save \"final\" PNG data.", KLF_FUNC_NAME);
599  res.pngdata.ba_assign(res.pngdata_raw);
600  }
601  }
602 
603  if (!settings.epstopdfexec.isEmpty()) {
604  // if we have epstopdf functionality, then we'll take advantage of it to generate pdf:
605  KLFBlockProcess proc;
606  QStringList args;
607  args << settings.epstopdfexec << dir_native_separators(fnFinalEps)
608  << ("--outfile="+dir_native_separators(fnPdf));
609 
610  qDebug("%s: %s: about to epstopdf... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
611  bool r = proc.startProcess(args, execenv);
612  qDebug("%s: %s: epstopdf returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
613 
614  if ( ! r ) {
615  res.status = KLFERR_NOEPSTOPDFPROG;
616  res.errorstr = QObject::tr("Unable to start epstopdf!\n", "KLFBackend");
617  return res;
618  }
619  if ( !proc.processNormalExit() ) {
620  res.status = KLFERR_EPSTOPDFNONORMALEXIT;
621  res.errorstr = QObject::tr("epstopdf died nastily!\n", "KLFBackend");
622  return res;
623  }
624  if ( proc.processExitStatus() != 0) {
625  res.status = KLFERR_PROGERR_EPSTOPDF;
626  res.errorstr = progErrorMsg("epstopdf", proc.processExitStatus(), proc.readStderrString(),
627  proc.readStdoutString());
628  return res;
629  }
630  if (!QFile::exists(fnPdf)) {
631  qDebug("%s: %s: pdf file '%s' didn't appear after epstopdf!", KLF_FUNC_NAME, KLF_SHORT_TIME,
632  qPrintable(fnPdf));
633  res.status = KLFERR_NOPDFFILE;
634  res.errorstr = QObject::tr("PDF file didn't appear after call to epstopdf!\n", "KLFBackend");
635  return res;
636  }
637 
638  // get and save PDF to memory
639  QFile pdffile(fnPdf);
640  r = pdffile.open(dev_READONLY);
641  if ( ! r ) {
642  res.status = KLFERR_PDFREADFAIL;
643  res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend").arg(fnPdf);
644  return res;
645  }
646  res.pdfdata = pdffile.readAll();
647 
648  }
649 
650  qDebug("%s: %s: end of function.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
651 
652  return res;
653 }
654 
655 
656 void KLFBackend::cleanup(QString tempfname)
657 {
658  const char *skipcleanup = getenv("KLFBACKEND_LEAVE_TEMP_FILES");
659  if (skipcleanup != NULL && (*skipcleanup == '1' || *skipcleanup == 't' || *skipcleanup == 'T' ||
660  *skipcleanup == 'y' || *skipcleanup == 'Y'))
661  return; // skip cleaning up temp files
662 
663  if (QFile::exists(tempfname+".tex")) QFile::remove(tempfname+".tex");
664  if (QFile::exists(tempfname+".dvi")) QFile::remove(tempfname+".dvi");
665  if (QFile::exists(tempfname+".aux")) QFile::remove(tempfname+".aux");
666  if (QFile::exists(tempfname+".log")) QFile::remove(tempfname+".log");
667  if (QFile::exists(tempfname+".toc")) QFile::remove(tempfname+".toc");
668  if (QFile::exists(tempfname+".eps")) QFile::remove(tempfname+".eps");
669  if (QFile::exists(tempfname+"-good.eps")) QFile::remove(tempfname+"-good.eps");
670  if (QFile::exists(tempfname+"-raw.eps")) QFile::remove(tempfname+"-raw.eps");
671  if (QFile::exists(tempfname+"-bbcorr.eps")) QFile::remove(tempfname+"-bbcorr.eps");
672  if (QFile::exists(tempfname+"-outlfonts.eps")) QFile::remove(tempfname+"-outlfonts.eps");
673  if (QFile::exists(tempfname+".png")) QFile::remove(tempfname+".png");
674  if (QFile::exists(tempfname+".pdf")) QFile::remove(tempfname+".pdf");
675 }
676 
677 // static private mutex object
678 QMutex KLFBackend::__mutex;
679 
680 KLF_EXPORT bool operator==(const KLFBackend::klfInput& a, const KLFBackend::klfInput& b)
681 {
682  return a.latex == b.latex &&
683  a.mathmode == b.mathmode &&
684  a.preamble == b.preamble &&
685  a.fg_color == b.fg_color &&
686  a.bg_color == b.bg_color &&
687  a.dpi == b.dpi;
688 }
689 
690 
691 bool KLFBackend::saveOutputToDevice(const klfOutput& klfoutput, QIODevice *device,
692  const QString& fmt, QString *errorStringPtr)
693 {
694  QString format = fmt.s_trimmed().s_toUpper();
695 
696  // now choose correct data source and write to fout
697  if (format == "EPS" || format == "PS") {
698  device->dev_write(klfoutput.epsdata);
699  } else if (format == "PNG") {
700  device->dev_write(klfoutput.pngdata);
701  } else if (format == "PDF") {
702  if (klfoutput.pdfdata.isEmpty()) {
703  QString error = QObject::tr("PDF format is not available!\n",
704  "KLFBackend::saveOutputToFile");
705  qWarning("%s", qPrintable(error));
706  if (errorStringPtr != NULL)
707  errorStringPtr->operator=(error);
708  return false;
709  }
710  device->dev_write(klfoutput.pdfdata);
711  } else {
712  bool res = klfoutput.result.save(device, format.s_toLatin1());
713  if ( ! res ) {
714  QString errstr = QObject::tr("Unable to save image in format `%1'!",
715  "KLFBackend::saveOutputToDevice").arg(format);
716  qWarning("%s", qPrintable(errstr));
717  if (errorStringPtr != NULL)
718  *errorStringPtr = errstr;
719  return false;
720  }
721  }
722 
723  return true;
724 }
725 
726 bool KLFBackend::saveOutputToFile(const klfOutput& klfoutput, const QString& fileName,
727  const QString& fmt, QString *errorStringPtr)
728 {
729  QString format = fmt;
730  // determine format first
731  if (format.isEmpty() && !fileName.isEmpty()) {
732  QFileInfo fi(fileName);
733  if ( ! fi.fi_suffix().isEmpty() )
734  format = fi.fi_suffix();
735  }
736  if (format.isEmpty())
737  format = QLatin1String("PNG");
738  format = format.s_trimmed().s_toUpper();
739  // got format. choose output now and prepare write
740  QFile fout;
741  if (fileName.isEmpty() || fileName == "-") {
742  if ( ! fout.f_open_fp(stdout) ) {
743  QString error = QObject::tr("Unable to open stderr for write! Error: %1\n",
744  "KLFBackend::saveOutputToFile").arg(fout.f_error());
745  qWarning("%s", qPrintable(error));
746  if (errorStringPtr != NULL)
747  *errorStringPtr = error;
748  return false;
749  }
750  } else {
751  fout.f_setFileName(fileName);
752  if ( ! fout.open(dev_WRITEONLY) ) {
753  QString error = QObject::tr("Unable to write to file `%1'! Error: %2\n",
754  "KLFBackend::saveOutputToFile")
755  .arg(fileName).arg(fout.f_error());
756  qWarning("%s", qPrintable(error));
757  if (errorStringPtr != NULL)
758  *errorStringPtr = error;
759  return false;
760  }
761  }
762 
763  return saveOutputToDevice(klfoutput, &fout, format, errorStringPtr);
764 }
765 
766 
767 bool KLFBackend::detectSettings(klfSettings *settings, const QString& extraPath)
768 {
770 
771  QStringList stdextrapaths;
772  int k, j;
773  for (k = 0; standard_extra_paths[k] != NULL; ++k) {
774  stdextrapaths.append(standard_extra_paths[k]);
775  }
776  QString extra_paths = stdextrapaths.join(QString("")+KLF_PATH_SEP);
777  if (!extraPath.isEmpty())
778  extra_paths += KLF_PATH_SEP + extraPath;
779 
780  // temp dir
781 #ifdef KLFBACKEND_QT4
783 #else
784 # if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_MACX)
785  settings->tempdir = "/tmp";
786 # elif defined(Q_OS_WIN32)
787  settings->tempdir = getenv("TEMP");
788 # else
789  settings->tempdir = QString();
790 # endif
791 #endif
792 
793  // sensible defaults
794  settings->lborderoffset = 1;
795  settings->tborderoffset = 1;
796  settings->rborderoffset = 1;
797  settings->bborderoffset = 1;
798 
799  // find executables
800  struct { QString * target_setting; QStringList prog_names; } progs_to_find[] = {
801  { & settings->latexexec, progLATEX },
802  { & settings->dvipsexec, progDVIPS },
803  { & settings->gsexec, progGS },
804  { & settings->epstopdfexec, progEPSTOPDF },
805  { NULL, QStringList() }
806  };
807  // replace @executable_path in extra_paths
808  klfDbg(klfFmtCC("Our base extra paths are: %s", qPrintable(extra_paths))) ;
809  QString ourextrapaths = extra_paths;
810  ourextrapaths.replace("@executable_path", qApp->applicationDirPath());
811  klfDbg(klfFmtCC("Our extra paths are: %s", qPrintable(ourextrapaths))) ;
812  // and actually search for those executables
813  for (k = 0; progs_to_find[k].target_setting != NULL; ++k) {
814  klfDbg("Looking for "+progs_to_find[k].prog_names.join(" or ")) ;
815  for (j = 0; j < (int)progs_to_find[k].prog_names.size(); ++j) {
816  klfDbg("Testing `"+progs_to_find[k].prog_names[j]+"'") ;
817  *progs_to_find[k].target_setting
818  = klfSearchPath(progs_to_find[k].prog_names[j], ourextrapaths);
819  if (!progs_to_find[k].target_setting->isEmpty()) {
820  klfDbg("Found! at `"+ *progs_to_find[k].target_setting+"'") ;
821  break; // found a program
822  }
823  }
824  }
825 
826  klf_detect_execenv(settings);
827 
828  bool result_failure =
829  settings->tempdir.isEmpty() || settings->latexexec.isEmpty() || settings->dvipsexec.isEmpty() ||
830  settings->gsexec.isEmpty(); // NOTE: settings->epstopdfexec.isEmpty() is NOT a failure
831 
832  return !result_failure;
833 }
834 
835 
852 KLF_EXPORT bool klf_detect_execenv(KLFBackend::klfSettings *settings)
853 {
854  // detect mgs.exe as ghostscript and setup its environment properly
855  QFileInfo gsfi(settings->gsexec);
856  if (gsfi.fileName() == "mgs.exe") {
857  QString mgsenv = QString("MIKTEX_GS_LIB=")
858  + dir_native_separators(gsfi.fi_absolutePath()+"/../../ghostscript/base")
859  + ";"
860  + dir_native_separators(gsfi.fi_absolutePath()+"/../../fonts");
861  __klf_append_replace_env_var(& settings->execenv, "MIKTEX_GS_LIB", mgsenv);
862  klfDbg("Adjusting environment for mgs.exe: `"+mgsenv+"'") ;
863  }
864 
865 #ifdef Q_WS_MAC
866  // make sure that epstopdf's path is in PATH because it wants to all gs
867  // (eg fink distributions)
868  if (!settings->epstopdfexec.isEmpty()) {
869  QFileInfo epstopdf_fi(settings->epstopdfexec);
870  QString execenvpath = QString("PATH=%1:$PATH").arg(epstopdf_fi.fi_absolutePath());
871  __klf_append_replace_env_var(& settings->execenv, "PATH", execenvpath);
872  }
873 #endif
874 
875  return true;
876 }
#define KLFERR_PNGREADFAIL
Error while opening .png file for reading.
Definition: klfbackend.h:87
Defines the KLFBlockProcess class.
#define KLFERR_PROGERR_LATEX
latex exited with a non-zero status
Definition: klfbackend.h:99
#define KLFERR_EPSREADFAIL
Error while opening .eps file for reading.
Definition: klfbackend.h:69
fromNativeSeparators(const QString &pathName)
bool KLF_EXPORT klf_detect_execenv(KLFBackend::klfSettings *settings)
detects any additional settings to environment variables
Definition: klfbackend.cpp:852
#define KLFERR_TEXWRITEFAIL
Error while opening .tex file for writing.
Definition: klfbackend.h:55
#define KLFERR_MISSINGMATHMODETHREEDOTS
The &quot;...&quot; is missing in math mode string.
Definition: klfbackend.h:53
save(const QString &fileName, const char *format=0, int quality=-1)
#define KLFERR_PROGERR_EPSTOPDF
epstopdf exited with non-zero status (if epstopdf is to be used)
Definition: klfbackend.h:107
find(char c, int from=0)
arg(const QString &a, int fieldWidth=0, const QChar &fillChar=QLatin1Char( ' ')
#define KLFERR_EPSREADFAIL_OF
Error while opening -outlfonts.eps after outlining fonts with gs.
Definition: klfbackend.h:79
#define klfDbg(streamableItems)
print debug stream items
#define KLFERR_PDFREADFAIL
Error while opening .pdf file for reading.
Definition: klfbackend.h:95
bool startProcess(QStringList cmd, QByteArray stdindata, QStringList env=QStringList())
replace(int pos, int len, const QByteArray &after)
#define KLFERR_EPSTOPDFNONORMALEXIT
epstopdf program did not exit properly (program killed) (see also KLFERR_PROGERR_EPSTOPDF) ...
Definition: klfbackend.h:91
#define KLFERR_NOERROR
No Error.
Definition: klfbackend.h:48
join(const QString &separator)
#define KLFERR_NOLATEXPROG
Error while launching the given latex program.
Definition: klfbackend.h:57
static bool saveOutputToDevice(const klfOutput &output, QIODevice *device, const QString &format=QString("PNG"), QString *errorString=NULL)
Saves the given output into the given device.
Definition: klfbackend.cpp:691
tr(const char *sourceText, const char *comment=0, int n=-1)
#define KLFERR_NOEPSBBOX
Error while searching file for %BoundingBox instruction in EPS.
Definition: klfbackend.h:71
replace(int position, int n, const QString &after)
#define KLFERR_NODVIFILE
No .dvi file appeared after runnig latex program.
Definition: klfbackend.h:61
number(long n, int base=10)
indexOf(const QByteArray &ba, int from=0)
fromLocal8Bit(const char *str, int size=-1)
tempPath()
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:130
KLF_EXPORT QString klfSearchPath(const QString &prog, const QString &extra_path="")
Smart executable searching in a given path list with wildcards.
Definition: klfdefs.cpp:1228
#define KLFERR_PROGERR_GS_OF
gs (while outlining fonts) exited with non-zero status
Definition: klfbackend.h:105
KLFBackend::getLatexFormula() result.
Definition: klfbackend.h:217
unsigned long fg_color
Definition: klfbackend.h:195
KLF_EXPORT QStringList klf_cur_environ()
The current process environment.
Specific input to KLFBackend::getLatexFormula()
Definition: klfbackend.h:179
#define KLFERR_NOEPSFILE
no .eps file appeared after running dvips program
Definition: klfbackend.h:67
unsigned long bg_color
Definition: klfbackend.h:201
#define KLFERR_GSNONORMALEXIT
gs program did not exit properly (program killed) (see also KLFERR_PROGERR_GS)
Definition: klfbackend.h:83
open(OpenMode mode)
#define KLFERR_NOEPSTOPDFPROG
Error while launching the given epstopdf program (if given)
Definition: klfbackend.h:89
#define KLF_DEBUG_TIME_BLOCK(msg)
Utility to time the execution of a block.
#define KLFERR_NOPDFFILE
No .pdf file appeared after running epstopdf program.
Definition: klfbackend.h:93
#define klfFmtCC
Definition: klfdefs.h:70
QString readStderrString()
#define KLF_FUNC_NAME
contains(const QString &str, Qt::CaseSensitivity cs=Qt::CaseSensitive)
setWorkingDirectory(const QString &dir)
#define KLFERR_PROGERR_DVIPS
dvips exited with a non-zero status
Definition: klfbackend.h:101
Definition of class KLFBackend.
#define KLFERR_DVIPSNONORMALEXIT
dvips program did not exit properly (program killed) (see also KLFERR_PROGERR_DVIPS) ...
Definition: klfbackend.h:65
bool processNormalExit() const
#define KLFERR_NOPNGFILE
No .png file appeared after running gs program.
Definition: klfbackend.h:85
#define KLFERR_NOEPSFILE_OF
No -outlfonts.eps file appeared after calling gs for outlining fonts.
Definition: klfbackend.h:77
int processExitStatus() const
#define KLFERR_EPSWRITEFAIL
Error while opening ...-good.eps file for writing.
Definition: klfbackend.h:75
QString readStdoutString()
#define KLFERR_MISSINGLATEXFORMULA
No LaTeX formula is specified (empty string)
Definition: klfbackend.h:51
#define KLFERR_NODVIPSPROG
Error while launching the given dvips program.
Definition: klfbackend.h:63
static bool saveOutputToFile(const klfOutput &output, const QString &fileName, const QString &format=QString(), QString *errorString=NULL)
Save the output to image file.
Definition: klfbackend.cpp:726
A QProcess subclass for code-blocking process execution.
#define KLFERR_NOGSPROG
Error while launching the given gs program.
Definition: klfbackend.h:81
fromLatin1(const char *str, int size=-1)
#define KLFERR_LATEXNONORMALEXIT
latex program did not exit properly (program killed) (see also KLFERR_PROGERR_LATEX) ...
Definition: klfbackend.h:59
operatorconst char *()
#define KLFERR_PROGERR_GS
gs exited with a non-zero status
Definition: klfbackend.h:103
static bool detectSettings(klfSettings *settings, const QString &extraPath=QString())
Detects the system settings and stores the guessed values in settings.
Definition: klfbackend.cpp:767
bool KLF_EXPORT operator==(const KLFBackend::klfInput &a, const KLFBackend::klfInput &b)
Definition: klfbackend.cpp:680
static klfOutput getLatexFormula(const klfInput &in, const klfSettings &settings)
The function that processes everything.
Definition: klfbackend.cpp:208
#define KLFERR_BADEPSBBOX
Error while parsing value for %BoundingBox instruction in EPS.
Definition: klfbackend.h:73
stripWhiteSpace()
int status
A code describing the status of the request.
Definition: klfbackend.h:227
#define KLF_PATH_SEP
The character used in the $PATH environment variable to separate different locations.
Definition: klfdefs.h:107

Generated by doxygen 1.8.5