├── renovate.json ├── lib ├── css.js └── filter.js ├── package.json ├── index.js ├── LICENSE ├── .gitignore ├── style.css ├── README.md ├── sample1.svg └── sample2.svg /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /lib/css.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const css = fs.readFileSync(path.resolve(__dirname, '../style.css'), 'utf8'); 4 | 5 | module.exports = css; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hexo-filter-mathjax", 3 | "version": "0.5.3", 4 | "description": "Server side MathJax renderer plugin for Hexo.", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/next-theme/hexo-filter-mathjax.git" 9 | }, 10 | "keywords": [ 11 | "math", 12 | "mathjax", 13 | "renderer", 14 | "hexo" 15 | ], 16 | "author": "Mimi (https://zhangshuqiao.org)", 17 | "readme": "https://github.com/next-theme/hexo-filter-mathjax/blob/master/README.md", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/next-theme/hexo-filter-mathjax/issues" 21 | }, 22 | "homepage": "https://github.com/next-theme/hexo-filter-mathjax", 23 | "dependencies": { 24 | "mathjax-full": "3.0.5", 25 | "string-width": "4.2.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* global hexo */ 2 | 3 | 'use strict'; 4 | 5 | const config = hexo.config.mathjax = Object.assign({ 6 | tags : 'none', 7 | single_dollars: true, 8 | cjk_width : 0.9, 9 | normal_width : 0.6, 10 | append_css : true, 11 | every_page : false 12 | }, hexo.config.mathjax); 13 | 14 | const mathjax = require('./lib/filter')(config); 15 | 16 | hexo.extend.filter.register('after_post_render', data => { 17 | if (!data.mathjax && !config.every_page) return; 18 | 19 | data.content = mathjax(data.content); 20 | return data; 21 | }, 5); 22 | 23 | if (config.append_css) { 24 | const css = require('./lib/css'); 25 | 26 | hexo.extend.filter.register('after_render:html', data => { 27 | // add unique token to prevent replacing repeatedly 28 | if (data.match(``)) return; 29 | return data.replace(/(?!<\/head>).+?<\/head>/s, str => str.replace('', 30 | ` `)); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Mimi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | .env.test 60 | 61 | # parcel-bundler cache (https://parceljs.org/) 62 | .cache 63 | 64 | # next.js build output 65 | .next 66 | 67 | # nuxt.js build output 68 | .nuxt 69 | 70 | # vuepress build output 71 | .vuepress/dist 72 | 73 | # Serverless directories 74 | .serverless/ 75 | 76 | # FuseBox cache 77 | .fusebox/ 78 | 79 | # DynamoDB Local files 80 | .dynamodb/ 81 | 82 | # Npm Lock files 83 | package-lock.json 84 | 85 | # Dist 86 | dist/ 87 | 88 | .DS_Store 89 | -------------------------------------------------------------------------------- /lib/filter.js: -------------------------------------------------------------------------------- 1 | const stringWidth = require('string-width'); 2 | 3 | // 4 | // Load the packages needed for MathJax 5 | // 6 | const { mathjax } = require('mathjax-full/js/mathjax.js'); 7 | const { TeX } = require('mathjax-full/js/input/tex.js'); 8 | const { SVG } = require('mathjax-full/js/output/svg.js'); 9 | const { LiteAdaptor } = require('mathjax-full/js/adaptors/liteAdaptor.js'); 10 | const { RegisterHTMLHandler } = require('mathjax-full/js/handlers/html.js'); 11 | 12 | const { AllPackages } = require('mathjax-full/js/input/tex/AllPackages.js'); 13 | 14 | // 15 | // Create DOM adaptor and register it for HTML documents 16 | // 17 | class myAdaptor extends LiteAdaptor { 18 | value(node) { 19 | // https://github.com/mathjax/MathJax-src/pull/523 20 | return node.value; 21 | } 22 | nodeSize(node) { 23 | const cjk = this.options.cjkWidth; 24 | const width = this.options.normalWidth; 25 | const text = this.textContent(node); 26 | let w = 0; 27 | for (const c of text.split('')) { 28 | w += (stringWidth(c) === 2 ? cjk : width); 29 | } 30 | return [w, 0]; 31 | } 32 | } 33 | myAdaptor.OPTIONS = { 34 | ...LiteAdaptor.OPTIONS, 35 | cjkWidth : 0.9, 36 | normalWidth: 0.6 37 | }; 38 | 39 | module.exports = function(config) { 40 | 41 | const adaptor = new myAdaptor({ 42 | fontSize : 16, 43 | cjkWidth : config.cjk_width, 44 | normalWidth: config.normal_width 45 | }); 46 | RegisterHTMLHandler(adaptor); 47 | 48 | return function(content) { 49 | 50 | // 51 | // Create input and output jax and a document using them on the content from the HTML file 52 | // 53 | const tex = new TeX({ 54 | packages : AllPackages, 55 | tags : config.tags, 56 | inlineMath: config.single_dollars ? { 57 | '[+]': [['$', '$']] 58 | } : {} 59 | }); 60 | const svg = new SVG({ 61 | fontCache: 'none' 62 | }); 63 | const html = mathjax.document(content, { 64 | InputJax : tex, 65 | OutputJax: svg 66 | }); 67 | 68 | // 69 | // Typeset the document 70 | // 71 | html.render(); 72 | 73 | // 74 | // Output the resulting HTML 75 | // 76 | return adaptor.innerHTML(adaptor.body(html.document)); 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | mjx-container[jax="SVG"] { 2 | direction: ltr; 3 | } 4 | 5 | mjx-container[jax="SVG"] > svg { 6 | overflow: visible; 7 | } 8 | 9 | mjx-container[jax="SVG"] > svg a { 10 | fill: blue; 11 | stroke: blue; 12 | } 13 | 14 | mjx-container[jax="SVG"][display="true"] { 15 | display: block; 16 | text-align: center; 17 | margin: 1em 0; 18 | } 19 | 20 | mjx-container[jax="SVG"][justify="left"] { 21 | text-align: left; 22 | } 23 | 24 | mjx-container[jax="SVG"][justify="right"] { 25 | text-align: right; 26 | } 27 | 28 | g[data-mml-node="merror"] > g { 29 | fill: red; 30 | stroke: red; 31 | } 32 | 33 | g[data-mml-node="merror"] > rect[data-background] { 34 | fill: yellow; 35 | stroke: none; 36 | } 37 | 38 | g[data-mml-node="mtable"] > line[data-line] { 39 | stroke-width: 70px; 40 | fill: none; 41 | } 42 | 43 | g[data-mml-node="mtable"] > rect[data-frame] { 44 | stroke-width: 70px; 45 | fill: none; 46 | } 47 | 48 | g[data-mml-node="mtable"] > .mjx-dashed { 49 | stroke-dasharray: 140; 50 | } 51 | 52 | g[data-mml-node="mtable"] > .mjx-dotted { 53 | stroke-linecap: round; 54 | stroke-dasharray: 0,140; 55 | } 56 | 57 | g[data-mml-node="mtable"] > svg { 58 | overflow: visible; 59 | } 60 | 61 | [jax="SVG"] mjx-tool { 62 | display: inline-block; 63 | position: relative; 64 | width: 0; 65 | height: 0; 66 | } 67 | 68 | [jax="SVG"] mjx-tool > mjx-tip { 69 | position: absolute; 70 | top: 0; 71 | left: 0; 72 | } 73 | 74 | mjx-tool > mjx-tip { 75 | display: inline-block; 76 | padding: .2em; 77 | border: 1px solid #888; 78 | font-size: 70%; 79 | background-color: #F8F8F8; 80 | color: black; 81 | box-shadow: 2px 2px 5px #AAAAAA; 82 | } 83 | 84 | g[data-mml-node="maction"][data-toggle] { 85 | cursor: pointer; 86 | } 87 | 88 | mjx-status { 89 | display: block; 90 | position: fixed; 91 | left: 1em; 92 | bottom: 1em; 93 | min-width: 25%; 94 | padding: .2em .4em; 95 | border: 1px solid #888; 96 | font-size: 90%; 97 | background-color: #F8F8F8; 98 | color: black; 99 | } 100 | 101 | foreignObject[data-mjx-xml] { 102 | font-family: initial; 103 | line-height: normal; 104 | overflow: visible; 105 | } 106 | 107 | .MathJax path { 108 | stroke-width: 3; 109 | } 110 | 111 | mjx-container[display="true"] { 112 | overflow: auto hidden; 113 | } 114 | 115 | mjx-container[display="true"] + br { 116 | display: none; 117 | } 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hexo Filter MathJax 2 | 3 | [![npm-image]][npm-url] 4 | [![lic-image]](LICENSE) 5 | 6 | Server side [MathJax](http://www.mathjax.org/) Renderer Plugin for [Hexo](http://hexo.io/). 7 | 8 | **WARNING: No front-end scripts and other Math plugins are required. Remove them ALL before using this plugin.** 9 | 10 | ## Installation 11 | 12 | ![size-image] 13 | [![dm-image]][npm-url] 14 | [![dt-image]][npm-url] 15 | 16 | ```bash 17 | $ npm install hexo-filter-mathjax 18 | $ hexo clean 19 | ``` 20 | 21 | ## Options 22 | 23 | You can configure this plugin in Hexo `_config.yml`. Default options: 24 | 25 | ```yaml 26 | mathjax: 27 | tags: none # or 'ams' or 'all' 28 | single_dollars: true # enable single dollar signs as in-line math delimiters 29 | cjk_width: 0.9 # relative CJK char width 30 | normal_width: 0.6 # relative normal (monospace) width 31 | append_css: true # add CSS to every page 32 | every_page: false # if true, every page will be rendered by mathjax regardless the `mathjax` setting in Front-matter of each article 33 | ``` 34 | 35 | ## Usage 36 | 37 | Set `mathjax: true` in Front-matter of each article (post / page) that you would like to enable mathjax. For example: 38 | 39 | ```md 40 | --- 41 | title: On the Electrodynamics of Moving Bodies 42 | categories: Physics 43 | date: 1905-06-30 12:00:00 44 | mathjax: true 45 | --- 46 | ``` 47 | 48 | Then you can use the LaTeX syntax in the article. 49 | 50 | ### Renderer 51 | 52 | [hexo-renderer-pandoc](https://github.com/wzpan/hexo-renderer-pandoc) is recommended because it can handle mathematical formulas in markdown documents perfectly. But you need to be aware that inline Math (..`$...$`) must not have white spaces **after the opening `$` and before the ending `$`**. For example: 53 | ```diff 54 | -$ \epsilon_0 $ 55 | +$\epsilon_0$ 56 | -$ \frac{\partial}{\partial t} $ 57 | +$\frac{\partial}{\partial t}$ 58 | ``` 59 | 60 | If you are using other renderers, such as [hexo-renderer-marked](https://github.com/hexojs/hexo-renderer-marked), you need to be aware of the conflict between LaTeX and Markdown syntax. Use `\` to escape if necessary: 61 | ```diff 62 | -$\epsilon_0$ 63 | +$\epsilon\_0$ 64 | -\begin{eqnarray*} 65 | +\begin{eqnarray\*} 66 | ``` 67 | 68 | ## Sample 69 | 70 | Write the following LaTeX code: 71 | ``` 72 | $$ 73 | i\hbar\frac{\partial}{\partial t}\psi=-\frac{\hbar^2}{2m}\nabla^2\psi+V\psi 74 | $$ 75 | ``` 76 | 77 | ``` 78 | \begin{eqnarray\*} 79 | \nabla\cdot\vec{E}&=&\frac{\rho}{\epsilon_0}\\\\ 80 | \nabla\cdot\vec{B}&=&0\\\\ 81 | \nabla\times\vec{E}&=&-\frac{\partial B}{\partial t}\\\\ 82 | \nabla\times\vec{B}&=&\mu_0\left(\vec{J}+\epsilon_0\frac{\partial E}{\partial t}\right)\\\\ 83 | \end{eqnarray\*} 84 | ``` 85 | 86 | Then you will get: 87 | 88 | ![](sample1.svg) 89 | 90 | ![](sample2.svg) 91 | 92 | ## License 93 | 94 | Released under the MIT License 95 | 96 | [npm-image]: https://img.shields.io/npm/v/hexo-filter-mathjax?style=flat-square 97 | [lic-image]: https://img.shields.io/npm/l/hexo-filter-mathjax?style=flat-square 98 | 99 | [size-image]: https://img.shields.io/github/languages/code-size/next-theme/hexo-filter-mathjax?style=flat-square 100 | [dm-image]: https://img.shields.io/npm/dm/hexo-filter-mathjax?style=flat-square 101 | [dt-image]: https://img.shields.io/npm/dt/hexo-filter-mathjax?style=flat-square 102 | 103 | [npm-url]: https://www.npmjs.com/package/hexo-filter-mathjax 104 | -------------------------------------------------------------------------------- /sample1.svg: -------------------------------------------------------------------------------- 1 | 2 | i\hbar\frac{\partial}{\partial t}\psi=-\frac{\hbar^2}{2m}\nabla^2\psi+V\psi 3 | 17 | 55 | 56 | -------------------------------------------------------------------------------- /sample2.svg: -------------------------------------------------------------------------------- 1 | 2 | \begin{eqnarray*} \nabla\cdot\vec{E}&=&\frac{\rho}{\epsilon_0}\\ \nabla\cdot\vec{B}&=&0\\ \nabla\times\vec{E}&=&-\frac{\partial B}{\partial t}\\ \nabla\times\vec{B}&=&\mu_0\left(\vec{J}+\epsilon_0\frac{\partial E}{\partial t}\right)\\ \end{eqnarray*} 3 | 25 | 136 | 137 | --------------------------------------------------------------------------------