Warning, file /education/cantor/src/lib/latexrenderer.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2011 Alexander Rieder <alexanderrieder@gmail.com> 0004 */ 0005 0006 #include "latexrenderer.h" 0007 using namespace Cantor; 0008 0009 #include <QProcess> 0010 #include <QDebug> 0011 #include <QDir> 0012 #include <QFileInfo> 0013 #include <QEventLoop> 0014 #include <QTemporaryFile> 0015 #include <KColorScheme> 0016 #include <QUuid> 0017 #include <QApplication> 0018 0019 #include <config-cantorlib.h> 0020 #include "settings.h" 0021 0022 class Cantor::LatexRendererPrivate 0023 { 0024 public: 0025 QString latexCode; 0026 QString header; 0027 LatexRenderer::Method method; 0028 bool isEquationOnly; 0029 LatexRenderer::EquationType equationType; 0030 QString errorMessage; 0031 bool success; 0032 QString latexFilename; 0033 QString epsFilename; 0034 QString uuid; 0035 QTemporaryFile* texFile; 0036 }; 0037 0038 static const QLatin1String tex("\\documentclass[fleqn]{article}"\ 0039 "\\usepackage{latexsym,amsfonts,amssymb,ulem}"\ 0040 "\\usepackage{amsmath}"\ 0041 "\\usepackage[dvips]{graphicx}"\ 0042 "\\usepackage[utf8]{inputenc}"\ 0043 "\\usepackage{xcolor}"\ 0044 "\\setlength\\textwidth{5in}"\ 0045 "\\setlength{\\parindent}{0pt}"\ 0046 "%1"\ 0047 "\\pagecolor[rgb]{%2,%3,%4}"\ 0048 "\\pagestyle{empty}"\ 0049 "\\begin{document}"\ 0050 "\\color[rgb]{%5,%6,%7}"\ 0051 "\\fontsize{%8}{%8}\\selectfont\n"\ 0052 "%9\n"\ 0053 "\\end{document}"); 0054 0055 static const QLatin1String eqnHeader("\\begin{eqnarray*}%1\\end{eqnarray*}"); 0056 static const QLatin1String inlineEqnHeader("$%1$"); 0057 0058 LatexRenderer::LatexRenderer(QObject* parent) : QObject(parent), 0059 d(new LatexRendererPrivate) 0060 { 0061 d->method=LatexMethod; 0062 d->isEquationOnly=false; 0063 d->equationType=InlineEquation; 0064 d->success=false; 0065 d->texFile=nullptr; 0066 } 0067 0068 LatexRenderer::~LatexRenderer() 0069 { 0070 delete d; 0071 } 0072 0073 QString LatexRenderer::latexCode() const 0074 { 0075 return d->latexCode; 0076 } 0077 0078 void LatexRenderer::setLatexCode(const QString& src) 0079 { 0080 d->latexCode=src; 0081 } 0082 0083 QString LatexRenderer::header() const 0084 { 0085 return d->header; 0086 } 0087 0088 void LatexRenderer::addHeader(const QString& header) 0089 { 0090 d->header.append(header); 0091 } 0092 0093 void LatexRenderer::setHeader(const QString& header) 0094 { 0095 d->header=header; 0096 } 0097 0098 LatexRenderer::Method LatexRenderer::method() const 0099 { 0100 return d->method; 0101 } 0102 0103 void LatexRenderer::setMethod(LatexRenderer::Method method) 0104 { 0105 d->method=method; 0106 } 0107 0108 void LatexRenderer::setEquationType(LatexRenderer::EquationType type) 0109 { 0110 d->equationType=type; 0111 } 0112 0113 LatexRenderer::EquationType LatexRenderer::equationType() const 0114 { 0115 return d->equationType; 0116 } 0117 0118 0119 void LatexRenderer::setErrorMessage(const QString& msg) 0120 { 0121 d->errorMessage=msg; 0122 } 0123 0124 QString LatexRenderer::errorMessage() const 0125 { 0126 return d->errorMessage; 0127 } 0128 0129 bool LatexRenderer::renderingSuccessful() const 0130 { 0131 return d->success; 0132 } 0133 0134 void LatexRenderer::setEquationOnly(bool isEquationOnly) 0135 { 0136 d->isEquationOnly=isEquationOnly; 0137 } 0138 0139 bool LatexRenderer::isEquationOnly() const 0140 { 0141 return d->isEquationOnly; 0142 } 0143 0144 0145 QString LatexRenderer::imagePath() const 0146 { 0147 return d->epsFilename; 0148 } 0149 0150 QString Cantor::LatexRenderer::uuid() const 0151 { 0152 return d->uuid; 0153 } 0154 0155 bool LatexRenderer::render() 0156 { 0157 switch(d->method) 0158 { 0159 case LatexRenderer::LatexMethod: 0160 return renderWithLatex(); 0161 0162 case LatexRenderer::MmlMethod: 0163 return renderWithMml(); 0164 0165 default: 0166 return false; 0167 }; 0168 } 0169 0170 void LatexRenderer::renderBlocking() 0171 { 0172 QEventLoop event; 0173 connect(this, &LatexRenderer::done, &event, &QEventLoop::quit); 0174 connect(this, &LatexRenderer::error, &event, &QEventLoop::quit); 0175 0176 bool success = render(); 0177 // We can't emit error before running event loop, so exit by passing false as an error indicator 0178 if (success) 0179 event.exec(); 0180 else 0181 return; 0182 } 0183 0184 bool LatexRenderer::renderWithLatex() 0185 { 0186 qDebug()<<"rendering using latex method"; 0187 QString dir=QStandardPaths::writableLocation(QStandardPaths::TempLocation); 0188 0189 if (d->texFile) 0190 delete d->texFile; 0191 0192 d->texFile=new QTemporaryFile(dir + QDir::separator() + QLatin1String("cantor_tex-XXXXXX.tex")); 0193 d->texFile->open(); 0194 0195 KColorScheme scheme(QPalette::Active); 0196 const QColor &backgroundColor=scheme.background().color(); 0197 const QColor &foregroundColor=scheme.foreground().color(); 0198 QString expressionTex=tex; 0199 expressionTex=expressionTex.arg(d->header) 0200 .arg(backgroundColor.redF()).arg(backgroundColor.greenF()).arg(backgroundColor.blueF()) 0201 .arg(foregroundColor.redF()).arg(foregroundColor.greenF()).arg(foregroundColor.blueF()); 0202 0203 int fontPointSize = QApplication::font().pointSize(); 0204 expressionTex=expressionTex.arg(fontPointSize); 0205 0206 if(isEquationOnly()) 0207 { 0208 switch(equationType()) 0209 { 0210 case FullEquation: expressionTex=expressionTex.arg(eqnHeader); break; 0211 case InlineEquation: expressionTex=expressionTex.arg(inlineEqnHeader); break; 0212 case CustomEquation: expressionTex=expressionTex.arg(QLatin1String("%1")); break; 0213 } 0214 } 0215 expressionTex=expressionTex.arg(d->latexCode); 0216 0217 // qDebug()<<"full tex:\n"<<expressionTex; 0218 0219 d->texFile->write(expressionTex.toUtf8()); 0220 d->texFile->flush(); 0221 0222 QString fileName = d->texFile->fileName(); 0223 qDebug()<<"fileName: "<<fileName; 0224 d->latexFilename=fileName; 0225 QProcess *p=new QProcess( this ); 0226 p->setWorkingDirectory(dir); 0227 0228 d->uuid = genUuid(); 0229 0230 qDebug() << Settings::self()->latexCommand(); 0231 QFileInfo info(Settings::self()->latexCommand()); 0232 if (info.exists() && info.isExecutable()) 0233 { 0234 p->setProgram(Settings::self()->latexCommand()); 0235 p->setArguments({QStringLiteral("-jobname=cantor_") + d->uuid, QStringLiteral("-halt-on-error"), fileName}); 0236 0237 connect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(convertToPs()) ); 0238 p->start(); 0239 return true; 0240 } 0241 else 0242 { 0243 setErrorMessage(QStringLiteral("failed to find latex executable")); 0244 return false; 0245 } 0246 } 0247 0248 void LatexRenderer::convertToPs() 0249 { 0250 const QString& dir=QStandardPaths::writableLocation(QStandardPaths::TempLocation); 0251 0252 QString dviFile = dir + QDir::separator() + QStringLiteral("cantor_") + d->uuid + QStringLiteral(".dvi"); 0253 d->epsFilename = dir + QDir::separator() + QLatin1String("cantor_")+d->uuid+QLatin1String(".eps"); 0254 0255 QProcess *p=new QProcess( this ); 0256 qDebug()<<"converting to eps: "<<Settings::self()->dvipsCommand()<<"-E"<<"-o"<<d->epsFilename<<dviFile; 0257 0258 QFileInfo info(Settings::self()->dvipsCommand()); 0259 if (info.exists() && info.isExecutable()) 0260 { 0261 p->setProgram(Settings::self()->dvipsCommand()); 0262 p->setArguments({QStringLiteral("-E"), QStringLiteral("-q"), QStringLiteral("-o"), d->epsFilename, dviFile}); 0263 0264 connect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(convertingDone()) ); 0265 p->start(); 0266 } 0267 else 0268 { 0269 setErrorMessage(QStringLiteral("failed to find dvips executable")); 0270 emit error(); 0271 } 0272 } 0273 0274 void LatexRenderer::convertingDone() 0275 { 0276 QFileInfo info(d->epsFilename); 0277 qDebug() <<"remove temporary files for " << d->latexFilename; 0278 0279 QString pathWithoutExtension = info.path() + QDir::separator() + info.completeBaseName(); 0280 QFile::remove(pathWithoutExtension + QLatin1String(".log")); 0281 QFile::remove(pathWithoutExtension + QLatin1String(".aux")); 0282 QFile::remove(pathWithoutExtension + QLatin1String(".dvi")); 0283 0284 if(info.exists()) 0285 { 0286 delete d->texFile; 0287 d->texFile = nullptr; 0288 0289 d->success=true; 0290 emit done(); 0291 } 0292 else 0293 { 0294 d->success=false; 0295 setErrorMessage(QStringLiteral("failed to create the latex preview image")); 0296 emit error(); 0297 } 0298 } 0299 0300 bool LatexRenderer::renderWithMml() 0301 { 0302 qWarning()<<"WARNING: MML rendering not implemented yet!"; 0303 emit error(); 0304 return false; 0305 } 0306 0307 QString LatexRenderer::genUuid() 0308 { 0309 QString uuid = QUuid::createUuid().toString(); 0310 uuid.remove(0, 1); 0311 uuid.chop(1); 0312 uuid.replace(QLatin1Char('-'), QLatin1Char('_')); 0313 return uuid; 0314 } 0315 0316 bool Cantor::LatexRenderer::isLatexAvailable() 0317 { 0318 QFileInfo infoLatex(Settings::self()->latexCommand()); 0319 QFileInfo infoPs(Settings::self()->dvipsCommand()); 0320 return infoLatex.exists() && infoLatex.isExecutable() && infoPs.exists() && infoPs.isExecutable(); 0321 }