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