[KLF Backend][KLF Tools][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$ */
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <ctype.h> // isspace()
27 #include <sys/time.h>
28 #include <math.h> // fabs()
29 
30 #include <QtGlobal>
31 #include <QByteArray>
32 #include <QSet>
33 #include <QCoreApplication>
34 #include <QRegExp>
35 #include <QFile>
36 #include <QDateTime>
37 #include <QTextStream>
38 #include <QBuffer>
39 #include <QDir>
40 #include <QColor>
41 #include <QTextDocument>
42 #include <QImageWriter>
43 #include <QTextCodec>
44 #include <QTemporaryDir>
45 
46 #include <klfutil.h>
47 #include <klfsysinfo.h>
48 #include <klfdatautil.h>
49 
50 #include "klfblockprocess.h"
51 #include "klffilterprocess.h"
52 #include "klfuserscript.h"
53 #include "klfbackend.h"
54 #include "klfbackend_p.h"
55 
56 
57 
82 // some standard guess settings for system configurations
83 
84 #ifdef KLF_EXTRA_SEARCH_PATHS
85 # define EXTRA_PATHS_PRE KLF_EXTRA_SEARCH_PATHS ,
86 //# define EXTRA_PATHS KLF_EXTRA_SEARCH_PATHS
87 #else
88 # define EXTRA_PATHS_PRE
89 //# define EXTRA_PATHS
90 #endif
91 
92 
93 #if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
94 QStringList progLATEX = QStringList() << "latex.exe";
95 QStringList progDVIPS = QStringList() << "dvips.exe";
96 QStringList progGS = QStringList() << "gswin32c.exe" << "gswin64c.exe" << "mgs.exe";
97 //QStringList progEPSTOPDF = QStringList() << "epstopdf.exe";
98 static const char * standard_extra_paths[] = {
99  EXTRA_PATHS_PRE
100  "C:\\Program Files*\\MiKTeX*\\miktex\\bin",
101  "C:\\texlive\\*\\bin\\win*",
102  "C:\\Program Files*\\gs*\\gs*\\bin",
103  NULL
104 };
105 #elif defined(KLF_WS_MAC)
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  "/usr/texbin:/Library/TeX/texbin:/usr/local/bin:/opt/local/bin:/sw/bin:/sw/usr/bin",
113  NULL
114 };
115 #else
116 QStringList progLATEX = QStringList() << "latex";
117 QStringList progDVIPS = QStringList() << "dvips";
118 QStringList progGS = QStringList() << "gs";
119 //QStringList progEPSTOPDF = QStringList() << "epstopdf";
120 static const char * standard_extra_paths[] = {
121  EXTRA_PATHS_PRE
122  NULL
123 };
124 #endif
125 
126 
127 
128 // ---------------------------------
129 
130 KLFAbstractLatexMetaInfo::KLFAbstractLatexMetaInfo()
131 {
132 }
133 KLFAbstractLatexMetaInfo::~KLFAbstractLatexMetaInfo()
134 {
135 }
136 
137 void KLFAbstractLatexMetaInfo::saveMetaInfo(const KLFBackend::klfInput& in,
138  const KLFBackend::klfSettings& settings)
139 {
140  static QString boolstr[2] = { QLatin1String("true"), QLatin1String("false") } ;
141 
142  saveField("AppVersion", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
143  saveField("Application",
144  QObject::tr("Created with KLatexFormula version %1", "KLFBackend::saveOutputToFile")
145  .arg(KLF_VERSION_STRING));
146  saveField("Software", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
147  saveField("InputLatex", in.latex);
148  saveField("InputMathMode", in.mathmode);
149  saveField("InputPreamble", in.preamble);
150  saveField("InputFontSize", QString::number(in.fontsize, 'g', 2));
151  saveField("InputFgColor", QString("rgb(%1, %2, %3)").arg(qRed(in.fg_color))
152  .arg(qGreen(in.fg_color)).arg(qBlue(in.fg_color)));
153  saveField("InputBgColor", QString("rgba(%1, %2, %3, %4)").arg(qRed(in.bg_color))
154  .arg(qGreen(in.bg_color)).arg(qBlue(in.bg_color))
155  .arg(qAlpha(in.bg_color)));
156  saveField("InputDPI", QString::number(in.dpi));
157  saveField("InputVectorScale", QString::number(in.vectorscale, 'g', 4));
158  saveField("InputBypassTemplate", boolstr[(int)in.bypassTemplate]);
159  saveField("InputUserScript", QFileInfo(in.userScript).fileName());
160  QString usparams;
162  saveField("InputUserScriptParams", usparams);
163  saveField("SettingsTBorderOffset", QString::number(settings.tborderoffset));
164  saveField("SettingsRBorderOffset", QString::number(settings.rborderoffset));
165  saveField("SettingsBBorderOffset", QString::number(settings.bborderoffset));
166  saveField("SettingsLBorderOffset", QString::number(settings.lborderoffset));
167  saveField("SettingsOutlineFonts", boolstr[(int)settings.outlineFonts]);
168  saveField("SettingsCalcEpsBoundingBox", boolstr[(int)settings.calcEpsBoundingBox]);
169  saveField("SettingsWantRaw", boolstr[(int)settings.wantRaw]);
170  saveField("SettingsWantPDF", boolstr[(int)settings.wantPDF]);
171  saveField("SettingsWantSVG", boolstr[(int)settings.wantSVG]);
172 
173  klfDbg("saved meta-info.") ;
174 }
175 
176 
177 KLFImageLatexMetaInfo::KLFImageLatexMetaInfo(QImage *imgwrite) : _w(imgwrite) { }
178 
179 void KLFImageLatexMetaInfo::saveField(const QString& k, const QString& v)
180 {
181  // QImageWriter::setText() uses QString::simplified() and does not save whitespace properly :(
182  // so encode text in some appropriate way.
183  _w->setText(k, klfDataToEscaped(v.toUtf8(), '%'));
184 }
185 QString KLFImageLatexMetaInfo::loadField(const QString &k) {
186  return QString::fromUtf8(klfEscapedToData(_w->text(k).toLatin1(), '%'));
187 }
188 
189 
190 KLF_EXPORT QByteArray klf_escape_ps_string(const QString& v)
191 {
192  // write escape codes
193  int i;
194  // if v is just ascii, no need to encode it in unicode
195  bool isascii = true;
196  for (i = 0; i < v.length(); ++i) {
197  if (v[i] < 0 || v[i] > 126) {
198  isascii = false;
199  break;
200  }
201  }
202  QByteArray vdata;
203  if (isascii) {
204  vdata = v.toLatin1();
205 
206  QByteArray escaped;
207  for (i = 0; i < vdata.size(); ++i) {
208  char c = vdata[i];
209  klfDbg("Char: "<<c);
210  if (QChar(vdata[i]).isLetterOrNumber() || c == ' ' || c == '.' || c == ',' || c == '/')
211  escaped += c;
212  else if (c == '\n')
213  escaped += "\\n";
214  else if (c == '\r')
215  escaped += "\\r";
216  else if (c == '\t')
217  escaped += "\\t";
218  else if (c == '\\')
219  escaped += "\\\\";
220  else if (c == '(')
221  escaped += "\\(";
222  else if (c == ')')
223  escaped += "\\)";
224  else {
225  klfDbg("escaping char: (int)c="<<(int)c<<" (uint)c="<<uint(c)<<", octal="<<klfFmtCC("%03o", (uint)c));
226  escaped += QString("\\%1").arg((unsigned int)(unsigned char)c, 3, 8, QChar('0')).toLatin1();
227  }
228  }
229 
230  return "("+escaped+")";
231  }
232 
233  // otherwise, do unicode encoding
234 
235  QTextCodec *codec = QTextCodec::codecForName("UTF-16BE");
236  vdata = codec->fromUnicode(v);
237  klfDbg("vdata is "<<klfDataToEscaped(vdata));
238 
239  QByteArray hex;
240  for (i = 0; i < (vdata.size()-1); i += 2) {
241  hex += klfFmt("%02x%02x ", (unsigned int)(unsigned char)vdata[i], (unsigned int)(unsigned char)vdata[i+1]);
242  }
243  return "<" + hex + ">";
244 }
245 
246 
247 
248 KLFPdfmarksWriteLatexMetaInfo::KLFPdfmarksWriteLatexMetaInfo(QByteArray * string)
249  : _s(string)
250 {
251  // See the following for more info:
252  // http://stackoverflow.com/questions/3010015/pdfmark-for-docinfo-metadata-in-pdf-is-not-accepting-accented-characters-in-keyw
253  // http://www.justskins.com/forums/adding-metadata-to-pdf-68647.html
254 
255  _s->append( // ensure pdfmark symbol defined in postscript
256  "/pdfmark where { pop } { /globaldict where { pop globaldict } { userdict } ifelse "
257  "/pdfmark /cleartomark load put } ifelse\n"
258  // now the proper PDFmarks DOCINFO dictionary
259  "[ "
260  );
261 }
262 
264 {
265  KLF_ASSERT_CONDITION(false, "N/A.", return QString(); ) ;
266 }
267 void KLFPdfmarksWriteLatexMetaInfo::saveField(const QString& k, const QString& v)
268 {
269  savePDFField("KLF"+k, v);
270 }
271 void KLFPdfmarksWriteLatexMetaInfo::finish()
272 {
273  _s->append(" /DOCINFO pdfmark\n");
274 }
276 {
277  QByteArray datavalue = klf_escape_ps_string(v);
278 
279  _s->append( " /"+k+" " + datavalue + "\n");
280 }
281 
282 
283 
284 
285 
286 
287 // ---------------------------------
288 
289 
290 static QMutex klf_mutex;
291 
292 struct GsInfo
293 {
294  GsInfo() { }
295 
296  QString version;
297  int version_maj;
298  int version_min;
299  QString help;
300  QSet<QString> availdevices;
301 };
302 
303 // cache gs version/help/etc. information (for each gs executable, in case there are several)
305 
306 static void initGsInfo(const KLFBackend::klfSettings *settings, bool isMainThread);
307 
308 
309 
310 
311 
312 
313 
314 // ---------------------------------
315 
316 
317 KLFBackend::TemplateGenerator::TemplateGenerator()
318 {
319 }
320 KLFBackend::TemplateGenerator::~TemplateGenerator()
321 {
322 }
323 
324 KLFBackend::DefaultTemplateGenerator::DefaultTemplateGenerator()
325 {
326 }
327 KLFBackend::DefaultTemplateGenerator::~DefaultTemplateGenerator()
328 {
329 }
330 
332  const klfSettings& /*settings*/)
333 {
334  QString latexin;
335  QString s;
336 
337  latexin = in.mathmode;
338  latexin.replace("...", in.latex);
339 
341  s += "\\documentclass{article}\n"
342  "\\usepackage[dvips]{color}\n";
343  s += in.preamble;
344  s += "\n"
345  "\\begin{document}\n"
346  "\\thispagestyle{empty}\n";
347  if (in.fontsize > 0) {
348  s += QString("\\fontsize{%1}{%2}\\selectfont\n").arg(in.fontsize, 0, 'f', 2).arg(in.fontsize*1.2, 0, 'f', 2);
349  }
350  s += QString("\\definecolor{klffgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.fg_color)/255.0)
351  .arg(qGreen(in.fg_color)/255.0).arg(qBlue(in.fg_color)/255.0);
352  s += QString("\\definecolor{klfbgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.bg_color)/255.0)
353  .arg(qGreen(in.bg_color)/255.0).arg(qBlue(in.bg_color)/255.0);
354  if (qAlpha(in.bg_color)>0)
355  s += "\\pagecolor{klfbgcolor}\n";
356  s += "{\\color{klffgcolor} ";
357  s += latexin;
358  s += "%\n"
359  "}\n"
360  "\\end{document}\n";
361 
362  return s;
363 }
364 
365 
366 
367 
368 // ---------------------------------
369 
370 KLFBackend::KLFBackend()
371 {
372 }
373 
374 
375 
376 
377 #define D_RX "([0-9eE.-]+)"
378 
379 // A Bounding Box
380 struct klfbbox {
381  double x1, x2, y1, y2;
382 };
383 
384 
385 
386 static bool calculate_gs_eps_bbox(const QByteArray& epsdata, const QString& epsFile, klfbbox *bbox,
387  KLFBackend::klfOutput * resError, const KLFBackend::klfSettings& settings,
388  bool isMainThread);
389 static bool read_eps_bbox(const QByteArray& epsdata, klfbbox *bbox, KLFBackend::klfOutput * resError);
390 static void correct_eps_bbox(const QByteArray& epsdata,
391  const klfbbox& bbox_corrected, const klfbbox& bbox_orig,
392  double vectorscale, QRgb bgcolor, QByteArray * epsdatacorrected);
393 
394 static void replace_svg_width_or_height(QByteArray *svgdata, const char * attr, double val);
395 
396 
397 static inline bool has_userscript_output(const QSet<QString>& fmts, const QString& format)
398 {
399  return fmts.contains(format);
400  // if (!fmts.contains(format))
401  // return false;
402  // return fn.isEmpty() ? true : QFile::exists(fn);
403 }
404 
405 
406 
408 
409 KLF_EXPORT KLFStringSet klfbackend_fmts =
410  KLFStringSet()
411  /* */ << "latex" << "dvi" << "eps-raw" << "eps-bbox" << "eps-processed"
412 /* */ << "png" << "pdf" << "svg-gs" << "svg" ;
413 
414 
415 KLF_EXPORT KLFStringSet klfbackend_dependencies(const QString& fmt, bool recursive = false)
416 {
417  static KLFStringSet fn_lock = KLFStringSet();
418 
419  if (fn_lock.contains(fmt)) {
420  klfWarning("Dependency loop detected for format "<<fmt) ;
421  return KLFStringSet();
422  }
423  fn_lock << fmt;
424 
425  KLFStringSet s;
426  if (fmt == QLatin1String("tex") || fmt == QLatin1String("latex")) {
427  // no dependency
428  } else if (fmt == QLatin1String("dvi")) {
429  s << "latex";
430  } else if (fmt == QLatin1String("eps-raw")) {
431  s << "dvi";
432  } else if (fmt == QLatin1String("eps-bbox")) {
433  s << "eps-raw";
434  } else if (fmt == QLatin1String("eps-processed")) {
435  s << "eps-bbox";
436  } else if (fmt == QLatin1String("png")) {
437  s << "eps-processed";
438  } else if (fmt == QLatin1String("pdf")) {
439  s << "eps-processed";
440  } else if (fmt == QLatin1String("svg-gs")) {
441  s << "eps-processed";
442  } else if (fmt == QLatin1String("svg")) {
443  s << "svg-gs";
444  } else {
445  klfWarning("Unknown format : "<<fmt) ;
446  }
447  if (!recursive) {
448  fn_lock.remove(fmt);
449  return s;
450  }
451  // explore dependencies recursively
452  KLFStringSet basedeps = s;
453  foreach (QString str, basedeps) {
454  KLFStringSet subdeps = klfbackend_dependencies(str, true);
455  foreach (QString subdep, subdeps) {
456  s << subdep;
457  }
458  }
459 
460  fn_lock.remove(fmt);
461  return s;
462 }
463 
464 static inline bool assert_have_formats_for(const KLFStringSet& outputs, const KLFStringSet& skipfmts,
465  const QString& forwhat)
466 {
467  KLFStringSet fmtlist = klfbackend_dependencies(forwhat);
468  foreach (QString s, fmtlist) {
469  if (skipfmts.contains(s) && !outputs.contains(s)) {
470  klfWarning("User Script Skipped format "<<s<<" which is necessary for "<<forwhat) ;
471  return false;
472  }
473  }
474  return true;
475 }
476 
477 #define ASSERT_HAVE_FORMATS_FOR(forwhat) \
478  { if (!assert_have_formats_for(us_outputs, us_skipfmts, forwhat)) { \
479  res.status = KLFERR_USERSCRIPT_BADSKIPFORMATS; \
480  res.errorstr = QObject::tr("User Script broke dependencies in skip-formats list", "KLFBackend"); \
481  return res; \
482  } \
483  }
484 
485 
486 
487 
489  bool isMainThread)
490 {
491  // ALLOW ONLY ONE RUNNING getLatexFormula() AT A TIME
492  QMutexLocker mutexlocker(&klf_mutex);
493 
495 
496  klfSettings settings;
497  settings = usersettings;
498 
499  klfInput in;
500  in = input;
501 
502  bool ok;
503 
504  klfDbg("called. latex="<<in.latex);
505 
506  { // get full, expanded exec environment
508  klfDbg("current environment is "<<curenv) ;
509  settings.execenv = klfMergeEnvironment(curenv, settings.execenv,
510  QStringList() << "PATH" << "TEXINPUTS" << "BIBINPUTS",
512  }
513 
514  klfDbg("execution environment for sub-processes is "<<settings.execenv) ;
515 
516 
517  klfOutput res;
518  res.status = KLFERR_NOERROR;
519  res.errorstr = QString();
520  res.result = QImage();
521  res.pngdata_raw = QByteArray();
522  res.pngdata = QByteArray();
523  res.dvidata = QByteArray();
524  res.epsdata_raw = QByteArray();
525  res.epsdata = QByteArray();
526  res.pdfdata = QByteArray();
527  res.svgdata = QByteArray();
528  res.input = in;
529  res.settings = settings;
530 
531 
532  // read GS version, will need later
533  initGsInfo(&settings, isMainThread);
534  if (!gsInfo.contains(settings.gsexec)) {
536  res.errorstr = QObject::tr("Can't query version of ghostscript located at `%1'.", "KLFBackend")
537  .arg(settings.gsexec);
538  return res;
539  }
540 
541  const GsInfo thisGsInfo = gsInfo.value(settings.gsexec);
542 
543  klfDebugf(("%s: queried ghostscript version: %s", KLF_FUNC_NAME, qPrintable(thisGsInfo.version))) ;
544 
545  // force some rules on settings
546 
547  // if calcEpsBoundingBox is being used, we need to add bg color at "correcting bbox time"
548  QRgb bgcolor_when_correcting_bbox = qRgba(0,0,0,0);
549  klfDebugf(("%s: settings.calcEpsBoundingBox=%d, in.bg_color=[RGBA %d,%d,%d,%d]", KLF_FUNC_NAME,
550  settings.calcEpsBoundingBox, qRed(in.bg_color), qGreen(in.bg_color), qBlue(in.bg_color),
551  qAlpha(in.bg_color)));
552 
553  if (settings.calcEpsBoundingBox &&
554  qAlpha(in.bg_color) != 0 && (in.bg_color & qRgb(255,255,255)) != qRgb(255,255,255)) {
555  bgcolor_when_correcting_bbox = in.bg_color;
556  in.bg_color = qRgba(0,0,0,0);
557  }
558 
559 
560  // PROCEDURE (V3.3)
561  //
562  // EACH STEP MIGHT BE DONE BY A USER SCRIPT INSTEAD IF THAT IS REQUESTED.
563  //
564  // - generate LaTeX file
565  //
566  // - latex --> get DVI file
567  //
568  // - dvips -E file.dvi -o file.eps --> get (first) EPS file
569  //
570  // - gs -dNOPAUSE -dSAFER -sDEVICE=bbox -q -dBATCH file.eps --> calculate correct bbox for EPS file
571  //
572  // will output something like
573  // %%BoundingBox: int(X1) int(Y1) int(X2) int(Y2)
574  // %%HiResBoundingBox: X1 Y1 X2 Y2
575  //
576  // - read file.eps, modify e.g. as file-bbox.eps: replace
577  // %%BoundingBox ***
578  // by
579  // %%HiResBoundingBox: 0 0 (X2-X1) (Y2-Y1)
580  // -X1 -Y1 translate
581  // while of course taking into account manual corrections given by [lrtb]borderoffset settings/overrides
582  //
583  // EITHER (gs >= 9.01 && !outlinefonts)
584  // ### PhF: update this doc!! it's wrong!!
585  // - gs -dNOPAUSE -dSAFER -dSetPageSize -sDEVICE=ps2write -dEPSCrop -sOutputFile=file-corrected.(e)ps
586  // -q -dBATCH file-bbox.eps --> generate (E)PS file w/ correct page size
587  // OR
588  // - gs -dNOCACHE -dNOPAUSE -dSAFER -sDEVICE=pswrite -dEPSCrop -sOutputFile=file-corrected.eps -q -dBATCH
589  // file-bbox.eps --> generate post-processed (E)PS file
590  //
591  // - gs -dNOPAUSE -dSAFER -sDEVICE=pdfwrite -sOutputFile=file.pdf -q -dBATCH file-corrected.eps
592  // with added pdfmarks..
593  //
594  // - if (reports-has-device(svg)) {
595  //
596  // - gs -dNOPAUSE -dSAFER -sDEVICE=svg -r72x72 -sOutputFile=file.svg -q -dBATCH file-corrected.eps
597  //
598  // - modify SVG file to replace width='WWpt' height='HHpt' by
599  // width='(X2-X1)px' height='(Y2-Y1)px'
600  // with data given by gs before, with full precision
601  //
602  // - }
603 
604  QString ver = KLF_VERSION_STRING;
605  ver.replace(".", "x"); // make friendly names with chars in [a-zA-Z0-9]
606  // the base name for all our temp files
607  // QString tempfname = settings.tempdir + "/klftmp" + ver + "T" + QDateTime::currentDateTime().toString("hhmmss")
608  // + "p"+ QString("%1").arg(QCoreApplication::applicationPid(), 0, 26);
609  QString temptemplate = settings.tempdir + "/klftmp"+ver+"-XXXXXX";
610 
611  // create the temporary directory
612  QTemporaryDir tempdir(temptemplate);
613 
614  if (!tempdir.isValid()) {
615  // failed to create temporary directory
616  res.errorstr = QObject::tr("Failed to create temporary directory inside `%1'",
617  "KLFBackend").arg(settings.tempdir);
619  return res;
620  }
621 
622  QString tempfname = tempdir.path() + "/klftemp";
623 
624  klfDbg("Temp location base name is "<<tempfname) ;
625 
626  QString fnTex = tempfname + ".tex";
627  QString fnDvi = tempfname + ".dvi";
628  QString fnRawEps = tempfname + ".eps";
629  QString fnBBoxEps = tempfname + "-bbox.eps";
630  QString fnProcessedEps = tempfname + "-processed.eps";
631  QString fnRawPng = tempfname + "-raw.png";
632  QString fnPdfMarks = tempfname + ".pdfmarks";
633  QString fnPdf = tempfname + ".pdf";
634  QString fnGsSvg = tempfname + "-gs.svg";
635  // some user scripts may provide directly .svg (even though the default process chain
636  // processes raw svg in memory, not generating final svg file)
637  QString fnSvg = tempfname + ".svg";
638 
639  // we need non-outlinedfont EPS data anyway.
640  QByteArray rawepsdata;
641  QByteArray bboxepsdata;
642  QByteArray gssvgdata;
643 
644 
645  QString latexsimplified = in.latex.trimmed();
646  if (latexsimplified.isEmpty()) {
647  res.errorstr = QObject::tr("You must specify a LaTeX formula!", "KLFBackend");
649  return res;
650  }
651 
652  if (!in.bypassTemplate) {
653  if (in.mathmode.contains("...") == 0) {
655  res.errorstr = QObject::tr("The math mode string doesn't contain '...'!", "KLFBackend");
656  return res;
657  }
658  }
659 
660  // prepare LaTeX file
661  {
662  QFile file(fnTex);
663  bool r = file.open(QIODevice::WriteOnly);
664  if ( ! r ) {
666  res.errorstr = QObject::tr("Can't open file for writing: '%1'!", "KLFBackend").arg(fnTex);
667  return res;
668  }
669  QTextStream stream(&file);
670  if (!in.bypassTemplate) {
671  TemplateGenerator *t = NULL;
673  if (settings.templateGenerator != NULL) {
674  klfDbg("using custom template generator") ;
675  t = settings.templateGenerator;
676  KLF_ASSERT_NOT_NULL(t, "Template Generator is NULL! Using default!", t = &deft; ) ;
677  } else {
678  t = &deft;
679  }
680  stream << t->generateTemplate(in, settings);
681  } else {
682  stream << in.latex;
683  }
684  }
685 
686  KLFStringSet us_outputs;
687  KLFStringSet us_skipfmts;
688  KLFStringSet our_skipfmts;
689 
690  if (!in.userScript.isEmpty()) {
691  // user has provided us a wrapper script. Query it and use it
692 
694 
695  if (scriptinfo.scriptInfoError() != KLFERR_NOERROR) {
696  res.status = scriptinfo.scriptInfoError();
697  res.errorstr = scriptinfo.scriptInfoErrorString();
698  return res;
699  }
700 
701  if ( (!scriptinfo.klfMinVersion().isEmpty()
702  && klfVersionCompare(scriptinfo.klfMinVersion(), KLF_VERSION_STRING) > 0) ||
703  (!scriptinfo.klfMaxVersion().isEmpty()
704  && klfVersionCompare(scriptinfo.klfMaxVersion(), KLF_VERSION_STRING) < 0) ) {
705  res.status = KLFERR_USERSCRIPT_BADKLFVERSION;
706  res.errorstr = QObject::tr("User Script `%1' is not compatible with current version of KLatexFormula.",
707  "KLFBackend").arg(scriptinfo.name());
708  return res;
709  }
710 
711  if (scriptinfo.category() != QLatin1String("klf-backend-engine")) {
712  res.status = KLFERR_USERSCRIPT_BADCATEGORY;
713  res.errorstr = QObject::tr("User Script `%1' is not usable as backend latex engine!",
714  "KLFBackend").arg(scriptinfo.name());
715  return res;
716  }
717 
718  // and run the script with the latex input
719  QStringList addenv;
720  addenv
721  // program executables
722  << "KLF_TEMPDIR=" + settings.tempdir
723  << "KLF_LATEX=" + settings.latexexec
724  << "KLF_DVIPS=" + settings.dvipsexec
725  << "KLF_GS=" + settings.gsexec
726  << "KLF_GS_VERSION=" + thisGsInfo.version
727  << "KLF_GS_DEVICES=" + QStringList(thisGsInfo.availdevices.toList()).join(",")
728  // input
729  << klfInputToEnvironmentForUserScript(in)
730  // more advanced settings
731  << klfSettingsToEnvironmentForUserScript(settings)
732  // file names (all formed with same basename...) to access by the script
733  << "KLF_TEMPFNAME=" + tempfname // the base name for all our temp files
734  << "KLF_FN_TEX=" + fnTex
735  << "KLF_FN_LATEX=" + fnTex
736  << "KLF_FN_DVI=" + fnDvi
737  << "KLF_FN_EPS_RAW=" + fnRawEps
738  << "KLF_FN_EPS_BBOX=" + fnBBoxEps
739  << "KLF_FN_EPS_PROCESSED=" + fnProcessedEps
740  << "KLF_FN_PNG=" + fnRawPng
741  << "KLF_FN_PDFMARKS=" + fnPdfMarks
742  << "KLF_FN_PDF=" + fnPdf
743  << "KLF_FN_SVG_GS=" + fnGsSvg
744  << "KLF_FN_SVG=" + fnSvg
745  ;
746 
747  { // now run the script
748  KLFUserScriptFilterProcess p(scriptinfo.userScriptPath(), &settings);
749 
750  p.addExecEnviron(addenv);
751 
752  p.setProcessAppEvents(false);
753 
754  QByteArray stderrdata;
755  QByteArray stdoutdata;
756  p.collectStderrTo(&stderrdata);
757  p.collectStdoutTo(&stdoutdata);
758 
759  p.addArgv(QDir::toNativeSeparators(fnTex));
760 
762  QStringList outfmts = scriptinfo.spitsOut();
763  foreach (QString fmt, outfmts) {
764  us_outputs << fmt;
765  if (fmt == QLatin1String("latex")) {
766  // user script overwrote the tex/latex file, don't collect the tex file data as it is not
767  // needed (not considered as useful output!). This new tex file will be seen and accessed
768  // by 'latex' in the next process block after userscript if needed.
769  } else if (fmt == QLatin1String("dvi")) {
770  outdata[fnDvi] = &res.dvidata;
771  } else if (fmt == QLatin1String("eps-raw")) {
772  // if we don't need this, it will be removed below from the list
773  outdata[fnRawEps] = &rawepsdata;
774  } else if (fmt == QLatin1String("eps-bbox")) {
775  outdata[fnBBoxEps] = &bboxepsdata;
776  } else if (fmt == QLatin1String("eps-processed")) {
777  outdata[fnProcessedEps] = &res.epsdata;
778  } else if (fmt == QLatin1String("png")) {
779  if (settings.wantRaw) {
780  outdata[fnRawPng] = &res.pngdata_raw;
781  }
782  } else if (fmt == QLatin1String("pdf")) {
783  if (settings.wantPDF) {
784  outdata[fnPdf] = &res.pdfdata;
785  }
786  } else if (fmt == QLatin1String("svg-gs")) {
787  // ignore this data, not returned in klfOutput but the created file is used to generate
788  // the processed SVG
789  outdata[fnGsSvg] = & gssvgdata;
790  } else if (fmt == QLatin1String("svg")) {
791  if (settings.wantSVG) {
792  outdata[fnSvg] = &res.svgdata;
793  }
794  } else {
795  klfWarning("Can't handle output format from user script: "<<fmt) ;
796  }
797  }
798  if (us_outputs.isEmpty()) {
799  us_outputs << "dvi"; // by default, the script is assumed to provide DVI.
800  }
801  if (us_outputs.contains("eps-bbox") && !settings.wantRaw) {
802  // don't need to fetch initial raw eps data
803  if (outdata.contains("eps-raw"))
804  outdata.remove(fnRawEps);
805  }
806  if (us_outputs.contains("eps-processed") && !settings.wantRaw) {
807  // don't need to fetch bbox raw eps data
808  if (outdata.contains("eps-bbox"))
809  outdata.remove(fnBBoxEps);
810  }
811 
812  QStringList skipfmts = scriptinfo.skipFormats();
813  bool invert = false;
814  int tempi;
815  if ((tempi = skipfmts.indexOf("ALL_EXCEPT")) >= 0) {
816  invert = true;
817  skipfmts.removeAt(tempi);
818  foreach (QString f, klfbackend_fmts) { us_skipfmts << f; }
819  }
820  foreach (QString fmt, skipfmts) {
821  if (!klfbackend_fmts.contains(fmt)) {
822  klfWarning("User Script Info: Unknown format to skip: "<<fmt) ;
823  }
824  if (!invert) {
825  us_skipfmts << fmt;
826  } else {
827  us_skipfmts.remove(fmt);
828  }
829  }
830  our_skipfmts = us_skipfmts;
831  foreach (QString fmt, outfmts) {
832  if (our_skipfmts.contains(fmt)) {
833  klfWarning("User Script Info: format " << fmt << " provided by script is also marked "
834  "as to be skipped!") ;
835  }
836  }
837 
838  if ((us_outputs.contains("eps-processed") || our_skipfmts.contains("eps-processed")) && !settings.wantRaw) {
839  our_skipfmts << "eps-bbox";
840  }
841  if ((us_outputs.contains("eps-bbox") || our_skipfmts.contains("eps-bbox")) && !settings.wantRaw) {
842  our_skipfmts << "eps-raw";
843  }
844  if (us_outputs.contains("svg") || our_skipfmts.contains("svg")) {
845  our_skipfmts << "svg-gs"; // don't need svg-gs if we're not generating svg
846  }
847 
848  klfDbg("us_skipfmts = " << us_skipfmts) ;
849 
850  ok = p.run(outdata);
851 
852  if (!ok) {
853  res.errorstr = p.resultErrorString();
854  switch (p.resultStatus()) {
855  case KLFFP_NOSTART: res.status = KLFERR_USERSCRIPT_NORUN; break;
856  case KLFFP_NOEXIT: res.status = KLFERR_USERSCRIPT_NONORMALEXIT; break;
857  case KLFFP_NOSUCCESSEXIT: res.status = KLFERR_PROGERR_USERSCRIPT; break;
858  case KLFFP_NODATA: res.status = KLFERR_USERSCRIPT_NOOUTPUT; break;
859  case KLFFP_DATAREADFAIL: res.status = KLFERR_USERSCRIPT_OUTPUTREADFAIL; break;
860  default:
861  res.status = p.resultStatus();
862  }
863  return res;
864  }
865 
866  // make sure all promised files have appeared
867  foreach (QString fmt, outfmts) {
868  QString corrfname;
869  if (fmt == QLatin1String("latex")) {
870  corrfname = fnTex;
871  } else if (fmt == QLatin1String("dvi")) {
872  corrfname = fnDvi;
873  } else if (fmt == QLatin1String("eps-raw")) {
874  corrfname = fnRawEps;
875  } else if (fmt == QLatin1String("eps-bbox")) {
876  corrfname = fnBBoxEps;
877  } else if (fmt == QLatin1String("eps-processed")) {
878  corrfname = fnProcessedEps;
879  } else if (fmt == QLatin1String("png")) {
880  corrfname = fnRawPng;
881  } else if (fmt == QLatin1String("pdf")) {
882  corrfname = fnPdf;
883  } else if (fmt == QLatin1String("svg-gs")) {
884  corrfname = fnGsSvg;
885  } else if (fmt == QLatin1String("svg")) {
886  corrfname = fnSvg;
887  } else {
888  klfWarning("Unknown format: " << fmt) ;
889  continue;
890  }
891  if (!QFile::exists(corrfname)) {
892  klfWarning("Promised format " << fmt << " did not appear after calling user script.") ;
893  }
894  }
895  }
896  }
897 
898  klfDbg("our_skipfmts = " << our_skipfmts) ;
899 
900 
901  if (!has_userscript_output(us_outputs, "dvi") && !our_skipfmts.contains("dvi")) {
902  // execute latex
903  klfDbg("preparing to launch latex.") ;
904 
905  if (settings.latexexec.isEmpty()) {
907  res.errorstr = QObject::tr("No latex executable given!\n", "KLFBackend");
908  return res;
909  }
910 
911  KLFBackendFilterProgram p(QLatin1String("LaTeX"), &settings, isMainThread, tempdir.path());
912  p.resErrCodes[KLFFP_NOSTART] = KLFERR_LATEX_NORUN;
913  p.resErrCodes[KLFFP_NOEXIT] = KLFERR_LATEX_NONORMALEXIT;
914  p.resErrCodes[KLFFP_NOSUCCESSEXIT] = KLFERR_PROGERR_LATEX;
915  p.resErrCodes[KLFFP_NODATA] = KLFERR_LATEX_NOOUTPUT;
916  p.resErrCodes[KLFFP_DATAREADFAIL] = KLFERR_LATEX_OUTPUTREADFAIL;
917 
918  p.setArgv(QStringList() << settings.latexexec << QDir::toNativeSeparators(fnTex));
919 
920  QByteArray userinputforerrors = "h\nr\n";
921 
922  ok = p.run(userinputforerrors, fnDvi, &res.dvidata);
923  if (!ok) {
924  p.errorToOutput(&res);
925  return res;
926  }
927  }
928 
929  if (!has_userscript_output(us_outputs, "eps-raw") && !our_skipfmts.contains("eps-raw")) {
930 
931  ASSERT_HAVE_FORMATS_FOR("eps-raw") ;
932 
933  if (settings.dvipsexec.isEmpty()) {
935  res.errorstr = QObject::tr("No dvips executable given!\n", "KLFBackend");
936  return res;
937  }
938 
939  // execute dvips -E
940  KLFBackendFilterProgram p(QLatin1String("dvips"), &settings, isMainThread, tempdir.path());
941  p.resErrCodes[KLFFP_NOSTART] = KLFERR_DVIPS_NORUN;
942  p.resErrCodes[KLFFP_NOEXIT] = KLFERR_DVIPS_NONORMALEXIT;
943  p.resErrCodes[KLFFP_NOSUCCESSEXIT] = KLFERR_PROGERR_DVIPS;
944  p.resErrCodes[KLFFP_NODATA] = KLFERR_DVIPS_NOOUTPUT;
945  p.resErrCodes[KLFFP_DATAREADFAIL] = KLFERR_DVIPS_OUTPUTREADFAIL;
946 
947  QFileInfo dvipsinf(settings.dvipsexec);
948  if (!dvipsinf.filePath().isEmpty()) {
949  // add the explicit dvips path to the PATH environment, in case dvips needs to
950  // execute helpers such as mktexpk
951  p.addExecEnviron(QStringList() << (
952  QLatin1String("PATH=") + dvipsinf.absoluteFilePath() + QLatin1String(":") +
954  )) ;
955  }
956 
957  p.setArgv(QStringList() << settings.dvipsexec << "-E" << QDir::toNativeSeparators(fnDvi)
958  << "-o" << QDir::toNativeSeparators(fnRawEps));
959 
960  ok = p.run(fnRawEps, &rawepsdata);
961 
962  if (!ok) {
963  p.errorToOutput(&res);
964  return res;
965  }
966  klfDbg("read raw EPS; rawepsdata/length="<<rawepsdata.size()) ;
967  } // end of 'dvips' block
968 
969  // the settings requires, save the intermediary data in to result output
970  if (settings.wantRaw)
971  res.epsdata_raw = rawepsdata;
972 
973  // This now also returned in 'res', directly saved there.
974  // // width and height of the (final) EPS bbox in postscript points
975  // double width_pt = 0, height_pt = 0;
976 
977  if (!has_userscript_output(us_outputs, "eps-bbox") && !our_skipfmts.contains("eps-bbox")) {
978  // find correct bounding box of EPS file, and modify EPS data manually to add boffset and
979  // translate to (0,0,width,height)
980 
981  ASSERT_HAVE_FORMATS_FOR("eps-bbox") ;
982 
983  klfbbox bbox, bbox_corrected;
984 
985  if (settings.calcEpsBoundingBox) {
986  bool ok = calculate_gs_eps_bbox(QByteArray(), fnRawEps, &bbox, &res, settings, isMainThread);
987  if (!ok)
988  return res; // res was set by the function
989  } else {
990  bool ok = read_eps_bbox(rawepsdata, &bbox, &res);
991  if (!ok)
992  return res; // res was set by the function
993  }
994 
995  bbox.x1 -= settings.lborderoffset;
996  bbox.y1 -= settings.bborderoffset;
997  bbox.x2 += settings.rborderoffset;
998  bbox.y2 += settings.tborderoffset;
999 
1000  res.width_pt = bbox.x2 - bbox.x1;
1001  res.height_pt = bbox.y2 - bbox.y1;
1002 
1003  // now correct the bbox to (0,0,width,height)
1004 
1005  bbox_corrected.x1 = 0;
1006  bbox_corrected.y1 = 0;
1007  bbox_corrected.x2 = res.width_pt;
1008  bbox_corrected.y2 = res.height_pt;
1009 
1010  // and generate corrected raw EPS
1011 
1012  correct_eps_bbox(rawepsdata, bbox_corrected, bbox, in.vectorscale,
1013  bgcolor_when_correcting_bbox, &bboxepsdata);
1014 
1015  klfDbg("corrected bbox to "<<bbox.x1<<","<<bbox.y1<<","<<bbox.x2<<","<<bbox.y2);
1016  } else if (!our_skipfmts.contains("eps-bbox")) {
1017  // userscript generated bbox-corrected EPS for us, but we still
1018  // need to set width_pt and height_pt appropriately.
1019 
1020  klfbbox bb;
1021 
1022  // read from fnRawEps, fnBBoxEps or fnProcessedEps ?
1023  QString fn;
1024  //QByteArray *dat;
1025  if (us_outputs.contains("eps-processed")) {
1026  fn = fnProcessedEps;
1027  //dat = & res.epsdata;
1028  } else {
1029  fn = fnBBoxEps;
1030  //dat = & bboxepsdata;
1031  }
1032 
1033  if (settings.calcEpsBoundingBox) {
1034  bool ok = calculate_gs_eps_bbox(QByteArray(), fn, &bb, &res, settings, isMainThread);
1035  if (!ok)
1036  return res; // res was set by the function
1037  } else {
1038  bool ok = read_eps_bbox(bboxepsdata, &bb, &res);
1039  if (!ok)
1040  return res; // res was set by the function
1041  }
1042 
1043  res.width_pt = bb.x2 - bb.x1;
1044  res.height_pt = bb.y2 - bb.y1;
1045  } // end 'correct bbox in eps' block
1046 
1047  // the settings requires, save the intermediary data in to result output
1048  if (settings.wantRaw)
1049  res.epsdata_bbox = bboxepsdata;
1050 
1051  if (!has_userscript_output(us_outputs, "eps-processed") && !our_skipfmts.contains("eps-processed")) {
1052  // need to process EPS, i.e. outline fonts
1053 
1054  ASSERT_HAVE_FORMATS_FOR("eps-processed") ;
1055 
1056  if (settings.outlineFonts) {
1057  // post-process EPS file to outline fonts if requested
1058 
1059  if (settings.gsexec.isEmpty()) {
1060  res.status = KLFERR_NOGSPROG;
1061  res.errorstr = QObject::tr("No gs executable given!\n", "KLFBackend");
1062  return res;
1063  }
1064 
1065  KLFBackendFilterProgram p(QLatin1String("gs (EPS Post-Processing Outline Fonts)"), &settings, isMainThread,
1066  tempdir.path());
1067  p.resErrCodes[KLFFP_NOSTART] = KLFERR_GSPOSTPROC_NORUN;
1068  p.resErrCodes[KLFFP_NOEXIT] = KLFERR_GSPOSTPROC_NONORMALEXIT;
1069  p.resErrCodes[KLFFP_NOSUCCESSEXIT] = KLFERR_PROGERR_GSPOSTPROC;
1070  p.resErrCodes[KLFFP_NODATA] = KLFERR_GSPOSTPROC_NOOUTPUT;
1071  p.resErrCodes[KLFFP_DATAREADFAIL] = KLFERR_GSPOSTPROC_OUTPUTREADFAIL;
1072 
1073  QString psdevice;
1074  QStringList gsoptions;
1075  const char *env_gs_device = getenv("KLFBACKEND_GS_PS_DEVICE");
1076  if (env_gs_device != NULL) {
1077  psdevice = QString::fromLatin1(env_gs_device);
1078  gsoptions = QStringList() << "-dNOCACHE -dNoOutputFonts";
1079  } else if (thisGsInfo.version_maj < 9 ||
1080  (thisGsInfo.version_maj == 9 && thisGsInfo.version_min <= 7)) {
1081  // until 9.07, we can still use 'pswrite' with -dNOCACHE
1082  psdevice = QLatin1String("pswrite");
1083  gsoptions = QStringList() << "-dNOCACHE";
1084  } else if (thisGsInfo.version_maj > 9 ||
1085  (thisGsInfo.version_maj == 9 && thisGsInfo.version_min >= 15)) {
1086  // Ghostscript removed the pswrite device after 9.07; The ghostscript developers
1087  // were very responsive and helpful to my feedback, and thanks to their prompt
1088  // reaction, starting from 9.15, we can use the ps2write device with the option
1089  // "-dNoOutputFonts".
1090  psdevice = QLatin1String("ps2write");
1091  gsoptions = QStringList() << "-dNoOutputFonts";
1092  } else {
1094  res.errorstr = QObject::tr("Installed Ghostscript version %1 may not be used to create font outlines."
1095  " Please upgrade to gs >= 9.15 (or downgrade to gs <= 9.07).",
1096  "KLFBackend").arg(thisGsInfo.version);
1097  return res;
1098  }
1099 
1100  p.addArgv(settings.gsexec);
1101  p.addArgv(QStringList() << gsoptions
1102  << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop" << QString::fromLatin1("-sDEVICE=%1").arg(psdevice)
1103  << "-sOutputFile="+QDir::toNativeSeparators(fnProcessedEps)
1104  << "-q" << "-dBATCH" << "-");
1105 
1106  ok = p.run(bboxepsdata, fnProcessedEps, &res.epsdata);
1107  if (!ok) {
1108  p.errorToOutput(&res);
1109  return res;
1110  }
1111 
1112  klfDebugf(("%s: res.epsdata has length=%d", KLF_FUNC_NAME, res.epsdata.size())) ;
1113 
1114  } else {
1115  // no post-processed EPS, copy raw (bbox-corrected) EPS data:
1116  res.epsdata = bboxepsdata;
1117  }
1118  }
1119 
1120  if (!has_userscript_output(us_outputs, "png") && !our_skipfmts.contains("png")) {
1121 
1122  ASSERT_HAVE_FORMATS_FOR("png") ;
1123 
1124  if (settings.gsexec.isEmpty()) {
1125  res.status = KLFERR_NOGSPROG;
1126  res.errorstr = QObject::tr("No gs executable given!\n", "KLFBackend");
1127  return res;
1128  }
1129 
1130  // run 'gs' to get PNG data
1131  KLFBackendFilterProgram p(QLatin1String("gs (PNG)"), &settings, isMainThread, tempdir.path());
1132  p.resErrCodes[KLFFP_NOSTART] = KLFERR_GSPNG_NORUN;
1133  p.resErrCodes[KLFFP_NOEXIT] = KLFERR_GSPNG_NONORMALEXIT;
1134  p.resErrCodes[KLFFP_NOSUCCESSEXIT] = KLFERR_PROGERR_GSPNG;
1135  p.resErrCodes[KLFFP_NODATA] = KLFERR_GSPNG_NOOUTPUT;
1136  p.resErrCodes[KLFFP_DATAREADFAIL] = KLFERR_GSPNG_OUTPUTREADFAIL;
1137 
1142  // ### wait... do we want vector scaling to apply to the PNG as well??
1143 
1144  p.setArgv(QStringList() << settings.gsexec
1145  << "-dNOPAUSE" << "-dSAFER" << "-dTextAlphaBits=4" << "-dGraphicsAlphaBits=4"
1146  << "-r"+QString::number(in.dpi) << "-dEPSCrop" << "-dMaxBitmap=2147483647");
1147  if (qAlpha(in.bg_color) > 0) { // we're forcing a background color
1148  p.addArgv("-sDEVICE=png16m");
1149  } else {
1150  p.addArgv("-sDEVICE=pngalpha");
1151  }
1152  p.addArgv(QStringList() << "-sOutputFile="+QDir::toNativeSeparators(fnRawPng) << "-q" << "-dBATCH" << "-");
1153 
1154  ok = p.run(bboxepsdata, fnRawPng, &res.pngdata_raw);
1155  if (!ok) {
1156  p.errorToOutput(&res);
1157  return res;
1158  }
1159 
1160  res.result.loadFromData(res.pngdata_raw, "PNG");
1161  } // raw PNG
1162  else {
1163  if (us_skipfmts.contains("png")) {
1164  klfWarning("PNG format was skipped by user script. The QImage object will be invalid.") ;
1165  res.result = QImage();
1166  res.pngdata = QByteArray();
1167  res.pngdata_raw = QByteArray();
1168  }
1169  if (!has_userscript_output(us_outputs, "png") || !QFile::exists(fnRawPng)) {
1170  klfWarning("PNG format is required to initialize the QImage object, but was not generated by user script.") ;
1171  res.result = QImage();
1172  } else {
1173  // load PNG into res.result
1174  res.result.load(fnRawPng);
1175  }
1176  }
1177 
1178  if (!our_skipfmts.contains("png")) { // generate tagged/labeled PNG
1179 
1180  // store some meta-information into result
1181  KLFImageLatexMetaInfo metainfo(&res.result);
1182  metainfo.saveMetaInfo(in, settings);
1183 
1184  { // create "final" PNG data
1185  QBuffer buf(&res.pngdata);
1186  buf.open(QIODevice::WriteOnly);
1187 
1188  bool r = res.result.save(&buf, "PNG");
1189  if (!r) {
1190  klfWarning("Can't save \"final\" PNG data.") ;
1191  res.pngdata = res.pngdata_raw;
1192  }
1193  }
1194 
1195  klfDbg("prepared final PNG data.") ;
1196  }
1197 
1198  if ( settings.wantPDF && !has_userscript_output(us_outputs, "pdf") && !our_skipfmts.contains("pdf") ) {
1199 
1200  ASSERT_HAVE_FORMATS_FOR("pdf") ;
1201 
1202  // prepare PDFMarks
1203  { QFile fpdfmarks(fnPdfMarks);
1204  bool r = fpdfmarks.open(QIODevice::WriteOnly);
1205  if ( ! r ) {
1207  res.errorstr = QObject::tr("Can't open file for writing: '%1'!", "KLFBackend").arg(fnPdfMarks);
1208  return res;
1209  }
1210  QByteArray pdfmarkstr;
1211  KLFPdfmarksWriteLatexMetaInfo pdfmetainfo(&pdfmarkstr);
1212  pdfmetainfo.savePDFField("Title", in.latex);
1213  pdfmetainfo.savePDFField("Keywords", "KLatexFormula KLF LaTeX equation formula");
1214  pdfmetainfo.savePDFField("Creator", "KLatexFormula " KLF_VERSION_STRING);
1215  pdfmetainfo.saveMetaInfo(in, settings);
1216  pdfmetainfo.finish();
1217  fpdfmarks.write(pdfmarkstr);
1218  // file is ready.
1219  }
1220 
1221  if (settings.gsexec.isEmpty()) {
1222  res.status = KLFERR_NOGSPROG;
1223  res.errorstr = QObject::tr("No gs executable given!\n", "KLFBackend");
1224  return res;
1225  }
1226 
1227  // run 'gs' to get PDF data
1228  KLFBackendFilterProgram p(QLatin1String("gs (PDF)"), &settings, isMainThread, tempdir.path());
1229  p.resErrCodes[KLFFP_NOSTART] = KLFERR_GSPDF_NORUN;
1230  p.resErrCodes[KLFFP_NOEXIT] = KLFERR_GSPDF_NONORMALEXIT;
1231  p.resErrCodes[KLFFP_NOSUCCESSEXIT] = KLFERR_PROGERR_GSPDF;
1232  p.resErrCodes[KLFFP_NODATA] = KLFERR_GSPDF_NOOUTPUT;
1233  p.resErrCodes[KLFFP_DATAREADFAIL] = KLFERR_GSPDF_OUTPUTREADFAIL;
1234 
1235  p.setArgv(QStringList() << settings.gsexec
1236  << "-dNOPAUSE" << "-dSAFER" << "-sDEVICE=pdfwrite"
1237  << "-sOutputFile="+QDir::toNativeSeparators(fnPdf)
1238  << "-q" << "-dBATCH" << "-" << fnPdfMarks);
1239 
1240  // input: res.epsdata is the processed EPS file, or the raw EPS + bbox/page correction if no post-processing
1241  ok = p.run(res.epsdata, fnPdf, &res.pdfdata);
1242  if (!ok) {
1243  p.errorToOutput(&res);
1244  return res;
1245  }
1246  }
1247 
1248  if (settings.wantSVG) {
1249 
1250  if (!has_userscript_output(us_outputs, "svg-gs") &&
1251  !our_skipfmts.contains("svg-gs")) {
1252 
1253  ASSERT_HAVE_FORMATS_FOR("svg-gs") ;
1254 
1255  // run 'gs' to get SVG (raw from gs)
1256  if (!thisGsInfo.availdevices.contains("svg")) {
1257  // not OK to get SVG...
1258  klfWarning("ghostscript cannot create SVG");
1259  res.status = KLFERR_GSSVG_NOSVG;
1260  res.errorstr = QObject::tr("This ghostscript (%1) cannot generate SVG.", "KLFBackend").arg(settings.gsexec);
1261  return res;
1262  }
1263 
1264  if (settings.gsexec.isEmpty()) {
1265  res.status = KLFERR_NOGSPROG;
1266  res.errorstr = QObject::tr("No gs executable given!\n", "KLFBackend");
1267  return res;
1268  }
1269 
1270  KLFBackendFilterProgram p(QLatin1String("gs (SVG)"), &settings, isMainThread, tempdir.path());
1271  p.resErrCodes[KLFFP_NOSTART] = KLFERR_GSSVG_NORUN;
1272  p.resErrCodes[KLFFP_NOEXIT] = KLFERR_GSSVG_NONORMALEXIT;
1273  p.resErrCodes[KLFFP_NOSUCCESSEXIT] = KLFERR_PROGERR_GSSVG;
1274  p.resErrCodes[KLFFP_NODATA] = KLFERR_GSSVG_NOOUTPUT;
1275  p.resErrCodes[KLFFP_DATAREADFAIL] = KLFERR_GSSVG_OUTPUTREADFAIL;
1276 
1277  p.addArgv(settings.gsexec);
1278  // unconditionally outline fonts, otherwise output is horrible
1279  p.addArgv(QStringList() << "-dNOCACHE" << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop" << "-sDEVICE=svg"
1280  << "-sOutputFile="+QDir::toNativeSeparators(fnGsSvg)
1281  << "-q" << "-dBATCH" << "-");
1282 
1283  // input: res.epsdata is the processed EPS file, or the raw EPS file if no post-processing
1284  ok = p.run(bboxepsdata, fnGsSvg, &gssvgdata);
1285  if (!ok) {
1286  p.errorToOutput(&res);
1287  return res;
1288  }
1289  }
1290 
1291  if (!has_userscript_output(us_outputs, "svg") && !our_skipfmts.contains("svg")) {
1292 
1293  ASSERT_HAVE_FORMATS_FOR("svg") ;
1294 
1295  // and now re-touch SVG generated by ghostscript that is not very clean...
1296  // find the first occurences of width='' and height='' and set them to the
1297  // appropriate width and heights given by BBox read earlier
1298 
1299  klfDebugf(("%s: gssvgdata/length=%d", KLF_FUNC_NAME, gssvgdata.size())) ;
1300 
1301  replace_svg_width_or_height(&gssvgdata, "width=", res.width_pt);
1302  replace_svg_width_or_height(&gssvgdata, "height=", res.height_pt);
1303 
1304  klfDebugf(("%s: now, gssvgdata/length=%d", KLF_FUNC_NAME, gssvgdata.size())) ;
1305 
1306  res.svgdata = gssvgdata;
1307  }
1308  } // end if(wantSVG)
1309 
1310  klfDbg("end of function.") ;
1311 
1312  return res;
1313 }
1314 
1315 
1316 
1317 static bool calculate_gs_eps_bbox(const QByteArray& epsData, const QString& epsFile, klfbbox *bbox,
1318  KLFBackend::klfOutput * resError, const KLFBackend::klfSettings& settings,
1319  bool isMainThread)
1320 {
1321  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
1322  // find correct bounding box of EPS file, using ghostscript
1323 
1324  int i;
1325 
1326  if (settings.gsexec.isEmpty()) {
1327  resError->status = KLFERR_NOGSPROG;
1328  resError->errorstr = QObject::tr("No gs executable given!\n", "KLFBackend");
1329  return false;
1330  }
1331 
1332  KLFBackendFilterProgram p(QLatin1String("GhostScript (bbox)"), &settings, isMainThread, settings.tempdir);
1333  p.resErrCodes[KLFFP_NOSTART] = KLFERR_GSBBOX_NORUN;
1334  p.resErrCodes[KLFFP_NOEXIT] = KLFERR_GSBBOX_NONORMALEXIT;
1335  p.resErrCodes[KLFFP_NOSUCCESSEXIT] = KLFERR_PROGERR_GSBBOX;
1336  p.resErrCodes[KLFFP_NODATA] = KLFERR_GSBBOX_NOOUTPUT;
1337  // p.resErrCodes[KLFFP_DATAREADFAIL] unused (used only for reading output files)
1338 
1339  p.setOutputStdout(true);
1340  p.setOutputStderr(true);
1341 
1342  QByteArray bboxdata;
1343 
1344  p.setArgv(QStringList() << settings.gsexec << "-dNOPAUSE" << "-dSAFER" << "-sDEVICE=bbox" << "-q" << "-dBATCH"
1345  << (epsFile.isEmpty() ? QString::fromLatin1("-") : epsFile));
1346 
1347  bool ok = p.run(epsData /*stdin*/, QString() /*no output file*/, &bboxdata/*collect stdout*/);
1348  if (!ok) {
1349  p.errorToOutput(resError);
1350  return false;
1351  }
1352 
1353  klfDbg("gs provided output:\n"<<bboxdata);
1354 
1355  // parse gs' bbox data
1356  QRegExp rx_gsbbox("%%HiResBoundingBox\\s*:\\s+" D_RX "\\s+" D_RX "\\s+" D_RX "\\s+" D_RX "");
1357  i = rx_gsbbox.indexIn(QString::fromLatin1(bboxdata));
1358  if (i < 0) {
1359  resError->status = KLFERR_GSBBOX_NOBBOX;
1360  resError->errorstr = QObject::tr("Ghostscript did not provide parsable BBox output!", "KLFBackend");
1361  return false;
1362  }
1363  bbox->x1 = rx_gsbbox.cap(1).toDouble();
1364  bbox->y1 = rx_gsbbox.cap(2).toDouble();
1365  bbox->x2 = rx_gsbbox.cap(3).toDouble();
1366  bbox->y2 = rx_gsbbox.cap(4).toDouble();
1367 
1368  return true;
1369 }
1370 
1371 
1372 static bool parse_bbox_values(const QString& str, klfbbox *bbox)
1373 {
1374  // parse bbox values
1375  QRegExp rx_bbvalues("" D_RX "\\s+" D_RX "\\s+" D_RX "\\s+" D_RX "");
1376  int i = rx_bbvalues.indexIn(str);
1377  if (i < 0) {
1378  return false;
1379  }
1380  bbox->x1 = rx_bbvalues.cap(1).toDouble();
1381  bbox->y1 = rx_bbvalues.cap(2).toDouble();
1382  bbox->x2 = rx_bbvalues.cap(3).toDouble();
1383  bbox->y2 = rx_bbvalues.cap(4).toDouble();
1384  return true;
1385 }
1386 
1387 static bool s_starts_with(const char * x, int len_x, const char *test, int len_test)
1388 {
1389  if (len_x < len_test)
1390  return false;
1391  return !strncmp(x, test, len_test);
1392 }
1393 
1394 static bool read_eps_bbox(const QByteArray& epsdata, klfbbox *bbox, KLFBackend::klfOutput * resError)
1395 {
1396  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
1397 
1398  static const char * hibboxtag = "%%HiResBoundingBox:";
1399  static const char * bboxtag = "%%BoundingBox:";
1400  static const int hibboxtaglen = strlen(hibboxtag);
1401  static const int bboxtaglen = strlen(bboxtag);
1402 
1403  // Read dvips' bounding box.
1404  QBuffer buf;
1405  buf.setData(epsdata);
1406  bool r = buf.open(QIODevice::ReadOnly | QIODevice::Text);
1407  if (!r) {
1408  klfWarning("What's going on!!?! can't open buffer for reading? Will Fail!!!") ;
1409  }
1410 
1411  QString nobboxerrstr =
1412  QObject::tr("DVIPS did not provide parsable %%BoundingBox: in its output!", "KLFBackend");
1413 
1414  char linebuffer[512];
1415  int n;
1416  bool gotepsbbox = false;
1417  int still_look_for_hiresbbox_lines = 5;
1418  while ((n = buf.readLine(linebuffer, sizeof(linebuffer)-1)) > 0) {
1419  if (gotepsbbox && still_look_for_hiresbbox_lines-- < 0) {
1420  // if we already got the %BoundingBox, and we've been looking at more than a certian number of lines
1421  // after that, abort because usually %BoundingBox and %HiResBoundingBox are together...
1422  klfDbg("stopped looking for hires-bbox.") ;
1423  break;
1424  }
1425  if (s_starts_with(linebuffer, n, hibboxtag, hibboxtaglen)) {
1426  // got hi-res bounding-box
1427  bool ok = parse_bbox_values(QString::fromLatin1(linebuffer+hibboxtaglen), bbox);
1428  if (!ok) {
1429  if (resError) {
1430  resError->status = KLFERR_DVIPS_OUTPUTNOBBOX;
1431  resError->errorstr = nobboxerrstr;
1432  }
1433  return false;
1434  }
1435  klfDbg("got hires-bbox.") ;
1436  // all ok, got hi-res bbox
1437  return true;
1438  }
1439  if (s_starts_with(linebuffer, n, bboxtag, bboxtaglen)) {
1440  // got bounding-box.
1441  bool ok = parse_bbox_values(QString::fromLatin1(linebuffer+bboxtaglen), bbox);
1442  if (!ok) {
1443  continue;
1444  }
1445  // stand by, continue in case we have a hi-res bbox.
1446  gotepsbbox = true;
1447  klfDbg("got normal bbox.") ;
1448  continue;
1449  }
1450  }
1451 
1452  // didn't get a hi-res bbox. see if we still got a regular %BoundingBox: and return that.
1453  if (gotepsbbox) {
1454  // bbox pointer is already set
1455  return true;
1456  }
1457 
1458  if (resError) {
1459  resError->status = KLFERR_DVIPS_OUTPUTNOBBOX;
1460  resError->errorstr = nobboxerrstr;
1461  }
1462  return false;
1463 }
1464 
1470 static void correct_eps_bbox(const QByteArray& rawepsdata, const klfbbox& bbox_corrected,
1471  const klfbbox& bbox_orig, double vectorscale, QRgb bgcolor,
1472  QByteArray * epsdatacorrected)
1473 {
1474  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
1475 
1476  static const char * bboxdecl = "%%BoundingBox:";
1477  static int bboxdecl_len = strlen(bboxdecl);
1478 
1479  double offx = bbox_corrected.x1 - bbox_orig.x1;
1480  double offy = bbox_corrected.y1 - bbox_orig.y1;
1481 
1482  // in raw EPS data, find '%%BoundingBox:' and length of the full BoundingBox instruction
1483  int i, len;
1484  char nl[] = "\0\0\0";
1485  i = rawepsdata.indexOf(bboxdecl);
1486  if (i < 0) {
1487  i = 0;
1488  len = 0;
1489  } else {
1490  int j = i+bboxdecl_len;
1491  while (j < (int)rawepsdata.size() && rawepsdata[j] != '\r' && rawepsdata[j] != '\n')
1492  ++j;
1493  len = j-i;
1494  if (rawepsdata[j] == '\r' && j < (int)rawepsdata.size()-1 && rawepsdata[j+1] == '\n') {
1495  nl[0] = '\r', nl[1] = '\n';
1496  } else {
1497  nl[0] = rawepsdata[j];
1498  }
1499  }
1500 
1501  double dwi = bbox_corrected.x2 * vectorscale;
1502  double dhi = bbox_corrected.y2 * vectorscale;
1503  int wi = (int)(dwi + 0.99999) ;
1504  int hi = (int)(dhi + 0.99999) ;
1505  char buffer[1024];
1506  int buffer_len;
1507  // recall that '%%' in printf is replaced by a single '%'...
1508  snprintf(buffer, sizeof(buffer)-1,
1509  "%%%%BoundingBox: 0 0 %d %d%s"
1510  "%%%%HiResBoundingBox: 0 0 %s %s%s",
1511  wi, hi, nl,
1512  klfFmtDoubleCC(dwi, 'g', 6), klfFmtDoubleCC(dhi, 'g', 6), nl);
1513  buffer_len = strlen(buffer);
1514 
1515  char backgroundfillps[1024] = "";
1516  if (qAlpha(bgcolor) > 0) {
1517  klfDbg("we have a bg color, so draw the background. bgcolor="<<klfFmt("%#10x", (uint)bgcolor));
1518  snprintf(backgroundfillps, sizeof(backgroundfillps)-1,
1519  // draw the background color, if any
1520  "newpath "
1521  "-2 -2 moveto "
1522  "%s -2 lineto "
1523  "%s %s lineto "
1524  "-2 %s lineto "
1525  "closepath "
1526  "gsave "
1527  "%s %s %s setrgbcolor "
1528  "fill "
1529  "grestore %s",
1530  klfFmtDoubleCC(dwi+1, 'g', 6),
1531  klfFmtDoubleCC(dwi+1, 'g', 6), klfFmtDoubleCC(dhi+1, 'g', 6),
1532  klfFmtDoubleCC(dhi+1, 'g', 6),
1533  // and the color, in RGB components:
1534  klfFmtDoubleCC(qRed(bgcolor)/255.0, 'f', 6),
1535  klfFmtDoubleCC(qGreen(bgcolor)/255.0, 'f', 6),
1536  klfFmtDoubleCC(qBlue(bgcolor)/255.0, 'f', 6),
1537  nl
1538  );
1539  }
1540 
1541  char buffer2[1024];
1542  //int buffer2_len;
1543  snprintf(buffer2, sizeof(buffer2)-1,
1544  "%s"
1545  "%%%%Page 1 1%s"
1546  "%%%%PageBoundingBox 0 0 %d %d%s"
1547  "<< /PageSize [%d %d] >> setpagedevice%s"
1548  "%s"
1549  "%s %s scale%s"
1550  "%s %s translate%s"
1551  ,
1552  nl,
1553  nl,
1554  wi, hi, nl,
1555  wi, hi, nl,
1556  backgroundfillps,
1557  klfFmtDoubleCC(vectorscale, 'f'), klfFmtDoubleCC(vectorscale, 'f'), nl,
1558  klfFmtDoubleCC(offx, 'f'), klfFmtDoubleCC(offy, 'f'), nl);
1559  //buffer2_len = strlen(buffer2);
1560 
1561  // char buffer2[128];
1562  // snprintf(buffer2, 127, "%sgrestore%s", nl, nl);
1563 
1564  klfDbg("buffer is `"<<buffer<<"', length="<<buffer_len) ;
1565  klfDbg("rawepsdata has length="<<rawepsdata.size()) ;
1566 
1567  // and modify the raw EPS data, to replace "%%BoundingBox:" instruction by our stuff...
1568  QByteArray neweps;
1569  neweps = rawepsdata;
1570  neweps.replace(i, len, buffer);
1571 
1572  const char * endsetupstr = "%%EndSetup";
1573  int i2 = neweps.indexOf(endsetupstr);
1574  if (i2 < 0)
1575  i2 = i + buffer_len; // add our info after modified %%BoundingBox'es instructions if %%EndSetup not found
1576  else
1577  i2 += strlen(endsetupstr);
1578 
1579  neweps.replace(i2, 0, buffer2);
1580 
1581  klfDbg("neweps has now length="<<neweps.size());
1582  klfDebugf(("New eps bbox is [0 0 %.6g %.6g] with translate [%.6g %.6g] and scale %.6g.",
1583  dwi, dhi, offx, offy, vectorscale));
1584 
1585  *epsdatacorrected = neweps;
1586 }
1587 
1588 
1589 static void replace_svg_width_or_height(QByteArray *svgdata, const char * attreq, double val)
1590 {
1591  QByteArray & svgdataref = * svgdata;
1592 
1593  int i = svgdataref.indexOf(attreq);
1594  int j = i;
1595  while (j < (int)svgdataref.size() && (!isspace(svgdataref[j]) && svgdataref[j] != '>'))
1596  ++j;
1597 
1598  char buffer[1024];
1599  snprintf(buffer, sizeof(buffer)-1, "%s'%s'", attreq, klfFmtDoubleCC(val, 'f', 3));
1600 
1601  svgdata->replace(i, j-i, buffer);
1602 }
1603 
1604 
1605 
1606 
1607 
1609 {
1610  return a.latex == b.latex &&
1611  a.mathmode == b.mathmode &&
1612  a.preamble == b.preamble &&
1613  fabs(a.fontsize - b.fontsize) < 0.001 &&
1614  a.fg_color == b.fg_color &&
1615  a.bg_color == b.bg_color &&
1616  a.dpi == b.dpi &&
1617  fabs(a.vectorscale - b.vectorscale) < 0.001 &&
1618  a.bypassTemplate == b.bypassTemplate &&
1619  a.userScript == b.userScript;
1620 }
1621 
1623 {
1624  return
1625  a.tempdir == b.tempdir &&
1626  a.latexexec == b.latexexec &&
1627  a.dvipsexec == b.dvipsexec &&
1628  a.gsexec == b.gsexec &&
1629  a.epstopdfexec == b.epstopdfexec &&
1630  a.tborderoffset == b.tborderoffset &&
1631  a.rborderoffset == b.rborderoffset &&
1632  a.bborderoffset == b.bborderoffset &&
1633  a.lborderoffset == b.lborderoffset &&
1635  a.outlineFonts == b.outlineFonts &&
1636  a.wantRaw == b.wantRaw &&
1637  a.wantPDF == b.wantPDF &&
1638  a.wantSVG == b.wantSVG &&
1639  a.execenv == b.execenv &&
1641 }
1642 
1643 
1645 {
1646  if (output != NULL)
1647  return availableSaveFormats(*output);
1648 
1649  QStringList fmts;
1650  fmts << "PNG" << "PS" << "EPS" << "DVI" << "PDF" << "SVG";
1651 
1653  foreach (QByteArray f, imgfmts) {
1654  f = f.trimmed().toUpper();
1655  if (f == "JPG")
1656  f = "JPEG"; // only report "JPEG", not both "JPG" and "JPEG"
1657  if (fmts.contains(f))
1658  continue;
1659  fmts << QString::fromLatin1(f);
1660  }
1661  return fmts;
1662 }
1663 
1665 {
1666  QStringList formats;
1667  // Most popular formats on top of list (pdf, png)
1668  if (!klfoutput.pdfdata.isEmpty())
1669  formats << "PDF";
1670  if (!klfoutput.pngdata.isEmpty())
1671  formats << "PNG";
1672  if (!klfoutput.svgdata.isEmpty())
1673  formats << "SVG";
1674  if (!klfoutput.epsdata.isEmpty())
1675  formats << "PS" << "EPS";
1676  if (!klfoutput.dvidata.isEmpty())
1677  formats << "DVI";
1678  // and, of course, all Qt-available image formats
1680  foreach (QByteArray f, imgfmts) {
1681  f = f.trimmed().toUpper();
1682  if (f == "JPG")
1683  f = "JPEG"; // only report "JPEG", not both "JPG" and "JPEG"
1684  if (formats.contains(f))
1685  continue;
1686  formats << QString::fromLatin1(f);
1687  }
1688  return formats;
1689 }
1690 
1691 bool KLFBackend::saveOutputToDevice(const klfOutput& klfoutput, QIODevice *device,
1692  const QString& fmt, QString *errorStringPtr)
1693 {
1694  QString format = fmt.trimmed().toUpper();
1695 
1696  // now choose correct data source and write to fout
1697  if (format == "PNG") {
1698  device->write(klfoutput.pngdata);
1699  } else if (format == "EPS" || format == "PS") {
1700  device->write(klfoutput.epsdata);
1701  } else if (format == "DVI") {
1702  device->write(klfoutput.dvidata);
1703  } else if (format == "PDF") {
1704  if (klfoutput.pdfdata.isEmpty()) {
1705  QString error = QObject::tr("PDF format is not available!",
1706  "KLFBackend::saveOutputToFile");
1707  qWarning("%s", qPrintable(error));
1708  if (errorStringPtr != NULL)
1709  errorStringPtr->operator=(error);
1710  return false;
1711  }
1712  device->write(klfoutput.pdfdata);
1713  } else if (format == "SVG") {
1714  if (klfoutput.svgdata.isEmpty()) {
1715  QString error = QObject::tr("SVG format is not available!",
1716  "KLFBackend::saveOutputToFile");
1717  qWarning("%s", qPrintable(error));
1718  if (errorStringPtr != NULL)
1719  errorStringPtr->operator=(error);
1720  return false;
1721  }
1722  device->write(klfoutput.svgdata);
1723  } else {
1724  bool res = klfoutput.result.save(device, format.toLatin1());
1725  if ( ! res ) {
1726  QString errstr = QObject::tr("Unable to save image in format `%1'!",
1727  "KLFBackend::saveOutputToDevice").arg(format);
1728  qWarning("%s", qPrintable(errstr));
1729  if (errorStringPtr != NULL)
1730  *errorStringPtr = errstr;
1731  return false;
1732  }
1733  }
1734 
1735  return true;
1736 }
1737 
1738 bool KLFBackend::saveOutputToFile(const klfOutput& klfoutput, const QString& fileName,
1739  const QString& fmt, QString *errorStringPtr)
1740 {
1741  QString format = fmt;
1742  // determine format first
1743  if (format.isEmpty() && !fileName.isEmpty()) {
1744  QFileInfo fi(fileName);
1745  if ( ! fi.suffix().isEmpty() )
1746  format = fi.suffix();
1747  }
1748  if (format.isEmpty())
1749  format = QLatin1String("PNG");
1750  format = format.trimmed().toUpper();
1751  // got format. choose output now and prepare write
1752  QFile fout;
1753  if (fileName.isEmpty() || fileName == "-") {
1754  if ( ! fout.open(stdout, QIODevice::WriteOnly) ) {
1755  QString error = QObject::tr("Unable to open stdout for write! Error: %1",
1756  "KLFBackend::saveOutputToFile").arg(fout.error());
1757  qWarning("%s", qPrintable(error));
1758  if (errorStringPtr != NULL)
1759  *errorStringPtr = error;
1760  return false;
1761  }
1762  } else {
1763  fout.setFileName(fileName);
1764  if ( ! fout.open(QIODevice::WriteOnly) ) {
1765  QString error = QObject::tr("Unable to write to file `%1'! Error: %2",
1766  "KLFBackend::saveOutputToFile")
1767  .arg(fileName).arg(fout.error());
1768  qWarning("%s", qPrintable(error));
1769  if (errorStringPtr != NULL)
1770  *errorStringPtr = error;
1771  return false;
1772  }
1773  }
1774 
1775  return saveOutputToDevice(klfoutput, &fout, format, errorStringPtr);
1776 }
1777 
1778 
1779 bool KLFBackend::detectSettings(klfSettings *settings, const QString& extraPath, bool isMainThread)
1780 {
1782 
1783  QStringList stdextrapaths;
1784  int k, j;
1785  for (k = 0; standard_extra_paths[k] != NULL; ++k) {
1786  stdextrapaths.append(standard_extra_paths[k]);
1787  }
1788  QString extra_paths = stdextrapaths.join(QString("")+KLF_PATH_SEP);
1789  if (!extraPath.isEmpty())
1790  extra_paths += KLF_PATH_SEP + extraPath;
1791 
1792  // temp dir
1794 
1795  // sensible defaults
1796  settings->lborderoffset = 1;
1797  settings->tborderoffset = 1;
1798  settings->rborderoffset = 1;
1799  settings->bborderoffset = 1;
1800 
1801  settings->epstopdfexec = QString(); // obsolete, no longer used
1802 
1803  // you'll want PDF
1804  settings->wantPDF = true;
1805 
1806  settings->wantSVG = false; // will be set to TRUE once we verify 'gs' available devices information
1807 
1808  // find executables
1809  struct { QString * target_setting; QStringList prog_names; } progs_to_find[] = {
1810  { & settings->latexexec, progLATEX },
1811  { & settings->dvipsexec, progDVIPS },
1812  { & settings->gsexec, progGS },
1813  // { & settings->epstopdfexec, progEPSTOPDF },
1814  { NULL, QStringList() }
1815  };
1816  // replace @executable_path in extra_paths
1817  klfDbg(klfFmtCC("Our base extra paths are: %s", qPrintable(extra_paths))) ;
1818  QString ourextrapaths = extra_paths;
1819  ourextrapaths.replace("@executable_path", qApp->applicationDirPath());
1820  klfDbg(klfFmtCC("Our extra paths are: %s", qPrintable(ourextrapaths))) ;
1821  // and actually search for those executables
1822  for (k = 0; progs_to_find[k].target_setting != NULL; ++k) {
1823  klfDbg("Looking for "+progs_to_find[k].prog_names.join(" or ")) ;
1824  for (j = 0; j < (int)progs_to_find[k].prog_names.size(); ++j) {
1825  klfDbg("Testing `"+progs_to_find[k].prog_names[j]+"'") ;
1826  *progs_to_find[k].target_setting
1827  = klfSearchPath(progs_to_find[k].prog_names[j], ourextrapaths);
1828  if (!progs_to_find[k].target_setting->isEmpty()) {
1829  klfDbg("Found! at `"+ *progs_to_find[k].target_setting+"'") ;
1830  break; // found a program
1831  }
1832  }
1833  }
1834 
1835  bool r1 = detectOptionSettings(settings, isMainThread);
1836 
1837  bool result_failure =
1838  settings->tempdir.isEmpty() || settings->latexexec.isEmpty() || settings->dvipsexec.isEmpty() ||
1839  settings->gsexec.isEmpty() || !r1;
1840 
1841  return !result_failure;
1842 }
1843 
1844 KLF_EXPORT bool KLFBackend::detectOptionSettings(klfSettings * settings, bool isMainThread)
1845 {
1847 
1848  bool r0 = klf_detect_execenv(settings);
1849  if (!r0) {
1850  return false;
1851  }
1852 
1853  settings->wantSVG = false;
1854 
1855  bool ok = true;
1856  if (settings->gsexec.length()) {
1857  initGsInfo(settings, isMainThread);
1858  if (!gsInfo.contains(settings->gsexec)) {
1859  klfWarning("Cannot get 'gs' devices information with "<<(settings->gsexec+" --version/--help"));
1860  ok = false;
1861  } else if (gsInfo[settings->gsexec].availdevices.contains("svg")) {
1862  settings->wantSVG = true;
1863  }
1864  }
1865 
1866  return ok;
1867 }
1868 
1869 
1871 {
1873 
1874  // detect mgs.exe as ghostscript and setup its environment properly
1875  QFileInfo gsfi(settings->gsexec);
1876  if (gsfi.fileName() == "mgs.exe") {
1877  QString mgsenv = QString("")
1878  + QDir::toNativeSeparators(gsfi.absolutePath()+"/../../ghostscript/base")
1880  + QDir::toNativeSeparators(gsfi.absolutePath()+"/../../fonts");
1881  settings->execenv = klfSetEnvironmentVariable(settings->execenv, "MIKTEX_GS_LIB", mgsenv);
1882  klfDbg("Adjusting environment for mgs.exe: `MIKTEX_GS_LIB="+mgsenv+"'") ;
1883  }
1884 
1885  return true;
1886 }
1887 
1888 
1889 
1890 
1891 // static
1892 void initGsInfo(const KLFBackend::klfSettings *settings, bool isMainThread)
1893 {
1894  KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
1895 
1896  if (gsInfo.contains(settings->gsexec)) // info already cached
1897  return;
1898 
1899  if (settings->gsexec.isEmpty()) {
1900  // no GS executable given
1901  return;
1902  }
1903 
1904  QString gsver;
1905  { // test 'gs' version, to see if we can provide SVG data
1906  KLFBackendFilterProgram p(QLatin1String("gs (test version)"), settings, isMainThread, settings->tempdir);
1907  // p.resErrCodes[KLFFP_NOSTART] = ;
1908  // p.resErrCodes[KLFFP_NOEXIT] = ;
1909  // p.resErrCodes[KLFFP_NOSUCCESSEXIT] = ;
1910  // p.resErrCodes[KLFFP_NODATA] = ;
1911  // p.resErrCodes[KLFFP_DATAREADFAIL] = ;
1912 
1913  p.setExecEnviron(settings->execenv);
1914 
1915  // I think there is a problem with app events processing if this is run during application start-up
1916  p.setProcessAppEvents(false);
1917 
1918  p.setArgv(QStringList() << settings->gsexec << "--version");
1919 
1920  QByteArray ba_gsver;
1921  bool ok = p.run(QString(), &ba_gsver);
1922  if (ok) {
1923  gsver = QString::fromLatin1(ba_gsver);
1924  klfDbg("gs version text (untrimmed): "<<gsver) ;
1925 
1926  gsver = gsver.trimmed();
1927  }
1928  }
1929 
1930  QString gshelp;
1931  KLFStringSet availdevices;
1932  { // test 'gs' version, to see if we can provide SVG data
1933  KLFBackendFilterProgram p(QLatin1String("gs (query help)"), settings, isMainThread, settings->tempdir);
1934  // p.resErrCodes[KLFFP_NOSTART] = ;
1935  // p.resErrCodes[KLFFP_NOEXIT] = ;
1936  // p.resErrCodes[KLFFP_NOSUCCESSEXIT] = ;
1937  // p.resErrCodes[KLFFP_NODATA] = ;
1938  // p.resErrCodes[KLFFP_DATAREADFAIL] = ;
1939 
1940  QStringList ee = settings->execenv;
1941  // make sure we have gs' output in english
1942  ee = klfSetEnvironmentVariable(ee, QLatin1String("LANG"), QLatin1String("en_US.UTF-8"));
1943  p.setExecEnviron(ee);
1944 
1945  // I think there is a problem with app events processing if this is run during application start-up
1946  p.setProcessAppEvents(false);
1947 
1948  p.setArgv(QStringList() << settings->gsexec << "--help");
1949 
1950  QByteArray ba_gshelp;
1951  bool ok = p.run(QString(), &ba_gshelp);
1952  if (ok) {
1953  gshelp = QString::fromLatin1(ba_gshelp);
1954 
1955  klfDbg("gs help text: "<<gshelp) ;
1956  // parse available devices
1957  const char * availdevstr = "Available devices:";
1958  int k = gshelp.indexOf(availdevstr, 0, Qt::CaseInsensitive) ;
1959  if (k == -1) {
1960  klfWarning("Unable to parse gs' available devices.") ;
1961  } else {
1962  k += strlen(availdevstr); // point to after 'available devices:' string
1963  // find end of available devices list, given by a line not starting with whitespace
1964  int kend = gshelp.indexOf(QRegExp("\\n\\S"), k);
1965  if (kend == -1)
1966  kend = gshelp.length();
1967  // now split this large string into the devices list
1968  QStringList devlist = gshelp.mid(k, kend-k).split(QRegExp("(\\s|[\r\n])+"), QString::SkipEmptyParts);
1969  availdevices = KLFStringSet::fromList(devlist);
1970  klfDbg("Detected devices: "<<availdevices) ;
1971  }
1972  }
1973  }
1974 
1975  int gsvermaj = -1;
1976  int gsvermin = -1;
1977  QRegExp rx_version("^(\\d+)\\.(\\d+)");
1978  int foundver = rx_version.indexIn(gsver);
1979  if (foundver >= 0) {
1980  gsvermaj = rx_version.cap(1).toInt();
1981  gsvermin = rx_version.cap(2).toInt();
1982  }
1983 
1984  klfDbg("GS Version: "<<gsver<<" = "<<gsvermaj<<"."<<gsvermin);
1985 
1986  GsInfo i;
1987  i.version = gsver;
1988  i.version_maj = gsvermaj;
1989  i.version_min = gsvermin;
1990  i.help = gshelp;
1991  i.availdevices = availdevices;
1992 
1993  gsInfo[settings->gsexec] = i;
1994 }
1995 
1996 
1997 
1998 
1999 
2000 KLF_EXPORT QStringList klfSettingsToEnvironmentForUserScript(const KLFBackend::klfSettings& settings)
2001 {
2002  QStringList env;
2003  env << "KLF_SETTINGS_BORDEROFFSET=" + klfFmt("%.3g,%.3g,%.3g,%.3g pt", settings.tborderoffset,
2004  settings.rborderoffset, settings.bborderoffset, settings.lborderoffset)
2005  << "KLF_SETTINGS_OUTLINEFONTS=" + QString::fromLatin1(settings.outlineFonts ? "1" : "0")
2006  << "KLF_SETTINGS_CALCEPSBOUNDINGBOX=" + QString::fromLatin1(settings.calcEpsBoundingBox ? "1" : "0")
2007  << "KLF_SETTINGS_WANT_RAW=" + QString::fromLatin1(settings.wantRaw ? "1" : "0")
2008  << "KLF_SETTINGS_WANT_PDF=" + QString::fromLatin1(settings.wantPDF ? "1" : "0")
2009  << "KLF_SETTINGS_WANT_SVG=" + QString::fromLatin1(settings.wantSVG ? "1" : "0");
2010  return env;
2011 }
2012 
2013 KLF_EXPORT QStringList klfInputToEnvironmentForUserScript(const KLFBackend::klfInput& in)
2014 {
2015  QStringList env;
2016  QString fgcol = QString("rgba(%1,%2,%3,%4)").arg(qRed(in.fg_color))
2017  .arg(qGreen(in.fg_color)).arg(qBlue(in.fg_color)).arg(qAlpha(in.fg_color));
2018  QString bgcol = QString("rgba(%1,%2,%3,%4)").arg(qRed(in.bg_color))
2019  .arg(qGreen(in.bg_color)).arg(qBlue(in.bg_color)).arg(qAlpha(in.bg_color));
2020  env << "KLF_INPUT_LATEX=" + in.latex
2021  << "KLF_INPUT_MATHMODE=" + in.mathmode
2022  << "KLF_INPUT_PREAMBLE=" + in.preamble
2023  << "KLF_INPUT_FONTSIZE=" + QString::number(in.fontsize)
2024  << "KLF_INPUT_FG_COLOR_WEB=" + QColor(in.fg_color).name()
2025  << "KLF_INPUT_FG_COLOR_RGBA=" + fgcol
2026  << "KLF_INPUT_BG_COLOR_TRANSPARENT=" + QString::fromLatin1(qAlpha(in.bg_color) > 50 ? "0" : "1")
2027  << "KLF_INPUT_BG_COLOR_WEB=" + QColor(in.bg_color).name()
2028  << "KLF_INPUT_BG_COLOR_RGBA=" + bgcol
2029  << "KLF_INPUT_DPI=" + QString::number(in.dpi)
2030  << "KLF_INPUT_VECTORSCALE=" + klfFmt("%.6g", in.vectorscale)
2031  << "KLF_INPUT_USERSCRIPT=" + in.userScript
2032  << "KLF_INPUT_BYPASS_TEMPLATE=" + QString::number(in.bypassTemplate)
2033  ;
2034 
2035  // and add custom user parameters
2037  for (cit = in.userScriptParam.constBegin(); cit != in.userScriptParam.constEnd(); ++cit) {
2038  env << "KLF_ARG_"+cit.key() + "=" + cit.value();
2039  }
2040 
2041  return env;
2042 }
virtual QString generateTemplate(const klfInput &input, const klfSettings &settings)
Definition: klfbackend.cpp:331
QStringList spitsOut() const
List of formats that this script will generate.
QStringList skipFormats() const
List of formats that klfbackend should not attempt to generate.
static QStringList availableSaveFormats(const klfOutput *output=NULL)
Get a list of available output formats.
static bool detectOptionSettings(klfSettings *settings, bool isMainThread=true)
Detects additional options (e.g. klfSettings::wantSVG) that depend on specific program versions.
static bool detectSettings(klfSettings *settings, const QString &extraPath=QString(), bool isMainThread=true)
Detects the system settings and stores the guessed values in settings.
static klfOutput getLatexFormula(const klfInput &in, const klfSettings &settings, bool isMainThread=true)
The function that processes everything.
Definition: klfbackend.cpp:488
static bool saveOutputToDevice(const klfOutput &output, QIODevice *device, const QString &format=QString("PNG"), QString *errorString=NULL)
Saves the given output into the given device.
static bool saveOutputToFile(const klfOutput &output, const QString &fileName, const QString &format=QString(), QString *errorString=NULL)
Save the output to image file.
void collectStderrTo(QByteArray *stderrstore)
virtual QString resultErrorString() const
virtual int resultStatus() const
void collectStdoutTo(QByteArray *stdoutstore)
void setProcessAppEvents(bool processEvents)
Write metainfo to PDF files via pdfmarks for ghostscript.
Definition: klfbackend.h:729
QString loadField(const QString &)
Definition: klfbackend.cpp:263
void savePDFField(const QString &k, const QString &v)
Saves the field without prepending 'KLF' to the key.
Definition: klfbackend.cpp:275
QString userScriptPath() const
e.g. "/path/to/klffeynmf.klfuserscript"
Definition of class KLFBackend.
#define KLFERR_GSPOSTPROC_NOOUTPUT
Program 'gs' didn't provide any data after post-processing EPS file.
Definition: klfbackend.h:105
#define KLFERR_GSPNG_NONORMALEXIT
Program 'gs' didn't exit noramally (crashed) while generating PNG (see also KLFERR_PROGERR_GSPNG)
Definition: klfbackend.h:119
#define KLFERR_PROGERR_GSPOSTPROC
gs exited with non-zero status while post-processing EPS file (page size, font outlines)
Definition: klfbackend.h:179
#define KLFERR_GSPDF_NOOUTPUT
No PDF file appeared after running 'gs'.
Definition: klfbackend.h:139
#define KLFERR_LATEX_NOOUTPUT
No .dvi file appeared after runnig latex program.
Definition: klfbackend.h:65
#define KLFERR_GSBBOX_NOOUTPUT
Program 'gs' didn't provide any output.
Definition: klfbackend.h:97
#define KLFERR_GSPOSTPROC_NONORMALEXIT
Program 'gs' crashed while post-processing EPS file (see also KLFERR_PROGERR_GSPOSTPROC)
Definition: klfbackend.h:103
#define KLFERR_GSSVG_NONORMALEXIT
Program 'gs' didn't exit noramally (crashed) while generating SVG (see also KLFERR_PROGERR_GSSVG)
Definition: klfbackend.h:153
#define KLFERR_PROGERR_LATEX
latex exited with a non-zero status
Definition: klfbackend.h:173
#define KLFERR_MISSINGMATHMODETHREEDOTS
The "..." is missing in math mode string.
Definition: klfbackend.h:53
#define KLFERR_PROGERR_GSPDF
gs exited with non-zero status while producing PDF
Definition: klfbackend.h:187
#define KLFERR_GSBBOX_NOBBOX
Program 'gs' calculating bbox didn't provide parsable output.
Definition: klfbackend.h:99
#define KLFERR_GSBBOX_NORUN
Program 'gs' cannot be executed to calculate bounding box.
Definition: klfbackend.h:93
#define KLFERR_LATEX_OUTPUTREADFAIL
Error while opening .dvi file for reading.
Definition: klfbackend.h:69
#define KLFERR_PROGERR_GSPNG
gs exited with a non-zero status while producing PNG
Definition: klfbackend.h:183
#define KLFERR_NOLATEXPROG
obsolete, same as KLFERR_LATEX_NORUN
Definition: klfbackend.h:59
#define KLFERR_LATEX_NORUN
Error while launching the given latex program.
Definition: klfbackend.h:57
#define KLFERR_DVIPS_OUTPUTREADFAIL
Error while opening .eps file for reading.
Definition: klfbackend.h:85
#define KLFERR_GSPOSTPROC_NORUN
Program 'gs' cannot be executed to post-process EPS file (page size, outline fonts)
Definition: klfbackend.h:101
#define KLFERR_GSSVG_NOOUTPUT
No SVG file appeared after running 'gs'.
Definition: klfbackend.h:155
#define KLFERR_GSSVG_NORUN
Program 'gs' couldn't be executed to generate SVG.
Definition: klfbackend.h:151
#define KLFERR_NODVIPSPROG
obsolete, same as KLFERR_DVIPS_NORUN
Definition: klfbackend.h:75
#define KLFERR_PROGERR_USERSCRIPT
user wrapper script exited with non-zero status
Definition: klfbackend.h:193
#define KLFERR_DVIPS_OUTPUTNOBBOX
Error while reading/parsing %BoundingBox: in dvips output.
Definition: klfbackend.h:89
#define KLFERR_DVIPS_NORUN
Error while launching the given dvips program.
Definition: klfbackend.h:73
#define KLFERR_GSPNG_NOOUTPUT
No PNG file appeared after running 'gs'.
Definition: klfbackend.h:123
#define KLFERR_USERSCRIPT_NORUN
Failed to execute user wrapper script.
Definition: klfbackend.h:159
#define KLFERR_GSPNG_OUTPUTREADFAIL
Failed to read PNG file produced by 'gs'.
Definition: klfbackend.h:127
#define KLFERR_DVIPS_NONORMALEXIT
dvips program did not exit properly (program killed) (see also KLFERR_PROGERR_DVIPS)
Definition: klfbackend.h:77
#define KLFERR_GSBBOX_NONORMALEXIT
Program 'gs' crashed while calculating bbox (see also KLFERR_PROGERR_GSBBOX)
Definition: klfbackend.h:95
#define KLFERR_GSPNG_NORUN
Program 'gs' couldn't be executed to generate PNG.
Definition: klfbackend.h:115
#define KLFERR_NOERROR
No Error.
Definition: klfbackend.h:46
#define KLFERR_PROGERR_DVIPS
dvips exited with a non-zero status
Definition: klfbackend.h:175
#define KLFERR_GSPDF_NORUN
Program 'gs' couldn't be executed to generate PDF.
Definition: klfbackend.h:133
#define KLFERR_PDFMARKSWRITEFAIL
Error while opening pdfmarks file for writing.
Definition: klfbackend.h:131
#define KLFERR_GSPDF_NONORMALEXIT
Program 'gs' didn't exit noramally (crashed) while generating PDF (see also KLFERR_PROGERR_GSPDF)
Definition: klfbackend.h:135
#define KLFERR_GSSVG_OUTPUTREADFAIL
Failed to read SVG file produced by 'gs'.
Definition: klfbackend.h:157
KLF_EXPORT bool operator==(const KLFBackend::klfInput &a, const KLFBackend::klfInput &b)
#define KLFERR_GSPDF_OUTPUTREADFAIL
Failed to read PDF file produced by 'gs'.
Definition: klfbackend.h:143
#define KLFERR_PROGERR_GSBBOX
gs exited with non-zero status while calculating bbox of EPS file generated by dvips
Definition: klfbackend.h:177
KLF_EXPORT bool klf_detect_execenv(KLFBackend::klfSettings *settings)
detects any additional settings to environment variables
#define KLFERR_NOGSVERSION
Failed to query gs version.
Definition: klfbackend.h:147
#define KLFERR_LATEX_NONORMALEXIT
latex program did not exit properly (program killed) (see also KLFERR_PROGERR_LATEX)
Definition: klfbackend.h:61
#define KLFERR_GSPOSTPROC_NOOUTLINEFONTS
'gs' cannot outline fonts: need version <= 9.07 (pswrite -dNOCACHE) or >= 9.15 (ps2write -dNoOutputFo...
Definition: klfbackend.h:107
#define KLFERR_DVIPS_NOOUTPUT
no .eps file appeared after running dvips program
Definition: klfbackend.h:81
#define KLFERR_TEMPDIR_FAIL
Failed to create the temporary directory.
Definition: klfbackend.h:49
#define KLFERR_NOGSPROG
obsolete, same as KLFERR_GSPNG_NORUN
Definition: klfbackend.h:117
#define KLFERR_MISSINGLATEXFORMULA
No LaTeX formula is specified (empty string)
Definition: klfbackend.h:51
#define KLFERR_PROGERR_GSSVG
gs exited with non-zero status while producing SVG
Definition: klfbackend.h:191
#define KLFERR_GSSVG_NOSVG
This version of gs cannot produce SVG.
Definition: klfbackend.h:149
#define KLFERR_GSPOSTPROC_OUTPUTREADFAIL
Couldn't read output provided by 'gs' program after post-processing EPS file.
Definition: klfbackend.h:111
#define KLFERR_TEXWRITEFAIL
Error while opening .tex file for writing.
Definition: klfbackend.h:55
Defines the KLFBlockProcess class.
KLF_EXPORT QByteArray klfEscapedToData(const QByteArray &data, char escapechar)
const char * format
KLF_EXPORT QByteArray klfDataToEscaped(const QByteArray &value_ba, char escapechar)
KLF_EXPORT QByteArray klfSaveVariantToText(const QVariant &value, bool saveListAndMapsAsXML, QByteArray *savedType, QByteArray *savedListOrMapType)
#define KLF_DEBUG_TIME_BLOCK(msg)
#define klfWarning(streamableItems)
#define KLF_DEBUG_BLOCK(msg)
#define KLF_ASSERT_NOT_NULL(ptr, msg, failaction)
#define KLF_ASSERT_CONDITION(expr, msg, failaction)
#define KLF_FUNC_NAME
#define klfDbg(streamableItems)
#define klfDebugf(arglist)
KLF_EXPORT int klfVersionCompare(const QString &v1, const QString &v2)
KLF_EXPORT QByteArray klfFmt(const char *fmt, va_list pp)
#define klfFmtDoubleCC
#define klfFmtCC
#define KLF_EXPORT
#define KLF_PATH_SEP
KLF_EXPORT void klfSetEnvironmentVariable(QStringList *env, const QString &var, const QString &value)
void klfMergeEnvironment(QStringList *env, const QStringList &addvars, const QStringList &pathvars, uint actions)
KLF_EXPORT QStringList klfCurrentEnvironment()
KLF_EXPORT QString klfSearchPath(const QString &programName, const QString &extra_path)
QVariantMap klfMapToVariantMap(const QMap< QString, T > &map)
KlfEnvPathPrepend
KlfEnvMergeExpandVars
virtual bool open(OpenMode flags)
void setData(const QByteArray &data)
QByteArray & append(char ch)
int indexOf(char ch, int from) const
bool isEmpty() const
QByteArray & replace(int pos, int len, const char *after)
int size() const
QByteArray toUpper() const
QByteArray trimmed() const
QString name() const
QString fromNativeSeparators(const QString &pathName)
QString tempPath()
QString toNativeSeparators(const QString &pathName)
bool exists() const
virtual bool open(OpenMode mode)
void setFileName(const QString &name)
FileError error() const
QString absoluteFilePath() const
QString absolutePath() const
QString fileName() const
QString filePath() const
QString suffix() const
bool load(QIODevice *device, const char *format)
bool loadFromData(const uchar *data, int len, const char *format)
bool save(const QString &fileName, const char *format, int quality) const
void setText(const QString &key, const QString &text)
QString text(const QString &key) const
QList< QByteArray > supportedImageFormats()
qint64 readLine(char *data, qint64 maxSize)
qint64 write(const char *data, qint64 maxSize)
void append(const T &value)
void removeAt(int i)
const_iterator constBegin() const
const_iterator constEnd() const
bool contains(const Key &key) const
const Key key(const T &value, const Key &defaultKey) const
int remove(const Key &key)
const T value(const Key &key, const T &defaultValue) const
QString tr(const char *sourceText, const char *disambiguation, int n)
QProcessEnvironment systemEnvironment()
bool contains(const T &value) const
QSet< T > fromList(const QList< T > &list)
bool isEmpty() const
bool remove(const T &value)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const
bool contains(QChar ch, Qt::CaseSensitivity cs) const
QString fromLatin1(const char *str, int size)
QString fromUtf8(const char *str, int size)
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
bool isEmpty() const
int length() const
QString mid(int position, int n) const
QString number(int n, int base)
QString & replace(int position, int n, QChar after)
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
QByteArray toLatin1() const
QString toUpper() const
QByteArray toUtf8() const
QString trimmed() const
bool contains(const QString &str, Qt::CaseSensitivity cs) const
int indexOf(const QRegExp &rx, int from) const
QString join(const QString &separator) const
bool isValid() const
QString path() const
QTextCodec * codecForName(const QByteArray &name)
QByteArray fromUnicode(const QString &str) const
Specific input to KLFBackend::getLatexFormula()
Definition: klfbackend.h:307
QMap< QString, QString > userScriptParam
Arbitrary parameters to pass to user script.
Definition: klfbackend.h:364
unsigned long bg_color
Definition: klfbackend.h:334
QString userScript
A Path to a user script that acts as wrapper around LaTeX.
Definition: klfbackend.h:356
KLFBackend::getLatexFormula() result.
Definition: klfbackend.h:371
klfSettings settings
The settings that this output was generated with.
Definition: klfbackend.h:398
double height_pt
Width in points of the resulting equation.
Definition: klfbackend.h:453
QByteArray pdfdata
data for a pdf file
Definition: klfbackend.h:447
QByteArray epsdata_raw
data for an (eps-)postscript file.
Definition: klfbackend.h:434
QByteArray epsdata_bbox
data for an (eps-)postscript file.
Definition: klfbackend.h:441
QByteArray pngdata
the data for a png file (re-processed with meta information)
Definition: klfbackend.h:428
int status
A code describing the status of the request.
Definition: klfbackend.h:381
klfInput input
The input parameters used to generate this output.
Definition: klfbackend.h:396
QByteArray epsdata
data for an (eps-)postscript file.
Definition: klfbackend.h:445
QByteArray pngdata_raw
the data for a png file (exact gs output content)
Definition: klfbackend.h:408
double width_pt
Width in points of the resulting equation.
Definition: klfbackend.h:451
QImage result
The actual resulting image.
Definition: klfbackend.h:393
QByteArray svgdata
data for a SVG file, if ghostscript supports SVG
Definition: klfbackend.h:449
QByteArray dvidata
The DVI file data outputted by latex executable.
Definition: klfbackend.h:401
QString errorstr
An explicit error string.
Definition: klfbackend.h:390
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:219
TemplateGenerator * templateGenerator
Definition: klfbackend.h:294

Generated by doxygen 1.9.1