├── LICENSE.txt ├── tests ├── simple.markdown ├── text.markdown ├── error.markdown ├── nested.markdown ├── markdown-latex.cfg ├── preamble.markdown └── math.markdown ├── tests2 ├── markdown-latex.cfg └── simple.markdown ├── README.markdown └── latex.py /LICENSE.txt: -------------------------------------------------------------------------------- 1 | http://creativecommons.org/publicdomain/mark/1.0/ 2 | -------------------------------------------------------------------------------- /tests/simple.markdown: -------------------------------------------------------------------------------- 1 | This is a simple test: $y = mx + b$. This should all be inlined. 2 | -------------------------------------------------------------------------------- /tests/text.markdown: -------------------------------------------------------------------------------- 1 | To which he said, %``My name is Justin Bruce Van Horne"% and I replied "Nay.". 2 | 3 | -------------------------------------------------------------------------------- /tests/error.markdown: -------------------------------------------------------------------------------- 1 | This page should generate an error. The output should be logged to a temporary file. 2 | $\undefinederror$ 3 | -------------------------------------------------------------------------------- /tests/nested.markdown: -------------------------------------------------------------------------------- 1 | %This should be an image% But this should not be %But this should be% 2 | \$9.99 is not an image, but $\$9.99$ is. Likewise, 6 \% 4 is not the same as %6 \% 4%. 3 | -------------------------------------------------------------------------------- /tests/markdown-latex.cfg: -------------------------------------------------------------------------------- 1 | # This configuration file is parsed by python's ConfigParser and therefore 2 | # follows RFC 822. 3 | 4 | [dvipng] 5 | # The following arguments are passed to dvipng. 6 | args = -q -T tight -bg Transparent -z 9 -D 106 7 | -------------------------------------------------------------------------------- /tests/preamble.markdown: -------------------------------------------------------------------------------- 1 | 2 | 3 | %% 4 | \definecolor{jvh}{rgb}{0.411, 0.407, 0.196} 5 | \pagecolor{jvh} 6 | \color{White} 7 | %% 8 | 9 | Oh, such strange color. Yet, %it does seem to look right!% 10 | $\displaystyle \sigma$! 11 | -------------------------------------------------------------------------------- /tests/math.markdown: -------------------------------------------------------------------------------- 1 | %We could also consider situations such as:% 2 | 3 | 4 | $ 5 | \begin{tabular}{|c|c|c|c|} 6 | Name & Age & Degree & Happy \\ 7 | \hline \\ 8 | Justin & 21 & Electrical Engineering & Yes \\ 9 | Miuche & 39 & None & No \\ 10 | \hline \\ 11 | \end{tabular} 12 | $ 13 | 14 | 15 | - Or even: $y = mx + b$ 16 | - Or even: $e^{\imath x} = \cos{x} + \imath\sin{x}$ 17 | -------------------------------------------------------------------------------- /tests2/markdown-latex.cfg: -------------------------------------------------------------------------------- 1 | # This configuration file is parsed by python's ConfigParser and therefore 2 | # follows RFC 822. 3 | 4 | [general] 5 | preamble : \usepackage[sc]{mathpazo} 6 | \usepackage{tgpagella} 7 | 8 | [dvipng] 9 | # The following arguments are passed to dvipng. 10 | args = -q -T tight -bg Transparent -z 9 -D 106 11 | 12 | [delimiters] 13 | text = %% 14 | math = $$ 15 | preamble = %%% 16 | -------------------------------------------------------------------------------- /tests2/simple.markdown: -------------------------------------------------------------------------------- 1 | This examples illustrates using delimiters \$$, \%%, and %\%% 2 | -------- 3 | 4 | This is math: $$\int_0^\infty e^{-x} \; d x = 1$$

5 | This is text: %%\LaTeX\ rocks, even in markdown with the latex plugin.%%

6 | 7 | %%% 8 | \usepackage[usenames]{xcolor} 9 | \color{red} 10 | %%% 11 | 12 | This is not latex text: \%% foo \%%

13 | This is not latex math: \$$ i^2 = -1 \$$

14 | This is a backslash with delimiters: \\\%%, \\%\%%, \\$$
15 | And again: \\\%%, \\%\%%, \\$$ 16 | 17 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Markdown-LaTeX 2 | This [Markdown](http://daringfireball.net/projects/markdown/) extension adds support for inline LaTeX expressions without the need for external images. 3 | The available modes are %TEXT%, $MATH$, and %%PREAMBLE%%. 4 | 5 | ## Modes 6 | Markdown-LaTeX can technically allow for any sort of LaTeX expression, but there are three main modes that make writing everything much easier. 7 | 8 | ### %TEXT% mode 9 | The most basic of the modes is %TEXT% mode. Any expression within %TEXT% will be parsed in a basic LaTeX text-mode, e.g. plain. For example, %``Justin Bruce Van Horne''%. 10 | 11 | ### $MATH$ mode 12 | Math mode is our second basic functionality of Markdown-LaTeX. It automatically puts LaTeX into math mode and allows for inline and multiline expressions. Let's say that we wanted to write [Euler's formula](http://en.wikipedia.org/wiki/Euler's_formula): We would then write $e^{\imath x} = \cos{x} + \imath\sin{x}$. 13 | 14 | ### %%PREAMBLE%% mode 15 | Preamble mode is the only "complex" mode. It allows you to add to the preamble of the LaTeX template. So, if there are specific packages or commands you want for the HTML page, then this is where to do it. This is a global modifier and will affect the entire document. I haven't come up with an intuitive design for a per-expression basis. 16 | 17 | ## Dependencies 18 | Markdown-LaTeX depends on: 19 | 20 | - [LaTeX](http://www.latex-project.org/) 21 | - [dvipng](http://sourceforge.net/projects/dvipng/) 22 | - [Python Markdown](http://www.freewisdom.org/projects/python-markdown/) 23 | 24 | ## Installation 25 | You can either copy it into the extensions sub-directory in your markdown folder (ex: /usr/lib/python2.7/site-packages/markdown/extensions/latex.py) or use it locally with the mdx_ prefix. See [Markdown Extensions](http://www.freewisdom.org/projects/python-markdown/Writing_Extensions) for more details. 26 | 27 | ## Usage 28 | markdown -x latex somefile.markdown > somefile.html 29 | 30 | ## Configuration 31 | 32 | This plugin uses dvipng in order to produce the output images. The arguments 33 | passed to dvipng are configurable, see tests/markdown-latex.cfg for an example. 34 | In particular, you may want to adapt the argument passed to the -D option in 35 | order to change output resolution resp. font size. 36 | 37 | You may also change the delimiters used for text, math and preamble mode within 38 | the configuration file, see tests2/markdown-latex.cfg. When running this plugin 39 | on existing text you need to escape all occurrences of % and $. Changing 40 | delimiters may reduce this effort. 41 | 42 | 43 | ---- 44 | 45 | ## How does it work? 46 | The LaTeX extension will search for either $text$ or %text% expressions. For each expression, it generates a tex file that is parsed by latex and then run through dvipng. The data is encoded via base64 and then inlined. A cache file (latex.cache) is used to store all expressions and their base64 counterparts. This is to prevent latex from being run each time. 47 | 48 | ---- 49 | 50 | ## Suggestions/Improvements: 51 | - [Geremy Condra](https://github.com/debatem1) 52 | - [Jabba Laci](https://github.com/jabbalaci) 53 | - [Stefan Huber](https://github.com/shuber2) 54 | -------------------------------------------------------------------------------- /latex.py: -------------------------------------------------------------------------------- 1 | """ 2 | Licensed under Public Domain Mark 1.0. 3 | See http://creativecommons.org/publicdomain/mark/1.0/ 4 | Author: Justin Bruce Van Horne 5 | """ 6 | 7 | 8 | """ 9 | Python-Markdown LaTeX Extension 10 | 11 | Adds support for $math mode$ and %text mode%. This plugin supports 12 | multiline equations/text. 13 | 14 | The actual image generation is done via LaTeX/DVI output. 15 | It encodes data as base64 so there is no need for images directly. 16 | All the work is done in the preprocessor. 17 | """ 18 | 19 | from sys import version 20 | import re 21 | import os 22 | import string 23 | import base64 24 | import tempfile 25 | import markdown 26 | 27 | 28 | from subprocess import call as rawcall, PIPE 29 | 30 | 31 | 32 | # Proxies to existing functions for conserving compatibility between 33 | # Python2 and Python3 34 | def isalnum(expr): 35 | '''Proxy to expr.isalnum() that can be used by filter()''' 36 | return expr.isalnum() 37 | 38 | def call(*args, **kwargs): 39 | ''' 40 | Proxy to subprocess.call(), removes timeout argument in case of 41 | Python2 because that was only implemented in Python3. 42 | ''' 43 | if 'timeout' in kwargs and version[0] == '2': 44 | del kwargs['timeout'] 45 | rawcall(*args, **kwargs) 46 | 47 | 48 | 49 | # Defines our basic inline image 50 | IMG_EXPR = "%s" 52 | 53 | 54 | # Base CSS template 55 | IMG_CSS = \ 56 | "\n" 57 | 58 | 59 | # Cache and temp file paths 60 | _TEMPDIR = tempfile.gettempdir() + '/markdown-latex' 61 | _CACHEFILE = _TEMPDIR + '/latex.cache' 62 | 63 | class LaTeXPreprocessor(markdown.preprocessors.Preprocessor): 64 | # These are our cached expressions that are stored in latex.cache 65 | cached = {} 66 | 67 | # Basic LaTex Setup as well as our list of expressions to parse 68 | tex_preamble = r"""\documentclass{article} 69 | \usepackage{amsmath} 70 | \usepackage{amsthm} 71 | \usepackage{amssymb} 72 | \usepackage{bm} 73 | \usepackage[usenames,dvipsnames]{color} 74 | \pagestyle{empty} 75 | """ 76 | 77 | def __init__(self, configs): 78 | if not os.path.isdir(_TEMPDIR): 79 | os.makedirs(_TEMPDIR) 80 | try: 81 | cache_file = open(_CACHEFILE, 'r+') 82 | for line in cache_file.readlines(): 83 | key, val = line.strip("\n").split(" ") 84 | self.cached[key] = val 85 | except IOError: 86 | pass 87 | 88 | self.config = { 89 | ("general", "preamble"): "", 90 | ("dvipng", "args"): "-q -T tight -bg Transparent -z 9 -D 106", 91 | ("delimiters", "text"): "%", 92 | ("delimiters", "math"): "$", 93 | ("delimiters", "preamble"): "%%" } 94 | 95 | try: 96 | # ConfigParser was renamed to configparser in Python3. 97 | # Import it in a way that works across versions, 98 | # using the Python3 naming convention in the rest of the code. 99 | try: 100 | import configparser 101 | except ImportError: 102 | import ConfigParser as configparser 103 | cfgfile = configparser.RawConfigParser() 104 | cfgfile.read('markdown-latex.cfg') 105 | 106 | for sec in cfgfile.sections(): 107 | for opt in cfgfile.options(sec): 108 | self.config[(sec, opt)] = cfgfile.get(sec, opt) 109 | except configparser.NoSectionError: 110 | pass 111 | 112 | def build_regexp(delim): 113 | delim = re.escape(delim) 114 | regexp = r'(?html_block") 262 | # Our cleanup postprocessing extension 263 | md.postprocessors.add('latex', 264 | LaTeXPostprocessor(self), ">amp_substitute") 265 | 266 | 267 | def makeExtension(*args, **kwargs): 268 | """Wrapper for a MarkDown extension""" 269 | return MarkdownLatex(*args, **kwargs) 270 | --------------------------------------------------------------------------------