File indexing completed on 2024-04-28 11:20:59
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2019 Sirgienko Nikita <warquark@gmail.com> 0004 */ 0005 0006 #include "mathrendertask.h" 0007 0008 #include <QTemporaryFile> 0009 #include <QStandardPaths> 0010 #include <QUuid> 0011 #include <QDir> 0012 #include <KColorScheme> 0013 #include <KProcess> 0014 #include <QScopedPointer> 0015 #include <QApplication> 0016 #include <QDebug> 0017 0018 #include "lib/renderer.h" 0019 0020 static const QLatin1String mathTex("\\documentclass%9{minimal}"\ 0021 "\\usepackage{amsfonts,amssymb}"\ 0022 "\\usepackage{amsmath}"\ 0023 "\\usepackage[utf8]{inputenc}"\ 0024 "\\usepackage[active,displaymath,textmath,tightpage]{preview}"\ 0025 "\\usepackage{color}"\ 0026 "\\setlength\\PreviewBorder{0pt}"\ 0027 "\\begin{document}"\ 0028 "\\begin{preview}"\ 0029 "\\setlength{\\fboxsep}{0.2pt}"\ 0030 "$"\ 0031 "\\colorbox[rgb]{%1,%2,%3}{"\ 0032 "\\color[rgb]{%4,%5,%6}"\ 0033 "\\fontsize{%7}{%7}\\selectfont"\ 0034 "%8}"\ 0035 "$"\ 0036 "\\end{preview}" 0037 "\\end{document}"); 0038 0039 static const QLatin1String eqnHeader("$\\displaystyle %1$"); 0040 static const QLatin1String inlineEqnHeader("$%1$"); 0041 0042 MathRenderTask::MathRenderTask( 0043 int jobId, 0044 const QString& code, 0045 Cantor::LatexRenderer::EquationType type, 0046 double scale, 0047 bool highResolution 0048 ): m_jobId(jobId), m_code(code), m_type(type), m_scale(scale), m_highResolution(highResolution) 0049 { 0050 0051 KColorScheme scheme(QPalette::Active); 0052 m_backgroundColor = scheme.background().color(); 0053 m_foregroundColor = scheme.foreground().color(); 0054 } 0055 0056 void MathRenderTask::setHandler(const QObject* receiver, const char* resultHandler) 0057 { 0058 connect(this, SIGNAL(finish(QSharedPointer<MathRenderResult>)), receiver, resultHandler); 0059 } 0060 0061 void MathRenderTask::run() 0062 { 0063 qDebug()<<"MathRenderTask::run " << m_jobId; 0064 QSharedPointer<MathRenderResult> result(new MathRenderResult()); 0065 0066 const QString& tempDir=QStandardPaths::writableLocation(QStandardPaths::TempLocation); 0067 0068 QTemporaryFile texFile(tempDir + QDir::separator() + QLatin1String("cantor_tex-XXXXXX.tex")); 0069 texFile.open(); 0070 0071 // make sure we have preview.sty available 0072 if (!tempDir.contains(QLatin1String("preview.sty"))) 0073 { 0074 QString file = QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String("latex/preview.sty")); 0075 0076 if (file.isEmpty()) 0077 file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("cantor/latex/preview.sty")); 0078 0079 if (file.isEmpty()) 0080 { 0081 result->successful = false; 0082 result->errorMessage = QString::fromLatin1("LaTeX style file preview.sty not found."); 0083 finalize(result); 0084 return; 0085 } 0086 else 0087 QFile::copy(file, tempDir + QDir::separator() + QLatin1String("preview.sty")); 0088 } 0089 QString expressionTex=mathTex; 0090 0091 expressionTex=expressionTex 0092 .arg(m_backgroundColor.redF()).arg(m_backgroundColor.greenF()).arg(m_backgroundColor.blueF()) 0093 .arg(m_foregroundColor.redF()).arg(m_foregroundColor.greenF()).arg(m_foregroundColor.blueF()); 0094 int fontPointSize = QApplication::font().pointSize(); 0095 expressionTex=expressionTex.arg(fontPointSize); 0096 0097 switch(m_type) 0098 { 0099 case Cantor::LatexRenderer::FullEquation: 0100 expressionTex=expressionTex.arg(eqnHeader, QString()); 0101 break; 0102 case Cantor::LatexRenderer::InlineEquation: 0103 expressionTex=expressionTex.arg(inlineEqnHeader, QString()); 0104 break; 0105 case Cantor::LatexRenderer::CustomEquation: 0106 expressionTex=expressionTex.arg(QLatin1String("%1"), QLatin1String("[preview]")); 0107 break; 0108 } 0109 0110 QString latex = m_code; 0111 // Looks hacky, but no sure, how do it better without overhead (like new latex type in lib/latexrender) 0112 static const QString& equationBegin = QLatin1String("\\begin{equation}"); 0113 static const QString& equationEnd = QLatin1String("\\end{equation}"); 0114 if (latex.startsWith(equationBegin) && latex.endsWith(equationEnd)) 0115 { 0116 latex.remove(0, equationBegin.size()); 0117 latex.chop(equationEnd.size()); 0118 latex = QLatin1String("\\begin{equation*}") + latex + QLatin1String("\\end{equation*}"); 0119 } 0120 0121 expressionTex=expressionTex.arg(latex); 0122 0123 texFile.write(expressionTex.toUtf8()); 0124 texFile.flush(); 0125 0126 QProcess p; 0127 p.setWorkingDirectory(tempDir); 0128 0129 // Create unique uuid for this job 0130 // It will be used as pdf filename, for preventing names collisions 0131 // And as internal url path too 0132 const QString& uuid = Cantor::LatexRenderer::genUuid(); 0133 0134 const QString& pdflatex = QStandardPaths::findExecutable(QLatin1String("pdflatex")); 0135 p.setProgram(pdflatex); 0136 p.setArguments({QStringLiteral("-jobname=cantor_") + uuid, QStringLiteral("-halt-on-error"), texFile.fileName()}); 0137 0138 p.start(); 0139 p.waitForFinished(); 0140 0141 if (p.exitCode() != 0) 0142 { 0143 // pdflatex render failed and we haven't pdf file 0144 result->successful = false; 0145 0146 QString renderErrorText = QString::fromUtf8(p.readAllStandardOutput()); 0147 renderErrorText.remove(0, renderErrorText.indexOf(QLatin1Char('!'))); 0148 renderErrorText.remove(renderErrorText.indexOf(QLatin1String("! ==> Fatal error occurred")), renderErrorText.size()); 0149 renderErrorText = renderErrorText.trimmed(); 0150 result->errorMessage = renderErrorText; 0151 0152 finalize(result); 0153 texFile.setAutoRemove(false); //Useful for debug 0154 return; 0155 } 0156 0157 //Clean up .aux and .log files 0158 QString pathWithoutExtension = tempDir + QDir::separator() + QLatin1String("cantor_")+uuid; 0159 QFile::remove(pathWithoutExtension + QLatin1String(".log")); 0160 QFile::remove(pathWithoutExtension + QLatin1String(".aux")); 0161 0162 // We shouldn't remove pdf file, because this file used in future in an another parts of Cantor 0163 // For example, this pdf will copied into .cws file on save 0164 const QString& pdfFileName = pathWithoutExtension + QLatin1String(".pdf"); 0165 0166 bool success; QString errorMessage; 0167 const auto& data = renderPdfToFormat(pdfFileName, m_code, uuid, m_type, m_scale, m_highResolution, &success, &errorMessage); 0168 result->successful = success; 0169 result->errorMessage = errorMessage; 0170 if (success == false) 0171 { 0172 finalize(result); 0173 return; 0174 } 0175 0176 result->renderedMath = data.first; 0177 result->image = data.second; 0178 result->jobId = m_jobId; 0179 0180 QUrl internal; 0181 internal.setScheme(QLatin1String("internal")); 0182 internal.setPath(uuid); 0183 result->uniqueUrl = internal; 0184 0185 finalize(result); 0186 } 0187 0188 void MathRenderTask::finalize(QSharedPointer<MathRenderResult> result) 0189 { 0190 emit finish(result); 0191 deleteLater(); 0192 } 0193 0194 std::pair<QTextImageFormat, QImage> MathRenderTask::renderPdfToFormat(const QString& filename, const QString& code, const QString uuid, Cantor::LatexRenderer::EquationType type, double scale, bool highResulution, bool* success, QString* errorReason) 0195 { 0196 QSizeF size; 0197 const QImage& image = Cantor::Renderer::pdfRenderToImage(QUrl::fromLocalFile(filename), scale, highResulution, &size, errorReason); 0198 if (success) 0199 *success = image.isNull() == false; 0200 0201 if (success && *success == false) 0202 return std::make_pair(QTextImageFormat(), QImage()); 0203 0204 QTextImageFormat format; 0205 0206 QUrl internal; 0207 internal.setScheme(QLatin1String("internal")); 0208 internal.setPath(uuid); 0209 0210 format.setName(internal.url()); 0211 format.setWidth(size.width()); 0212 format.setHeight(size.height()); 0213 format.setProperty(Cantor::Renderer::CantorFormula, type); 0214 format.setProperty(Cantor::Renderer::ImagePath, filename); 0215 format.setProperty(Cantor::Renderer::Code, code); 0216 format.setVerticalAlignment(QTextCharFormat::AlignBaseline); 0217 0218 switch(type) 0219 { 0220 case Cantor::LatexRenderer::FullEquation: 0221 format.setProperty(Cantor::Renderer::Delimiter, QLatin1String("$$")); 0222 break; 0223 0224 case Cantor::LatexRenderer::InlineEquation: 0225 format.setProperty(Cantor::Renderer::Delimiter, QLatin1String("$")); 0226 break; 0227 0228 case Cantor::LatexRenderer::CustomEquation: 0229 format.setProperty(Cantor::Renderer::Delimiter, QLatin1String("")); 0230 break; 0231 } 0232 0233 return std::make_pair(std::move(format), std::move(image)); 0234 }