From 2ad63c8253974d72d0e1c2f9f1520731760347e1 Mon Sep 17 00:00:00 2001 From: Davide Olianas Date: Sun, 17 Jan 2016 21:45:50 +0100 Subject: [PATCH] Allow loading LaTeX macros from text files --- Readme.md | 16 +++++++-- math.py | 76 +++++++++++++++++++++++++++++++++++++++++ mathjax_script_template | 2 +- 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/Readme.md b/Readme.md index bbe9020..490bb95 100644 --- a/Readme.md +++ b/Readme.md @@ -52,6 +52,14 @@ 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. +### Load custom LaTeX macros + +If you use the same macros over and over, it's a good idea to not repeat yourself defining them in multiple Markdown or reStructuredText documents. What you can do instead is tell the plugin absolute paths for text files containing macro definitions. + +If the same macro name has multiple definitions, the last one is used and a warning is printed to stdout. + +See below in the Usage section for examples. + Usage ----- ### Templates @@ -99,11 +107,15 @@ Requires [BeautifulSoup4](http://www.crummy.com/software/BeautifulSoup/bs4/doc/) **Default Value**: `False` * `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 +* `macros`: [list] each element of the list is a [string] containing the absolute path to a file with macro definitions. +**Default Value**: `[]` #### Settings Examples -Make math render in blue and displaymath align to the left: +Make math render in blue, displaymath align to the left and load macros from `/home/user/latex-macros.tex`: + + macros = ['/home/user/latex-macros.tex'] + MATH_JAX = {'color': 'blue', 'align': 'left', 'macros': macros} - 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: diff --git a/math.py b/math.py index f1c492e..f532af1 100644 --- a/math.py +++ b/math.py @@ -71,6 +71,7 @@ def process_settings(pelicanobj): mathjax_settings['process_summary'] = BeautifulSoup is not None # will fix up summaries if math is cut off. Requires beautiful soup mathjax_settings['force_tls'] = 'false' # will force mathjax to be served by https - if set as False, it will only use https if site is served using https 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['macros'] = '{}' # Source for MathJax mathjax_settings['source'] = "'//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'" @@ -192,8 +193,83 @@ def process_settings(pelicanobj): mathjax_settings[key] = value + if key == 'macros': + text_lines = [] + macros = parse_tex_macro(*value) + for macro in macros: + if 'args' in macro.keys(): + # number of arguments > 1 + text_lines.append("{0}: ['{1}', {2}]".format(macro['name'], macro['definition'], macro['args'])) + else: + text_lines.append("{0}: '{1}'".format(macro['name'], macro['definition'])) + mathjax_settings[key] = '{' + ", ".join(text_lines) + '}' + + return mathjax_settings +def parse_tex_macro(*args): + """Returns a list of from input files. + + Each argument has to be an absolute path to a file. TeX macro definitions + are read and each one is translated to a dictionary containing the name + without backslash and the definition; if arguments are present, their + number is added too. + + If a macro is defined multiple times, a warning is printed to stdout. + The last definition is used. + + Backslashes in the definition are added in order to ensure the proper + form in the final html page. + + Example: + > [{'name': 'pd', + 'definition': '\\\\\\\\frac{\\\\\\\\partial #1}{\\\\\\\\partial #2}', + 'args': 2}, + {'name': 'R', 'definition': '\\\\\\\\mathbb{R}'}] + """ + temp_macros = [] + for arg in args: + with open(arg, 'rt') as input_file: + lines = input_file.read().splitlines() + for i, command in enumerate(lines): + splitted = command.split('{') + name_number = splitted[1].split('}') + name = name_number[0].strip('\\') + # for the definition, remove the last character from the last string which is } + # remember that strings are immutable objects in python + last_def_token = splitted[-1][:-1] + splitted_def = splitted[2:-1] + [last_def_token] + complete_def = '{'.join(splitted_def).replace('\\','\\\\\\\\') + final_command = {'line': i+1, 'file': arg, 'name': name, 'definition': complete_def} + if name_number[1]: + # the number of arguments is defined, therefore name_number[1] is not null string + args_number = name_number[1].lstrip('[').rstrip(']') + final_command['args'] = args_number + temp_macros.append(final_command) + names = [] + for macro in temp_macros: + names.append(macro['name']) + import collections + seen = set() + duplicate_indices = [names.index(item) for item, count in collections.Counter(names).items() if count > 1] + if len(duplicate_indices) > 0: + duplicates = [] + for i in duplicate_indices: + name = temp_macros[i]['name'] + duplicate = {'name': name, 'where':[]} + for j in temp_macros: + if j['name'] == name: + duplicate['where'].append((j['line'], j['file'])) + duplicates.append(duplicate) + exception_text = "WARNING: macros where defined more than once, the last definition is used\n" + for dup in duplicates: + exception_text += "Macro {} defined in\n".format(dup['name'].strip('\\')) + for place in dup['where']: + exception_text += "{}, line {}\n".format(place[1], place[0]) + print(exception_text) + # remove line and file keys from temp_macros (added for debug in case of duplicates) + return [{k: v for k, v in elem.items() if k in ['name', 'definition', 'args']} for elem in temp_macros] + def process_summary(article): """Ensures summaries are not cut off. Also inserts mathjax script so that math will be rendered""" diff --git a/mathjax_script_template b/mathjax_script_template index 590755b..cfafaca 100644 --- a/mathjax_script_template +++ b/mathjax_script_template @@ -18,7 +18,7 @@ if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {{ mathjaxscript[(window.opera ? "innerHTML" : "text")] = "MathJax.Hub.Config({{" + " config: ['MMLorHTML.js']," + - " TeX: {{ extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'{tex_extensions}], equationNumbers: {{ autoNumber: 'AMS' }} }}," + + " TeX: {{ extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'{tex_extensions}], equationNumbers: {{ autoNumber: 'AMS' }}, Macros: {macros} }}," + " jax: ['input/TeX','input/MathML','output/HTML-CSS']," + " extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," + " displayAlign: '"+ align +"'," +