diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt index 756c98f75..98c24e546 100644 --- a/src/3rdparty/CMakeLists.txt +++ b/src/3rdparty/CMakeLists.txt @@ -1,3 +1,7 @@ IF (APPLE) add_subdirectory(kdmactouchbar) ENDIF() + +# preview.sty +install(FILES preview.sty DESTINATION ${DATA_INSTALL_DIR}/${PROJECT_NAME}/latex) + diff --git a/src/3rdparty/README.md b/src/3rdparty/README.md new file mode 100644 index 000000000..cebffd3ed --- /dev/null +++ b/src/3rdparty/README.md @@ -0,0 +1,23 @@ +## 3rd party libraries + +This folder contains versions of libraries and files LabPlot depends on. + + +## KDMacTouchBar + +KDAB's Qt Widget for the Mac Touch Bar ([link](https://github.com/KDAB/KDMacTouchBar)) + + +## preview.sty + +This file provides the LaTeX style 'preview' ([link](https://www.ctan.org/tex-archive/macros/latex/contrib/preview)). + +The main purpose of the preview package is the extraction of selected +elements from a LaTeX source, like formulas or graphics, into separate +pages of a DVI file. A flexible and convenient interface allows it to +specify what commands and constructs should be extracted. This works +with DVI files postprocessed by either Dvips and Ghostscript or +dvipng, but it also works when you are using PDFTeX for generating PDF +files. + +This package is used for the rendering of mathematical LaTeX expressions embedded in TextLabel. diff --git a/src/3rdparty/preview.sty b/src/3rdparty/preview.sty new file mode 100644 index 000000000..ce87715fc --- /dev/null +++ b/src/3rdparty/preview.sty @@ -0,0 +1,392 @@ +%% +%% This is file `preview.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% preview.dtx (with options: `style') +%% preview.dtx (with options: `style,active') +%% +%% IMPORTANT NOTICE: +%% +%% For the copyright see the source file. +%% +%% Any modified versions of this file must be renamed +%% with new filenames distinct from preview.sty. +%% +%% For distribution of the original source see the terms +%% for copying and modification in the file preview.dtx preview.dtx. +%% +%% This generated file may be distributed as long as the +%% original source files, as listed above, are part of the +%% same distribution. (The sources need not necessarily be +%% in the same archive or directory.) +%% The preview style for extracting previews from LaTeX documents. +%% Developed as part of AUCTeX . +\NeedsTeXFormat{LaTeX2e} \def\reserved@a #1#2$#3: +#4${\xdef#1{\reserved@c #2#4 $}} \def\reserved@c #1 #2${#1} +\begingroup \catcode`\_=12 +\reserved@a\pr@version $Name: release_11_90 $ \ifx\pr@version\@empty +\reserved@a\pr@version CVS-$Revision: 1.126 $ \endgroup \else + \def\next release_{} \lccode`\_=`. + \edef\next{\lowercase{\endgroup + \def\noexpand\pr@version{\expandafter\next\pr@version}}} \next \fi +\reserved@a\next $Date: 2017/04/24 13:20:00 $ +\edef\next{\noexpand\ProvidesPackage{preview}% + [\next\space \pr@version\space (AUCTeX/preview-latex)]} +\next +\let\ifPreview\iffalse +\let\preview@delay=\@gobble +\let\pr@advise=\@gobbletwo +\long\def\pr@advise@ship#1#2#3{} +\def\pr@loadcfg#1{\InputIfFileExists{#1.cfg}{}{}} +\IfFileExists{luatex85.sty}{\RequirePackage{luatex85}}{} +\DeclareOption{noconfig}{\let\pr@loadcfg=\@gobble} +\long\def\pr@addto@front#1#2{% + \toks@{#2}\toks@\expandafter{\the\expandafter\toks@#1}% + \xdef#1{\the\toks@}} +\DeclareOption{active}{% + \let\ifPreview\iftrue + \def\pr@advise#1{% + \expandafter\pr@adviseii\csname pr@\string#1\endcsname#1}% + \long\def\pr@advise@ship#1#2#3{\pr@advise#1{\pr@protect@ship{#2}{#3}}}% + \let\preview@delay\@firstofone} +\long\def\pr@adviseii#1#2#3{\preview@delay{% + \ifx#1\relax \let#1#2\fi + \toks@{#3#1}% + \ifx\@undefined\protected \else \protected\fi + \long\edef#2{\the\toks@}}} +\DeclareOption{delayed}{% + \ifPreview \def\preview@delay{\AtBeginDocument}\fi +} +\newif\ifpr@fixbb +\pr@fixbbfalse +\DeclareOption{psfixbb}{\ifPreview% + \pr@fixbbtrue + \newbox\pr@markerbox + \setbox\pr@markerbox\hbox{\special{psfile=/dev/null}}\fi +} +\let\pr@graphicstype=\z@ +\DeclareOption{dvips}{% + \let\pr@graphicstype\@ne + \preview@delay{\AtBeginDvi{% + \special{!/preview@version(\pr@version)def} + \special{!userdict begin/preview-bop-level 0 def% + /bop-hook{/preview-bop-level dup load dup 0 le{/isls false def% + /vsize 792 def/hsize 612 def}if 1 add store}bind def% + /eop-hook{/preview-bop-level dup load dup 0 gt{1 sub}if + store}bind def end}}}} +\DeclareOption{pdftex}{% + \let\pr@graphicstype\tw@} +\DeclareOption{xetex}{% + \let\pr@graphicstype\thr@@} +\begingroup +\catcode`\*=11 +\@firstofone{\endgroup +\DeclareOption{displaymath}{% + \preview@delay{\toks@{% + \pr@startbox{\noindent$$% + \aftergroup\pr@endbox\@gobbletwo}{$$}\@firstofone}% + \everydisplay\expandafter{\the\expandafter\toks@ + \expandafter{\the\everydisplay}}}% + \pr@advise@ship\equation{\begingroup\aftergroup\pr@endbox + \def\dt@ptrue{\m@ne=\m@ne}\noindent}% + {\endgroup}% + \pr@advise@ship\equation*{\begingroup\aftergroup\pr@endbox + \def\dt@ptrue{\m@ne=\m@ne}\noindent}% + {\endgroup}% + \PreviewOpen[][\def\dt@ptrue{\m@ne=\m@ne}\noindent#1]\[% + \PreviewClose\]% + \PreviewEnvironment[][\noindent#1]{eqnarray}% + \PreviewEnvironment[][\noindent#1]{eqnarray*}% + \PreviewEnvironment{displaymath}% +}} +\begingroup +\def\next#1#2{% + \endgroup + \DeclareOption{textmath}{% + \PreviewEnvironment{math}% + \preview@delay{\ifx#1\@undefined \let#1=$%$ + \fi\catcode`\$=\active + \ifx\xyreuncatcodes\@undefined\else + \edef\next{\catcode`@=\the\catcode`@\relax}% + \makeatother\expandafter\xyreuncatcodes\next\fi}% + \pr@advise@ship\(\pr@endaftergroup{}% \) + \pr@advise@ship#1{\@firstoftwo{\let#1=#2% + \futurelet\reserved@a\pr@textmathcheck}}{}}% + \def\pr@textmathcheck{\expandafter\pr@endaftergroup + \ifx\reserved@a#1{#2#2}\expandafter\@gobbletwo\fi#2}} +\lccode`\~=`\$ +\lowercase{\expandafter\next\expandafter~}% + \csname pr@\string$%$ + \endcsname +\DeclareOption{graphics}{% + \PreviewMacro[*[[!]{\includegraphics}%]] +} +\def\pr@floatfix#1#2{\ifx#1#2% + \ifx#1\@undefined\else + \PackageWarningNoLine{preview}{% +Your document class has a bad definition^^J +of \string#1, most likely^^J +\string\let\string#1=\string#2^^J +which has now been changed to^^J +\string\def\string#1{\string#2}^^J +because otherwise subsequent changes to \string#2^^J +(like done by several packages changing float behaviour)^^J +can't take effect on \string#1.^^J +Please complain to your document class author}% + \def#1{#2}\fi\fi} +\begingroup +\def\next#1#2{\endgroup + \DeclareOption{floats}{% + \pr@floatfix\endfigure\end@float + \pr@floatfix\endtable\end@float + \pr@floatfix#1\end@dblfloat + \pr@floatfix#2\end@dblfloat + \PreviewSnarfEnvironment[![]{@float}%] + \PreviewSnarfEnvironment[![]{@dblfloat}%] + }} +\expandafter\next\csname endfigure*\expandafter\endcsname + \csname endtable*\endcsname +\DeclareOption{sections}{% + \PreviewMacro[!!!!!!*[[!]{\@startsection}%]] + \PreviewMacro[*[[!]{\chapter}%]] +} +\DeclareOption* + {\InputIfFileExists{pr\CurrentOption.def}{}{\OptionNotUsed}} +\def\PreviewMacro{\@ifstar\pr@starmacro\pr@macro} +\long\def\pr@domacro#1#2{% + \long\def\next##1{#2}% + \pr@callafter\next#1]\pr@endparse} +\newcommand\pr@macro[1][]{% + \toks@{\pr@domacro{#1}}% + \long\edef\next[##1]##2{% + \noexpand\pr@advise@ship{##2}{\the\toks@{##1\noexpand\pr@endbox}}{}}% + \@ifnextchar[\next\pr@macroii} +\def\pr@macroii{\next[##1]} +\long\def\pr@endmacro#1{#1\pr@endbox} +\long\def\pr@protect@domacro#1#2{\pr@protect{% + \long\def\next##1{#2}% + \pr@callafter\next#1]\pr@endparse}} +\newcommand\pr@starmacro[1][]{\toks@{\pr@protect@domacro{#1}}% + \long\edef\next[##1]##2{% + \noexpand\pr@advise##2{\the\toks@{##1}}}% + \@ifnextchar[\next{\next[]}} +\def\PreviewOpen{\@ifstar\pr@starmacro\pr@open} +\newcommand\pr@open[1][]{% + \toks@{\pr@domacro{#1}}% + \long\edef\next[##1]##2{% + \noexpand\pr@advise##2{\begingroup + \noexpand\pr@protect@ship + {\the\toks@{\begingroup\aftergroup\noexpand\pr@endbox##1}}% + {\endgroup}}}% + \@ifnextchar[\next\pr@macroii} +\def\PreviewClose{\@ifstar\pr@starmacro\pr@close} +\newcommand\pr@close[1][]{% + \toks@{\pr@domacro{#1}}% + \long\edef\next[##1]##2{% + \noexpand\pr@advise{##2}{\the\toks@{##1\endgroup}}}% + \@ifnextchar[\next\pr@macroii} +\def\PreviewEnvironment{\@ifstar\pr@starenv\pr@env} +\newcommand\pr@starenv[1][]{\toks@{\pr@starmacro[{#1}]}% + \long\edef\next##1##2{% + \the\toks@[{##2}]##1}% + \begingroup\pr@starenvii} +\newcommand\pr@starenvii[2][]{\endgroup + \expandafter\next\csname#2\endcsname{#1}% + \expandafter\pr@starmacro\csname end#2\endcsname} +\newcommand\pr@env[1][]{% + \toks@{\pr@domacro{#1}}% + \long\edef\next[##1]##2{% + \noexpand\expandafter\noexpand\pr@advise@ship + \noexpand\csname##2\noexpand\endcsname{\the\toks@ + {\begingroup\aftergroup\noexpand\pr@endbox##1}}{\endgroup}}% + \@ifnextchar[\next\pr@macroii %] + } +\newcommand{\PreviewSnarfEnvironment}[2][]{% + \expandafter\pr@advise + \csname #2\endcsname{\pr@snarfafter{#1}}% + \expandafter\pr@advise + \csname end#2\endcsname{\pr@endsnarf}} +\let\pr@ship@start\@empty +\let\pr@ship@end\@empty +\newenvironment{preview}{\ignorespaces}{\ifhmode\unskip\fi} +\newenvironment{nopreview}{\ignorespaces}{\ifhmode\unskip\fi} +\ProcessOptions\relax +\ifPreview\else\expandafter\endinput\fi +%% The preview style for extracting previews from LaTeX documents. +%% Developed as part of AUCTeX . +\newif\ifpr@outer +\pr@outertrue +\newcount\pr@snippet +\global\pr@snippet=1 +\def\pr@protect{\ifx\protect\@typeset@protect + \ifpr@outer \expandafter\expandafter\expandafter + \@secondoftwo\fi\fi\@gobble} +\def\pr@protect@ship{\pr@protect{\@firstoftwo\pr@startbox}% + \@gobbletwo} +\def\pr@insert{\begingroup\afterassignment\pr@insertii\count@} +\def\pr@insertii{\endgroup\setbox\pr@box\vbox} +\def\pr@mark{{\afterassignment}\toks@} +\def\pr@marks{{\aftergroup\pr@mark\afterassignment}\count@} +\newbox\pr@box +\long\def\pr@startbox#1#2{% + \ifpr@outer + \toks@{#2}% + \edef\pr@cleanup{\the\toks@}% + \setbox\pr@box\vbox\bgroup + \break + \pr@outerfalse\@arrayparboxrestore + \let\insert\pr@insert + \let\mark\pr@mark + \let\marks\pr@marks + \expandafter\expandafter\expandafter + \pr@ship@start + \expandafter\@firstofone + \else + \expandafter \@gobble + \fi{#1}} +\def\pr@endbox{% + \let\reserved@a\relax + \ifvmode \edef\reserved@a{\the\everypar}% + \ifx\reserved@a\@empty\else + \dimen@\prevdepth + \noindent\par + \setbox\z@\lastbox\unskip\unpenalty + \prevdepth\dimen@ + \setbox\z@\hbox\bgroup\penalty-\maxdimen\unhbox\z@ + \ifnum\lastpenalty=-\maxdimen\egroup + \else\egroup\box\z@ \fi\fi\fi + \ifhmode \par\unskip\setbox\z@\lastbox + \nointerlineskip\hbox{\unhbox\z@\/}% + \else \unskip\unpenalty\unskip \fi + \egroup + \setbox\pr@box\vbox{% + \baselineskip\z@skip \lineskip\z@skip \lineskiplimit\z@ + \@begindvi + \nointerlineskip + \splittopskip\z@skip\setbox\z@\vsplit\pr@box to\z@ + \unvbox\z@ + \nointerlineskip + %\color@setgroup + \box\pr@box + %\color@endgroup + }% + \pr@ship@end + {\let\protect\noexpand + \ifx\pr@offset@override\@undefined + \voffset=-\ht\pr@box + \hoffset=\z@ + \fi + \c@page=\pr@snippet + \pr@shipout + \ifpr@fixbb\hbox{% + \dimen@\wd\pr@box + \@tempdima\ht\pr@box + \@tempdimb\dp\pr@box + \box\pr@box + \llap{\raise\@tempdima\copy\pr@markerbox\kern\dimen@}% + \lower\@tempdimb\copy\pr@markerbox}% + \else \box\pr@box \fi}% + \global\advance\pr@snippet\@ne + \pr@cleanup +} +\let\pr@shipout=\shipout +\def\shipout{\deadcycles\z@\bgroup\setbox\z@\box\voidb@x + \afterassignment\pr@shipoutegroup\setbox\z@} +\def\pr@shipoutegroup{\ifvoid\z@ \expandafter\aftergroup\fi \egroup} +\def\pr@parseit#1{\csname pr@parse#1\endcsname} +\let\pr@endparse=\@percentchar +\def\next#1{% +\def\pr@callafter{% + \afterassignment\pr@parseit + \let#1= }} +\expandafter\next\csname pr@parse\pr@endparse\endcsname +\long\expandafter\def\csname pr@parse*\endcsname#1\pr@endparse#2{% + \begingroup\toks@{#1\pr@endparse{#2}}% + \edef\next##1{\endgroup##1\the\toks@}% + \@ifstar{\next{\pr@parse@*}}{\next\pr@parseit}} +\long\expandafter\def\csname pr@parse[\endcsname#1\pr@endparse#2{% + \begingroup\toks@{#1\pr@endparse{#2}}% + \edef\next##1{\endgroup##1\the\toks@}% + \@ifnextchar[{\next\pr@bracket}{\next\pr@parseit}} +\long\def\pr@bracket#1\pr@endparse#2[#3]{% + \pr@parseit#1\pr@endparse{#2[{#3}]}} +\expandafter\let\csname pr@parse]\endcsname=\pr@parseit +\long\def\pr@parse#1\pr@endparse#2#3{% + \pr@parseit#1\pr@endparse{#2{#3}}} +\expandafter\let\csname pr@parse!\endcsname=\pr@parse +\long\expandafter\def\csname pr@parse?\endcsname#1#2\pr@endparse#3{% + \begingroup\toks@{#2\pr@endparse{#3}}% + \@ifnextchar#1{\pr@parsecond\@firstoftwo}% + {\pr@parsecond\@secondoftwo}} +\def\pr@parsecond#1{\expandafter\endgroup + \expandafter\expandafter\expandafter\pr@parseit + \expandafter#1\the\toks@} + \long\def\pr@parse@#1#2\pr@endparse#3{% + \pr@parseit #2\pr@endparse{#3#1}} +\long\expandafter\def\csname pr@parse-\endcsname + #1\pr@endparse#2{\begingroup + \toks@{\endgroup\pr@parseit #1\pr@endparse{#2}}% + {\aftergroup\the\aftergroup\toks@ \afterassignment}% + \let\next= } +\long\expandafter\def\csname pr@parse:\endcsname + #1#2#3\pr@endparse#4{\begingroup + \toks@{\endgroup \pr@parseit#3\pr@endparse{#4}}% + \long\def\next#1{#2}% + \the\expandafter\toks@\next} +\long\expandafter\def\csname pr@parse#\endcsname + #1#2#3\pr@endparse#4{\begingroup + \toks@{#4}% + \long\edef\next##1{\toks@{\the\toks@##1}}% + \toks@{\endgroup \pr@parseit#3\pr@endparse}% + \long\def\reserved@a#1{{#2}}% + \the\expandafter\next\reserved@a} +\def\pr@endaftergroup#1{#1\aftergroup\pr@endbox} +\let\pr@endsnarf\relax +\long\def\pr@snarfafter#1{\ifpr@outer + \pr@ship@start + \let\pr@ship@start\relax + \let\pr@endsnarf\endgroup + \else + \let\pr@endsnarf\relax + \fi + \pr@protect{\pr@callafter\pr@startsnarf#1]\pr@endparse}} +\def\pr@startsnarf#1{#1\begingroup + \pr@startbox{\begingroup\aftergroup\pr@endbox}{\endgroup}% + \ignorespaces} +\renewenvironment{preview}{\begingroup + \pr@startbox{\begingroup\aftergroup\pr@endbox}% + {\endgroup}% + \ignorespaces}% + {\ifhmode\unskip\fi\endgroup} +\renewenvironment{nopreview}{\pr@outerfalse\ignorespaces}% + {\ifhmode\unskip\fi} +\newtoks\pr@output +\pr@output\output +\output{% + \pr@outerfalse + \let\@begindvi\@empty + \the\pr@output} +\let\output\pr@output +\def\pr@typeinfos{\typeout{Preview: Fontsize \f@size pt}% + \ifnum\mag=\@m\else\typeout{Preview: Magnification \number\mag}\fi + \ifx\pdfoutput\@undefined + \ifx\XeTeXversion\@undefined \else + % FIXME: The message should not be emitted if XeTeX does not produce + % PDF. There does not seem to be a primitive for that, though. + \typeout{Preview: PDFoutput 1}% + \fi + \else + \ifx\pdfoutput\relax \else + \ifnum\pdfoutput>\z@ + \typeout{Preview: PDFoutput 1}% + \fi + \fi + \fi +} +\AtBeginDocument{\pr@typeinfos} +\pr@loadcfg{prdefault} +\endinput +%% +%% End of file `preview.sty'. diff --git a/src/tools/TeXRenderer.cpp b/src/tools/TeXRenderer.cpp index 51a089a69..07e213a27 100644 --- a/src/tools/TeXRenderer.cpp +++ b/src/tools/TeXRenderer.cpp @@ -1,315 +1,327 @@ /*************************************************************************** File : TeXRenderer.cc Project : LabPlot Description : TeX renderer class -------------------------------------------------------------------- Copyright : (C) 2008-2016 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2012 by Stefan Gerlach (stefan.gerlach@uni-konstanz.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "TeXRenderer.h" #include "backend/lib/macros.h" #include #include #include #include #include #include #include #include #include /*! \class TeXRenderer \brief Implements rendering of latex code to a PNG image. Uses latex engine specified by the user (default xelatex) to render LaTeX text \ingroup tools */ QImage TeXRenderer::renderImageLaTeX(const QString& teXString, bool* success, const TeXRenderer::Formatting& format) { const QColor& fontColor = format.fontColor; const QColor& backgroundColor = format.backgroundColor; const int fontSize = format.fontSize; const QString& fontFamily = format.fontFamily; const int dpi = format.dpi; //determine the temp directory where the produced files are going to be created QString tempPath; #ifdef Q_OS_LINUX //on linux try to use shared memory device first if available static bool useShm = QDir("/dev/shm/").exists(); if (useShm) tempPath = "/dev/shm/"; else tempPath = QDir::tempPath(); #else tempPath = QDir::tempPath(); #endif + // make sure we have preview.sty available + if (!tempPath.contains(QLatin1String("preview.sty"))) { + QString file = QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String("latex/preview.sty")); + if (file.isEmpty()) { + WARN("Couldn't find preview.sty."); + *success = false; + return QImage(); + } + else + QFile::copy(file, tempPath + QDir::separator() + QLatin1String("preview.sty")); + } + //create a temporary file QTemporaryFile file(tempPath + QDir::separator() + "labplot_XXXXXX.tex"); // FOR DEBUG: file.setAutoRemove(false); // DEBUG("temp file path = " << file.fileName().toUtf8().constData()); if (file.open()) { QDir::setCurrent(tempPath); } else { WARN(QString("Couldn't open the file " + file.fileName()).toStdString()); *success = false; return QImage(); } //determine latex engine to be used KConfigGroup group = KSharedConfig::openConfig()->group("Settings_Worksheet"); QString engine = group.readEntry("LaTeXEngine", "pdflatex"); // create latex code QTextStream out(&file); int headerIndex = teXString.indexOf("\\begin{document}"); QString body; if (headerIndex != -1) { //user provided a complete latex document -> extract the document header and body QString header = teXString.left(headerIndex); int footerIndex = teXString.indexOf("\\end{document}"); body = teXString.mid(headerIndex + 16, footerIndex - headerIndex - 16); out << header; } else { //user simply provided a document body (assume it's a math. expression) -> add a minimal header out << "\\documentclass{minimal}"; if (teXString.indexOf('$') == -1) body = '$' + teXString + '$'; else body = teXString; //replace line breaks with tex command for a line break '\\' body = body.replace(QLatin1String("\n"), QLatin1String("\\\\")); } if (engine == "xelatex" || engine == "lualatex") { out << "\\usepackage{xltxtra}"; out << "\\defaultfontfeatures{Ligatures=TeX}"; if (!fontFamily.isEmpty()) out << "\\setmainfont[Mapping=tex-text]{" << fontFamily << "}"; } out << "\\usepackage{color}"; out << "\\usepackage[active,displaymath,textmath,tightpage]{preview}"; // TODO: this fails with pdflatex //out << "\\usepackage{mathtools}"; out << "\\begin{document}"; out << "\\begin{preview}"; out << "\\colorbox[rgb]{" << backgroundColor.redF() << ',' << backgroundColor.greenF() << ',' << backgroundColor.blueF() << "}{"; out << "\\fontsize{" << QString::number(fontSize) << "}{" << QString::number(fontSize) << "}\\selectfont"; out << "\\color[rgb]{" << fontColor.redF() << ',' << fontColor.greenF() << ',' << fontColor.blueF() << "}"; out << body; out << "}"; out << "\\end{preview}"; out << "\\end{document}"; out.flush(); if (engine == "latex") return imageFromDVI(file, dpi, success); else return imageFromPDF(file, dpi, engine, success); } // TEX -> PDF -> PNG QImage TeXRenderer::imageFromPDF(const QTemporaryFile& file, const int dpi, const QString& engine, bool* success) { QFileInfo fi(file.fileName()); const QString& baseName = fi.completeBaseName(); // pdflatex: produce the PDF file QProcess latexProcess; #if defined(HAVE_WINDOWS) latexProcess.setNativeArguments("-interaction=batchmode " + file.fileName()); latexProcess.start(engine, QStringList() << QString()); #else latexProcess.start(engine, QStringList() << "-interaction=batchmode" << file.fileName()); #endif if (!latexProcess.waitForFinished() || latexProcess.exitCode() != 0) { WARN("pdflatex process failed, exit code = " << latexProcess.exitCode()); *success = false; QFile::remove(baseName + ".aux"); QFile::remove(baseName + ".log"); return QImage(); } // convert: PDF -> PNG QProcess convertProcess; #if defined(HAVE_WINDOWS) // need to set path to magick coder modules (which are in the labplot2 directory) QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("MAGICK_CODER_MODULE_PATH", qPrintable(qgetenv("PROGRAMFILES") + QString("\\labplot2"))); convertProcess.setProcessEnvironment(env); #endif const QStringList params{"-density", QString::number(dpi), baseName + ".pdf", baseName + ".png"}; convertProcess.start("convert", params); if (!convertProcess.waitForFinished() || convertProcess.exitCode() != 0) { WARN("convert process failed, exit code = " << convertProcess.exitCode()); *success = false; QFile::remove(baseName + ".aux"); QFile::remove(baseName + ".log"); QFile::remove(baseName + ".pdf"); return QImage(); } // read png file QImage image(baseName + ".png", "png"); // final clean up QFile::remove(baseName + ".aux"); QFile::remove(baseName + ".log"); QFile::remove(baseName + ".pdf"); QFile::remove(baseName + ".png"); *success = true; return image; } // TEX -> DVI -> PS -> PNG QImage TeXRenderer::imageFromDVI(const QTemporaryFile& file, const int dpi, bool* success) { QFileInfo fi(file.fileName()); const QString& baseName = fi.completeBaseName(); //latex: produce the DVI file QProcess latexProcess; latexProcess.start("latex", QStringList() << "-interaction=batchmode" << file.fileName()); if (!latexProcess.waitForFinished() || latexProcess.exitCode() != 0) { WARN("latex process failed, exit code = " << latexProcess.exitCode()); *success = false; QFile::remove(baseName + ".aux"); QFile::remove(baseName + ".log"); return QImage(); } // dvips: DVI -> PS QProcess dvipsProcess; dvipsProcess.start("dvips", QStringList() << "-E" << baseName); if (!dvipsProcess.waitForFinished() || dvipsProcess.exitCode() != 0) { WARN("dvips process failed, exit code = " << dvipsProcess.exitCode()); *success = false; QFile::remove(baseName + ".aux"); QFile::remove(baseName + ".log"); QFile::remove(baseName + ".dvi"); return QImage(); } // convert: PS -> PNG QProcess convertProcess; #if defined(HAVE_WINDOWS) // need to set path to magick coder modules (which are in the labplot2 directory) QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("MAGICK_CODER_MODULE_PATH", qPrintable(qgetenv("PROGRAMFILES") + QString("\\labplot2"))); convertProcess.setProcessEnvironment(env); #endif const QStringList params{"-density", QString::number(dpi), baseName + ".ps", baseName + ".png"}; convertProcess.start("convert", params); if (!convertProcess.waitForFinished() || convertProcess.exitCode() != 0) { WARN("convert process failed, exit code = " << convertProcess.exitCode()); *success = false; QFile::remove(baseName + ".aux"); QFile::remove(baseName + ".log"); QFile::remove(baseName + ".dvi"); QFile::remove(baseName + ".ps"); return QImage(); } // read png file QImage image(baseName + ".png", "png"); // final clean up QFile::remove(baseName + ".aux"); QFile::remove(baseName + ".log"); QFile::remove(baseName + ".dvi"); QFile::remove(baseName + ".ps"); QFile::remove(baseName + ".png"); *success = true; return image; } bool TeXRenderer::enabled() { KConfigGroup group = KSharedConfig::openConfig()->group("Settings_Worksheet"); QString engine = group.readEntry("LaTeXEngine", ""); if (engine.isEmpty()) { //empty string was found in the settings (either the settings never saved or no tex engine was available during the last save) //->check whether the latex environment was installed in the meantime engine = QLatin1String("xelatex"); if (!executableExists(engine)) { engine = QLatin1String("lualatex"); if (!executableExists(engine)) { engine = QLatin1String("pdflatex"); if (!executableExists(engine)) engine = QLatin1String("latex"); } } if (!engine.isEmpty()) { //one of the tex engines was found -> automatically save it in the settings without any user action group.writeEntry(QLatin1String("LaTeXEngine"), engine); group.sync(); } } else if (!executableExists(engine)) { WARN("LaTeX engine does not exist"); return false; } //engine found, check the presence of other required tools (s.a. TeXRenderer.cpp): //to convert the generated PDF/PS files to PNG we need 'convert' from the ImageMagic package if (!executableExists(QLatin1String("convert"))) { WARN("program \"convert\" does not exist"); return false; } //to convert the generated PS files to DVI we need 'dvips' if (engine == "latex") { if (!executableExists(QLatin1String("dvips"))) { WARN("program \"dvips\" does not exist"); return false; } } #if defined(_WIN64) if (!executableExists(QLatin1String("gswin64c")) && !QDir(qgetenv("PROGRAMFILES") + QString("/gs")).exists() && !QDir(qgetenv("PROGRAMFILES(X86)") + QString("/gs")).exists()) { WARN("ghostscript (64bit) does not exist"); return false; } #elif defined(HAVE_WINDOWS) if (!executableExists(QLatin1String("gswin32c")) && !QDir(qgetenv("PROGRAMFILES") + QString("/gs")).exists()) { WARN("ghostscript (32bit) does not exist"); return false; } #endif return true; } bool TeXRenderer::executableExists(const QString& exe) { return !QStandardPaths::findExecutable(exe).isEmpty(); }