16 changed files with 803 additions and 17 deletions
-
14Makefile
-
31content/blog/03_quadrocopter_en.md
-
5content/blog/04_keyboad_en.md
-
BINcontent/extra/favicon.ico
-
BINcontent/images_org/favicon.xcf
-
BINcontent/images_org/quad/prop.jpg
-
BINcontent/images_org/quad/prop_d.jpg
-
BINcontent/images_org/quad/prop_pitch.jpg
-
16pelicanconf.py
-
2plugins/extended-sitemap/__init__.py
-
164plugins/render_math/Readme.md
-
1plugins/render_math/__init__.py
-
367plugins/render_math/math.py
-
61plugins/render_math/mathjax_script_template
-
158plugins/render_math/pelican_mathjax_markdown_extension.py
-
1themes/bootstrap-4-blog-theme/templates/base.html
After Width: 575 | Height: 398 | Size: 24 KiB |
After Width: 400 | Height: 300 | Size: 17 KiB |
After Width: 608 | Height: 194 | Size: 14 KiB |
@ -0,0 +1,164 @@ |
|||||
|
Math Render Plugin For Pelican |
||||
|
============================== |
||||
|
This plugin gives pelican the ability to render mathematics. It accomplishes |
||||
|
this by using the [MathJax](http://www.mathjax.org/) javascript engine. |
||||
|
|
||||
|
The plugin also ensures that Typogrify and recognized math "play" nicely together, by |
||||
|
ensuring [Typogrify](https://github.com/mintchaos/typogrify) does not alter math content. |
||||
|
|
||||
|
Both Markdown and reStructuredText is supported. |
||||
|
|
||||
|
Requirements |
||||
|
------------ |
||||
|
|
||||
|
* Pelican version *3.6* or above is required. |
||||
|
* Typogrify version *2.0.7* or higher is needed for Typogrify to play |
||||
|
"nicely" with this plugin. If this version is not available, Typogrify |
||||
|
will be disabled for the entire site. |
||||
|
* BeautifulSoup4 is required to correct summaries. If BeautifulSoup4 is |
||||
|
not installed, summary processing will be ignored, even if specified |
||||
|
in user settings. |
||||
|
|
||||
|
Installation |
||||
|
------------ |
||||
|
To enable, ensure that `render_math` plugin is accessible. |
||||
|
Then add the following to settings.py: |
||||
|
|
||||
|
PLUGINS = ["render_math"] |
||||
|
|
||||
|
Your site is now capable of rendering math math using the mathjax JavaScript |
||||
|
engine. No alterations to the template is needed, just use and enjoy! |
||||
|
|
||||
|
However, if you wish, you can set the `auto_insert` setting to `False` which |
||||
|
will disable the mathjax script from being automatically inserted into the |
||||
|
content. You would only want to do this if you had control over the template |
||||
|
and wanted to insert the script manually. |
||||
|
|
||||
|
### Typogrify |
||||
|
In the past, using [Typgogrify](https://github.com/mintchaos/typogrify) would |
||||
|
alter the math contents resulting in math that could not be rendered by MathJax. |
||||
|
The only option was to ensure that Typogrify was disabled in the settings. |
||||
|
|
||||
|
The problem has been rectified in this plugin, but it requires at a minimum |
||||
|
[Typogrify version 2.0.7](https://pypi.python.org/pypi/typogrify) (or higher). |
||||
|
If this version is not present, the plugin will disable Typogrify for the entire |
||||
|
site. |
||||
|
|
||||
|
### BeautifulSoup4 |
||||
|
Pelican creates summaries by truncating the contents to a specified user length. |
||||
|
The truncation process is oblivious to any math and can therefore destroy |
||||
|
the math output in the summary. |
||||
|
|
||||
|
To restore math, [BeautifulSoup4](https://pypi.python.org/pypi/beautifulsoup4/4.4.0) |
||||
|
is used. If it is not installed, no summary processing will happen. |
||||
|
|
||||
|
Usage |
||||
|
----- |
||||
|
### Templates |
||||
|
No alteration is needed to a template for this plugin to work. Just install |
||||
|
the plugin and start writing your Math. |
||||
|
|
||||
|
### Settings |
||||
|
Certain MathJax rendering options can be set. These options |
||||
|
are in a dictionary variable called `MATH_JAX` in the pelican |
||||
|
settings file. |
||||
|
|
||||
|
The dictionary can be set with the following keys: |
||||
|
|
||||
|
* `align`: [string] controls how displayed math will be aligned. Can be set to either |
||||
|
`'left'`, `'right'` or `'center'`. **Default Value**: `'center'`. |
||||
|
* `auto_insert`: [boolean] will insert the mathjax script into content that it is |
||||
|
detected to have math in it. Setting it to false is not recommended. |
||||
|
**Default Value**: `True` |
||||
|
* `indent`: [string] if `align` not set to `'center'`, then this controls the indent |
||||
|
level. **Default Value**: `'0em'`. |
||||
|
* `show_menu`: [boolean] controls whether the mathjax contextual menu is shown. |
||||
|
**Default Value**: `True` |
||||
|
* `process_escapes`: [boolean] controls whether mathjax processes escape sequences. |
||||
|
**Default Value**: `True` |
||||
|
* `mathjax_font`: [string] will force mathjax to use the chosen font. Current choices |
||||
|
for the font is `sanserif`, `typewriter` or `fraktur`. If this is not set, it will |
||||
|
use the default font settings. **Default Value**: `default` |
||||
|
* `latex_preview`: [string] controls the preview message users are shown while mathjax is |
||||
|
rendering LaTex. If set to `'Tex'`, then the TeX code is used as the preview |
||||
|
(which will be visible until it is processed by MathJax). **Default Value**: `'Tex'` |
||||
|
* `color`: [string] controls the color of the mathjax rendered font. **Default Value**: `'inherit'` |
||||
|
* `linebreak_automatic`: [boolean] If set, Mathjax will try to *intelligently* break up displayed math |
||||
|
(Note: It will not work for inline math). This is very useful for a responsive site. It |
||||
|
is turned off by default due to it potentially being CPU expensive. **Default Value**: `False` |
||||
|
* `tex_extensions`: [list] a list of [latex extensions](http://docs.mathjax.org/en/latest/tex.html#tex-and-latex-extensions) |
||||
|
accepted by mathjax. **Default Value**: `[]` (empty list) |
||||
|
* `responsive`: [boolean] tries to make displayed math render responsively. It does by determining if the width |
||||
|
is less than `responsive_break` (see below) and if so, sets `align` to `left`, `indent` to `0em` and `linebreak_automatic` to `True`. |
||||
|
**Default Value**: `False` (defaults to `False` for backward compatibility) |
||||
|
* `responsive_break`: [integer] a number (in pixels) representing the width breakpoint that is used |
||||
|
when setting `responsive_align` to `True`. **Default Value**: 768 |
||||
|
* `process_summary`: [boolean] ensures math will render in summaries and fixes math in that were cut off. |
||||
|
Requires [BeautifulSoup4](http://www.crummy.com/software/BeautifulSoup/bs4/doc/) be installed. **Default Value**: `True` |
||||
|
* `message_style`: [string] This value controls the verbosity of the messages in the lower left-hand corner. Set it to `None` to eliminate all messages. |
||||
|
**Default Value**: normal |
||||
|
|
||||
|
#### Settings Examples |
||||
|
Make math render in blue and displaymath align to the left: |
||||
|
|
||||
|
MATH_JAX = {'color':'blue','align':left} |
||||
|
|
||||
|
Use the [color](http://docs.mathjax.org/en/latest/tex.html#color) and |
||||
|
[mhchem](http://docs.mathjax.org/en/latest/tex.html#mhchem) extensions: |
||||
|
|
||||
|
MATH_JAX = {'tex_extensions': ['color.js','mhchem.js']} |
||||
|
|
||||
|
#### Resulting HTML |
||||
|
Inlined math is wrapped in `span` tags, while displayed math is wrapped in `div` tags. |
||||
|
These tags will have a class attribute that is set to `math` which |
||||
|
can be used by template designers to alter the display of the math. |
||||
|
|
||||
|
Markdown |
||||
|
-------- |
||||
|
This plugin implements a custom extension for markdown resulting in math |
||||
|
being a "first class citizen" for Pelican. |
||||
|
|
||||
|
### Inlined Math |
||||
|
Math between `$`..`$`, for example, `$`x^2`$`, will be rendered inline |
||||
|
with respect to the current html block. Note: To use inline math, there |
||||
|
must *not* be any whitespace before the ending `$`. So for example: |
||||
|
|
||||
|
* **Relevant inline math**: `$e=mc^2$` |
||||
|
* **Will not render as inline math**: `$40 vs $50` |
||||
|
|
||||
|
### Displayed Math |
||||
|
Math between `$$`..`$$` will be rendered "block style", for example, `$$`x^2`$$`, will be rendered centered in a |
||||
|
new paragraph. |
||||
|
|
||||
|
#### Other Latex Display Math commands |
||||
|
The other LaTeX commands which usually invoke display math mode from text mode |
||||
|
are supported, |
||||
|
and are automatically treated like `$$`-style displayed math |
||||
|
in that they are rendered "block" style on their own lines. |
||||
|
For example, `\begin{equation}` x^2 `\end{equation}`, |
||||
|
will be rendered in its own block with a right justified equation number |
||||
|
at the top of the block. This equation number can be referenced in the document. |
||||
|
To do this, use a `label` inside of the equation format and then refer to that label |
||||
|
using `ref`. For example: `\begin{equation}` `\label{eq}` X^2 `\end{equation}`. |
||||
|
Now refer to that equation number by `$`\ref{eq}`$`. |
||||
|
|
||||
|
reStructuredText |
||||
|
---------------- |
||||
|
If there is math detected in reStructuredText document, the plugin will automatically |
||||
|
set the [math_output](http://docutils.sourceforge.net/docs/user/config.html#math-output) configuration setting to `MathJax`. |
||||
|
|
||||
|
### Inlined Math |
||||
|
Inlined math needs to use the [math role](http://docutils.sourceforge.net/docs/ref/rst/roles.html#math): |
||||
|
|
||||
|
``` |
||||
|
The area of a circle is :math:`A_\text{c} = (\pi/4) d^2`. |
||||
|
``` |
||||
|
|
||||
|
### Displayed Math |
||||
|
Displayed math uses the [math block](http://docutils.sourceforge.net/docs/ref/rst/directives.html#math): |
||||
|
|
||||
|
``` |
||||
|
.. math:: |
||||
|
|
||||
|
α_t(i) = P(O_1, O_2, … O_t, q_t = S_i λ) |
||||
|
``` |
@ -0,0 +1 @@ |
|||||
|
from .math import * |
@ -0,0 +1,367 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
""" |
||||
|
Math Render Plugin for Pelican |
||||
|
============================== |
||||
|
This plugin allows your site to render Math. It uses |
||||
|
the MathJax JavaScript engine. |
||||
|
|
||||
|
For markdown, the plugin works by creating a Markdown |
||||
|
extension which is used during the markdown compilation |
||||
|
stage. Math therefore gets treated like a "first class |
||||
|
citizen" in Pelican |
||||
|
|
||||
|
For reStructuredText, the plugin instructs the rst engine |
||||
|
to output Mathjax for all math. |
||||
|
|
||||
|
The mathjax script is by default automatically inserted |
||||
|
into the HTML. |
||||
|
|
||||
|
Typogrify Compatibility |
||||
|
----------------------- |
||||
|
This plugin now plays nicely with Typogrify, but it |
||||
|
requires Typogrify version 2.07 or above. |
||||
|
|
||||
|
User Settings |
||||
|
------------- |
||||
|
Users are also able to pass a dictionary of settings |
||||
|
in the settings file which will control how the MathJax |
||||
|
library renders things. This could be very useful for |
||||
|
template builders that want to adjust the look and feel of |
||||
|
the math. See README for more details. |
||||
|
""" |
||||
|
|
||||
|
import os |
||||
|
import sys |
||||
|
|
||||
|
from pelican import signals, generators |
||||
|
|
||||
|
try: |
||||
|
from bs4 import BeautifulSoup |
||||
|
except ImportError as e: |
||||
|
BeautifulSoup = None |
||||
|
|
||||
|
try: |
||||
|
from . pelican_mathjax_markdown_extension import PelicanMathJaxExtension |
||||
|
except ImportError as e: |
||||
|
PelicanMathJaxExtension = None |
||||
|
|
||||
|
try: |
||||
|
string_type = basestring |
||||
|
except NameError: |
||||
|
string_type = str |
||||
|
|
||||
|
|
||||
|
def process_settings(pelicanobj): |
||||
|
"""Sets user specified MathJax settings (see README for more details)""" |
||||
|
|
||||
|
mathjax_settings = {} |
||||
|
|
||||
|
# NOTE TO FUTURE DEVELOPERS: Look at the README and what is happening in |
||||
|
# this function if any additional changes to the mathjax settings need to |
||||
|
# be incorporated. Also, please inline comment what the variables |
||||
|
# will be used for |
||||
|
|
||||
|
# Default settings |
||||
|
mathjax_settings['auto_insert'] = True # if set to true, it will insert mathjax script automatically into content without needing to alter the template. |
||||
|
mathjax_settings['align'] = 'center' # controls alignment of of displayed equations (values can be: left, right, center) |
||||
|
mathjax_settings['indent'] = '0em' # if above is not set to 'center', then this setting acts as an indent |
||||
|
mathjax_settings['show_menu'] = 'true' # controls whether to attach mathjax contextual menu |
||||
|
mathjax_settings['process_escapes'] = 'true' # controls whether escapes are processed |
||||
|
mathjax_settings['latex_preview'] = 'TeX' # controls what user sees while waiting for LaTex to render |
||||
|
mathjax_settings['color'] = 'inherit' # controls color math is rendered in |
||||
|
mathjax_settings['linebreak_automatic'] = 'false' # Set to false by default for performance reasons (see http://docs.mathjax.org/en/latest/output.html#automatic-line-breaking) |
||||
|
mathjax_settings['tex_extensions'] = '' # latex extensions that can be embedded inside mathjax (see http://docs.mathjax.org/en/latest/tex.html#tex-and-latex-extensions) |
||||
|
mathjax_settings['responsive'] = 'false' # Tries to make displayed math responsive |
||||
|
mathjax_settings['responsive_break'] = '768' # The break point at which it math is responsively aligned (in pixels) |
||||
|
mathjax_settings['mathjax_font'] = 'default' # forces mathjax to use the specified font. |
||||
|
mathjax_settings['process_summary'] = BeautifulSoup is not None # will fix up summaries if math is cut off. Requires beautiful soup |
||||
|
mathjax_settings['message_style'] = 'normal' # This value controls the verbosity of the messages in the lower left-hand corner. Set it to "none" to eliminate all messages |
||||
|
mathjax_settings['font_list'] = ['STIX', 'TeX'] # Include in order of preference among TeX, STIX-Web, Asana-Math, Neo-Euler, Gyre-Pagella, Gyre-Termes and Latin-Modern |
||||
|
mathjax_settings['equation_numbering'] = 'none' # AMS, auto, none |
||||
|
|
||||
|
# Source for MathJax |
||||
|
mathjax_settings['source'] = "'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML'" |
||||
|
|
||||
|
# Get the user specified settings |
||||
|
try: |
||||
|
settings = pelicanobj.settings['MATH_JAX'] |
||||
|
except: |
||||
|
settings = None |
||||
|
|
||||
|
# If no settings have been specified, then return the defaults |
||||
|
if not isinstance(settings, dict): |
||||
|
return mathjax_settings |
||||
|
|
||||
|
# The following mathjax settings can be set via the settings dictionary |
||||
|
for key, value in ((key, settings[key]) for key in settings): |
||||
|
# Iterate over dictionary in a way that is compatible with both version 2 |
||||
|
# and 3 of python |
||||
|
|
||||
|
if key == 'align': |
||||
|
typeVal = isinstance(value, string_type) |
||||
|
|
||||
|
if not typeVal: |
||||
|
continue |
||||
|
|
||||
|
if value == 'left' or value == 'right' or value == 'center': |
||||
|
mathjax_settings[key] = value |
||||
|
else: |
||||
|
mathjax_settings[key] = 'center' |
||||
|
|
||||
|
if key == 'indent': |
||||
|
mathjax_settings[key] = value |
||||
|
|
||||
|
if key == 'source': |
||||
|
mathjax_settings[key] = value |
||||
|
|
||||
|
if key == 'show_menu' and isinstance(value, bool): |
||||
|
mathjax_settings[key] = 'true' if value else 'false' |
||||
|
|
||||
|
if key == 'message_style': |
||||
|
mathjax_settings[key] = value if value is not None else 'none' |
||||
|
|
||||
|
if key == 'auto_insert' and isinstance(value, bool): |
||||
|
mathjax_settings[key] = value |
||||
|
|
||||
|
if key == 'process_escapes' and isinstance(value, bool): |
||||
|
mathjax_settings[key] = 'true' if value else 'false' |
||||
|
|
||||
|
if key == 'latex_preview': |
||||
|
typeVal = isinstance(value, string_type) |
||||
|
|
||||
|
if not typeVal: |
||||
|
continue |
||||
|
|
||||
|
mathjax_settings[key] = value |
||||
|
|
||||
|
if key == 'color': |
||||
|
typeVal = isinstance(value, string_type) |
||||
|
|
||||
|
if not typeVal: |
||||
|
continue |
||||
|
|
||||
|
mathjax_settings[key] = value |
||||
|
|
||||
|
if key == 'linebreak_automatic' and isinstance(value, bool): |
||||
|
mathjax_settings[key] = 'true' if value else 'false' |
||||
|
|
||||
|
if key == 'process_summary' and isinstance(value, bool): |
||||
|
if value and BeautifulSoup is None: |
||||
|
print("BeautifulSoup4 is needed for summaries to be processed by render_math\nPlease install it") |
||||
|
value = False |
||||
|
|
||||
|
mathjax_settings[key] = value |
||||
|
|
||||
|
if key == 'responsive' and isinstance(value, bool): |
||||
|
mathjax_settings[key] = 'true' if value else 'false' |
||||
|
|
||||
|
if key == 'responsive_break' and isinstance(value, int): |
||||
|
mathjax_settings[key] = str(value) |
||||
|
|
||||
|
if key == 'tex_extensions' and isinstance(value, list): |
||||
|
# filter string values, then add '' to them |
||||
|
value = filter(lambda string: isinstance(string, string_type), value) |
||||
|
value = map(lambda string: "'%s'" % string, value) |
||||
|
mathjax_settings[key] = ',' + ','.join(value) |
||||
|
|
||||
|
if key == 'mathjax_font': |
||||
|
typeVal = isinstance(value, string_type) |
||||
|
|
||||
|
if not typeVal: |
||||
|
continue |
||||
|
|
||||
|
value = value.lower() |
||||
|
|
||||
|
if value == 'sanserif': |
||||
|
value = 'SansSerif' |
||||
|
elif value == 'fraktur': |
||||
|
value = 'Fraktur' |
||||
|
elif value == 'typewriter': |
||||
|
value = 'Typewriter' |
||||
|
else: |
||||
|
value = 'default' |
||||
|
|
||||
|
mathjax_settings[key] = value |
||||
|
|
||||
|
if key == 'font_list' and isinstance(value, list): |
||||
|
# make an array string from the list |
||||
|
value = filter(lambda string: isinstance(string, string_type), value) |
||||
|
value = map(lambda string: ",'%s'" % string, value) |
||||
|
mathjax_settings[key] = ''.join(value)[1:] |
||||
|
|
||||
|
if key == 'equation_numbering': |
||||
|
mathjax_settings[key] = value if value is not None else 'none' |
||||
|
|
||||
|
return mathjax_settings |
||||
|
|
||||
|
def process_summary(article): |
||||
|
"""Ensures summaries are not cut off. Also inserts |
||||
|
mathjax script so that math will be rendered""" |
||||
|
|
||||
|
summary = article.summary |
||||
|
summary_parsed = BeautifulSoup(summary, 'html.parser') |
||||
|
math = summary_parsed.find_all(class_='math') |
||||
|
|
||||
|
if len(math) > 0: |
||||
|
last_math_text = math[-1].get_text() |
||||
|
if len(last_math_text) > 3 and last_math_text[-3:] == '...': |
||||
|
content_parsed = BeautifulSoup(article._content, 'html.parser') |
||||
|
full_text = content_parsed.find_all(class_='math')[len(math)-1].get_text() |
||||
|
math[-1].string = "%s ..." % full_text |
||||
|
summary = summary_parsed.decode() |
||||
|
|
||||
|
# clear memoization cache |
||||
|
import functools |
||||
|
if isinstance(article.get_summary, functools.partial): |
||||
|
memoize_instance = article.get_summary.func.__self__ |
||||
|
memoize_instance.cache.clear() |
||||
|
|
||||
|
article._summary = "%s<script type='text/javascript'>%s</script>" % (summary, process_summary.mathjax_script) |
||||
|
|
||||
|
def configure_typogrify(pelicanobj, mathjax_settings): |
||||
|
"""Instructs Typogrify to ignore math tags - which allows Typogrify |
||||
|
to play nicely with math related content""" |
||||
|
|
||||
|
# If Typogrify is not being used, then just exit |
||||
|
if not pelicanobj.settings.get('TYPOGRIFY', False): |
||||
|
return |
||||
|
|
||||
|
try: |
||||
|
import typogrify |
||||
|
from distutils.version import LooseVersion |
||||
|
|
||||
|
if LooseVersion(typogrify.__version__) < LooseVersion('2.0.7'): |
||||
|
raise TypeError('Incorrect version of Typogrify') |
||||
|
|
||||
|
from typogrify.filters import typogrify |
||||
|
|
||||
|
# At this point, we are happy to use Typogrify, meaning |
||||
|
# it is installed and it is a recent enough version |
||||
|
# that can be used to ignore all math |
||||
|
# Instantiate markdown extension and append it to the current extensions |
||||
|
pelicanobj.settings['TYPOGRIFY_IGNORE_TAGS'].extend(['.math', 'script']) # ignore math class and script |
||||
|
|
||||
|
except (ImportError, TypeError) as e: |
||||
|
pelicanobj.settings['TYPOGRIFY'] = False # disable Typogrify |
||||
|
|
||||
|
if isinstance(e, ImportError): |
||||
|
print("\nTypogrify is not installed, so it is being ignored.\nIf you want to use it, please install via: pip install typogrify\n") |
||||
|
|
||||
|
if isinstance(e, TypeError): |
||||
|
print("\nA more recent version of Typogrify is needed for the render_math module.\nPlease upgrade Typogrify to the latest version (anything equal or above version 2.0.7 is okay).\nTypogrify will be turned off due to this reason.\n") |
||||
|
|
||||
|
def process_mathjax_script(mathjax_settings): |
||||
|
"""Load the mathjax script template from file, and render with the settings""" |
||||
|
|
||||
|
# Read the mathjax javascript template from file |
||||
|
with open (os.path.dirname(os.path.realpath(__file__)) |
||||
|
+ '/mathjax_script_template', 'r') as mathjax_script_template: |
||||
|
mathjax_template = mathjax_script_template.read() |
||||
|
|
||||
|
return mathjax_template.format(**mathjax_settings) |
||||
|
|
||||
|
def mathjax_for_markdown(pelicanobj, mathjax_script, mathjax_settings): |
||||
|
"""Instantiates a customized markdown extension for handling mathjax |
||||
|
related content""" |
||||
|
|
||||
|
# Create the configuration for the markdown template |
||||
|
config = {} |
||||
|
config['mathjax_script'] = mathjax_script |
||||
|
config['math_tag_class'] = 'math' |
||||
|
config['auto_insert'] = mathjax_settings['auto_insert'] |
||||
|
|
||||
|
# Instantiate markdown extension and append it to the current extensions |
||||
|
try: |
||||
|
if isinstance(pelicanobj.settings.get('MD_EXTENSIONS'), list): # pelican 3.6.3 and earlier |
||||
|
pelicanobj.settings['MD_EXTENSIONS'].append(PelicanMathJaxExtension(config)) |
||||
|
else: |
||||
|
pelicanobj.settings['MARKDOWN'].setdefault('extensions', []).append(PelicanMathJaxExtension(config)) |
||||
|
except: |
||||
|
sys.excepthook(*sys.exc_info()) |
||||
|
sys.stderr.write("\nError - the pelican mathjax markdown extension failed to configure. MathJax is non-functional.\n") |
||||
|
sys.stderr.flush() |
||||
|
|
||||
|
def mathjax_for_rst(pelicanobj, mathjax_script, mathjax_settings): |
||||
|
"""Setup math for RST""" |
||||
|
docutils_settings = pelicanobj.settings.get('DOCUTILS_SETTINGS', {}) |
||||
|
docutils_settings.setdefault('math_output', 'MathJax %s' % mathjax_settings['source']) |
||||
|
pelicanobj.settings['DOCUTILS_SETTINGS'] = docutils_settings |
||||
|
rst_add_mathjax.mathjax_script = mathjax_script |
||||
|
|
||||
|
def pelican_init(pelicanobj): |
||||
|
""" |
||||
|
Loads the mathjax script according to the settings. |
||||
|
Instantiate the Python markdown extension, passing in the mathjax |
||||
|
script as config parameter. |
||||
|
""" |
||||
|
|
||||
|
# Process settings, and set global var |
||||
|
mathjax_settings = process_settings(pelicanobj) |
||||
|
|
||||
|
# Generate mathjax script |
||||
|
mathjax_script = process_mathjax_script(mathjax_settings) |
||||
|
|
||||
|
# Configure Typogrify |
||||
|
configure_typogrify(pelicanobj, mathjax_settings) |
||||
|
|
||||
|
# Configure Mathjax For Markdown |
||||
|
if PelicanMathJaxExtension: |
||||
|
mathjax_for_markdown(pelicanobj, mathjax_script, mathjax_settings) |
||||
|
|
||||
|
# Configure Mathjax For RST |
||||
|
mathjax_for_rst(pelicanobj, mathjax_script, mathjax_settings) |
||||
|
|
||||
|
# Set process_summary's mathjax_script variable |
||||
|
process_summary.mathjax_script = None |
||||
|
if mathjax_settings['process_summary']: |
||||
|
process_summary.mathjax_script = mathjax_script |
||||
|
|
||||
|
def rst_add_mathjax(content): |
||||
|
"""Adds mathjax script for reStructuredText""" |
||||
|
|
||||
|
# .rst is the only valid extension for reStructuredText files |
||||
|
_, ext = os.path.splitext(os.path.basename(content.source_path)) |
||||
|
if ext != '.rst': |
||||
|
return |
||||
|
|
||||
|
# If math class is present in text, add the javascript |
||||
|
# note that RST hardwires mathjax to be class "math" |
||||
|
if 'class="math"' in content._content: |
||||
|
content._content += "<script type='text/javascript'>%s</script>" % rst_add_mathjax.mathjax_script |
||||
|
|
||||
|
def process_rst_and_summaries(content_generators): |
||||
|
""" |
||||
|
Ensure mathjax script is applied to RST and summaries are |
||||
|
corrected if specified in user settings. |
||||
|
|
||||
|
Handles content attached to ArticleGenerator and PageGenerator objects, |
||||
|
since the plugin doesn't know how to handle other Generator types. |
||||
|
|
||||
|
For reStructuredText content, examine both articles and pages. |
||||
|
If article or page is reStructuredText and there is math present, |
||||
|
append the mathjax script. |
||||
|
|
||||
|
Also process summaries if present (only applies to articles) |
||||
|
and user wants summaries processed (via user settings) |
||||
|
""" |
||||
|
|
||||
|
for generator in content_generators: |
||||
|
if isinstance(generator, generators.ArticlesGenerator): |
||||
|
for article in ( |
||||
|
generator.articles + |
||||
|
generator.translations + |
||||
|
generator.drafts): |
||||
|
rst_add_mathjax(article) |
||||
|
#optionally fix truncated formulae in summaries. |
||||
|
if process_summary.mathjax_script is not None: |
||||
|
process_summary(article) |
||||
|
elif isinstance(generator, generators.PagesGenerator): |
||||
|
for page in generator.pages: |
||||
|
rst_add_mathjax(page) |
||||
|
for page in generator.hidden_pages: |
||||
|
rst_add_mathjax(page) |
||||
|
|
||||
|
def register(): |
||||
|
"""Plugin registration""" |
||||
|
signals.initialized.connect(pelican_init) |
||||
|
signals.all_generators_finalized.connect(process_rst_and_summaries) |
@ -0,0 +1,61 @@ |
|||||
|
if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {{ |
||||
|
var align = "{align}", |
||||
|
indent = "{indent}", |
||||
|
linebreak = "{linebreak_automatic}"; |
||||
|
|
||||
|
if ({responsive}) {{ |
||||
|
align = (screen.width < {responsive_break}) ? "left" : align; |
||||
|
indent = (screen.width < {responsive_break}) ? "0em" : indent; |
||||
|
linebreak = (screen.width < {responsive_break}) ? 'true' : linebreak; |
||||
|
}} |
||||
|
|
||||
|
var mathjaxscript = document.createElement('script'); |
||||
|
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#'; |
||||
|
mathjaxscript.type = 'text/javascript'; |
||||
|
mathjaxscript.src = {source}; |
||||
|
|
||||
|
var configscript = document.createElement('script'); |
||||
|
configscript.type = 'text/x-mathjax-config'; |
||||
|
configscript[(window.opera ? "innerHTML" : "text")] = |
||||
|
"MathJax.Hub.Config({{" + |
||||
|
" config: ['MMLorHTML.js']," + |
||||
|
" TeX: {{ extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'{tex_extensions}], equationNumbers: {{ autoNumber: '{equation_numbering}' }} }}," + |
||||
|
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," + |
||||
|
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," + |
||||
|
" displayAlign: '"+ align +"'," + |
||||
|
" displayIndent: '"+ indent +"'," + |
||||
|
" showMathMenu: {show_menu}," + |
||||
|
" messageStyle: '{message_style}'," + |
||||
|
" tex2jax: {{ " + |
||||
|
" inlineMath: [ ['\\\\(','\\\\)'] ], " + |
||||
|
" displayMath: [ ['$$','$$'] ]," + |
||||
|
" processEscapes: {process_escapes}," + |
||||
|
" preview: '{latex_preview}'," + |
||||
|
" }}, " + |
||||
|
" 'HTML-CSS': {{ " + |
||||
|
" availableFonts: {font_list}," + |
||||
|
" preferredFont: 'STIX'," + |
||||
|
" styles: {{ '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {{color: '{color} ! important'}} }}," + |
||||
|
" linebreaks: {{ automatic: "+ linebreak +", width: '90% container' }}," + |
||||
|
" }}, " + |
||||
|
"}}); " + |
||||
|
"if ('{mathjax_font}' !== 'default') {{" + |
||||
|
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {{" + |
||||
|
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" + |
||||
|
"VARIANT['normal'].fonts.unshift('MathJax_{mathjax_font}');" + |
||||
|
"VARIANT['bold'].fonts.unshift('MathJax_{mathjax_font}-bold');" + |
||||
|
"VARIANT['italic'].fonts.unshift('MathJax_{mathjax_font}-italic');" + |
||||
|
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_{mathjax_font}-italic');" + |
||||
|
"}});" + |
||||
|
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {{" + |
||||
|
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" + |
||||
|
"VARIANT['normal'].fonts.unshift('MathJax_{mathjax_font}');" + |
||||
|
"VARIANT['bold'].fonts.unshift('MathJax_{mathjax_font}-bold');" + |
||||
|
"VARIANT['italic'].fonts.unshift('MathJax_{mathjax_font}-italic');" + |
||||
|
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_{mathjax_font}-italic');" + |
||||
|
"}});" + |
||||
|
"}}"; |
||||
|
|
||||
|
(document.body || document.getElementsByTagName('head')[0]).appendChild(configscript); |
||||
|
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript); |
||||
|
}} |
@ -0,0 +1,158 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
""" |
||||
|
Pelican Mathjax Markdown Extension |
||||
|
================================== |
||||
|
An extension for the Python Markdown module that enables |
||||
|
the Pelican python blog to process mathjax. This extension |
||||
|
gives Pelican the ability to use Mathjax as a "first class |
||||
|
citizen" of the blog |
||||
|
""" |
||||
|
|
||||
|
import markdown |
||||
|
|
||||
|
from markdown.util import etree |
||||
|
from markdown.util import AtomicString |
||||
|
|
||||
|
class PelicanMathJaxPattern(markdown.inlinepatterns.Pattern): |
||||
|
"""Inline markdown processing that matches mathjax""" |
||||
|
|
||||
|
def __init__(self, pelican_mathjax_extension, tag, pattern): |
||||
|
super(PelicanMathJaxPattern,self).__init__(pattern) |
||||
|
self.math_tag_class = pelican_mathjax_extension.getConfig('math_tag_class') |
||||
|
self.pelican_mathjax_extension = pelican_mathjax_extension |
||||
|
self.tag = tag |
||||
|
|
||||
|
def handleMatch(self, m): |
||||
|
node = markdown.util.etree.Element(self.tag) |
||||
|
node.set('class', self.math_tag_class) |
||||
|
|
||||
|
prefix = '\\(' if m.group('prefix') == '$' else m.group('prefix') |
||||
|
suffix = '\\)' if m.group('suffix') == '$' else m.group('suffix') |
||||
|
node.text = markdown.util.AtomicString(prefix + m.group('math') + suffix) |
||||
|
|
||||
|
# If mathjax was successfully matched, then JavaScript needs to be added |
||||
|
# for rendering. The boolean below indicates this |
||||
|
self.pelican_mathjax_extension.mathjax_needed = True |
||||
|
return node |
||||
|
|
||||
|
class PelicanMathJaxCorrectDisplayMath(markdown.treeprocessors.Treeprocessor): |
||||
|
"""Corrects invalid html that results from a <div> being put inside |
||||
|
a <p> for displayed math""" |
||||
|
|
||||
|
def __init__(self, pelican_mathjax_extension): |
||||
|
self.pelican_mathjax_extension = pelican_mathjax_extension |
||||
|
|
||||
|
def correct_html(self, root, children, div_math, insert_idx, text): |
||||
|
"""Separates out <div class="math"> from the parent tag <p>. Anything |
||||
|
in between is put into its own parent tag of <p>""" |
||||
|
|
||||
|
current_idx = 0 |
||||
|
|
||||
|
for idx in div_math: |
||||
|
el = markdown.util.etree.Element('p') |
||||
|
el.text = text |
||||
|
el.extend(children[current_idx:idx]) |
||||
|
|
||||
|
# Test to ensure that empty <p> is not inserted |
||||
|
if len(el) != 0 or (el.text and not el.text.isspace()): |
||||
|
root.insert(insert_idx, el) |
||||
|
insert_idx += 1 |
||||
|
|
||||
|
text = children[idx].tail |
||||
|
children[idx].tail = None |
||||
|
root.insert(insert_idx, children[idx]) |
||||
|
insert_idx += 1 |
||||
|
current_idx = idx+1 |
||||
|
|
||||
|
el = markdown.util.etree.Element('p') |
||||
|
el.text = text |
||||
|
el.extend(children[current_idx:]) |
||||
|
|
||||
|
if len(el) != 0 or (el.text and not el.text.isspace()): |
||||
|
root.insert(insert_idx, el) |
||||
|
|
||||
|
def run(self, root): |
||||
|
"""Searches for <div class="math"> that are children in <p> tags and corrects |
||||
|
the invalid HTML that results""" |
||||
|
|
||||
|
math_tag_class = self.pelican_mathjax_extension.getConfig('math_tag_class') |
||||
|
|
||||
|
for parent in root: |
||||
|
div_math = [] |
||||
|
children = list(parent) |
||||
|
|
||||
|
for div in parent.findall('div'): |
||||
|
if div.get('class') == math_tag_class: |
||||
|
div_math.append(children.index(div)) |
||||
|
|
||||
|
# Do not process further if no displayed math has been found |
||||
|
if not div_math: |
||||
|
continue |
||||
|
|
||||
|
insert_idx = list(root).index(parent) |
||||
|
self.correct_html(root, children, div_math, insert_idx, parent.text) |
||||
|
root.remove(parent) # Parent must be removed last for correct insertion index |
||||
|
|
||||
|
return root |
||||
|
|
||||
|
class PelicanMathJaxAddJavaScript(markdown.treeprocessors.Treeprocessor): |
||||
|
"""Tree Processor for adding Mathjax JavaScript to the blog""" |
||||
|
|
||||
|
def __init__(self, pelican_mathjax_extension): |
||||
|
self.pelican_mathjax_extension = pelican_mathjax_extension |
||||
|
|
||||
|
def run(self, root): |
||||
|
# If no mathjax was present, then exit |
||||
|
if (not self.pelican_mathjax_extension.mathjax_needed): |
||||
|
return root |
||||
|
|
||||
|
# Add the mathjax script to the html document |
||||
|
mathjax_script = etree.Element('script') |
||||
|
mathjax_script.set('type','text/javascript') |
||||
|
mathjax_script.text = AtomicString(self.pelican_mathjax_extension.getConfig('mathjax_script')) |
||||
|
root.append(mathjax_script) |
||||
|
|
||||
|
# Reset the boolean switch to false so that script is only added |
||||
|
# to other pages if needed |
||||
|
self.pelican_mathjax_extension.mathjax_needed = False |
||||
|
return root |
||||
|
|
||||
|
class PelicanMathJaxExtension(markdown.Extension): |
||||
|
"""A markdown extension enabling mathjax processing in Markdown for Pelican""" |
||||
|
def __init__(self, config): |
||||
|
|
||||
|
try: |
||||
|
# Needed for markdown versions >= 2.5 |
||||
|
self.config['mathjax_script'] = ['', 'Mathjax JavaScript script'] |
||||
|
self.config['math_tag_class'] = ['math', 'The class of the tag in which mathematics is wrapped'] |
||||
|
self.config['auto_insert'] = [True, 'Determines if mathjax script is automatically inserted into content'] |
||||
|
super(PelicanMathJaxExtension,self).__init__(**config) |
||||
|
except AttributeError: |
||||
|
# Markdown versions < 2.5 |
||||
|
config['mathjax_script'] = [config['mathjax_script'], 'Mathjax JavaScript script'] |
||||
|
config['math_tag_class'] = [config['math_tag_class'], 'The class of the tag in which mathematic is wrapped'] |
||||
|
config['auto_insert'] = [config['auto_insert'], 'Determines if mathjax script is automatically inserted into content'] |
||||
|
super(PelicanMathJaxExtension,self).__init__(config) |
||||
|
|
||||
|
# Used as a flag to determine if javascript |
||||
|
# needs to be injected into a document |
||||
|
self.mathjax_needed = False |
||||
|
|
||||
|
def extendMarkdown(self, md, md_globals): |
||||
|
# Regex to detect mathjax |
||||
|
mathjax_inline_regex = r'(?P<prefix>\$)(?P<math>.+?)(?P<suffix>(?<!\s)\2)' |
||||
|
mathjax_display_regex = r'(?P<prefix>\$\$|\\begin\{(.+?)\})(?P<math>.+?)(?P<suffix>\2|\\end\{\3\})' |
||||
|
|
||||
|
# Process mathjax before escapes are processed since escape processing will |
||||
|
# intefer with mathjax. The order in which the displayed and inlined math |
||||
|
# is registered below matters |
||||
|
md.inlinePatterns.add('mathjax_displayed', PelicanMathJaxPattern(self, 'div', mathjax_display_regex), '<escape') |
||||
|
md.inlinePatterns.add('mathjax_inlined', PelicanMathJaxPattern(self, 'span', mathjax_inline_regex), '<escape') |
||||
|
|
||||
|
# Correct the invalid HTML that results from teh displayed math (<div> tag within a <p> tag) |
||||
|
md.treeprocessors.add('mathjax_correctdisplayedmath', PelicanMathJaxCorrectDisplayMath(self), '>inline') |
||||
|
|
||||
|
# If necessary, add the JavaScript Mathjax library to the document. This must |
||||
|
# be last in the ordered dict (hence it is given the position '_end') |
||||
|
if self.getConfig('auto_insert'): |
||||
|
md.treeprocessors.add('mathjax_addjavascript', PelicanMathJaxAddJavaScript(self), '_end') |
Write
Preview
Loading…
Cancel
Save
Reference in new issue