Эх сурвалжийг харах

增强了xml的预览效果

kl 5 жил өмнө
parent
commit
da1553920b

+ 5 - 0
jodconverter-web/src/main/config/freemarker_implicit.ftl

@@ -1,5 +1,10 @@
 [#ftl]
 [#-- @implicitly included --]
+[#-- @ftlvariable name="xmlContent" type="java.lang.String" --]
+[#-- @ftlvariable name="textContent" type="java.lang.String" --]
+[#-- @ftlvariable name="textType" type="java.lang.String" --]
+[#-- @ftlvariable name="markdown" type="String" --]
+[#-- @ftlvariable name="xml" type="String" --]
 [#-- @ftlvariable name="switchDisabled" type="String" --]
 [#-- @ftlvariable name="imgurls" type="String" --]
 [#-- @ftlvariable name="watermarkAngle" type="String" --]

+ 1 - 0
jodconverter-web/src/main/java/cn/keking/model/FileType.java

@@ -13,6 +13,7 @@ public enum FileType {
     other("otherFilePreviewImpl"),
     media("mediaFilePreviewImpl"),
     markdown("markdownFilePreviewImpl"),
+    xml("xmlFilePreviewImpl"),
     cad("cadFilePreviewImpl");
 
 

+ 1 - 1
jodconverter-web/src/main/java/cn/keking/service/impl/MarkdownFilePreviewImpl.java

@@ -21,7 +21,7 @@ public class MarkdownFilePreviewImpl implements FilePreview {
 
     @Override
     public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
-        model.addAttribute("markdown","true");
+        model.addAttribute("textType","markdown");
         return simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
     }
 }

+ 9 - 0
jodconverter-web/src/main/java/cn/keking/service/impl/SimTextFilePreviewImpl.java

@@ -1,14 +1,18 @@
 package cn.keking.service.impl;
 
 import cn.keking.model.FileAttribute;
+import cn.keking.model.FileType;
 import cn.keking.model.ReturnResponse;
 import cn.keking.service.FilePreview;
 import cn.keking.utils.DownloadUtils;
+import org.apache.commons.io.FileUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.ui.Model;
+import org.springframework.util.Base64Utils;
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 
 /**
@@ -40,6 +44,11 @@ public class SimTextFilePreviewImpl implements FilePreview {
                 previewFile.delete();
             }
             Files.copy(originFile.toPath(), previewFile.toPath());
+            if(fileAttribute.getType().equals(FileType.xml)){
+                String xmlString = FileUtils.readFileToString(previewFile, StandardCharsets.UTF_8);
+
+                model.addAttribute("xmlContent", Base64Utils.encodeToString(xmlString.getBytes()));
+            }
         } catch (IOException e) {
             model.addAttribute("msg", e.getLocalizedMessage());
             model.addAttribute("fileType",fileAttribute.getSuffix());

+ 27 - 0
jodconverter-web/src/main/java/cn/keking/service/impl/XmlFilePreviewImpl.java

@@ -0,0 +1,27 @@
+package cn.keking.service.impl;
+
+import cn.keking.model.FileAttribute;
+import cn.keking.service.FilePreview;
+import org.springframework.stereotype.Service;
+import org.springframework.ui.Model;
+
+/**
+ * @author kl (http://kailing.pub)
+ * @since 2020/12/25
+ */
+@Service
+public class XmlFilePreviewImpl implements FilePreview {
+
+    private final SimTextFilePreviewImpl simTextFilePreview;
+
+    public XmlFilePreviewImpl(SimTextFilePreviewImpl simTextFilePreview) {
+        this.simTextFilePreview = simTextFilePreview;
+    }
+
+
+    @Override
+    public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
+        model.addAttribute("textType","xml");
+        return simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
+    }
+}

+ 3 - 0
jodconverter-web/src/main/java/cn/keking/utils/FileUtils.java

@@ -83,6 +83,9 @@ public class FileUtils {
         if("md".equalsIgnoreCase(fileType)){
             return FileType.markdown;
         }
+        if("xml".equalsIgnoreCase(fileType)){
+            return FileType.xml;
+        }
         if (Arrays.asList(simText).contains(fileType.toLowerCase())) {
             return FileType.simText;
         }

BIN
jodconverter-web/src/main/resources/static/css/img/Collapsed.gif


BIN
jodconverter-web/src/main/resources/static/css/img/Expanded.gif


+ 110 - 0
jodconverter-web/src/main/resources/static/css/xmlTreeViewer.css

@@ -0,0 +1,110 @@
+/* Copyright 2014 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+div.header {
+    border-bottom: 2px solid black;
+    padding-bottom: 5px;
+    margin: 10px;
+}
+
+div.collapsible > div.hidden {
+    display:none;
+}
+
+.pretty-print {
+    margin-top: 1em;
+    margin-left: 20px;
+    font-family: monospace;
+    font-size: 13px;
+}
+
+#xml-viewer-source-xml {
+    display: none;
+}
+
+.collapsible-content {
+    margin-left: 2em;
+}
+.comment {
+    white-space: pre;
+}
+
+.button {
+    -webkit-user-select: none;
+    cursor: pointer;
+    display: inline-block;
+    margin-left: -10px;
+    width: 10px;
+    background-repeat: no-repeat;
+    background-position: left top;
+    vertical-align: bottom;
+}
+
+.collapse-button {
+    /*background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='#909090' width='10' height='10'><path d='M0 0 L8 0 L4 7 Z'/></svg>");*/
+    background: url("./img/Expanded.gif");
+    height: 12px;
+}
+
+.expand-button {
+    /*background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='#909090' width='10' height='10'><path d='M0 0 L0 8 L7 4 Z'/></svg>");*/
+    background: url("./img/Collapsed.gif");
+    height: 12px;
+}
+
+.line-content {
+    padding: 0 5px !important;
+}
+
+.highlight {
+    background-color: rgb(100%, 42%, 42%);
+    border: 2px solid rgb(100%, 31%, 31%);
+}
+
+.html-tag {
+    /* Keep this in sync with inspector.css (.webkit-html-tag) */
+    color: rgb(136, 18, 128);
+}
+
+.html-attribute-name {
+    /* Keep this in sync with inspector.css (.webkit-html-attribute-name) */
+    color: rgb(153, 69, 0);
+}
+
+.html-attribute-value {
+    /* Keep this in sync with inspector.css (.webkit-html-attribute-value) */
+    color: rgb(26, 26, 166);
+}
+
+.html-external-link, .html-resource-link {
+    /* Keep this in sync with inspectorSyntaxHighlight.css (.webkit-html-external-link, .webkit-html-resource-link) */
+    color: #00e;
+}
+
+.html-external-link {
+    /* Keep this in sync with inspectorSyntaxHighlight.css (.webkit-html-external-link) */
+    text-decoration: none;
+}
+
+.html-external-link:hover {
+    /* Keep this in sync with inspectorSyntaxHighlight.css (.webkit-html-external-link:hover) */
+    text-decoration: underline;
+}
+
+.html-comment {
+    /* Keep this in sync with inspectorSyntaxHighlight.css (.webkit-html-comment) */
+    color: rgb(35, 110, 37);
+}
+
+.html-doctype {
+    /* Keep this in sync with inspectorSyntaxHighlight.css (.webkit-html-doctype) */
+    color: rgb(192, 192, 192);
+}
+
+.html-end-of-file {
+    /* Keep this in sync with inspectorSyntaxHighlight.css (.webkit-html-end-of-file) */
+    color: rgb(255, 0, 0);
+    font-weight: bold;
+}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 6 - 0
jodconverter-web/src/main/resources/static/js/base64.min.js


+ 373 - 0
jodconverter-web/src/main/resources/static/js/xmlTreeViewer.js

@@ -0,0 +1,373 @@
+window.xmlTreeViewer = (function() {
+
+    var documentNode = null;
+    var nodeParentPairs = [];
+    var tree;
+    var container;
+
+    function prepareXMLViewer(node) {
+        documentNode = node;
+        var body = createHTMLElement('div');
+        var sourceXML = createHTMLElement('div');
+        sourceXML.id = 'xml-viewer-source-xml';
+        body.appendChild(sourceXML);
+
+        var child;
+        while (child = documentNode.firstChild) {
+            documentNode.removeChild(child);
+            if (child.nodeType != Node.DOCUMENT_TYPE_NODE)
+                sourceXML.appendChild(child);
+        }
+
+        tree = createHTMLElement('div');
+        body.appendChild(tree);
+        tree.classList.add('pretty-print');
+        //window.onload = sourceXMLLoaded;
+        container = body;
+        sourceXMLLoaded();
+        return body;
+    }
+
+    function sourceXMLLoaded() {
+        //var sourceXML = container.getElementById('webkit-xml-viewer-source-xml');
+        var sourceXML = container.firstChild;
+        if (!sourceXML)
+            return; // Stop if some XML tree extension is already processing this document
+
+        for (var child = sourceXML.firstChild; child; child = child.nextSibling)
+            nodeParentPairs.push({parentElement: tree, node: child});
+
+        for (var i = 0; i < nodeParentPairs.length; i++)
+            processNode(nodeParentPairs[i].parentElement, nodeParentPairs[i].node);
+
+        initButtons();
+
+        return false;
+    }
+
+    // Tree processing.
+
+    function processNode(parentElement, node) {
+        var map = processNode.processorsMap;
+        if (!map) {
+            map = {};
+            processNode.processorsMap = map;
+            map[Node.PROCESSING_INSTRUCTION_NODE] = processProcessingInstruction;
+            map[Node.ELEMENT_NODE] = processElement;
+            map[Node.COMMENT_NODE] = processComment;
+            map[Node.TEXT_NODE] = processText;
+            map[Node.CDATA_SECTION_NODE] = processCDATA;
+        }
+        if (processNode.processorsMap[node.nodeType])
+            processNode.processorsMap[node.nodeType].call(this, parentElement, node);
+    }
+
+    function processElement(parentElement, node) {
+        if (!node.firstChild)
+            processEmptyElement(parentElement, node);
+        else {
+            var child = node.firstChild;
+            if (child.nodeType == Node.TEXT_NODE && isShort(child.nodeValue) && !child.nextSibling)
+                processShortTextOnlyElement(parentElement, node);
+            else
+                processComplexElement(parentElement, node);
+        }
+    }
+
+    function processEmptyElement(parentElement, node) {
+        var line = createLine();
+        line.appendChild(createTag(node, false, true));
+        parentElement.appendChild(line);
+    }
+
+    function processShortTextOnlyElement(parentElement, node) {
+        var line = createLine();
+        line.appendChild(createTag(node, false, false));
+        for (var child = node.firstChild; child; child = child.nextSibling)
+            line.appendChild(createText(child.nodeValue));
+        line.appendChild(createTag(node, true, false));
+        parentElement.appendChild(line);
+    }
+
+    function processComplexElement(parentElement, node) {
+        var collapsible = createCollapsible();
+
+        collapsible.expanded.start.appendChild(createTag(node, false, false));
+        for (var child = node.firstChild; child; child = child.nextSibling)
+            nodeParentPairs.push({parentElement: collapsible.expanded.content, node: child});
+        collapsible.expanded.end.appendChild(createTag(node, true, false));
+
+        collapsible.collapsed.content.appendChild(createTag(node, false, false));
+        collapsible.collapsed.content.appendChild(createText('...'));
+        collapsible.collapsed.content.appendChild(createTag(node, true, false));
+        parentElement.appendChild(collapsible);
+    }
+
+    function processComment(parentElement, node) {
+        if (isShort(node.nodeValue)) {
+            var line = createLine();
+            line.appendChild(createComment('<!-- ' + node.nodeValue + ' -->'));
+            parentElement.appendChild(line);
+        } else {
+            var collapsible = createCollapsible();
+
+            collapsible.expanded.start.appendChild(createComment('<!--'));
+            collapsible.expanded.content.appendChild(createComment(node.nodeValue));
+            collapsible.expanded.end.appendChild(createComment('-->'));
+
+            collapsible.collapsed.content.appendChild(createComment('<!--'));
+            collapsible.collapsed.content.appendChild(createComment('...'));
+            collapsible.collapsed.content.appendChild(createComment('-->'));
+            parentElement.appendChild(collapsible);
+        }
+    }
+
+    function processCDATA(parentElement, node) {
+        if (isShort(node.nodeValue)) {
+            var line = createLine();
+            line.appendChild(createText('<![CDATA[ ' + node.nodeValue + ' ]]>'));
+            parentElement.appendChild(line);
+        } else {
+            var collapsible = createCollapsible();
+
+            collapsible.expanded.start.appendChild(createText('<![CDATA['));
+            collapsible.expanded.content.appendChild(createText(node.nodeValue));
+            collapsible.expanded.end.appendChild(createText(']]>'));
+
+            collapsible.collapsed.content.appendChild(createText('<![CDATA['));
+            collapsible.collapsed.content.appendChild(createText('...'));
+            collapsible.collapsed.content.appendChild(createText(']]>'));
+            parentElement.appendChild(collapsible);
+        }
+    }
+
+    function processProcessingInstruction(parentElement, node) {
+        if (isShort(node.nodeValue)) {
+            var line = createLine();
+            line.appendChild(createComment('<?' + node.nodeName + ' ' + node.nodeValue + '?>'));
+            parentElement.appendChild(line);
+        } else {
+            var collapsible = createCollapsible();
+
+            collapsible.expanded.start.appendChild(createComment('<?' + node.nodeName));
+            collapsible.expanded.content.appendChild(createComment(node.nodeValue));
+            collapsible.expanded.end.appendChild(createComment('?>'));
+
+            collapsible.collapsed.content.appendChild(createComment('<?' + node.nodeName));
+            collapsible.collapsed.content.appendChild(createComment('...'));
+            collapsible.collapsed.content.appendChild(createComment('?>'));
+            parentElement.appendChild(collapsible);
+        }
+    }
+
+    function processText(parentElement, node) {
+        parentElement.appendChild(createText(node.nodeValue));
+    }
+
+    // Processing utils.
+
+    function trim(value) {
+        return value.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+    }
+
+    function isShort(value) {
+        return trim(value).length <= 50;
+    }
+
+    // Tree rendering.
+
+    function createHTMLElement(elementName) {
+        //return document.createElementNS('http://www.w3.org/1999/xhtml', elementName);
+        return document.createElement(elementName);
+    }
+
+    function createCollapsible() {
+        var collapsible = createHTMLElement('div');
+        collapsible.classList.add('collapsible');
+        collapsible.expanded = createHTMLElement('div');
+        collapsible.expanded.classList.add('expanded');
+        collapsible.appendChild(collapsible.expanded);
+
+        collapsible.expanded.start = createLine();
+        collapsible.expanded.start.appendChild(createCollapseButton());
+        collapsible.expanded.appendChild(collapsible.expanded.start);
+
+        collapsible.expanded.content = createHTMLElement('div');
+        collapsible.expanded.content.classList.add('collapsible-content');
+        collapsible.expanded.appendChild(collapsible.expanded.content);
+
+        collapsible.expanded.end = createLine();
+        collapsible.expanded.appendChild(collapsible.expanded.end);
+
+        collapsible.collapsed = createHTMLElement('div');
+        collapsible.collapsed.classList.add('collapsed');
+        collapsible.collapsed.classList.add('hidden');
+        collapsible.appendChild(collapsible.collapsed);
+        collapsible.collapsed.content = createLine();
+        collapsible.collapsed.content.appendChild(createExpandButton());
+        collapsible.collapsed.appendChild(collapsible.collapsed.content);
+
+        return collapsible;
+    }
+
+    function createButton() {
+        var button = createHTMLElement('span');
+        button.classList.add('button');
+        return button;
+    }
+
+    function createCollapseButton(str) {
+        var button = createButton();
+        button.classList.add('collapse-button');
+        return button;
+    }
+
+    function createExpandButton(str) {
+        var button = createButton();
+        button.classList.add('expand-button');
+        return button;
+    }
+
+    function createComment(commentString) {
+        var comment = createHTMLElement('span');
+        comment.classList.add('comment');
+        comment.classList.add('html-comment');
+        comment.textContent = commentString;
+        return comment;
+    }
+
+    function createText(value) {
+        var text = createHTMLElement('span');
+        text.textContent = trim(value);
+        text.classList.add('html-text');
+        return text;
+    }
+
+    function createLine() {
+        var line = createHTMLElement('div');
+        line.classList.add('line');
+        return line;
+    }
+
+    function createTag(node, isClosing, isEmpty) {
+        var tag = createHTMLElement('span');
+        tag.classList.add('html-tag');
+
+        var stringBeforeAttrs = '<';
+        if (isClosing)
+            stringBeforeAttrs += '/';
+        //stringBeforeAttrs += node.nodeName;
+        var textBeforeAttrs = document.createTextNode(stringBeforeAttrs);
+        tag.appendChild(textBeforeAttrs);
+        var tagNode = createHTMLElement('span');
+        tagNode.classList.add('html-tag-name');
+        tagNode.textContent = node.nodeName;
+        tag.appendChild(tagNode);
+
+        if (!isClosing) {
+            for (var i = 0; i < node.attributes.length; i++)
+                tag.appendChild(createAttribute(node.attributes[i]));
+        }
+
+        var stringAfterAttrs = '';
+        if (isEmpty)
+            stringAfterAttrs += '/';
+        stringAfterAttrs += '>';
+        var textAfterAttrs = document.createTextNode(stringAfterAttrs);
+        tag.appendChild(textAfterAttrs);
+
+        return tag;
+    }
+
+    function createAttribute(attributeNode) {
+        var attribute = createHTMLElement('span');
+        attribute.classList.add('html-attribute');
+
+        var attributeName = createHTMLElement('span');
+        attributeName.classList.add('html-attribute-name');
+        attributeName.textContent = attributeNode.name;
+
+        var textBefore = document.createTextNode(' ');
+        var textBetween = document.createTextNode('="');
+
+        var attributeValue = createHTMLElement('span');
+        attributeValue.classList.add('html-attribute-value');
+        attributeValue.textContent = attributeNode.value;
+
+        var textAfter = document.createTextNode('"');
+
+        attribute.appendChild(textBefore);
+        attribute.appendChild(attributeName);
+        attribute.appendChild(textBetween);
+        attribute.appendChild(attributeValue);
+        attribute.appendChild(textAfter);
+        return attribute;
+    }
+
+    function expandFunction(sectionId) {
+        return function () {
+            container.querySelector('#' + sectionId + ' > .expanded').className = 'expanded';
+            container.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed hidden';
+        };
+    }
+
+    function collapseFunction(sectionId) {
+        return function () {
+            container.querySelector('#' + sectionId + ' > .expanded').className = 'expanded hidden';
+            container.querySelector('#' + sectionId + ' > .collapsed').className = 'collapsed';
+        };
+    }
+
+    function initButtons() {
+        var sections = container.querySelectorAll('.collapsible');
+        for (var i = 0; i < sections.length; i++) {
+            var sectionId = 'collapsible' + i;
+            sections[i].id = sectionId;
+
+            var expandedPart = sections[i].querySelector('#' + sectionId + ' > .expanded');
+            var collapseButton = expandedPart.querySelector('.collapse-button');
+            collapseButton.onclick = collapseFunction(sectionId);
+            collapseButton.onmousedown = handleButtonMouseDown;
+
+            var collapsedPart = sections[i].querySelector('#' + sectionId + ' > .collapsed');
+            var expandButton = collapsedPart.querySelector('.expand-button');
+            expandButton.onclick = expandFunction(sectionId);
+            expandButton.onmousedown = handleButtonMouseDown;
+        }
+
+    }
+
+    function handleButtonMouseDown(e) {
+        // To prevent selection on double click
+        e.preventDefault();
+    }
+
+    var parseXML = function( data ) {
+        var xml, tmp;
+        if ( !data || typeof data !== "string" ) {
+            return null;
+        }
+        try {
+            if ( window.DOMParser ) { // Standard
+                tmp = new window.DOMParser();
+                xml = tmp.parseFromString( data, "text/xml" );
+            } else { // IE
+                xml = new window.ActiveXObject( "Microsoft.XMLDOM" );
+                xml.async = "false";
+                xml.loadXML( data );
+            }
+        } catch ( e ) {
+            xml = undefined;
+        }
+        var errMsg;
+        if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+            errMsg = xml.getElementsByTagName( "parsererror" )[0].textContent;
+        }
+        return {xml: xml, errMsg: errMsg};
+    };
+
+    return {
+        getXMLViewerNode: prepareXMLViewer,
+        parseXML: parseXML
+    }
+})();

+ 48 - 8
jodconverter-web/src/main/resources/web/txt.ftl

@@ -6,28 +6,38 @@
     <title>普通文本预览</title>
 </head>
 <body>
+<input hidden id="textType" value="${textType}">
 
-<div class="container" >
-    <#if markdown??>
+<div class="container">
+    <#if textType?? && textType == "markdown">
         <p>
             <button id="markdown_btn" type="button" class="btn btn-primary">切换markdown</button>
             <button id="text_btn" type="button" class="btn btn-primary">切换text</button>
         </p>
         <div id="markdown" style="padding: 18px;"></div>
+    <#elseif textType?? && textType == "xml" >
+        <input hidden id="xmlContent" value="${xmlContent}">
+        <div id="xml" style="padding: 18px;"></div>
     <#else>
         <div id="text"></div>
     </#if>
 </div>
 
+<link rel="stylesheet" href="css/xmlTreeViewer.css"/>
 <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/>
+
 <script src="js/jquery-3.0.0.min.js" type="text/javascript"></script>
 <script src="js/jquery.form.min.js" type="text/javascript"></script>
 <script src="bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
 <script src="js/watermark.js" type="text/javascript"></script>
 <script src="js/marked.min.js" type="text/javascript"></script>
+<script src="js/xmlTreeViewer.js" type="text/javascript"></script>
+<script src="js/base64.min.js" type="text/javascript"></script>
 
 <script>
-    /*初始化水印*/
+    /**
+     * 初始化
+     */
     window.onload = function () {
         $("#markdown_btn").hide()
         initWaterMark();
@@ -69,13 +79,42 @@
             success: function (data) {
                 window.textData = data;
                 window.textPreData = "<pre>" + data + "</pre>";
-                window.textMarkdownData =  marked(window.textData);
-                $("#text").html(window.textPreData);
-                $("#markdown").html(window.textMarkdownData);
+                loadText();
+                loadXmlData()
+                loadMarkdown();
             }
         });
     }
 
+    /**
+     *加载普通文本
+     */
+    function loadText() {
+        $("#text").html(window.textPreData);
+    }
+
+    /**
+     * 加载markdown
+     */
+    function loadMarkdown() {
+        if ($("#textType").val() === "markdown") {
+            window.textMarkdownData = marked(window.textData);
+            $("#markdown").html(window.textMarkdownData);
+        }
+    }
+
+    /**
+     * 加载xml数据
+     */
+    function loadXmlData() {
+        if ($("#textType").val() === "xml") {
+            var xmlStr = Base64.decode($("#xmlContent").val());
+            var xmlNode = xmlTreeViewer.parseXML(xmlStr);
+            var retNode = xmlTreeViewer.getXMLViewerNode(xmlNode.xml);
+            $("#xml").html(retNode);
+        }
+    }
+
     $(function () {
         $("#markdown_btn").click(function () {
             $("#markdown").html(window.textMarkdownData);
@@ -101,10 +140,11 @@
         height: 100%;
         width: 100%;
     }
-    #markdown {
+
+    #markdown, #xml {
         height: 97%;
         max-height: 97%;
-        border: 1px solid #eee;
+        border: 1px solid #ece7e7;
         overflow-y: scroll;
         width: 100%;
     }

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно