[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 894 2014-07-28 19:10:42Z 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 #include "klfblockprocess.h"
37 #include "klfbackend.h"
38 
39 // write Qt 3/4 compatible code
40 #include "klfqt34common.h"
41 
42 
73 // some standard guess settings for system configurations
74 
75 #ifdef KLF_EXTRA_SEARCH_PATHS
76 # define EXTRA_PATHS_PRE KLF_EXTRA_SEARCH_PATHS ,
77 # define EXTRA_PATHS KLF_EXTRA_SEARCH_PATHS
78 #else
79 # define EXTRA_PATHS_PRE
80 # define EXTRA_PATHS
81 #endif
82 
83 
84 #if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
85 QStringList progLATEX = QStringList() << "latex.exe";
86 QStringList progDVIPS = QStringList() << "dvips.exe";
87 QStringList progGS = QStringList() << "gswin32c.exe" << "mgs.exe";
88 QStringList progEPSTOPDF = QStringList() << "epstopdf.exe";
89 static const char * standard_extra_paths[] = {
90  EXTRA_PATHS_PRE
91  "C:\\Program Files\\MiKTeX*\\miktex\\bin",
92  "C:\\Program Files\\gs\\gs*\\bin",
93  NULL
94 };
95 #elif defined(Q_WS_MAC)
96 QStringList progLATEX = QStringList() << "latex";
97 QStringList progDVIPS = QStringList() << "dvips";
98 QStringList progGS = QStringList() << "gs";
99 QStringList progEPSTOPDF = QStringList() << "epstopdf";
100 static const char * standard_extra_paths[] = {
101  EXTRA_PATHS_PRE
102  "/usr/texbin:/usr/local/bin:/sw/bin:/sw/usr/bin",
103  NULL
104 };
105 #else
106 QStringList progLATEX = QStringList() << "latex";
107 QStringList progDVIPS = QStringList() << "dvips";
108 QStringList progGS = QStringList() << "gs";
109 QStringList progEPSTOPDF = QStringList() << "epstopdf";
110 static const char * standard_extra_paths[] = {
111  EXTRA_PATHS_PRE
112  NULL
113 };
114 #endif
115 
116 
117 
118 // ---------------------------------
119 
120 KLFBackend::KLFBackend()
121 {
122 }
123 
124 
125 // Utility function
126 QString progErrorMsg(QString progname, int exitstatus, QString stderrstr, QString stdoutstr)
127 {
128  QString stdouthtml = stdoutstr;
129  QString stderrhtml = stderrstr;
130  stdouthtml.replace("&", "&amp;");
131  stdouthtml.replace("<", "&lt;");
132  stdouthtml.replace(">", "&gt;");
133  stderrhtml.replace("&", "&amp;");
134  stderrhtml.replace("<", "&lt;");
135  stderrhtml.replace(">", "&gt;");
136 
137  if (stderrstr.isEmpty() && stdoutstr.isEmpty())
138  return QObject::tr("<p><b>%1</b> reported an error (exit status %2). No Output was generated.</p>",
139  "KLFBackend")
140  .arg(progname).arg(exitstatus);
141  if (stderrstr.isEmpty())
142  return
143  QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stdout output:</p>\n"
144  "<pre>\n%3</pre>", "KLFBackend")
145  .arg(progname).arg(exitstatus).arg(stdouthtml);
146  if (stdoutstr.isEmpty())
147  return
148  QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
149  "<pre>\n%3</pre>", "KLFBackend")
150  .arg(progname).arg(exitstatus).arg(stderrhtml);
151 
152  return QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
153  "<pre>\n%3</pre><p>And here is full stdout output:</p><pre>\n%4</pre>", "KLFBackend")
154  .arg(progname).arg(exitstatus).arg(stderrhtml).arg(stdouthtml);
155 }
156 
157 
158 /* * Internal.
159  * \internal */
160 struct cleanup_caller {
161  QString tempfname;
162  cleanup_caller(QString fn) : tempfname(fn) { }
163  ~cleanup_caller() {
164  KLFBackend::cleanup(tempfname);
165  }
166 };
167 
168 QString __klf_expand_env_vars(const QString& envexpr)
169 {
170  QString s = envexpr;
171  QRegExp rx("\\$(?:(\\$|(?:[A-Za-z0-9_]+))|\\{([A-Za-z0-9_]+)\\})");
172  int i = 0;
173  while ( (i = rx.rx_indexin_i(s, i)) != -1 ) {
174  // match found, replace it
175  QString envvarname = rx.cap(1);
176  if (envvarname.isEmpty() || envvarname == QLatin1String("$")) {
177  // note: empty variable name expands to a literal '$'
178  s.replace(i, rx.matchedLength(), QLatin1String("$"));
179  i += 1;
180  continue;
181  }
182  const char *svalue = getenv(qPrintable(envvarname));
183  QString qsvalue = (svalue != NULL) ? QString::fromLocal8Bit(svalue) : QString();
184  s.replace(i, rx.matchedLength(), qsvalue);
185  i += qsvalue.length();
186  }
187  // replacements performed
188  return s;
189 }
190 
191 void __klf_append_replace_env_var(QStringList *list, const QString& var, const QString& line)
192 {
193  // search for declaration of var in list
194  int k;
195  for (k = 0; k < (int)list->size(); ++k) {
196  if (list->operator[](k).startsWith(var+QString("="))) {
197  list->operator[](k) = line;
198  return;
199  }
200  }
201  // declaration not found, just append
202  list->append(line);
203 }
204 
205 
206 
207 // utilities for dealing with bounding boxes in EPS file
208 // (backported from 3.3)
209 
210 // A Bounding Box
211 struct klfbbox {
212  double x1, x2, y1, y2;
213 };
214 static bool read_eps_bbox(const QByteArray& epsdata, klfbbox *bbox, KLFBackend::klfOutput * resError);
215 static void correct_eps_bbox(const QByteArray& epsdata, const klfbbox& bbox_corrected, const klfbbox& bbox_orig,
216  double vectorscale, QByteArray * epsdatacorrected);
217 
218 
219 
220 
221 
222 
224 {
225  // ALLOW ONLY ONE RUNNING getLatexFormula() AT A TIME
226  QMutexLocker mutexlocker(&__mutex);
227 
228  int k;
229 
230  qDebug("%s: %s: KLFBackend::getLatexFormula() called. latex=%s", KLF_FUNC_NAME, KLF_SHORT_TIME,
231  qPrintable(in.latex));
232 
233  // get full, expanded exec environment
234  QStringList execenv = klf_cur_environ();
235  for (k = 0; k < (int)settings.execenv.size(); ++k) {
236  int eqpos = settings.execenv[k].s_indexOf(QChar('='));
237  if (eqpos == -1) {
238  qWarning("%s: badly formed environment definition in `environ': %s", KLF_FUNC_NAME,
239  qPrintable(settings.execenv[k]));
240  continue;
241  }
242  QString varname = settings.execenv[k].mid(0, eqpos);
243  QString newenvdef = __klf_expand_env_vars(settings.execenv[k]);
244  __klf_append_replace_env_var(&execenv, varname, newenvdef);
245  }
246 
247  klfDbg("execution environment for sub-processes:\n"+execenv.join("\n")) ;
248 
249  klfOutput res;
250  res.status = KLFERR_NOERROR;
251  res.errorstr = QString();
252  res.result = QImage();
253  res.pngdata_raw = QByteArray();
254  res.pngdata = QByteArray();
255  res.epsdata = QByteArray();
256  res.pdfdata = QByteArray();
257  res.input = in;
258  res.settings = settings;
259 
260  // PROCEDURE:
261  // - generate LaTeX-file
262  // - latex --> get DVI file
263  // - dvips -E file.dvi it to get an EPS file
264  // - expand BBox by editing EPS file (if applicable)
265  // - outline fonts with gs (if applicable)
266  // - Run gs: gs -dNOPAUSE -dSAFER -dEPSCrop -r600 -dTextAlphaBits=4 -dGraphicsAlphaBits=4
267  // -sDEVICE=pngalpha|png16m -sOutputFile=xxxxxx.png -q -dBATCH xxxxxx.eps
268  // to eventually get PNG data
269  // - if epstopdfexec is not empty, run epstopdf and get PDF file.
270 
271  QString tempfname = settings.tempdir + "/klatexformulatmp" KLF_VERSION_STRING "-"
272  + QDateTime::currentDateTime().toString("hh-mm-ss")
273 #ifdef KLFBACKEND_QT4
274  + "-p"+ QString("%1").arg(QApplication::applicationPid(), 0, 26)
275 #else
276  + "-p" + QString("%1").arg(rand()%100000, 0, 26)
277 #endif
278  ;
279 
280  QString fnTex = tempfname + ".tex";
281  QString fnDvi = tempfname + ".dvi";
282  QString fnRawEps = tempfname + "-raw.eps";
283  QString fnBBCorrEps = tempfname + "-bbcorr.eps";
284  QString fnOutlFontsEps = tempfname + "-outlfonts.eps";
285  QString fnFinalEps = settings.outlineFonts ? fnOutlFontsEps : fnBBCorrEps;
286  QString fnPng = tempfname + ".png";
287  QString fnPdf = tempfname + ".pdf";
288 
289  // upon destruction (on stack) of this object, cleanup() will be
290  // automatically called as wanted
291  cleanup_caller cleanupcallerinstance(tempfname);
292 
293 #ifdef KLFBACKEND_QT4
294  QString latexsimplified = in.latex.s_trimmed();
295 #else
296  QString latexsimplified = in.latex.stripWhiteSpace();
297 #endif
298  if (latexsimplified.isEmpty()) {
299  res.errorstr = QObject::tr("You must specify a LaTeX formula!", "KLFBackend");
300  res.status = KLFERR_MISSINGLATEXFORMULA;
301  return res;
302  }
303 
304  QString latexin;
305  if (!in.bypassTemplate) {
306  if (in.mathmode.contains("...") == 0) {
307  res.status = KLFERR_MISSINGMATHMODETHREEDOTS;
308  res.errorstr = QObject::tr("The math mode string doesn't contain '...'!", "KLFBackend");
309  return res;
310  }
311  latexin = in.mathmode;
312  latexin.replace("...", in.latex);
313  }
314 
315  {
316  QFile file(fnTex);
317  bool r = file.open(dev_WRITEONLY);
318  if ( ! r ) {
319  res.status = KLFERR_TEXWRITEFAIL;
320  res.errorstr = QObject::tr("Can't open file for writing: '%1'!", "KLFBackend")
321  .arg(fnTex);
322  return res;
323  }
324  QTextStream stream(&file);
325  if (!in.bypassTemplate) {
326  stream << "\\documentclass{article}\n"
327  << "\\usepackage[dvips]{color}\n"
328  << in.preamble << "\n"
329  << "\\begin{document}\n"
330  << "\\thispagestyle{empty}\n"
331  << QString("\\definecolor{klffgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.fg_color)/255.0)
332  .arg(qGreen(in.fg_color)/255.0).arg(qBlue(in.fg_color)/255.0)
333  << QString("\\definecolor{klfbgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.bg_color)/255.0)
334  .arg(qGreen(in.bg_color)/255.0).arg(qBlue(in.bg_color)/255.0)
335  << ( (qAlpha(in.bg_color)>0) ? "\\pagecolor{klfbgcolor}\n" : "" )
336  << "{\\color{klffgcolor} " << latexin << " }\n"
337  << "\\end{document}\n";
338  } else {
339  stream << in.latex;
340  }
341  }
342 
343  { // execute latex
344 
345  if (settings.latexexec.isEmpty()) {
346  res.status = KLFERR_NOLATEXPROG;
347  res.errorstr = QObject::tr("No latex executable given!\n", "KLFBackend");
348  return res;
349  }
350 
351  KLFBlockProcess proc;
352  QStringList args;
353 
354  proc.setWorkingDirectory(settings.tempdir);
355 
356  args << settings.latexexec << dir_native_separators(fnTex);
357 
358  qDebug("%s: %s: about to exec latex...", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
359  bool r = proc.startProcess(args, execenv);
360  qDebug("%s: %s: latex returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
361 
362  if (!r) {
363  res.status = KLFERR_NOLATEXPROG;
364  res.errorstr = QObject::tr("Unable to start Latex program %1!", "KLFBackend")
365  .arg(settings.latexexec);
366  return res;
367  }
368  if (!proc.processNormalExit()) {
369  res.status = KLFERR_LATEXNONORMALEXIT;
370  res.errorstr = QObject::tr("Latex was killed!", "KLFBackend");
371  return res;
372  }
373  if (proc.processExitStatus() != 0) {
374  res.status = KLFERR_PROGERR_LATEX;
375  res.errorstr = progErrorMsg("LaTeX", proc.processExitStatus(), proc.readStderrString(),
376  proc.readStdoutString());
377  return res;
378  }
379 
380  if (!QFile::exists(fnDvi)) {
381  res.status = KLFERR_NODVIFILE;
382  res.errorstr = QObject::tr("DVI file didn't appear after having called Latex!", "KLFBackend");
383  return res;
384  }
385 
386  } // end of 'latex' block
387 
388  { // execute dvips -E
389 
390  if (settings.dvipsexec.isEmpty()) {
391  res.status = KLFERR_NODVIPSPROG;
392  res.errorstr = QObject::tr("No dvips executable given!\n", "KLFBackend");
393  return res;
394  }
395 
396  KLFBlockProcess proc;
397  QStringList args;
398  args << settings.dvipsexec << "-E" << dir_native_separators(fnDvi)
399  << "-o" << dir_native_separators(fnRawEps);
400 
401  qDebug("%s: %s: about to dvips... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
402  bool r = proc.startProcess(args, execenv);
403  qDebug("%s: %s: dvips returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
404 
405  if ( ! r ) {
406  res.status = KLFERR_NODVIPSPROG;
407  res.errorstr = QObject::tr("Unable to start dvips!\n", "KLFBackend");
408  return res;
409  }
410  if ( !proc.processNormalExit() ) {
411  res.status = KLFERR_DVIPSNONORMALEXIT;
412  res.errorstr = QObject::tr("Dvips was mercilessly killed!\n", "KLFBackend");
413  return res;
414  }
415  if ( proc.processExitStatus() != 0) {
416  res.status = KLFERR_PROGERR_DVIPS;
417  res.errorstr = progErrorMsg("dvips", proc.processExitStatus(), proc.readStderrString(),
418  proc.readStdoutString());
419  return res;
420  }
421  if (!QFile::exists(fnRawEps)) {
422  res.status = KLFERR_NOEPSFILE;
423  res.errorstr = QObject::tr("EPS file didn't appear after dvips call!\n", "KLFBackend");
424  return res;
425  }
426 
427 // { // DEAL WITH BOUNDING BOX
428 
429 // // add some space on bounding-box to avoid some too tight bounding box bugs
430 // // read eps file
431 // QFile epsfile(fnRawEps);
432 // r = epsfile.open(dev_READONLY);
433 // if ( ! r ) {
434 // res.status = KLFERR_EPSREADFAIL;
435 // res.errorstr = QObject::tr("Can't read file '%1'!\n", "KLFBackend").arg(fnRawEps);
436 // return res;
437 // }
438 // /** \todo Hi-Res bounding box adjustment. Shouldn't be too hard to do, but needs tests to see
439 // * how this works... [ Currently: only integer-valued BoundingBox: is adjusted. ] */
440 // QByteArray epscontent = epsfile.readAll();
441 // #ifdef KLFBACKEND_QT4
442 // QByteArray epscontent_s = epscontent;
443 // int i = epscontent_s.indexOf("%%BoundingBox: ");
444 // #else
445 // QCString epscontent_s(epscontent.data(), epscontent.size());
446 // int i = epscontent_s.find("%%BoundingBox: ");
447 // #endif
448 // // process file data and transform it
449 // if ( i == -1 ) {
450 // res.status = KLFERR_NOEPSBBOX;
451 // res.errorstr = QObject::tr("File '%1' does not contain line \"%%BoundingBox: ... \" !",
452 // "KLFBackend").arg(fnRawEps);
453 // return res;
454 // }
455 // int ax, ay, bx, by;
456 // char temp[250];
457 // const int k = i;
458 // i += strlen("%%BoundingBox:");
459 // int n = sscanf(epscontent_s.data()+i, "%d %d %d %d", &ax, &ay, &bx, &by);
460 // if ( n != 4 ) {
461 // res.status = KLFERR_BADEPSBBOX;
462 // res.errorstr = QObject::tr("file %1: Line %%BoundingBox: can't read values!\n", "KLFBackend")
463 // .arg(fnRawEps);
464 // return res;
465 // }
466 // // grow bbox by settings.Xborderoffset points
467 // // Don't forget: '%' in printf has special meaning (!) -> double percent signs '%'->'%%'
468 // sprintf(temp, "%%%%BoundingBox: %d %d %d %d",
469 // (int)(ax-settings.lborderoffset+0.5),
470 // (int)(ay-settings.bborderoffset+0.5),
471 // (int)(bx+settings.rborderoffset+0.5),
472 // (int)(by+settings.tborderoffset+0.5));
473 // QString chunk = QString::fromLocal8Bit(epscontent_s.data()+k);
474 // QRegExp rx("^%%BoundingBox: [0-9]+ [0-9]+ [0-9]+ [0-9]+");
475 // rx.rx_indexin(chunk);
476 // int l = rx.matchedLength();
477 // epscontent_s.replace(k, l, temp);
478 
479 // // write content back to second file
480 // QFile epsgoodfile(fnBBCorrEps);
481 // r = epsgoodfile.open(dev_WRITEONLY);
482 // if ( ! r ) {
483 // res.status = KLFERR_EPSWRITEFAIL;
484 // res.errorstr = QObject::tr("Can't write to file '%1'!\n", "KLFBackend")
485 // .arg(fnBBCorrEps);
486 // return res;
487 // }
488 // epsgoodfile.dev_write(epscontent_s);
489 
490 // if ( ! settings.outlineFonts ) {
491 // res.epsdata.ba_assign(epscontent_s);
492 // }
493 // // res.epsdata is now set.
494 
495 // }
496 
497  { // DEAL WITH BBOX: BACKPORT FROM 3.3
498 
499  // read eps file
500  QFile epsfile(fnRawEps);
501  r = epsfile.open(dev_READONLY);
502  if ( ! r ) {
503  res.status = KLFERR_EPSREADFAIL;
504  res.errorstr = QObject::tr("Can't read file '%1'!\n", "KLFBackend").arg(fnRawEps);
505  return res;
506  }
507  QByteArray rawepsdata = epsfile.readAll();
508 
509  klfbbox bbox, bbox_corrected;
510  bool ok = read_eps_bbox(rawepsdata, &bbox, &res);
511  if (!ok)
512  return res; // res was set by the function
513 
514  bbox.x1 -= settings.lborderoffset;
515  bbox.y1 -= settings.bborderoffset;
516  bbox.x2 += settings.rborderoffset;
517  bbox.y2 += settings.tborderoffset;
518 
519  int width_pt = bbox.x2 - bbox.x1;
520  int height_pt = bbox.y2 - bbox.y1;
521 
522  // now correct the bbox to (0,0,width,height)
523 
524  bbox_corrected.x1 = 0;
525  bbox_corrected.y1 = 0;
526  bbox_corrected.x2 = width_pt;
527  bbox_corrected.y2 = height_pt;
528 
529  // and generate corrected raw EPS
530  correct_eps_bbox(rawepsdata, bbox_corrected, bbox, 1.0,
531  &res.epsdata);
532 
533  QFile epsgoodfile(fnBBCorrEps);
534  r = epsgoodfile.open(dev_WRITEONLY);
535  if ( ! r ) {
536  res.status = KLFERR_EPSWRITEFAIL;
537  res.errorstr = QObject::tr("Can't write to file '%1'!\n", "KLFBackend")
538  .arg(fnBBCorrEps);
539  return res;
540  }
541  epsgoodfile.dev_write(res.epsdata);
542  }
543 
544  qDebug("%s: %s: eps bbox set.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
545 
546  } // end of block "make EPS"
547 
548  if (settings.outlineFonts) {
549  // run 'gs' to outline fonts
550  KLFBlockProcess proc;
551 
552  // In 3.2 we don't query gs version so we have no idea. So just let the user define an environment
553  // variable in case. KLFBACKEND_GS_PS_DEVICE="pswrite" or "epswrite" or "ps2write" (note: with epswrite
554  // you can't expand the bbox)
555 
556  QStringList try_ps_devices;
557  QStringList try_gs_options;
558  const char *env_gs_device = getenv("KLFBACKEND_GS_PS_DEVICE");
559  if (env_gs_device != NULL) {
560  // Use device given by environment variable
561  try_ps_devices << QString::fromLatin1(env_gs_device);
562  try_gs_options << "-dNOCACHE -dNoOutputFonts";
563  } else {
564  // try:
565  //
566  // 1. "Traditional method" (gs <= 9.07): pswrite with -dNOCACHE
567  //
568  // 2. Ghostscript removed the pswrite device after that; The ghostscript
569  // developers were very responsive and helpful to my feedback, and thanks to
570  // their prompt reaction, starting from 9.15, we can use the ps2write device
571  // with the option "-dNoOutputFonts".
572 
573  try_ps_devices << QLatin1String("pswrite") << QLatin1String("ps2write");
574  try_gs_options << "-dNOCACHE" << "-dNoOutputFonts";
575  }
576 
577  bool r = false;
578  int try_ps_dev_i = 0;
579  for (try_ps_dev_i = 0; try_ps_dev_i < try_ps_devices.size(); try_ps_dev_i++) {
580  QString psdev = try_ps_devices[try_ps_dev_i];
581  qDebug("trying with gs device %s ...", qPrintable(psdev));
582 
583  if (settings.gsexec.isEmpty()) {
584  res.status = KLFERR_NOGSPROG;
585  res.errorstr = QObject::tr("No gs executable given!\n", "KLFBackend");
586  return res;
587  }
588 
589  QStringList args;
590  args << settings.gsexec << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop" << try_gs_options[try_ps_dev_i]
591  << QString("-sDEVICE=%1").arg(psdev)
592  << "-sOutputFile="+dir_native_separators(fnOutlFontsEps)
593  << "-q" << "-dBATCH" << dir_native_separators(fnBBCorrEps);
594 
595  qDebug("%s: %s: about to gs (for outline fonts)...\n%s", KLF_FUNC_NAME, KLF_SHORT_TIME,
596  qPrintable(args.join(" ")));
597  r = proc.startProcess(args, execenv);
598  qDebug("%s: %s: gs returned (for outline fonts).", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
599 
600  if (r && proc.processNormalExit() && proc.processExitStatus() == 0) {
601  // successful run
602  break;
603  }
604  }
605 
606  if ( ! r ) {
607  res.status = KLFERR_NOGSPROG;
608  res.errorstr = QObject::tr("Unable to start gs!\n", "KLFBackend");
609  return res;
610  }
611  if ( !proc.processNormalExit() ) {
612  res.status = KLFERR_GSNONORMALEXIT;
613  res.errorstr = QObject::tr("gs died abnormally!\n", "KLFBackend");
614  return res;
615  }
616  if ( proc.processExitStatus() != 0) {
617  res.status = KLFERR_PROGERR_GS_OF;
618  res.errorstr = progErrorMsg("gs", proc.processExitStatus(), proc.readStderrString(),
619  proc.readStdoutString());
620  return res;
621  }
622  if (!QFile::exists(fnOutlFontsEps)) {
623  res.status = KLFERR_NOEPSFILE_OF;
624  res.errorstr = QObject::tr("EPS file (with outlined fonts) didn't appear after call to gs!\n",
625  "KLFBackend");
626  return res;
627  }
628 
629  // get and save outlined EPS to memory
630  QFile ofepsfile(fnOutlFontsEps);
631  r = ofepsfile.open(dev_READONLY);
632  if ( ! r ) {
633  res.status = KLFERR_EPSREADFAIL_OF;
634  res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend")
635  .arg(fnOutlFontsEps);
636  return res;
637  }
638  res.epsdata = ofepsfile.readAll();
639  ofepsfile.close();
640  // res.epsdata is now set to the outlined-fonts version
641  }
642 
643  if (settings.gsexec.isEmpty()) {
644  res.status = KLFERR_NOGSPROG;
645  res.errorstr = QObject::tr("No gs executable given!\n", "KLFBackend");
646  return res;
647  }
648 
649  { // run 'gs' to get png
650  KLFBlockProcess proc;
651  QStringList args;
652  args << settings.gsexec << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop"
653  << "-r"+QString::number(in.dpi) << "-dTextAlphaBits=4"
654  << "-dGraphicsAlphaBits=4";
655  if (qAlpha(in.bg_color) > 0) { // we're forcing a background color
656  args << "-sDEVICE=png16m";
657  } else {
658  args << "-sDEVICE=pngalpha";
659  }
660  args << "-sOutputFile="+dir_native_separators(fnPng) << "-q" << "-dBATCH"
661  << dir_native_separators(fnFinalEps);
662 
663  qDebug("%s: %s: about to gs... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
664  bool r = proc.startProcess(args, execenv);
665  qDebug("%s: %s: gs returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
666 
667  if ( ! r ) {
668  res.status = KLFERR_NOGSPROG;
669  res.errorstr = QObject::tr("Unable to start gs!\n", "KLFBackend");
670  return res;
671  }
672  if ( !proc.processNormalExit() ) {
673  res.status = KLFERR_GSNONORMALEXIT;
674  res.errorstr = QObject::tr("gs died abnormally!\n", "KLFBackend");
675  return res;
676  }
677  if ( proc.processExitStatus() != 0) {
678  res.status = KLFERR_PROGERR_GS;
679  res.errorstr = progErrorMsg("gs", proc.processExitStatus(), proc.readStderrString(),
680  proc.readStdoutString());
681  return res;
682  }
683  if (!QFile::exists(fnPng)) {
684  res.status = KLFERR_NOPNGFILE;
685  res.errorstr = QObject::tr("PNG file didn't appear after call to gs!\n", "KLFBackend");
686  return res;
687  }
688 
689  // get and save PNG to memory
690  QFile pngfile(fnPng);
691  r = pngfile.open(dev_READONLY);
692  if ( ! r ) {
693  res.status = KLFERR_PNGREADFAIL;
694  res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend")
695  .arg(fnPng);
696  return res;
697  }
698  res.pngdata_raw = pngfile.readAll();
699  pngfile.close();
700  // res.pngdata_raw is now set.
701  res.result.loadFromData(res.pngdata_raw, "PNG");
702 
703  // store some meta-information into result
704  res.result.img_settext("AppVersion", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
705  res.result.img_settext("Application",
706  QObject::tr("Created with KLatexFormula version %1", "KLFBackend::saveOutputToFile"));
707  res.result.img_settext("Software", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
708  res.result.img_settext("InputLatex", in.latex);
709  res.result.img_settext("InputMathMode", in.mathmode);
710  res.result.img_settext("InputPreamble", in.preamble);
711  res.result.img_settext("InputFgColor", QString("rgb(%1, %2, %3)").arg(qRed(in.fg_color))
712  .arg(qGreen(in.fg_color)).arg(qBlue(in.fg_color)));
713  res.result.img_settext("InputBgColor", QString("rgba(%1, %2, %3, %4)").arg(qRed(in.bg_color))
714  .arg(qGreen(in.bg_color)).arg(qBlue(in.bg_color))
715  .arg(qAlpha(in.bg_color)));
716  res.result.img_settext("InputDPI", QString::number(in.dpi));
717  res.result.img_settext("SettingsTBorderOffset", QString::number(settings.tborderoffset));
718  res.result.img_settext("SettingsRBorderOffset", QString::number(settings.rborderoffset));
719  res.result.img_settext("SettingsBBorderOffset", QString::number(settings.bborderoffset));
720  res.result.img_settext("SettingsLBorderOffset", QString::number(settings.lborderoffset));
721  res.result.img_settext("SettingsOutlineFonts", settings.outlineFonts?QString("true"):QString("false"));
722  }
723 
724  { // create "final" PNG data
725 #ifdef KLFBACKEND_QT4
726  QBuffer buf(&res.pngdata);
727 #else
728  QBuffer buf(res.pngdata);
729 #endif
730  buf.open(dev_WRITEONLY);
731  bool r = res.result.save(&buf, "PNG");
732  if (!r) {
733  qWarning("%s: Error: Can't save \"final\" PNG data.", KLF_FUNC_NAME);
734  res.pngdata.ba_assign(res.pngdata_raw);
735  }
736  }
737 
738  if (!settings.epstopdfexec.isEmpty()) {
739  // if we have epstopdf functionality, then we'll take advantage of it to generate pdf:
740  KLFBlockProcess proc;
741  QStringList args;
742  args << settings.epstopdfexec << dir_native_separators(fnFinalEps)
743  << ("--outfile="+dir_native_separators(fnPdf));
744 
745  qDebug("%s: %s: about to epstopdf... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
746  bool r = proc.startProcess(args, execenv);
747  qDebug("%s: %s: epstopdf returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
748 
749  if ( ! r ) {
750  res.status = KLFERR_NOEPSTOPDFPROG;
751  res.errorstr = QObject::tr("Unable to start epstopdf!\n", "KLFBackend");
752  return res;
753  }
754  if ( !proc.processNormalExit() ) {
755  res.status = KLFERR_EPSTOPDFNONORMALEXIT;
756  res.errorstr = QObject::tr("epstopdf died nastily!\n", "KLFBackend");
757  return res;
758  }
759  if ( proc.processExitStatus() != 0) {
760  res.status = KLFERR_PROGERR_EPSTOPDF;
761  res.errorstr = progErrorMsg("epstopdf", proc.processExitStatus(), proc.readStderrString(),
762  proc.readStdoutString());
763  return res;
764  }
765  if (!QFile::exists(fnPdf)) {
766  qDebug("%s: %s: pdf file '%s' didn't appear after epstopdf!", KLF_FUNC_NAME, KLF_SHORT_TIME,
767  qPrintable(fnPdf));
768  res.status = KLFERR_NOPDFFILE;
769  res.errorstr = QObject::tr("PDF file didn't appear after call to epstopdf!\n", "KLFBackend");
770  return res;
771  }
772 
773  // get and save PDF to memory
774  QFile pdffile(fnPdf);
775  r = pdffile.open(dev_READONLY);
776  if ( ! r ) {
777  res.status = KLFERR_PDFREADFAIL;
778  res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend").arg(fnPdf);
779  return res;
780  }
781  res.pdfdata = pdffile.readAll();
782 
783  }
784 
785  qDebug("%s: %s: end of function.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
786 
787  return res;
788 }
789 
790 
791 
792 static bool s_starts_with(const char * x, int len_x, const char *test, int len_test)
793 {
794  if (len_x < len_test)
795  return false;
796  return !strncmp(x, test, len_test);
797 }
798 
799 #define D_RX "([0-9eE.-]+)"
800 
801 static bool parse_bbox_values(const QString& str, klfbbox *bbox)
802 {
803  // parse bbox values
804  QRegExp rx_bbvalues("" D_RX "\\s+" D_RX "\\s+" D_RX "\\s+" D_RX "");
805  int i = rx_bbvalues.rx_indexin(str);
806  if (i < 0) {
807  return false;
808  }
809  bbox->x1 = rx_bbvalues.cap(1).toDouble();
810  bbox->y1 = rx_bbvalues.cap(2).toDouble();
811  bbox->x2 = rx_bbvalues.cap(3).toDouble();
812  bbox->y2 = rx_bbvalues.cap(4).toDouble();
813  return true;
814 }
815 
816 static bool read_eps_bbox(const QByteArray& epsdata, klfbbox *bbox, KLFBackend::klfOutput * resError)
817 {
818  static const char * hibboxtag = "%%HiResBoundingBox:";
819  static const char * bboxtag = "%%BoundingBox:";
820  static const int hibboxtaglen = strlen(hibboxtag);
821  static const int bboxtaglen = strlen(bboxtag);
822 
823  // Read dvips' bounding box.
824  QBuffer buf;
825  buf_setdata(buf, epsdata);
826  bool r = buf.open(dev_READONLY);
827  if (!r) {
828  qWarning("What's going on!!?! can't open buffer for reading? Will Fail!!!") ;
829  }
830 
831  QString nobboxerrstr =
832  QObject::tr("DVIPS did not provide parsable %%BoundingBox: in its output!", "KLFBackend");
833 
834  char linebuffer[512];
835  int n;
836  bool gotepsbbox = false;
837  int still_look_for_hiresbbox_lines = 5;
838  while ((n = buf.readLine(linebuffer, sizeof(linebuffer)-1)) > 0) {
839  if (gotepsbbox && still_look_for_hiresbbox_lines-- < 0) {
840  // if we already got the %BoundingBox, and we've been looking at more than a certian number of lines
841  // after that, abort because usually %BoundingBox and %HiResBoundingBox are together...
842  klfDbg("stopped looking for hires-bbox.") ;
843  break;
844  }
845  if (s_starts_with(linebuffer, n-1, hibboxtag, hibboxtaglen)) {
846  // got hi-res bounding-box
847  bool ok = parse_bbox_values(QString::fromLatin1(linebuffer+hibboxtaglen), bbox);
848  if (!ok) {
849  resError->status = KLFERR_BADEPSBBOX;
850  resError->errorstr = nobboxerrstr;
851  return false;
852  }
853  klfDbg("got hires-bbox.") ;
854  // all ok, got hi-res bbox
855  return true;
856  }
857  if (s_starts_with(linebuffer, n-1, bboxtag, bboxtaglen)) {
858  // got bounding-box.
859  bool ok = parse_bbox_values(QString::fromLatin1(linebuffer+bboxtaglen), bbox);
860  if (!ok) {
861  continue;
862  }
863  // stand by, continue in case we have a hi-res bbox.
864  gotepsbbox = true;
865  klfDbg("got normal bbox.") ;
866  continue;
867  }
868  }
869 
870  // didn't get a hi-res bbox. see if we still got a regular %BoundingBox: and return that.
871  if (gotepsbbox) {
872  // bbox pointer is already set
873  return true;
874  }
875 
876  resError->status = KLFERR_BADEPSBBOX;
877  resError->errorstr = nobboxerrstr;
878  return false;
879 }
880 
881 // static int find_ba_in_ba(const QByteArray& haystack, const QByteArray& needle)
882 // {
883 // #ifdef KLFBACKEND_QT4
884 // return haystack.indexOf(needle);
885 // #else
886 // int k, j;
887 // for (k = 0; k < haystack.length()-needle.length(); ++k) {
888 // // locally compare haystack and needle
889 // for (j = 0; j < needle.length(); ++j) {
890 // if (haystack[k+j] != needle[j])
891 // break; // nope they're not the same
892 // }
893 // if (j == needle.length())
894 // // found the needle
895 // return k;
896 // }
897 // return -1;
898 // #endif
899 // }
900 
901 static void correct_eps_bbox(const QByteArray& rawepsdata, const klfbbox& bbox_corrected,
902  const klfbbox& bbox_orig, double vectorscale,
903  QByteArray * epsdatacorrected)
904 {
905  static const char * bboxdecl = "%%BoundingBox:";
906  static int bboxdecl_len = strlen(bboxdecl);
907 
908  double offx = bbox_corrected.x1 - bbox_orig.x1;
909  double offy = bbox_corrected.y1 - bbox_orig.y1;
910 
911  // in raw EPS data, find '%%BoundingBox:' and length of the full BoundingBox instruction
912  int i, len;
913  char nl[] = "\0\0\0";
914 #ifdef KLFBACKEND_QT4
915  i = rawepsdata.indexOf(bboxdecl);
916 #else
917  QCString rawepsdata_s(rawepsdata.data(), rawepsdata.size());
918  i = rawepsdata_s.find(bboxdecl);
919 #endif
920  if (i < 0) {
921  i = 0;
922  len = 0;
923  } else {
924  int j = i+bboxdecl_len;
925  while (j < (int)rawepsdata.size() && rawepsdata[j] != '\r' && rawepsdata[j] != '\n')
926  ++j;
927  len = j-i;
928  // also determine what the newline is (\n, \r, \r\n?)
929  if (rawepsdata[j] == '\r' && j < (int)rawepsdata.size()-1 && rawepsdata[j+1] == '\n') {
930  nl[0] = '\r', nl[1] = '\n';
931  } else {
932  nl[0] = rawepsdata[j];
933  }
934  }
935 
936  double dwi = bbox_corrected.x2 * vectorscale;
937  double dhi = bbox_corrected.y2 * vectorscale;
938  int wi = (int)(dwi + 0.99999) ;
939  int hi = (int)(dhi + 0.99999) ;
940  char buffer[1024];
941  int buffer_len;
942  // recall that '%%' in printf is replaced by a single '%'...
943  snprintf(buffer, sizeof(buffer)-1,
944  "%%%%BoundingBox: 0 0 %d %d%s"
945  "%%%%HiResBoundingBox: 0 0 %.6g %.6g%s",
946  wi, hi, nl,
947  dwi, dhi, nl);
948  buffer_len = strlen(buffer);
949 
950  /*
951  char backgroundfillps[1024] = "";
952  if (qAlpha(bgcolor) > 0) {
953  sprintf(backgroundfillps,
954  // draw the background color, if any
955  "newpath "
956  "-2 -2 moveto "
957  "%s -2 lineto "
958  "%s %s lineto "
959  "-2 %s lineto "
960  "closepath "
961  "gsave "
962  "%s %s %s setrgbcolor "
963  "fill "
964  "grestore %s",
965  klfFmtDoubleCC(dwi+1, 'g', 6),
966  klfFmtDoubleCC(dwi+1, 'g', 6), klfFmtDoubleCC(dhi+1, 'g', 6),
967  klfFmtDoubleCC(dhi+1, 'g', 6),
968  // and the color, in RGB components:
969  klfFmtDoubleCC(qRed(bgcolor)/255.0, 'f', 6),
970  klfFmtDoubleCC(qGreen(bgcolor)/255.0, 'f', 6),
971  klfFmtDoubleCC(qBlue(bgcolor)/255.0, 'f', 6),
972  nl
973  );
974  }
975  */
976 
977  char buffer2[1024];
978  int buffer2_len;
979  snprintf(buffer2, sizeof(buffer2)-1,
980  "%s"
981  "%%%%Page 1 1%s"
982  "%%%%PageBoundingBox 0 0 %d %d%s"
983  "<< /PageSize [%d %d] >> setpagedevice%s"
984  //"%s"
985  "%f %f scale%s"
986  "%f %f translate%s"
987  ,
988  nl,
989  nl,
990  wi, hi, nl,
991  wi, hi, nl,
992  //backgroundfillps,
993  vectorscale, vectorscale, nl,
994  offx, offy, nl);
995  buffer2_len = strlen(buffer2);
996 
997  // char buffer2[128];
998  // snprintf(buffer2, 127, "%sgrestore%s", nl, nl);
999 
1000  //klfDbg("buffer is `"<<buffer<<"', length="<<buffer_len) ;
1001  //klfDbg("rawepsdata has length="<<rawepsdata.size()) ;
1002 
1003  // and modify the raw EPS data, to replace "%%BoundingBox:" instruction by our stuff...
1004 #ifdef KLFBACKEND_QT4
1005  QByteArray neweps;
1006  neweps = rawepsdata;
1007 #else
1008  QCString neweps(rawepsdata.data(), rawepsdata.size()); // makes deep copy
1009 #endif
1010  neweps.replace(i, len, buffer);
1011 
1012  const char * endsetupstr = "%%EndSetup";
1013  int i2 = neweps.s_indexOf(endsetupstr);
1014  if (i2 < 0)
1015  i2 = i + buffer_len; // add our info after modified %%BoundingBox'es instructions if %%EndSetup not found
1016  else
1017  i2 += strlen(endsetupstr);
1018 
1019  neweps.replace(i2, 0, buffer2);
1020 
1021  qDebug("neweps has now length=%d",neweps.size());
1022  qDebug("New eps bbox is [0 0 %.6g %.6g] with translate [%.6g %.6g] and scale %.6g.",
1023  dwi, dhi, offx, offy, vectorscale);
1024 
1025  epsdatacorrected->ba_assign(neweps);
1026 }
1027 
1028 
1029 
1030 
1031 
1032 
1033 
1034 
1035 
1036 
1037 
1038 void KLFBackend::cleanup(QString tempfname)
1039 {
1040  const char *skipcleanup = getenv("KLFBACKEND_LEAVE_TEMP_FILES");
1041  if (skipcleanup != NULL && (*skipcleanup == '1' || *skipcleanup == 't' || *skipcleanup == 'T' ||
1042  *skipcleanup == 'y' || *skipcleanup == 'Y'))
1043  return; // skip cleaning up temp files
1044 
1045  if (QFile::exists(tempfname+".tex")) QFile::remove(tempfname+".tex");
1046  if (QFile::exists(tempfname+".dvi")) QFile::remove(tempfname+".dvi");
1047  if (QFile::exists(tempfname+".aux")) QFile::remove(tempfname+".aux");
1048  if (QFile::exists(tempfname+".log")) QFile::remove(tempfname+".log");
1049  if (QFile::exists(tempfname+".toc")) QFile::remove(tempfname+".toc");
1050  if (QFile::exists(tempfname+".eps")) QFile::remove(tempfname+".eps");
1051  if (QFile::exists(tempfname+"-good.eps")) QFile::remove(tempfname+"-good.eps");
1052  if (QFile::exists(tempfname+"-raw.eps")) QFile::remove(tempfname+"-raw.eps");
1053  if (QFile::exists(tempfname+"-bbcorr.eps")) QFile::remove(tempfname+"-bbcorr.eps");
1054  if (QFile::exists(tempfname+"-outlfonts.eps")) QFile::remove(tempfname+"-outlfonts.eps");
1055  if (QFile::exists(tempfname+".png")) QFile::remove(tempfname+".png");
1056  if (QFile::exists(tempfname+".pdf")) QFile::remove(tempfname+".pdf");
1057 }
1058 
1059 
1060 
1061 
1062 // static private mutex object
1063 QMutex KLFBackend::__mutex;
1064 
1065 
1066 
1067 KLF_EXPORT bool operator==(const KLFBackend::klfInput& a, const KLFBackend::klfInput& b)
1068 {
1069  return a.latex == b.latex &&
1070  a.mathmode == b.mathmode &&
1071  a.preamble == b.preamble &&
1072  a.fg_color == b.fg_color &&
1073  a.bg_color == b.bg_color &&
1074  a.dpi == b.dpi;
1075 }
1076 
1077 
1078 bool KLFBackend::saveOutputToDevice(const klfOutput& klfoutput, QIODevice *device,
1079  const QString& fmt, QString *errorStringPtr)
1080 {
1081  QString format = fmt.s_trimmed().s_toUpper();
1082 
1083  // now choose correct data source and write to fout
1084  if (format == "EPS" || format == "PS") {
1085  device->dev_write(klfoutput.epsdata);
1086  } else if (format == "PNG") {
1087  device->dev_write(klfoutput.pngdata);
1088  } else if (format == "PDF") {
1089  if (klfoutput.pdfdata.isEmpty()) {
1090  QString error = QObject::tr("PDF format is not available!\n",
1091  "KLFBackend::saveOutputToFile");
1092  qWarning("%s", qPrintable(error));
1093  if (errorStringPtr != NULL)
1094  errorStringPtr->operator=(error);
1095  return false;
1096  }
1097  device->dev_write(klfoutput.pdfdata);
1098  } else {
1099  bool res = klfoutput.result.save(device, format.s_toLatin1());
1100  if ( ! res ) {
1101  QString errstr = QObject::tr("Unable to save image in format `%1'!",
1102  "KLFBackend::saveOutputToDevice").arg(format);
1103  qWarning("%s", qPrintable(errstr));
1104  if (errorStringPtr != NULL)
1105  *errorStringPtr = errstr;
1106  return false;
1107  }
1108  }
1109 
1110  return true;
1111 }
1112 
1113 bool KLFBackend::saveOutputToFile(const klfOutput& klfoutput, const QString& fileName,
1114  const QString& fmt, QString *errorStringPtr)
1115 {
1116  QString format = fmt;
1117  // determine format first
1118  if (format.isEmpty() && !fileName.isEmpty()) {
1119  QFileInfo fi(fileName);
1120  if ( ! fi.fi_suffix().isEmpty() )
1121  format = fi.fi_suffix();
1122  }
1123  if (format.isEmpty())
1124  format = QLatin1String("PNG");
1125  format = format.s_trimmed().s_toUpper();
1126  // got format. choose output now and prepare write
1127  QFile fout;
1128  if (fileName.isEmpty() || fileName == "-") {
1129  if ( ! fout.f_open_fp(stdout) ) {
1130  QString error = QObject::tr("Unable to open stderr for write! Error: %1\n",
1131  "KLFBackend::saveOutputToFile").arg(fout.f_error());
1132  qWarning("%s", qPrintable(error));
1133  if (errorStringPtr != NULL)
1134  *errorStringPtr = error;
1135  return false;
1136  }
1137  } else {
1138  fout.f_setFileName(fileName);
1139  if ( ! fout.open(dev_WRITEONLY) ) {
1140  QString error = QObject::tr("Unable to write to file `%1'! Error: %2\n",
1141  "KLFBackend::saveOutputToFile")
1142  .arg(fileName).arg(fout.f_error());
1143  qWarning("%s", qPrintable(error));
1144  if (errorStringPtr != NULL)
1145  *errorStringPtr = error;
1146  return false;
1147  }
1148  }
1149 
1150  return saveOutputToDevice(klfoutput, &fout, format, errorStringPtr);
1151 }
1152 
1153 
1154 bool KLFBackend::detectSettings(klfSettings *settings, const QString& extraPath)
1155 {
1157 
1158  QStringList stdextrapaths;
1159  int k, j;
1160  for (k = 0; standard_extra_paths[k] != NULL; ++k) {
1161  stdextrapaths.append(standard_extra_paths[k]);
1162  }
1163  QString extra_paths = stdextrapaths.join(QString("")+KLF_PATH_SEP);
1164  if (!extraPath.isEmpty())
1165  extra_paths += KLF_PATH_SEP + extraPath;
1166 
1167  // temp dir
1168 #ifdef KLFBACKEND_QT4
1170 #else
1171 # if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_MACX)
1172  settings->tempdir = "/tmp";
1173 # elif defined(Q_OS_WIN32)
1174  settings->tempdir = getenv("TEMP");
1175 # else
1176  settings->tempdir = QString();
1177 # endif
1178 #endif
1179 
1180  // sensible defaults
1181  settings->lborderoffset = 1;
1182  settings->tborderoffset = 1;
1183  settings->rborderoffset = 1;
1184  settings->bborderoffset = 1;
1185 
1186  // find executables
1187  struct { QString * target_setting; QStringList prog_names; } progs_to_find[] = {
1188  { & settings->latexexec, progLATEX },
1189  { & settings->dvipsexec, progDVIPS },
1190  { & settings->gsexec, progGS },
1191  { & settings->epstopdfexec, progEPSTOPDF },
1192  { NULL, QStringList() }
1193  };
1194  // replace @executable_path in extra_paths
1195  klfDbg(klfFmtCC("Our base extra paths are: %s", qPrintable(extra_paths))) ;
1196  QString ourextrapaths = extra_paths;
1197  ourextrapaths.replace("@executable_path", qApp->applicationDirPath());
1198  klfDbg(klfFmtCC("Our extra paths are: %s", qPrintable(ourextrapaths))) ;
1199  // and actually search for those executables
1200  for (k = 0; progs_to_find[k].target_setting != NULL; ++k) {
1201  klfDbg("Looking for "+progs_to_find[k].prog_names.join(" or ")) ;
1202  for (j = 0; j < (int)progs_to_find[k].prog_names.size(); ++j) {
1203  klfDbg("Testing `"+progs_to_find[k].prog_names[j]+"'") ;
1204  *progs_to_find[k].target_setting
1205  = klfSearchPath(progs_to_find[k].prog_names[j], ourextrapaths);
1206  if (!progs_to_find[k].target_setting->isEmpty()) {
1207  klfDbg("Found! at `"+ *progs_to_find[k].target_setting+"'") ;
1208  break; // found a program
1209  }
1210  }
1211  }
1212 
1213  klf_detect_execenv(settings);
1214 
1215  bool result_failure =
1216  settings->tempdir.isEmpty() || settings->latexexec.isEmpty() || settings->dvipsexec.isEmpty() ||
1217  settings->gsexec.isEmpty(); // NOTE: settings->epstopdfexec.isEmpty() is NOT a failure
1218 
1219  return !result_failure;
1220 }
1221 
1222 
1240 {
1241  // detect mgs.exe as ghostscript and setup its environment properly
1242  QFileInfo gsfi(settings->gsexec);
1243  if (gsfi.fileName() == "mgs.exe") {
1244  QString mgsenv = QString("MIKTEX_GS_LIB=")
1245  + dir_native_separators(gsfi.fi_absolutePath()+"/../../ghostscript/base")
1246  + ";"
1247  + dir_native_separators(gsfi.fi_absolutePath()+"/../../fonts");
1248  __klf_append_replace_env_var(& settings->execenv, "MIKTEX_GS_LIB", mgsenv);
1249  klfDbg("Adjusting environment for mgs.exe: `"+mgsenv+"'") ;
1250  }
1251 
1252 #ifdef Q_WS_MAC
1253  // make sure that epstopdf's path is in PATH because it wants to all gs
1254  // (eg fink distributions)
1255  if (!settings->epstopdfexec.isEmpty()) {
1256  QFileInfo epstopdf_fi(settings->epstopdfexec);
1257  QString execenvpath = QString("PATH=%1:$PATH").arg(epstopdf_fi.fi_absolutePath());
1258  __klf_append_replace_env_var(& settings->execenv, "PATH", execenvpath);
1259  }
1260 #endif
1261 
1262  return true;
1263 }
#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
#define KLFERR_TEXWRITEFAIL
Error while opening .tex file for writing.
Definition: klfbackend.h:55
#define KLFERR_MISSINGMATHMODETHREEDOTS
The "..." is missing in math mode string.
Definition: klfbackend.h:53
save(const QString &fileName, const char *format=0, int quality=-1)
QString errorstr
An explicit error string.
Definition: klfbackend.h:235
#define KLFERR_PROGERR_EPSTOPDF
epstopdf exited with non-zero status (if epstopdf is to be used)
Definition: klfbackend.h:107
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.
tr(const char *sourceText, const char *comment=0, int n=-1)
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:1229
#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.
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.
bool KLF_EXPORT operator==(const KLFBackend::klfInput &a, const KLFBackend::klfInput &b)
static klfOutput getLatexFormula(const klfInput &in, const klfSettings &settings)
The function that processes everything.
Definition: klfbackend.cpp:223
#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.7