[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 863 2013-11-23 13:14:34Z 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  KLFBlockProcess proc;
346  QStringList args;
347 
348  proc.setWorkingDirectory(settings.tempdir);
349 
350  args << settings.latexexec << dir_native_separators(fnTex);
351 
352  qDebug("%s: %s: about to exec latex...", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
353  bool r = proc.startProcess(args, execenv);
354  qDebug("%s: %s: latex returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
355 
356  if (!r) {
357  res.status = KLFERR_NOLATEXPROG;
358  res.errorstr = QObject::tr("Unable to start Latex program %1!", "KLFBackend")
359  .arg(settings.latexexec);
360  return res;
361  }
362  if (!proc.processNormalExit()) {
363  res.status = KLFERR_LATEXNONORMALEXIT;
364  res.errorstr = QObject::tr("Latex was killed!", "KLFBackend");
365  return res;
366  }
367  if (proc.processExitStatus() != 0) {
368  res.status = KLFERR_PROGERR_LATEX;
369  res.errorstr = progErrorMsg("LaTeX", proc.processExitStatus(), proc.readStderrString(),
370  proc.readStdoutString());
371  return res;
372  }
373 
374  if (!QFile::exists(fnDvi)) {
375  res.status = KLFERR_NODVIFILE;
376  res.errorstr = QObject::tr("DVI file didn't appear after having called Latex!", "KLFBackend");
377  return res;
378  }
379 
380  } // end of 'latex' block
381 
382  { // execute dvips -E
383 
384  KLFBlockProcess proc;
385  QStringList args;
386  args << settings.dvipsexec << "-E" << dir_native_separators(fnDvi)
387  << "-o" << dir_native_separators(fnRawEps);
388 
389  qDebug("%s: %s: about to dvips... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
390  bool r = proc.startProcess(args, execenv);
391  qDebug("%s: %s: dvips returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
392 
393  if ( ! r ) {
394  res.status = KLFERR_NODVIPSPROG;
395  res.errorstr = QObject::tr("Unable to start dvips!\n", "KLFBackend");
396  return res;
397  }
398  if ( !proc.processNormalExit() ) {
399  res.status = KLFERR_DVIPSNONORMALEXIT;
400  res.errorstr = QObject::tr("Dvips was mercilessly killed!\n", "KLFBackend");
401  return res;
402  }
403  if ( proc.processExitStatus() != 0) {
404  res.status = KLFERR_PROGERR_DVIPS;
405  res.errorstr = progErrorMsg("dvips", proc.processExitStatus(), proc.readStderrString(),
406  proc.readStdoutString());
407  return res;
408  }
409  if (!QFile::exists(fnRawEps)) {
410  res.status = KLFERR_NOEPSFILE;
411  res.errorstr = QObject::tr("EPS file didn't appear after dvips call!\n", "KLFBackend");
412  return res;
413  }
414 
415 // { // DEAL WITH BOUNDING BOX
416 
417 // // add some space on bounding-box to avoid some too tight bounding box bugs
418 // // read eps file
419 // QFile epsfile(fnRawEps);
420 // r = epsfile.open(dev_READONLY);
421 // if ( ! r ) {
422 // res.status = KLFERR_EPSREADFAIL;
423 // res.errorstr = QObject::tr("Can't read file '%1'!\n", "KLFBackend").arg(fnRawEps);
424 // return res;
425 // }
426 // /** \todo Hi-Res bounding box adjustment. Shouldn't be too hard to do, but needs tests to see
427 // * how this works... [ Currently: only integer-valued BoundingBox: is adjusted. ] */
428 // QByteArray epscontent = epsfile.readAll();
429 // #ifdef KLFBACKEND_QT4
430 // QByteArray epscontent_s = epscontent;
431 // int i = epscontent_s.indexOf("%%BoundingBox: ");
432 // #else
433 // QCString epscontent_s(epscontent.data(), epscontent.size());
434 // int i = epscontent_s.find("%%BoundingBox: ");
435 // #endif
436 // // process file data and transform it
437 // if ( i == -1 ) {
438 // res.status = KLFERR_NOEPSBBOX;
439 // res.errorstr = QObject::tr("File '%1' does not contain line \"%%BoundingBox: ... \" !",
440 // "KLFBackend").arg(fnRawEps);
441 // return res;
442 // }
443 // int ax, ay, bx, by;
444 // char temp[250];
445 // const int k = i;
446 // i += strlen("%%BoundingBox:");
447 // int n = sscanf(epscontent_s.data()+i, "%d %d %d %d", &ax, &ay, &bx, &by);
448 // if ( n != 4 ) {
449 // res.status = KLFERR_BADEPSBBOX;
450 // res.errorstr = QObject::tr("file %1: Line %%BoundingBox: can't read values!\n", "KLFBackend")
451 // .arg(fnRawEps);
452 // return res;
453 // }
454 // // grow bbox by settings.Xborderoffset points
455 // // Don't forget: '%' in printf has special meaning (!) -> double percent signs '%'->'%%'
456 // sprintf(temp, "%%%%BoundingBox: %d %d %d %d",
457 // (int)(ax-settings.lborderoffset+0.5),
458 // (int)(ay-settings.bborderoffset+0.5),
459 // (int)(bx+settings.rborderoffset+0.5),
460 // (int)(by+settings.tborderoffset+0.5));
461 // QString chunk = QString::fromLocal8Bit(epscontent_s.data()+k);
462 // QRegExp rx("^%%BoundingBox: [0-9]+ [0-9]+ [0-9]+ [0-9]+");
463 // rx.rx_indexin(chunk);
464 // int l = rx.matchedLength();
465 // epscontent_s.replace(k, l, temp);
466 
467 // // write content back to second file
468 // QFile epsgoodfile(fnBBCorrEps);
469 // r = epsgoodfile.open(dev_WRITEONLY);
470 // if ( ! r ) {
471 // res.status = KLFERR_EPSWRITEFAIL;
472 // res.errorstr = QObject::tr("Can't write to file '%1'!\n", "KLFBackend")
473 // .arg(fnBBCorrEps);
474 // return res;
475 // }
476 // epsgoodfile.dev_write(epscontent_s);
477 
478 // if ( ! settings.outlineFonts ) {
479 // res.epsdata.ba_assign(epscontent_s);
480 // }
481 // // res.epsdata is now set.
482 
483 // }
484 
485  { // DEAL WITH BBOX: BACKPORT FROM 3.3
486 
487  // read eps file
488  QFile epsfile(fnRawEps);
489  r = epsfile.open(dev_READONLY);
490  if ( ! r ) {
491  res.status = KLFERR_EPSREADFAIL;
492  res.errorstr = QObject::tr("Can't read file '%1'!\n", "KLFBackend").arg(fnRawEps);
493  return res;
494  }
495  QByteArray rawepsdata = epsfile.readAll();
496 
497  klfbbox bbox, bbox_corrected;
498  bool ok = read_eps_bbox(rawepsdata, &bbox, &res);
499  if (!ok)
500  return res; // res was set by the function
501 
502  bbox.x1 -= settings.lborderoffset;
503  bbox.y1 -= settings.bborderoffset;
504  bbox.x2 += settings.rborderoffset;
505  bbox.y2 += settings.tborderoffset;
506 
507  int width_pt = bbox.x2 - bbox.x1;
508  int height_pt = bbox.y2 - bbox.y1;
509 
510  // now correct the bbox to (0,0,width,height)
511 
512  bbox_corrected.x1 = 0;
513  bbox_corrected.y1 = 0;
514  bbox_corrected.x2 = width_pt;
515  bbox_corrected.y2 = height_pt;
516 
517  // and generate corrected raw EPS
518  correct_eps_bbox(rawepsdata, bbox_corrected, bbox, 1.0,
519  &res.epsdata);
520 
521  QFile epsgoodfile(fnBBCorrEps);
522  r = epsgoodfile.open(dev_WRITEONLY);
523  if ( ! r ) {
524  res.status = KLFERR_EPSWRITEFAIL;
525  res.errorstr = QObject::tr("Can't write to file '%1'!\n", "KLFBackend")
526  .arg(fnBBCorrEps);
527  return res;
528  }
529  epsgoodfile.dev_write(res.epsdata);
530  }
531 
532  qDebug("%s: %s: eps bbox set.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
533 
534  } // end of block "make EPS"
535 
536  if (settings.outlineFonts) {
537  // run 'gs' to outline fonts
538  KLFBlockProcess proc;
539 
540  // Very bad joke from ghostscript's guys: they deprecate pswrite device, which worked very well, and
541  // so I had to adapt the code so that it works with the new ps2write device. The bounding boxes were
542  // going like hell. Hopefully a backport of the new system in 3.3 seemed to fix the issue.
543 
544  // So now we have to make sure we use ps2write on newer systems but make sure we still use pswrite on
545  // old systems which don't support ps2write. THANKS A TON GS GUYS :(
546 
547  // In 3.2 we don't query gs version so we have no idea. So just let the user define an environment
548  // variable in case. KLFBACKEND_GS_PS_DEVICE="pswrite" or "epswrite" or "ps2write" (note: with epswrite
549  // you can't expand the bbox)
550 
551  QStringList try_ps_devices;
552  const char *env_gs_device = getenv("KLFBACKEND_GS_PS_DEVICE");
553  if (env_gs_device != NULL) {
554  try_ps_devices << QString::fromLatin1(env_gs_device);
555  } else {
556  try_ps_devices << QLatin1String("pswrite") << QLatin1String("ps2write");
557  }
558 
559  bool r = false;
560  int try_ps_dev_i = 0;
561  for (try_ps_dev_i = 0; try_ps_dev_i < try_ps_devices.size(); try_ps_dev_i++) {
562  QString psdev = try_ps_devices[try_ps_dev_i];
563  qDebug("trying with gs device %s ...", qPrintable(psdev));
564 
565  QStringList args;
566  args << settings.gsexec << "-dNOCACHE" << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop"
567  << QString("-sDEVICE=%1").arg(psdev)
568  << "-sOutputFile="+dir_native_separators(fnOutlFontsEps)
569  << "-q" << "-dBATCH" << dir_native_separators(fnBBCorrEps);
570 
571  qDebug("%s: %s: about to gs (for outline fonts)...\n%s", KLF_FUNC_NAME, KLF_SHORT_TIME,
572  qPrintable(args.join(" ")));
573  r = proc.startProcess(args, execenv);
574  qDebug("%s: %s: gs returned (for outline fonts).", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
575 
576  if (r && proc.processNormalExit() && proc.processExitStatus() == 0) {
577  // successful run
578  break;
579  }
580  }
581 
582  if ( ! r ) {
583  res.status = KLFERR_NOGSPROG;
584  res.errorstr = QObject::tr("Unable to start gs!\n", "KLFBackend");
585  return res;
586  }
587  if ( !proc.processNormalExit() ) {
588  res.status = KLFERR_GSNONORMALEXIT;
589  res.errorstr = QObject::tr("gs died abnormally!\n", "KLFBackend");
590  return res;
591  }
592  if ( proc.processExitStatus() != 0) {
593  res.status = KLFERR_PROGERR_GS_OF;
594  res.errorstr = progErrorMsg("gs", proc.processExitStatus(), proc.readStderrString(),
595  proc.readStdoutString());
596  return res;
597  }
598  if (!QFile::exists(fnOutlFontsEps)) {
599  res.status = KLFERR_NOEPSFILE_OF;
600  res.errorstr = QObject::tr("EPS file (with outlined fonts) didn't appear after call to gs!\n",
601  "KLFBackend");
602  return res;
603  }
604 
605  // get and save outlined EPS to memory
606  QFile ofepsfile(fnOutlFontsEps);
607  r = ofepsfile.open(dev_READONLY);
608  if ( ! r ) {
609  res.status = KLFERR_EPSREADFAIL_OF;
610  res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend")
611  .arg(fnOutlFontsEps);
612  return res;
613  }
614  res.epsdata = ofepsfile.readAll();
615  ofepsfile.close();
616  // res.epsdata is now set to the outlined-fonts version
617  }
618 
619  { // run 'gs' to get png
620  KLFBlockProcess proc;
621  QStringList args;
622  args << settings.gsexec << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop"
623  << "-r"+QString::number(in.dpi) << "-dTextAlphaBits=4"
624  << "-dGraphicsAlphaBits=4";
625  if (qAlpha(in.bg_color) > 0) { // we're forcing a background color
626  args << "-sDEVICE=png16m";
627  } else {
628  args << "-sDEVICE=pngalpha";
629  }
630  args << "-sOutputFile="+dir_native_separators(fnPng) << "-q" << "-dBATCH"
631  << dir_native_separators(fnFinalEps);
632 
633  qDebug("%s: %s: about to gs... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
634  bool r = proc.startProcess(args, execenv);
635  qDebug("%s: %s: gs returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
636 
637  if ( ! r ) {
638  res.status = KLFERR_NOGSPROG;
639  res.errorstr = QObject::tr("Unable to start gs!\n", "KLFBackend");
640  return res;
641  }
642  if ( !proc.processNormalExit() ) {
643  res.status = KLFERR_GSNONORMALEXIT;
644  res.errorstr = QObject::tr("gs died abnormally!\n", "KLFBackend");
645  return res;
646  }
647  if ( proc.processExitStatus() != 0) {
648  res.status = KLFERR_PROGERR_GS;
649  res.errorstr = progErrorMsg("gs", proc.processExitStatus(), proc.readStderrString(),
650  proc.readStdoutString());
651  return res;
652  }
653  if (!QFile::exists(fnPng)) {
654  res.status = KLFERR_NOPNGFILE;
655  res.errorstr = QObject::tr("PNG file didn't appear after call to gs!\n", "KLFBackend");
656  return res;
657  }
658 
659  // get and save PNG to memory
660  QFile pngfile(fnPng);
661  r = pngfile.open(dev_READONLY);
662  if ( ! r ) {
663  res.status = KLFERR_PNGREADFAIL;
664  res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend")
665  .arg(fnPng);
666  return res;
667  }
668  res.pngdata_raw = pngfile.readAll();
669  pngfile.close();
670  // res.pngdata_raw is now set.
671  res.result.loadFromData(res.pngdata_raw, "PNG");
672 
673  // store some meta-information into result
674  res.result.img_settext("AppVersion", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
675  res.result.img_settext("Application",
676  QObject::tr("Created with KLatexFormula version %1", "KLFBackend::saveOutputToFile"));
677  res.result.img_settext("Software", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
678  res.result.img_settext("InputLatex", in.latex);
679  res.result.img_settext("InputMathMode", in.mathmode);
680  res.result.img_settext("InputPreamble", in.preamble);
681  res.result.img_settext("InputFgColor", QString("rgb(%1, %2, %3)").arg(qRed(in.fg_color))
682  .arg(qGreen(in.fg_color)).arg(qBlue(in.fg_color)));
683  res.result.img_settext("InputBgColor", QString("rgba(%1, %2, %3, %4)").arg(qRed(in.bg_color))
684  .arg(qGreen(in.bg_color)).arg(qBlue(in.bg_color))
685  .arg(qAlpha(in.bg_color)));
686  res.result.img_settext("InputDPI", QString::number(in.dpi));
687  res.result.img_settext("SettingsTBorderOffset", QString::number(settings.tborderoffset));
688  res.result.img_settext("SettingsRBorderOffset", QString::number(settings.rborderoffset));
689  res.result.img_settext("SettingsBBorderOffset", QString::number(settings.bborderoffset));
690  res.result.img_settext("SettingsLBorderOffset", QString::number(settings.lborderoffset));
691  res.result.img_settext("SettingsOutlineFonts", settings.outlineFonts?QString("true"):QString("false"));
692  }
693 
694  { // create "final" PNG data
695 #ifdef KLFBACKEND_QT4
696  QBuffer buf(&res.pngdata);
697 #else
698  QBuffer buf(res.pngdata);
699 #endif
700  buf.open(dev_WRITEONLY);
701  bool r = res.result.save(&buf, "PNG");
702  if (!r) {
703  qWarning("%s: Error: Can't save \"final\" PNG data.", KLF_FUNC_NAME);
704  res.pngdata.ba_assign(res.pngdata_raw);
705  }
706  }
707 
708  if (!settings.epstopdfexec.isEmpty()) {
709  // if we have epstopdf functionality, then we'll take advantage of it to generate pdf:
710  KLFBlockProcess proc;
711  QStringList args;
712  args << settings.epstopdfexec << dir_native_separators(fnFinalEps)
713  << ("--outfile="+dir_native_separators(fnPdf));
714 
715  qDebug("%s: %s: about to epstopdf... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
716  bool r = proc.startProcess(args, execenv);
717  qDebug("%s: %s: epstopdf returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
718 
719  if ( ! r ) {
720  res.status = KLFERR_NOEPSTOPDFPROG;
721  res.errorstr = QObject::tr("Unable to start epstopdf!\n", "KLFBackend");
722  return res;
723  }
724  if ( !proc.processNormalExit() ) {
725  res.status = KLFERR_EPSTOPDFNONORMALEXIT;
726  res.errorstr = QObject::tr("epstopdf died nastily!\n", "KLFBackend");
727  return res;
728  }
729  if ( proc.processExitStatus() != 0) {
730  res.status = KLFERR_PROGERR_EPSTOPDF;
731  res.errorstr = progErrorMsg("epstopdf", proc.processExitStatus(), proc.readStderrString(),
732  proc.readStdoutString());
733  return res;
734  }
735  if (!QFile::exists(fnPdf)) {
736  qDebug("%s: %s: pdf file '%s' didn't appear after epstopdf!", KLF_FUNC_NAME, KLF_SHORT_TIME,
737  qPrintable(fnPdf));
738  res.status = KLFERR_NOPDFFILE;
739  res.errorstr = QObject::tr("PDF file didn't appear after call to epstopdf!\n", "KLFBackend");
740  return res;
741  }
742 
743  // get and save PDF to memory
744  QFile pdffile(fnPdf);
745  r = pdffile.open(dev_READONLY);
746  if ( ! r ) {
747  res.status = KLFERR_PDFREADFAIL;
748  res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend").arg(fnPdf);
749  return res;
750  }
751  res.pdfdata = pdffile.readAll();
752 
753  }
754 
755  qDebug("%s: %s: end of function.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
756 
757  return res;
758 }
759 
760 
761 
762 static bool s_starts_with(const char * x, int len_x, const char *test, int len_test)
763 {
764  if (len_x < len_test)
765  return false;
766  return !strncmp(x, test, len_test);
767 }
768 
769 #define D_RX "([0-9eE.-]+)"
770 
771 static bool parse_bbox_values(const QString& str, klfbbox *bbox)
772 {
773  // parse bbox values
774  QRegExp rx_bbvalues("" D_RX "\\s+" D_RX "\\s+" D_RX "\\s+" D_RX "");
775  int i = rx_bbvalues.rx_indexin(str);
776  if (i < 0) {
777  return false;
778  }
779  bbox->x1 = rx_bbvalues.cap(1).toDouble();
780  bbox->y1 = rx_bbvalues.cap(2).toDouble();
781  bbox->x2 = rx_bbvalues.cap(3).toDouble();
782  bbox->y2 = rx_bbvalues.cap(4).toDouble();
783  return true;
784 }
785 
786 static bool read_eps_bbox(const QByteArray& epsdata, klfbbox *bbox, KLFBackend::klfOutput * resError)
787 {
788  static const char * hibboxtag = "%%HiResBoundingBox:";
789  static const char * bboxtag = "%%BoundingBox:";
790  static const int hibboxtaglen = strlen(hibboxtag);
791  static const int bboxtaglen = strlen(bboxtag);
792 
793  // Read dvips' bounding box.
794  QBuffer buf;
795  buf_setdata(buf, epsdata);
796  bool r = buf.open(dev_READONLY);
797  if (!r) {
798  qWarning("What's going on!!?! can't open buffer for reading? Will Fail!!!") ;
799  }
800 
801  QString nobboxerrstr =
802  QObject::tr("DVIPS did not provide parsable %%BoundingBox: in its output!", "KLFBackend");
803 
804  char linebuffer[512];
805  int n;
806  bool gotepsbbox = false;
807  int still_look_for_hiresbbox_lines = 5;
808  while ((n = buf.readLine(linebuffer, sizeof(linebuffer)-1)) > 0) {
809  if (gotepsbbox && still_look_for_hiresbbox_lines-- < 0) {
810  // if we already got the %BoundingBox, and we've been looking at more than a certian number of lines
811  // after that, abort because usually %BoundingBox and %HiResBoundingBox are together...
812  klfDbg("stopped looking for hires-bbox.") ;
813  break;
814  }
815  if (s_starts_with(linebuffer, n-1, hibboxtag, hibboxtaglen)) {
816  // got hi-res bounding-box
817  bool ok = parse_bbox_values(QString::fromLatin1(linebuffer+hibboxtaglen), bbox);
818  if (!ok) {
819  resError->status = KLFERR_BADEPSBBOX;
820  resError->errorstr = nobboxerrstr;
821  return false;
822  }
823  klfDbg("got hires-bbox.") ;
824  // all ok, got hi-res bbox
825  return true;
826  }
827  if (s_starts_with(linebuffer, n-1, bboxtag, bboxtaglen)) {
828  // got bounding-box.
829  bool ok = parse_bbox_values(QString::fromLatin1(linebuffer+bboxtaglen), bbox);
830  if (!ok) {
831  continue;
832  }
833  // stand by, continue in case we have a hi-res bbox.
834  gotepsbbox = true;
835  klfDbg("got normal bbox.") ;
836  continue;
837  }
838  }
839 
840  // didn't get a hi-res bbox. see if we still got a regular %BoundingBox: and return that.
841  if (gotepsbbox) {
842  // bbox pointer is already set
843  return true;
844  }
845 
846  resError->status = KLFERR_BADEPSBBOX;
847  resError->errorstr = nobboxerrstr;
848  return false;
849 }
850 
851 // static int find_ba_in_ba(const QByteArray& haystack, const QByteArray& needle)
852 // {
853 // #ifdef KLFBACKEND_QT4
854 // return haystack.indexOf(needle);
855 // #else
856 // int k, j;
857 // for (k = 0; k < haystack.length()-needle.length(); ++k) {
858 // // locally compare haystack and needle
859 // for (j = 0; j < needle.length(); ++j) {
860 // if (haystack[k+j] != needle[j])
861 // break; // nope they're not the same
862 // }
863 // if (j == needle.length())
864 // // found the needle
865 // return k;
866 // }
867 // return -1;
868 // #endif
869 // }
870 
871 static void correct_eps_bbox(const QByteArray& rawepsdata, const klfbbox& bbox_corrected,
872  const klfbbox& bbox_orig, double vectorscale,
873  QByteArray * epsdatacorrected)
874 {
875  static const char * bboxdecl = "%%BoundingBox:";
876  static int bboxdecl_len = strlen(bboxdecl);
877 
878  double offx = bbox_corrected.x1 - bbox_orig.x1;
879  double offy = bbox_corrected.y1 - bbox_orig.y1;
880 
881  // in raw EPS data, find '%%BoundingBox:' and length of the full BoundingBox instruction
882  int i, len;
883  char nl[] = "\0\0\0";
884 #ifdef KLFBACKEND_QT4
885  i = rawepsdata.indexOf(bboxdecl);
886 #else
887  QCString rawepsdata_s(rawepsdata.data(), rawepsdata.size());
888  i = rawepsdata_s.find(bboxdecl);
889 #endif
890  if (i < 0) {
891  i = 0;
892  len = 0;
893  } else {
894  int j = i+bboxdecl_len;
895  while (j < (int)rawepsdata.size() && rawepsdata[j] != '\r' && rawepsdata[j] != '\n')
896  ++j;
897  len = j-i;
898  // also determine what the newline is (\n, \r, \r\n?)
899  if (rawepsdata[j] == '\r' && j < (int)rawepsdata.size()-1 && rawepsdata[j+1] == '\n') {
900  nl[0] = '\r', nl[1] = '\n';
901  } else {
902  nl[0] = rawepsdata[j];
903  }
904  }
905 
906  double dwi = bbox_corrected.x2 * vectorscale;
907  double dhi = bbox_corrected.y2 * vectorscale;
908  int wi = (int)(dwi + 0.99999) ;
909  int hi = (int)(dhi + 0.99999) ;
910  char buffer[1024];
911  int buffer_len;
912  // recall that '%%' in printf is replaced by a single '%'...
913  snprintf(buffer, sizeof(buffer)-1,
914  "%%%%BoundingBox: 0 0 %d %d%s"
915  "%%%%HiResBoundingBox: 0 0 %.6g %.6g%s",
916  wi, hi, nl,
917  dwi, dhi, nl);
918  buffer_len = strlen(buffer);
919 
920  /*
921  char backgroundfillps[1024] = "";
922  if (qAlpha(bgcolor) > 0) {
923  sprintf(backgroundfillps,
924  // draw the background color, if any
925  "newpath "
926  "-2 -2 moveto "
927  "%s -2 lineto "
928  "%s %s lineto "
929  "-2 %s lineto "
930  "closepath "
931  "gsave "
932  "%s %s %s setrgbcolor "
933  "fill "
934  "grestore %s",
935  klfFmtDoubleCC(dwi+1, 'g', 6),
936  klfFmtDoubleCC(dwi+1, 'g', 6), klfFmtDoubleCC(dhi+1, 'g', 6),
937  klfFmtDoubleCC(dhi+1, 'g', 6),
938  // and the color, in RGB components:
939  klfFmtDoubleCC(qRed(bgcolor)/255.0, 'f', 6),
940  klfFmtDoubleCC(qGreen(bgcolor)/255.0, 'f', 6),
941  klfFmtDoubleCC(qBlue(bgcolor)/255.0, 'f', 6),
942  nl
943  );
944  }
945  */
946 
947  char buffer2[1024];
948  int buffer2_len;
949  snprintf(buffer2, sizeof(buffer2)-1,
950  "%s"
951  "%%%%Page 1 1%s"
952  "%%%%PageBoundingBox 0 0 %d %d%s"
953  "<< /PageSize [%d %d] >> setpagedevice%s"
954  //"%s"
955  "%f %f scale%s"
956  "%f %f translate%s"
957  ,
958  nl,
959  nl,
960  wi, hi, nl,
961  wi, hi, nl,
962  //backgroundfillps,
963  vectorscale, vectorscale, nl,
964  offx, offy, nl);
965  buffer2_len = strlen(buffer2);
966 
967  // char buffer2[128];
968  // snprintf(buffer2, 127, "%sgrestore%s", nl, nl);
969 
970  //klfDbg("buffer is `"<<buffer<<"', length="<<buffer_len) ;
971  //klfDbg("rawepsdata has length="<<rawepsdata.size()) ;
972 
973  // and modify the raw EPS data, to replace "%%BoundingBox:" instruction by our stuff...
974 #ifdef KLFBACKEND_QT4
975  QByteArray neweps;
976  neweps = rawepsdata;
977 #else
978  QCString neweps(rawepsdata.data(), rawepsdata.size()); // makes deep copy
979 #endif
980  neweps.replace(i, len, buffer);
981 
982  const char * endsetupstr = "%%EndSetup";
983  int i2 = neweps.s_indexOf(endsetupstr);
984  if (i2 < 0)
985  i2 = i + buffer_len; // add our info after modified %%BoundingBox'es instructions if %%EndSetup not found
986  else
987  i2 += strlen(endsetupstr);
988 
989  neweps.replace(i2, 0, buffer2);
990 
991  qDebug("neweps has now length=%d",neweps.size());
992  qDebug("New eps bbox is [0 0 %.6g %.6g] with translate [%.6g %.6g] and scale %.6g.",
993  dwi, dhi, offx, offy, vectorscale);
994 
995  epsdatacorrected->ba_assign(neweps);
996 }
997 
998 
999 
1000 
1001 
1002 
1003 
1004 
1005 
1006 
1007 
1008 void KLFBackend::cleanup(QString tempfname)
1009 {
1010  const char *skipcleanup = getenv("KLFBACKEND_LEAVE_TEMP_FILES");
1011  if (skipcleanup != NULL && (*skipcleanup == '1' || *skipcleanup == 't' || *skipcleanup == 'T' ||
1012  *skipcleanup == 'y' || *skipcleanup == 'Y'))
1013  return; // skip cleaning up temp files
1014 
1015  if (QFile::exists(tempfname+".tex")) QFile::remove(tempfname+".tex");
1016  if (QFile::exists(tempfname+".dvi")) QFile::remove(tempfname+".dvi");
1017  if (QFile::exists(tempfname+".aux")) QFile::remove(tempfname+".aux");
1018  if (QFile::exists(tempfname+".log")) QFile::remove(tempfname+".log");
1019  if (QFile::exists(tempfname+".toc")) QFile::remove(tempfname+".toc");
1020  if (QFile::exists(tempfname+".eps")) QFile::remove(tempfname+".eps");
1021  if (QFile::exists(tempfname+"-good.eps")) QFile::remove(tempfname+"-good.eps");
1022  if (QFile::exists(tempfname+"-raw.eps")) QFile::remove(tempfname+"-raw.eps");
1023  if (QFile::exists(tempfname+"-bbcorr.eps")) QFile::remove(tempfname+"-bbcorr.eps");
1024  if (QFile::exists(tempfname+"-outlfonts.eps")) QFile::remove(tempfname+"-outlfonts.eps");
1025  if (QFile::exists(tempfname+".png")) QFile::remove(tempfname+".png");
1026  if (QFile::exists(tempfname+".pdf")) QFile::remove(tempfname+".pdf");
1027 }
1028 
1029 
1030 
1031 
1032 // static private mutex object
1033 QMutex KLFBackend::__mutex;
1034 
1035 
1036 
1037 KLF_EXPORT bool operator==(const KLFBackend::klfInput& a, const KLFBackend::klfInput& b)
1038 {
1039  return a.latex == b.latex &&
1040  a.mathmode == b.mathmode &&
1041  a.preamble == b.preamble &&
1042  a.fg_color == b.fg_color &&
1043  a.bg_color == b.bg_color &&
1044  a.dpi == b.dpi;
1045 }
1046 
1047 
1048 bool KLFBackend::saveOutputToDevice(const klfOutput& klfoutput, QIODevice *device,
1049  const QString& fmt, QString *errorStringPtr)
1050 {
1051  QString format = fmt.s_trimmed().s_toUpper();
1052 
1053  // now choose correct data source and write to fout
1054  if (format == "EPS" || format == "PS") {
1055  device->dev_write(klfoutput.epsdata);
1056  } else if (format == "PNG") {
1057  device->dev_write(klfoutput.pngdata);
1058  } else if (format == "PDF") {
1059  if (klfoutput.pdfdata.isEmpty()) {
1060  QString error = QObject::tr("PDF format is not available!\n",
1061  "KLFBackend::saveOutputToFile");
1062  qWarning("%s", qPrintable(error));
1063  if (errorStringPtr != NULL)
1064  errorStringPtr->operator=(error);
1065  return false;
1066  }
1067  device->dev_write(klfoutput.pdfdata);
1068  } else {
1069  bool res = klfoutput.result.save(device, format.s_toLatin1());
1070  if ( ! res ) {
1071  QString errstr = QObject::tr("Unable to save image in format `%1'!",
1072  "KLFBackend::saveOutputToDevice").arg(format);
1073  qWarning("%s", qPrintable(errstr));
1074  if (errorStringPtr != NULL)
1075  *errorStringPtr = errstr;
1076  return false;
1077  }
1078  }
1079 
1080  return true;
1081 }
1082 
1083 bool KLFBackend::saveOutputToFile(const klfOutput& klfoutput, const QString& fileName,
1084  const QString& fmt, QString *errorStringPtr)
1085 {
1086  QString format = fmt;
1087  // determine format first
1088  if (format.isEmpty() && !fileName.isEmpty()) {
1089  QFileInfo fi(fileName);
1090  if ( ! fi.fi_suffix().isEmpty() )
1091  format = fi.fi_suffix();
1092  }
1093  if (format.isEmpty())
1094  format = QLatin1String("PNG");
1095  format = format.s_trimmed().s_toUpper();
1096  // got format. choose output now and prepare write
1097  QFile fout;
1098  if (fileName.isEmpty() || fileName == "-") {
1099  if ( ! fout.f_open_fp(stdout) ) {
1100  QString error = QObject::tr("Unable to open stderr for write! Error: %1\n",
1101  "KLFBackend::saveOutputToFile").arg(fout.f_error());
1102  qWarning("%s", qPrintable(error));
1103  if (errorStringPtr != NULL)
1104  *errorStringPtr = error;
1105  return false;
1106  }
1107  } else {
1108  fout.f_setFileName(fileName);
1109  if ( ! fout.open(dev_WRITEONLY) ) {
1110  QString error = QObject::tr("Unable to write to file `%1'! Error: %2\n",
1111  "KLFBackend::saveOutputToFile")
1112  .arg(fileName).arg(fout.f_error());
1113  qWarning("%s", qPrintable(error));
1114  if (errorStringPtr != NULL)
1115  *errorStringPtr = error;
1116  return false;
1117  }
1118  }
1119 
1120  return saveOutputToDevice(klfoutput, &fout, format, errorStringPtr);
1121 }
1122 
1123 
1124 bool KLFBackend::detectSettings(klfSettings *settings, const QString& extraPath)
1125 {
1127 
1128  QStringList stdextrapaths;
1129  int k, j;
1130  for (k = 0; standard_extra_paths[k] != NULL; ++k) {
1131  stdextrapaths.append(standard_extra_paths[k]);
1132  }
1133  QString extra_paths = stdextrapaths.join(QString("")+KLF_PATH_SEP);
1134  if (!extraPath.isEmpty())
1135  extra_paths += KLF_PATH_SEP + extraPath;
1136 
1137  // temp dir
1138 #ifdef KLFBACKEND_QT4
1140 #else
1141 # if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_MACX)
1142  settings->tempdir = "/tmp";
1143 # elif defined(Q_OS_WIN32)
1144  settings->tempdir = getenv("TEMP");
1145 # else
1146  settings->tempdir = QString();
1147 # endif
1148 #endif
1149 
1150  // sensible defaults
1151  settings->lborderoffset = 1;
1152  settings->tborderoffset = 1;
1153  settings->rborderoffset = 1;
1154  settings->bborderoffset = 1;
1155 
1156  // find executables
1157  struct { QString * target_setting; QStringList prog_names; } progs_to_find[] = {
1158  { & settings->latexexec, progLATEX },
1159  { & settings->dvipsexec, progDVIPS },
1160  { & settings->gsexec, progGS },
1161  { & settings->epstopdfexec, progEPSTOPDF },
1162  { NULL, QStringList() }
1163  };
1164  // replace @executable_path in extra_paths
1165  klfDbg(klfFmtCC("Our base extra paths are: %s", qPrintable(extra_paths))) ;
1166  QString ourextrapaths = extra_paths;
1167  ourextrapaths.replace("@executable_path", qApp->applicationDirPath());
1168  klfDbg(klfFmtCC("Our extra paths are: %s", qPrintable(ourextrapaths))) ;
1169  // and actually search for those executables
1170  for (k = 0; progs_to_find[k].target_setting != NULL; ++k) {
1171  klfDbg("Looking for "+progs_to_find[k].prog_names.join(" or ")) ;
1172  for (j = 0; j < (int)progs_to_find[k].prog_names.size(); ++j) {
1173  klfDbg("Testing `"+progs_to_find[k].prog_names[j]+"'") ;
1174  *progs_to_find[k].target_setting
1175  = klfSearchPath(progs_to_find[k].prog_names[j], ourextrapaths);
1176  if (!progs_to_find[k].target_setting->isEmpty()) {
1177  klfDbg("Found! at `"+ *progs_to_find[k].target_setting+"'") ;
1178  break; // found a program
1179  }
1180  }
1181  }
1182 
1183  klf_detect_execenv(settings);
1184 
1185  bool result_failure =
1186  settings->tempdir.isEmpty() || settings->latexexec.isEmpty() || settings->dvipsexec.isEmpty() ||
1187  settings->gsexec.isEmpty(); // NOTE: settings->epstopdfexec.isEmpty() is NOT a failure
1188 
1189  return !result_failure;
1190 }
1191 
1192 
1210 {
1211  // detect mgs.exe as ghostscript and setup its environment properly
1212  QFileInfo gsfi(settings->gsexec);
1213  if (gsfi.fileName() == "mgs.exe") {
1214  QString mgsenv = QString("MIKTEX_GS_LIB=")
1215  + dir_native_separators(gsfi.fi_absolutePath()+"/../../ghostscript/base")
1216  + ";"
1217  + dir_native_separators(gsfi.fi_absolutePath()+"/../../fonts");
1218  __klf_append_replace_env_var(& settings->execenv, "MIKTEX_GS_LIB", mgsenv);
1219  klfDbg("Adjusting environment for mgs.exe: `"+mgsenv+"'") ;
1220  }
1221 
1222 #ifdef Q_WS_MAC
1223  // make sure that epstopdf's path is in PATH because it wants to all gs
1224  // (eg fink distributions)
1225  if (!settings->epstopdfexec.isEmpty()) {
1226  QFileInfo epstopdf_fi(settings->epstopdfexec);
1227  QString execenvpath = QString("PATH=%1:$PATH").arg(epstopdf_fi.fi_absolutePath());
1228  __klf_append_replace_env_var(& settings->execenv, "PATH", execenvpath);
1229  }
1230 #endif
1231 
1232  return true;
1233 }
#define KLFERR_PNGREADFAIL
Error while opening .png file for reading.
Definition: klfbackend.h:87
cap(int nth=0)
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
#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: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.
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.11