Преглед на файлове

InplacePreview: support online PlantUML in-place preview

Le Tan преди 7 години
родител
ревизия
d66fd7c1c5
променени са 4 файла, в които са добавени 157 реда и са изтрити 75 реда
  1. 116 55
      src/resources/mathjax_preview.js
  2. 5 0
      src/utils/vutils.cpp
  3. 29 19
      src/vlivepreviewhelper.cpp
  4. 7 1
      src/vmathjaxpreviewhelper.cpp

+ 116 - 55
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);
+}

+ 5 - 0
src/utils/vutils.cpp

@@ -942,6 +942,11 @@ QString VUtils::generateMathJaxPreviewTemplate()
                  "                    messageStyle: \"none\"});\n"
                  "</script>\n";
 
+    // PlantUML.
+    extraFile += "<script type=\"text/javascript\" src=\"" + VNote::c_plantUMLJsFile + "\"></script>\n" +
+                 "<script type=\"text/javascript\" src=\"" + VNote::c_plantUMLZopfliJsFile + "\"></script>\n" +
+                 "<script>var VPlantUMLServer = '" + g_config->getPlantUMLServer() + "';</script>\n";
+
     templ.replace(HtmlHolder::c_extraHolder, extraFile);
 
     return templ;

+ 29 - 19
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<CodeBlockImageCacheEntry> 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<CodeBlockImageCacheEntry> 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;

+ 7 - 1
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);
             });