00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <stdio.h>
00025 #include <stdlib.h>
00026 #include <sys/time.h>
00027
00028 #include <qapplication.h>
00029 #include <qregexp.h>
00030 #include <qfile.h>
00031 #include <qdatetime.h>
00032 #include <qtextstream.h>
00033 #include <qbuffer.h>
00034 #include <qdir.h>
00035
00036 #include "klfblockprocess.h"
00037 #include "klfbackend.h"
00038
00039
00040 #include "klfqt34common.h"
00041
00042
00073
00074
00075 #ifdef KLF_EXTRA_SEARCH_PATHS
00076 # define EXTRA_PATHS_PRE KLF_EXTRA_SEARCH_PATHS ,
00077 # define EXTRA_PATHS KLF_EXTRA_SEARCH_PATHS
00078 #else
00079 # define EXTRA_PATHS_PRE
00080 # define EXTRA_PATHS
00081 #endif
00082
00083
00084 #if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
00085 QStringList progLATEX = QStringList() << "latex.exe";
00086 QStringList progDVIPS = QStringList() << "dvips.exe";
00087 QStringList progGS = QStringList() << "gswin32c.exe" << "mgs.exe";
00088 QStringList progEPSTOPDF = QStringList() << "epstopdf.exe";
00089 static const char * standard_extra_paths[] = {
00090 EXTRA_PATHS_PRE
00091 "C:\\Program Files\\MiKTeX*\\miktex\\bin",
00092 "C:\\Program Files\\gs\\gs*\\bin",
00093 NULL
00094 };
00095 #elif defined(Q_WS_MAC)
00096 QStringList progLATEX = QStringList() << "latex";
00097 QStringList progDVIPS = QStringList() << "dvips";
00098 QStringList progGS = QStringList() << "gs";
00099 QStringList progEPSTOPDF = QStringList() << "epstopdf";
00100 static const char * standard_extra_paths[] = {
00101 EXTRA_PATHS_PRE
00102 "/usr/texbin:/usr/local/bin:/sw/bin:/sw/usr/bin",
00103 NULL
00104 };
00105 #else
00106 QStringList progLATEX = QStringList() << "latex";
00107 QStringList progDVIPS = QStringList() << "dvips";
00108 QStringList progGS = QStringList() << "gs";
00109 QStringList progEPSTOPDF = QStringList() << "epstopdf";
00110 static const char * standard_extra_paths[] = {
00111 EXTRA_PATHS_PRE
00112 NULL
00113 };
00114 #endif
00115
00116
00117
00118
00119
00120 KLFBackend::KLFBackend()
00121 {
00122 }
00123
00124
00125
00126 QString progErrorMsg(QString progname, int exitstatus, QString stderrstr, QString stdoutstr)
00127 {
00128 QString stdouthtml = stdoutstr;
00129 QString stderrhtml = stderrstr;
00130 stdouthtml.replace("&", "&");
00131 stdouthtml.replace("<", "<");
00132 stdouthtml.replace(">", ">");
00133 stderrhtml.replace("&", "&");
00134 stderrhtml.replace("<", "<");
00135 stderrhtml.replace(">", ">");
00136
00137 if (stderrstr.isEmpty() && stdoutstr.isEmpty())
00138 return QObject::tr("<p><b>%1</b> reported an error (exit status %2). No Output was generated.</p>",
00139 "KLFBackend")
00140 .arg(progname).arg(exitstatus);
00141 if (stderrstr.isEmpty())
00142 return
00143 QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stdout output:</p>\n"
00144 "<pre>\n%3</pre>", "KLFBackend")
00145 .arg(progname).arg(exitstatus).arg(stdouthtml);
00146 if (stdoutstr.isEmpty())
00147 return
00148 QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
00149 "<pre>\n%3</pre>", "KLFBackend")
00150 .arg(progname).arg(exitstatus).arg(stderrhtml);
00151
00152 return QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
00153 "<pre>\n%3</pre><p>And here is full stdout output:</p><pre>\n%4</pre>", "KLFBackend")
00154 .arg(progname).arg(exitstatus).arg(stderrhtml).arg(stdouthtml);
00155 }
00156
00157
00158
00159
00160 struct cleanup_caller {
00161 QString tempfname;
00162 cleanup_caller(QString fn) : tempfname(fn) { }
00163 ~cleanup_caller() {
00164 KLFBackend::cleanup(tempfname);
00165 }
00166 };
00167
00168 QString __klf_expand_env_vars(const QString& envexpr)
00169 {
00170 QString s = envexpr;
00171 QRegExp rx("\\$(?:(\\$|(?:[A-Za-z0-9_]+))|\\{([A-Za-z0-9_]+)\\})");
00172 int i = 0;
00173 while ( (i = rx.rx_indexin_i(s, i)) != -1 ) {
00174
00175 QString envvarname = rx.cap(1);
00176 if (envvarname.isEmpty() || envvarname == QLatin1String("$")) {
00177
00178 s.replace(i, rx.matchedLength(), QLatin1String("$"));
00179 i += 1;
00180 continue;
00181 }
00182 const char *svalue = getenv(qPrintable(envvarname));
00183 QString qsvalue = (svalue != NULL) ? QString::fromLocal8Bit(svalue) : QString();
00184 s.replace(i, rx.matchedLength(), qsvalue);
00185 i += qsvalue.length();
00186 }
00187
00188 return s;
00189 }
00190
00191 void __klf_append_replace_env_var(QStringList *list, const QString& var, const QString& line)
00192 {
00193
00194 int k;
00195 for (k = 0; k < (int)list->size(); ++k) {
00196 if (list->operator[](k).startsWith(var+QString("="))) {
00197 list->operator[](k) = line;
00198 return;
00199 }
00200 }
00201
00202 list->append(line);
00203 }
00204
00205
00206
00207
00208
00209
00210
00211 struct klfbbox {
00212 double x1, x2, y1, y2;
00213 };
00214 static bool read_eps_bbox(const QByteArray& epsdata, klfbbox *bbox, KLFBackend::klfOutput * resError);
00215 static void correct_eps_bbox(const QByteArray& epsdata, const klfbbox& bbox_corrected, const klfbbox& bbox_orig,
00216 double vectorscale, QByteArray * epsdatacorrected);
00217
00218
00219
00220
00221
00222
00223 KLFBackend::klfOutput KLFBackend::getLatexFormula(const klfInput& in, const klfSettings& settings)
00224 {
00225
00226 QMutexLocker mutexlocker(&__mutex);
00227
00228 int k;
00229
00230 qDebug("%s: %s: KLFBackend::getLatexFormula() called. latex=%s", KLF_FUNC_NAME, KLF_SHORT_TIME,
00231 qPrintable(in.latex));
00232
00233
00234 QStringList execenv = klf_cur_environ();
00235 for (k = 0; k < (int)settings.execenv.size(); ++k) {
00236 int eqpos = settings.execenv[k].s_indexOf(QChar('='));
00237 if (eqpos == -1) {
00238 qWarning("%s: badly formed environment definition in `environ': %s", KLF_FUNC_NAME,
00239 qPrintable(settings.execenv[k]));
00240 continue;
00241 }
00242 QString varname = settings.execenv[k].mid(0, eqpos);
00243 QString newenvdef = __klf_expand_env_vars(settings.execenv[k]);
00244 __klf_append_replace_env_var(&execenv, varname, newenvdef);
00245 }
00246
00247 klfDbg("execution environment for sub-processes:\n"+execenv.join("\n")) ;
00248
00249 klfOutput res;
00250 res.status = KLFERR_NOERROR;
00251 res.errorstr = QString();
00252 res.result = QImage();
00253 res.pngdata_raw = QByteArray();
00254 res.pngdata = QByteArray();
00255 res.epsdata = QByteArray();
00256 res.pdfdata = QByteArray();
00257 res.input = in;
00258 res.settings = settings;
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271 QString tempfname = settings.tempdir + "/klatexformulatmp" KLF_VERSION_STRING "-"
00272 + QDateTime::currentDateTime().toString("hh-mm-ss")
00273 #ifdef KLFBACKEND_QT4
00274 + "-p"+ QString("%1").arg(QApplication::applicationPid(), 0, 26)
00275 #else
00276 + "-p" + QString("%1").arg(rand()%100000, 0, 26)
00277 #endif
00278 ;
00279
00280 QString fnTex = tempfname + ".tex";
00281 QString fnDvi = tempfname + ".dvi";
00282 QString fnRawEps = tempfname + "-raw.eps";
00283 QString fnBBCorrEps = tempfname + "-bbcorr.eps";
00284 QString fnOutlFontsEps = tempfname + "-outlfonts.eps";
00285 QString fnFinalEps = settings.outlineFonts ? fnOutlFontsEps : fnBBCorrEps;
00286 QString fnPng = tempfname + ".png";
00287 QString fnPdf = tempfname + ".pdf";
00288
00289
00290
00291 cleanup_caller cleanupcallerinstance(tempfname);
00292
00293 #ifdef KLFBACKEND_QT4
00294 QString latexsimplified = in.latex.s_trimmed();
00295 #else
00296 QString latexsimplified = in.latex.stripWhiteSpace();
00297 #endif
00298 if (latexsimplified.isEmpty()) {
00299 res.errorstr = QObject::tr("You must specify a LaTeX formula!", "KLFBackend");
00300 res.status = KLFERR_MISSINGLATEXFORMULA;
00301 return res;
00302 }
00303
00304 QString latexin;
00305 if (!in.bypassTemplate) {
00306 if (in.mathmode.contains("...") == 0) {
00307 res.status = KLFERR_MISSINGMATHMODETHREEDOTS;
00308 res.errorstr = QObject::tr("The math mode string doesn't contain '...'!", "KLFBackend");
00309 return res;
00310 }
00311 latexin = in.mathmode;
00312 latexin.replace("...", in.latex);
00313 }
00314
00315 {
00316 QFile file(fnTex);
00317 bool r = file.open(dev_WRITEONLY);
00318 if ( ! r ) {
00319 res.status = KLFERR_TEXWRITEFAIL;
00320 res.errorstr = QObject::tr("Can't open file for writing: '%1'!", "KLFBackend")
00321 .arg(fnTex);
00322 return res;
00323 }
00324 QTextStream stream(&file);
00325 if (!in.bypassTemplate) {
00326 stream << "\\documentclass{article}\n"
00327 << "\\usepackage[dvips]{color}\n"
00328 << in.preamble << "\n"
00329 << "\\begin{document}\n"
00330 << "\\thispagestyle{empty}\n"
00331 << QString("\\definecolor{klffgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.fg_color)/255.0)
00332 .arg(qGreen(in.fg_color)/255.0).arg(qBlue(in.fg_color)/255.0)
00333 << QString("\\definecolor{klfbgcolor}{rgb}{%1,%2,%3}\n").arg(qRed(in.bg_color)/255.0)
00334 .arg(qGreen(in.bg_color)/255.0).arg(qBlue(in.bg_color)/255.0)
00335 << ( (qAlpha(in.bg_color)>0) ? "\\pagecolor{klfbgcolor}\n" : "" )
00336 << "{\\color{klffgcolor} " << latexin << " }\n"
00337 << "\\end{document}\n";
00338 } else {
00339 stream << in.latex;
00340 }
00341 }
00342
00343 {
00344
00345 KLFBlockProcess proc;
00346 QStringList args;
00347
00348 proc.setWorkingDirectory(settings.tempdir);
00349
00350 args << settings.latexexec << dir_native_separators(fnTex);
00351
00352 qDebug("%s: %s: about to exec latex...", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00353 bool r = proc.startProcess(args, execenv);
00354 qDebug("%s: %s: latex returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00355
00356 if (!r) {
00357 res.status = KLFERR_NOLATEXPROG;
00358 res.errorstr = QObject::tr("Unable to start Latex program %1!", "KLFBackend")
00359 .arg(settings.latexexec);
00360 return res;
00361 }
00362 if (!proc.processNormalExit()) {
00363 res.status = KLFERR_LATEXNONORMALEXIT;
00364 res.errorstr = QObject::tr("Latex was killed!", "KLFBackend");
00365 return res;
00366 }
00367 if (proc.processExitStatus() != 0) {
00368 res.status = KLFERR_PROGERR_LATEX;
00369 res.errorstr = progErrorMsg("LaTeX", proc.processExitStatus(), proc.readStderrString(),
00370 proc.readStdoutString());
00371 return res;
00372 }
00373
00374 if (!QFile::exists(fnDvi)) {
00375 res.status = KLFERR_NODVIFILE;
00376 res.errorstr = QObject::tr("DVI file didn't appear after having called Latex!", "KLFBackend");
00377 return res;
00378 }
00379
00380 }
00381
00382 {
00383
00384 KLFBlockProcess proc;
00385 QStringList args;
00386 args << settings.dvipsexec << "-E" << dir_native_separators(fnDvi)
00387 << "-o" << dir_native_separators(fnRawEps);
00388
00389 qDebug("%s: %s: about to dvips... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
00390 bool r = proc.startProcess(args, execenv);
00391 qDebug("%s: %s: dvips returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00392
00393 if ( ! r ) {
00394 res.status = KLFERR_NODVIPSPROG;
00395 res.errorstr = QObject::tr("Unable to start dvips!\n", "KLFBackend");
00396 return res;
00397 }
00398 if ( !proc.processNormalExit() ) {
00399 res.status = KLFERR_DVIPSNONORMALEXIT;
00400 res.errorstr = QObject::tr("Dvips was mercilessly killed!\n", "KLFBackend");
00401 return res;
00402 }
00403 if ( proc.processExitStatus() != 0) {
00404 res.status = KLFERR_PROGERR_DVIPS;
00405 res.errorstr = progErrorMsg("dvips", proc.processExitStatus(), proc.readStderrString(),
00406 proc.readStdoutString());
00407 return res;
00408 }
00409 if (!QFile::exists(fnRawEps)) {
00410 res.status = KLFERR_NOEPSFILE;
00411 res.errorstr = QObject::tr("EPS file didn't appear after dvips call!\n", "KLFBackend");
00412 return res;
00413 }
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485 {
00486
00487
00488 QFile epsfile(fnRawEps);
00489 r = epsfile.open(dev_READONLY);
00490 if ( ! r ) {
00491 res.status = KLFERR_EPSREADFAIL;
00492 res.errorstr = QObject::tr("Can't read file '%1'!\n", "KLFBackend").arg(fnRawEps);
00493 return res;
00494 }
00495 QByteArray rawepsdata = epsfile.readAll();
00496
00497 klfbbox bbox, bbox_corrected;
00498 bool ok = read_eps_bbox(rawepsdata, &bbox, &res);
00499 if (!ok)
00500 return res;
00501
00502 bbox.x1 -= settings.lborderoffset;
00503 bbox.y1 -= settings.bborderoffset;
00504 bbox.x2 += settings.rborderoffset;
00505 bbox.y2 += settings.tborderoffset;
00506
00507 int width_pt = bbox.x2 - bbox.x1;
00508 int height_pt = bbox.y2 - bbox.y1;
00509
00510
00511
00512 bbox_corrected.x1 = 0;
00513 bbox_corrected.y1 = 0;
00514 bbox_corrected.x2 = width_pt;
00515 bbox_corrected.y2 = height_pt;
00516
00517
00518 correct_eps_bbox(rawepsdata, bbox_corrected, bbox, 1.0,
00519 &res.epsdata);
00520
00521 QFile epsgoodfile(fnBBCorrEps);
00522 r = epsgoodfile.open(dev_WRITEONLY);
00523 if ( ! r ) {
00524 res.status = KLFERR_EPSWRITEFAIL;
00525 res.errorstr = QObject::tr("Can't write to file '%1'!\n", "KLFBackend")
00526 .arg(fnBBCorrEps);
00527 return res;
00528 }
00529 epsgoodfile.dev_write(res.epsdata);
00530 }
00531
00532 qDebug("%s: %s: eps bbox set.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00533
00534 }
00535
00536 if (settings.outlineFonts) {
00537
00538 KLFBlockProcess proc;
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551 QStringList try_ps_devices;
00552 const char *env_gs_device = getenv("KLFBACKEND_GS_PS_DEVICE");
00553 if (env_gs_device != NULL) {
00554 try_ps_devices << QString::fromLatin1(env_gs_device);
00555 } else {
00556 try_ps_devices << QLatin1String("pswrite") << QLatin1String("ps2write");
00557 }
00558
00559 bool r = false;
00560 int try_ps_dev_i = 0;
00561 for (try_ps_dev_i = 0; try_ps_dev_i < try_ps_devices.size(); try_ps_dev_i++) {
00562 QString psdev = try_ps_devices[try_ps_dev_i];
00563 qDebug("trying with gs device %s ...", qPrintable(psdev));
00564
00565 QStringList args;
00566 args << settings.gsexec << "-dNOCACHE" << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop"
00567 << QString("-sDEVICE=%1").arg(psdev)
00568 << "-sOutputFile="+dir_native_separators(fnOutlFontsEps)
00569 << "-q" << "-dBATCH" << dir_native_separators(fnBBCorrEps);
00570
00571 qDebug("%s: %s: about to gs (for outline fonts)...\n%s", KLF_FUNC_NAME, KLF_SHORT_TIME,
00572 qPrintable(args.join(" ")));
00573 r = proc.startProcess(args, execenv);
00574 qDebug("%s: %s: gs returned (for outline fonts).", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00575
00576 if (r && proc.processNormalExit() && proc.processExitStatus() == 0) {
00577
00578 break;
00579 }
00580 }
00581
00582 if ( ! r ) {
00583 res.status = KLFERR_NOGSPROG;
00584 res.errorstr = QObject::tr("Unable to start gs!\n", "KLFBackend");
00585 return res;
00586 }
00587 if ( !proc.processNormalExit() ) {
00588 res.status = KLFERR_GSNONORMALEXIT;
00589 res.errorstr = QObject::tr("gs died abnormally!\n", "KLFBackend");
00590 return res;
00591 }
00592 if ( proc.processExitStatus() != 0) {
00593 res.status = KLFERR_PROGERR_GS_OF;
00594 res.errorstr = progErrorMsg("gs", proc.processExitStatus(), proc.readStderrString(),
00595 proc.readStdoutString());
00596 return res;
00597 }
00598 if (!QFile::exists(fnOutlFontsEps)) {
00599 res.status = KLFERR_NOEPSFILE_OF;
00600 res.errorstr = QObject::tr("EPS file (with outlined fonts) didn't appear after call to gs!\n",
00601 "KLFBackend");
00602 return res;
00603 }
00604
00605
00606 QFile ofepsfile(fnOutlFontsEps);
00607 r = ofepsfile.open(dev_READONLY);
00608 if ( ! r ) {
00609 res.status = KLFERR_EPSREADFAIL_OF;
00610 res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend")
00611 .arg(fnOutlFontsEps);
00612 return res;
00613 }
00614 res.epsdata = ofepsfile.readAll();
00615 ofepsfile.close();
00616
00617 }
00618
00619 {
00620 KLFBlockProcess proc;
00621 QStringList args;
00622 args << settings.gsexec << "-dNOPAUSE" << "-dSAFER" << "-dEPSCrop"
00623 << "-r"+QString::number(in.dpi) << "-dTextAlphaBits=4"
00624 << "-dGraphicsAlphaBits=4";
00625 if (qAlpha(in.bg_color) > 0) {
00626 args << "-sDEVICE=png16m";
00627 } else {
00628 args << "-sDEVICE=pngalpha";
00629 }
00630 args << "-sOutputFile="+dir_native_separators(fnPng) << "-q" << "-dBATCH"
00631 << dir_native_separators(fnFinalEps);
00632
00633 qDebug("%s: %s: about to gs... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
00634 bool r = proc.startProcess(args, execenv);
00635 qDebug("%s: %s: gs returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00636
00637 if ( ! r ) {
00638 res.status = KLFERR_NOGSPROG;
00639 res.errorstr = QObject::tr("Unable to start gs!\n", "KLFBackend");
00640 return res;
00641 }
00642 if ( !proc.processNormalExit() ) {
00643 res.status = KLFERR_GSNONORMALEXIT;
00644 res.errorstr = QObject::tr("gs died abnormally!\n", "KLFBackend");
00645 return res;
00646 }
00647 if ( proc.processExitStatus() != 0) {
00648 res.status = KLFERR_PROGERR_GS;
00649 res.errorstr = progErrorMsg("gs", proc.processExitStatus(), proc.readStderrString(),
00650 proc.readStdoutString());
00651 return res;
00652 }
00653 if (!QFile::exists(fnPng)) {
00654 res.status = KLFERR_NOPNGFILE;
00655 res.errorstr = QObject::tr("PNG file didn't appear after call to gs!\n", "KLFBackend");
00656 return res;
00657 }
00658
00659
00660 QFile pngfile(fnPng);
00661 r = pngfile.open(dev_READONLY);
00662 if ( ! r ) {
00663 res.status = KLFERR_PNGREADFAIL;
00664 res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend")
00665 .arg(fnPng);
00666 return res;
00667 }
00668 res.pngdata_raw = pngfile.readAll();
00669 pngfile.close();
00670
00671 res.result.loadFromData(res.pngdata_raw, "PNG");
00672
00673
00674 res.result.img_settext("AppVersion", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
00675 res.result.img_settext("Application",
00676 QObject::tr("Created with KLatexFormula version %1", "KLFBackend::saveOutputToFile"));
00677 res.result.img_settext("Software", QString::fromLatin1("KLatexFormula " KLF_VERSION_STRING));
00678 res.result.img_settext("InputLatex", in.latex);
00679 res.result.img_settext("InputMathMode", in.mathmode);
00680 res.result.img_settext("InputPreamble", in.preamble);
00681 res.result.img_settext("InputFgColor", QString("rgb(%1, %2, %3)").arg(qRed(in.fg_color))
00682 .arg(qGreen(in.fg_color)).arg(qBlue(in.fg_color)));
00683 res.result.img_settext("InputBgColor", QString("rgba(%1, %2, %3, %4)").arg(qRed(in.bg_color))
00684 .arg(qGreen(in.bg_color)).arg(qBlue(in.bg_color))
00685 .arg(qAlpha(in.bg_color)));
00686 res.result.img_settext("InputDPI", QString::number(in.dpi));
00687 res.result.img_settext("SettingsTBorderOffset", QString::number(settings.tborderoffset));
00688 res.result.img_settext("SettingsRBorderOffset", QString::number(settings.rborderoffset));
00689 res.result.img_settext("SettingsBBorderOffset", QString::number(settings.bborderoffset));
00690 res.result.img_settext("SettingsLBorderOffset", QString::number(settings.lborderoffset));
00691 res.result.img_settext("SettingsOutlineFonts", settings.outlineFonts?QString("true"):QString("false"));
00692 }
00693
00694 {
00695 #ifdef KLFBACKEND_QT4
00696 QBuffer buf(&res.pngdata);
00697 #else
00698 QBuffer buf(res.pngdata);
00699 #endif
00700 buf.open(dev_WRITEONLY);
00701 bool r = res.result.save(&buf, "PNG");
00702 if (!r) {
00703 qWarning("%s: Error: Can't save \"final\" PNG data.", KLF_FUNC_NAME);
00704 res.pngdata.ba_assign(res.pngdata_raw);
00705 }
00706 }
00707
00708 if (!settings.epstopdfexec.isEmpty()) {
00709
00710 KLFBlockProcess proc;
00711 QStringList args;
00712 args << settings.epstopdfexec << dir_native_separators(fnFinalEps)
00713 << ("--outfile="+dir_native_separators(fnPdf));
00714
00715 qDebug("%s: %s: about to epstopdf... %s", KLF_FUNC_NAME, KLF_SHORT_TIME, qPrintable(args.join(" "))) ;
00716 bool r = proc.startProcess(args, execenv);
00717 qDebug("%s: %s: epstopdf returned.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00718
00719 if ( ! r ) {
00720 res.status = KLFERR_NOEPSTOPDFPROG;
00721 res.errorstr = QObject::tr("Unable to start epstopdf!\n", "KLFBackend");
00722 return res;
00723 }
00724 if ( !proc.processNormalExit() ) {
00725 res.status = KLFERR_EPSTOPDFNONORMALEXIT;
00726 res.errorstr = QObject::tr("epstopdf died nastily!\n", "KLFBackend");
00727 return res;
00728 }
00729 if ( proc.processExitStatus() != 0) {
00730 res.status = KLFERR_PROGERR_EPSTOPDF;
00731 res.errorstr = progErrorMsg("epstopdf", proc.processExitStatus(), proc.readStderrString(),
00732 proc.readStdoutString());
00733 return res;
00734 }
00735 if (!QFile::exists(fnPdf)) {
00736 qDebug("%s: %s: pdf file '%s' didn't appear after epstopdf!", KLF_FUNC_NAME, KLF_SHORT_TIME,
00737 qPrintable(fnPdf));
00738 res.status = KLFERR_NOPDFFILE;
00739 res.errorstr = QObject::tr("PDF file didn't appear after call to epstopdf!\n", "KLFBackend");
00740 return res;
00741 }
00742
00743
00744 QFile pdffile(fnPdf);
00745 r = pdffile.open(dev_READONLY);
00746 if ( ! r ) {
00747 res.status = KLFERR_PDFREADFAIL;
00748 res.errorstr = QObject::tr("Unable to read file %1!\n", "KLFBackend").arg(fnPdf);
00749 return res;
00750 }
00751 res.pdfdata = pdffile.readAll();
00752
00753 }
00754
00755 qDebug("%s: %s: end of function.", KLF_FUNC_NAME, KLF_SHORT_TIME) ;
00756
00757 return res;
00758 }
00759
00760
00761
00762 static bool s_starts_with(const char * x, int len_x, const char *test, int len_test)
00763 {
00764 if (len_x < len_test)
00765 return false;
00766 return !strncmp(x, test, len_test);
00767 }
00768
00769 #define D_RX "([0-9eE.-]+)"
00770
00771 static bool parse_bbox_values(const QString& str, klfbbox *bbox)
00772 {
00773
00774 QRegExp rx_bbvalues("" D_RX "\\s+" D_RX "\\s+" D_RX "\\s+" D_RX "");
00775 int i = rx_bbvalues.rx_indexin(str);
00776 if (i < 0) {
00777 return false;
00778 }
00779 bbox->x1 = rx_bbvalues.cap(1).toDouble();
00780 bbox->y1 = rx_bbvalues.cap(2).toDouble();
00781 bbox->x2 = rx_bbvalues.cap(3).toDouble();
00782 bbox->y2 = rx_bbvalues.cap(4).toDouble();
00783 return true;
00784 }
00785
00786 static bool read_eps_bbox(const QByteArray& epsdata, klfbbox *bbox, KLFBackend::klfOutput * resError)
00787 {
00788 static const char * hibboxtag = "%%HiResBoundingBox:";
00789 static const char * bboxtag = "%%BoundingBox:";
00790 static const int hibboxtaglen = strlen(hibboxtag);
00791 static const int bboxtaglen = strlen(bboxtag);
00792
00793
00794 QBuffer buf;
00795 buf_setdata(buf, epsdata);
00796 bool r = buf.open(dev_READONLY);
00797 if (!r) {
00798 qWarning("What's going on!!?! can't open buffer for reading? Will Fail!!!") ;
00799 }
00800
00801 QString nobboxerrstr =
00802 QObject::tr("DVIPS did not provide parsable %%BoundingBox: in its output!", "KLFBackend");
00803
00804 char linebuffer[512];
00805 int n;
00806 bool gotepsbbox = false;
00807 int still_look_for_hiresbbox_lines = 5;
00808 while ((n = buf.readLine(linebuffer, sizeof(linebuffer)-1)) > 0) {
00809 if (gotepsbbox && still_look_for_hiresbbox_lines-- < 0) {
00810
00811
00812 klfDbg("stopped looking for hires-bbox.") ;
00813 break;
00814 }
00815 if (s_starts_with(linebuffer, n-1, hibboxtag, hibboxtaglen)) {
00816
00817 bool ok = parse_bbox_values(QString::fromLatin1(linebuffer+hibboxtaglen), bbox);
00818 if (!ok) {
00819 resError->status = KLFERR_BADEPSBBOX;
00820 resError->errorstr = nobboxerrstr;
00821 return false;
00822 }
00823 klfDbg("got hires-bbox.") ;
00824
00825 return true;
00826 }
00827 if (s_starts_with(linebuffer, n-1, bboxtag, bboxtaglen)) {
00828
00829 bool ok = parse_bbox_values(QString::fromLatin1(linebuffer+bboxtaglen), bbox);
00830 if (!ok) {
00831 continue;
00832 }
00833
00834 gotepsbbox = true;
00835 klfDbg("got normal bbox.") ;
00836 continue;
00837 }
00838 }
00839
00840
00841 if (gotepsbbox) {
00842
00843 return true;
00844 }
00845
00846 resError->status = KLFERR_BADEPSBBOX;
00847 resError->errorstr = nobboxerrstr;
00848 return false;
00849 }
00850
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871 static void correct_eps_bbox(const QByteArray& rawepsdata, const klfbbox& bbox_corrected,
00872 const klfbbox& bbox_orig, double vectorscale,
00873 QByteArray * epsdatacorrected)
00874 {
00875 static const char * bboxdecl = "%%BoundingBox:";
00876 static int bboxdecl_len = strlen(bboxdecl);
00877
00878 double offx = bbox_corrected.x1 - bbox_orig.x1;
00879 double offy = bbox_corrected.y1 - bbox_orig.y1;
00880
00881
00882 int i, len;
00883 char nl[] = "\0\0\0";
00884 #ifdef KLFBACKEND_QT4
00885 i = rawepsdata.indexOf(bboxdecl);
00886 #else
00887 QCString rawepsdata_s(rawepsdata.data(), rawepsdata.size());
00888 i = rawepsdata_s.find(bboxdecl);
00889 #endif
00890 if (i < 0) {
00891 i = 0;
00892 len = 0;
00893 } else {
00894 int j = i+bboxdecl_len;
00895 while (j < (int)rawepsdata.size() && rawepsdata[j] != '\r' && rawepsdata[j] != '\n')
00896 ++j;
00897 len = j-i;
00898
00899 if (rawepsdata[j] == '\r' && j < (int)rawepsdata.size()-1 && rawepsdata[j+1] == '\n') {
00900 nl[0] = '\r', nl[1] = '\n';
00901 } else {
00902 nl[0] = rawepsdata[j];
00903 }
00904 }
00905
00906 double dwi = bbox_corrected.x2 * vectorscale;
00907 double dhi = bbox_corrected.y2 * vectorscale;
00908 int wi = (int)(dwi + 0.99999) ;
00909 int hi = (int)(dhi + 0.99999) ;
00910 char buffer[1024];
00911 int buffer_len;
00912
00913 snprintf(buffer, sizeof(buffer)-1,
00914 "%%%%BoundingBox: 0 0 %d %d%s"
00915 "%%%%HiResBoundingBox: 0 0 %.6g %.6g%s",
00916 wi, hi, nl,
00917 dwi, dhi, nl);
00918 buffer_len = strlen(buffer);
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943
00944
00945
00946
00947 char buffer2[1024];
00948 int buffer2_len;
00949 snprintf(buffer2, sizeof(buffer2)-1,
00950 "%s"
00951 "%%%%Page 1 1%s"
00952 "%%%%PageBoundingBox 0 0 %d %d%s"
00953 "<< /PageSize [%d %d] >> setpagedevice%s"
00954
00955 "%f %f scale%s"
00956 "%f %f translate%s"
00957 ,
00958 nl,
00959 nl,
00960 wi, hi, nl,
00961 wi, hi, nl,
00962
00963 vectorscale, vectorscale, nl,
00964 offx, offy, nl);
00965 buffer2_len = strlen(buffer2);
00966
00967
00968
00969
00970
00971
00972
00973
00974 #ifdef KLFBACKEND_QT4
00975 QByteArray neweps;
00976 neweps = rawepsdata;
00977 #else
00978 QCString neweps(rawepsdata.data(), rawepsdata.size());
00979 #endif
00980 neweps.replace(i, len, buffer);
00981
00982 const char * endsetupstr = "%%EndSetup";
00983 int i2 = neweps.s_indexOf(endsetupstr);
00984 if (i2 < 0)
00985 i2 = i + buffer_len;
00986 else
00987 i2 += strlen(endsetupstr);
00988
00989 neweps.replace(i2, 0, buffer2);
00990
00991 qDebug("neweps has now length=%d",neweps.size());
00992 qDebug("New eps bbox is [0 0 %.6g %.6g] with translate [%.6g %.6g] and scale %.6g.",
00993 dwi, dhi, offx, offy, vectorscale);
00994
00995 epsdatacorrected->ba_assign(neweps);
00996 }
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008 void KLFBackend::cleanup(QString tempfname)
01009 {
01010 const char *skipcleanup = getenv("KLFBACKEND_LEAVE_TEMP_FILES");
01011 if (skipcleanup != NULL && (*skipcleanup == '1' || *skipcleanup == 't' || *skipcleanup == 'T' ||
01012 *skipcleanup == 'y' || *skipcleanup == 'Y'))
01013 return;
01014
01015 if (QFile::exists(tempfname+".tex")) QFile::remove(tempfname+".tex");
01016 if (QFile::exists(tempfname+".dvi")) QFile::remove(tempfname+".dvi");
01017 if (QFile::exists(tempfname+".aux")) QFile::remove(tempfname+".aux");
01018 if (QFile::exists(tempfname+".log")) QFile::remove(tempfname+".log");
01019 if (QFile::exists(tempfname+".toc")) QFile::remove(tempfname+".toc");
01020 if (QFile::exists(tempfname+".eps")) QFile::remove(tempfname+".eps");
01021 if (QFile::exists(tempfname+"-good.eps")) QFile::remove(tempfname+"-good.eps");
01022 if (QFile::exists(tempfname+"-raw.eps")) QFile::remove(tempfname+"-raw.eps");
01023 if (QFile::exists(tempfname+"-bbcorr.eps")) QFile::remove(tempfname+"-bbcorr.eps");
01024 if (QFile::exists(tempfname+"-outlfonts.eps")) QFile::remove(tempfname+"-outlfonts.eps");
01025 if (QFile::exists(tempfname+".png")) QFile::remove(tempfname+".png");
01026 if (QFile::exists(tempfname+".pdf")) QFile::remove(tempfname+".pdf");
01027 }
01028
01029
01030
01031
01032
01033 QMutex KLFBackend::__mutex;
01034
01035
01036
01037 KLF_EXPORT bool operator==(const KLFBackend::klfInput& a, const KLFBackend::klfInput& b)
01038 {
01039 return a.latex == b.latex &&
01040 a.mathmode == b.mathmode &&
01041 a.preamble == b.preamble &&
01042 a.fg_color == b.fg_color &&
01043 a.bg_color == b.bg_color &&
01044 a.dpi == b.dpi;
01045 }
01046
01047
01048 bool KLFBackend::saveOutputToDevice(const klfOutput& klfoutput, QIODevice *device,
01049 const QString& fmt, QString *errorStringPtr)
01050 {
01051 QString format = fmt.s_trimmed().s_toUpper();
01052
01053
01054 if (format == "EPS" || format == "PS") {
01055 device->dev_write(klfoutput.epsdata);
01056 } else if (format == "PNG") {
01057 device->dev_write(klfoutput.pngdata);
01058 } else if (format == "PDF") {
01059 if (klfoutput.pdfdata.isEmpty()) {
01060 QString error = QObject::tr("PDF format is not available!\n",
01061 "KLFBackend::saveOutputToFile");
01062 qWarning("%s", qPrintable(error));
01063 if (errorStringPtr != NULL)
01064 errorStringPtr->operator=(error);
01065 return false;
01066 }
01067 device->dev_write(klfoutput.pdfdata);
01068 } else {
01069 bool res = klfoutput.result.save(device, format.s_toLatin1());
01070 if ( ! res ) {
01071 QString errstr = QObject::tr("Unable to save image in format `%1'!",
01072 "KLFBackend::saveOutputToDevice").arg(format);
01073 qWarning("%s", qPrintable(errstr));
01074 if (errorStringPtr != NULL)
01075 *errorStringPtr = errstr;
01076 return false;
01077 }
01078 }
01079
01080 return true;
01081 }
01082
01083 bool KLFBackend::saveOutputToFile(const klfOutput& klfoutput, const QString& fileName,
01084 const QString& fmt, QString *errorStringPtr)
01085 {
01086 QString format = fmt;
01087
01088 if (format.isEmpty() && !fileName.isEmpty()) {
01089 QFileInfo fi(fileName);
01090 if ( ! fi.fi_suffix().isEmpty() )
01091 format = fi.fi_suffix();
01092 }
01093 if (format.isEmpty())
01094 format = QLatin1String("PNG");
01095 format = format.s_trimmed().s_toUpper();
01096
01097 QFile fout;
01098 if (fileName.isEmpty() || fileName == "-") {
01099 if ( ! fout.f_open_fp(stdout) ) {
01100 QString error = QObject::tr("Unable to open stderr for write! Error: %1\n",
01101 "KLFBackend::saveOutputToFile").arg(fout.f_error());
01102 qWarning("%s", qPrintable(error));
01103 if (errorStringPtr != NULL)
01104 *errorStringPtr = error;
01105 return false;
01106 }
01107 } else {
01108 fout.f_setFileName(fileName);
01109 if ( ! fout.open(dev_WRITEONLY) ) {
01110 QString error = QObject::tr("Unable to write to file `%1'! Error: %2\n",
01111 "KLFBackend::saveOutputToFile")
01112 .arg(fileName).arg(fout.f_error());
01113 qWarning("%s", qPrintable(error));
01114 if (errorStringPtr != NULL)
01115 *errorStringPtr = error;
01116 return false;
01117 }
01118 }
01119
01120 return saveOutputToDevice(klfoutput, &fout, format, errorStringPtr);
01121 }
01122
01123
01124 bool KLFBackend::detectSettings(klfSettings *settings, const QString& extraPath)
01125 {
01126 KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01127
01128 QStringList stdextrapaths;
01129 int k, j;
01130 for (k = 0; standard_extra_paths[k] != NULL; ++k) {
01131 stdextrapaths.append(standard_extra_paths[k]);
01132 }
01133 QString extra_paths = stdextrapaths.join(QString("")+KLF_PATH_SEP);
01134 if (!extraPath.isEmpty())
01135 extra_paths += KLF_PATH_SEP + extraPath;
01136
01137
01138 #ifdef KLFBACKEND_QT4
01139 settings->tempdir = QDir::fromNativeSeparators(QDir::tempPath());
01140 #else
01141 # if defined(Q_OS_UNIX) || defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_MACX)
01142 settings->tempdir = "/tmp";
01143 # elif defined(Q_OS_WIN32)
01144 settings->tempdir = getenv("TEMP");
01145 # else
01146 settings->tempdir = QString();
01147 # endif
01148 #endif
01149
01150
01151 settings->lborderoffset = 1;
01152 settings->tborderoffset = 1;
01153 settings->rborderoffset = 1;
01154 settings->bborderoffset = 1;
01155
01156
01157 struct { QString * target_setting; QStringList prog_names; } progs_to_find[] = {
01158 { & settings->latexexec, progLATEX },
01159 { & settings->dvipsexec, progDVIPS },
01160 { & settings->gsexec, progGS },
01161 { & settings->epstopdfexec, progEPSTOPDF },
01162 { NULL, QStringList() }
01163 };
01164
01165 klfDbg(klfFmtCC("Our base extra paths are: %s", qPrintable(extra_paths))) ;
01166 QString ourextrapaths = extra_paths;
01167 ourextrapaths.replace("@executable_path", qApp->applicationDirPath());
01168 klfDbg(klfFmtCC("Our extra paths are: %s", qPrintable(ourextrapaths))) ;
01169
01170 for (k = 0; progs_to_find[k].target_setting != NULL; ++k) {
01171 klfDbg("Looking for "+progs_to_find[k].prog_names.join(" or ")) ;
01172 for (j = 0; j < (int)progs_to_find[k].prog_names.size(); ++j) {
01173 klfDbg("Testing `"+progs_to_find[k].prog_names[j]+"'") ;
01174 *progs_to_find[k].target_setting
01175 = klfSearchPath(progs_to_find[k].prog_names[j], ourextrapaths);
01176 if (!progs_to_find[k].target_setting->isEmpty()) {
01177 klfDbg("Found! at `"+ *progs_to_find[k].target_setting+"'") ;
01178 break;
01179 }
01180 }
01181 }
01182
01183 klf_detect_execenv(settings);
01184
01185 bool result_failure =
01186 settings->tempdir.isEmpty() || settings->latexexec.isEmpty() || settings->dvipsexec.isEmpty() ||
01187 settings->gsexec.isEmpty();
01188
01189 return !result_failure;
01190 }
01191
01192
01209 KLF_EXPORT bool klf_detect_execenv(KLFBackend::klfSettings *settings)
01210 {
01211
01212 QFileInfo gsfi(settings->gsexec);
01213 if (gsfi.fileName() == "mgs.exe") {
01214 QString mgsenv = QString("MIKTEX_GS_LIB=")
01215 + dir_native_separators(gsfi.fi_absolutePath()+"/../../ghostscript/base")
01216 + ";"
01217 + dir_native_separators(gsfi.fi_absolutePath()+"/../../fonts");
01218 __klf_append_replace_env_var(& settings->execenv, "MIKTEX_GS_LIB", mgsenv);
01219 klfDbg("Adjusting environment for mgs.exe: `"+mgsenv+"'") ;
01220 }
01221
01222 #ifdef Q_WS_MAC
01223
01224
01225 if (!settings->epstopdfexec.isEmpty()) {
01226 QFileInfo epstopdf_fi(settings->epstopdfexec);
01227 QString execenvpath = QString("PATH=%1:$PATH").arg(epstopdf_fi.fi_absolutePath());
01228 __klf_append_replace_env_var(& settings->execenv, "PATH", execenvpath);
01229 }
01230 #endif
01231
01232 return true;
01233 }