okular/part/latexrenderer.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

202 lines
7.3 KiB
C++
Raw Normal View History

2021-05-24 07:25:56 +00:00
/*
SPDX-FileCopyrightText: 2004 Duncan Mac-Vicar Prett <duncan@kde.org>
SPDX-FileCopyrightText: 2004-2005 Olivier Goffart <ogoffart@kde.org>
SPDX-FileCopyrightText: 2011 Niels Ole Salscheider
<niels_ole@salscheider-online.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
2011-09-22 17:09:26 +00:00
#include "latexrenderer.h"
2018-08-31 09:23:45 +00:00
#include <QDebug>
2014-10-17 21:30:54 +00:00
2020-07-08 11:54:37 +00:00
#include <KProcess>
2011-09-22 17:09:26 +00:00
2011-09-22 17:11:19 +00:00
#include <QColor>
#include <QDir>
#include <QFileInfo>
#include <QImage>
#include <QRegularExpression>
2014-10-17 21:30:54 +00:00
#include <QStandardPaths>
2020-07-08 11:54:37 +00:00
#include <QTemporaryFile>
2011-09-22 17:11:19 +00:00
#include <QTextStream>
#include "gui/debug_ui.h"
2014-09-11 19:12:27 +00:00
2011-09-22 17:09:26 +00:00
namespace GuiUtils
{
LatexRenderer::LatexRenderer()
{
}
LatexRenderer::~LatexRenderer()
{
for (const QString &file : qAsConst(m_fileList)) {
QFile::remove(file);
}
}
2011-09-22 17:09:26 +00:00
LatexRenderer::Error LatexRenderer::renderLatexInHtml(QString &html, const QColor &textColor, int fontSize, int resolution, QString &latexOutput)
{
2015-10-29 12:37:11 +00:00
if (!html.contains(QStringLiteral("$$"))) {
2011-09-22 17:09:26 +00:00
return NoError;
}
2011-09-22 17:09:26 +00:00
// this searches for $$formula$$
QRegularExpression rg(QStringLiteral("\\$\\$.+?\\$\\$"));
QRegularExpressionMatchIterator it = rg.globalMatch(html);
2011-09-22 17:09:26 +00:00
QMap<QString, QString> replaceMap;
while (it.hasNext()) {
QRegularExpressionMatch match = it.next();
const QString matchedString = match.captured(0);
QString formul = matchedString;
// first remove the $$ delimiters on start and end
formul.remove(QStringLiteral("$$"));
// then trim the result, so we can skip totally empty/whitespace-only formulas
formul = formul.trimmed();
if (formul.isEmpty() || !securityCheck(formul)) {
continue;
2011-09-22 17:09:26 +00:00
}
// unescape formula
formul.replace(QLatin1String("&gt;"), QLatin1String(">"));
formul.replace(QLatin1String("&lt;"), QLatin1String("<"));
formul.replace(QLatin1String("&amp;"), QLatin1String("&"));
formul.replace(QLatin1String("&quot;"), QLatin1String("\""));
formul.replace(QLatin1String("&apos;"), QLatin1String("\'"));
formul.replace(QLatin1String("<br>"), QLatin1String(" "));
QString fileName;
Error returnCode = handleLatex(fileName, formul, textColor, fontSize, resolution, latexOutput);
if (returnCode != NoError) {
return returnCode;
}
replaceMap[matchedString] = fileName;
2011-09-22 17:09:26 +00:00
}
2011-09-22 17:09:26 +00:00
if (replaceMap.isEmpty()) { // we haven't found any LaTeX strings
return NoError;
}
2011-09-22 17:09:26 +00:00
int imagePxWidth, imagePxHeight;
for (QMap<QString, QString>::ConstIterator it = replaceMap.constBegin(); it != replaceMap.constEnd(); ++it) {
QImage theImage(*it);
if (theImage.isNull()) {
continue;
}
2011-09-22 17:09:26 +00:00
imagePxWidth = theImage.width();
imagePxHeight = theImage.height();
QString escapedLATEX = it.key().toHtmlEscaped().replace(QLatin1Char('"'), QLatin1String("&quot;")); // we need the escape quotes because that string will be in a title="" argument, but not the \n
html.replace(it.key(),
QStringLiteral(" <img width=\"") + QString::number(imagePxWidth) + QStringLiteral("\" height=\"") + QString::number(imagePxHeight) + QStringLiteral("\" align=\"middle\" src=\"") + (*it) + QStringLiteral("\" alt=\"") +
escapedLATEX + QStringLiteral("\" title=\"") + escapedLATEX + QStringLiteral("\" /> "));
2011-09-22 17:09:26 +00:00
}
return NoError;
}
bool LatexRenderer::mightContainLatex(const QString &text)
{
2015-10-29 12:37:11 +00:00
if (!text.contains(QStringLiteral("$$"))) {
2011-09-22 17:09:26 +00:00
return false;
}
2011-09-22 17:09:26 +00:00
// this searches for $$formula$$
QRegularExpression rg(QStringLiteral("\\$\\$.+?\\$\\$"));
if (!rg.match(text).hasMatch()) {
2011-09-22 17:09:26 +00:00
return false;
}
2011-09-22 17:09:26 +00:00
return true;
}
LatexRenderer::Error LatexRenderer::handleLatex(QString &fileName, const QString &latexFormula, const QColor &textColor, int fontSize, int resolution, QString &latexOutput)
{
KProcess latexProc;
KProcess dvipngProc;
2014-09-17 22:30:39 +00:00
QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/okular_kdelatex-XXXXXX.tex"));
2011-09-22 17:09:26 +00:00
tempFile->open();
QString tempFileName = tempFile->fileName();
QFileInfo *tempFileInfo = new QFileInfo(tempFileName);
QString tempFileNameNS = tempFileInfo->absolutePath() + QLatin1Char('/') + tempFileInfo->baseName();
2011-09-22 17:09:26 +00:00
QString tempFilePath = tempFileInfo->absolutePath();
delete tempFileInfo;
QTextStream tempStream(tempFile);
tempStream << "\
\\documentclass["
<< fontSize << "pt]{article} \
\\usepackage{color} \
\\usepackage{amsmath,latexsym,amsfonts,amssymb,ulem} \
\\pagestyle{empty} \
\\begin{document} \
{\\color[rgb]{" << textColor.redF()
<< "," << textColor.greenF() << "," << textColor.blueF() << "} \
\\begin{eqnarray*} \
" << latexFormula
<< " \
\\end{eqnarray*}} \
\\end{document}";
tempFile->close();
2015-10-29 12:37:11 +00:00
QString latexExecutable = QStandardPaths::findExecutable(QStringLiteral("latex"));
2011-09-22 17:09:26 +00:00
if (latexExecutable.isEmpty()) {
2014-09-11 17:36:01 +00:00
qCDebug(OkularUiDebug) << "Could not find latex!";
2011-09-22 17:09:26 +00:00
delete tempFile;
2011-09-22 17:16:03 +00:00
fileName = QString();
2011-09-22 17:09:26 +00:00
return LatexNotFound;
}
2015-10-29 12:37:11 +00:00
latexProc << latexExecutable << QStringLiteral("-interaction=nonstopmode") << QStringLiteral("-halt-on-error") << QStringLiteral("-output-directory=%1").arg(tempFilePath) << tempFile->fileName();
2011-09-22 17:09:26 +00:00
latexProc.setOutputChannelMode(KProcess::MergedChannels);
latexProc.execute();
latexOutput = QString::fromLocal8Bit(latexProc.readAll());
2011-09-22 17:09:26 +00:00
tempFile->remove();
2015-10-29 12:37:11 +00:00
QFile::remove(tempFileNameNS + QStringLiteral(".log"));
QFile::remove(tempFileNameNS + QStringLiteral(".aux"));
2011-09-22 17:09:26 +00:00
delete tempFile;
2015-10-29 12:37:11 +00:00
if (!QFile::exists(tempFileNameNS + QStringLiteral(".dvi"))) {
2011-09-22 17:16:03 +00:00
fileName = QString();
2011-09-22 17:09:26 +00:00
return LatexFailed;
}
2015-10-29 12:37:11 +00:00
QString dvipngExecutable = QStandardPaths::findExecutable(QStringLiteral("dvipng"));
2011-09-22 17:09:26 +00:00
if (dvipngExecutable.isEmpty()) {
2014-09-11 17:36:01 +00:00
qCDebug(OkularUiDebug) << "Could not find dvipng!";
2011-09-22 17:16:03 +00:00
fileName = QString();
2011-09-22 17:09:26 +00:00
return DvipngNotFound;
}
2015-10-29 12:37:11 +00:00
dvipngProc << dvipngExecutable << QStringLiteral("-o%1").arg(tempFileNameNS + QStringLiteral(".png")) << QStringLiteral("-Ttight") << QStringLiteral("-bgTransparent") << QStringLiteral("-D %1").arg(resolution)
<< QStringLiteral("%1").arg(tempFileNameNS + QStringLiteral(".dvi"));
2011-09-22 17:09:26 +00:00
dvipngProc.setOutputChannelMode(KProcess::MergedChannels);
dvipngProc.execute();
2015-10-29 12:37:11 +00:00
QFile::remove(tempFileNameNS + QStringLiteral(".dvi"));
2015-10-29 12:37:11 +00:00
if (!QFile::exists(tempFileNameNS + QStringLiteral(".png"))) {
2011-09-22 17:16:03 +00:00
fileName = QString();
2011-09-22 17:09:26 +00:00
return DvipngFailed;
}
2015-10-29 12:37:11 +00:00
fileName = tempFileNameNS + QStringLiteral(".png");
m_fileList << fileName;
2011-09-22 17:09:26 +00:00
return NoError;
}
bool LatexRenderer::securityCheck(const QString &latexFormula)
{
return !latexFormula.contains(
QRegularExpression(QString::fromLatin1("\\\\(def|let|futurelet|newcommand|renewcommand|else|fi|write|input|include"
2011-09-22 17:09:26 +00:00
"|chardef|catcode|makeatletter|noexpand|toksdef|every|errhelp|errorstopmode|scrollmode|nonstopmode|batchmode"
"|read|csname|newhelp|relax|afterground|afterassignment|expandafter|noexpand|special|command|loop|repeat|toks"
2015-10-29 12:37:11 +00:00
"|output|line|mathcode|name|item|section|mbox|DeclareRobustCommand)[^a-zA-Z]")));
2011-09-22 17:09:26 +00:00
}
2011-09-22 17:11:19 +00:00
}