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

Generated by doxygen 1.8.13