diff options
author | Sébastien Dailly <sebastien@chimrod.com> | 2021-01-04 13:40:21 +0100 |
---|---|---|
committer | Sébastien Dailly <sebastien@chimrod.com> | 2021-01-05 11:06:20 +0100 |
commit | 1cad4c9044d47c6653d804d7fb58581d92f96cc8 (patch) | |
tree | 9696b75a4288976a00dbfc694090768ac93cdd14 /plugins/render_math | |
parent | 0c09a00a0b298cbd3bbd0082cc1026e22db9b1c5 (diff) |
Change organisation
Diffstat (limited to 'plugins/render_math')
-rwxr-xr-x | plugins/render_math/Readme.md | 167 | ||||
-rw-r--r--[-rwxr-xr-x] | plugins/render_math/__init__.py | 2 | ||||
-rw-r--r--[-rwxr-xr-x] | plugins/render_math/math.py | 252 | ||||
-rw-r--r--[-rwxr-xr-x] | plugins/render_math/mathjax_script_template | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | plugins/render_math/pelican_mathjax_markdown_extension.py | 120 | ||||
-rwxr-xr-x | plugins/render_math/requirements.txt | 1 | ||||
-rw-r--r--[-rwxr-xr-x] | plugins/render_math/test_data/article_with_math_formulas.rst | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | plugins/render_math/test_render_math.py | 28 | ||||
-rw-r--r-- | plugins/render_math/version.py | 3 |
9 files changed, 257 insertions, 316 deletions
diff --git a/plugins/render_math/Readme.md b/plugins/render_math/Readme.md deleted file mode 100755 index 7d541aa..0000000 --- a/plugins/render_math/Readme.md +++ /dev/null @@ -1,167 +0,0 @@ -Math Render Plugin For Pelican -============================== - -**NOTE: [This plugin has been moved to its own repository](https://github.com/pelican-plugins/render-math). Please file any issues/PRs there. Once all plugins have been migrated to the [new Pelican Plugins organization](https://github.com/pelican-plugins), this monolithic repository will be archived.** - -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 λ) -``` diff --git a/plugins/render_math/__init__.py b/plugins/render_math/__init__.py index 2ac15dd..513bf02 100755..100644 --- a/plugins/render_math/__init__.py +++ b/plugins/render_math/__init__.py @@ -1 +1 @@ -from .math import * +from .math import * # NOQA diff --git a/plugins/render_math/math.py b/plugins/render_math/math.py index 165d59e..a189ba2 100755..100644 --- a/plugins/render_math/math.py +++ b/plugins/render_math/math.py @@ -19,7 +19,7 @@ into the HTML. Typogrify Compatibility ----------------------- This plugin now plays nicely with Typogrify, but it -requires Typogrify version 2.07 or above. +requires Typogrify version 2.0.7 or above. User Settings ------------- @@ -33,16 +33,16 @@ the math. See README for more details. import os import sys -from pelican import signals, generators +from pelican import generators, signals try: from bs4 import BeautifulSoup -except ImportError as e: +except ImportError: BeautifulSoup = None try: - from . pelican_mathjax_markdown_extension import PelicanMathJaxExtension -except ImportError as e: + from .pelican_mathjax_markdown_extension import PelicanMathJaxExtension +except ImportError: PelicanMathJaxExtension = None try: @@ -51,7 +51,7 @@ except NameError: string_type = str -def process_settings(pelicanobj): +def process_settings(pelicanobj): # NOQA: C901 """Sets user specified MathJax settings (see README for more details)""" mathjax_settings = {} @@ -62,30 +62,59 @@ def process_settings(pelicanobj): # 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 + 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'" + 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: + if "MATH_JAX" in pelicanobj.settings: + settings = pelicanobj.settings["MATH_JAX"] + else: settings = None # If no settings have been specified, then return the defaults @@ -97,36 +126,36 @@ def process_settings(pelicanobj): # Iterate over dictionary in a way that is compatible with both version 2 # and 3 of python - if key == 'align': + if key == "align": typeVal = isinstance(value, string_type) if not typeVal: continue - if value == 'left' or value == 'right' or value == 'center': + if value == "left" or value == "right" or value == "center": mathjax_settings[key] = value else: - mathjax_settings[key] = 'center' + mathjax_settings[key] = "center" - if key == 'indent': + if key == "indent": mathjax_settings[key] = value - if key == 'source': + 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 == "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 == "message_style": + mathjax_settings[key] = value if value is not None else "none" - if key == 'auto_insert' and isinstance(value, bool): + 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 == "process_escapes" and isinstance(value, bool): + mathjax_settings[key] = "true" if value else "false" - if key == 'latex_preview': + if key == "latex_preview": typeVal = isinstance(value, string_type) if not typeVal: @@ -134,7 +163,7 @@ def process_settings(pelicanobj): mathjax_settings[key] = value - if key == 'color': + if key == "color": typeVal = isinstance(value, string_type) if not typeVal: @@ -142,29 +171,31 @@ def process_settings(pelicanobj): mathjax_settings[key] = value - if key == 'linebreak_automatic' and isinstance(value, bool): - mathjax_settings[key] = 'true' if value else 'false' + 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 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") + 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" and isinstance(value, bool): + mathjax_settings[key] = "true" if value else "false" - if key == 'responsive_break' and isinstance(value, int): + if key == "responsive_break" and isinstance(value, int): mathjax_settings[key] = str(value) - if key == 'tex_extensions' and isinstance(value, list): + 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) + mathjax_settings[key] = "," + ",".join(value) - if key == 'mathjax_font': + if key == "mathjax_font": typeVal = isinstance(value, string_type) if not typeVal: @@ -172,66 +203,73 @@ def process_settings(pelicanobj): value = value.lower() - if value == 'sanserif': - value = 'SansSerif' - elif value == 'fraktur': - value = 'Fraktur' - elif value == 'typewriter': - value = 'Typewriter' + if value == "sanserif": + value = "SansSerif" + elif value == "fraktur": + value = "Fraktur" + elif value == "typewriter": + value = "Typewriter" else: - value = 'default' + value = "default" mathjax_settings[key] = value - if key == 'font_list' and isinstance(value, list): + 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:] + mathjax_settings[key] = "".join(value)[1:] - if key == 'equation_numbering': - mathjax_settings[key] = value if value is not None else 'none' + 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') + 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() + 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) + article.metadata["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): + 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') + import typogrify + + if LooseVersion(typogrify.__version__) < LooseVersion("2.0.7"): + raise TypeError("Incorrect version of Typogrify") from typogrify.filters import typogrify @@ -239,55 +277,74 @@ def configure_typogrify(pelicanobj, mathjax_settings): # 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 + pelicanobj.settings["TYPOGRIFY_IGNORE_TAGS"].extend( + [".math", "script"] + ) # ignore math class and script except (ImportError, TypeError) as e: - pelicanobj.settings['TYPOGRIFY'] = False # disable Typogrify + 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") + 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") + 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: + 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'] + 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)) + 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: + pelicanobj.settings["MARKDOWN"].setdefault("extensions", []).append( + PelicanMathJaxExtension(config) + ) + except: # NOQA E722 sys.excepthook(*sys.exc_info()) - sys.stderr.write("\nError - the pelican mathjax markdown extension failed to configure. MathJax is non-functional.\n") + 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 + 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. @@ -313,21 +370,26 @@ def pelican_init(pelicanobj): # Set process_summary's mathjax_script variable process_summary.mathjax_script = None - if mathjax_settings['process_summary']: + 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': + 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 + content._content += ( + "<script type='text/javascript'>%s</script>" + % rst_add_mathjax.mathjax_script + ) + def process_rst_and_summaries(content_generators): """ @@ -348,11 +410,10 @@ def process_rst_and_summaries(content_generators): for generator in content_generators: if isinstance(generator, generators.ArticlesGenerator): for article in ( - generator.articles + - generator.translations + - generator.drafts): + generator.articles + generator.translations + generator.drafts + ): rst_add_mathjax(article) - #optionally fix truncated formulae in summaries. + # optionally fix truncated formulae in summaries. if process_summary.mathjax_script is not None: process_summary(article) elif isinstance(generator, generators.PagesGenerator): @@ -361,6 +422,7 @@ def process_rst_and_summaries(content_generators): for page in generator.hidden_pages: rst_add_mathjax(page) + def register(): """Plugin registration""" signals.initialized.connect(pelican_init) diff --git a/plugins/render_math/mathjax_script_template b/plugins/render_math/mathjax_script_template index db8aeba..db8aeba 100755..100644 --- a/plugins/render_math/mathjax_script_template +++ b/plugins/render_math/mathjax_script_template diff --git a/plugins/render_math/pelican_mathjax_markdown_extension.py b/plugins/render_math/pelican_mathjax_markdown_extension.py index e739363..77e0593 100755..100644 --- a/plugins/render_math/pelican_mathjax_markdown_extension.py +++ b/plugins/render_math/pelican_mathjax_markdown_extension.py @@ -8,33 +8,35 @@ gives Pelican the ability to use Mathjax as a "first class citizen" of the blog """ -import markdown +from xml.etree.ElementTree import Element -from markdown.util import etree +import markdown 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') + 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) + node = 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) + 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""" @@ -49,22 +51,22 @@ class PelicanMathJaxCorrectDisplayMath(markdown.treeprocessors.Treeprocessor): current_idx = 0 for idx in div_math: - el = markdown.util.etree.Element('p') + el = Element("p") el.text = text el.extend(children[current_idx:idx]) - # Test to ensure that empty <p> is not inserted + # 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 + 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 + current_idx = idx + 1 - el = markdown.util.etree.Element('p') + el = Element("p") el.text = text el.extend(children[current_idx:]) @@ -75,14 +77,14 @@ class PelicanMathJaxCorrectDisplayMath(markdown.treeprocessors.Treeprocessor): """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') + 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: + 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 @@ -90,11 +92,14 @@ class PelicanMathJaxCorrectDisplayMath(markdown.treeprocessors.Treeprocessor): 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 + 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""" @@ -103,13 +108,15 @@ class PelicanMathJaxAddJavaScript(markdown.treeprocessors.Treeprocessor): def run(self, root): # If no mathjax was present, then exit - if (not self.pelican_mathjax_extension.mathjax_needed): + 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')) + mathjax_script = 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 @@ -117,22 +124,39 @@ class PelicanMathJaxAddJavaScript(markdown.treeprocessors.Treeprocessor): 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) + 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) + 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 @@ -140,19 +164,33 @@ class PelicanMathJaxExtension(markdown.Extension): def extendMarkdown(self, md): # 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\})' + 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: we should have higher priority than 'escape' which has 180 - md.inlinePatterns.register(PelicanMathJaxPattern(self, 'div', mathjax_display_regex), 'mathjax_displayed', 186) - md.inlinePatterns.register(PelicanMathJaxPattern(self, 'span', mathjax_inline_regex), 'mathjax_inlined', 185) - - # Correct the invalid HTML that results from teh displayed math (<div> tag within a <p> tag) - md.treeprocessors.register(PelicanMathJaxCorrectDisplayMath(self), 'mathjax_correctdisplayedmath', 15) + md.inlinePatterns.register( + PelicanMathJaxPattern(self, "div", mathjax_display_regex), + "mathjax_displayed", + 186, + ) + md.inlinePatterns.register( + PelicanMathJaxPattern(self, "span", mathjax_inline_regex), + "mathjax_inlined", + 185, + ) + + # Correct the invalid HTML that results from teh displayed math (<div> tag within a <p> tag) + md.treeprocessors.register( + PelicanMathJaxCorrectDisplayMath(self), "mathjax_correctdisplayedmath", 15 + ) # 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.register(PelicanMathJaxAddJavaScript(self), 'mathjax_addjavascript', 0) + if self.getConfig("auto_insert"): + md.treeprocessors.register( + PelicanMathJaxAddJavaScript(self), "mathjax_addjavascript", 0 + ) diff --git a/plugins/render_math/requirements.txt b/plugins/render_math/requirements.txt deleted file mode 100755 index be64ec9..0000000 --- a/plugins/render_math/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -typogrify diff --git a/plugins/render_math/test_data/article_with_math_formulas.rst b/plugins/render_math/test_data/article_with_math_formulas.rst index 87dcc45..87dcc45 100755..100644 --- a/plugins/render_math/test_data/article_with_math_formulas.rst +++ b/plugins/render_math/test_data/article_with_math_formulas.rst diff --git a/plugins/render_math/test_render_math.py b/plugins/render_math/test_render_math.py index b71f4e7..dc1e5fd 100755..100644 --- a/plugins/render_math/test_render_math.py +++ b/plugins/render_math/test_render_math.py @@ -10,22 +10,22 @@ from pelican.writers import Writer from .math import pelican_init, process_rst_and_summaries - CUR_DIR = dirname(__file__) class RenderMathTest(unittest.TestCase): def test_ok_on_shared_test_data(self): settings = get_settings(filenames={}) - settings['PATH'] = join(CUR_DIR, '..', 'test_data') + settings["PATH"] = join(CUR_DIR, "..", "test_data") pelican_init(PelicanMock(settings)) with TemporaryDirectory() as tmpdirname: generator = _build_article_generator(settings, tmpdirname) process_rst_and_summaries([generator]) + def test_ok_on_custom_data(self): settings = get_settings(filenames={}) - settings['PATH'] = join(CUR_DIR, 'test_data') - settings['PLUGINS'] = ['pelican-ipynb.markup'] # to also parse .ipynb files + settings["PATH"] = join(CUR_DIR, "test_data") + settings["PLUGINS"] = ["pelican-ipynb.markup"] # to also parse .ipynb files configure_settings(settings) pelican_mock = PelicanMock(settings) pelican_init(pelican_mock) @@ -34,23 +34,29 @@ class RenderMathTest(unittest.TestCase): generator = _build_article_generator(settings, tmpdirname) process_rst_and_summaries([generator]) for article in generator.articles: - if article.source_path.endswith('.rst'): - self.assertIn('mathjaxscript_pelican', article.content) + if article.source_path.endswith(".rst"): + self.assertIn("mathjaxscript_pelican", article.content) generator.generate_output(Writer(tmpdirname, settings=settings)) def _build_article_generator(settings, output_path): context = settings.copy() - context['generated_content'] = dict() - context['static_links'] = set() + context["generated_content"] = dict() + context["static_links"] = set() article_generator = ArticlesGenerator( - context=context, settings=settings, - path=settings['PATH'], theme=settings['THEME'], output_path=output_path) + context=context, + settings=settings, + path=settings["PATH"], + theme=settings["THEME"], + output_path=output_path, + ) article_generator.generate_context() return article_generator + class PelicanMock: - 'A dummy class exposing the only attributes needed' + "A dummy class exposing the only attributes needed" + def __init__(self, settings): self.plugins = [] self.settings = settings diff --git a/plugins/render_math/version.py b/plugins/render_math/version.py new file mode 100644 index 0000000..0c86483 --- /dev/null +++ b/plugins/render_math/version.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python + +__version__ = "1.0.3" |