diff --git a/src/epsrenderer.cpp b/src/epsrenderer.cpp index 772a1977..fcb2f7f0 100644 --- a/src/epsrenderer.cpp +++ b/src/epsrenderer.cpp @@ -1,144 +1,149 @@ /* 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2012 Martin Kuettler */ #include "epsrenderer.h" #include #ifdef LIBSPECTRE_FOUND #include "libspectre/spectre.h" #endif +#include #include EpsRenderer::EpsRenderer() : m_scale(1), m_useHighRes(false) { } void EpsRenderer::setScale(qreal scale) { m_scale = scale; } qreal EpsRenderer::scale() { return m_scale; } void EpsRenderer::useHighResolution(bool b) { m_useHighRes = b; } QTextImageFormat EpsRenderer::render(QTextDocument *document, const QUrl &url) { QTextImageFormat epsCharFormat; - QSizeF s = renderToResource(document, url); - - QUrl internal = url; + QUrl internal; internal.setScheme(QLatin1String("internal")); + QString path = QUuid::createUuid().toString(); + // Remove { and } + path.remove(0, 1); + path.chop(1); + internal.setPath(path); + + QSizeF s = renderToResource(document, url, internal); + if(s.isValid()) { epsCharFormat.setName(internal.url()); epsCharFormat.setWidth(s.width()); epsCharFormat.setHeight(s.height()); } return epsCharFormat; } QTextImageFormat EpsRenderer::render(QTextDocument *document, const Cantor::LatexRenderer* latex) { QTextImageFormat format = render(document, QUrl::fromLocalFile(latex->imagePath())); if (!format.name().isEmpty()) { format.setProperty(CantorFormula, latex->method()); format.setProperty(ImagePath, latex->imagePath()); format.setProperty(Code, latex->latexCode()); } return format; } -QSizeF EpsRenderer::renderToResource(QTextDocument *document, const QUrl &url) +QSizeF EpsRenderer::renderToResource(QTextDocument *document, const QUrl &url, const QUrl& internal) { QSizeF size; QImage img = renderToImage(url, &size); - QUrl internal = url; - internal.setScheme(QLatin1String("internal")); qDebug() << internal; document->addResource(QTextDocument::ImageResource, internal, QVariant(img) ); return size; } QImage EpsRenderer::renderToImage(const QUrl& url, QSizeF* size) { #ifdef LIBSPECTRE_FOUND SpectreDocument* doc = spectre_document_new(); SpectreRenderContext* rc = spectre_render_context_new(); qDebug() << "rendering eps file: " << url; QByteArray local_file = url.toLocalFile().toUtf8(); spectre_document_load(doc, local_file.data()); bool isEps = spectre_document_is_eps(doc); if (!isEps) qDebug() << "Error: spectre document is not eps! It means, that url is invalid"; int wdoc, hdoc; qreal w, h; double scale; spectre_document_get_page_size(doc, &wdoc, &hdoc); if(m_useHighRes) { scale=1.2*4.0; //1.2 scaling factor, to make it look nice, 4x for high resolution w = 1.2 * wdoc; h = 1.2 * hdoc; } else { scale=1.8*m_scale; w = 1.8 * wdoc; h = 1.8 * hdoc; } qDebug()<<"scale: "< */ #ifndef EPSRENDERER_H #define EPSRENDERER_H #include #include #include #include #include #include "lib/latexrenderer.h" class EpsRenderer { public: EpsRenderer(); ~EpsRenderer() = default; enum FormulaProperties {CantorFormula = 1, ImagePath = 2, Code = 3, Delimiter = 4}; enum FormulaType {LatexFormula = Cantor::LatexRenderer::LatexMethod, MmlFormula = Cantor::LatexRenderer::MmlMethod}; QTextImageFormat render(QTextDocument *document, const QUrl& url); QTextImageFormat render(QTextDocument *document, const Cantor::LatexRenderer* latex); void setScale(qreal scale); qreal scale(); void useHighResolution(bool b); - QSizeF renderToResource(QTextDocument *document, const QUrl& url); + QSizeF renderToResource(QTextDocument *document, const QUrl& url, const QUrl& internal); QImage renderToImage(const QUrl& url, QSizeF* size = nullptr); private: double m_scale; bool m_useHighRes; }; #endif //EPSRENDERER_H diff --git a/src/latexentry.cpp b/src/latexentry.cpp index d6ae7c61..f9acb8d8 100644 --- a/src/latexentry.cpp +++ b/src/latexentry.cpp @@ -1,542 +1,542 @@ /* 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder Copyright (C) 2012 Martin Kuettler */ #include "latexentry.h" #include "worksheetentry.h" #include "worksheet.h" #include "epsrenderer.h" #include "jupyterutils.h" #include "lib/defaulthighlighter.h" #include "lib/latexrenderer.h" #include "config-cantor.h" #include #include #include #include #include #include #include #include #include #include LatexEntry::LatexEntry(Worksheet* worksheet) : WorksheetEntry(worksheet), m_textItem(new WorksheetTextItem(this, Qt::TextEditorInteraction)) { m_textItem->installEventFilter(this); connect(m_textItem, &WorksheetTextItem::moveToPrevious, this, &LatexEntry::moveToPreviousEntry); connect(m_textItem, &WorksheetTextItem::moveToNext, this, &LatexEntry::moveToNextEntry); connect(m_textItem, SIGNAL(execute()), this, SLOT(evaluate())); } void LatexEntry::populateMenu(QMenu* menu, QPointF pos) { bool imageSelected = false; QTextCursor cursor = m_textItem->textCursor(); const QChar repl = QChar::ObjectReplacementCharacter; if (cursor.hasSelection()) { QString selection = m_textItem->textCursor().selectedText(); imageSelected = selection.contains(repl); } else { // we need to try both the current cursor and the one after the that cursor = m_textItem->cursorForPosition(pos); for (int i = 2; i; --i) { int p = cursor.position(); if (m_textItem->document()->characterAt(p-1) == repl && cursor.charFormat().hasProperty(EpsRenderer::CantorFormula)) { m_textItem->setTextCursor(cursor); imageSelected = true; break; } cursor.movePosition(QTextCursor::NextCharacter); } } if (imageSelected) { menu->addAction(i18n("Show LaTeX code"), this, SLOT(resolveImagesAtCursor())); menu->addSeparator(); } WorksheetEntry::populateMenu(menu, pos); } int LatexEntry::type() const { return Type; } bool LatexEntry::isEmpty() { return m_textItem->document()->isEmpty(); } bool LatexEntry::acceptRichText() { return false; } bool LatexEntry::focusEntry(int pos, qreal xCoord) { if (aboutToBeRemoved()) return false; m_textItem->setFocusAt(pos, xCoord); return true; } void LatexEntry::setContent(const QString& content) { m_latex = content; m_textItem->setPlainText(m_latex); } void LatexEntry::setContent(const QDomElement& content, const KZip& file) { m_latex = content.text(); qDebug() << m_latex; m_textItem->document()->clear(); QTextCursor cursor = m_textItem->textCursor(); cursor.movePosition(QTextCursor::Start); QString imagePath; bool useLatexCode = true; if(content.hasAttribute(QLatin1String("filename"))) { const KArchiveEntry* imageEntry=file.directory()->entry(content.attribute(QLatin1String("filename"))); if (imageEntry&&imageEntry->isFile()) { const KArchiveFile* imageFile=static_cast(imageEntry); const QString& dir=QStandardPaths::writableLocation(QStandardPaths::TempLocation); imageFile->copyTo(dir); imagePath = dir + QDir::separator() + imageFile->name(); #ifdef LIBSPECTRE_FOUND m_renderedFormat = worksheet()->epsRenderer()->render(m_textItem->document(), QUrl::fromLocalFile(imagePath)); qDebug()<<"rendering successful? " << !m_renderedFormat.name().isEmpty(); m_renderedFormat.setProperty(EpsRenderer::CantorFormula, EpsRenderer::LatexFormula); m_renderedFormat.setProperty(EpsRenderer::ImagePath, imagePath); m_renderedFormat.setProperty(EpsRenderer::Code, m_latex); cursor.insertText(QString(QChar::ObjectReplacementCharacter), m_renderedFormat); useLatexCode = false; m_textItem->denyEditing(); #endif } } if (useLatexCode && content.hasAttribute(QLatin1String("image"))) { const QByteArray& ba = QByteArray::fromBase64(content.attribute(QLatin1String("image")).toLatin1()); QImage image; if (image.loadFromData(ba)) { // Create unique internal url for this loaded image QUrl internal; internal.setScheme(QLatin1String("internal")); internal.setPath(QUuid::createUuid().toString()); m_textItem->document()->addResource(QTextDocument::ImageResource, internal, QVariant(image)); m_renderedFormat.setName(internal.url()); m_renderedFormat.setWidth(image.width()); m_renderedFormat.setHeight(image.height()); m_renderedFormat.setProperty(EpsRenderer::CantorFormula, EpsRenderer::LatexFormula); if (!imagePath.isEmpty()) m_renderedFormat.setProperty(EpsRenderer::ImagePath, imagePath); m_renderedFormat.setProperty(EpsRenderer::Code, m_latex); cursor.insertText(QString(QChar::ObjectReplacementCharacter), m_renderedFormat); useLatexCode = false; m_textItem->denyEditing(); } } if (useLatexCode) cursor.insertText(m_latex); } void LatexEntry::setContentFromJupyter(const QJsonObject& cell) { if (!JupyterUtils::isCodeCell(cell)) return; m_textItem->document()->clear(); QTextCursor cursor = m_textItem->textCursor(); cursor.movePosition(QTextCursor::Start); bool useLatexCode = true; QString source = JupyterUtils::getSource(cell); m_latex = source.remove(QLatin1String("%%latex\n")); QJsonArray outputs = cell.value(JupyterUtils::outputsKey).toArray(); if (outputs.size() == 1 && JupyterUtils::isJupyterDisplayOutput(outputs[0])) { const QJsonObject data = outputs[0].toObject().value(JupyterUtils::dataKey).toObject(); const QImage& image = JupyterUtils::loadImage(data, JupyterUtils::pngMime); if (!image.isNull()) { QUrl internal; internal.setScheme(QLatin1String("internal")); internal.setPath(QUuid::createUuid().toString()); m_textItem->document()->addResource(QTextDocument::ImageResource, internal, QVariant(image)); m_renderedFormat.setName(internal.url()); m_renderedFormat.setWidth(image.width()); m_renderedFormat.setHeight(image.height()); m_renderedFormat.setProperty(EpsRenderer::CantorFormula, EpsRenderer::LatexFormula); m_renderedFormat.setProperty(EpsRenderer::Code, m_latex); cursor.insertText(QString(QChar::ObjectReplacementCharacter), m_renderedFormat); useLatexCode = false; m_textItem->denyEditing(); } } if (useLatexCode) { cursor.insertText(m_latex); m_latex.clear(); // We don't render image, so clear latex code cache } } QJsonValue LatexEntry::toJupyterJson() { QJsonObject entry; entry.insert(JupyterUtils::cellTypeKey, QLatin1String("code")); entry.insert(JupyterUtils::executionCountKey, QJsonValue()); QJsonObject metadata, cantorMetadata; cantorMetadata.insert(QLatin1String("latex_entry"), true); metadata.insert(JupyterUtils::cantorMetadataKey, cantorMetadata); entry.insert(JupyterUtils::metadataKey, metadata); QJsonArray outputs; QTextCursor cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter)); if (!cursor.isNull()) { QTextImageFormat format=cursor.charFormat().toImageFormat(); QUrl internal; internal.setUrl(format.name()); const QImage& image = m_textItem->document()->resource(QTextDocument::ImageResource, internal).value(); if (!image.isNull()) { QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "PNG"); // Add image result with latex rendered image to this Jupyter code cell QJsonObject imageResult; imageResult.insert(JupyterUtils::outputTypeKey, QLatin1String("display_data")); QJsonObject data; data.insert(JupyterUtils::pngMime, JupyterUtils::toJupyterMultiline(QString::fromLatin1(ba.toBase64()))); imageResult.insert(QLatin1String("data"), data); imageResult.insert(JupyterUtils::metadataKey, QJsonObject()); outputs.append(imageResult); } } entry.insert(JupyterUtils::outputsKey, outputs); const QString& latex = latexCode(); JupyterUtils::setSource(entry, QLatin1String("%%latex\n") + latex); return entry; } QDomElement LatexEntry::toXml(QDomDocument& doc, KZip* archive) { QDomElement el = doc.createElement(QLatin1String("Latex")); el.appendChild( doc.createTextNode( latexCode() )); QTextCursor cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter)); if (!cursor.isNull()) { QTextImageFormat format=cursor.charFormat().toImageFormat(); QString fileName = format.property(EpsRenderer::ImagePath).toString(); // Check, if eps file exists, and if not true, rerender latex code bool isEpsFileExists = QFile::exists(fileName); #ifdef LIBSPECTRE_FOUND if (!isEpsFileExists && renderLatexCode()) { cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter)); format=cursor.charFormat().toImageFormat(); fileName = format.property(EpsRenderer::ImagePath).toString(); isEpsFileExists = QFile::exists(fileName); } #endif if (isEpsFileExists && archive) { const QUrl& url=QUrl::fromLocalFile(fileName); archive->addLocalFile(url.toLocalFile(), url.fileName()); el.setAttribute(QLatin1String("filename"), url.fileName()); } // Save also rendered QImage, if exist. QUrl internal; internal.setUrl(format.name()); const QImage& image = m_textItem->document()->resource(QTextDocument::ImageResource, internal).value(); if (!image.isNull()) { QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "PNG"); el.setAttribute(QLatin1String("image"), QString::fromLatin1(ba.toBase64())); } } return el; } QString LatexEntry::toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq) { Q_UNUSED(commandSep); if (commentStartingSeq.isEmpty()) return QString(); QString text = latexCode(); if (!commentEndingSeq.isEmpty()) return commentStartingSeq + text + commentEndingSeq + QLatin1String("\n"); return commentStartingSeq + text.replace(QLatin1String("\n"), QLatin1String("\n") + commentStartingSeq) + QLatin1String("\n"); } void LatexEntry::interruptEvaluation() { } bool LatexEntry::evaluate(EvaluationOption evalOp) { bool success = false; if (isOneImageOnly()) { success = true; } else { if (m_latex == latexCode()) { QTextCursor cursor = m_textItem->textCursor(); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); cursor.insertText(QString(QChar::ObjectReplacementCharacter), m_renderedFormat); m_textItem->denyEditing(); } else { m_latex = latexCode(); success = renderLatexCode(); } } qDebug()<<"rendering successful? "<document()->find(QString(QChar::ObjectReplacementCharacter)); while (!cursor.isNull()) { qDebug()<<"found a formula... rendering the eps..."; - QTextCharFormat format=cursor.charFormat(); + QTextImageFormat format=cursor.charFormat().toImageFormat(); const QUrl& url=QUrl::fromLocalFile(format.property(EpsRenderer::ImagePath).toString()); - QSizeF s = worksheet()->epsRenderer()->renderToResource(m_textItem->document(), url); + QSizeF s = worksheet()->epsRenderer()->renderToResource(m_textItem->document(), url, QUrl(format.name())); qDebug()<<"rendering successful? "<< s.isValid(); cursor.movePosition(QTextCursor::NextCharacter); cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter), cursor); } } bool LatexEntry::eventFilter(QObject* object, QEvent* event) { if(object == m_textItem && event->type() == QEvent::GraphicsSceneMouseDoubleClick) { // One image if we have rendered entry if (isOneImageOnly()) { QTextCursor cursor = m_textItem->textCursor(); if (!cursor.hasSelection()) cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); cursor.insertText(m_textItem->resolveImages(cursor)); m_textItem->allowEditing(); return true; } } return false; } QString LatexEntry::latexCode() { QTextCursor cursor = m_textItem->textCursor(); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); QString code = m_textItem->resolveImages(cursor); code.replace(QChar::ParagraphSeparator, QLatin1Char('\n')); //Replace the U+2029 paragraph break by a Normal Newline code.replace(QChar::LineSeparator, QLatin1Char('\n')); //Replace the line break by a Normal Newline return code; } bool LatexEntry::isOneImageOnly() { QTextCursor cursor = m_textItem->textCursor(); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); return (cursor.selectionEnd() == 1 && cursor.selectedText() == QString(QChar::ObjectReplacementCharacter)); } int LatexEntry::searchText(const QString& text, const QString& pattern, QTextDocument::FindFlags qt_flags) { Qt::CaseSensitivity caseSensitivity; if (qt_flags & QTextDocument::FindCaseSensitively) caseSensitivity = Qt::CaseSensitive; else caseSensitivity = Qt::CaseInsensitive; int position; if (qt_flags & QTextDocument::FindBackward) position = text.lastIndexOf(pattern, -1, caseSensitivity); else position = text.indexOf(pattern, 0, caseSensitivity); return position; } WorksheetCursor LatexEntry::search(const QString& pattern, unsigned flags, QTextDocument::FindFlags qt_flags, const WorksheetCursor& pos) { if (!(flags & WorksheetEntry::SearchLaTeX)) return WorksheetCursor(); if (pos.isValid() && (pos.entry() != this || pos.textItem() != m_textItem)) return WorksheetCursor(); QTextCursor textCursor = m_textItem->search(pattern, qt_flags, pos); int position = 0; QString latex; const QString repl = QString(QChar::ObjectReplacementCharacter); QTextCursor latexCursor = m_textItem->search(repl, qt_flags, pos); while (!latexCursor.isNull()) { latex = m_textItem->resolveImages(latexCursor); position = searchText(latex, pattern, qt_flags); if (position >= 0) { break; } WorksheetCursor c(this, m_textItem, latexCursor); latexCursor = m_textItem->search(repl, qt_flags, c); } if (latexCursor.isNull()) { if (textCursor.isNull()) return WorksheetCursor(); else return WorksheetCursor(this, m_textItem, textCursor); } else { if (textCursor.isNull() || latexCursor < textCursor) { int start = latexCursor.selectionStart(); latexCursor.insertText(latex); QTextCursor c = m_textItem->textCursor(); c.setPosition(start + position); c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, pattern.length()); return WorksheetCursor(this, m_textItem, c); } else { return WorksheetCursor(this, m_textItem, textCursor); } } } void LatexEntry::layOutForWidth(qreal w, bool force) { if (size().width() == w && !force) return; m_textItem->setGeometry(0, 0, w); setSize(QSizeF(m_textItem->width(), m_textItem->height() + VerticalMargin)); } bool LatexEntry::wantToEvaluate() { return !isOneImageOnly(); } bool LatexEntry::renderLatexCode() { bool success = false; QString latex = latexCode(); Cantor::LatexRenderer* renderer = new Cantor::LatexRenderer(this); renderer->setLatexCode(latex); renderer->setEquationOnly(false); renderer->setMethod(Cantor::LatexRenderer::LatexMethod); renderer->renderBlocking(); if (renderer->renderingSuccessful()) { EpsRenderer* epsRend = worksheet()->epsRenderer(); m_renderedFormat = epsRend->render(m_textItem->document(), renderer); success = !m_renderedFormat.name().isEmpty(); } if(success) { QTextCursor cursor = m_textItem->textCursor(); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); cursor.insertText(QString(QChar::ObjectReplacementCharacter), m_renderedFormat); m_textItem->denyEditing(); } delete renderer; return success; } bool LatexEntry::isConvertableToLatexEntry(const QJsonObject& cell) { if (!JupyterUtils::isCodeCell(cell)) return false; const QString& source = JupyterUtils::getSource(cell); return source.startsWith(QLatin1String("%%latex\n")); } diff --git a/src/textentry.cpp b/src/textentry.cpp index cc7e457e..d9d5bac6 100644 --- a/src/textentry.cpp +++ b/src/textentry.cpp @@ -1,479 +1,479 @@ /* 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder Copyright (C) 2012 Martin Kuettler */ #include "textentry.h" #include "worksheettextitem.h" #include "epsrenderer.h" #include "latexrenderer.h" #include "jupyterutils.h" #include "settings.h" #include #include #include #include #include #include #include TextEntry::TextEntry(Worksheet* worksheet) : WorksheetEntry(worksheet) , m_convertCell(false) , m_convertTarget() , m_textItem(new WorksheetTextItem(this, Qt::TextEditorInteraction)) { m_textItem->enableRichText(true); connect(m_textItem, &WorksheetTextItem::moveToPrevious, this, &TextEntry::moveToPreviousEntry); connect(m_textItem, &WorksheetTextItem::moveToNext, this, &TextEntry::moveToNextEntry); connect(m_textItem, SIGNAL(execute()), this, SLOT(evaluate())); connect(m_textItem, &WorksheetTextItem::doubleClick, this, &TextEntry::resolveImagesAtCursor); } void TextEntry::populateMenu(QMenu* menu, QPointF pos) { bool imageSelected = false; QTextCursor cursor = m_textItem->textCursor(); const QChar repl = QChar::ObjectReplacementCharacter; if (cursor.hasSelection()) { QString selection = m_textItem->textCursor().selectedText(); imageSelected = selection.contains(repl); } else { // we need to try both the current cursor and the one after the that cursor = m_textItem->cursorForPosition(pos); qDebug() << cursor.position(); for (int i = 2; i; --i) { int p = cursor.position(); if (m_textItem->document()->characterAt(p-1) == repl && cursor.charFormat().hasProperty(EpsRenderer::CantorFormula)) { m_textItem->setTextCursor(cursor); imageSelected = true; break; } cursor.movePosition(QTextCursor::NextCharacter); } } if (imageSelected) { menu->addAction(i18n("Show LaTeX code"), this, SLOT(resolveImagesAtCursor())); menu->addSeparator(); } WorksheetEntry::populateMenu(menu, pos); } bool TextEntry::isEmpty() { return m_textItem->document()->isEmpty(); } int TextEntry::type() const { return Type; } bool TextEntry::acceptRichText() { return true; } bool TextEntry::focusEntry(int pos, qreal xCoord) { if (aboutToBeRemoved()) return false; m_textItem->setFocusAt(pos, xCoord); return true; } void TextEntry::setContent(const QString& content) { m_textItem->setPlainText(content); } void TextEntry::setContent(const QDomElement& content, const KZip& file) { Q_UNUSED(file); if(content.firstChildElement(QLatin1String("body")).isNull()) return; if (content.hasAttribute(QLatin1String("convertTarget"))) { m_convertCell = true; m_convertTarget = content.attribute(QLatin1String("convertTarget")); } else m_convertCell = false; QDomDocument doc = QDomDocument(); QDomNode n = doc.importNode(content.firstChildElement(QLatin1String("body")), true); doc.appendChild(n); QString html = doc.toString(); qDebug() << html; m_textItem->setHtml(html); } void TextEntry::setContentFromJupyter(const QJsonObject& cell) { if (JupyterUtils::isRawCell(cell)) { m_convertCell = true; const QJsonObject& metadata = cell.value(QLatin1String("metadata")).toObject(QJsonObject()); m_convertTarget = metadata.value(QLatin1String("format")).toString(QString()); m_textItem->setPlainText(JupyterUtils::getSource(cell)); } else if (JupyterUtils::isMarkdownCell(cell)) { m_convertCell = false; m_convertTarget.clear(); QJsonObject cantorMetadata = JupyterUtils::getCantorMetadata(cell); m_textItem->setHtml(cantorMetadata.value(QLatin1String("text_entry_content")).toString()); } } QJsonValue TextEntry::toJupyterJson() { // Simple logic: // If convertTarget is empty, it's user maded cell and we convert it to a markdown // If convertTarget setted, it's raw cell from Jupyter and we convert it to Jupyter cell QTextDocument* doc = m_textItem->document()->clone(); QTextCursor cursor = doc->find(QString(QChar::ObjectReplacementCharacter)); while(!cursor.isNull()) { QTextCharFormat format = cursor.charFormat(); if (format.hasProperty(EpsRenderer::CantorFormula)) { showLatexCode(cursor); } cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter), cursor); } QJsonObject metadata; QString entryData; QString entryType; if (!m_convertCell) { entryType = QLatin1String("markdown"); // Jupyter TODO: Handle metadata // Add raw text of entry to metadata, for situation when // Cantor opens .ipynb converted from our .cws format QJsonObject cantorMetadata; if (Settings::storeTextEntryFormatting()) { entryData = doc->toHtml(); // Remove DOCTYPE from html entryData.remove(QRegExp(QLatin1String("]*>\\n"))); cantorMetadata.insert(QLatin1String("text_entry_content"), entryData); } else entryData = doc->toPlainText(); metadata.insert(JupyterUtils::cantorMetadataKey, cantorMetadata); // Replace our $$ formulas to $ entryData.replace(QLatin1String("$$"), QLatin1String("$")); } else { entryType = QLatin1String("raw"); metadata.insert(QLatin1String("format"), m_convertTarget); entryData = doc->toPlainText(); } QJsonObject entry; entry.insert(QLatin1String("cell_type"), entryType); entry.insert(QLatin1String("metadata"), metadata); JupyterUtils::setSource(entry, entryData); return entry; } QDomElement TextEntry::toXml(QDomDocument& doc, KZip* archive) { Q_UNUSED(archive); QScopedPointer document(m_textItem->document()->clone()); //make sure that the latex code is shown instead of the rendered formulas QTextCursor cursor = document->find(QString(QChar::ObjectReplacementCharacter)); while(!cursor.isNull()) { QTextCharFormat format = cursor.charFormat(); if (format.hasProperty(EpsRenderer::CantorFormula)) showLatexCode(cursor); cursor = document->find(QString(QChar::ObjectReplacementCharacter), cursor); } const QString& html = document->toHtml(); qDebug() << html; QDomElement el = doc.createElement(QLatin1String("Text")); QDomDocument myDoc = QDomDocument(); myDoc.setContent(html); el.appendChild(myDoc.documentElement().firstChildElement(QLatin1String("body"))); if (m_convertCell) el.setAttribute(QLatin1String("convertTarget"), m_convertTarget); return el; } QString TextEntry::toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq) { Q_UNUSED(commandSep); if (commentStartingSeq.isEmpty()) return QString(); /* // would this be plain enough? QTextCursor cursor = m_textItem->textCursor(); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); QString text = m_textItem->resolveImages(cursor); text.replace(QChar::ParagraphSeparator, '\n'); text.replace(QChar::LineSeparator, '\n'); */ QString text = m_textItem->toPlainText(); if (!commentEndingSeq.isEmpty()) return commentStartingSeq + text + commentEndingSeq + QLatin1String("\n"); return commentStartingSeq + text.replace(QLatin1String("\n"), QLatin1String("\n") + commentStartingSeq) + QLatin1String("\n"); } void TextEntry::interruptEvaluation() { } bool TextEntry::evaluate(EvaluationOption evalOp) { QTextCursor cursor = findLatexCode(); while (!cursor.isNull()) { QString latexCode = cursor.selectedText(); qDebug()<<"found latex: "<setLatexCode(latexCode); renderer->setEquationOnly(true); renderer->setEquationType(Cantor::LatexRenderer::InlineEquation); renderer->setMethod(Cantor::LatexRenderer::LatexMethod); renderer->renderBlocking(); bool success; QTextImageFormat formulaFormat; if (renderer->renderingSuccessful()) { EpsRenderer* epsRend = worksheet()->epsRenderer(); formulaFormat = epsRend->render(m_textItem->document(), renderer); success = !formulaFormat.name().isEmpty(); } else { success = false; } qDebug()<<"rendering successful? "<document()->find(QString(QChar::ObjectReplacementCharacter)); while(!cursor.isNull()) { - QTextCharFormat format = cursor.charFormat(); + QTextImageFormat format=cursor.charFormat().toImageFormat(); if (format.hasProperty(EpsRenderer::CantorFormula)) { qDebug() << "found a formula... rendering the eps..."; - QUrl url = format.property(EpsRenderer::ImagePath).toUrl(); - QSizeF s = worksheet()->epsRenderer()->renderToResource(m_textItem->document(), url); + const QUrl& url=QUrl::fromLocalFile(format.property(EpsRenderer::ImagePath).toString()); + QSizeF s = worksheet()->epsRenderer()->renderToResource(m_textItem->document(), url, QUrl(format.name())); qDebug() << "rendering successful? " << s.isValid(); //cursor.deletePreviousChar(); //cursor.insertText(QString(QChar::ObjectReplacementCharacter), format); } cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter), cursor); } } void TextEntry::resolveImagesAtCursor() { QTextCursor cursor = m_textItem->textCursor(); if (!cursor.hasSelection()) cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); cursor.insertText(m_textItem->resolveImages(cursor)); } QTextCursor TextEntry::findLatexCode(const QTextCursor& cursor) const { QTextDocument *doc = m_textItem->document(); QTextCursor startCursor; if (cursor.isNull()) startCursor = doc->find(QLatin1String("$$")); else startCursor = doc->find(QLatin1String("$$"), cursor); if (startCursor.isNull()) return startCursor; const QTextCursor endCursor = doc->find(QLatin1String("$$"), startCursor); if (endCursor.isNull()) return endCursor; startCursor.setPosition(startCursor.selectionStart()); startCursor.setPosition(endCursor.position(), QTextCursor::KeepAnchor); return startCursor; } QString TextEntry::showLatexCode(QTextCursor& cursor) { QString latexCode = cursor.charFormat().property(EpsRenderer::Code).toString(); cursor.deletePreviousChar(); latexCode = QLatin1String("$$") + latexCode + QLatin1String("$$"); cursor.insertText(latexCode); return latexCode; } int TextEntry::searchText(const QString& text, const QString& pattern, QTextDocument::FindFlags qt_flags) { Qt::CaseSensitivity caseSensitivity; if (qt_flags & QTextDocument::FindCaseSensitively) caseSensitivity = Qt::CaseSensitive; else caseSensitivity = Qt::CaseInsensitive; int position; if (qt_flags & QTextDocument::FindBackward) position = text.lastIndexOf(pattern, -1, caseSensitivity); else position = text.indexOf(pattern, 0, caseSensitivity); return position; } WorksheetCursor TextEntry::search(const QString& pattern, unsigned flags, QTextDocument::FindFlags qt_flags, const WorksheetCursor& pos) { if (!(flags & WorksheetEntry::SearchText) || (pos.isValid() && pos.entry() != this)) return WorksheetCursor(); QTextCursor textCursor = m_textItem->search(pattern, qt_flags, pos); int position = 0; QTextCursor latexCursor; QString latex; if (flags & WorksheetEntry::SearchLaTeX) { const QString repl = QString(QChar::ObjectReplacementCharacter); latexCursor = m_textItem->search(repl, qt_flags, pos); while (!latexCursor.isNull()) { latex = m_textItem->resolveImages(latexCursor); position = searchText(latex, pattern, qt_flags); if (position >= 0) { break; } WorksheetCursor c(this, m_textItem, latexCursor); latexCursor = m_textItem->search(repl, qt_flags, c); } } if (latexCursor.isNull()) { if (textCursor.isNull()) return WorksheetCursor(); else return WorksheetCursor(this, m_textItem, textCursor); } else { if (textCursor.isNull() || latexCursor < textCursor) { int start = latexCursor.selectionStart(); latexCursor.insertText(latex); QTextCursor c = m_textItem->textCursor(); c.setPosition(start + position); c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, pattern.length()); return WorksheetCursor(this, m_textItem, c); } else { return WorksheetCursor(this, m_textItem, textCursor); } } } void TextEntry::layOutForWidth(qreal w, bool force) { if (size().width() == w && !force) return; m_textItem->setGeometry(0, 0, w); setSize(QSizeF(m_textItem->width(), m_textItem->height() + VerticalMargin)); } bool TextEntry::wantToEvaluate() { return !findLatexCode().isNull(); } bool TextEntry::isConvertableToTextEntry(const QJsonObject& cell) { if (!JupyterUtils::isMarkdownCell(cell)) return false; QJsonObject cantorMetadata = JupyterUtils::getCantorMetadata(cell); const QJsonValue& textContentValue = cantorMetadata.value(QLatin1String("text_entry_content")); if (!textContentValue.isString()) return false; const QString& textContent = textContentValue.toString(); const QString& source = JupyterUtils::getSource(cell); return textContent == source; }