/*************************************************************************** * Copyright (C) 2004 by Duncan Mac-Vicar Prett * * Copyright (C) 2004-2005 by Olivier Goffart * * Copyright (C) 2011 by Niels Ole Salscheider * * * * * * 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. * ***************************************************************************/ #include "latexrenderer.h" #include #include #include #include #include #include #include #include #include #include namespace GuiUtils { LatexRenderer::LatexRenderer() { } LatexRenderer::~LatexRenderer() { foreach(const QString &file, m_fileList) { QFile::remove(file); } } LatexRenderer::Error LatexRenderer::renderLatexInHtml( QString& html, const QColor& textColor, int fontSize, int resolution, QString& latexOutput ) { if( !html.contains("$$")) return NoError; // this searches for $$formula$$ QRegExp rg("\\$\\$.+\\$\\$"); rg.setMinimal(true); int pos = 0; QMap replaceMap; while (pos >= 0 && pos < html.length()) { pos = rg.indexIn(html, pos); if (pos >= 0 ) { const QString match = rg.cap(0); pos += rg.matchedLength(); QString formul=match; // first remove the $$ delimiters on start and end formul.remove("$$"); // then trim the result, so we can skip totally empty/whitespace-only formulas formul = formul.trimmed(); if (formul.isEmpty() || !securityCheck(formul)) continue; QString fileName; Error returnCode = handleLatex(fileName, formul, textColor, fontSize, resolution, latexOutput); if (returnCode != NoError) return returnCode; replaceMap[match] = fileName; } } if(replaceMap.isEmpty()) //we haven't found any LaTeX strings return NoError; int imagePxWidth,imagePxHeight; for (QMap::ConstIterator it = replaceMap.constBegin(); it != replaceMap.constEnd(); ++it) { QImage theImage(*it); if(theImage.isNull()) continue; imagePxWidth = theImage.width(); imagePxHeight = theImage.height(); QString escapedLATEX=Qt::escape(it.key()).replace('\"',"""); //we need the escape quotes because that string will be in a title="" argument, but not the \n html.replace(Qt::escape(it.key()), " \"" "); } return NoError; } bool LatexRenderer::mightContainLatex (const QString& text) { if( !text.contains("$$")) return false; // this searches for $$formula$$ QRegExp rg("\\$\\$.+\\$\\$"); rg.setMinimal(true); if( rg.lastIndexIn(text) == -1 ) return false; return true; } LatexRenderer::Error LatexRenderer::handleLatex( QString& fileName, const QString& latexFormula, const QColor& textColor, int fontSize, int resolution, QString& latexOutput ) { KProcess latexProc; KProcess dvipngProc; KTemporaryFile *tempFile = new KTemporaryFile(); tempFile->setPrefix("kdelatex-"); tempFile->setSuffix(".tex"); tempFile->open(); QString tempFileName = tempFile->fileName(); QFileInfo *tempFileInfo = new QFileInfo(tempFileName); QString tempFileNameNS = tempFileInfo->absolutePath() + QDir::separator() + tempFileInfo->baseName(); 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(); QString latexExecutable = KStandardDirs::findExe("latex"); if (latexExecutable.isEmpty()) { kDebug() << "Could not find latex!"; delete tempFile; fileName = QString(); return LatexNotFound; } latexProc << latexExecutable << "-interaction=nonstopmode" << "-halt-on-error" << QString("-output-directory=%1").arg(tempFilePath) << tempFile->fileName(); latexProc.setOutputChannelMode( KProcess::MergedChannels ); latexProc.execute(); latexOutput = latexProc.readAll(); tempFile->remove(); QFile::remove(tempFileNameNS + QString(".log")); QFile::remove(tempFileNameNS + QString(".aux")); delete tempFile; if (!QFile::exists(tempFileNameNS + QString(".dvi"))) { fileName = QString(); return LatexFailed; } QString dvipngExecutable = KStandardDirs::findExe("dvipng"); if (dvipngExecutable.isEmpty()) { kDebug() << "Could not find dvipng!"; fileName = QString(); return DvipngNotFound; } dvipngProc << dvipngExecutable << QString("-o%1").arg(tempFileNameNS + QString(".png")) << "-Ttight" << "-bgTransparent" << QString("-D %1").arg(resolution) << QString("%1").arg(tempFileNameNS + QString(".dvi")); dvipngProc.setOutputChannelMode( KProcess::MergedChannels ); dvipngProc.execute(); QFile::remove(tempFileNameNS + QString(".dvi")); if (!QFile::exists(tempFileNameNS + QString(".png"))) { fileName = QString(); return DvipngFailed; } fileName = tempFileNameNS + QString(".png"); m_fileList << fileName; return NoError; } bool LatexRenderer::securityCheck( const QString &latexFormula ) { return !latexFormula.contains(QRegExp("\\\\(def|let|futurelet|newcommand|renewcomment|else|fi|write|input|include" "|chardef|catcode|makeatletter|noexpand|toksdef|every|errhelp|errorstopmode|scrollmode|nonstopmode|batchmode" "|read|csname|newhelp|relax|afterground|afterassignment|expandafter|noexpand|special|command|loop|repeat|toks" "|output|line|mathcode|name|item|section|mbox|DeclareRobustCommand)[^a-zA-Z]")); } }