Commit 4a8de5be authored by Nikita Sirgienko's avatar Nikita Sirgienko
Browse files

[Jupyter] Save intermediate .pdf files of embedded math to .cws format. Also,...

[Jupyter] Save intermediate .pdf files of embedded math to .cws format. Also, it will allow see rendered embedded math on system without pdflatex (but this math will be only for view, not for editing)
parent a32640ae
......@@ -28,6 +28,8 @@
#include <QBuffer>
#include <KLocalizedString>
#include <QDebug>
#include <QStandardPaths>
#include <QDir>
#include "jupyterutils.h"
#include "mathrender.h"
......@@ -111,8 +113,6 @@ void MarkdownEntry::setContent(const QString& content)
void MarkdownEntry::setContent(const QDomElement& content, const KZip& file)
{
Q_UNUSED(file);
rendered = content.attribute(QLatin1String("rendered"), QLatin1String("1")) == QLatin1String("1");
QDomElement htmlEl = content.firstChildElement(QLatin1String("HTML"));
if(!htmlEl.isNull())
......@@ -162,7 +162,38 @@ void MarkdownEntry::setContent(const QDomElement& content, const KZip& file)
foundMath.push_back(std::make_pair(mathCode, mathRendered));
if (rendered && mathRendered)
renderMathExpression(mathCode);
{
const KArchiveEntry* imageEntry=file.directory()->entry(math.attribute(QLatin1String("path")));
if (imageEntry && imageEntry->isFile())
{
const KArchiveFile* imageFile=static_cast<const KArchiveFile*>(imageEntry);
const QString& dir=QStandardPaths::writableLocation(QStandardPaths::TempLocation);
imageFile->copyTo(dir);
const QString& pdfPath = dir + QDir::separator() + imageFile->name();
QString latex;
Cantor::LatexRenderer::EquationType type;
std::tie(latex, type) = parseMathCode(mathCode);
// Get uuid by removing 'cantor_' and '.pdf' extention
// len('cantor_') == 7, len('.pdf') == 4
QString uuid = pdfPath;
uuid.remove(0, 7);
uuid.chop(4);
bool success;
const auto& data = worksheet()->mathRenderer()->renderExpressionFromPdf(pdfPath, uuid, latex, type, &success);
if (success)
{
QUrl internal;
internal.setScheme(QLatin1String("internal"));
internal.setPath(uuid);
setRenderedMath(data.first, internal, data.second);
}
}
else
renderMathExpression(mathCode);
}
}
}
......@@ -195,8 +226,6 @@ void MarkdownEntry::setContentFromJupyter(const QJsonObject& cell)
QDomElement MarkdownEntry::toXml(QDomDocument& doc, KZip* archive)
{
Q_UNUSED(archive)
if(!rendered)
plain = m_textItem->toPlainText();
......@@ -230,19 +259,39 @@ QDomElement MarkdownEntry::toXml(QDomDocument& doc, KZip* archive)
attachmentEl.appendChild(doc.createTextNode(QString::fromLatin1(ba.toBase64())));
el.appendChild(attachmentEl);
/*
QString attachmentKey = url.toString().remove(QLatin1String("attachment:"));
attachments.insert(attachmentKey, JupyterUtils::packMimeBundle(image, key));
*/
}
// If math rendered, then append result .pdf to archive
QTextCursor cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter));
for (const auto& data : foundMath)
{
QDomElement mathEl = doc.createElement(QLatin1String("EmbeddedMath"));
mathEl.setAttribute(QStringLiteral("rendered"), data.second);
mathEl.appendChild(doc.createTextNode(data.first));
if (data.second)
{
bool foundNeededImage = false;
while(!cursor.isNull() && !foundNeededImage)
{
QTextImageFormat format=cursor.charFormat().toImageFormat();
if (format.hasProperty(EpsRenderer::CantorFormula))
{
const QString& latex = format.property(EpsRenderer::Code).toString();
const QString& delimiter = format.property(EpsRenderer::Delimiter).toString();
const QString& code = delimiter + latex + delimiter;
if (code == data.first)
{
const QUrl& url = QUrl::fromLocalFile(format.property(EpsRenderer::ImagePath).toString());
qDebug() << QFile::exists(url.toLocalFile());
mathEl.setAttribute(QStringLiteral("path"), url.fileName());
foundNeededImage = true;
}
}
cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter), cursor);
}
}
el.appendChild(mathEl);
}
......@@ -483,48 +532,63 @@ void MarkdownEntry::handleMathRender(QSharedPointer<MathRenderResult> result)
return;
}
const QString& code = result->renderedMath.property(EpsRenderer::Code).toString();
const QString& delimiter = result->renderedMath.property(EpsRenderer::Delimiter).toString();
QString searchText = delimiter + code + delimiter;
searchText.replace(QRegExp(QLatin1String("\\s")), QLatin1String(" "));
QTextCursor cursor = m_textItem->document()->find(searchText);
if (!cursor.isNull())
{
m_textItem->document()->addResource(QTextDocument::ImageResource, result->uniqueUrl, QVariant(result->image));
cursor.insertText(QString(QChar::ObjectReplacementCharacter), result->renderedMath);
// Set that the formulas is rendered
for (auto iter = foundMath.begin(); iter != foundMath.end(); iter++)
{
const QString& formulas = delimiter + code + delimiter;
if (iter->first == formulas)
{
iter->second = true;
break;
}
}
}
setRenderedMath(result->renderedMath, result->uniqueUrl, result->image);
}
void MarkdownEntry::renderMathExpression(QString mathCode)
{
QString latex;
Cantor::LatexRenderer::EquationType type;
std::tie(latex, type) = parseMathCode(mathCode);
if (!latex.isNull())
worksheet()->mathRenderer()->renderExpression(latex, type, this, SLOT(handleMathRender(QSharedPointer<MathRenderResult>)));
}
std::pair<QString, Cantor::LatexRenderer::EquationType> MarkdownEntry::parseMathCode(QString mathCode)
{
static const QLatin1String inlineDelimiter("$");
static const QLatin1String displayedDelimiter("$$");
MathRenderer* renderer = worksheet()->mathRenderer();
if (mathCode.startsWith(displayedDelimiter) && mathCode.endsWith(displayedDelimiter))
if (mathCode.startsWith(displayedDelimiter) && mathCode.endsWith(displayedDelimiter))
{
mathCode.remove(0, 2);
mathCode.chop(2);
renderer->renderExpression(mathCode, Cantor::LatexRenderer::FullEquation, this, SLOT(handleMathRender(QSharedPointer<MathRenderResult>)));
return std::make_pair(mathCode, Cantor::LatexRenderer::FullEquation);
}
else if (mathCode.startsWith(inlineDelimiter) && mathCode.endsWith(inlineDelimiter))
{
mathCode.remove(0, 1);
mathCode.chop(1);
renderer->renderExpression(mathCode, Cantor::LatexRenderer::InlineEquation, this, SLOT(handleMathRender(QSharedPointer<MathRenderResult>)));
return std::make_pair(mathCode, Cantor::LatexRenderer::InlineEquation);
}
else
return std::make_pair(QString(), Cantor::LatexRenderer::InlineEquation);
}
void MarkdownEntry::setRenderedMath(const QTextImageFormat& format, const QUrl& internal, const QImage& image)
{
const QString& code = format.property(EpsRenderer::Code).toString();
const QString& delimiter = format.property(EpsRenderer::Delimiter).toString();
QString searchText = delimiter + code + delimiter;
searchText.replace(QRegExp(QLatin1String("\\s")), QLatin1String(" "));
QTextCursor cursor = m_textItem->document()->find(searchText);
if (!cursor.isNull())
{
m_textItem->document()->addResource(QTextDocument::ImageResource, internal, QVariant(image));
cursor.insertText(QString(QChar::ObjectReplacementCharacter), format);
// Set that the formulas is rendered
for (auto iter = foundMath.begin(); iter != foundMath.end(); iter++)
{
const QString& formulas = delimiter + code + delimiter;
if (iter->first == formulas)
{
iter->second = true;
break;
}
}
}
}
......@@ -29,6 +29,7 @@
#include "worksheettextitem.h"
#include "mathrendertask.h"
#include "lib/latexrenderer.h"
class QJsonObject;
......@@ -78,6 +79,9 @@ class MarkdownEntry : public WorksheetEntry
void setPlainText(const QString& plain);
void renderMath();
void renderMathExpression(QString mathCode);
void setRenderedMath(const QTextImageFormat& format, const QUrl& internal, const QImage& image);
static std::pair<QString, Cantor::LatexRenderer::EquationType> parseMathCode(QString mathCode);
protected Q_SLOTS:
void handleMathRender(QSharedPointer<MathRenderResult> result);
......
......@@ -85,3 +85,21 @@ void MathRenderer::rerender(QTextDocument* document, const QTextImageFormat& mat
qDebug() << "Rerender embedded math failed with message: " << errorMessage;
}
}
std::pair<QTextImageFormat, QImage> MathRenderer::renderExpressionFromPdf(const QString& filename, const QString& uuid, const QString& code, Cantor::LatexRenderer::EquationType type, bool* outSuccess)
{
if (!QFile::exists(filename))
{
if (outSuccess)
*outSuccess = false;
return std::make_pair(QTextImageFormat(), QImage());
}
bool success; QString errorMessage;
const auto& data = MathRenderTask::renderPdfToFormat(filename, code, uuid, type, m_scale, m_useHighRes, &success, &errorMessage, &popplerMutex);
if (success == false)
qDebug() << "Render embedded math from pdf failed with message: " << errorMessage;
if (outSuccess)
*outSuccess = success;
return data;
}
......@@ -64,6 +64,13 @@ class MathRenderer : public QObject {
*/
void rerender(QTextDocument* document, const QTextImageFormat& math);
/**
* Render math expression from existing .pdf
* Like MathRenderer::rerender is blocking
*/
std::pair<QTextImageFormat, QImage> renderExpressionFromPdf(
const QString& filename, const QString& uuid, const QString& code, Cantor::LatexRenderer::EquationType type, bool* success
);
private:
double m_scale;
......
......@@ -121,10 +121,7 @@ void MathRenderTask::run()
// Create unique uuid for this job
// It will be used as pdf filename, for preventing names collisions
// And as internal url path too
QString uuid = QUuid::createUuid().toString();
uuid.remove(0, 1);
uuid.chop(1);
uuid.replace(QLatin1Char('-'), QLatin1Char('_'));
const QString& uuid = genUuid();
const QString& pdflatex = QStandardPaths::findExecutable(QLatin1String("pdflatex"));
p << pdflatex << QStringLiteral("-interaction=batchmode") << QStringLiteral("-jobname=cantor_") + uuid << QStringLiteral("-halt-on-error") << texFile.fileName();
......@@ -160,32 +157,21 @@ void MathRenderTask::run()
return;
}
QTextImageFormat format;
const auto& data = renderPdfToFormat(pdfFileName, m_code, uuid, m_type, m_scale, m_highResolution, &success, &errorMessage, m_mutex);
result->successfull = success;
result->errorMessage = errorMessage;
if (success == false)
{
finalize(result);
return;
}
result->renderedMath = data.first;
result->image = data.second;
QUrl internal;
internal.setScheme(QLatin1String("internal"));
internal.setPath(uuid);
format.setName(internal.url());
format.setWidth(size.width());
format.setHeight(size.height());
format.setProperty(EpsRenderer::CantorFormula, m_type);
format.setProperty(EpsRenderer::ImagePath, pdfFileName);
format.setProperty(EpsRenderer::Code, m_code);
format.setVerticalAlignment(QTextCharFormat::AlignBaseline);
switch(m_type)
{
case Cantor::LatexRenderer::FullEquation:
format.setProperty(EpsRenderer::Delimiter, QLatin1String("$$"));
break;
case Cantor::LatexRenderer::InlineEquation:
format.setProperty(EpsRenderer::Delimiter, QLatin1String("$"));
break;
}
result->renderedMath = format;
result->uniqueUrl = internal;
finalize(result);
......@@ -267,3 +253,48 @@ QImage MathRenderTask::renderPdf(const QString& filename, double scale, bool hig
*size = QSizeF(w, h);
return image;
}
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, QMutex* mutex)
{
QSizeF size;
const QImage& image = renderPdf(filename, scale, highResulution, success, &size, errorReason, mutex);
if (success && *success == false)
return std::make_pair(QTextImageFormat(), QImage());
QTextImageFormat format;
QUrl internal;
internal.setScheme(QLatin1String("internal"));
internal.setPath(uuid);
format.setName(internal.url());
format.setWidth(size.width());
format.setHeight(size.height());
format.setProperty(EpsRenderer::CantorFormula, type);
format.setProperty(EpsRenderer::ImagePath, filename);
format.setProperty(EpsRenderer::Code, code);
format.setVerticalAlignment(QTextCharFormat::AlignBaseline);
switch(type)
{
case Cantor::LatexRenderer::FullEquation:
format.setProperty(EpsRenderer::Delimiter, QLatin1String("$$"));
break;
case Cantor::LatexRenderer::InlineEquation:
format.setProperty(EpsRenderer::Delimiter, QLatin1String("$"));
break;
}
return std::make_pair(std::move(format), std::move(image));
}
QString MathRenderTask::genUuid()
{
QString uuid = QUuid::createUuid().toString();
uuid.remove(0, 1);
uuid.chop(1);
uuid.replace(QLatin1Char('-'), QLatin1Char('_'));
return uuid;
}
......@@ -59,7 +59,22 @@ class MathRenderTask : public QObject, public QRunnable
void run() override;
static QImage renderPdf(const QString& filename, double scale, bool highResulution, bool* success = nullptr, QSizeF* size = nullptr, QString* errorReason = nullptr, QMutex* mutex = nullptr);
static QImage renderPdf(
const QString& filename, double scale, bool highResulution, bool* success = nullptr, QSizeF* size = nullptr, QString* errorReason = nullptr, QMutex* mutex = nullptr
);
static std::pair<QTextImageFormat, QImage> renderPdfToFormat(
const QString& filename,
const QString& code,
const QString uuid,
Cantor::LatexRenderer::EquationType type,
double scale,
bool highResulution,
bool* success = nullptr,
QString* errorReason = nullptr,
QMutex* mutex = nullptr
);
static QString genUuid();
Q_SIGNALS:
void finish(QSharedPointer<MathRenderResult> result);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment