diff --git a/src/resources/mathjax_preview.js b/src/resources/mathjax_preview.js index a765fbcb..2830b505 100644 --- a/src/resources/mathjax_preview.js +++ b/src/resources/mathjax_preview.js @@ -6,6 +6,11 @@ var content; var VMermaidDivClass = 'mermaid-diagram'; var VFlowchartDivClass = 'flowchart-diagram'; +var VPlantUMLDivClass = 'plantuml-diagram'; + +if (typeof VPlantUMLServer == 'undefined') { + VPlantUMLServer = 'http://www.plantuml.com/plantuml'; +} new QWebChannel(qt.webChannelTransport, function(channel) { @@ -128,59 +133,12 @@ var previewDiagram = function(identifier, id, timeStamp, lang, text) { var div = null; if (lang == 'flow' || lang == 'flowchart') { - flowchartIdx++; - try { - var graph = flowchart.parse(text); - } catch (err) { - content.setLog("err: " + err); - content.diagramResultReady(identifier, id, timeStamp, 'png', ''); - return; - } - - if (typeof graph == "undefined") { - content.diagramResultReady(identifier, id, timeStamp, 'png', ''); - return; - } - - div = document.createElement('div'); - div.id = 'flowchart-diagram-' + flowchartIdx; - div.classList.add(VFlowchartDivClass); - - contentDiv.appendChild(div); - - // Draw on it after adding it to page. - try { - graph.drawSVG(div.id); - } catch (err) { - content.setLog("err: " + err); - contentDiv.removeChild(div); - delete div; - content.diagramResultReady(identifier, id, timeStamp, 'png', ''); - return; - } + div = renderFlowchartOne(identifier, id, timeStamp, text); } else if (lang == 'mermaid') { - mermaidParserErr = false; - mermaidIdx++; - try { - // Do not increment mermaidIdx here. - var graph = mermaidAPI.render('mermaid-diagram-' + mermaidIdx, - text, - function(){}); - } catch (err) { - content.setLog("err: " + err); - content.diagramResultReady(identifier, id, timeStamp, 'png', ''); - return; - } - - if (mermaidParserErr || typeof graph == "undefined") { - content.diagramResultReady(identifier, id, timeStamp, 'png', ''); - return; - } - - div = document.createElement('div'); - div.classList.add(VMermaidDivClass); - div.innerHTML = graph; - contentDiv.appendChild(div); + div = renderMermaidOne(identifier, id, timeStamp, text); + } else if (lang == 'puml') { + renderPlantUMLOne(identifier, id, timeStamp, text); + return; } if (!div) { @@ -188,9 +146,13 @@ var previewDiagram = function(identifier, id, timeStamp, lang, text) { return; } - // For Flowchart.js, we need to add addtitional height. Since Mermaid is not - // supported now, we just add it simply here. - domtoimage.toPng(div, { height: div.clientHeight + 30 }).then(function (dataUrl) { + // For Flowchart.js, we need to add addtitional height. + var dtiOpt = {}; + if (lang == 'flow' || lang == 'flowchart') { + dtiOpt = { height: div.clientHeight + 30 }; + } + + domtoimage.toPng(div, dtiOpt).then(function (dataUrl) { var png = dataUrl.substring(dataUrl.indexOf(',') + 1); content.diagramResultReady(identifier, id, timeStamp, 'png', png); @@ -203,3 +165,102 @@ var previewDiagram = function(identifier, id, timeStamp, lang, text) { delete div; }); }; + +var renderMermaidOne = function(identifier, id, timeStamp, text) { + mermaidParserErr = false; + mermaidIdx++; + try { + // Do not increment mermaidIdx here. + var graph = mermaidAPI.render('mermaid-diagram-' + mermaidIdx, + text, + function(){}); + } catch (err) { + content.setLog("err: " + err); + return null; + } + + if (mermaidParserErr || typeof graph == "undefined") { + return null; + } + + var div = document.createElement('div'); + div.classList.add(VMermaidDivClass); + div.innerHTML = graph; + contentDiv.appendChild(div); + return div; +}; + +var renderFlowchartOne = function(identifier, id, timeStamp, text) { + flowchartIdx++; + try { + var graph = flowchart.parse(text); + } catch (err) { + content.setLog("err: " + err); + return null; + } + + if (typeof graph == "undefined") { + return null; + } + + var div = document.createElement('div'); + div.id = 'flowchart-diagram-' + flowchartIdx; + div.classList.add(VFlowchartDivClass); + + contentDiv.appendChild(div); + + // Draw on it after adding it to page. + try { + graph.drawSVG(div.id); + } catch (err) { + content.setLog("err: " + err); + contentDiv.removeChild(div); + delete div; + return null; + } + + return div; +}; + +var renderPlantUMLOne = function(identifier, id, timeStamp, text) { + var format = 'svg'; + var s = unescape(encodeURIComponent(text)); + var arr = []; + for (var i = 0; i < s.length; i++) { + arr.push(s.charCodeAt(i)); + } + + var compressor = new Zopfli.RawDeflate(arr); + var compressed = compressor.compress(); + var url = VPlantUMLServer + "/" + format + "/" + encode64_(compressed); + + if (format == 'png') { + httpGet(url, 'blob', function(resp) { + var blob = resp; + var reader = new FileReader(); + reader.onload = function () { + var dataUrl = reader.result; + var png = dataUrl.substring(dataUrl.indexOf(',') + 1); + content.diagramResultReady(identifier, id, timeStamp, 'png', png); + }; + + reader.readAsDataURL(blob); + }); + } else if (format == 'svg') { + httpGet(url, 'text', function(resp) { + content.diagramResultReady(identifier, id, timeStamp, 'svg', resp); + }); + } +}; + +var httpGet = function(url, type, callback) { + var xmlHttp = new XMLHttpRequest(); + xmlHttp.open("GET", url); + xmlHttp.responseType = type; + + xmlHttp.onload = function() { + callback(xmlHttp.response); + }; + + xmlHttp.send(null); +} diff --git a/src/utils/vutils.cpp b/src/utils/vutils.cpp index fcc1764d..1ddd9083 100644 --- a/src/utils/vutils.cpp +++ b/src/utils/vutils.cpp @@ -942,6 +942,11 @@ QString VUtils::generateMathJaxPreviewTemplate() " messageStyle: \"none\"});\n" "\n"; + // PlantUML. + extraFile += "\n" + + "\n" + + "\n"; + templ.replace(HtmlHolder::c_extraHolder, extraFile); return templ; diff --git a/src/vlivepreviewhelper.cpp b/src/vlivepreviewhelper.cpp index 522c4b82..e4525e99 100644 --- a/src/vlivepreviewhelper.cpp +++ b/src/vlivepreviewhelper.cpp @@ -124,8 +124,7 @@ void VLivePreviewHelper::checkLang(const QString &p_lang, if (m_flowchartEnabled && (p_lang == "flow" || p_lang == "flowchart")) { p_livePreview = p_inplacePreview = true; } else if (m_plantUMLMode != PlantUMLMode::DisablePlantUML && p_lang == "puml") { - p_livePreview = true; - p_inplacePreview = m_plantUMLMode == PlantUMLMode::LocalPlantUML; + p_livePreview = p_inplacePreview = true; } else if (m_graphvizEnabled && p_lang == "dot") { p_livePreview = p_inplacePreview = true; } else if (m_mermaidEnabled && p_lang == "mermaid") { @@ -380,14 +379,12 @@ void VLivePreviewHelper::localAsyncResultReady(int p_id, } CodeBlockPreviewInfo &cb = m_codeBlocks[idx]; - const QString &text = cb.codeBlock().m_text; - QSharedPointer entry(new CodeBlockImageCacheEntry(p_timeStamp, p_format, p_result, background, getScaleFactor(cb))); - m_cache.insert(text, entry); + m_cache.insert(cb.codeBlock().m_text, entry); cb.setImageData(p_format, p_result); cb.updateInplacePreview(m_editor, m_doc, entry->m_image, QString(), background); @@ -425,17 +422,25 @@ void VLivePreviewHelper::processForInplacePreview(int p_idx) m_timeStamp, "svg", VEditUtils::removeCodeBlockFence(vcb.m_text)); - } else if (vcb.m_lang == "puml" && m_plantUMLMode == PlantUMLMode::LocalPlantUML) { - if (!m_plantUMLHelper) { - m_plantUMLHelper = new VPlantUMLHelper(this); - connect(m_plantUMLHelper, &VPlantUMLHelper::resultReady, - this, &VLivePreviewHelper::localAsyncResultReady); - } + } else if (vcb.m_lang == "puml") { + if (m_plantUMLMode == PlantUMLMode::LocalPlantUML) { + if (!m_plantUMLHelper) { + m_plantUMLHelper = new VPlantUMLHelper(this); + connect(m_plantUMLHelper, &VPlantUMLHelper::resultReady, + this, &VLivePreviewHelper::localAsyncResultReady); + } - m_plantUMLHelper->processAsync(p_idx | LANG_PREFIX_PLANTUML | TYPE_INPLACE_PREVIEW, - m_timeStamp, - "svg", - VEditUtils::removeCodeBlockFence(vcb.m_text)); + m_plantUMLHelper->processAsync(p_idx | LANG_PREFIX_PLANTUML | TYPE_INPLACE_PREVIEW, + m_timeStamp, + "svg", + VEditUtils::removeCodeBlockFence(vcb.m_text)); + } else { + m_mathJaxHelper->previewDiagram(m_mathJaxID, + p_idx, + m_timeStamp, + vcb.m_lang, + VEditUtils::removeCodeBlockFence(vcb.m_text)); + } } else if (vcb.m_lang == "flow" || vcb.m_lang == "flowchart") { m_mathJaxHelper->previewDiagram(m_mathJaxID, @@ -497,16 +502,21 @@ void VLivePreviewHelper::mathjaxPreviewResultReady(int p_identitifer, } CodeBlockPreviewInfo &cb = m_codeBlocks[p_id]; - const QString &text = cb.codeBlock().m_text; + const VCodeBlock &vcb = cb.codeBlock(); + + QString background; + if (vcb.m_lang == "puml") { + background = g_config->getEditorPreviewImageBg(); + } QSharedPointer entry(new CodeBlockImageCacheEntry(p_timeStamp, p_format, p_data, - "", + background, getScaleFactor(cb))); - m_cache.insert(text, entry); + m_cache.insert(vcb.m_text, entry); - cb.updateInplacePreview(m_editor, m_doc, entry->m_image); + cb.updateInplacePreview(m_editor, m_doc, entry->m_image, QString(), background); if (cb.inplacePreview()) { entry->m_imageName = cb.inplacePreview()->m_name; diff --git a/src/vmathjaxpreviewhelper.cpp b/src/vmathjaxpreviewhelper.cpp index 62c741bf..127a4827 100644 --- a/src/vmathjaxpreviewhelper.cpp +++ b/src/vmathjaxpreviewhelper.cpp @@ -60,7 +60,13 @@ void VMathJaxPreviewHelper::doInit() TimeStamp p_timeStamp, const QString &p_format, const QString &p_data) { - QByteArray ba = QByteArray::fromBase64(p_data.toUtf8()); + QByteArray ba; + if (p_format == "png") { + ba = QByteArray::fromBase64(p_data.toUtf8()); + } else { + ba = p_data.toUtf8(); + } + emit diagramPreviewResultReady(p_identifier, p_id, p_timeStamp, p_format, ba); });