diff --git a/src/services/import/markdown.js b/src/services/import/markdown.js
index e350cd065..f9cac8eab 100644
--- a/src/services/import/markdown.js
+++ b/src/services/import/markdown.js
@@ -1,10 +1,12 @@
"use strict";
const marked = require("marked");
+const texPlugin = require("./tex").texPlugin;
const htmlSanitizer = require("../html_sanitizer");
const importUtils = require("./utils");
function renderToHtml(content, title) {
+ marked.use(texPlugin());
const html = marked.parse(content, {
mangle: false,
headerIds: false
diff --git a/src/services/import/tex.js b/src/services/import/tex.js
new file mode 100644
index 000000000..d763b2106
--- /dev/null
+++ b/src/services/import/tex.js
@@ -0,0 +1,95 @@
+const inlineRule = /^(\${1,2})(?!\$)((?:\\.|[^\\\n])*?(?:\\.|[^\\\n$]))\1(?=[\s?!.,:?!。,:]|$)/;
+const blockRule = /^(\${1,2})\n((?:\\[^]|[^\\])+?)\n\1(?:\n|$)/;
+
+function texPlugin(options = {}) {
+ return {
+ extensions: [
+ inlineKatex(options, createRenderer(options, false)),
+ blockKatex(options, createRenderer(options, true))
+ ]
+ };
+}
+
+function createRenderer(options, newlineAfter) {
+ return (token) => {
+ let result = token.text;
+ if (token.displayMode) {
+ // full block mode
+ result = `\\[ ${result} \\]`;
+ } else {
+ // inline block mode
+ result = `\\( ${result} \\)`;
+ }
+
+ if (newlineAfter) {
+ result += '\n';
+ }
+
+ return result;
+ };
+}
+
+function inlineKatex(options, renderer) {
+ return {
+ name: 'inlineKatex',
+ level: 'inline',
+ start(src) {
+ let index;
+ let indexSrc = src;
+
+ while (indexSrc) {
+ index = indexSrc.indexOf('$');
+ if (index === -1) {
+ return;
+ }
+
+ if (index === 0 || indexSrc.charAt(index - 1) === ' ') {
+ const possibleKatex = indexSrc.substring(index);
+
+ if (possibleKatex.match(inlineRule)) {
+ return index;
+ }
+ }
+
+ indexSrc = indexSrc.substring(index + 1).replace(/^\$+/, '');
+ }
+ },
+ tokenizer(src, tokens) {
+ const match = src.match(inlineRule);
+ if (match) {
+ return {
+ type: 'inlineKatex',
+ raw: match[0],
+ text: match[2].trim(),
+ displayMode: match[1].length === 2
+ };
+ }
+ },
+ renderer
+ };
+}
+
+function blockKatex(options, renderer) {
+ return {
+ name: 'blockKatex',
+ level: 'block',
+ tokenizer(src, tokens) {
+ const match = src.match(blockRule);
+ if (match) {
+ return {
+ type: 'blockKatex',
+ raw: match[0],
+ text: match[2].trim(),
+ displayMode: match[1].length === 2
+ };
+ }
+ },
+ renderer
+ };
+}
+
+
+module.exports = {
+ texPlugin
+};
+