summaryrefslogtreecommitdiff
path: root/support/gladtex
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/gladtex
Initial commit
Diffstat (limited to 'support/gladtex')
-rw-r--r--support/gladtex/COPYING165
-rw-r--r--support/gladtex/ChangeLog184
-rw-r--r--support/gladtex/README.md125
-rw-r--r--support/gladtex/ToDo.FAQ6
-rw-r--r--support/gladtex/ToDo.md28
-rw-r--r--support/gladtex/create_windows_distributions.py140
-rw-r--r--support/gladtex/examples/md2epub.py84
-rw-r--r--support/gladtex/gladtex.py331
-rw-r--r--support/gladtex/gleetex/__init__.py12
-rw-r--r--support/gladtex/gleetex/cachedconverter.py235
-rw-r--r--support/gladtex/gleetex/caching.py233
-rw-r--r--support/gladtex/gleetex/htmlhandling.py485
-rw-r--r--support/gladtex/gleetex/image.py293
-rw-r--r--support/gladtex/gleetex/pandoc.py75
-rw-r--r--support/gladtex/gleetex/parser.py64
-rw-r--r--support/gladtex/gleetex/typesetting.py331
-rw-r--r--support/gladtex/gleetex/unicode.py2390
-rw-r--r--support/gladtex/manpage.md249
-rwxr-xr-xsupport/gladtex/runtests11
-rw-r--r--support/gladtex/setup.py99
-rw-r--r--support/gladtex/tests/test_cachedconverter.py143
-rw-r--r--support/gladtex/tests/test_caching.py149
-rw-r--r--support/gladtex/tests/test_htmlhandling.py404
-rw-r--r--support/gladtex/tests/test_imagecreation.py165
-rw-r--r--support/gladtex/tests/test_typesetting.py218
-rw-r--r--support/gladtex/update_unicode_table.py142
26 files changed, 6761 insertions, 0 deletions
diff --git a/support/gladtex/COPYING b/support/gladtex/COPYING
new file mode 100644
index 0000000000..65c5ca88a6
--- /dev/null
+++ b/support/gladtex/COPYING
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/support/gladtex/ChangeLog b/support/gladtex/ChangeLog
new file mode 100644
index 0000000000..2c1f932c96
--- /dev/null
+++ b/support/gladtex/ChangeLog
@@ -0,0 +1,184 @@
+3.0.0
+
+ - new features and incompatible changes:
+ - add `-P` command-line switch to be used as a Pandoc document filter,
+ see <https://pandoc.org/filters.html>
+ - add environment variable `GLADTEX_ARGS` to pass command-line
+ switches when used as pandocfilter where passing additional
+ arguments is impossible
+ - redefine colour handling: use xcolor package, therefore handling
+ text and background colour the same way for both PNG and SVG
+ - add SVG support for scalable images
+ - use SVG output by default
+ - gleetex.htmlhandling.HtmlImageFormatter: rename link_path to
+ link_prefix
+ - bug fixes:
+ - correctly parse HTML5 file encoding declarations
+ - add more exceptions to the unicode table for the unicode replacement
+ mode (see -R)
+ - treat -d as a relative path
+
+2.3.1 Avoid useless spaces
+
+ - When formula replacement with `-R` is requested, it could happen that
+ additional spaces were inserted, even if not necessary. "für" would for
+ instance become "f\"{u} r". Fixed.
+
+2.3 Fix formula sizing
+
+ - It seems as if 16px / 12pt were the default font size these days for
+ browsers. Therefore, the default resolution has been set to 115 DPI.
+ Furthermore, the DPI switch now accepts pt values for fontsizes and will
+ calculate the corresponding DPI itself.
+ - When the environment variable `DEBUG=1` is set, the full backtrace will be
+ printed.
+ - Extend unicode table creation script to allow blacklisting of certain
+ commands.
+
+2.2.1 - fix handling of non-ascii alphabetical characters
+
+ - replace characters with diacritics in the LaTeX source, but keep the
+ unmodified character in image alt attribute (for better readibility)
+
+2.2 - make alternative text of formulas more readable
+
+ - replace formatting commands in alt attribute; this shortens the
+ formula and makes it mor readable
+ - replace unicode signs also in alt attribute (good for screen reader
+ users and text-mode browsers)
+ - recognize upper-case `ENV` attribute of `EQ` tag (so that e.g.
+ displaymath is recognized correctly)
+
+2.1.1 Bug Fix Release
+
+ - treat eq element content as verbatim
+ - decode HTML entities within formula tags
+
+2.1 add support for unicode math with translation table
+ - handle subprocess stdin and stdout encoding properly
+ - set UTF-8 as encoding for all LaTeX documents
+ - add -R option (replace non ascii characters)
+ - formulas in .htex documents may now contain umlauts or unicode math
+ characters; conversion will work without adjustments, only -R has to
+ be specified
+ - handle encoding better and more strictly for LaTeX 2E
+
+2.0.1 Bug Fix Release
+ - show user a meaningful error message if LaTeX or dvipng is missing
+ - setup.py: build manual page, if pandoc present
+ - freeze multiprocessing on Windows, to make executables distributable
+
+2.0 - make GladTeX truely platform independent
+ - add formula number in error output; makes tracking of formulas easier in
+ error case
+ - write man page
+ - set css class correctly for display math formulas
+ - HTML label/id generation:
+ - do not create overlong id's
+ - do only generate id's starting with an alphabetical character
+ - squeeze multiple identical characters
+ - reparse outsourced formulas correctly (was a mixture of formatted vs.
+ unformatted formulas)
+ - do not use absolute links when operating on file which is not in current
+ working directory
+ - be more careful with backslashes vs. slashes
+ - allow formulas consisting only of numbers (i.e. example calculations) by
+ prefixing "form_" in front of the HTML id (id must start with a letter
+ but may be followed by digits)
+ - allow removal of unreadable caches with the `-n` switch (extend library
+ with this functionality)
+ - introduce `-m` switch to print the output in a less concise, but more
+ machine-parseable format
+
+1.6 - complete rewrite
+ - rewrite GladTeX fully in Python
+ - allows easy compilation into a binary for a specific platform
+ - comes with a new library to use GladTeX functionality within other
+ applications
+ - fully unit-tested
+ - enable piping support; GladTeX can read from stdin and write to stdout
+ - drop -t switch; image is either transparent by default or has a background
+ color which can be set with -b
+ - drop -s switch
+ - introduce -o (output) option
+ - introduce new cache format containing version numbers; json, so
+ interface to other programming languages
+
+1.5 - Introduce options to make embedding of GladTeX easier
+ - Try to parse LaTeX's error output and display it to help users to find the
+ issue quicker and to make GladTeX better embeddable.
+ - Add option to remove error log file, produced by LaTeX, automatically.
+ - Rewrite some help messages.
+ - Add signal handling to get meaningful error messages if GladTeX hangs.
+
+1.4.2 - bug fix release
+ - Add some eval's to cope with some failures.
+ - Since there were some incompatibilities between Perl 5.10 and 5.18 in how
+ the cache of the generated images is stored, GladTeX now removes this file
+ along with all images starting with "eqn" and generates them again.
+
+1.4.1 - bug fix release
+ - Remove desc.html if created and empty
+
+1.4 - put LaTeX equations into alt tag for text-mode browsers and blind people
+ (and disabled images)
+ - If requested (-a), exclude equations longer than 80 characters in an extra
+ file and make the equation image a link to the longer, excluded image
+ alternative
+ - eqn2img: patch to allow building on windows
+ - Change build system from make to cmake
+ - Refactored gladtex code a lot to allow the usage of "use strict/use
+ warnings"
+ - Fix bug where multiple equations couldn't be on a single (html) line
+ - Rework manpage
+
+1.3 - Un-escape common entities before processing equations
+ - Update man page with CSS class options
+ - Add support for setting the CSS class of images when the
+ environment is "math" or "displaymath"
+ - eqn2img: changed redirection syntax (from dvips to /dev/null)
+ for portability
+ - GladTeX: exit with status 1 when a closing EQ tag is missing
+ - GladTeX: print error messages to stderr instead of stdout
+ - Fix environment-passing to eqn2img
+ - Add support for a "dpi" attribute on EQ tags to customize the
+ DPI used for each equation
+
+1.2 - Fixed a serious memory allocation error, pointed out by Eric J.
+ Francois. Also fixed several leaks.
+ - Added full alpha channel to PNG files (also suggested by Eric)
+ - The -e option was ignored, fixed (pointed out by Andr\'e Schleife)
+ - Added man page, contributed by Volker Schatz
+
+1.1 - Portability fixes: Do not assume a specific location for perl
+ (use "env" in the shebang line) and do not rely on the bash style
+ "&>" redirection.
+
+1.0 - Image alignment workaround (most browsers interpret
+ "ALIGN=MIDDLE" somewhat strangely, so it has been changed
+ to "STYLE=vertical-align: -xx")
+ - Added cache file, so that gladtex doesn't have to regenerate
+ images for equations that haven't changed.
+ - Added ENV option (as in <EQ ENV=align*>) to support environments
+ other than "displaymath".
+ - Bug fixes.
+
+0.3 - Added BoundingBox workaround (dvips sometimes outputs wrong
+ BoundingBox, for instance when using \mathbb{})
+ - Moved the whole "LaTeX eqn to image" conversion into the C code,
+ turning the C program (renamed from pngmodify to eqn2img) into
+ a standalone utility (e.g. echo '\sqrt{2}' | eqn2img -o
+ eqn.png).
+ - Added colour options (-c -b and -t).
+ - Fixed bug causing segfault when adding space _above_ an image.
+ - Fixed image reusing bug (in 0.2 image reuse didn't work across
+ separate files when processing files outside startup cwd).
+ - And some other minor bugs and cosmetic changes.
+ - Makefile added to distribution
+
+0.2 - First official release, completely rewritten code.
+
+0.1 - Used only internally at the Dept. of Mathematics at the Univ. of Oslo
+ July-August 1999 (for the project "Matteknekker'n") under the name
+ htmleqn.
+# vim: set expandtab sts=4 ts=4 sw=4 expandtab:
diff --git a/support/gladtex/README.md b/support/gladtex/README.md
new file mode 100644
index 0000000000..9fa684e08e
--- /dev/null
+++ b/support/gladtex/README.md
@@ -0,0 +1,125 @@
+GladTeX
+=======
+
+GladTeX is a preprocessor that enables the use of LaTeX formulas within HTML
+files. The formulas, embedded in <eq>...</eq> tags, as if within $$..$$ in LaTeX,
+is fed through latex and replaced by images.
+
+Additionally all images get an alt-tag for alternative texts that contains the
+LaTeX-equivalent of the image. This is handy for text-mode browsers or blind
+screen reader users.
+
+This is a complete rewrite of the old GladTeX which was implemented in Perl and
+in C. The old version was not easily portable across platforms. The
+new version is purely implemented in Python, gets rid of the Ghostscript
+dependency and additionally offers the GladTeX functionality in a Python module
+called gleetex. to be embedded in other applications.
+
+Features
+--------
+
+- both program and library: GladTeX and GleeTeX (both Python)
+- easy-to-adjust font size of formulas through command-line switch
+- customisation of commands using a custom LaTeX preamble
+- automated replacement of non-ascii characters through LaTeX control
+ sequences
+- usage as Pandoc filter for export in HTML-based formats as for instance Epub
+
+License
+-------
+
+- (C) 1999-2010 Martin G. Gulbrandsen
+- (C) 2011-2013 Jonathan Daugherty (especially release 1.3)
+- (C) 2013-2018 Sebastian Humenda
+
+This program is distributed under the LGPL-3, or at your option, any later
+version of the license; for details see the accompanying file COPYING.
+
+The official project homepage is at <http://humenda.github.io/GladTeX>
+
+Installation
+============
+
+### Debian/Ubuntu
+
+On all derivatives of Debian (as Ubuntu/Mint, etc.), installing GladTeX is as
+easy as
+
+ # apt-get install gladtex
+
+### Windows
+
+If you want to use the program without the Python library, you should download a
+pre-compiled binary from <https://github.com/humenda/GladTeX/releases>.
+
+Just unzip the archive and move the files to a directory within `%PATH%`.
+
+### From Source
+
+The following is required for installing GladTeX:
+
+- Python >= 3.4
+- LaTeX (2e), dvipng
+- the LaTeX package preview.sty
+
+
+#### Debian / Ubuntu
+
+On Debian/Ubuntu systems the following commands will satisfy the dependencies:
+
+ # apt-get install python3-all texlive-fonts-recommended texlive-latex-recommended preview-latex-style dvipng
+
+The package can then be installed using
+
+ # python3 setup.py install
+
+Note: If your system ships `python` as the command for Python3 you have to use
+`python in` the above command instead.
+
+#### OS X
+
+You need to install a LaTeX distribution on your Mac. GladTeX was successfully
+run with [MacTex](http://www.tug.org/mactex/).
+
+You can download a zip source archive from
+[GitHub](https://github.com/humenda/GladTeX) or use git:
+
+ $ git clone https://github.com/humenda/GladTeX.git
+
+Use `cd` to change to the GladTeX source directory and issue
+
+ $ python setup.py install
+
+
+
+### Compilation On Windows
+
+To compile GladTeX on Windows, yu need a Python3 installation. Assumed is
+python3.4, newer versions should work fine as well. Only the paths need to be
+adjusted
+
+Install py2exe:
+
+ c:\python34\scripts\pip.exe install py2exe
+
+Given that GladTeX is located in c:\users\user\gladtex:
+
+ cd c:\users\user\gladtex
+ c:\python34\python.exe setup.py install
+
+That will install GladTeX as a library to `c:\python34\lib` and the script to
+`c:\python34\scripts`. It also allows py2exe to find the gleetex module. Now the
+executable can be build:
+
+ c:\python34\scripts\build_exe.exe -b 0 -c gladtex.py
+
+That'll create a `dist/` folder containing the executable. If you have other
+python applications in your project it is useful to read about the `-b` switch
+to share some python components included in the just-built executable.
+
+Documentation
+-------------
+
+Please use `man gladtex` for further instructions or have a look at the file
+[manpage.md](manpage.md).
+
diff --git a/support/gladtex/ToDo.FAQ b/support/gladtex/ToDo.FAQ
new file mode 100644
index 0000000000..923c9836ba
--- /dev/null
+++ b/support/gladtex/ToDo.FAQ
@@ -0,0 +1,6 @@
+A few items which could go into a FAQ:
+
+What do the error messages mean?
+Why is my formula positioned awkardly within the text? (most probably displaymath instead of inline math)
+Why are limits over sums and similar not correctly set? (use displaymath, sometimes `\\limits`)
+Why don't my special characters like (unicode) math symbols or umlauts not work
diff --git a/support/gladtex/ToDo.md b/support/gladtex/ToDo.md
new file mode 100644
index 0000000000..e5a00c6ea7
--- /dev/null
+++ b/support/gladtex/ToDo.md
@@ -0,0 +1,28 @@
+To Do
+=====
+
+This list contains things to be implemented in GladTeX. If you have additions or
+even feel like you want to do it, feel free to drop me an email: `shumenda |aT|
+gmx //dot-- de`.
+
+Uncategorized
+-------------
+
+- introduce command line option which will check whether all all formulas in a
+ cache are used and if not, remove the formula (only useful for caches
+ corresponding to a single document)
+
+Gettext
+-------
+
+
+Gettext should be integrated to localize messages (especially errors).
+
+Compressed Cache
+----------------
+
+The cache stores the path, the formula and the positioning of an image. For
+large documents, this might be quite big, hence it makes sense to compress them.
+
+To make things easier, the cache should have a .gz extension.
+
diff --git a/support/gladtex/create_windows_distributions.py b/support/gladtex/create_windows_distributions.py
new file mode 100644
index 0000000000..3c7ca2c6cf
--- /dev/null
+++ b/support/gladtex/create_windows_distributions.py
@@ -0,0 +1,140 @@
+"""
+This file builds windows distributions, zip files with GladTeX and all other
+files."""
+import os
+import shutil
+import stat
+import sys
+import zipfile
+import gleetex
+
+def exec_setup_py(arg_string):
+ """Execute `python setup.py` as a subprocess. Use Wine, if necessary."""
+ ret = None
+ if sys.platform.startswith('win'):
+ ret = os.system('python setup.py ' + arg_string)
+ else:
+ if not shutil.which('wine'):
+ print("Error: Wine is not installed, aborting…")
+ sys.exit(5)
+ ret = os.system('wine python setup.py ' + arg_string)
+ if ret:
+ if sys.platform.startswith('win'):
+ print("Aborting at command `python setup.py %s`." % arg_string)
+ else:
+ print("Aborting at command `wine python setup.py %s`." % arg_string)
+ sys.exit(7)
+
+def get_python_version():
+ """Return the python version as a string."""
+ import re, subprocess
+ args = ['python', '--version']
+ if not sys.platform.startswith('win'):
+ args = ['wine'] + args
+ proc = subprocess.Popen(args, stdout=subprocess.PIPE)
+ stdout = proc.communicate()[0].decode(sys.getdefaultencoding())
+ if proc.wait():
+ raise TypeError("Abnormal subprocess termination while querying python version.")
+ return re.search(r'.*?(\d+\.\d+\.\d+)', stdout).groups()[0]
+
+def get_executable_name(label):
+ """Construct the name of an executable"""
+ return 'gladtex-win64-%s-py_%s-%s.zip' % (gleetex.VERSION, get_python_version(),
+ label)
+
+def bundle_files(src, output_name):
+ """Bundle the compiled binary files with README, ChangeLog and COPYING."""
+ if os.path.exists(output_name):
+ shutil.rmtree(output_name)
+ os.rename(src, output_name)
+ # add README.first
+ with open(os.path.join(output_name, 'README.first.txt'), 'w') as f:
+ f.write('GladTeX for Windows\r\n===================\r\n\r\n')
+ f.write('This program has been compiled with python 3.4.4. If you want to embedd it in binary form with your binary python application, the version numbers HAVE TO match.\r\n')
+ f.write('\r\nFor more information, see the file README.md or http://humenda.github.io/GladTeX\r\n')
+
+ # copy README and other files
+ for file in ['README.md', 'COPYING', 'ChangeLog']:
+ dest = os.path.join(output_name, file)
+ # check whether file ending exists
+ if not '.' in dest[-5:]:
+ dest += '.txt'
+ shutil.copy(file, dest)
+
+ files = [os.path.join(root, file)
+ for root, _, files in os.walk(output_name) for file in files]
+ with zipfile.ZipFile(output_name + '.zip', 'w', zipfile.ZIP_DEFLATED) as z:
+ for file in files:
+ z.write(file)
+ shutil.rmtree(output_name)
+
+class TemporaryBuildDirectory():
+ """Context handler to guard the build process.
+ Upon entering the context, the source is copied to a temporary directory and
+ the program changes to this directory. After all build actions have been
+ done, the output file is copied back to the original directory, the program
+ resets the current working directory and deletes the temporary directory."""
+ def __init__(self, output_file_name):
+ self.orig_cwd = os.getcwd()
+ self.tmpdir = None
+ self.output_file_name = output_file_name
+
+ def __enter__(self):
+ self.tmpdir = self.get_temp_directory()
+ shutil.copytree(os.getcwd(), self.tmpdir)
+ os.chdir(self.tmpdir)
+ return self
+
+ def __exit__(self, _a, _b, _c):
+ os.chdir(self.orig_cwd)
+ shutil.copy(os.path.join(self.tmpdir, self.output_file_name),
+ self.output_file_name)
+ shutil.rmtree(self.tmpdir, onerror=self.__onerror)
+
+ def get_temp_directory(self):
+ """Find a temporary directory to work in. The checks are done to find a
+ directory which does not reside within the user's path, because py2exe
+ includes absolute paths for python scripts (in their tracebacks). It is not
+ desirable to show the whole world the directory layout of the computer where
+ the source code was built on."""
+ tmp_base = None
+ if os.path.exists('/tmp'):
+ tmp_base = '/tmp'
+ elif os.path.exists('\\temp'):
+ tmp_base = '\\temp'
+ elif os.path.exists('\\windows\\temp'):
+ tmp_base = '\\windows\\temp'
+ else:
+ import tempfile
+ tmp_base = tempfile.gettempdir()
+ tmpdir = os.path.join(tmp_base, 'gladtex.build')
+ if os.path.exists(tmpdir):
+ shutil.rmtree(tmpdir, onerror=self.__onerror)
+ return tmpdir
+
+ def __onerror(self, func, path, exc_info):
+ """
+ Error handler for ``shutil.rmtree``.
+ If the error is due to an access error (read only file) it attempts to
+ add write permission and then retries. If the error is for another reason it re-raises the error.
+ Usage : ``shutil.rmtree(path, onerror=onerror)``."""
+ if not os.access(path, os.W_OK):
+ # Is the error an access error ?
+ os.chmod(path, stat.S_IWUSR)
+ func(path)
+ else:
+ raise exc_info
+
+
+if __name__ == '__main__':
+ with TemporaryBuildDirectory(get_executable_name('embeddable')) as tb:
+ # build embeddable release, where all files are separate DLL's; if somebody
+ # distributes a python app, these DLL files can be shared
+ exec_setup_py('py2exe -c -O 2 -i gleetex --bundle-files 3')
+ bundle_files('dist', os.path.splitext(tb.output_file_name)[0])
+
+ # create a stand-alone version of GladTeX
+ with TemporaryBuildDirectory(get_executable_name('standalone')) as tb:
+ exec_setup_py('py2exe -i gleetex -c -O 2 --bundle-files 1')
+ bundle_files('dist', os.path.splitext(tb.output_file_name)[0])
+
diff --git a/support/gladtex/examples/md2epub.py b/support/gladtex/examples/md2epub.py
new file mode 100644
index 0000000000..0354cfeb70
--- /dev/null
+++ b/support/gladtex/examples/md2epub.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+"""This demo script converts from markdown to Epub using GladTeX. It requires
+Pandoc for the conversion.
+
+Throughout this script, the abbreviation AST for Abstract Syntax Tree is used."""
+
+import json
+import os
+import shutil
+import subprocess
+import sys
+
+import gleetex
+
+def transform_ast(ast):
+ # extract formulas from Pandoc document AST
+ formulas = gleetex.pandoc.extract_formulas(ast)
+ # converter using cache, helps avoiding the same formula twice
+ conv = gleetex.cachedconverter.CachedConverter('.', True,
+ encoding="UTF-8")
+ # automatically handle unicode
+ conv.set_replace_nonascii(True)
+ # go parallel
+ conv.convert_all('.', formulas)
+
+ # an converted image has information like image depth and height, adjust
+ # data structure for write-back
+ formulas = [conv.get_data_for(eqn, style) for _p, style, eqn in formulas]
+ # get a formatter instance
+ with gleetex.htmlhandling.HtmlImageFormatter(".") as img_fmt:
+ # non-ascii sequences will be replaced in the laternative text
+ img_fmt.set_replace_nonascii(True)
+ # this alters the AST reference, so no return value required
+ gleetex.pandoc.replace_formulas_in_ast(img_fmt, ast['blocks'],
+ formulas)
+
+def cleanup(path):
+ # remove images and ache, relevant data is included within the EPUB
+ for file in os.listdir(path):
+ if file.endswith('.png') or file.endswith('.cache'):
+ os.remove(os.path.join(path, file))
+
+def main():
+ for prog in ('pandoc', 'gladtex'):
+ if not shutil.which(prog):
+ sys.stderr.write(('This script requires %s, please install and rerun '
+ 'this script.') % prog)
+ sys.exit(1)
+
+ usage = False
+ if len(sys.argv) < 2:
+ print("Missing command arguments.")
+ usage = True
+ elif len(sys.argv) > 2 or (len(sys.argv) == 2 and not
+ os.path.exists(sys.argv[1])):
+ print("Exactly one input path required")
+ usage = True
+ if usage:
+ print("%s <INPUTFILE>\n\nConvert given file to epub using GladTeX." % \
+ sys.argv[0])
+ sys.exit(0)
+
+ inputfile = sys.argv[1]
+ outputfile = '%s.epub' % os.path.splitext(inputfile)[0]
+ # get the document AST
+ proc = subprocess.Popen(['pandoc', '-t', 'json', inputfile],
+ stdout=subprocess.PIPE)
+ ast = json.loads(proc.communicate()[0].decode(sys.getdefaultencoding()))
+ if proc.wait() != 0:
+ sys.exit(2)
+
+ # the actual GleeTeX calls are here
+ transform_ast(ast)
+
+ # write back to stdin of pandoc
+ proc = subprocess.Popen(['pandoc', '-o', outputfile, '-f', 'json', '-t',
+ 'epub'], stdin=subprocess.PIPE)
+ proc.communicate(json.dumps(ast).encode(sys.getdefaultencoding()))
+ if proc.wait():
+ sys.exit(2)
+ cleanup('.')
+
+if __name__ == '__main__':
+ main()
diff --git a/support/gladtex/gladtex.py b/support/gladtex/gladtex.py
new file mode 100644
index 0000000000..c9c991dc93
--- /dev/null
+++ b/support/gladtex/gladtex.py
@@ -0,0 +1,331 @@
+#!/usr/bin/env python3
+# (c) 2013-2018 Sebastian Humenda
+# This code is licenced under the terms of the LGPL-3+, see the file COPYING for
+# more details.
+import argparse
+import multiprocessing
+import os
+import posixpath
+import sys
+import gleetex
+from gleetex import parser
+
+
+class HelpfulCmdParser(argparse.ArgumentParser):
+ """This variant of arg parser always prints the full help whenever an error
+ occurs."""
+ def error(self, message):
+ sys.stderr.write('error: %s\n' % message)
+ self.print_help()
+ sys.exit(2)
+
+
+
+def format_ordinal(number):
+ endings = ['th', 'st', 'nd', 'rd'] + ['th'] * 6
+ return '%d%s' % (number, endings[number%10])
+
+class Main:
+ """This class parses command line arguments and deals with the
+ conversion. Only the run method needs to be called."""
+ def __init__(self):
+ self.__encoding = "utf-8"
+
+ def _parse_args(self, args):
+ """Parse command line arguments and return option instance."""
+ epilog = "GladTeX %s, http://humenda.github.io/GladTeX" % gleetex.VERSION
+ description = ("GladTeX is a preprocessor that enables the use of LaTeX"
+ " maths within HTML files. The maths, embedded in <EQ>...</EQ> "
+ "tags, as if within \\(..\\) in LaTeX (or $...$ in TeX), is fed "
+ "through latex and replaced by images.\n\nPlease also see the "
+ "documentation on the web or from the manual page for more "
+ "information, especially on environment variables.")
+ cmd = HelpfulCmdParser(epilog=epilog, description=description)
+ cmd.add_argument("-a", action="store_true", dest="exclusionfile", help="save text alternatives " +
+ "for images which are too long for the alt attribute into a " +
+ "single separate file and link images to it")
+ cmd.add_argument('-b', dest='background_color',
+ help=("Set background color for resulting images "
+ "(default transparent, use hex)"))
+ cmd.add_argument('-c', dest='foreground_color',
+ help=("Set foreground color for resulting images (default "
+ "000000, hex)"))
+ cmd.add_argument('-d', dest='directory', help="Directory in which to" +
+ " store generated images in (relative to the output file)")
+ cmd.add_argument('-e', dest='latex_maths_env',
+ help="Set custom maths environment to surround the formula" + \
+ " (e.g. flalign)")
+ cmd.add_argument('-f', metavar='SIZE', dest='fontsize', default=12,
+ help="Set font size in pt (default 12)")
+ cmd.add_argument('-E', dest='encoding', default=None,
+ help="Overwrite encoding to use (default UTF-8)")
+ cmd.add_argument('-i', metavar='CLASS', dest='inlinemath',
+ help="CSS class to assign to inline math (default: 'inlinemath')")
+ cmd.add_argument('-l', metavar='CLASS', dest='displaymath',
+ help="CSS class to assign to block-level math (default: 'displaymath')")
+ cmd.add_argument('-K', dest='keep_latex_source', action="store_true",
+ default=False, help="keep LaTeX file(s) when converting formulas (useful for debugging)")
+ cmd.add_argument('-m', dest='machinereadable', action="store_true",
+ default=False,
+ help="Print output in machine-readable format (less concise, better parseable)")
+ cmd.add_argument("-n", action="store_true", dest="notkeepoldcache",
+ help=("Purge unreadable caches along with all eqn*.png files. "
+ "Caches can be unreadable if the used GladTeX version is "
+ "incompatible. If this option is unset, GladTeX will "
+ "simply fail when the cache is unreadable."))
+ cmd.add_argument('-o', metavar='FILENAME', dest='output',
+ help=("Set output file name; '-' will print text to stdout (by"
+ "default input file name is used and .htex extension changed "
+ "to .html)"))
+ cmd.add_argument('-p', metavar='LATEX_STATEMENT', dest="preamble",
+ help="Add given LaTeX code to preamble of document; that'll " +\
+ "affect the conversion of every image")
+ cmd.add_argument('-P', dest="pandocfilter", action='store_true',
+ help="Use GladTeX as a Pandoc filter: read a Pandoc JSON AST "
+ "from stdin, convert the images, change math blocks to "
+ "images and write JSON to stdout")
+ cmd.add_argument('--png', action='store_true', dest='png',
+ help="Use PNG instead of SVG for images")
+ cmd.add_argument('-r', '--resolution', metavar='DPI', dest='dpi',
+ default=None,
+ help=("Set resolution in DPI, only available if PNG output "
+ "selected; also see `-f`"))
+ cmd.add_argument('-R', action="store_true", dest='replace_nonascii',
+ default=False, help="Replace non-ascii characters in formulas "
+ "through their LaTeX commands")
+ cmd.add_argument("-u", metavar="URL", dest='url',
+ help="URL to image files (relative links are default)")
+ cmd.add_argument('input', help="Input .htex file with LaTeX " +
+ "formulas (if omitted or -, stdin will be read)")
+ return cmd.parse_args(args)
+
+ def exit(self, text, status):
+ """Exit function. Could be used to register any clean up action."""
+ sys.stderr.write(text)
+ if not text.endswith('\n'):
+ sys.stderr.write('\n')
+ sys.exit(status)
+
+ def validate_options(self, opts):
+ """Validate certain arguments suppliedon the command line. The user will
+ get a (hopefully) helpful error message if he/she gave an invalid
+ parameter."""
+ if opts.fontsize and opts.dpi:
+ print("Options -f and -d can't be used at the same time.")
+ sys.exit(14)
+ if opts.dpi and not opts.png:
+ print(("Impossible to set resolution when using SVG as output, "
+ "try -f"))
+ sys.exit(14)
+
+ def get_input_output(self, options):
+ """Determine whether GladTeX is reading from stdin/file, writing to
+ stdout/file and determine base_directory if files are in another
+ directory.
+ If no output file name is given and there is a input file to read
+ from, output is written to a file ending on .html instead of .htex.
+ The returned document is either string or byte, the latter if encoding
+ is unknown."""
+ data = None
+ output = '-'
+ if options.input == '-':
+ data = sys.stdin.read()
+ else:
+ try:
+ # if encoding was specified or if a pandoc filter is supplied,
+ # read document with default encoding
+ if options.encoding or options.pandocfilter:
+ encoding = ('UTF-8' if options.pandoc else options.encoding)
+ with open(options.input, encoding=encoding) as f:
+ data = f.read()
+ else: # read as binary and guess from HTML meta charset
+ with open(options.input, 'rb') as file:
+ data = file.read()
+ except UnicodeDecodeError as e:
+ self.exit(('Error while reading from %s: %s\nProbably this file'
+ ' has a different encoding, try specifying -E.') % \
+ (options.input, str(e)), 88)
+ except IsADirectoryError:
+ self.exit("Error: cannot open %s for reading: is a directory." \
+ % options.input, 19)
+ except FileNotFoundError:
+ self.exit("Error: file %s not found." % options.input, 20)
+
+ # check which output file name to use
+ if options.output:
+ output = options.output
+ elif options.input != '-':
+ output = os.path.splitext(options.input)[0] + '.html'
+
+ # else case: output = '-' (see above)
+ base_path = ''
+ if options.output and os.path.dirname(options.output):
+ base_path = os.path.dirname(output)
+ elif options.input != '-' and os.path.dirname(options.input):
+ base_path = os.path.dirname(options.input)
+ if base_path: # if finally a basepath found:, strip \\ if on Windows
+ base_path = posixpath.join(*(base_path.split('\\')))
+ # the basepath needs to be relative to the output file
+ return (data, base_path, output)
+
+
+ def run(self, args):
+ options = self._parse_args(args[1:])
+ self.validate_options(options)
+ self.__encoding = options.encoding
+ fmt = ('pandocfilter' if options.pandocfilter else 'html')
+ doc, base_path, output = self.get_input_output(options)
+ old_cwd = os.getcwd()
+ if base_path:
+ os.chdir(base_path)
+ try:
+ # doc is either a list of raw HTML chunks and formulas or a tuple of
+ # (document AST, list of formulas) if options.pandocfilter
+ self.__encoding, doc = parser.parse_document(doc, fmt)
+ except gleetex.parser.ParseException as e:
+ input_fn = ('stdin' if options.input == '-' else options.input)
+ self.exit('Error while parsing {}: {}'.format(input_fn,
+ str(e)), 5)
+
+ link_path = (options.directory if options.directory else '')
+ processed = self.convert_images(doc, link_path, options)
+ with gleetex.htmlhandling.HtmlImageFormatter(base_path=link_path,
+ link_path=options.url) as img_fmt:
+ img_fmt.set_exclude_long_formulas(True)
+ if options.replace_nonascii:
+ img_fmt.set_replace_nonascii(True)
+ if options.url:
+ img_fmt.set_url(options.url)
+ if options.inlinemath:
+ img_fmt.set_inline_math_css_class(options.inlinemath)
+ if options.displaymath:
+ img_fmt.set_display_math_css_class(options.displaymath)
+
+ os.chdir(old_cwd)
+ with (sys.stdout if output == '-'
+ else open(output, 'w', encoding=self.__encoding)) as file:
+ if options.pandocfilter:
+ gleetex.pandoc.write_pandoc_ast(file, processed, img_fmt)
+ else:
+ gleetex.htmlhandling.write_html(file, processed, img_fmt)
+
+ def convert_images(self, parsed_document, base_path, options):
+ """Convert all formulas to images and store file path and equation in a
+ list to be processed later on."""
+ base_path = ('' if not base_path or base_path == '.' else base_path)
+ result = []
+ try:
+ conv = gleetex.cachedconverter.CachedConverter(base_path,
+ not options.notkeepoldcache, encoding=self.__encoding)
+ except gleetex.caching.JsonParserException as e:
+ self.exit(e.args[0], 78)
+
+ self.set_options(conv, options)
+ if options.pandocfilter:
+ formulas = parsed_document[1]
+ else: # HTML chunks from EqnParser
+ formulas = [c for c in parsed_document if isinstance(c, (tuple,
+ list))]
+ try:
+ conv.convert_all(formulas)
+ except gleetex.cachedconverter.ConversionException as e:
+ self.emit_latex_error(e, options.machinereadable,
+ options.replace_nonascii)
+
+ if options.pandocfilter:
+ # return (ast, formulas), just with formulas being replaced with the
+ # conversion data
+ return (parsed_document[0], [conv.get_data_for(eqn, style)
+ for _p, style, eqn in formulas])
+ for chunk in parsed_document:
+ # output of EqnParser: list-alike is formula, str is raw HTML
+ if isinstance(chunk, (tuple, list)):
+ _p, displaymath, formula = chunk
+ try:
+ result.append(conv.get_data_for(formula, displaymath))
+ except KeyError as e:
+ raise KeyError(("formula '%s' not found; that means it was "
+ "not converted which should usually not happen.") % e.args[0])
+ else:
+ result.append(chunk)
+ return result
+
+
+ def set_options(self, conv, options):
+ """Apply options from command line parser to the converter."""
+ # set options
+ options_to_query = ['preamble', 'latex_maths_env',
+ 'png', 'keep_latex_source', 'foreground_color',
+ 'background_color']
+ for option_str in options_to_query:
+ option = getattr(options, option_str)
+ if option:
+ if option in ('True', 'False', 'false', 'true'):
+ option = bool(option)
+ conv.set_option(option_str, option)
+ if options.dpi:
+ conv.set_option("dpi", float(options.dpi))
+ elif options.fontsize:
+ conv.set_option("fontsize", options.fontsize)
+ if options.replace_nonascii:
+ conv.set_replace_nonascii(True)
+
+ def emit_latex_error(self, err, machine_readable, escape):
+ """Format a LaTeX error in a meaningful way. The argument escape
+ specifies, whether the -R switch had been passed. If the pandocfilter
+ mode is active, formula positions will be omitted; this makes the code
+ more complex."""
+ if 'DEBUG' in os.environ and os.environ['DEBUG'] == '1':
+ raise err
+ escaped = err.formula
+ if escape:
+ escaped = gleetex.typesetting.escape_unicode_maths(err.formula)
+ msg = None
+ additional = ''
+ if 'Package inputenc' in err.args[0]:
+ additional += ('Add the switch `-R` to automatically replace unicode '
+ 'characters with LaTeX command sequences.')
+ if machine_readable:
+ msg = 'Number: {}\nFormula: {}{}\nMessage: {}'.format(err.formula_count,
+ err.formula,
+ ('' if escaped == err.formula
+ else '\nLaTeXified formula: %s' % escaped),
+ err.cause)
+ if err.src_line_number and err.src_pos_on_line:
+ msg = ('Line: {}, {}\n' + msg).format(err.src_line_number,
+ err.src_pos_on_line)
+ if additional:
+ msg += '; ' + additional
+ else:
+ formula = ' ' + err.formula.replace('\n', '\n ')
+ escaped = (' ' + escaped.replace('\n', '\n ') if escaped !=
+ err.formula else '')
+ msg = "Error while converting formula %d\n" % err.formula_count
+ if err.src_line_number and err.src_pos_on_line:
+ msg += " at line %d, %d:\n" % (err.src_line_number,
+ err.src_pos_on_line,)
+ msg += '%s%s\n%s' % (formula, (''
+ if not escaped or escaped == err.formula
+ else '\nFormula without unicode symbols:\n%s' % escaped),
+ err.cause)
+ if additional:
+ import textwrap
+ msg += ' undefined.\n' + '\n'.join(textwrap.wrap(additional, 80))
+ self.exit(msg, 91)
+
+
+def main():
+ """Entry point for setuptools."""
+ # enable multiprocessing on Windows, see python docs
+ multiprocessing.freeze_support()
+ m = Main()
+ # run as pandoc filter?
+ args = sys.argv # fallback if no environment variable set
+ if 'GLADTEX_ARGS' in os.environ:
+ args = [sys.argv[0]] + os.environ['GLADTEX_ARGS'].split(' ')
+ if '-P' not in args:
+ args = [args[0]] + ['-P'] + args[1:] + ['-']
+ m.run(args)
+
+if __name__ == '__main__':
+ main()
diff --git a/support/gladtex/gleetex/__init__.py b/support/gladtex/gleetex/__init__.py
new file mode 100644
index 0000000000..89bfbed854
--- /dev/null
+++ b/support/gladtex/gleetex/__init__.py
@@ -0,0 +1,12 @@
+from . import caching
+from . import cachedconverter
+from . import htmlhandling
+from . import image
+from . import pandoc
+from . import parser
+from . import typesetting
+
+VERSION = '3.0.0'
+
+__all__ = ['caching', 'cachedconverter', 'document', 'htmlhandling', 'image',
+ 'pandoc', 'parser', 'unicode', 'VERSION']
diff --git a/support/gladtex/gleetex/cachedconverter.py b/support/gladtex/gleetex/cachedconverter.py
new file mode 100644
index 0000000000..04161aab71
--- /dev/null
+++ b/support/gladtex/gleetex/cachedconverter.py
@@ -0,0 +1,235 @@
+# (c) 2013-2018 Sebastian Humenda
+# This code is licenced under the terms of the LGPL-3+, see the file COPYING for
+# more details.
+"""In order to convert images only if they are not already cached, the cached
+converter sacrifices customizability for convenience and provides a class
+converting a formula directly to a png file."""
+
+import concurrent.futures
+import multiprocessing
+import os
+import subprocess
+
+from . import caching, image, typesetting
+from .caching import normalize_formula
+from .image import Format
+
+class ConversionException(Exception):
+ """This exception is raised whenever a problem occurs during conversion.
+ Example:
+ c = ConversionException("cause", "\\tau", 10, 38, 5)
+ assert c.cause == cause
+ assert c.formula == '\\tau'
+ assert c.src_line_number == 10 # line number in source document (counting from 1)
+ assert c.src_pos_on_line == 38 # position of formula in source line, counting from 1
+ assert c.formula_count == 5 # fifth formula in document (starting from 1)
+ """
+ # mind your own business mr. pylint:
+ #pylint: disable=too-many-arguments
+ def __init__(self, cause, formula, formula_count, src_line_number=None,
+ src_pos_on_line=None):
+ # provide a default error message
+ if src_line_number and src_pos_on_line:
+ super().__init__("LaTeX failed at formula line {}, {}, no. {}: {}".format(
+ src_line_number, src_pos_on_line, formula_count, cause))
+ else:
+ super().__init__("LaTeX failed at formula no. {}: {}".format(
+ formula_count, cause))
+ # provide attributes for upper level error handling
+ self.cause = cause
+ self.formula = formula
+ self.src_line_number = src_line_number
+ self.src_pos_on_line = src_pos_on_line
+ self.formula_count = formula_count
+
+class CachedConverter:
+ """Convert formulas to images.
+
+ c = CachedConverter(base_path)
+ for formula in [... formulas ...]:
+ pos, file_path = c.convert(formula)
+ ...
+
+ The formula is either converted or retrieved from a cache in the same
+ directory like the images.
+
+ :param base_path directory to place files in (relative to the file
+ converted. So if the file being converted is foo/bar.htex and the
+ base_path is set to img, the image will be placed in foo/img/.
+ :param keep_old_cache If an existing cache cannot be read (incompatible
+ GladTeX version, ...) Aand the flag is set, the program will simply
+ crash and tell the user to remove the cache (default). If set to False,
+ the program will instead remove the cache and all eqn* files and
+ recreate the cache.
+ :param encoding The encoding for the LaTeX document, default None
+ """
+ GLADTEX_CACHE_FILE_NAME = 'gladtex.cache'
+
+ def __init__(self, base_path, keep_old_cache=True, encoding=None):
+ cache_path = os.path.join(base_path,
+ CachedConverter.GLADTEX_CACHE_FILE_NAME)
+ self.__base_path = base_path
+ self.__cache = caching.ImageCache(cache_path,
+ keep_old_cache=keep_old_cache)
+ self.__converter = None
+ self.__options = {'dpi': None, 'transparency': None, 'fontsize': None,
+ 'background_color' : None, 'foreground_color' : None,
+ 'preamble' : None, 'latex_maths_env' : None,
+ 'keep_latex_source': False, 'png': False}
+ self.__encoding = encoding
+ self.__replace_nonascii = False
+
+
+ def set_option(self, option, value):
+ """Set one of the options accepted for gleetex.image.Tex2img. It is a
+ proxy function.
+ `option` must be one of dpi, fontsize, transparency, background_color,
+ foreground_color, preamble, latex_maths_env, keep_latex_source, png."""
+ if not option in self.__options.keys():
+ raise ValueError("Option must be one of " + \
+ ', '.join(self.__options.keys()))
+ self.__options[option] = value
+
+ def set_replace_nonascii(self, flag):
+ """If set, GladTeX will convert all non-ascii character to LaTeX
+ commands. This setting is passed through to typesetting.LaTeXDocument."""
+ self.__replace_nonascii = flag
+
+
+ def convert_all(self, formulas):
+ """convert_all(formulas)
+ Convert all formulas using self.convert concurrently. Each element of
+ `formulas` must be a tuple containing (formula, displaymath,
+ Formulas already contained in the cache are not converted."""
+ formulas_to_convert = self._get_formulas_to_convert(formulas)
+ if formulas_to_convert:
+ self.__converter = image.Tex2img(Format.Png
+ if self.__options['png'] else Format.Svg)
+ # apply configured image output options
+ for option, value in self.__options.items():
+ if value and hasattr(self.__converter, 'set_' + option):
+ if isinstance(value, str): # only try string -> number
+ try: # some values are numbers
+ value = float(value)
+ except ValueError:
+ pass
+ getattr(self.__converter, 'set_' + option)(value)
+ self._convert_concurrently(formulas_to_convert)
+
+ def _get_formulas_to_convert(self, formulas):
+ """Return a list of formulas to convert, along with their count in the
+ global list of formulas of the document being converted and the file
+ name. Function was decomposed for better testability."""
+ formulas_to_convert = [] # find as many file names as equations
+ file_ext = (Format.Png.value if self.__options['png']
+ else Format.Svg.value)
+ eqn_path = lambda x: os.path.join(self.__base_path, 'eqn%03d.%s' % (x,
+ file_ext))
+
+ # is (formula, display_math) already in the list of formulas to convert;
+ # displaymath is important since formulas look different in inline maths
+ formula_was_converted = lambda f, dsp: (normalize_formula(f), dsp) in \
+ ((normalize_formula(u[0]), u[3]) for u in formulas_to_convert)
+ # find enough free file names
+ file_name_count = 0
+ used_file_names = [] # track which file names have been assigned
+ for formula_count, (pos, dsp, formula) in enumerate(formulas):
+ if not self.__cache.contains(formula, dsp) and \
+ not formula_was_converted(formula, dsp):
+ while os.path.exists(eqn_path(file_name_count)) or \
+ eqn_path(file_name_count) in used_file_names:
+ file_name_count += 1
+ used_file_names.append(eqn_path(file_name_count))
+ formulas_to_convert.append((formula, pos, eqn_path(file_name_count),
+ dsp, formula_count + 1))
+ return formulas_to_convert
+
+
+ def _convert_concurrently(self, formulas_to_convert):
+ """The actual concurrent conversion process. Method is intended to be
+ called from convert_all()."""
+ if self.__base_path and not os.path.exists(self.__base_path):
+ # create directory *before* it is required in the concurrent
+ # formulacreation step
+ os.makedirs(self.__base_path)
+
+ thread_count = int(multiprocessing.cpu_count() * 2.5)
+ # convert missing formulas
+ with concurrent.futures.ThreadPoolExecutor(max_workers=thread_count) as executor:
+ # start conversion and mark each thread with its formula, position
+ # in the source file and formula_count (index into a global list of
+ # formulas)
+ jobs = {executor.submit(self.__convert, eqn, path, dsp): (eqn, pos, count)
+ for (eqn, pos, path, dsp, count) in formulas_to_convert}
+ error_occurred = None
+ for future in concurrent.futures.as_completed(jobs):
+ if error_occurred and not future.done():
+ future.cancel()
+ continue
+ formula, pos_in_src, formula_count = jobs[future]
+ try:
+ data = future.result()
+ except subprocess.SubprocessError as e:
+ # retrieve the position (line, pos on line) in the source
+ # document from original formula list
+ if pos_in_src: # missing for the pandocfilter case
+ pos_in_src = list(p+1 for p in pos_in_src) # user expects lines/pos_in_src' to count from 1
+ self.__cache.write() # write back cache with valid entries
+ if not pos_in_src: # pandocfilter case:
+ error_occurred = ConversionException(str(e.args[0]),
+ formula, formula_count)
+ else:
+ error_occurred = ConversionException(str(e.args[0]), formula,
+ formula_count, pos_in_src[0], pos_in_src[1])
+ else:
+ self.__cache.add_formula(formula, data['pos'], data['path'],
+ data['displaymath'])
+ self.__cache.write()
+ #pylint: disable=raising-bad-type
+ if error_occurred:
+ raise error_occurred
+
+
+
+ def __convert(self, formula, output_path, displaymath=False):
+ """convert(formula, output_path, displaymath=False)
+ Convert given formula with displaymath/inlinemath.
+ This method wraps the formula in a tex document, executes all the steps
+ to produce a image and return the positioning information for the
+ HTML output. It does not check the cache.
+ :param formula formula to convert
+ :param output_path image output path
+ :param displaymath whether or not to use displaymath during the conversion
+ :return dictionary with position (pos), image path (path) and formula
+ style (displaymath, boolean) as a dictionary with the keys in
+ parenthesis"""
+ latex = typesetting.LaTeXDocument(formula)
+ latex.set_displaymath(displaymath)
+ def set(opt, setter):
+ if self.__options[opt]:
+ getattr(latex, 'set_' + setter)(self.__options[opt])
+ set('preamble', 'preamble_string')
+ set('latex_maths_env', 'latex_environment')
+ set('background_color', 'background_color')
+ set('foreground_color', 'foreground_color')
+
+ if self.__encoding:
+ latex.set_encoding(self.__encoding)
+ if self.__replace_nonascii:
+ latex.set_replace_nonascii(True)
+ # dvipng needs the additionalindication of transparency (enabled by
+ # default) when setting a background colour
+ if self.__options['background_color']:
+ self.__converter.set_transparency(False)
+ pos = self.__converter.convert(latex,
+ os.path.splitext(output_path)[0])
+ return {'pos' : pos, 'path' : output_path, 'displaymath' :
+ displaymath}
+
+ def get_data_for(self, formula, display_math):
+ """Simple wrapper around ImageCache, enriching the returned data with
+ the information provided as arguments to this function. This helps when
+ using a formula without its context."""
+ data = self.__cache.get_data_for(formula, display_math).copy()
+ data.update({'formula': formula, 'displaymath': display_math})
+ return data
diff --git a/support/gladtex/gleetex/caching.py b/support/gladtex/gleetex/caching.py
new file mode 100644
index 0000000000..9fbae2c924
--- /dev/null
+++ b/support/gladtex/gleetex/caching.py
@@ -0,0 +1,233 @@
+# (c) 2013-2018 Sebastian Humenda
+# This code is licenced under the terms of the LGPL-3+, see the file COPYING for
+# more details.
+"""This module contains the ImageCache, caching formulas which have already been
+converted. This allows to re-use images for formulas which occur multiple timesd
+within a document. Furthermore, it can significantly speed up incremental
+document creation, because the cache is remembered across GladTeX runs.
+
+Cache format:
+
+ { # dict of formulas
+ 'some formula': # formula as key into dictionary
+ { # list of display math / inline maths variants
+ True: # displaymath = True
+ { # dictionary of values describing formula
+ 'path': 'some/path'
+ 'pos': { # positioning within the HTML document
+ 'height': ..., 'width':..., 'depth:....
+ }
+ }
+ }
+ }
+ }
+
+Formulas are `normalized`, so spacing is unified to detect possibly equal
+formulas more easyly.
+"""
+
+import json
+import os
+
+CACHE_VERSION = '2.0'
+
+def normalize_formula(formula):
+ """This function normalizes a formula. This e.g. means that multiple white
+ spaces are squeezed into one and a tab will be replaced by a space. With
+ this it is more realistic that a recurring formula in a document is detected
+ as such, even though if it might have been written with different spacing.
+ Empty braces ({}) are removed as well."""
+ return formula.replace('{}', ' ').replace('\t', ' ').replace(' ', ' '). \
+ rstrip().lstrip()
+
+def recover_bools(object):
+ """After JSon is read from disk, keys as False or True have been serialized
+ to 'false' and 'true', but they're not recovered by the json parser. This
+ function alters converts these keys back to booleans; note: it only works
+ with references, so this function doesn't return anything."""
+ if isinstance(object, dict):
+ for key in ['false', 'true']:
+ if key in object:
+ val = object[key] # store value
+ object[key == 'true'] = val # safe it with boolean representation
+ del object[key] # remove string key
+ # iterate recursively through dict
+ for value in object.values():
+ recover_bools(value)
+ if isinstance(object, list):
+ for item in object:
+ recover_bools(item)
+
+class JsonParserException(Exception):
+ """Specialized exception class for handling errors while parsing the JSON
+ cache."""
+ pass
+
+class ImageCache:
+ """
+ This cache stores formulas which have been converted already and don't need
+ to be converted again. This is both a disk usage and performance
+ improvement. The cache can be written and read from disk.
+
+ If the argument keep_old_cache is True, the cache will raise a
+ JsonParserException if that file could not be read (i.e. incompatible
+ GladTeX version). If set to False, it'll discard the cache along with all
+ eqn* files and start with a clean cache.
+
+ cache = ImageCache()
+ c.add_formula('\\tau', # the formulas
+ {'height': 1, 'depth': 2, 'width='3'}, # the positioning information for the output document
+ 'eqn042.png', displaymath=True):
+ assert len(cache) == 1 # one entry
+ c.write()
+ assert os.path.exists('gladtex.cache')
+ """
+ VERSION_STR = 'GladTeX__cache__version'
+
+ def __init__(self, path='gladtex.cache', keep_old_cache=True):
+ self.__cache = {}
+ self.__set_version(CACHE_VERSION)
+ self.__path = path
+ if os.path.exists(path):
+ try:
+ self._read()
+ except JsonParserException:
+ if keep_old_cache:
+ raise
+ else:
+ self._remove_old_cache_and_files()
+
+ def __len__(self):
+ """Return number of formulas in the cache."""
+ # ignore version
+ return len(self.__cache) - 1
+
+ def __set_version(self, version):
+ """Set version of cache (data structure format)."""
+ self.__cache[ImageCache.VERSION_STR] = version
+
+ def write(self):
+ """Write cache to disk. The file name will be the one configured during
+ initialisation of the cache."""
+ if len(self.__cache) == 0:
+ return
+ with open(self.__path, 'w', encoding='UTF-8') as file:
+ file.write(json.dumps(self.__cache))
+
+ def _read(self):
+ """Read Json from disk into cache, if file exists.
+ :raises JsonParserException if json could not be parsed"""
+ def raise_error(msg):
+ raise JsonParserException(msg + "\nPlease delete the cache (and" + \
+ " the images) and rerun the program.")
+ if os.path.exists(self.__path):
+ #pylint: disable=broad-except
+ try:
+ with open(self.__path, 'r', encoding='utf-8') as file:
+ self.__cache = json.load(file)
+ except Exception as e:
+ msg = "error while reading cache from %s: " % os.path.abspath(self.__path)
+ if isinstance(e, (ValueError, OSError)):
+ msg += str(e.args[0])
+ elif isinstance(e, UnicodeDecodeError):
+ msg += 'expected UTF-8 encoding, erroneous byte ' + \
+ '{0} at {1}:{2} ({3})'.format(*(e.args[1:]))
+ else:
+ msg += str(e.args[0])
+ raise_error(msg)
+ if not isinstance(self.__cache, dict):
+ raise_error("Decoded Json is not a dictionary.")
+ if not self.__cache.get(ImageCache.VERSION_STR):
+ self.__set_version(CACHE_VERSION)
+ cur_version = self.__cache.get(ImageCache.VERSION_STR)
+ if cur_version != CACHE_VERSION:
+ raise_error("Cache in %s has version %s, expected %s." % \
+ (self.__path, cur_version, CACHE_VERSION))
+ recover_bools(self.__cache)
+
+ def _remove_old_cache_and_files(self):
+ os.remove(self.__path)
+ directory = os.path.split(self.__path)[0]
+ if not directory:
+ directory = '.'
+ # remove all files starting with eqn*
+ for file in os.listdir(directory):
+ if not file.startswith('eqn'):
+ continue
+ file = os.path.join(directory, file)
+ if os.path.isfile(file):
+ os.remove(file)
+
+ def add_formula(self, formula, pos, file_path, displaymath=False):
+ """Add formula to cache. The pos argument contains the positioning
+ info for the output document and is a dict with 'height', 'width' and
+ 'depth'.
+ Keep in mind that formulas set with displaymath are not the same as
+ those set iwth inlinemath.
+ This method raises OSError if specified image doesn't exist or if it got
+ an absolute file_path."""
+ if not pos or not formula or not file_path:
+ raise ValueError("the supplied arguments may not be empty/none")
+ if not isinstance(displaymath, bool):
+ raise ValueError("displaymath must be a boolean")
+ if os.path.isabs(file_path):
+ raise OSError(("The file path to the image may NOT be an absolute "
+ "path: ") + file_path)
+ if '\\' in file_path:
+ file_path = file_path.replace('\\', '/')
+ if not os.path.exists(file_path):
+ raise OSError("cannot add %s to the cache: doesn't exist" %
+ file_path)
+ formula = normalize_formula(formula)
+ if not formula in self.__cache:
+ self.__cache[formula] = {}
+ val = self.__cache[formula]
+ if not displaymath in val:
+ val[displaymath] = {'pos' : pos, 'path' : file_path}
+
+ def remove_formula(self, formula, displaymath):
+ """This method removes the given formula from the cache. A KeyError is
+ raised, if the formula did not exist. Internally, formulas are
+ normalized to detect similarities."""
+ formula = normalize_formula(formula)
+ if not formula in self.__cache:
+ raise KeyError("key %s not in cache" % formula)
+ else:
+ value = self.__cache[formula]
+ if displaymath in value:
+ del self.__cache[formula]
+ else:
+ raise KeyError("key %s (%s) not in cache" % (formula, displaymath))
+
+ def contains(self, formula, displaymath):
+ """Check whether a formula was already cached and return True if
+ found."""
+ try:
+ return bool(self.get_data_for(formula, displaymath))
+ except KeyError:
+ return False
+
+
+ def get_data_for(self, formula, displaymath):
+ """
+ Retrieve meta data about a formula from the cache.
+
+ The meta information is used to embed the formula in the HTML document.
+ It is a dictionary with the keys 'pos' and 'path'. The positioning info
+ is described in the documentation of this class.
+ This method raises a KeyError if the formula wasn't found."""
+ formula = normalize_formula(formula)
+ if not formula in self.__cache:
+ raise KeyError(formula, displaymath)
+ else:
+ # check whether file still exists
+ value = self.__cache[formula]
+ if displaymath in value.keys():
+ if not os.path.exists(value[displaymath]['path']):
+ del self.__cache[formula]
+ raise KeyError((formula, displaymath))
+ else:
+ return value[displaymath]
+ else:
+ raise KeyError((formula, displaymath))
+
diff --git a/support/gladtex/gleetex/htmlhandling.py b/support/gladtex/gleetex/htmlhandling.py
new file mode 100644
index 0000000000..458de3142c
--- /dev/null
+++ b/support/gladtex/gleetex/htmlhandling.py
@@ -0,0 +1,485 @@
+# (c) 2013-2018 Sebastian Humenda
+# This code is licenced under the terms of the LGPL-3+, see the file COPYING for
+# more details.
+"""GleeTeX is designed to allow the re-use of the image creation code
+independently of the HTML conversion code. Therefore, this modules contains the
+code required to parse equations from HTML, to write converted HTML documents
+back and to handle the outsourcing of formulas too long for an HTML alt tag to
+an external file.
+The pandoc module contains similar functions for using GleeTeX as a pandoc
+filter."""
+
+import collections
+import enum
+import html.parser
+import os
+import posixpath
+import re
+
+from . import typesetting
+
+# match HTML 4 and 5
+CHARSET_PATTERN = re.compile(
+ rb'(?:content="text/html; charset=(.*?)"|charset="(.*?)")')
+
+class ParseException(Exception):
+ """Exception to propagate a parsing error."""
+ def __init__(self, msg, pos=None):
+ self.msg = msg
+ self.pos = pos
+ super().__init__(msg, pos)
+
+ def __str__(self):
+ return ('line {0.pos[0]}, {0.pos[1]}: {0.msg}'.format(self)
+ if self.pos else self.msg)
+
+def get_position(document, index):
+ """This returns the line number and position on line for the given String.
+ Note: lines and positions are counted from 0."""
+ line = document[:index+1].count('\n')
+ if document[index] == '\n':
+ return (line, 0)
+ newline = document[:index+1].rfind('\n')
+ newline = (newline if newline >= 0 else 0)
+ return (line, len(document[newline:index]))
+
+
+def find_anycase(where, what):
+ """Find with both lower or upper case."""
+ lower = where.find(what.lower())
+ upper = where.find(what.upper())
+ if lower >= 0:
+ return lower
+ else:
+ return upper
+
+class EqnParser:
+ """This parser parses <eq>...</eq> our of a document. It's not an HTML
+ parser, because the content within <eq>.*<eq> is parsed verbatim.
+ It also parses comments, to not consider formulas within comments. All other
+ cases are unhandled. Especially CData is problematic, although it seems like
+ a rare use case."""
+ class State(enum.Enum): # ([\s\S]*?) also matches newlines
+ Comment = re.compile(r'<!--([\s\S]*?)-->', re.MULTILINE)
+ Equation = re.compile(r'<\s*(?:eq|EQ)\s*(.*?)?>([\s\S.]+?)<\s*/\s*(?:eq|EQ)>',
+ re.MULTILINE)
+
+ HTML_ENTITY = re.compile(r'(&(:?#\d+|[a-zA-Z]+);)')
+
+ def __init__(self):
+ self.__document = None
+ self.__data = []
+ self.__encoding = None
+
+ def feed(self, document):
+ """Feed a string or a bytes instance and start parsing. If a bytes
+ instance is fed, an HTML encoding header has to be present, so that the
+ encoding can be extracted."""
+ if isinstance(document, bytes): # try to guess encoding
+ try:
+ encoding = next(filter(bool, CHARSET_PATTERN.search(document)
+ .groups())).decode('ascii')
+ document = document.decode(encoding)
+ except AttributeError:
+ raise ParseException(("Could not determine encoding of "
+ "document, no charset information in the HTML header "
+ "found."))
+ self.__encoding = encoding
+ self.__document = document[:]
+ self._parse()
+
+ def find_with_offset(self, doc, start, what):
+ """This find method searches in the document for a given string, staking
+ the offset into account. REturned is the absolute position (so offset +
+ relative match position) or -1 for no hit."""
+ if isinstance(what, str):
+ pos = doc[start:].find(what)
+ else:
+ match = what.search(doc[start:])
+ pos = (-1 if not match else match.span()[0])
+ return (pos if pos == -1 else pos + start)
+
+
+ def _parse(self):
+ """This function parses the document, while maintaining state using the
+ State enum."""
+ in_document = lambda x: (False if x == -1 else True)
+ # maintain a lower-case copy, which eases searching, but doesn't affect
+ # the handler methods
+ doc = self.__document[:].lower()
+
+ end = len(self.__document) - 1
+ eq_start = re.compile(r'<\s*eq\s*(.*?)>')
+
+ start_pos = 0
+ while start_pos < end:
+ comment = self.find_with_offset(doc, start_pos, '<!--')
+ formula = self.find_with_offset(doc, start_pos, eq_start)
+ if in_document(comment) and in_document(formula): # both present, take closest
+ if comment < formula:
+ self.__data.append(self.__document[start_pos:comment])
+ start_pos = self.handle_comment(comment)
+ else:
+ self.__data.append(self.__document[start_pos:formula])
+ start_pos = self.handle_equation(formula)
+ elif in_document(formula):
+ self.__data.append(self.__document[start_pos:formula])
+ start_pos = self.handle_equation(formula)
+ elif in_document(comment):
+ self.__data.append(self.__document[start_pos:comment])
+ start_pos = self.handle_comment(comment)
+ else: # only data left
+ self.__data.append(self.__document[start_pos:])
+ start_pos = end
+
+
+ def handle_equation(self, start_pos):
+ """Parse an equation. The given offset should mark the beginning of this
+ equation."""
+ # get line and column of `start_pos`
+ lnum, pos = get_position(self.__document, start_pos)
+
+ match = EqnParser.State.Equation.value.search(self.__document[start_pos:])
+ if not match:
+ next_eq = find_anycase(self.__document[start_pos+1:], '<eq')
+ closing = find_anycase(self.__document[start_pos:], '</eq>')
+ if next_eq > -1 and closing > -1 and next_eq < closing:
+ raise ParseException("Unclosed tag found", (lnum, pos))
+ else:
+ raise ParseException("Malformed equation tag found", (lnum, pos))
+ end = start_pos + match.span()[1]
+ attrs, formula = match.groups()
+ if '<eq>' in formula or '<EQ' in formula:
+ raise ParseException("Invalid nesting of formulas detected.", (lnum,
+ pos))
+
+ # replace HTML entities
+ entity = EqnParser.HTML_ENTITY.search(formula)
+ while entity:
+ formula = re.sub(EqnParser.HTML_ENTITY,
+ html.unescape(entity.groups()[0]), formula)
+ entity = EqnParser.HTML_ENTITY.search(formula)
+ attrs = attrs.lower()
+ displaymath = (True if attrs and 'env' in attrs and 'displaymath' in attrs
+ else False)
+ self.__data.append(((lnum, pos), # let line number count from 0 as well
+ displaymath, formula))
+ return end
+
+
+ def handle_comment(self, start_pos):
+ match = EqnParser.State.Comment.value.search(self.__document[start_pos:])
+ if not match:
+ lnum, pos = get_position(self.__document, start_pos)
+ # this could be a parser issue, too
+ raise ParseException("Improperly formatted comment found", (lnum,
+ pos))
+ self.__data.append('<!--%s-->' % match.groups()[0])
+ return start_pos + match.span()[1] # return end of match
+
+ def get_encoding(self):
+ """Return the parsed encoding from the HTML meta data. If none was set,
+ UTF-8 is assumed."""
+ return self.__encoding
+
+ def get_data(self):
+ """Return parsed chunks. These are either strings or tuples with formula
+ information, see class documentation."""
+ return list(x for x in self.__data if x) # filter empty bits
+
+
+def gen_id(formula):
+ """Generate an id for identifying a formula as an anchor in a document.
+ The generated ID is guaranteed to be valid in an XML attribute and it won't
+ exceed a certain length.
+ If you happen to have a lot of formulas > 150 characters with exactly
+ the same content in the document, that'll cause a clash of id's."""
+ # for some characters we just use a simple replacement (otherwise the
+ # would be lost)
+ mapped = {'{':'_', '}':'_', '(':'-', ')':'-', '\\':'.', '^':',', '*':'_'}
+ id = []
+ prevchar = ''
+ for c in formula:
+ if prevchar == c:
+ continue # avoid multiple same characters
+ if c in mapped:
+ id.append(mapped[c])
+ elif c.isalpha() or c.isdigit():
+ id.append(c)
+ prevchar = c
+ # id's must start with an alphabetical character, so prefix the formula with
+ # "formula" to make it a valid html id
+ if id and not id[0].isalpha():
+ id = ['f', 'o', 'r', 'm', '_'] + id
+ if not id: # is empty
+ raise ValueError("For the formula '%s' no referencable id could be generated." \
+ % formula)
+ return ''.join(id[:150])
+
+
+class OutsourcedFormulaParser(html.parser.HTMLParser):
+ """This HTML parser parses the head and tries to keep it close to the
+ original document as possible. As soon as a formula is encountered, only
+ the formulas are parsed. Everything in between and after the them will be
+ fully ignored.
+ A sample formula would look like:
+
+ <p id="id_as_generated_by_gen_id"><pre>stuff</pre></p>
+ """
+ def __init__(self):
+ self.__head = []
+ self.__id = None
+ self.__passed_head = False
+ self.__equations = collections.OrderedDict()
+ super().__init__(convert_charrefs=False)
+
+ def handle_starttag(self, tag, attrs):
+ if tag == 'p':
+ attrs = dict(attrs)
+ if attrs.get('id'):
+ self.__id = attrs['id'] # marks beginning of a formula paragraph
+ self.__equations[self.__id] = ''
+ return
+ elif tag == 'body':
+ self.__passed_head = True
+ self.__head.append('\n<body>\n')
+ if not self.__passed_head:
+ self.__head.append(self.get_starttag_text())
+
+ def handle_startendtag(self, tag, attrs):
+ if self.__id or self.__passed_head: return # skip everything inside a formula
+ if attrs:
+ self.__head.append('<{} {} />'.format(tag,
+ ' '.join(['%s="%s"' % (x[0], x[1]) for x in attrs])))
+ else:
+ self.__head.append('<%s />' % tag)
+
+ def handle_endtag(self, tag):
+ if self.__id: # inside a formula
+ if tag == 'p':
+ self.__id = None # end formula block
+ elif not self.__passed_head:
+ formatted = '</%s>' % tag
+ self.__head.append(formatted)
+
+ def handle_data(self, data):
+ if self.__id:
+ self.__equations[self.__id] += data
+ elif not self.__passed_head:
+ self.__head.append(data)
+
+ def handle_entityref(self, name):
+ if self.__id:
+ self.__equations[self.__id] += '&%s;' % name
+ elif not self.__passed_head:
+ self.__head.append('&%s;' % name)
+
+ def handle_charref(self, name):
+ if self.__id:
+ self.__equations[self.__id] += '&#%s;' % name
+ elif not self.__passed_head:
+ self.__head.append('&#%s;' % name)
+
+ def handle_comment(self, blah):
+ if not self.__passed_head:
+ self.__head.append('<!--%s-->' % blah)
+
+ def handle_decl(self, declaration):
+ if not self.__passed_head:
+ self.__head.append('<!%s>' % declaration)
+
+
+ def get_head(self):
+ """Return a string containing everything before the first formula."""
+ return ''.join(self.__head)
+
+ def get_formulas(self):
+ """Return an ordered dictionary with id : formula paragraph."""
+ return self.__equations
+
+ def error(self, message):
+ raise ParseException(message, ('unknown', 'unknown'))
+
+def format_formula_paragraph(formula):
+ """Format a formula to appear as if it would have been outsourced into an
+ external file."""
+ return '<p id="%s"><pre>%s</pre></span></p>\n' % \
+ (gen_id(formula), formula)
+
+
+class HtmlImageFormatter: # ToDo: localisation
+ """HtmlImageFormatter(exclusion_filepath='outsourced_formulas.html',
+ encoding="UTF-8")
+ Format converted formula to be included into the HTML. A typical image
+ attribute will contain the path to the image, style information, a CSS class
+ to be used in custom CSS style sheets and an alternative text (the LaTeX
+ source) for people who disabled images or for blind screen reader users.
+ If set, LaTeX formulas exceeding a configurable maximum length will be
+ excluded. The image will be a link which leads to the excluded image text.
+ The alt attribute is a text-only attribute and e.g. line breaks will be lost
+ for screen reader users, so it makes sense for longer formulas to be
+ external to be easily readable. Furthermore the alt attribute is limited to
+ 255 characters, so formula blocks exceeding that limit need to be treated
+ differently anyway. If that behavior is not wanted, it can be disabled and
+ nothing will be excluded."""
+
+ EXCLUSION_FILE_NAME = 'outsourced-descriptions.html'
+ HTML_TEMPLATE_HEAD = ('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"' +
+ '\n "http://www.w3.org/TR/html4/strict.dtd">\n<html>\n<head>\n' +
+ '<meta http-equiv="content-type" content="text/html; charset=utf-8"/>' +
+ '\n<title>Outsourced Formulas</title>\n</head>\n<!-- ' +
+ 'DO NOT MODIFY THIS FILE, IT IS AUTOMATICALLY GENERATED -->\n<body>\n')
+ def __init__(self, base_path='', link_prefix=None):
+ self.__exclude_descriptions = False
+ self.__link_prefix = (link_prefix if link_prefix else '')
+ self.__base_path = (base_path if base_path else '')
+ self.__exclusion_filepath = posixpath.join(self.__base_path, HtmlImageFormatter.EXCLUSION_FILE_NAME)
+ if os.path.exists(self.__exclusion_filepath):
+ if not os.access(self.__exclusion_filepath, os.W_OK):
+ raise OSError('The file %s is not writable!' %
+ self.__exclusion_filepath)
+ self.__inline_maxlength=100
+ self.__file_head = HtmlImageFormatter.HTML_TEMPLATE_HEAD
+ self.__cached_formula_pars = collections.OrderedDict()
+ self.__url = ''
+ self.initialized = False
+ self.initialize() # read already written file, if any
+ self.__css = {'inline' : 'inlinemath', 'display' : 'displaymath'}
+ self.__replace_nonascii = False
+
+ def set_replace_nonascii(self, flag):
+ """If True, non-ascii characters will be replaced through their LaTeX
+ command. Note that alphabetical characters will not be replaced, to
+ allow easier readibility."""
+ self.__replace_nonascii = flag
+
+ def set_max_formula_length(self, length):
+ """Set maximum length of a formula before it gets outsourced into a
+ separate file."""
+ self.__inline_maxlength = length
+
+ def set_inline_math_css_class(self, css):
+ """set css class for inline math."""
+ self.__css['inline'] = css
+
+ def set_display_math_css_class(self, css):
+ """set css class for display math."""
+ self.__css['display'] = css
+
+ def set_exclude_long_formulas(self, flag):
+ """When set, the LaTeX code of a formula longer than the configured
+ maxlength will be excluded and written + linked into a separate file."""
+ self.__exclude_descriptions = flag
+
+ def set_url(self, prefix):
+ """Set URL prefix which is used as a prefix to the image file in the
+ HTML link."""
+ self.__url = prefix
+
+ def initialize(self):
+ """Initialize the image writer. If a file with already written image
+ descriptions exists, this one will be parsed first and new formulas
+ appended to it. Otherwise a new file will be written upon ending the
+ with-resources block."""
+ if self.initialized:
+ return
+ self.initialized = True
+ if not os.path.exists(self.__exclusion_filepath):
+ return self
+ document = None
+ with open(self.__exclusion_filepath, 'r', encoding='UTF-8') as f:
+ document = f.read()
+ # parse html document:
+ parser = OutsourcedFormulaParser()
+ parser.feed(document)
+ self.__file_head = parser.get_head()
+ self.__cached_formula_pars = parser.get_formulas()
+ return self
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, useless, unused, not_applicable):
+ self.close()
+
+ def close(self):
+ """Write back file with excluded image descriptions, if any."""
+ def formula2paragraph(frml):
+ return '<p id="%s"><pre>%s</pre></p>' % (gen_id(frml), frml)
+ if not self.__cached_formula_pars:
+ return
+ with open(self.__exclusion_filepath, 'w', encoding='utf-8') as f:
+ f.write(self.__file_head)
+ f.write('\n<hr />\n'.join([formula2paragraph(formula) \
+ for formula in self.__cached_formula_pars.values()]))
+ f.write('\n</body>\n</html>\n')
+
+ def get_html_img(self, pos, formula, img_path, displaymath=False):
+ """:param pos dictionary containing keys depth, height and width
+ :param formula LaTeX alternative text
+ :param img_path: path to image
+ :param displaymath display or inline math (default False, inline maths)
+ :returns a string with the formatted HTML"""
+ full_url = img_path
+ if self.__url:
+ if self.__url.endswith('/'): self.__url = self.__url[:-1]
+ full_url = self.__url + '/' + img_path
+ # depth is a negative offset
+ depth = float(pos['depth']) * -1
+ css = (self.__css['display'] if displaymath else self.__css['inline'])
+ return ('<img src="{0}" style="vertical-align: {3:.2f}px; margin: 0;" '
+ 'height="{2[height]:.2f}" width="{2[width]:.2f}" alt="{1}" '
+ 'class="{4}" />').format(full_url, formula, pos, depth, css)
+
+ def format_excluded(self, pos, formula, img_path, displaymath=False):
+ """This method formats a formula and an formula image in HTML and
+ additionally writes the formula to an external (configured) file to
+ which the image links to. That's useful for blind screen reader users
+ who can then easily have a look at the formula.
+ :param pos dictionary containing keys depth, height and width
+ :param formula LaTeX alternative text
+ :param img_path: path to image
+ :param displaymath if set to true, image is treated as display math formula (default False)
+ :returns string with formatted HTML image which also links to excluded
+ formula"""
+ shortened = (formula[:100] + '...' if len(formula) > 100 else formula)
+ img = self.get_html_img(pos, shortened, img_path, displaymath)
+ identifier = gen_id(formula)
+ # write formula out to external file
+ if identifier not in self.__cached_formula_pars:
+ self.__cached_formula_pars[identifier] = formula
+ exclusion_filelink = posixpath.join(self.__link_prefix, self.__exclusion_filepath)
+ return '<a href="{}#{}">{}</a>'.format(exclusion_filelink,
+ gen_id(formula), img)
+
+ def format(self, pos, formula, img_path, displaymath=False):
+ """This method formats a formula. If self.__exclude_descriptions is set
+ and the formula igreater than the configured length, the formula will be
+ outsourced, otherwise it'll be included in the IMG's alt tag. In either
+ case, a string for the current document containing the formatted HTML is returned.
+ :param pos dictionary containing keys depth, height and width
+ :param formula LaTeX alternative text
+ :param img_path: path to image
+ :param displaymath whether or not formula is in display math (default: no)
+ :returns string with formatted HTML image which also links to excluded
+ formula"""
+ formula = typesetting.increase_readability(formula,
+ self.__replace_nonascii)
+ if self.__exclude_descriptions and \
+ len(formula) > self.__inline_maxlength:
+ return self.format_excluded(pos, formula, img_path, displaymath)
+ return self.get_html_img(pos, formula, img_path, displaymath)
+
+def write_html(file, document, formatter):
+ """Processed HTML documents are made up of raw HTML chunks which are written
+ back unaltered and of processed image. An processed image is a former
+ formula converted to an image with additional meta data. This is passed to
+ the format function of the supplied formatter and the result is written to
+ the given (open) file handle."""
+ for chunk in document:
+ if isinstance(chunk, dict):
+ is_displaymath = chunk['displaymath']
+ file.write(formatter.format(chunk['pos'], chunk['formula'],
+ chunk['path'], is_displaymath))
+ else:
+ file.write(chunk)
diff --git a/support/gladtex/gleetex/image.py b/support/gladtex/gleetex/image.py
new file mode 100644
index 0000000000..12c92761ad
--- /dev/null
+++ b/support/gladtex/gleetex/image.py
@@ -0,0 +1,293 @@
+# (c) 2013-2018 Sebastian Humenda
+# This code is licenced under the terms of the LGPL-3+, see the file COPYING for
+# more details.
+"""This module takes care of the actual image creation process.
+
+Each formula is saved as an image, either as PNG or SVG. SVG is advised, since
+it is a properly scalable format.
+"""
+
+import enum
+import os
+import re
+import shutil
+import subprocess
+import sys
+
+from .typesetting import LaTeXDocument
+
+DVIPNG_REGEX = re.compile(r"^ depth=(-?\d+) height=(\d+) width=(\d+)")
+DVISVGM_DEPTH_REGEX = re.compile(r"^\s*width=.*?pt, height=.*?pt, depth=(.*?)pt")
+DVISVGM_SIZE_REGEX = re.compile(r"^\s*graphic size: (.*?)pt x (.*?)pt")
+
+def remove_all(*files):
+ """Guarded remove of files (rm -f); no exception is thrown if a file
+ couldn't be removed."""
+ for file in files:
+ try:
+ os.remove(file)
+ except OSError:
+ pass
+
+
+def proc_call(cmd, cwd=None, install_recommends=True):
+ """Execute cmd (list of arguments) as a subprocess. Returned is a tuple with
+ stdout and stderr, decoded if not None. If the return value is not equal 0, a
+ subprocess error is raised. Timeouts will happen after 20 seconds."""
+ with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ bufsize=1, universal_newlines=False, cwd=cwd) as proc:
+ data = []
+ try:
+ data = [d.decode(sys.getdefaultencoding(), errors="surrogateescape")
+ for d in proc.communicate(timeout=20) if d]
+ if proc.wait():
+ raise subprocess.SubprocessError("Error while executing %s\n%s\n" %
+ (' '.join(cmd), '\n'.join(data)))
+ except subprocess.TimeoutExpired as e:
+ proc.kill()
+ note = 'Subprocess expired with time out: ' + str(cmd) + '\n'
+ poll = proc.poll()
+ if poll:
+ note += str(poll) + '\n'
+ if data:
+ raise subprocess.SubprocessError(str(data + '\n' + note))
+ else:
+ raise subprocess.SubprocessError('execution timed out after ' +
+ str(e.args[1]) + ' s: ' + ' '.join(e.args[0]))
+ except KeyboardInterrupt as e:
+ sys.stderr.write("\nInterrupted; ")
+ import traceback
+ traceback.print_exc(file=sys.stderr)
+ except FileNotFoundError:
+ # program missing, try to help
+ text = "Command `%s` not found." % cmd[0]
+ if install_recommends and shutil.which('dpkg'):
+ text += ' Install it using `sudo apt install ' + install_recommends
+ else:
+ text += ' Install a TeX distribution of your choice, e.g. MikTeX or TeXlive.'
+ raise subprocess.SubprocessError(text) from None
+ if isinstance(data, list):
+ return '\n'.join(data)
+ return data
+
+#pylint: disable=too-few-public-methods
+class Format(enum.Enum):
+ """Chose the image output format."""
+ Png = 'png'
+ Svg = 'svg'
+
+class Tex2img:
+ """Convert a TeX document string into a png file.
+ This class interacts with the LaTeX and dvipng sub processes. Upon error
+ the methods throw a SubprocessError with all necessary information to fix
+ the issue.
+
+ The background of the PNG files will be transparent by default. If you set a
+ background colour within the LaTeX document, you need to turn off
+ transparency in this converter manually."""
+ def __init__(self, fmt, encoding="UTF-8"):
+ if not isinstance(fmt, Format):
+ raise ValueError("Enumeration of type Format expected."+str(fmt))
+ self.__format = fmt
+ self.__encoding = encoding
+ self.__parsed_data = None
+ self.__size = [115, None]
+ self.__background = 'transparent'
+ self.__keep_latex_source = False
+
+ def set_dpi(self, dpi):
+ """Set output resolution for formula images. This has no effect ifthe
+ output format is SVG. It will automatically overwrite a font size, if
+ set."""
+ if not isinstance(dpi, (int, float)):
+ raise TypeError("Dpi must be an integer or floating point number")
+ self.__size[0] = int(dpi)
+
+ def set_fontsize(self, size):
+ """Set font size for formulas. This will be automatically translated
+ into a DPI resolution for PNG images and taken literally for SVG
+ graphics."""
+ if not isinstance(size, (int, float)):
+ raise TypeError("Dpi must be an integer or floating point number")
+ self.__size[1] = float(size)
+
+ def set_transparency(self, flag):
+ """Set whether or not to use background colour information from the DVI
+ file. This is only relevant for PNG output and if a background colour
+ other than "transparent" is required, in this case this set'r should be
+ set to false. It is set to True, resulting in a transparent
+ background."""
+ self.__background = ('transparent' if flag else 'not transparent')
+ def set_keep_latex_source(self, flag):
+ """Set whether LaTeX source document should be kept."""
+ if not isinstance(flag, bool):
+ raise TypeError("boolean object required, got %s." % repr(flag))
+ self.__keep_latex_source = flag
+
+
+ def create_dvi(self, tex_document, dvi_fn):
+ """Call LaTeX to produce a dvi file with the given LaTeX document.
+ Temporary files will be removed, even in the case of a LaTeX error.
+ This method raises a SubprocessError with the helpful part of LaTeX's
+ error output."""
+ path = os.path.dirname(dvi_fn)
+ if path and not os.path.exists(path):
+ os.makedirs(path)
+ if not path:
+ path = os.getcwd()
+ new_extension = lambda x: os.path.splitext(dvi_fn)[0] + '.' + x
+
+ if self.__size[1]: # font size in pt
+ tex_document.set_fontsize(self.__size[1])
+ tex_fn = new_extension('tex')
+ aux_fn = new_extension('aux')
+ log_fn = new_extension('log')
+ cmd = None
+ encoding = self.__encoding
+ with open(tex_fn, mode='w', encoding=encoding) as tex:
+ tex.write(str(tex_document))
+ cmd = ['latex', '-halt-on-error', os.path.basename(tex_fn)]
+ try:
+ proc_call(cmd, cwd=path, install_recommends='texlive-recommended')
+ except subprocess.SubprocessError as e:
+ remove_all(dvi_fn)
+ msg = ''
+ if e.args:
+ data = self.parse_latex_log(e.args[0])
+ if data:
+ msg += data
+ else:
+ msg += str(e.args[0])
+ raise subprocess.SubprocessError(msg) # propagate subprocess error
+ finally:
+ if self.__keep_latex_source:
+ remove_all(aux_fn, log_fn)
+ else:
+ remove_all(tex_fn, aux_fn, log_fn)
+
+ def create_image(self, dvi_fn):
+ """Create the image containing the formula, using either dvisvgm or
+ dvipng."""
+ dirname = os.path.dirname(dvi_fn)
+ if dirname and not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ output_fn = '%s.%s' % (os.path.splitext(dvi_fn)[0], self.__format.value)
+ if self.__format == Format.Png:
+ dpi = (fontsize2dpi(self.__size[1]) if self.__size[1]
+ else self.__size[0])
+ return create_png(dvi_fn, output_fn,dpi,
+ self.__background)
+ if not self.__size[1]:
+ self.__size[1] = 12 # 12 pt
+ return create_svg(dvi_fn, output_fn)
+
+ def convert(self, tex_document, base_name):
+ """Convert the given TeX document into an image. The base name is used
+ to create the required intermediate files and the resulting file will be
+ made of the base_name and the format-specific file extension.
+ This function returns the positioning information used in the CSS style
+ attribute."""
+ if not isinstance(tex_document, LaTeXDocument):
+ raise TypeError(("expected object of type typesetting.LaTeXDocument,"
+ " got %s") % type(tex_document))
+ dvi = '%s.dvi' % base_name
+ try:
+ self.create_dvi(tex_document, dvi)
+ return self.create_image(dvi)
+ except OSError:
+ remove_all('%s.%s' % (base_name, self.__format.value))
+ raise
+
+ def parse_latex_log(self, logdata):
+ """Parse the LaTeX error output and return the relevant part of it."""
+ if not logdata:
+ return None
+ line = None
+ for line in logdata.split('\n'):
+ if line.startswith('! '):
+ line = line[2:]
+ break
+ if line: # try to remove LaTeX line numbers
+ lineno = re.search(r'\s*on input line \d+', line)
+ if lineno:
+ line = line[:lineno.span()[0]] + line[lineno.span()[1]:]
+ return line
+ return None
+
+def fontsize2dpi(size_pt):
+ """This function calculates the DPI for the resulting image. Depending on
+ the font size, a different resolution needs to be used. According to the
+ dvipng manual page, the formula is:
+ <dpi> = <font_px> * 72.27 / 10 [px * TeXpt/in / TeXpt]"""
+ size_px = size_pt * 1.3333333 # and more 3s!
+ return size_px * 72.27 / 10
+
+def create_png(dvi_fn, output_name, dpi, background):
+ """Create a PNG file from a given dvi file. The side effect is the PNG file
+ being written to disk.
+ By default, the background of the resulting image is transparent, setting
+ any other value will make it use whatever was is set in the DVI file.
+ :param dvi_fn Dvi file name
+ :param output_name Output file name
+ :param dpi Output resolution
+ :param background Background colour (default: transparent)
+ :return dimensions for embedding into an HTML document
+ :raises ValueError raised whenever dvipng output coudln't be parsed"""
+ if not output_name:
+ raise ValueError("Empty output_name")
+ cmd = ['dvipng', '-q*', '-D', str(dpi)]
+ if background == 'transparent':
+ cmd += ['-bg', background]
+ cmd += ['--height*', '--depth*', '--width*', # print information for embedding
+ '-o', output_name, dvi_fn]
+ data = None
+ try:
+ data = proc_call(cmd, install_recommends='dvipng')
+ except subprocess.SubprocessError:
+ remove_all(output_name)
+ raise
+ finally:
+ remove_all(dvi_fn)
+ for line in data.split('\n'):
+ found = DVIPNG_REGEX.search(line)
+ if found:
+ return dict(zip(['depth', 'height', 'width'],
+ map(float, found.groups())))
+ raise ValueError("Could not parse dvi output: " + repr(data))
+
+def create_svg(dvi_fn, output_name):
+ """Create a SVG file from a given dvi file. The side effect is the SVG file
+ being written to disk.
+ :param dvi_fn Dvi file name
+ :param output_name Output file name
+ :param size font size in pt
+ :return dimensions for embedding into an HTML document
+ :raises ValueError raised whenever dvipng output coudln't be parsed"""
+ if not output_name:
+ raise ValueError("Empty output_name")
+ cmd = ['dvisvgm', '--exact', '--no-fonts', '-o', output_name,
+ '--bbox=preview', dvi_fn]
+ data = None
+ try:
+ data = proc_call(cmd, install_recommends='texlive-binaries')
+ except subprocess.SubprocessError:
+ remove_all(output_name)
+ raise
+ finally:
+ remove_all(dvi_fn)
+ pos = {}
+ for line in data.split('\n'):
+ if not pos:
+ found = DVISVGM_DEPTH_REGEX.search(line)
+ if found:
+ # convert from pt to px (assuming 96 dpi)
+ pos['depth'] = float(found.groups()[0]) * 1.3333333
+ else:
+ found = DVISVGM_SIZE_REGEX.search(line)
+ if found:
+ pos.update(dict(zip(['width', 'height'],
+ # convert from pt to px (assuming 96 dpi)
+ (float(v) * 1.3333333 for v in found.groups()))))
+ return pos
+ raise ValueError("Could not parse dvisvgm output: " + repr(data))
diff --git a/support/gladtex/gleetex/pandoc.py b/support/gladtex/gleetex/pandoc.py
new file mode 100644
index 0000000000..44fa9bbfb9
--- /dev/null
+++ b/support/gladtex/gleetex/pandoc.py
@@ -0,0 +1,75 @@
+# (c) 2013-2018 Sebastian Humenda
+# This code is licenced under the terms of the LGPL-3+, see the file COPYING for
+# more details.
+"""This module contains functionality to parse formulas from a given Pandoc
+document AST and to replace these through formatted HTML equations. Even
+though this could be done in a single run, this would conflict with the internal
+GleeTeX structure and allows for an easy parallelisation of the formula
+conversion."""
+
+import json
+
+from .htmlhandling import ParseException
+
+def __extract_formulas(formulas, ast):
+ """Recursively extract 'Math' elements from the given AST and add them to
+ `formulas (list)`."""
+ if isinstance(ast, list):
+ for item in ast:
+ __extract_formulas(formulas, item)
+ elif isinstance(ast, dict):
+ if 't' in ast and ast['t'] == 'Math':
+ style, formula = ast['c']
+ # style = {'t': 'blah'} -> we want blah
+ style = next(iter(style.values()))
+ if style not in ['InlineMath', 'DisplayMath']:
+ raise ParseException("[pandoc] unknown formula formatting: " + \
+ repr(ast['c']))
+ style = (True if style == 'DisplayMath' else False)
+ # position is None (only applicable for HTML parsing)
+ formulas.append((None, style, formula))
+ elif 'c' in ast:
+ __extract_formulas(formulas, ast['c'])
+ # ^ all other cases do not matter
+
+def extract_formulas(ast):
+ """Extract formulas from a given Pandoc document AST.
+ The returned formulas are typed like those form the HTML parser, therefore
+ the first argument of the tuple is unused and hence None.
+ :param ast Structure of lists and dicts representing a Pandoc document AST
+ :return a list of formulas where each formula is (None, style, formula)"""
+ formulas = []
+ __extract_formulas(formulas, ast['blocks'])
+ return formulas
+
+def replace_formulas_in_ast(formatter, ast, formulas):
+ """replace 'Math' elements from the given AST with a formatted variant
+ Each 'Math' element found in the Pandoc AST will be replaced through a
+ formatted (HTML) image link. The formulas are taken from the supplied
+ formulas list. The number of formulas in the document has to match the
+ number of formulas form the list."""
+ if not formulas:
+ return
+ if isinstance(ast, list):
+ for item in ast:
+ replace_formulas_in_ast(formatter, item, formulas)
+ elif isinstance(ast, dict):
+ if 't' in ast and ast['t'] == 'Math':
+ ast['t'] = 'RawInline' # raw HTML
+ eqn = formulas.pop(0)
+ ast['c'] = ['html',formatter.format(eqn['pos'], eqn['formula'], eqn['path'],
+ eqn['displaymath'])]
+ elif 'c' in ast:
+ replace_formulas_in_ast(formatter, ast['c'], formulas)
+ # ^ ignore all other cases
+
+def write_pandoc_ast(file, document, formatter):
+ """Replace 'Math' elements from a Pandoc AST with 'RawInline' elements,
+ containing formatted HTML image tags.
+ :param formatter A formatter offering the "format" method (see ImageFormatter)
+ :param formulas A list of formulas with the information (pos, formula, path, displaymath)
+ :param ast Document ast to modified"""
+ ast, formulas = document
+ replace_formulas_in_ast(formatter, ast['blocks'], formulas)
+ file.write(json.dumps(ast))
+
diff --git a/support/gladtex/gleetex/parser.py b/support/gladtex/gleetex/parser.py
new file mode 100644
index 0000000000..dba08451ed
--- /dev/null
+++ b/support/gladtex/gleetex/parser.py
@@ -0,0 +1,64 @@
+# (c) 2013-2018 Sebastian Humenda
+# This code is licenced under the terms of the LGPL-3+, see the file COPYING for
+# more details.
+"""Top-level API to parse input documents.
+
+The main point of the parsing is to extract formulas from a given input
+document, while preserving the remaining formatting.
+The returned parsed document structure is highly dependent on the input format
+and hence document in their respective functions."""
+
+import enum
+import json
+import sys
+
+from . import htmlhandling
+from . import pandoc
+ParseException = htmlhandling.ParseException # re-export for consistent API from outside
+
+class Format(enum.Enum):
+ HTML = 0
+ # while this is json, we never know what other applications might decide to
+ # use json as their intermediate representation ;)
+ PANDOCFILTER = 1
+
+ @staticmethod
+ def parse(string):
+ string = string.lower()
+ if string == 'html':
+ return Format.HTML
+ elif string == 'pandocfilter':
+ return Format.PANDOCFILTER
+ else:
+ raise ValueError("unrecognised format: %s" % string)
+
+def parse_document(doc, fmt):
+ """This function parses an input document (string or bytes) with the given
+ format specifier. For HTML, the returned "parsed" document is a list of
+ chunks, where raw chunks are just plain HTML instructions and data and
+ formula chunks are parsed from the '<eq/>' tags.
+ If the input document is a pandoc AST, the formulas will be extracted and
+ the document is a tuple of (pandoc AST, formulas).
+
+ :param doc input of bytes or string to parse
+ :param fmt either the enum type `Format` or a string understood by Format.parse
+ :return (encoding, document) (a tuple)"""
+ if isinstance(fmt, str):
+ fmt = Format.parse(fmt)
+ encoding = None
+ if fmt == Format.HTML:
+ docparser = htmlhandling.EqnParser()
+ docparser.feed(doc)
+ encoding = docparser.get_encoding()
+ encoding = (encoding if encoding else 'utf-8')
+ doc = docparser.get_data()
+ elif fmt == Format.PANDOCFILTER:
+ if isinstance(doc, bytes):
+ doc = doc.decode(sys.getdefaultencoding())
+ ast = json.loads(doc)
+ formulas = pandoc.extract_formulas(ast)
+ doc = (ast, formulas) # ← see doc string
+ if not encoding:
+ encoding = sys.getdefaultencoding()
+ return encoding, doc
+
diff --git a/support/gladtex/gleetex/typesetting.py b/support/gladtex/gleetex/typesetting.py
new file mode 100644
index 0000000000..c55cab5b12
--- /dev/null
+++ b/support/gladtex/gleetex/typesetting.py
@@ -0,0 +1,331 @@
+# (c) 2013-2018 Sebastian Humenda
+# This code is licenced under the terms of the LGPL-3+, see the file COPYING for
+# more details.
+"""This module contains functionality to typeset formuas for the usage in a
+LaTeX document (e.g. creating the preamble, replacing non-ascii letters) and to
+typeset LaTeX formulas in a more readable way as alternate description of the
+resulting image. """
+
+from . import unicode
+
+FORMATTING_COMMANDS = ['\\ ', '\\,', '\\;', '\\big', '\\Big', '\\left',
+ '\\right', '\\limits']
+
+
+
+class DocumentSerializationException(Exception):
+ """This error is raised whenever a non-ascii character contained in a
+ formula could not be replaced by a LaTeX command.
+ It provides the following attributes:
+ formula - the formula
+ index - position in formula
+ upoint - unicode point."""
+ def __init__(self, formula, index, upoint):
+ self.formula = formula
+ self.index = index
+ self.upoint = upoint
+ super().__init__(formula, index, upoint)
+
+ def __str__(self):
+ return ("could not find LaTeX replacement command for unicode "
+ "character %d, index %d in formula %s") % (self.upoint,
+ self.index, self.formula)
+
+
+def escape_unicode_maths(formula, replace_alphabeticals=True):
+ """This function uses the unicode table to replace any non-ascii character
+ (identified with its unicode code point) with a LaTeX command.
+ It also parses the formula for commands as e.g. \\\text or \\mbox and
+ applies text-mode commands within them.
+ This allows the conversion of formulas with unicode maths with old-style
+ LaTeX2e, which gleetex depends on."""
+ if not any(ord(ch) > 160 for ch in formula):
+ return formula # no umlauts, no replacement
+
+ # characters in math mode need a different replacement than in text mode.
+ # Therefore, the string has to be split into parts of math and text mode.
+ chunks = []
+ if not ('\\text' in formula or '\\mbox' in formula):
+ # no text mode, so tread a
+ chunks = [formula]
+ else:
+ start = 0
+ while '\\text' in formula[start:] or '\\mbox' in formula[start:]:
+ index = formula[start:].find('\\text')
+ if index < 0:
+ index = formula[start:].find('\\mbox')
+ opening_brace = formula[start + index:].find('{') + start + index
+ # add text before text-alike command and the command itself to chunks
+ chunks.append(formula[start:opening_brace])
+ closing_brace = get_matching_brace(formula, opening_brace)
+ # add text-mode stuff
+ chunks.append(formula[opening_brace:closing_brace + 1])
+ start = closing_brace + 1
+ # add last chunk
+ chunks.append(formula[start:])
+
+ is_math = True
+ for index, chunk in enumerate(chunks):
+ try:
+ chunks[index] = replace_unicode_characters(chunk, is_math,
+ replace_alphabeticals=replace_alphabeticals)
+ except ValueError as e: # unicode point missing
+ index = int(e.args[0])
+ raise DocumentSerializationException(formula, index,
+ ord(formula[index])) from None
+ is_math = not is_math
+ return ''.join(chunks)
+
+
+def replace_unicode_characters(characters, is_math, replace_alphabeticals=True):
+ """Replace all non-ascii characters within the given string with their LaTeX
+ equivalent. The boolean is_math indicates, whether text-mode commands (like
+ in \\text{}) or the amsmath equivalents should be used.
+ When replace_alphabeticals is False, alphabetical characters will not be
+ replaced through their LaTeX command when in text mode, so that text within
+ \\text{} (and similar) is not garbled. For instance, \\text{für} is be
+ replaced by \\text{f\"{u}r} when replace_alphabeticals=True. This is useful
+ for the alt attribute of an image, where the reader might want to read
+ the normal text as such.
+ This function raises a ValueError if a unicode point is not in the table.
+ The first argument of the ValueError is the index within the string, where
+ the unknown unicode character has been encountered."""
+ result = []
+ for idx, character in enumerate(characters):
+ if ord(character) < 168: # ignore normal ascii character and unicode control sequences
+ result.append(character)
+ # treat alphanumerical characters differently when in text mode, see doc
+ # string; don't replace alphabeticals if specified
+ elif character.isalpha() and not replace_alphabeticals:
+ result.append(character)
+ else:
+ mode = (unicode.LaTeXMode.mathmode if is_math else
+ unicode.LaTeXMode.textmode)
+ commands = unicode.unicode_table.get(ord(character))
+ if not commands: # unicode point missing in table
+ # is catched one level above; provide index for more concise error output
+ raise ValueError(characters.index(character))
+ # if math mode and only a text alternative exists, add \\text{}
+ # around it
+ if mode == unicode.LaTeXMode.mathmode and mode not in commands:
+ result.append('\\text{%s}' % commands[unicode.LaTeXMode.textmode])
+ else:
+ result.append(commands[mode])
+ # if the next character is alphabetical, add space
+ if (idx+1) < len(characters) and characters[idx+1].isalpha() \
+ and commands[mode][-1].isalpha():
+ result.append(' ')
+ return ''.join(result)
+
+def get_matching_brace(string, pos_of_opening_brace):
+ if string[pos_of_opening_brace] != '{':
+ raise ValueError("index %s in string %s: not a opening brace" % \
+ (pos_of_opening_brace, repr(string)))
+ counter = 1
+ for index, ch in enumerate(string[pos_of_opening_brace + 1:]):
+ if ch == '{':
+ counter += 1
+ elif ch == '}':
+ counter -= 1
+ if counter == 0:
+ return pos_of_opening_brace + index + 1
+ if counter != 0:
+ raise ValueError("Unbalanced braces in formula " + repr(string))
+
+
+
+#pylint: disable=too-many-instance-attributes
+class LaTeXDocument:
+ """This class represents a LaTeX document. It is intended to contain an
+ equation as main content and properties to customize it. Its main purpose is
+ to provide a str method which will serialize it to a full LaTeX document.
+ """
+ def __init__(self, eqn):
+ self.__encoding = None
+ self.__equation = eqn
+ self.__displaymath = False
+ self.__fontsize = 12
+ self.__background_color = None
+ self.__foreground_color = None
+ self._preamble = ''
+ self.__maths_env = None
+ self.__replace_nonascii = False
+
+ def _parse_color(self, color):
+ # could be a valid color name
+ try: # hex number?
+ return int(color, 16)
+ except ValueError:
+ return color # treat as normal dvips compatible colour name
+
+ def set_background_color(self, color):
+ """Set the background color. The `color` can be either a valid dvips
+ name or a tuple with RGB values between 0 and 1. If unset, the image
+ will be transparent."""
+ self.__background_color = self._parse_color(color)
+
+ def set_foreground_color(self, color):
+ """Set the foreground color. The `color` can be either a valid dvips
+ name or a tuple with RGB values between 0 and 1. If unset, the text
+ will be black."""
+ self.__foreground_color = self._parse_color(color)
+
+ def set_replace_nonascii(self, flag):
+ """If True, all non-ascii character will be replaced through a LaTeX
+ command."""
+ self.__replace_nonascii = flag
+
+ def set_latex_environment(self, env):
+ """Set maths environment name like `displaymath` or `flalign*`."""
+ self.__maths_env = env
+
+ def get_latex_environment(self):
+ return self.__maths_env
+
+ def get_encoding(self):
+ """Return encoding for the document (or None)."""
+ return self.__encoding
+
+ def set_preamble_string(self, p):
+ """Set the string to add to the preamble of the LaTeX document."""
+ self._preamble = p
+
+ def set_encoding(self, encoding):
+ """Set the encoding as used by the inputenc package."""
+ if encoding.lower().startswith('utf') and '8' in encoding:
+ self.__encoding = 'utf8'
+ elif (encoding.lower().startswith('iso') and '8859' in encoding) or \
+ encoding.lower() == 'latin1':
+ self.__encoding = 'latin1'
+ else:
+ # if you plan to add an encoding, you have to adjust the str
+ # function, which also loads the fontenc package
+ raise ValueError(("Encoding %s is not supported at the moment. If "
+ "you want to use LaTeX 2e, you should report a bug at the home "
+ "page of GladTeX.") % encoding)
+
+ def set_displaymath(self, flag):
+ """Set whether the formula is set in displaymath."""
+ if not isinstance(flag, bool):
+ raise TypeError("Displaymath parameter must be of type bool.")
+ self.__displaymath = flag
+
+ def is_displaymath(self):
+ return self.__displaymath
+
+ def _get_encoding_preamble(self):
+ # first check whether there are umlauts within the formula and if so, an
+ # encoding has been set
+ if any(ord(ch) > 128 for ch in self.__equation) and \
+ not self.__replace_nonascii:
+ if not self.__encoding:
+ raise ValueError(("No encoding set, but non-ascii characters "
+ "present. Please specify an encoding."))
+ encoding_preamble = ''
+ if self.__encoding:
+ # try to guess language and hence character set (fontenc)
+ import locale
+ language = locale.getdefaultlocale()
+ if language and language[0]: # extract just the language code
+ language = language[0].split('_')[0]
+ if not language or not language[0]:
+ language = 'en'
+ # check whether language on computer is within T1 and hence whether
+ # it should be loaded; I know that this can be a misleading
+ # assumption, but there's no better way that I know of
+ if language in ['fr', 'es', 'it', 'de', 'nl', 'ro', 'en']:
+ encoding_preamble += '\n\\usepackage[T1]{fontenc}'
+ else:
+ raise ValueError(("Language not supported by T1 fontenc "
+ "encoding; please report this to the GladTeX project."))
+ return encoding_preamble
+
+ def set_fontsize(self, size_in_pt):
+ """Set fontsize in pt, 12 pt by default."""
+ self.__fontsize = size_in_pt
+
+ def get_fontsize(self, size_in_pt):
+ return self.__fontsize
+
+ def __str__(self):
+ preamble = self._get_encoding_preamble() + \
+ ('\n\\usepackage[utf8]{inputenc}\n\\usepackage{amsmath, amssymb}'
+ '\n') + (self._preamble if self._preamble else '')
+ return self._format_document(preamble)
+
+ def _format_color_definition(self, which):
+ color = getattr(self, '_%s__%s_color' % (self.__class__.__name__,
+ which))
+ if not color or isinstance(color, str):
+ return ''
+ return ('\\definecolor{%s}{HTML}{%s}' % (which,
+ hex(color)[2:].upper().zfill(6)))
+
+ def _format_colors(self):
+ color_defs = (self._format_color_definition('background'),
+ self._format_color_definition('foreground'),)
+ color_body = ''
+ if self.__background_color:
+ color_body += ('\\pagecolor{%s}' % ('background' if color_defs[0]
+ else self.__background_color))
+ if self.__foreground_color:
+ # opening brace isn't required here, inserted automatically
+ color_body += ('\\color{%s}' % ('foreground' if color_defs[1]
+ else self.__foreground_color))
+ return (''.join(color_defs), color_body)
+
+
+ def _format_document(self, preamble):
+ """Return a formatted LaTeX document with the specified formula
+ embedded."""
+ opening, closing = None,None
+ if self.__maths_env:
+ opening = '\\begin{%s}' % self.__maths_env
+ closing = '\\end{%s}' % self.__maths_env
+ else:
+ # determine characters with which to surround the formula
+ opening = '\\[' if self.__displaymath else '\\('
+ closing = '\\]' if self.__displaymath else '\\)'
+ formula = self.__equation.lstrip().rstrip()
+ if self.__replace_nonascii:
+ formula = escape_unicode_maths(formula, replace_alphabeticals=True)
+ fontsize = 'fontsize=%ipt' % self.__fontsize
+ color_preamble, color_body = self._format_colors()
+ return ("\\documentclass[%s, fleqn]{scrartcl}\n\n%s\n"
+ "\\usepackage[dvipsnames]{xcolor}\n"
+ "%s\n" # color definitions, if applicable
+ "\\usepackage[active,textmath,displaymath,tightpage]{preview} "
+ "%% must be last one, see doc\n\n\\begin{document}\n"
+ "\\noindent%%\n"
+ "\\begin{preview}{%s"
+ "%s%s%s}\\end{preview}\n"
+ "\\end{document}\n") % (fontsize, preamble, color_preamble,
+ color_body, opening, formula, closing)
+
+
+def increase_readability(formula, replace_nonascii=False):
+ """In alternate texts for non-image users or those using a screen reader,
+ the LaTeX code should be as readable as possible. Therefore the formula
+ should not contain unicode characters or formatting instructions."""
+ if replace_nonascii:
+ # keep umlauts, etc; makes the alt more readable, yet wouldn't compile
+ formula = escape_unicode_maths(formula, replace_alphabeticals=False)
+ # replace formatting-only symbols which distract the reader
+ formula_changed = True
+ while formula_changed:
+ formula_changed = False
+ for command in FORMATTING_COMMANDS:
+ idx = formula.find(command)
+ # only replace if it's not after a \\ and not part of a longer command
+ if (idx > 0 and formula[idx-1] != '\\') or idx == 0:
+ end = idx + len(command)
+ # following conditions for replacement must be met:
+ # command doesn't end on alphabet. char. and is followed by same
+ # category OR end of string reached OR command does not # end on
+ # alphabetical char. at all
+ if end >= len(formula) or not command[-1].isalpha() \
+ or not formula[end].isalpha():
+ formula = formula[:idx] + ' ' + formula[idx + len(command):]
+ formula = formula.replace(' ', ' ')
+ formula_changed = True
+ return formula
diff --git a/support/gladtex/gleetex/unicode.py b/support/gladtex/gleetex/unicode.py
new file mode 100644
index 0000000000..f6a6fbd8f8
--- /dev/null
+++ b/support/gladtex/gleetex/unicode.py
@@ -0,0 +1,2390 @@
+"""
+DO NOT ALTER THIS FILE IN ANY WAY, IT IS GENERATED AUTOMATICALLY. SEE THE SCRIPT
+`update_unicode_table.py` FOR MORE INFORMATION.
+
+This file contains a table of unicode code point to LaTeX command mapping. It
+has 2369 entries and was derived from
+<https://raw.githubusercontent.com/w3c/xml-entities/gh-pages/unicode.xml>."""
+#pylint: disable=too-many-lines,missing-docstring
+
+
+import enum
+
+class LaTeXMode(enum.Enum):
+ """Represent either math or text mode. Math mode in LaTeX is e.g.
+ everything between $ and $."""
+ mathmode = 1
+ textmode = 0
+
+unicode_table = {
+ 161: {LaTeXMode.textmode: '\\textexclamdown ', LaTeXMode.mathmode: '\\textexclamdown'},
+ 162: {LaTeXMode.textmode: '\\textcent ', LaTeXMode.mathmode: '\\mbox{\\textcent}'},
+ 163: {LaTeXMode.textmode: '\\textsterling ', LaTeXMode.mathmode: '\\sterling'},
+ 164: {LaTeXMode.textmode: '\\textcurrency ', LaTeXMode.mathmode: '\\mbox{\\textcurrency}'},
+ 165: {LaTeXMode.textmode: '\\textyen ', LaTeXMode.mathmode: '\\yen'},
+ 166: {LaTeXMode.textmode: '\\textbrokenbar '},
+ 167: {LaTeXMode.textmode: '\\textsection ', LaTeXMode.mathmode: '\\S'},
+ 168: {LaTeXMode.textmode: '\\textasciidieresis '},
+ 169: {LaTeXMode.textmode: '\\textcopyright ', LaTeXMode.mathmode: '\\copyright'},
+ 170: {LaTeXMode.textmode: '\\textordfeminine '},
+ 171: {LaTeXMode.textmode: '\\guillemotleft ', LaTeXMode.mathmode: '\\mbox{\\guillemotleft}'},
+ 172: {LaTeXMode.textmode: '\\lnot ', LaTeXMode.mathmode: '\\neg'},
+ 173: {LaTeXMode.textmode: '\\-'},
+ 174: {LaTeXMode.textmode: '\\textregistered ', LaTeXMode.mathmode: '\\circledR'},
+ 175: {LaTeXMode.textmode: '\\textasciimacron '},
+ 176: {LaTeXMode.textmode: '\\textdegree ', LaTeXMode.mathmode: '\\degree'},
+ 177: {LaTeXMode.textmode: '\\pm ', LaTeXMode.mathmode: '\\pm'},
+ 178: {LaTeXMode.textmode: '{^2}', LaTeXMode.mathmode: '^2'},
+ 179: {LaTeXMode.textmode: '{^3}', LaTeXMode.mathmode: '^3'},
+ 180: {LaTeXMode.textmode: '\\textasciiacute ', LaTeXMode.mathmode: '\\textasciiacute'},
+ 181: {LaTeXMode.textmode: '\\mathrm{\\mu}', LaTeXMode.mathmode: '\\mu'},
+ 182: {LaTeXMode.textmode: '\\textparagraph ', LaTeXMode.mathmode: '\\P'},
+ 183: {LaTeXMode.textmode: '\\cdot ', LaTeXMode.mathmode: '\\cdotp'},
+ 184: {LaTeXMode.textmode: '\\c{}', LaTeXMode.mathmode: '\\mbox{\\c{}}'},
+ 185: {LaTeXMode.textmode: '{^1}', LaTeXMode.mathmode: '^1'},
+ 186: {LaTeXMode.textmode: '\\textordmasculine '},
+ 187: {LaTeXMode.textmode: '\\guillemotright ', LaTeXMode.mathmode: '\\mbox{\\guillemotright}'},
+ 188: {LaTeXMode.textmode: '\\textonequarter '},
+ 189: {LaTeXMode.textmode: '\\textonehalf '},
+ 190: {LaTeXMode.textmode: '\\textthreequarters '},
+ 191: {LaTeXMode.textmode: '\\textquestiondown ', LaTeXMode.mathmode: '\\textquestiondown'},
+ 192: {LaTeXMode.textmode: '\\`{A}', LaTeXMode.mathmode: '\\grave{A}'},
+ 193: {LaTeXMode.textmode: "\\'{A}", LaTeXMode.mathmode: '\\acute{A}'},
+ 194: {LaTeXMode.textmode: '\\^{A}', LaTeXMode.mathmode: '\\hat{A}'},
+ 195: {LaTeXMode.textmode: '\\~{A}', LaTeXMode.mathmode: '\\tilde{A}'},
+ 196: {LaTeXMode.textmode: '\\"{A}', LaTeXMode.mathmode: '\\ddot{A}'},
+ 197: {LaTeXMode.textmode: '\\AA '},
+ 198: {LaTeXMode.textmode: '\\AE ', LaTeXMode.mathmode: '\\AE'},
+ 199: {LaTeXMode.textmode: '\\c{C}', LaTeXMode.mathmode: '\\mbox{\\c{C}}'},
+ 200: {LaTeXMode.textmode: '\\`{E}', LaTeXMode.mathmode: '\\grave{E}'},
+ 201: {LaTeXMode.textmode: "\\'{E}", LaTeXMode.mathmode: '\\acute{E}'},
+ 202: {LaTeXMode.textmode: '\\^{E}', LaTeXMode.mathmode: '\\hat{E}'},
+ 203: {LaTeXMode.textmode: '\\"{E}', LaTeXMode.mathmode: '\\ddot{E}'},
+ 204: {LaTeXMode.textmode: '\\`{I}', LaTeXMode.mathmode: '\\grave{I}'},
+ 205: {LaTeXMode.textmode: "\\'{I}", LaTeXMode.mathmode: '\\acute{I}'},
+ 206: {LaTeXMode.textmode: '\\^{I}', LaTeXMode.mathmode: '\\hat{I}'},
+ 207: {LaTeXMode.textmode: '\\"{I}', LaTeXMode.mathmode: '\\ddot{I}'},
+ 208: {LaTeXMode.textmode: '\\DH '},
+ 209: {LaTeXMode.textmode: '\\~{N}', LaTeXMode.mathmode: '\\tilde{N}'},
+ 210: {LaTeXMode.textmode: '\\`{O}', LaTeXMode.mathmode: '\\grave{O}'},
+ 211: {LaTeXMode.textmode: "\\'{O}", LaTeXMode.mathmode: '\\acute{O}'},
+ 212: {LaTeXMode.textmode: '\\^{O}', LaTeXMode.mathmode: '\\hat{O}'},
+ 213: {LaTeXMode.textmode: '\\~{O}', LaTeXMode.mathmode: '\\tilde{O}'},
+ 214: {LaTeXMode.textmode: '\\"{O}', LaTeXMode.mathmode: '\\ddot{O}'},
+ 215: {LaTeXMode.textmode: '\\texttimes ', LaTeXMode.mathmode: '\\times'},
+ 216: {LaTeXMode.textmode: '\\O ', LaTeXMode.mathmode: '\\O'},
+ 217: {LaTeXMode.textmode: '\\`{U}', LaTeXMode.mathmode: '\\grave{U}'},
+ 218: {LaTeXMode.textmode: "\\'{U}", LaTeXMode.mathmode: '\\acute{U}'},
+ 219: {LaTeXMode.textmode: '\\^{U}', LaTeXMode.mathmode: '\\hat{U}'},
+ 220: {LaTeXMode.textmode: '\\"{U}', LaTeXMode.mathmode: '\\ddot{U}'},
+ 221: {LaTeXMode.textmode: "\\'{Y}", LaTeXMode.mathmode: '\\acute{Y}'},
+ 222: {LaTeXMode.textmode: '\\TH '},
+ 223: {LaTeXMode.textmode: '\\ss ', LaTeXMode.mathmode: '\\ss'},
+ 224: {LaTeXMode.textmode: '\\`{a}', LaTeXMode.mathmode: '\\grave{a}'},
+ 225: {LaTeXMode.textmode: "\\'{a}", LaTeXMode.mathmode: '\\acute{a}'},
+ 226: {LaTeXMode.textmode: '\\^{a}', LaTeXMode.mathmode: '\\hat{a}'},
+ 227: {LaTeXMode.textmode: '\\~{a}', LaTeXMode.mathmode: '\\tilde{a}'},
+ 228: {LaTeXMode.textmode: '\\"{a}', LaTeXMode.mathmode: '\\ddot{a}'},
+ 229: {LaTeXMode.textmode: '\\aa '},
+ 230: {LaTeXMode.textmode: '\\ae ', LaTeXMode.mathmode: '\\ae'},
+ 231: {LaTeXMode.textmode: '\\c{c}', LaTeXMode.mathmode: '\\mbox{\\c{c}}'},
+ 232: {LaTeXMode.textmode: '\\`{e}', LaTeXMode.mathmode: '\\grave{e}'},
+ 233: {LaTeXMode.textmode: "\\'{e}", LaTeXMode.mathmode: '\\acute{e}'},
+ 234: {LaTeXMode.textmode: '\\^{e}', LaTeXMode.mathmode: '\\hat{e}'},
+ 235: {LaTeXMode.textmode: '\\"{e}', LaTeXMode.mathmode: '\\ddot{e}'},
+ 236: {LaTeXMode.textmode: '\\`{\\i}', LaTeXMode.mathmode: '\\grave{\\imath}'},
+ 237: {LaTeXMode.textmode: "\\'{\\i}", LaTeXMode.mathmode: '\\acute{\\imath}'},
+ 238: {LaTeXMode.textmode: '\\^{\\i}', LaTeXMode.mathmode: '\\hat{\\imath}'},
+ 239: {LaTeXMode.textmode: '\\"{\\i}', LaTeXMode.mathmode: '\\ddot{\\imath}'},
+ 240: {LaTeXMode.textmode: '\\dh ', LaTeXMode.mathmode: '\\eth'},
+ 241: {LaTeXMode.textmode: '\\~{n}', LaTeXMode.mathmode: '\\tilde{n}'},
+ 242: {LaTeXMode.textmode: '\\`{o}', LaTeXMode.mathmode: '\\grave{o}'},
+ 243: {LaTeXMode.textmode: "\\'{o}", LaTeXMode.mathmode: '\\acute{o}'},
+ 244: {LaTeXMode.textmode: '\\^{o}', LaTeXMode.mathmode: '\\hat{o}'},
+ 245: {LaTeXMode.textmode: '\\~{o}', LaTeXMode.mathmode: '\\tilde{o}'},
+ 246: {LaTeXMode.textmode: '\\"{o}', LaTeXMode.mathmode: '\\ddot{o}'},
+ 247: {LaTeXMode.textmode: '\\div ', LaTeXMode.mathmode: '\\div'},
+ 248: {LaTeXMode.textmode: '\\o ', LaTeXMode.mathmode: '\\o'},
+ 249: {LaTeXMode.textmode: '\\`{u}', LaTeXMode.mathmode: '\\grave{u}'},
+ 250: {LaTeXMode.textmode: "\\'{u}", LaTeXMode.mathmode: '\\acute{u}'},
+ 251: {LaTeXMode.textmode: '\\^{u}', LaTeXMode.mathmode: '\\hat{u}'},
+ 252: {LaTeXMode.textmode: '\\"{u}', LaTeXMode.mathmode: '\\ddot{u}'},
+ 253: {LaTeXMode.textmode: "\\'{y}", LaTeXMode.mathmode: '\\acute{y}'},
+ 254: {LaTeXMode.textmode: '\\th '},
+ 255: {LaTeXMode.textmode: '\\"{y}', LaTeXMode.mathmode: '\\ddot{y}'},
+ 256: {LaTeXMode.textmode: '\\={A}', LaTeXMode.mathmode: '\\bar{A}'},
+ 257: {LaTeXMode.textmode: '\\={a}', LaTeXMode.mathmode: '\\bar{a}'},
+ 258: {LaTeXMode.textmode: '\\u{A}', LaTeXMode.mathmode: '\\breve{A}'},
+ 259: {LaTeXMode.textmode: '\\u{a}', LaTeXMode.mathmode: '\\u{a}'},
+ 260: {LaTeXMode.textmode: '\\k{A}'},
+ 261: {LaTeXMode.textmode: '\\k{a}'},
+ 262: {LaTeXMode.textmode: "\\'{C}", LaTeXMode.mathmode: '\\acute{C}'},
+ 263: {LaTeXMode.textmode: "\\'{c}", LaTeXMode.mathmode: '\\acute{c}'},
+ 264: {LaTeXMode.textmode: '\\^{C}', LaTeXMode.mathmode: '\\hat{C}'},
+ 265: {LaTeXMode.textmode: '\\^{c}', LaTeXMode.mathmode: '\\hat{c}'},
+ 266: {LaTeXMode.textmode: '\\.{C}', LaTeXMode.mathmode: '\\dot{C}'},
+ 267: {LaTeXMode.textmode: '\\.{c}', LaTeXMode.mathmode: '\\dot{c}'},
+ 268: {LaTeXMode.textmode: '\\v{C}', LaTeXMode.mathmode: '\\check{C}'},
+ 269: {LaTeXMode.textmode: '\\v{c}', LaTeXMode.mathmode: '\\check{c}'},
+ 270: {LaTeXMode.textmode: '\\v{D}', LaTeXMode.mathmode: '\\check{D}'},
+ 271: {LaTeXMode.textmode: '\\v{d}', LaTeXMode.mathmode: '\\check{d}'},
+ 272: {LaTeXMode.textmode: '\\DJ '},
+ 273: {LaTeXMode.textmode: '\\dj '},
+ 274: {LaTeXMode.textmode: '\\={E}', LaTeXMode.mathmode: '\\bar{E}'},
+ 275: {LaTeXMode.textmode: '\\={e}', LaTeXMode.mathmode: '\\bar{e}'},
+ 276: {LaTeXMode.textmode: '\\u{E}', LaTeXMode.mathmode: '\\breve{E}'},
+ 277: {LaTeXMode.textmode: '\\u{e}', LaTeXMode.mathmode: '\\breve{e}'},
+ 278: {LaTeXMode.textmode: '\\.{E}', LaTeXMode.mathmode: '\\dot{E}'},
+ 279: {LaTeXMode.textmode: '\\.{e}', LaTeXMode.mathmode: '\\dot{e}'},
+ 280: {LaTeXMode.textmode: '\\k{E}', LaTeXMode.mathmode: '\\k{E}'},
+ 281: {LaTeXMode.textmode: '\\k{e}'},
+ 282: {LaTeXMode.textmode: '\\v{E}', LaTeXMode.mathmode: '\\check{E}'},
+ 283: {LaTeXMode.textmode: '\\v{e}', LaTeXMode.mathmode: '\\check{e}'},
+ 284: {LaTeXMode.textmode: '\\^{G}', LaTeXMode.mathmode: '\\hat{G}'},
+ 285: {LaTeXMode.textmode: '\\^{g}', LaTeXMode.mathmode: '\\hat{g}'},
+ 286: {LaTeXMode.textmode: '\\u{G}', LaTeXMode.mathmode: '\\breve{G}'},
+ 287: {LaTeXMode.textmode: '\\u{g}', LaTeXMode.mathmode: '\\breve{g}'},
+ 288: {LaTeXMode.textmode: '\\.{G}', LaTeXMode.mathmode: '\\dot{G}'},
+ 289: {LaTeXMode.textmode: '\\.{g}', LaTeXMode.mathmode: '\\dot{g}'},
+ 290: {LaTeXMode.textmode: '\\c{G}', LaTeXMode.mathmode: '\\mbox{\\c{G}}'},
+ 291: {LaTeXMode.textmode: '\\c{g}', LaTeXMode.mathmode: '\\mbox{\\c{g}}'},
+ 292: {LaTeXMode.textmode: '\\^{H}', LaTeXMode.mathmode: '\\hat{H}'},
+ 293: {LaTeXMode.textmode: '\\^{h}', LaTeXMode.mathmode: '\\hat{h}'},
+ 294: {LaTeXMode.textmode: '{\\fontencoding{LELA}\\selectfont\\char40}'},
+ 295: {LaTeXMode.textmode: '\\Elzxh '},
+ 296: {LaTeXMode.textmode: '\\~{I}', LaTeXMode.mathmode: '\\tilde{I}'},
+ 297: {LaTeXMode.textmode: '\\~{\\i}', LaTeXMode.mathmode: '\\tilde{\\imath}'},
+ 298: {LaTeXMode.textmode: '\\={I}', LaTeXMode.mathmode: '\\bar{I}'},
+ 299: {LaTeXMode.textmode: '\\={\\i}', LaTeXMode.mathmode: '\\bar{\\imath}'},
+ 300: {LaTeXMode.textmode: '\\u{I}', LaTeXMode.mathmode: '\\breve{I}'},
+ 301: {LaTeXMode.textmode: '\\u{\\i}', LaTeXMode.mathmode: '\\breve{\\imath}'},
+ 302: {LaTeXMode.textmode: '\\k{I}'},
+ 303: {LaTeXMode.textmode: '\\k{i}'},
+ 304: {LaTeXMode.textmode: '\\.{I}', LaTeXMode.mathmode: '\\dot{I}'},
+ 305: {LaTeXMode.textmode: '\\i ', LaTeXMode.mathmode: '\\imath'},
+ 306: {LaTeXMode.textmode: 'IJ'},
+ 307: {LaTeXMode.textmode: 'ij'},
+ 308: {LaTeXMode.textmode: '\\^{J}', LaTeXMode.mathmode: '\\hat{J}'},
+ 309: {LaTeXMode.textmode: '\\^{\\j}', LaTeXMode.mathmode: '\\hat{\\jmath}'},
+ 310: {LaTeXMode.textmode: '\\c{K}', LaTeXMode.mathmode: '\\mbox{\\c{K}}'},
+ 311: {LaTeXMode.textmode: '\\c{k}', LaTeXMode.mathmode: '\\mbox{\\c{k}}'},
+ 312: {LaTeXMode.textmode: '{\\fontencoding{LELA}\\selectfont\\char91}'},
+ 313: {LaTeXMode.textmode: "\\'{L}", LaTeXMode.mathmode: '\\acute{L}'},
+ 314: {LaTeXMode.textmode: "\\'{l}", LaTeXMode.mathmode: '\\acute{l}'},
+ 315: {LaTeXMode.textmode: '\\c{L}', LaTeXMode.mathmode: '\\mbox{\\c{L}}'},
+ 316: {LaTeXMode.textmode: '\\c{l}', LaTeXMode.mathmode: '\\mbox{\\c{l}}'},
+ 317: {LaTeXMode.textmode: '\\v{L}', LaTeXMode.mathmode: '\\check{L}'},
+ 318: {LaTeXMode.textmode: '\\v{l}', LaTeXMode.mathmode: '\\check{l}'},
+ 319: {LaTeXMode.textmode: '{\\fontencoding{LELA}\\selectfont\\char201}'},
+ 320: {LaTeXMode.textmode: '{\\fontencoding{LELA}\\selectfont\\char202}'},
+ 321: {LaTeXMode.textmode: '\\L ', LaTeXMode.mathmode: '\\L'},
+ 322: {LaTeXMode.textmode: '\\l ', LaTeXMode.mathmode: '\\l'},
+ 323: {LaTeXMode.textmode: "\\'{N}", LaTeXMode.mathmode: '\\acute{N}'},
+ 324: {LaTeXMode.textmode: "\\'{n}", LaTeXMode.mathmode: '\\acute{n}'},
+ 325: {LaTeXMode.textmode: '\\c{N}', LaTeXMode.mathmode: '\\mbox{\\c{N}}'},
+ 326: {LaTeXMode.textmode: '\\c{n}', LaTeXMode.mathmode: '\\mbox{\\c{n}}'},
+ 327: {LaTeXMode.textmode: '\\v{N}', LaTeXMode.mathmode: '\\check{N}'},
+ 328: {LaTeXMode.textmode: '\\v{n}', LaTeXMode.mathmode: '\\check{n}'},
+ 329: {LaTeXMode.textmode: "'n"},
+ 330: {LaTeXMode.textmode: '\\NG '},
+ 331: {LaTeXMode.textmode: '\\ng '},
+ 332: {LaTeXMode.textmode: '\\={O}', LaTeXMode.mathmode: '\\bar{O}'},
+ 333: {LaTeXMode.textmode: '\\={o}', LaTeXMode.mathmode: '\\bar{o}'},
+ 334: {LaTeXMode.textmode: '\\u{O}', LaTeXMode.mathmode: '\\breve{O}'},
+ 335: {LaTeXMode.textmode: '\\u{o}', LaTeXMode.mathmode: '\\breve{o}'},
+ 336: {LaTeXMode.textmode: '\\H{O}', LaTeXMode.mathmode: '\\mbox{\\H{O}}'},
+ 337: {LaTeXMode.textmode: '\\H{o}', LaTeXMode.mathmode: '\\mbox{\\H{o}}'},
+ 338: {LaTeXMode.textmode: '\\OE ', LaTeXMode.mathmode: '\\OE'},
+ 339: {LaTeXMode.textmode: '\\oe ', LaTeXMode.mathmode: '\\oe'},
+ 340: {LaTeXMode.textmode: "\\'{R}", LaTeXMode.mathmode: '\\acute{R}'},
+ 341: {LaTeXMode.textmode: "\\'{r}", LaTeXMode.mathmode: '\\acute{r}'},
+ 342: {LaTeXMode.textmode: '\\c{R}', LaTeXMode.mathmode: '\\mbox{\\c{R}}'},
+ 343: {LaTeXMode.textmode: '\\c{r}', LaTeXMode.mathmode: '\\mbox{\\c{r}}'},
+ 344: {LaTeXMode.textmode: '\\v{R}', LaTeXMode.mathmode: '\\check{R}'},
+ 345: {LaTeXMode.textmode: '\\v{r}', LaTeXMode.mathmode: '\\check{r}'},
+ 346: {LaTeXMode.textmode: "\\'{S}", LaTeXMode.mathmode: '\\acute{S}'},
+ 347: {LaTeXMode.textmode: "\\'{s}", LaTeXMode.mathmode: '\\acute{s}'},
+ 348: {LaTeXMode.textmode: '\\^{S}', LaTeXMode.mathmode: '\\hat{S}'},
+ 349: {LaTeXMode.textmode: '\\^{s}', LaTeXMode.mathmode: '\\hat{s}'},
+ 350: {LaTeXMode.textmode: '\\c{S}', LaTeXMode.mathmode: '\\mbox{\\c{S}}'},
+ 351: {LaTeXMode.textmode: '\\c{s}', LaTeXMode.mathmode: '\\mbox{\\c{s}}'},
+ 352: {LaTeXMode.textmode: '\\v{S}', LaTeXMode.mathmode: '\\check{S}'},
+ 353: {LaTeXMode.textmode: '\\v{s}', LaTeXMode.mathmode: '\\check{s}'},
+ 354: {LaTeXMode.textmode: '\\c{T}', LaTeXMode.mathmode: '\\mbox{\\c{T}}'},
+ 355: {LaTeXMode.textmode: '\\c{t}', LaTeXMode.mathmode: '\\mbox{\\c{t}}'},
+ 356: {LaTeXMode.textmode: '\\v{T}', LaTeXMode.mathmode: '\\check{T}'},
+ 357: {LaTeXMode.textmode: '\\v{t}', LaTeXMode.mathmode: '\\check{t}'},
+ 358: {LaTeXMode.textmode: '{\\fontencoding{LELA}\\selectfont\\char47}'},
+ 359: {LaTeXMode.textmode: '{\\fontencoding{LELA}\\selectfont\\char63}'},
+ 360: {LaTeXMode.textmode: '\\~{U}', LaTeXMode.mathmode: '\\tilde{U}'},
+ 361: {LaTeXMode.textmode: '\\~{u}', LaTeXMode.mathmode: '\\tilde{u}'},
+ 362: {LaTeXMode.textmode: '\\={U}', LaTeXMode.mathmode: '\\bar{U}'},
+ 363: {LaTeXMode.textmode: '\\={u}', LaTeXMode.mathmode: '\\bar{u}'},
+ 364: {LaTeXMode.textmode: '\\u{U}', LaTeXMode.mathmode: '\\breve{U}'},
+ 365: {LaTeXMode.textmode: '\\u{u}', LaTeXMode.mathmode: '\\breve{u}'},
+ 366: {LaTeXMode.textmode: '\\r{U}', LaTeXMode.mathmode: '\\mathring{U}'},
+ 367: {LaTeXMode.textmode: '\\r{u}', LaTeXMode.mathmode: '\\mathring{u}'},
+ 368: {LaTeXMode.textmode: '\\H{U}', LaTeXMode.mathmode: '\\mbox{\\H{U}}'},
+ 369: {LaTeXMode.textmode: '\\H{u}', LaTeXMode.mathmode: '\\mbox{\\H{u}}'},
+ 370: {LaTeXMode.textmode: '\\k{U}', LaTeXMode.mathmode: '\\k{U}'},
+ 371: {LaTeXMode.textmode: '\\k{u}'},
+ 372: {LaTeXMode.textmode: '\\^{W}', LaTeXMode.mathmode: '\\hat{W}'},
+ 373: {LaTeXMode.textmode: '\\^{w}', LaTeXMode.mathmode: '\\hat{w}'},
+ 374: {LaTeXMode.textmode: '\\^{Y}', LaTeXMode.mathmode: '\\hat{Y}'},
+ 375: {LaTeXMode.textmode: '\\^{y}', LaTeXMode.mathmode: '\\hat{y}'},
+ 376: {LaTeXMode.textmode: '\\"{Y}', LaTeXMode.mathmode: '\\ddot{Y}'},
+ 377: {LaTeXMode.textmode: "\\'{Z}", LaTeXMode.mathmode: '\\acute{Z}'},
+ 378: {LaTeXMode.textmode: "\\'{z}", LaTeXMode.mathmode: '\\acute{z}'},
+ 379: {LaTeXMode.textmode: '\\.{Z}', LaTeXMode.mathmode: '\\dot{Z}'},
+ 380: {LaTeXMode.textmode: '\\.{z}', LaTeXMode.mathmode: '\\dot{z}'},
+ 381: {LaTeXMode.textmode: '\\v{Z}', LaTeXMode.mathmode: '\\check{Z}'},
+ 382: {LaTeXMode.textmode: '\\v{z}', LaTeXMode.mathmode: '\\check{z}'},
+ 402: {LaTeXMode.textmode: 'f'},
+ 405: {LaTeXMode.textmode: '\\texthvlig '},
+ 414: {LaTeXMode.textmode: '\\textnrleg '},
+ 426: {LaTeXMode.textmode: '\\eth '},
+ 450: {LaTeXMode.textmode: '\\textdoublepipe '},
+ 501: {LaTeXMode.textmode: "\\'{g}", LaTeXMode.mathmode: '\\acute{g}'},
+ 592: {LaTeXMode.textmode: '\\Elztrna '},
+ 594: {LaTeXMode.textmode: '\\Elztrnsa '},
+ 596: {LaTeXMode.textmode: '\\Elzopeno '},
+ 598: {LaTeXMode.textmode: '\\Elzrtld '},
+ 601: {LaTeXMode.textmode: '\\Elzschwa '},
+ 603: {LaTeXMode.textmode: '\\varepsilon '},
+ 609: {LaTeXMode.textmode: 'g'},
+ 611: {LaTeXMode.textmode: '\\Elzpgamma '},
+ 612: {LaTeXMode.textmode: '\\Elzpbgam '},
+ 613: {LaTeXMode.textmode: '\\Elztrnh '},
+ 620: {LaTeXMode.textmode: '\\Elzbtdl '},
+ 621: {LaTeXMode.textmode: '\\Elzrtll '},
+ 623: {LaTeXMode.textmode: '\\Elztrnm '},
+ 624: {LaTeXMode.textmode: '\\Elztrnmlr '},
+ 625: {LaTeXMode.textmode: '\\Elzltlmr '},
+ 627: {LaTeXMode.textmode: '\\Elzrtln '},
+ 631: {LaTeXMode.textmode: '\\Elzclomeg '},
+ 632: {LaTeXMode.textmode: '\\textphi '},
+ 633: {LaTeXMode.textmode: '\\Elztrnr '},
+ 634: {LaTeXMode.textmode: '\\Elztrnrl '},
+ 635: {LaTeXMode.textmode: '\\Elzrttrnr '},
+ 636: {LaTeXMode.textmode: '\\Elzrl '},
+ 637: {LaTeXMode.textmode: '\\Elzrtlr '},
+ 638: {LaTeXMode.textmode: '\\Elzfhr '},
+ 642: {LaTeXMode.textmode: '\\Elzrtls '},
+ 643: {LaTeXMode.textmode: '\\Elzesh '},
+ 647: {LaTeXMode.textmode: '\\Elztrnt '},
+ 648: {LaTeXMode.textmode: '\\Elzrtlt '},
+ 650: {LaTeXMode.textmode: '\\Elzpupsil '},
+ 651: {LaTeXMode.textmode: '\\Elzpscrv '},
+ 652: {LaTeXMode.textmode: '\\Elzinvv '},
+ 653: {LaTeXMode.textmode: '\\Elzinvw '},
+ 654: {LaTeXMode.textmode: '\\Elztrny '},
+ 656: {LaTeXMode.textmode: '\\Elzrtlz '},
+ 658: {LaTeXMode.textmode: '\\Elzyogh '},
+ 660: {LaTeXMode.textmode: '\\Elzglst '},
+ 661: {LaTeXMode.textmode: '\\Elzreglst '},
+ 662: {LaTeXMode.textmode: '\\Elzinglst '},
+ 670: {LaTeXMode.textmode: '\\textturnk '},
+ 676: {LaTeXMode.textmode: '\\Elzdyogh '},
+ 679: {LaTeXMode.textmode: '\\Elztesh '},
+ 700: {LaTeXMode.textmode: "'", LaTeXMode.mathmode: '\\rasp'},
+ 711: {LaTeXMode.textmode: '\\textasciicaron '},
+ 712: {LaTeXMode.textmode: '\\Elzverts '},
+ 716: {LaTeXMode.textmode: '\\Elzverti '},
+ 720: {LaTeXMode.textmode: '\\Elzlmrk '},
+ 721: {LaTeXMode.textmode: '\\Elzhlmrk '},
+ 722: {LaTeXMode.textmode: '\\Elzsbrhr '},
+ 723: {LaTeXMode.textmode: '\\Elzsblhr '},
+ 724: {LaTeXMode.textmode: '\\Elzrais '},
+ 725: {LaTeXMode.textmode: '\\Elzlow '},
+ 728: {LaTeXMode.textmode: '\\textasciibreve ', LaTeXMode.mathmode: '\\u'},
+ 729: {LaTeXMode.textmode: '\\textperiodcentered ', LaTeXMode.mathmode: '\\dot{}'},
+ 730: {LaTeXMode.textmode: '\\r{}', LaTeXMode.mathmode: '\\mathring{}'},
+ 731: {LaTeXMode.textmode: '\\k{}', LaTeXMode.mathmode: '\\mbox{\\k{}}'},
+ 732: {LaTeXMode.textmode: '\\texttildelow '},
+ 733: {LaTeXMode.textmode: '\\H{}', LaTeXMode.mathmode: '\\mbox{\\H{}}'},
+ 741: {LaTeXMode.textmode: '\\tone{55}'},
+ 742: {LaTeXMode.textmode: '\\tone{44}'},
+ 743: {LaTeXMode.textmode: '\\tone{33}'},
+ 744: {LaTeXMode.textmode: '\\tone{22}'},
+ 745: {LaTeXMode.textmode: '\\tone{11}'},
+ 768: {LaTeXMode.textmode: '\\`', LaTeXMode.mathmode: '\\grave'},
+ 769: {LaTeXMode.textmode: "\\'", LaTeXMode.mathmode: '\\acute'},
+ 770: {LaTeXMode.textmode: '\\^', LaTeXMode.mathmode: '\\hat'},
+ 771: {LaTeXMode.textmode: '\\~', LaTeXMode.mathmode: '\\tilde'},
+ 772: {LaTeXMode.textmode: '\\=', LaTeXMode.mathmode: '\\bar'},
+ 774: {LaTeXMode.textmode: '\\u', LaTeXMode.mathmode: '\\breve'},
+ 775: {LaTeXMode.textmode: '\\.', LaTeXMode.mathmode: '\\dot'},
+ 776: {LaTeXMode.textmode: '\\"', LaTeXMode.mathmode: '\\ddot'},
+ 778: {LaTeXMode.textmode: '\\r', LaTeXMode.mathmode: '\\ocirc'},
+ 779: {LaTeXMode.textmode: '\\H', LaTeXMode.mathmode: '\\H'},
+ 780: {LaTeXMode.textmode: '\\v', LaTeXMode.mathmode: '\\check'},
+ 783: {LaTeXMode.textmode: '\\cyrchar\\C'},
+ 801: {LaTeXMode.textmode: '\\Elzpalh '},
+ 807: {LaTeXMode.textmode: '\\c', LaTeXMode.mathmode: '\\c'},
+ 810: {LaTeXMode.textmode: '\\Elzsbbrg '},
+ 824: {LaTeXMode.mathmode: '\\forks'},
+ 902: {LaTeXMode.textmode: "\\'{A}", LaTeXMode.mathmode: '\\acute{A}'},
+ 904: {LaTeXMode.textmode: "\\'{E}", LaTeXMode.mathmode: '\\acute{E}'},
+ 905: {LaTeXMode.textmode: "\\'{H}", LaTeXMode.mathmode: '\\grave{H}'},
+ 906: {LaTeXMode.textmode: "\\'{}{I}", LaTeXMode.mathmode: "\\mathrm{'I}"},
+ 908: {LaTeXMode.textmode: "\\'{}O", LaTeXMode.mathmode: "\\mathrm{'O}"},
+ 910: {LaTeXMode.textmode: "\\mathrm{'Y}"},
+ 911: {LaTeXMode.textmode: "\\mathrm{'\\Omega}"},
+ 912: {LaTeXMode.textmode: '\\acute{\\ddot{\\iota}}'},
+ 913: {LaTeXMode.textmode: '\\Alpha '},
+ 914: {LaTeXMode.textmode: '\\Beta '},
+ 915: {LaTeXMode.textmode: '\\Gamma ', LaTeXMode.mathmode: '\\Gamma'},
+ 916: {LaTeXMode.textmode: '\\Delta ', LaTeXMode.mathmode: '\\Delta'},
+ 917: {LaTeXMode.textmode: '\\Epsilon '},
+ 918: {LaTeXMode.textmode: '\\Zeta '},
+ 919: {LaTeXMode.textmode: '\\Eta '},
+ 920: {LaTeXMode.textmode: '\\Theta ', LaTeXMode.mathmode: '\\Theta'},
+ 921: {LaTeXMode.textmode: '\\Iota '},
+ 922: {LaTeXMode.textmode: '\\Kappa '},
+ 923: {LaTeXMode.textmode: '\\Lambda ', LaTeXMode.mathmode: '\\Lambda'},
+ 924: {LaTeXMode.textmode: 'M'},
+ 925: {LaTeXMode.textmode: 'N'},
+ 926: {LaTeXMode.textmode: '\\Xi ', LaTeXMode.mathmode: '\\Xi'},
+ 927: {LaTeXMode.textmode: 'O'},
+ 928: {LaTeXMode.textmode: '\\Pi ', LaTeXMode.mathmode: '\\Pi'},
+ 929: {LaTeXMode.textmode: '\\Rho '},
+ 931: {LaTeXMode.textmode: '\\Sigma ', LaTeXMode.mathmode: '\\Sigma'},
+ 932: {LaTeXMode.textmode: '\\Tau '},
+ 933: {LaTeXMode.textmode: '\\Upsilon '},
+ 934: {LaTeXMode.textmode: '\\Phi ', LaTeXMode.mathmode: '\\Phi'},
+ 935: {LaTeXMode.textmode: '\\Chi '},
+ 936: {LaTeXMode.textmode: '\\Psi ', LaTeXMode.mathmode: '\\Psi'},
+ 937: {LaTeXMode.textmode: '\\Omega ', LaTeXMode.mathmode: '\\Omega'},
+ 938: {LaTeXMode.textmode: '\\mathrm{\\ddot{I}}'},
+ 939: {LaTeXMode.textmode: '\\mathrm{\\ddot{Y}}'},
+ 940: {LaTeXMode.textmode: "\\'{$\\alpha$}", LaTeXMode.mathmode: '\\acute{\\alpha}'},
+ 941: {LaTeXMode.textmode: '\\acute{\\epsilon}'},
+ 942: {LaTeXMode.textmode: '\\acute{\\eta}'},
+ 943: {LaTeXMode.textmode: '\\acute{\\iota}'},
+ 944: {LaTeXMode.textmode: '\\acute{\\ddot{\\upsilon}}'},
+ 945: {LaTeXMode.textmode: '\\alpha ', LaTeXMode.mathmode: '\\alpha'},
+ 946: {LaTeXMode.textmode: '\\beta ', LaTeXMode.mathmode: '\\beta'},
+ 947: {LaTeXMode.textmode: '\\gamma ', LaTeXMode.mathmode: '\\gamma'},
+ 948: {LaTeXMode.textmode: '\\delta ', LaTeXMode.mathmode: '\\delta'},
+ 949: {LaTeXMode.textmode: '\\epsilon ', LaTeXMode.mathmode: '\\varepsilon'},
+ 950: {LaTeXMode.textmode: '\\zeta ', LaTeXMode.mathmode: '\\zeta'},
+ 951: {LaTeXMode.textmode: '\\eta ', LaTeXMode.mathmode: '\\eta'},
+ 952: {LaTeXMode.textmode: '\\texttheta ', LaTeXMode.mathmode: '\\theta'},
+ 953: {LaTeXMode.textmode: '\\iota ', LaTeXMode.mathmode: '\\iota'},
+ 954: {LaTeXMode.textmode: '\\kappa ', LaTeXMode.mathmode: '\\kappa'},
+ 955: {LaTeXMode.textmode: '\\lambda ', LaTeXMode.mathmode: '\\lambda'},
+ 956: {LaTeXMode.textmode: '\\mu ', LaTeXMode.mathmode: '\\mu'},
+ 957: {LaTeXMode.textmode: '\\nu ', LaTeXMode.mathmode: '\\nu'},
+ 958: {LaTeXMode.textmode: '\\xi ', LaTeXMode.mathmode: '\\xi'},
+ 959: {LaTeXMode.textmode: 'o'},
+ 960: {LaTeXMode.textmode: '\\pi ', LaTeXMode.mathmode: '\\pi'},
+ 961: {LaTeXMode.textmode: '\\rho ', LaTeXMode.mathmode: '\\rho'},
+ 962: {LaTeXMode.textmode: '\\varsigma ', LaTeXMode.mathmode: '\\varsigma'},
+ 963: {LaTeXMode.textmode: '\\sigma ', LaTeXMode.mathmode: '\\sigma'},
+ 964: {LaTeXMode.textmode: '\\tau ', LaTeXMode.mathmode: '\\tau'},
+ 965: {LaTeXMode.textmode: '\\upsilon ', LaTeXMode.mathmode: '\\upsilon'},
+ 966: {LaTeXMode.textmode: '\\varphi ', LaTeXMode.mathmode: '\\varphi'},
+ 967: {LaTeXMode.textmode: '\\chi ', LaTeXMode.mathmode: '\\chi'},
+ 968: {LaTeXMode.textmode: '\\psi ', LaTeXMode.mathmode: '\\psi'},
+ 969: {LaTeXMode.textmode: '\\omega ', LaTeXMode.mathmode: '\\omega'},
+ 970: {LaTeXMode.textmode: '\\ddot{\\iota}'},
+ 971: {LaTeXMode.textmode: '\\ddot{\\upsilon}'},
+ 972: {LaTeXMode.textmode: "\\'{o}", LaTeXMode.mathmode: '\\acute{o}'},
+ 973: {LaTeXMode.textmode: '\\acute{\\upsilon}'},
+ 974: {LaTeXMode.textmode: '\\acute{\\omega}'},
+ 977: {LaTeXMode.textmode: '\\textvartheta ', LaTeXMode.mathmode: '\\vartheta '},
+ 978: {LaTeXMode.textmode: '\\Upsilon ', LaTeXMode.mathmode: '\\Upsilon'},
+ 981: {LaTeXMode.textmode: '\\phi ', LaTeXMode.mathmode: '\\phi'},
+ 982: {LaTeXMode.textmode: '\\varpi ', LaTeXMode.mathmode: '\\varpi'},
+ 986: {LaTeXMode.textmode: '\\Stigma '},
+ 988: {LaTeXMode.textmode: '\\Digamma '},
+ 989: {LaTeXMode.textmode: '\\digamma ', LaTeXMode.mathmode: '\\digamma'},
+ 990: {LaTeXMode.textmode: '\\Koppa '},
+ 992: {LaTeXMode.textmode: '\\Sampi '},
+ 1008: {LaTeXMode.textmode: '\\varkappa ', LaTeXMode.mathmode: '\\varkappa'},
+ 1009: {LaTeXMode.textmode: '\\varrho ', LaTeXMode.mathmode: '\\varrho'},
+ 1014: {LaTeXMode.textmode: '\\backepsilon ', LaTeXMode.mathmode: '\\backepsilon'},
+ 1025: {LaTeXMode.textmode: '\\cyrchar\\CYRYO '},
+ 1026: {LaTeXMode.textmode: '\\cyrchar\\CYRDJE '},
+ 1027: {LaTeXMode.textmode: "\\cyrchar{\\'\\CYRG}"},
+ 1028: {LaTeXMode.textmode: '\\cyrchar\\CYRIE '},
+ 1029: {LaTeXMode.textmode: '\\cyrchar\\CYRDZE '},
+ 1030: {LaTeXMode.textmode: '\\cyrchar\\CYRII '},
+ 1031: {LaTeXMode.textmode: '\\cyrchar\\CYRYI '},
+ 1032: {LaTeXMode.textmode: '\\cyrchar\\CYRJE '},
+ 1033: {LaTeXMode.textmode: '\\cyrchar\\CYRLJE '},
+ 1034: {LaTeXMode.textmode: '\\cyrchar\\CYRNJE '},
+ 1035: {LaTeXMode.textmode: '\\cyrchar\\CYRTSHE '},
+ 1036: {LaTeXMode.textmode: "\\cyrchar{\\'\\CYRK}"},
+ 1038: {LaTeXMode.textmode: '\\cyrchar\\CYRUSHRT '},
+ 1039: {LaTeXMode.textmode: '\\cyrchar\\CYRDZHE '},
+ 1040: {LaTeXMode.textmode: '\\cyrchar\\CYRA '},
+ 1041: {LaTeXMode.textmode: '\\cyrchar\\CYRB '},
+ 1042: {LaTeXMode.textmode: '\\cyrchar\\CYRV '},
+ 1043: {LaTeXMode.textmode: '\\cyrchar\\CYRG '},
+ 1044: {LaTeXMode.textmode: '\\cyrchar\\CYRD '},
+ 1045: {LaTeXMode.textmode: '\\cyrchar\\CYRE '},
+ 1046: {LaTeXMode.textmode: '\\cyrchar\\CYRZH '},
+ 1047: {LaTeXMode.textmode: '\\cyrchar\\CYRZ '},
+ 1048: {LaTeXMode.textmode: '\\cyrchar\\CYRI '},
+ 1049: {LaTeXMode.textmode: '\\cyrchar\\CYRISHRT '},
+ 1050: {LaTeXMode.textmode: '\\cyrchar\\CYRK '},
+ 1051: {LaTeXMode.textmode: '\\cyrchar\\CYRL '},
+ 1052: {LaTeXMode.textmode: '\\cyrchar\\CYRM '},
+ 1053: {LaTeXMode.textmode: '\\cyrchar\\CYRN '},
+ 1054: {LaTeXMode.textmode: '\\cyrchar\\CYRO '},
+ 1055: {LaTeXMode.textmode: '\\cyrchar\\CYRP '},
+ 1056: {LaTeXMode.textmode: '\\cyrchar\\CYRR '},
+ 1057: {LaTeXMode.textmode: '\\cyrchar\\CYRS '},
+ 1058: {LaTeXMode.textmode: '\\cyrchar\\CYRT '},
+ 1059: {LaTeXMode.textmode: '\\cyrchar\\CYRU '},
+ 1060: {LaTeXMode.textmode: '\\cyrchar\\CYRF '},
+ 1061: {LaTeXMode.textmode: '\\cyrchar\\CYRH '},
+ 1062: {LaTeXMode.textmode: '\\cyrchar\\CYRC '},
+ 1063: {LaTeXMode.textmode: '\\cyrchar\\CYRCH '},
+ 1064: {LaTeXMode.textmode: '\\cyrchar\\CYRSH '},
+ 1065: {LaTeXMode.textmode: '\\cyrchar\\CYRSHCH '},
+ 1066: {LaTeXMode.textmode: '\\cyrchar\\CYRHRDSN '},
+ 1067: {LaTeXMode.textmode: '\\cyrchar\\CYRERY '},
+ 1068: {LaTeXMode.textmode: '\\cyrchar\\CYRSFTSN '},
+ 1069: {LaTeXMode.textmode: '\\cyrchar\\CYREREV '},
+ 1070: {LaTeXMode.textmode: '\\cyrchar\\CYRYU '},
+ 1071: {LaTeXMode.textmode: '\\cyrchar\\CYRYA '},
+ 1072: {LaTeXMode.textmode: '\\cyrchar\\cyra '},
+ 1073: {LaTeXMode.textmode: '\\cyrchar\\cyrb '},
+ 1074: {LaTeXMode.textmode: '\\cyrchar\\cyrv '},
+ 1075: {LaTeXMode.textmode: '\\cyrchar\\cyrg '},
+ 1076: {LaTeXMode.textmode: '\\cyrchar\\cyrd '},
+ 1077: {LaTeXMode.textmode: '\\cyrchar\\cyre '},
+ 1078: {LaTeXMode.textmode: '\\cyrchar\\cyrzh '},
+ 1079: {LaTeXMode.textmode: '\\cyrchar\\cyrz '},
+ 1080: {LaTeXMode.textmode: '\\cyrchar\\cyri '},
+ 1081: {LaTeXMode.textmode: '\\cyrchar\\cyrishrt '},
+ 1082: {LaTeXMode.textmode: '\\cyrchar\\cyrk '},
+ 1083: {LaTeXMode.textmode: '\\cyrchar\\cyrl '},
+ 1084: {LaTeXMode.textmode: '\\cyrchar\\cyrm '},
+ 1085: {LaTeXMode.textmode: '\\cyrchar\\cyrn '},
+ 1086: {LaTeXMode.textmode: '\\cyrchar\\cyro '},
+ 1087: {LaTeXMode.textmode: '\\cyrchar\\cyrp '},
+ 1088: {LaTeXMode.textmode: '\\cyrchar\\cyrr '},
+ 1089: {LaTeXMode.textmode: '\\cyrchar\\cyrs '},
+ 1090: {LaTeXMode.textmode: '\\cyrchar\\cyrt '},
+ 1091: {LaTeXMode.textmode: '\\cyrchar\\cyru '},
+ 1092: {LaTeXMode.textmode: '\\cyrchar\\cyrf '},
+ 1093: {LaTeXMode.textmode: '\\cyrchar\\cyrh '},
+ 1094: {LaTeXMode.textmode: '\\cyrchar\\cyrc '},
+ 1095: {LaTeXMode.textmode: '\\cyrchar\\cyrch '},
+ 1096: {LaTeXMode.textmode: '\\cyrchar\\cyrsh '},
+ 1097: {LaTeXMode.textmode: '\\cyrchar\\cyrshch '},
+ 1098: {LaTeXMode.textmode: '\\cyrchar\\cyrhrdsn '},
+ 1099: {LaTeXMode.textmode: '\\cyrchar\\cyrery '},
+ 1100: {LaTeXMode.textmode: '\\cyrchar\\cyrsftsn '},
+ 1101: {LaTeXMode.textmode: '\\cyrchar\\cyrerev '},
+ 1102: {LaTeXMode.textmode: '\\cyrchar\\cyryu '},
+ 1103: {LaTeXMode.textmode: '\\cyrchar\\cyrya '},
+ 1105: {LaTeXMode.textmode: '\\cyrchar\\cyryo '},
+ 1106: {LaTeXMode.textmode: '\\cyrchar\\cyrdje '},
+ 1107: {LaTeXMode.textmode: "\\cyrchar{\\'\\cyrg}"},
+ 1108: {LaTeXMode.textmode: '\\cyrchar\\cyrie '},
+ 1109: {LaTeXMode.textmode: '\\cyrchar\\cyrdze '},
+ 1110: {LaTeXMode.textmode: '\\cyrchar\\cyrii '},
+ 1111: {LaTeXMode.textmode: '\\cyrchar\\cyryi '},
+ 1112: {LaTeXMode.textmode: '\\cyrchar\\cyrje '},
+ 1113: {LaTeXMode.textmode: '\\cyrchar\\cyrlje '},
+ 1114: {LaTeXMode.textmode: '\\cyrchar\\cyrnje '},
+ 1115: {LaTeXMode.textmode: '\\cyrchar\\cyrtshe '},
+ 1116: {LaTeXMode.textmode: "\\cyrchar{\\'\\cyrk}"},
+ 1118: {LaTeXMode.textmode: '\\cyrchar\\cyrushrt '},
+ 1119: {LaTeXMode.textmode: '\\cyrchar\\cyrdzhe '},
+ 1120: {LaTeXMode.textmode: '\\cyrchar\\CYROMEGA '},
+ 1121: {LaTeXMode.textmode: '\\cyrchar\\cyromega '},
+ 1122: {LaTeXMode.textmode: '\\cyrchar\\CYRYAT '},
+ 1124: {LaTeXMode.textmode: '\\cyrchar\\CYRIOTE '},
+ 1125: {LaTeXMode.textmode: '\\cyrchar\\cyriote '},
+ 1126: {LaTeXMode.textmode: '\\cyrchar\\CYRLYUS '},
+ 1127: {LaTeXMode.textmode: '\\cyrchar\\cyrlyus '},
+ 1128: {LaTeXMode.textmode: '\\cyrchar\\CYRIOTLYUS '},
+ 1129: {LaTeXMode.textmode: '\\cyrchar\\cyriotlyus '},
+ 1130: {LaTeXMode.textmode: '\\cyrchar\\CYRBYUS '},
+ 1132: {LaTeXMode.textmode: '\\cyrchar\\CYRIOTBYUS '},
+ 1133: {LaTeXMode.textmode: '\\cyrchar\\cyriotbyus '},
+ 1134: {LaTeXMode.textmode: '\\cyrchar\\CYRKSI '},
+ 1135: {LaTeXMode.textmode: '\\cyrchar\\cyrksi '},
+ 1136: {LaTeXMode.textmode: '\\cyrchar\\CYRPSI '},
+ 1137: {LaTeXMode.textmode: '\\cyrchar\\cyrpsi '},
+ 1138: {LaTeXMode.textmode: '\\cyrchar\\CYRFITA '},
+ 1140: {LaTeXMode.textmode: '\\cyrchar\\CYRIZH '},
+ 1144: {LaTeXMode.textmode: '\\cyrchar\\CYRUK '},
+ 1145: {LaTeXMode.textmode: '\\cyrchar\\cyruk '},
+ 1146: {LaTeXMode.textmode: '\\cyrchar\\CYROMEGARND '},
+ 1147: {LaTeXMode.textmode: '\\cyrchar\\cyromegarnd '},
+ 1148: {LaTeXMode.textmode: '\\cyrchar\\CYROMEGATITLO '},
+ 1149: {LaTeXMode.textmode: '\\cyrchar\\cyromegatitlo '},
+ 1150: {LaTeXMode.textmode: '\\cyrchar\\CYROT '},
+ 1151: {LaTeXMode.textmode: '\\cyrchar\\cyrot '},
+ 1152: {LaTeXMode.textmode: '\\cyrchar\\CYRKOPPA '},
+ 1153: {LaTeXMode.textmode: '\\cyrchar\\cyrkoppa '},
+ 1154: {LaTeXMode.textmode: '\\cyrchar\\cyrthousands '},
+ 1160: {LaTeXMode.textmode: '\\cyrchar\\cyrhundredthousands '},
+ 1161: {LaTeXMode.textmode: '\\cyrchar\\cyrmillions '},
+ 1164: {LaTeXMode.textmode: '\\cyrchar\\CYRSEMISFTSN '},
+ 1165: {LaTeXMode.textmode: '\\cyrchar\\cyrsemisftsn '},
+ 1166: {LaTeXMode.textmode: '\\cyrchar\\CYRRTICK '},
+ 1167: {LaTeXMode.textmode: '\\cyrchar\\cyrrtick '},
+ 1168: {LaTeXMode.textmode: '\\cyrchar\\CYRGUP '},
+ 1169: {LaTeXMode.textmode: '\\cyrchar\\cyrgup '},
+ 1170: {LaTeXMode.textmode: '\\cyrchar\\CYRGHCRS '},
+ 1171: {LaTeXMode.textmode: '\\cyrchar\\cyrghcrs '},
+ 1172: {LaTeXMode.textmode: '\\cyrchar\\CYRGHK '},
+ 1173: {LaTeXMode.textmode: '\\cyrchar\\cyrghk '},
+ 1174: {LaTeXMode.textmode: '\\cyrchar\\CYRZHDSC '},
+ 1175: {LaTeXMode.textmode: '\\cyrchar\\cyrzhdsc '},
+ 1176: {LaTeXMode.textmode: '\\cyrchar\\CYRZDSC '},
+ 1177: {LaTeXMode.textmode: '\\cyrchar\\cyrzdsc '},
+ 1178: {LaTeXMode.textmode: '\\cyrchar\\CYRKDSC '},
+ 1179: {LaTeXMode.textmode: '\\cyrchar\\cyrkdsc '},
+ 1180: {LaTeXMode.textmode: '\\cyrchar\\CYRKVCRS '},
+ 1181: {LaTeXMode.textmode: '\\cyrchar\\cyrkvcrs '},
+ 1182: {LaTeXMode.textmode: '\\cyrchar\\CYRKHCRS '},
+ 1183: {LaTeXMode.textmode: '\\cyrchar\\cyrkhcrs '},
+ 1184: {LaTeXMode.textmode: '\\cyrchar\\CYRKBEAK '},
+ 1185: {LaTeXMode.textmode: '\\cyrchar\\cyrkbeak '},
+ 1186: {LaTeXMode.textmode: '\\cyrchar\\CYRNDSC '},
+ 1187: {LaTeXMode.textmode: '\\cyrchar\\cyrndsc '},
+ 1188: {LaTeXMode.textmode: '\\cyrchar\\CYRNG '},
+ 1189: {LaTeXMode.textmode: '\\cyrchar\\cyrng '},
+ 1190: {LaTeXMode.textmode: '\\cyrchar\\CYRPHK '},
+ 1191: {LaTeXMode.textmode: '\\cyrchar\\cyrphk '},
+ 1192: {LaTeXMode.textmode: '\\cyrchar\\CYRABHHA '},
+ 1193: {LaTeXMode.textmode: '\\cyrchar\\cyrabhha '},
+ 1194: {LaTeXMode.textmode: '\\cyrchar\\CYRSDSC '},
+ 1195: {LaTeXMode.textmode: '\\cyrchar\\cyrsdsc '},
+ 1196: {LaTeXMode.textmode: '\\cyrchar\\CYRTDSC '},
+ 1197: {LaTeXMode.textmode: '\\cyrchar\\cyrtdsc '},
+ 1198: {LaTeXMode.textmode: '\\cyrchar\\CYRY '},
+ 1199: {LaTeXMode.textmode: '\\cyrchar\\cyry '},
+ 1200: {LaTeXMode.textmode: '\\cyrchar\\CYRYHCRS '},
+ 1201: {LaTeXMode.textmode: '\\cyrchar\\cyryhcrs '},
+ 1202: {LaTeXMode.textmode: '\\cyrchar\\CYRHDSC '},
+ 1203: {LaTeXMode.textmode: '\\cyrchar\\cyrhdsc '},
+ 1204: {LaTeXMode.textmode: '\\cyrchar\\CYRTETSE '},
+ 1205: {LaTeXMode.textmode: '\\cyrchar\\cyrtetse '},
+ 1206: {LaTeXMode.textmode: '\\cyrchar\\CYRCHRDSC '},
+ 1207: {LaTeXMode.textmode: '\\cyrchar\\cyrchrdsc '},
+ 1208: {LaTeXMode.textmode: '\\cyrchar\\CYRCHVCRS '},
+ 1209: {LaTeXMode.textmode: '\\cyrchar\\cyrchvcrs '},
+ 1210: {LaTeXMode.textmode: '\\cyrchar\\CYRSHHA '},
+ 1211: {LaTeXMode.textmode: '\\cyrchar\\cyrshha '},
+ 1212: {LaTeXMode.textmode: '\\cyrchar\\CYRABHCH '},
+ 1213: {LaTeXMode.textmode: '\\cyrchar\\cyrabhch '},
+ 1214: {LaTeXMode.textmode: '\\cyrchar\\CYRABHCHDSC '},
+ 1215: {LaTeXMode.textmode: '\\cyrchar\\cyrabhchdsc '},
+ 1216: {LaTeXMode.textmode: '\\cyrchar\\CYRpalochka '},
+ 1219: {LaTeXMode.textmode: '\\cyrchar\\CYRKHK '},
+ 1220: {LaTeXMode.textmode: '\\cyrchar\\cyrkhk '},
+ 1223: {LaTeXMode.textmode: '\\cyrchar\\CYRNHK '},
+ 1224: {LaTeXMode.textmode: '\\cyrchar\\cyrnhk '},
+ 1227: {LaTeXMode.textmode: '\\cyrchar\\CYRCHLDSC '},
+ 1228: {LaTeXMode.textmode: '\\cyrchar\\cyrchldsc '},
+ 1236: {LaTeXMode.textmode: '\\cyrchar\\CYRAE '},
+ 1237: {LaTeXMode.textmode: '\\cyrchar\\cyrae '},
+ 1240: {LaTeXMode.textmode: '\\cyrchar\\CYRSCHWA '},
+ 1241: {LaTeXMode.textmode: '\\cyrchar\\cyrschwa '},
+ 1248: {LaTeXMode.textmode: '\\cyrchar\\CYRABHDZE '},
+ 1249: {LaTeXMode.textmode: '\\cyrchar\\cyrabhdze '},
+ 1256: {LaTeXMode.textmode: '\\cyrchar\\CYROTLD '},
+ 1257: {LaTeXMode.textmode: '\\cyrchar\\cyrotld '},
+ 8194: {LaTeXMode.textmode: '\\hspace{0.6em}', LaTeXMode.mathmode: '\\enspace'},
+ 8195: {LaTeXMode.textmode: '\\hspace{1em}', LaTeXMode.mathmode: '\\quad'},
+ 8196: {LaTeXMode.textmode: '\\hspace{0.33em}'},
+ 8197: {LaTeXMode.textmode: '\\hspace{0.25em}', LaTeXMode.mathmode: '\\thickspace'},
+ 8198: {LaTeXMode.textmode: '\\hspace{0.166em}'},
+ 8199: {LaTeXMode.textmode: '\\hphantom{0}'},
+ 8200: {LaTeXMode.textmode: '\\hphantom{,}'},
+ 8201: {LaTeXMode.textmode: '\\hspace{0.167em}', LaTeXMode.mathmode: '\\thinspace'},
+ 8202: {LaTeXMode.textmode: '\\mkern1mu ', LaTeXMode.mathmode: '\\hspace'},
+ 8208: {LaTeXMode.textmode: '-'},
+ 8211: {LaTeXMode.textmode: '\\textendash ', LaTeXMode.mathmode: '\\mathrm{\\textendash}'},
+ 8212: {LaTeXMode.textmode: '\\textemdash ', LaTeXMode.mathmode: '\\emdash'},
+ 8213: {LaTeXMode.textmode: '\\rule{1em}{1pt}'},
+ 8214: {LaTeXMode.textmode: '\\Vert ', LaTeXMode.mathmode: '\\Vert'},
+ 8216: {LaTeXMode.textmode: '`', LaTeXMode.mathmode: '\\lq'},
+ 8217: {LaTeXMode.textmode: "'", LaTeXMode.mathmode: '\\rq'},
+ 8218: {LaTeXMode.textmode: ','},
+ 8219: {LaTeXMode.textmode: '\\Elzreapos '},
+ 8220: {LaTeXMode.textmode: '\\textquotedblleft ', LaTeXMode.mathmode: '\\textquotedblleft'},
+ 8221: {LaTeXMode.textmode: '\\textquotedblright ', LaTeXMode.mathmode: '\\textquotedblright'},
+ 8222: {LaTeXMode.textmode: ',,'},
+ 8224: {LaTeXMode.textmode: '\\textdagger ', LaTeXMode.mathmode: '\\dagger'},
+ 8225: {LaTeXMode.textmode: '\\textdaggerdbl ', LaTeXMode.mathmode: '\\ddagger'},
+ 8226: {LaTeXMode.textmode: '\\textbullet ', LaTeXMode.mathmode: '\\bullet'},
+ 8228: {LaTeXMode.textmode: '.'},
+ 8229: {LaTeXMode.textmode: '..'},
+ 8230: {LaTeXMode.textmode: '\\ldots ', LaTeXMode.mathmode: '\\dots'},
+ 8240: {LaTeXMode.textmode: '\\textperthousand '},
+ 8241: {LaTeXMode.textmode: '\\textpertenthousand '},
+ 8242: {LaTeXMode.textmode: "{'}", LaTeXMode.mathmode: '\\prime'},
+ 8243: {LaTeXMode.textmode: "{''}"},
+ 8244: {LaTeXMode.textmode: "{'''}"},
+ 8245: {LaTeXMode.textmode: '\\backprime ', LaTeXMode.mathmode: '\\backprime'},
+ 8279: {LaTeXMode.textmode: "''''"},
+ 8411: {LaTeXMode.textmode: '\\dddot ', LaTeXMode.mathmode: '\\dddot'},
+ 8412: {LaTeXMode.textmode: '\\ddddot ', LaTeXMode.mathmode: '\\ddddot'},
+ 8421: {LaTeXMode.textmode: '{\\rlap{\\textbackslash}{{/}\\!\\!{/}}}'},
+ 8450: {LaTeXMode.textmode: '\\mathbb{C}'},
+ 8459: {LaTeXMode.textmode: '\\mathscr{H}'},
+ 8460: {LaTeXMode.textmode: '\\mathfrak{H}'},
+ 8461: {LaTeXMode.textmode: '\\mathbb{H}'},
+ 8463: {LaTeXMode.textmode: '\\hslash ', LaTeXMode.mathmode: '\\hslash'},
+ 8464: {LaTeXMode.textmode: '\\mathscr{I}'},
+ 8465: {LaTeXMode.textmode: '\\mathfrak{I}', LaTeXMode.mathmode: '\\Im'},
+ 8466: {LaTeXMode.textmode: '\\mathscr{L}'},
+ 8467: {LaTeXMode.textmode: '\\mathscr{l}', LaTeXMode.mathmode: '\\ell'},
+ 8469: {LaTeXMode.textmode: '\\mathbb{N}'},
+ 8470: {LaTeXMode.textmode: '\\cyrchar\\textnumero ', LaTeXMode.mathmode: '\\textnumero'},
+ 8472: {LaTeXMode.textmode: '\\wp ', LaTeXMode.mathmode: '\\wp'},
+ 8473: {LaTeXMode.textmode: '\\mathbb{P}'},
+ 8474: {LaTeXMode.textmode: '\\mathbb{Q}'},
+ 8475: {LaTeXMode.textmode: '\\mathscr{R}'},
+ 8476: {LaTeXMode.textmode: '\\mathfrak{R}', LaTeXMode.mathmode: '\\Re'},
+ 8477: {LaTeXMode.textmode: '\\mathbb{R}'},
+ 8478: {LaTeXMode.textmode: '\\Elzxrat '},
+ 8482: {LaTeXMode.textmode: '\\texttrademark '},
+ 8484: {LaTeXMode.textmode: '\\mathbb{Z}'},
+ 8486: {LaTeXMode.textmode: '\\Omega '},
+ 8487: {LaTeXMode.textmode: '\\mho ', LaTeXMode.mathmode: '\\mho'},
+ 8488: {LaTeXMode.textmode: '\\mathfrak{Z}'},
+ 8489: {LaTeXMode.textmode: '\\ElsevierGlyph{2129}'},
+ 8491: {LaTeXMode.textmode: '\\AA ', LaTeXMode.mathmode: '\\AA'},
+ 8492: {LaTeXMode.textmode: '\\mathscr{B}'},
+ 8493: {LaTeXMode.textmode: '\\mathfrak{C}'},
+ 8495: {LaTeXMode.textmode: '\\mathscr{e}'},
+ 8496: {LaTeXMode.textmode: '\\mathscr{E}'},
+ 8497: {LaTeXMode.textmode: '\\mathscr{F}'},
+ 8499: {LaTeXMode.textmode: '\\mathscr{M}'},
+ 8500: {LaTeXMode.textmode: '\\mathscr{o}'},
+ 8501: {LaTeXMode.textmode: '\\aleph ', LaTeXMode.mathmode: '\\aleph'},
+ 8502: {LaTeXMode.textmode: '\\beth ', LaTeXMode.mathmode: '\\beth'},
+ 8503: {LaTeXMode.textmode: '\\gimel ', LaTeXMode.mathmode: '\\gimel'},
+ 8504: {LaTeXMode.textmode: '\\daleth ', LaTeXMode.mathmode: '\\daleth'},
+ 8512: {LaTeXMode.mathmode: '\\bbsum'},
+ 8531: {LaTeXMode.textmode: '\\textfrac{1}{3}'},
+ 8532: {LaTeXMode.textmode: '\\textfrac{2}{3}'},
+ 8533: {LaTeXMode.textmode: '\\textfrac{1}{5}'},
+ 8534: {LaTeXMode.textmode: '\\textfrac{2}{5}'},
+ 8535: {LaTeXMode.textmode: '\\textfrac{3}{5}'},
+ 8536: {LaTeXMode.textmode: '\\textfrac{4}{5}'},
+ 8537: {LaTeXMode.textmode: '\\textfrac{1}{6}'},
+ 8538: {LaTeXMode.textmode: '\\textfrac{5}{6}'},
+ 8539: {LaTeXMode.textmode: '\\textfrac{1}{8}'},
+ 8540: {LaTeXMode.textmode: '\\textfrac{3}{8}'},
+ 8541: {LaTeXMode.textmode: '\\textfrac{5}{8}'},
+ 8542: {LaTeXMode.textmode: '\\textfrac{7}{8}'},
+ 8592: {LaTeXMode.textmode: '\\leftarrow ', LaTeXMode.mathmode: '\\leftarrow'},
+ 8593: {LaTeXMode.textmode: '\\uparrow ', LaTeXMode.mathmode: '\\uparrow'},
+ 8594: {LaTeXMode.textmode: '\\rightarrow ', LaTeXMode.mathmode: '\\rightarrow'},
+ 8595: {LaTeXMode.textmode: '\\downarrow ', LaTeXMode.mathmode: '\\downarrow'},
+ 8596: {LaTeXMode.textmode: '\\leftrightarrow ', LaTeXMode.mathmode: '\\leftrightarrow'},
+ 8597: {LaTeXMode.textmode: '\\updownarrow ', LaTeXMode.mathmode: '\\updownarrow'},
+ 8598: {LaTeXMode.textmode: '\\nwarrow ', LaTeXMode.mathmode: '\\nwarrow'},
+ 8599: {LaTeXMode.textmode: '\\nearrow ', LaTeXMode.mathmode: '\\nearrow'},
+ 8600: {LaTeXMode.textmode: '\\searrow ', LaTeXMode.mathmode: '\\searrow'},
+ 8601: {LaTeXMode.textmode: '\\swarrow ', LaTeXMode.mathmode: '\\swarrow'},
+ 8602: {LaTeXMode.textmode: '\\nleftarrow ', LaTeXMode.mathmode: '\\nleftarrow'},
+ 8603: {LaTeXMode.textmode: '\\nrightarrow ', LaTeXMode.mathmode: '\\nrightarrow'},
+ 8604: {LaTeXMode.textmode: '\\arrowwaveleft ', LaTeXMode.mathmode: '\\leftsquigarrow'},
+ 8605: {LaTeXMode.textmode: '\\arrowwaveright ', LaTeXMode.mathmode: '\\rightsquigarrow'},
+ 8606: {LaTeXMode.textmode: '\\twoheadleftarrow ', LaTeXMode.mathmode: '\\twoheadleftarrow'},
+ 8608: {LaTeXMode.textmode: '\\twoheadrightarrow ', LaTeXMode.mathmode: '\\twoheadrightarrow'},
+ 8610: {LaTeXMode.textmode: '\\leftarrowtail ', LaTeXMode.mathmode: '\\leftarrowtail'},
+ 8611: {LaTeXMode.textmode: '\\rightarrowtail ', LaTeXMode.mathmode: '\\rightarrowtail'},
+ 8614: {LaTeXMode.textmode: '\\mapsto ', LaTeXMode.mathmode: '\\mapsto'},
+ 8617: {LaTeXMode.textmode: '\\hookleftarrow ', LaTeXMode.mathmode: '\\hookleftarrow'},
+ 8618: {LaTeXMode.textmode: '\\hookrightarrow ', LaTeXMode.mathmode: '\\hookrightarrow'},
+ 8619: {LaTeXMode.textmode: '\\looparrowleft ', LaTeXMode.mathmode: '\\looparrowleft'},
+ 8620: {LaTeXMode.textmode: '\\looparrowright ', LaTeXMode.mathmode: '\\looparrowright'},
+ 8621: {LaTeXMode.textmode: '\\leftrightsquigarrow ', LaTeXMode.mathmode: '\\leftrightsquigarrow'},
+ 8622: {LaTeXMode.textmode: '\\nleftrightarrow ', LaTeXMode.mathmode: '\\nleftrightarrow'},
+ 8624: {LaTeXMode.textmode: '\\Lsh ', LaTeXMode.mathmode: '\\Lsh'},
+ 8625: {LaTeXMode.textmode: '\\Rsh ', LaTeXMode.mathmode: '\\Rsh'},
+ 8627: {LaTeXMode.textmode: '\\ElsevierGlyph{21B3}'},
+ 8630: {LaTeXMode.textmode: '\\curvearrowleft ', LaTeXMode.mathmode: '\\curvearrowleft'},
+ 8631: {LaTeXMode.textmode: '\\curvearrowright ', LaTeXMode.mathmode: '\\curvearrowright'},
+ 8634: {LaTeXMode.textmode: '\\circlearrowleft '},
+ 8635: {LaTeXMode.textmode: '\\circlearrowright '},
+ 8636: {LaTeXMode.textmode: '\\leftharpoonup ', LaTeXMode.mathmode: '\\leftharpoonup'},
+ 8637: {LaTeXMode.textmode: '\\leftharpoondown ', LaTeXMode.mathmode: '\\leftharpoondown'},
+ 8638: {LaTeXMode.textmode: '\\upharpoonright ', LaTeXMode.mathmode: '\\upharpoonleft'},
+ 8639: {LaTeXMode.textmode: '\\upharpoonleft ', LaTeXMode.mathmode: '\\upharpoonright'},
+ 8640: {LaTeXMode.textmode: '\\rightharpoonup ', LaTeXMode.mathmode: '\\rightharpoonup'},
+ 8641: {LaTeXMode.textmode: '\\rightharpoondown ', LaTeXMode.mathmode: '\\rightharpoondown'},
+ 8642: {LaTeXMode.textmode: '\\downharpoonright ', LaTeXMode.mathmode: '\\downharpoonright'},
+ 8643: {LaTeXMode.textmode: '\\downharpoonleft ', LaTeXMode.mathmode: '\\downharpoonleft'},
+ 8644: {LaTeXMode.textmode: '\\rightleftarrows ', LaTeXMode.mathmode: '\\rightleftarrows'},
+ 8645: {LaTeXMode.textmode: '\\dblarrowupdown '},
+ 8646: {LaTeXMode.textmode: '\\leftrightarrows ', LaTeXMode.mathmode: '\\leftrightarrows'},
+ 8647: {LaTeXMode.textmode: '\\leftleftarrows ', LaTeXMode.mathmode: '\\leftleftarrows'},
+ 8648: {LaTeXMode.textmode: '\\upuparrows ', LaTeXMode.mathmode: '\\upuparrows'},
+ 8649: {LaTeXMode.textmode: '\\rightrightarrows ', LaTeXMode.mathmode: '\\rightrightarrows'},
+ 8650: {LaTeXMode.textmode: '\\downdownarrows ', LaTeXMode.mathmode: '\\downdownarrows'},
+ 8651: {LaTeXMode.textmode: '\\leftrightharpoons ', LaTeXMode.mathmode: '\\leftrightharpoons'},
+ 8652: {LaTeXMode.textmode: '\\rightleftharpoons ', LaTeXMode.mathmode: '\\rightleftharpoons'},
+ 8653: {LaTeXMode.textmode: '\\nLeftarrow ', LaTeXMode.mathmode: '\\nLeftarrow'},
+ 8654: {LaTeXMode.textmode: '\\nLeftrightarrow ', LaTeXMode.mathmode: '\\nLeftrightarrow'},
+ 8655: {LaTeXMode.textmode: '\\nRightarrow ', LaTeXMode.mathmode: '\\nRightarrow'},
+ 8656: {LaTeXMode.textmode: '\\Leftarrow ', LaTeXMode.mathmode: '\\Leftarrow'},
+ 8657: {LaTeXMode.textmode: '\\Uparrow ', LaTeXMode.mathmode: '\\Uparrow'},
+ 8658: {LaTeXMode.textmode: '\\Rightarrow ', LaTeXMode.mathmode: '\\Rightarrow'},
+ 8659: {LaTeXMode.textmode: '\\Downarrow ', LaTeXMode.mathmode: '\\Downarrow'},
+ 8660: {LaTeXMode.textmode: '\\Leftrightarrow ', LaTeXMode.mathmode: '\\Leftrightarrow'},
+ 8661: {LaTeXMode.textmode: '\\Updownarrow ', LaTeXMode.mathmode: '\\Updownarrow'},
+ 8666: {LaTeXMode.textmode: '\\Lleftarrow ', LaTeXMode.mathmode: '\\Lleftarrow'},
+ 8667: {LaTeXMode.textmode: '\\Rrightarrow ', LaTeXMode.mathmode: '\\Rrightarrow'},
+ 8669: {LaTeXMode.textmode: '\\rightsquigarrow '},
+ 8693: {LaTeXMode.textmode: '\\DownArrowUpArrow '},
+ 8701: {LaTeXMode.mathmode: '\\leftarrowtriangle'},
+ 8702: {LaTeXMode.mathmode: '\\rightarrowtriangle'},
+ 8703: {LaTeXMode.mathmode: '\\leftrightarrowtria*'},
+ 8704: {LaTeXMode.textmode: '\\forall ', LaTeXMode.mathmode: '\\forall'},
+ 8705: {LaTeXMode.textmode: '\\complement ', LaTeXMode.mathmode: '\\complement'},
+ 8706: {LaTeXMode.textmode: '\\partial ', LaTeXMode.mathmode: '\\partial'},
+ 8707: {LaTeXMode.textmode: '\\exists ', LaTeXMode.mathmode: '\\exists'},
+ 8708: {LaTeXMode.textmode: '\\nexists ', LaTeXMode.mathmode: '\\nexists'},
+ 8709: {LaTeXMode.textmode: '\\varnothing ', LaTeXMode.mathmode: '\\varnothing'},
+ 8711: {LaTeXMode.textmode: '\\nabla ', LaTeXMode.mathmode: '\\nabla'},
+ 8712: {LaTeXMode.textmode: '\\in '},
+ 8713: {LaTeXMode.textmode: '\\not\\in ', LaTeXMode.mathmode: '\\notin'},
+ 8714: {LaTeXMode.mathmode: '\\in'},
+ 8715: {LaTeXMode.textmode: '\\ni ', LaTeXMode.mathmode: '\\ni'},
+ 8716: {LaTeXMode.textmode: '\\not\\ni '},
+ 8719: {LaTeXMode.textmode: '\\prod ', LaTeXMode.mathmode: '\\prod'},
+ 8720: {LaTeXMode.textmode: '\\coprod ', LaTeXMode.mathmode: '\\coprod'},
+ 8721: {LaTeXMode.textmode: '\\sum ', LaTeXMode.mathmode: '\\sum'},
+ 8722: {LaTeXMode.textmode: '-', LaTeXMode.mathmode: '-'},
+ 8723: {LaTeXMode.textmode: '\\mp ', LaTeXMode.mathmode: '\\mp'},
+ 8724: {LaTeXMode.textmode: '\\dotplus ', LaTeXMode.mathmode: '\\dotplus'},
+ 8726: {LaTeXMode.textmode: '\\setminus ', LaTeXMode.mathmode: '\\setminus'},
+ 8727: {LaTeXMode.textmode: '{_\\ast}', LaTeXMode.mathmode: '\\ast'},
+ 8728: {LaTeXMode.textmode: '\\circ ', LaTeXMode.mathmode: '\\circ'},
+ 8729: {LaTeXMode.textmode: '\\bullet '},
+ 8730: {LaTeXMode.textmode: '\\surd ', LaTeXMode.mathmode: '\\surd'},
+ 8733: {LaTeXMode.textmode: '\\propto ', LaTeXMode.mathmode: '\\propto'},
+ 8734: {LaTeXMode.textmode: '\\infty ', LaTeXMode.mathmode: '\\infty'},
+ 8735: {LaTeXMode.textmode: '\\rightangle '},
+ 8736: {LaTeXMode.textmode: '\\angle ', LaTeXMode.mathmode: '\\angle'},
+ 8737: {LaTeXMode.textmode: '\\measuredangle ', LaTeXMode.mathmode: '\\measuredangle'},
+ 8738: {LaTeXMode.textmode: '\\sphericalangle ', LaTeXMode.mathmode: '\\sphericalangle'},
+ 8739: {LaTeXMode.textmode: '\\mid ', LaTeXMode.mathmode: '\\mid'},
+ 8740: {LaTeXMode.textmode: '\\nmid ', LaTeXMode.mathmode: '\\nmid'},
+ 8741: {LaTeXMode.textmode: '\\parallel ', LaTeXMode.mathmode: '\\parallel'},
+ 8742: {LaTeXMode.textmode: '\\nparallel ', LaTeXMode.mathmode: '\\nparallel'},
+ 8743: {LaTeXMode.textmode: '\\wedge ', LaTeXMode.mathmode: '\\wedge'},
+ 8744: {LaTeXMode.textmode: '\\vee ', LaTeXMode.mathmode: '\\vee'},
+ 8745: {LaTeXMode.textmode: '\\cap ', LaTeXMode.mathmode: '\\cap'},
+ 8746: {LaTeXMode.textmode: '\\cup ', LaTeXMode.mathmode: '\\cup'},
+ 8747: {LaTeXMode.textmode: '\\int ', LaTeXMode.mathmode: '\\int'},
+ 8748: {LaTeXMode.textmode: '\\int\\!\\int ', LaTeXMode.mathmode: '\\iint'},
+ 8749: {LaTeXMode.textmode: '\\int\\!\\int\\!\\int ', LaTeXMode.mathmode: '\\iiint'},
+ 8750: {LaTeXMode.textmode: '\\oint ', LaTeXMode.mathmode: '\\oint'},
+ 8751: {LaTeXMode.textmode: '\\surfintegral ', LaTeXMode.mathmode: '\\oiint'},
+ 8752: {LaTeXMode.textmode: '\\volintegral ', LaTeXMode.mathmode: '\\oiiint'},
+ 8753: {LaTeXMode.textmode: '\\clwintegral '},
+ 8754: {LaTeXMode.textmode: '\\ElsevierGlyph{2232}'},
+ 8755: {LaTeXMode.textmode: '\\ElsevierGlyph{2233}'},
+ 8756: {LaTeXMode.textmode: '\\therefore ', LaTeXMode.mathmode: '\\therefore'},
+ 8757: {LaTeXMode.textmode: '\\because ', LaTeXMode.mathmode: '\\because'},
+ 8759: {LaTeXMode.textmode: '\\Colon ', LaTeXMode.mathmode: '\\Colon'},
+ 8760: {LaTeXMode.textmode: '\\ElsevierGlyph{2238}', LaTeXMode.mathmode: '\\dotminus'},
+ 8762: {LaTeXMode.textmode: '\\mathbin{{:}\\!\\!{-}\\!\\!{:}}'},
+ 8763: {LaTeXMode.textmode: '\\homothetic ', LaTeXMode.mathmode: '\\kernelcontraction'},
+ 8764: {LaTeXMode.textmode: '\\sim ', LaTeXMode.mathmode: '\\sim'},
+ 8765: {LaTeXMode.textmode: '\\backsim ', LaTeXMode.mathmode: '\\backsim'},
+ 8766: {LaTeXMode.textmode: '\\lazysinv '},
+ 8768: {LaTeXMode.textmode: '\\wr ', LaTeXMode.mathmode: '\\wr'},
+ 8769: {LaTeXMode.textmode: '\\not\\sim ', LaTeXMode.mathmode: '\\nsim'},
+ 8770: {LaTeXMode.textmode: '\\NotEqualTilde ', LaTeXMode.mathmode: '\\neqsim'},
+ 8771: {LaTeXMode.textmode: '\\simeq ', LaTeXMode.mathmode: '\\simeq'},
+ 8772: {LaTeXMode.textmode: '\\not\\simeq ', LaTeXMode.mathmode: '\\nsime'},
+ 8773: {LaTeXMode.textmode: '\\cong ', LaTeXMode.mathmode: '\\cong'},
+ 8774: {LaTeXMode.textmode: '\\approxnotequal '},
+ 8775: {LaTeXMode.textmode: '\\not\\cong ', LaTeXMode.mathmode: '\\ncong'},
+ 8776: {LaTeXMode.textmode: '\\approx ', LaTeXMode.mathmode: '\\approx'},
+ 8777: {LaTeXMode.textmode: '\\not\\approx ', LaTeXMode.mathmode: '\\napprox'},
+ 8778: {LaTeXMode.textmode: '\\approxeq ', LaTeXMode.mathmode: '\\approxeq'},
+ 8779: {LaTeXMode.textmode: '\\not\\apid '},
+ 8780: {LaTeXMode.textmode: '\\allequal '},
+ 8781: {LaTeXMode.textmode: '\\asymp ', LaTeXMode.mathmode: '\\asymp'},
+ 8782: {LaTeXMode.textmode: '\\NotHumpDownHump ', LaTeXMode.mathmode: '\\nBumpeq'},
+ 8783: {LaTeXMode.textmode: '\\NotHumpEqual ', LaTeXMode.mathmode: '\\nbumpeq'},
+ 8784: {LaTeXMode.textmode: '\\not\\doteq'},
+ 8785: {LaTeXMode.textmode: '\\doteqdot ', LaTeXMode.mathmode: '\\Doteq'},
+ 8786: {LaTeXMode.textmode: '\\fallingdotseq ', LaTeXMode.mathmode: '\\fallingdotseq'},
+ 8787: {LaTeXMode.textmode: '\\risingdotseq ', LaTeXMode.mathmode: '\\risingdotseq'},
+ 8788: {LaTeXMode.textmode: ':=', LaTeXMode.mathmode: '\\coloneq'},
+ 8789: {LaTeXMode.textmode: '=:', LaTeXMode.mathmode: '\\eqcolon'},
+ 8790: {LaTeXMode.textmode: '\\eqcirc ', LaTeXMode.mathmode: '\\eqcirc'},
+ 8791: {LaTeXMode.textmode: '\\circeq ', LaTeXMode.mathmode: '\\circeq'},
+ 8793: {LaTeXMode.textmode: '\\estimates ', LaTeXMode.mathmode: '\\wedgeq'},
+ 8794: {LaTeXMode.textmode: '\\ElsevierGlyph{225A}'},
+ 8795: {LaTeXMode.textmode: '\\starequal '},
+ 8796: {LaTeXMode.textmode: '\\triangleq ', LaTeXMode.mathmode: '\\triangleq'},
+ 8799: {LaTeXMode.textmode: '\\ElsevierGlyph{225F}', LaTeXMode.mathmode: '\\questeq'},
+ 8800: {LaTeXMode.textmode: '\\not =', LaTeXMode.mathmode: '\\ne'},
+ 8801: {LaTeXMode.textmode: '\\equiv ', LaTeXMode.mathmode: '\\equiv'},
+ 8802: {LaTeXMode.textmode: '\\not\\equiv ', LaTeXMode.mathmode: '\\nequiv'},
+ 8804: {LaTeXMode.textmode: '\\leq ', LaTeXMode.mathmode: '\\le'},
+ 8805: {LaTeXMode.textmode: '\\geq ', LaTeXMode.mathmode: '\\ge'},
+ 8806: {LaTeXMode.textmode: '\\leqq ', LaTeXMode.mathmode: '\\leqq'},
+ 8807: {LaTeXMode.textmode: '\\geqq ', LaTeXMode.mathmode: '\\geqq'},
+ 8808: {LaTeXMode.textmode: '\\lvertneqq ', LaTeXMode.mathmode: '\\lvertneqq'},
+ 8809: {LaTeXMode.textmode: '\\gvertneqq ', LaTeXMode.mathmode: '\\gvertneqq'},
+ 8810: {LaTeXMode.textmode: '\\NotLessLess '},
+ 8811: {LaTeXMode.textmode: '\\NotGreaterGreater '},
+ 8812: {LaTeXMode.textmode: '\\between ', LaTeXMode.mathmode: '\\between'},
+ 8813: {LaTeXMode.textmode: '\\not\\kern-0.3em\\times '},
+ 8814: {LaTeXMode.textmode: '\\not<', LaTeXMode.mathmode: '\\nless'},
+ 8815: {LaTeXMode.textmode: '\\not>', LaTeXMode.mathmode: '\\ngtr'},
+ 8816: {LaTeXMode.textmode: '\\not\\leq ', LaTeXMode.mathmode: '\\nleq'},
+ 8817: {LaTeXMode.textmode: '\\not\\geq ', LaTeXMode.mathmode: '\\ngeq'},
+ 8818: {LaTeXMode.textmode: '\\lessequivlnt ', LaTeXMode.mathmode: '\\lesssim'},
+ 8819: {LaTeXMode.textmode: '\\greaterequivlnt ', LaTeXMode.mathmode: '\\gtrsim'},
+ 8820: {LaTeXMode.textmode: '\\ElsevierGlyph{2274}'},
+ 8821: {LaTeXMode.textmode: '\\ElsevierGlyph{2275}'},
+ 8822: {LaTeXMode.textmode: '\\lessgtr ', LaTeXMode.mathmode: '\\lessgtr'},
+ 8823: {LaTeXMode.textmode: '\\gtrless ', LaTeXMode.mathmode: '\\gtrless'},
+ 8824: {LaTeXMode.textmode: '\\notlessgreater '},
+ 8825: {LaTeXMode.textmode: '\\notgreaterless '},
+ 8826: {LaTeXMode.textmode: '\\prec ', LaTeXMode.mathmode: '\\prec'},
+ 8827: {LaTeXMode.textmode: '\\succ ', LaTeXMode.mathmode: '\\succ'},
+ 8828: {LaTeXMode.textmode: '\\preccurlyeq ', LaTeXMode.mathmode: '\\preccurlyeq'},
+ 8829: {LaTeXMode.textmode: '\\succcurlyeq ', LaTeXMode.mathmode: '\\succcurlyeq'},
+ 8830: {LaTeXMode.textmode: '\\NotPrecedesTilde ', LaTeXMode.mathmode: '\\nprecsim'},
+ 8831: {LaTeXMode.textmode: '\\NotSucceedsTilde ', LaTeXMode.mathmode: '\\nsuccsim'},
+ 8832: {LaTeXMode.textmode: '\\not\\prec ', LaTeXMode.mathmode: '\\nprec'},
+ 8833: {LaTeXMode.textmode: '\\not\\succ ', LaTeXMode.mathmode: '\\nsucc'},
+ 8834: {LaTeXMode.textmode: '\\subset ', LaTeXMode.mathmode: '\\subset'},
+ 8835: {LaTeXMode.textmode: '\\supset ', LaTeXMode.mathmode: '\\supset'},
+ 8836: {LaTeXMode.textmode: '\\not\\subset ', LaTeXMode.mathmode: '\\nsubset'},
+ 8837: {LaTeXMode.textmode: '\\not\\supset ', LaTeXMode.mathmode: '\\nsupset'},
+ 8838: {LaTeXMode.textmode: '\\subseteq ', LaTeXMode.mathmode: '\\subseteq'},
+ 8839: {LaTeXMode.textmode: '\\supseteq ', LaTeXMode.mathmode: '\\supseteq'},
+ 8840: {LaTeXMode.textmode: '\\not\\subseteq ', LaTeXMode.mathmode: '\\nsubseteq'},
+ 8841: {LaTeXMode.textmode: '\\not\\supseteq ', LaTeXMode.mathmode: '\\nsupseteq'},
+ 8842: {LaTeXMode.textmode: '\\varsubsetneqq ', LaTeXMode.mathmode: '\\varsubsetneqq'},
+ 8843: {LaTeXMode.textmode: '\\varsupsetneq ', LaTeXMode.mathmode: '\\varsupsetneq'},
+ 8846: {LaTeXMode.textmode: '\\uplus ', LaTeXMode.mathmode: '\\uplus'},
+ 8847: {LaTeXMode.textmode: '\\NotSquareSubset '},
+ 8848: {LaTeXMode.textmode: '\\NotSquareSuperset '},
+ 8849: {LaTeXMode.textmode: '\\sqsubseteq ', LaTeXMode.mathmode: '\\sqsubseteq'},
+ 8850: {LaTeXMode.textmode: '\\sqsupseteq ', LaTeXMode.mathmode: '\\sqsupseteq'},
+ 8851: {LaTeXMode.textmode: '\\sqcap ', LaTeXMode.mathmode: '\\sqcap'},
+ 8852: {LaTeXMode.textmode: '\\sqcup ', LaTeXMode.mathmode: '\\sqcup'},
+ 8853: {LaTeXMode.textmode: '\\oplus ', LaTeXMode.mathmode: '\\oplus'},
+ 8854: {LaTeXMode.textmode: '\\ominus ', LaTeXMode.mathmode: '\\ominus'},
+ 8855: {LaTeXMode.textmode: '\\otimes ', LaTeXMode.mathmode: '\\otimes'},
+ 8856: {LaTeXMode.textmode: '\\oslash ', LaTeXMode.mathmode: '\\oslash'},
+ 8857: {LaTeXMode.textmode: '\\odot ', LaTeXMode.mathmode: '\\odot'},
+ 8858: {LaTeXMode.textmode: '\\circledcirc ', LaTeXMode.mathmode: '\\circledcirc'},
+ 8859: {LaTeXMode.textmode: '\\circledast ', LaTeXMode.mathmode: '\\circledast'},
+ 8861: {LaTeXMode.textmode: '\\circleddash ', LaTeXMode.mathmode: '\\circleddash'},
+ 8862: {LaTeXMode.textmode: '\\boxplus ', LaTeXMode.mathmode: '\\boxplus'},
+ 8863: {LaTeXMode.textmode: '\\boxminus ', LaTeXMode.mathmode: '\\boxminus'},
+ 8864: {LaTeXMode.textmode: '\\boxtimes ', LaTeXMode.mathmode: '\\boxtimes'},
+ 8865: {LaTeXMode.textmode: '\\boxdot ', LaTeXMode.mathmode: '\\boxdot'},
+ 8866: {LaTeXMode.textmode: '\\vdash ', LaTeXMode.mathmode: '\\vdash'},
+ 8867: {LaTeXMode.textmode: '\\dashv ', LaTeXMode.mathmode: '\\dashv'},
+ 8868: {LaTeXMode.textmode: '\\top ', LaTeXMode.mathmode: '\\top'},
+ 8869: {LaTeXMode.textmode: '\\perp ', LaTeXMode.mathmode: '\\perp'},
+ 8871: {LaTeXMode.textmode: '\\truestate ', LaTeXMode.mathmode: '\\models'},
+ 8872: {LaTeXMode.textmode: '\\forcesextra ', LaTeXMode.mathmode: '\\vDash'},
+ 8873: {LaTeXMode.textmode: '\\Vdash ', LaTeXMode.mathmode: '\\Vdash'},
+ 8874: {LaTeXMode.textmode: '\\Vvdash ', LaTeXMode.mathmode: '\\Vvdash'},
+ 8875: {LaTeXMode.textmode: '\\VDash '},
+ 8876: {LaTeXMode.textmode: '\\nvdash ', LaTeXMode.mathmode: '\\nvdash'},
+ 8877: {LaTeXMode.textmode: '\\nvDash ', LaTeXMode.mathmode: '\\nvDash'},
+ 8878: {LaTeXMode.textmode: '\\nVdash ', LaTeXMode.mathmode: '\\nVdash'},
+ 8879: {LaTeXMode.textmode: '\\nVDash ', LaTeXMode.mathmode: '\\nVDash'},
+ 8882: {LaTeXMode.textmode: '\\vartriangleleft ', LaTeXMode.mathmode: '\\vartriangleleft'},
+ 8883: {LaTeXMode.textmode: '\\vartriangleright ', LaTeXMode.mathmode: '\\vartriangleright'},
+ 8884: {LaTeXMode.textmode: '\\trianglelefteq ', LaTeXMode.mathmode: '\\trianglelefteq'},
+ 8885: {LaTeXMode.textmode: '\\trianglerighteq ', LaTeXMode.mathmode: '\\trianglerighteq'},
+ 8886: {LaTeXMode.textmode: '\\original '},
+ 8887: {LaTeXMode.textmode: '\\image '},
+ 8888: {LaTeXMode.textmode: '\\multimap ', LaTeXMode.mathmode: '\\multimap'},
+ 8889: {LaTeXMode.textmode: '\\hermitconjmatrix '},
+ 8890: {LaTeXMode.textmode: '\\intercal ', LaTeXMode.mathmode: '\\intercal'},
+ 8891: {LaTeXMode.textmode: '\\veebar ', LaTeXMode.mathmode: '\\veebar'},
+ 8894: {LaTeXMode.textmode: '\\rightanglearc '},
+ 8896: {LaTeXMode.textmode: '\\ElsevierGlyph{22C0}', LaTeXMode.mathmode: '\\bigwedge'},
+ 8897: {LaTeXMode.textmode: '\\ElsevierGlyph{22C1}', LaTeXMode.mathmode: '\\bigvee'},
+ 8898: {LaTeXMode.textmode: '\\bigcap ', LaTeXMode.mathmode: '\\bigcap'},
+ 8899: {LaTeXMode.textmode: '\\bigcup ', LaTeXMode.mathmode: '\\bigcup'},
+ 8900: {LaTeXMode.textmode: '\\diamond ', LaTeXMode.mathmode: '\\diamond'},
+ 8901: {LaTeXMode.textmode: '\\cdot ', LaTeXMode.mathmode: '\\cdot'},
+ 8902: {LaTeXMode.textmode: '\\star ', LaTeXMode.mathmode: '\\star'},
+ 8903: {LaTeXMode.textmode: '\\divideontimes ', LaTeXMode.mathmode: '\\divideontimes'},
+ 8904: {LaTeXMode.textmode: '\\bowtie ', LaTeXMode.mathmode: '\\bowtie'},
+ 8905: {LaTeXMode.textmode: '\\ltimes ', LaTeXMode.mathmode: '\\ltimes'},
+ 8906: {LaTeXMode.textmode: '\\rtimes ', LaTeXMode.mathmode: '\\rtimes'},
+ 8907: {LaTeXMode.textmode: '\\leftthreetimes ', LaTeXMode.mathmode: '\\leftthreetimes'},
+ 8908: {LaTeXMode.textmode: '\\rightthreetimes ', LaTeXMode.mathmode: '\\rightthreetimes'},
+ 8909: {LaTeXMode.textmode: '\\backsimeq ', LaTeXMode.mathmode: '\\backsimeq'},
+ 8910: {LaTeXMode.textmode: '\\curlyvee ', LaTeXMode.mathmode: '\\curlyvee'},
+ 8911: {LaTeXMode.textmode: '\\curlywedge ', LaTeXMode.mathmode: '\\curlywedge'},
+ 8912: {LaTeXMode.textmode: '\\Subset ', LaTeXMode.mathmode: '\\Subset'},
+ 8913: {LaTeXMode.textmode: '\\Supset ', LaTeXMode.mathmode: '\\Supset'},
+ 8914: {LaTeXMode.textmode: '\\Cap ', LaTeXMode.mathmode: '\\Cap'},
+ 8915: {LaTeXMode.textmode: '\\Cup ', LaTeXMode.mathmode: '\\Cup'},
+ 8916: {LaTeXMode.textmode: '\\pitchfork ', LaTeXMode.mathmode: '\\pitchfork'},
+ 8918: {LaTeXMode.textmode: '\\lessdot ', LaTeXMode.mathmode: '\\lessdot'},
+ 8919: {LaTeXMode.textmode: '\\gtrdot ', LaTeXMode.mathmode: '\\gtrdot'},
+ 8920: {LaTeXMode.textmode: '\\verymuchless '},
+ 8921: {LaTeXMode.textmode: '\\verymuchgreater ', LaTeXMode.mathmode: '\\ggg'},
+ 8922: {LaTeXMode.textmode: '\\lesseqgtr ', LaTeXMode.mathmode: '\\lesseqgtr'},
+ 8923: {LaTeXMode.textmode: '\\gtreqless ', LaTeXMode.mathmode: '\\gtreqless'},
+ 8926: {LaTeXMode.textmode: '\\curlyeqprec ', LaTeXMode.mathmode: '\\curlyeqprec'},
+ 8927: {LaTeXMode.textmode: '\\curlyeqsucc ', LaTeXMode.mathmode: '\\curlyeqsucc'},
+ 8930: {LaTeXMode.textmode: '\\not\\sqsubseteq '},
+ 8931: {LaTeXMode.textmode: '\\not\\sqsupseteq '},
+ 8933: {LaTeXMode.textmode: '\\Elzsqspne '},
+ 8934: {LaTeXMode.textmode: '\\lnsim ', LaTeXMode.mathmode: '\\lnsim'},
+ 8935: {LaTeXMode.textmode: '\\gnsim ', LaTeXMode.mathmode: '\\gnsim'},
+ 8936: {LaTeXMode.textmode: '\\precedesnotsimilar ', LaTeXMode.mathmode: '\\precnsim'},
+ 8937: {LaTeXMode.textmode: '\\succnsim ', LaTeXMode.mathmode: '\\succnsim'},
+ 8938: {LaTeXMode.textmode: '\\ntriangleleft ', LaTeXMode.mathmode: '\\ntriangleleft'},
+ 8939: {LaTeXMode.textmode: '\\ntriangleright ', LaTeXMode.mathmode: '\\ntriangleright'},
+ 8940: {LaTeXMode.textmode: '\\ntrianglelefteq ', LaTeXMode.mathmode: '\\ntrianglelefteq'},
+ 8941: {LaTeXMode.textmode: '\\ntrianglerighteq ', LaTeXMode.mathmode: '\\ntrianglerighteq'},
+ 8942: {LaTeXMode.textmode: '\\vdots ', LaTeXMode.mathmode: '\\vdots'},
+ 8943: {LaTeXMode.textmode: '\\cdots ', LaTeXMode.mathmode: '\\cdots'},
+ 8944: {LaTeXMode.textmode: '\\upslopeellipsis ', LaTeXMode.mathmode: '\\adots'},
+ 8945: {LaTeXMode.textmode: '\\downslopeellipsis ', LaTeXMode.mathmode: '\\ddots'},
+ 8966: {LaTeXMode.textmode: '\\varperspcorrespond '},
+ 8968: {LaTeXMode.textmode: '\\lceil ', LaTeXMode.mathmode: '\\lceil'},
+ 8969: {LaTeXMode.textmode: '\\rceil ', LaTeXMode.mathmode: '\\rceil'},
+ 8970: {LaTeXMode.textmode: '\\lfloor ', LaTeXMode.mathmode: '\\lfloor'},
+ 8971: {LaTeXMode.textmode: '\\rfloor ', LaTeXMode.mathmode: '\\rfloor'},
+ 8981: {LaTeXMode.textmode: '\\recorder '},
+ 8982: {LaTeXMode.textmode: '\\mathchar"2208'},
+ 8988: {LaTeXMode.textmode: '\\ulcorner ', LaTeXMode.mathmode: '\\ulcorner'},
+ 8989: {LaTeXMode.textmode: '\\urcorner ', LaTeXMode.mathmode: '\\urcorner'},
+ 8990: {LaTeXMode.textmode: '\\llcorner ', LaTeXMode.mathmode: '\\llcorner'},
+ 8991: {LaTeXMode.textmode: '\\lrcorner ', LaTeXMode.mathmode: '\\lrcorner'},
+ 8994: {LaTeXMode.textmode: '\\frown ', LaTeXMode.mathmode: '\\frown'},
+ 8995: {LaTeXMode.textmode: '\\smile ', LaTeXMode.mathmode: '\\smile'},
+ 9021: {LaTeXMode.textmode: '\\ElsevierGlyph{E838}', LaTeXMode.mathmode: '\\obar'},
+ 9123: {LaTeXMode.textmode: '\\Elzdlcorn '},
+ 9136: {LaTeXMode.textmode: '\\lmoustache ', LaTeXMode.mathmode: '\\lmoustache'},
+ 9137: {LaTeXMode.textmode: '\\rmoustache ', LaTeXMode.mathmode: '\\rmoustache'},
+ 9251: {LaTeXMode.textmode: '\\textvisiblespace '},
+ 9312: {LaTeXMode.textmode: '\\ding{172}'},
+ 9313: {LaTeXMode.textmode: '\\ding{173}'},
+ 9314: {LaTeXMode.textmode: '\\ding{174}'},
+ 9315: {LaTeXMode.textmode: '\\ding{175}'},
+ 9316: {LaTeXMode.textmode: '\\ding{176}'},
+ 9317: {LaTeXMode.textmode: '\\ding{177}'},
+ 9318: {LaTeXMode.textmode: '\\ding{178}'},
+ 9319: {LaTeXMode.textmode: '\\ding{179}'},
+ 9320: {LaTeXMode.textmode: '\\ding{180}'},
+ 9321: {LaTeXMode.textmode: '\\ding{181}'},
+ 9416: {LaTeXMode.textmode: '\\circledS ', LaTeXMode.mathmode: '\\circledS'},
+ 9478: {LaTeXMode.textmode: '\\Elzdshfnc '},
+ 9497: {LaTeXMode.textmode: '\\Elzsqfnw '},
+ 9585: {LaTeXMode.textmode: '\\diagup ', LaTeXMode.mathmode: '\\diagup'},
+ 9586: {LaTeXMode.mathmode: '\\diagdown'},
+ 9632: {LaTeXMode.textmode: '\\ding{110}', LaTeXMode.mathmode: '\\blacksquare'},
+ 9633: {LaTeXMode.textmode: '\\square ', LaTeXMode.mathmode: '\\square'},
+ 9642: {LaTeXMode.textmode: '\\blacksquare '},
+ 9645: {LaTeXMode.textmode: '\\fbox{~~}'},
+ 9647: {LaTeXMode.textmode: '\\Elzvrecto '},
+ 9649: {LaTeXMode.textmode: '\\ElsevierGlyph{E381}'},
+ 9650: {LaTeXMode.textmode: '\\ding{115}'},
+ 9651: {LaTeXMode.textmode: '\\bigtriangleup ', LaTeXMode.mathmode: '\\bigtriangleup'},
+ 9652: {LaTeXMode.textmode: '\\blacktriangle ', LaTeXMode.mathmode: '\\blacktriangle'},
+ 9653: {LaTeXMode.textmode: '\\vartriangle ', LaTeXMode.mathmode: '\\vartriangle'},
+ 9656: {LaTeXMode.textmode: '\\blacktriangleright ', LaTeXMode.mathmode: '\\blacktriangleright'},
+ 9657: {LaTeXMode.textmode: '\\triangleright ', LaTeXMode.mathmode: '\\triangleright'},
+ 9660: {LaTeXMode.textmode: '\\ding{116}'},
+ 9661: {LaTeXMode.textmode: '\\bigtriangledown ', LaTeXMode.mathmode: '\\bigtriangledown'},
+ 9662: {LaTeXMode.textmode: '\\blacktriangledown ', LaTeXMode.mathmode: '\\blacktriangledown'},
+ 9663: {LaTeXMode.textmode: '\\triangledown ', LaTeXMode.mathmode: '\\triangledown'},
+ 9666: {LaTeXMode.textmode: '\\blacktriangleleft ', LaTeXMode.mathmode: '\\blacktriangleleft'},
+ 9667: {LaTeXMode.textmode: '\\triangleleft ', LaTeXMode.mathmode: '\\triangleleft'},
+ 9670: {LaTeXMode.textmode: '\\ding{117}'},
+ 9674: {LaTeXMode.textmode: '\\lozenge ', LaTeXMode.mathmode: '\\lozenge'},
+ 9675: {LaTeXMode.textmode: '\\bigcirc ', LaTeXMode.mathmode: '\\bigcirc'},
+ 9679: {LaTeXMode.textmode: '\\ding{108}'},
+ 9680: {LaTeXMode.textmode: '\\Elzcirfl '},
+ 9681: {LaTeXMode.textmode: '\\Elzcirfr '},
+ 9682: {LaTeXMode.textmode: '\\Elzcirfb '},
+ 9687: {LaTeXMode.textmode: '\\ding{119}'},
+ 9688: {LaTeXMode.textmode: '\\Elzrvbull '},
+ 9703: {LaTeXMode.textmode: '\\Elzsqfl '},
+ 9704: {LaTeXMode.textmode: '\\Elzsqfr '},
+ 9706: {LaTeXMode.textmode: '\\Elzsqfse '},
+ 9711: {LaTeXMode.textmode: '\\bigcirc '},
+ 9733: {LaTeXMode.textmode: '\\ding{72}', LaTeXMode.mathmode: '\\bigstar'},
+ 9742: {LaTeXMode.textmode: '\\ding{37}'},
+ 9755: {LaTeXMode.textmode: '\\ding{42}'},
+ 9758: {LaTeXMode.textmode: '\\ding{43}'},
+ 9792: {LaTeXMode.textmode: '\\venus '},
+ 9794: {LaTeXMode.textmode: '\\male '},
+ 9799: {LaTeXMode.textmode: '\\pluto '},
+ 9800: {LaTeXMode.textmode: '\\aries '},
+ 9801: {LaTeXMode.textmode: '\\taurus '},
+ 9802: {LaTeXMode.textmode: '\\gemini '},
+ 9803: {LaTeXMode.textmode: '\\cancer '},
+ 9804: {LaTeXMode.textmode: '\\leo '},
+ 9805: {LaTeXMode.textmode: '\\virgo '},
+ 9806: {LaTeXMode.textmode: '\\libra '},
+ 9807: {LaTeXMode.textmode: '\\scorpio '},
+ 9808: {LaTeXMode.textmode: '\\sagittarius '},
+ 9809: {LaTeXMode.textmode: '\\capricornus '},
+ 9810: {LaTeXMode.textmode: '\\aquarius '},
+ 9811: {LaTeXMode.textmode: '\\pisces '},
+ 9824: {LaTeXMode.textmode: '\\ding{171}', LaTeXMode.mathmode: '\\spadesuit'},
+ 9826: {LaTeXMode.textmode: '\\diamond ', LaTeXMode.mathmode: '\\diamondsuit'},
+ 9827: {LaTeXMode.textmode: '\\ding{168}', LaTeXMode.mathmode: '\\clubsuit'},
+ 9829: {LaTeXMode.textmode: '\\ding{170}'},
+ 9830: {LaTeXMode.textmode: '\\ding{169}'},
+ 9833: {LaTeXMode.textmode: '\\quarternote '},
+ 9834: {LaTeXMode.textmode: '\\eighthnote '},
+ 9837: {LaTeXMode.textmode: '\\flat ', LaTeXMode.mathmode: '\\flat'},
+ 9838: {LaTeXMode.textmode: '\\natural ', LaTeXMode.mathmode: '\\natural'},
+ 9839: {LaTeXMode.textmode: '\\sharp ', LaTeXMode.mathmode: '\\sharp'},
+ 9985: {LaTeXMode.textmode: '\\ding{33}'},
+ 9986: {LaTeXMode.textmode: '\\ding{34}'},
+ 9987: {LaTeXMode.textmode: '\\ding{35}'},
+ 9988: {LaTeXMode.textmode: '\\ding{36}'},
+ 9990: {LaTeXMode.textmode: '\\ding{38}'},
+ 9991: {LaTeXMode.textmode: '\\ding{39}'},
+ 9992: {LaTeXMode.textmode: '\\ding{40}'},
+ 9993: {LaTeXMode.textmode: '\\ding{41}'},
+ 9996: {LaTeXMode.textmode: '\\ding{44}'},
+ 9997: {LaTeXMode.textmode: '\\ding{45}'},
+ 9998: {LaTeXMode.textmode: '\\ding{46}'},
+ 9999: {LaTeXMode.textmode: '\\ding{47}'},
+ 10000: {LaTeXMode.textmode: '\\ding{48}'},
+ 10001: {LaTeXMode.textmode: '\\ding{49}'},
+ 10002: {LaTeXMode.textmode: '\\ding{50}'},
+ 10003: {LaTeXMode.textmode: '\\ding{51}', LaTeXMode.mathmode: '\\checkmark'},
+ 10004: {LaTeXMode.textmode: '\\ding{52}'},
+ 10005: {LaTeXMode.textmode: '\\ding{53}'},
+ 10006: {LaTeXMode.textmode: '\\ding{54}'},
+ 10007: {LaTeXMode.textmode: '\\ding{55}'},
+ 10008: {LaTeXMode.textmode: '\\ding{56}'},
+ 10009: {LaTeXMode.textmode: '\\ding{57}'},
+ 10010: {LaTeXMode.textmode: '\\ding{58}'},
+ 10011: {LaTeXMode.textmode: '\\ding{59}'},
+ 10012: {LaTeXMode.textmode: '\\ding{60}'},
+ 10013: {LaTeXMode.textmode: '\\ding{61}'},
+ 10014: {LaTeXMode.textmode: '\\ding{62}'},
+ 10015: {LaTeXMode.textmode: '\\ding{63}'},
+ 10016: {LaTeXMode.textmode: '\\ding{64}', LaTeXMode.mathmode: '\\maltese'},
+ 10017: {LaTeXMode.textmode: '\\ding{65}'},
+ 10018: {LaTeXMode.textmode: '\\ding{66}'},
+ 10019: {LaTeXMode.textmode: '\\ding{67}'},
+ 10020: {LaTeXMode.textmode: '\\ding{68}'},
+ 10021: {LaTeXMode.textmode: '\\ding{69}'},
+ 10022: {LaTeXMode.textmode: '\\ding{70}'},
+ 10023: {LaTeXMode.textmode: '\\ding{71}'},
+ 10025: {LaTeXMode.textmode: '\\ding{73}'},
+ 10026: {LaTeXMode.textmode: '\\ding{74}'},
+ 10027: {LaTeXMode.textmode: '\\ding{75}'},
+ 10028: {LaTeXMode.textmode: '\\ding{76}'},
+ 10029: {LaTeXMode.textmode: '\\ding{77}'},
+ 10030: {LaTeXMode.textmode: '\\ding{78}'},
+ 10031: {LaTeXMode.textmode: '\\ding{79}'},
+ 10032: {LaTeXMode.textmode: '\\ding{80}'},
+ 10033: {LaTeXMode.textmode: '\\ding{81}'},
+ 10034: {LaTeXMode.textmode: '\\ding{82}'},
+ 10035: {LaTeXMode.textmode: '\\ding{83}'},
+ 10036: {LaTeXMode.textmode: '\\ding{84}'},
+ 10037: {LaTeXMode.textmode: '\\ding{85}'},
+ 10038: {LaTeXMode.textmode: '\\ding{86}'},
+ 10039: {LaTeXMode.textmode: '\\ding{87}'},
+ 10040: {LaTeXMode.textmode: '\\ding{88}'},
+ 10041: {LaTeXMode.textmode: '\\ding{89}'},
+ 10042: {LaTeXMode.textmode: '\\ding{90}'},
+ 10043: {LaTeXMode.textmode: '\\ding{91}'},
+ 10044: {LaTeXMode.textmode: '\\ding{92}'},
+ 10045: {LaTeXMode.textmode: '\\ding{93}'},
+ 10046: {LaTeXMode.textmode: '\\ding{94}'},
+ 10047: {LaTeXMode.textmode: '\\ding{95}'},
+ 10048: {LaTeXMode.textmode: '\\ding{96}'},
+ 10049: {LaTeXMode.textmode: '\\ding{97}'},
+ 10050: {LaTeXMode.textmode: '\\ding{98}'},
+ 10051: {LaTeXMode.textmode: '\\ding{99}'},
+ 10052: {LaTeXMode.textmode: '\\ding{100}'},
+ 10053: {LaTeXMode.textmode: '\\ding{101}'},
+ 10054: {LaTeXMode.textmode: '\\ding{102}'},
+ 10055: {LaTeXMode.textmode: '\\ding{103}'},
+ 10056: {LaTeXMode.textmode: '\\ding{104}'},
+ 10057: {LaTeXMode.textmode: '\\ding{105}'},
+ 10058: {LaTeXMode.textmode: '\\ding{106}'},
+ 10059: {LaTeXMode.textmode: '\\ding{107}'},
+ 10061: {LaTeXMode.textmode: '\\ding{109}'},
+ 10063: {LaTeXMode.textmode: '\\ding{111}'},
+ 10064: {LaTeXMode.textmode: '\\ding{112}'},
+ 10065: {LaTeXMode.textmode: '\\ding{113}'},
+ 10066: {LaTeXMode.textmode: '\\ding{114}'},
+ 10070: {LaTeXMode.textmode: '\\ding{118}'},
+ 10072: {LaTeXMode.textmode: '\\ding{120}'},
+ 10073: {LaTeXMode.textmode: '\\ding{121}'},
+ 10074: {LaTeXMode.textmode: '\\ding{122}'},
+ 10075: {LaTeXMode.textmode: '\\ding{123}'},
+ 10076: {LaTeXMode.textmode: '\\ding{124}'},
+ 10077: {LaTeXMode.textmode: '\\ding{125}'},
+ 10078: {LaTeXMode.textmode: '\\ding{126}'},
+ 10081: {LaTeXMode.textmode: '\\ding{161}'},
+ 10082: {LaTeXMode.textmode: '\\ding{162}'},
+ 10083: {LaTeXMode.textmode: '\\ding{163}'},
+ 10084: {LaTeXMode.textmode: '\\ding{164}'},
+ 10085: {LaTeXMode.textmode: '\\ding{165}'},
+ 10086: {LaTeXMode.textmode: '\\ding{166}'},
+ 10087: {LaTeXMode.textmode: '\\ding{167}'},
+ 10102: {LaTeXMode.textmode: '\\ding{182}'},
+ 10103: {LaTeXMode.textmode: '\\ding{183}'},
+ 10104: {LaTeXMode.textmode: '\\ding{184}'},
+ 10105: {LaTeXMode.textmode: '\\ding{185}'},
+ 10106: {LaTeXMode.textmode: '\\ding{186}'},
+ 10107: {LaTeXMode.textmode: '\\ding{187}'},
+ 10108: {LaTeXMode.textmode: '\\ding{188}'},
+ 10109: {LaTeXMode.textmode: '\\ding{189}'},
+ 10110: {LaTeXMode.textmode: '\\ding{190}'},
+ 10111: {LaTeXMode.textmode: '\\ding{191}'},
+ 10112: {LaTeXMode.textmode: '\\ding{192}'},
+ 10113: {LaTeXMode.textmode: '\\ding{193}'},
+ 10114: {LaTeXMode.textmode: '\\ding{194}'},
+ 10115: {LaTeXMode.textmode: '\\ding{195}'},
+ 10116: {LaTeXMode.textmode: '\\ding{196}'},
+ 10117: {LaTeXMode.textmode: '\\ding{197}'},
+ 10118: {LaTeXMode.textmode: '\\ding{198}'},
+ 10119: {LaTeXMode.textmode: '\\ding{199}'},
+ 10120: {LaTeXMode.textmode: '\\ding{200}'},
+ 10121: {LaTeXMode.textmode: '\\ding{201}'},
+ 10122: {LaTeXMode.textmode: '\\ding{202}'},
+ 10123: {LaTeXMode.textmode: '\\ding{203}'},
+ 10124: {LaTeXMode.textmode: '\\ding{204}'},
+ 10125: {LaTeXMode.textmode: '\\ding{205}'},
+ 10126: {LaTeXMode.textmode: '\\ding{206}'},
+ 10127: {LaTeXMode.textmode: '\\ding{207}'},
+ 10128: {LaTeXMode.textmode: '\\ding{208}'},
+ 10129: {LaTeXMode.textmode: '\\ding{209}'},
+ 10130: {LaTeXMode.textmode: '\\ding{210}'},
+ 10131: {LaTeXMode.textmode: '\\ding{211}'},
+ 10132: {LaTeXMode.textmode: '\\ding{212}'},
+ 10136: {LaTeXMode.textmode: '\\ding{216}'},
+ 10137: {LaTeXMode.textmode: '\\ding{217}'},
+ 10138: {LaTeXMode.textmode: '\\ding{218}'},
+ 10139: {LaTeXMode.textmode: '\\ding{219}'},
+ 10140: {LaTeXMode.textmode: '\\ding{220}'},
+ 10141: {LaTeXMode.textmode: '\\ding{221}'},
+ 10142: {LaTeXMode.textmode: '\\ding{222}'},
+ 10143: {LaTeXMode.textmode: '\\ding{223}'},
+ 10144: {LaTeXMode.textmode: '\\ding{224}'},
+ 10145: {LaTeXMode.textmode: '\\ding{225}'},
+ 10146: {LaTeXMode.textmode: '\\ding{226}'},
+ 10147: {LaTeXMode.textmode: '\\ding{227}'},
+ 10148: {LaTeXMode.textmode: '\\ding{228}'},
+ 10149: {LaTeXMode.textmode: '\\ding{229}'},
+ 10150: {LaTeXMode.textmode: '\\ding{230}'},
+ 10151: {LaTeXMode.textmode: '\\ding{231}'},
+ 10152: {LaTeXMode.textmode: '\\ding{232}'},
+ 10153: {LaTeXMode.textmode: '\\ding{233}'},
+ 10154: {LaTeXMode.textmode: '\\ding{234}'},
+ 10155: {LaTeXMode.textmode: '\\ding{235}'},
+ 10156: {LaTeXMode.textmode: '\\ding{236}'},
+ 10157: {LaTeXMode.textmode: '\\ding{237}'},
+ 10158: {LaTeXMode.textmode: '\\ding{238}'},
+ 10159: {LaTeXMode.textmode: '\\ding{239}'},
+ 10161: {LaTeXMode.textmode: '\\ding{241}'},
+ 10162: {LaTeXMode.textmode: '\\ding{242}'},
+ 10163: {LaTeXMode.textmode: '\\ding{243}'},
+ 10164: {LaTeXMode.textmode: '\\ding{244}'},
+ 10165: {LaTeXMode.textmode: '\\ding{245}'},
+ 10166: {LaTeXMode.textmode: '\\ding{246}'},
+ 10167: {LaTeXMode.textmode: '\\ding{247}'},
+ 10168: {LaTeXMode.textmode: '\\ding{248}'},
+ 10169: {LaTeXMode.textmode: '\\ding{249}'},
+ 10170: {LaTeXMode.textmode: '\\ding{250}'},
+ 10171: {LaTeXMode.textmode: '\\ding{251}'},
+ 10172: {LaTeXMode.textmode: '\\ding{252}'},
+ 10173: {LaTeXMode.textmode: '\\ding{253}'},
+ 10174: {LaTeXMode.textmode: '\\ding{254}'},
+ 10229: {LaTeXMode.textmode: '\\longleftarrow ', LaTeXMode.mathmode: '\\longleftarrow'},
+ 10230: {LaTeXMode.textmode: '\\longrightarrow ', LaTeXMode.mathmode: '\\longrightarrow'},
+ 10231: {LaTeXMode.textmode: '\\longleftrightarrow ', LaTeXMode.mathmode: '\\longleftrightarrow'},
+ 10232: {LaTeXMode.textmode: '\\Longleftarrow ', LaTeXMode.mathmode: '\\Longleftarrow'},
+ 10233: {LaTeXMode.textmode: '\\Longrightarrow ', LaTeXMode.mathmode: '\\Longrightarrow'},
+ 10234: {LaTeXMode.textmode: '\\Longleftrightarrow ', LaTeXMode.mathmode: '\\Longleftrightarrow'},
+ 10236: {LaTeXMode.textmode: '\\longmapsto ', LaTeXMode.mathmode: '\\longmapsto'},
+ 10239: {LaTeXMode.textmode: '\\sim\\joinrel\\leadsto'},
+ 10501: {LaTeXMode.textmode: '\\ElsevierGlyph{E212}'},
+ 10502: {LaTeXMode.mathmode: '\\Mapsfrom'},
+ 10503: {LaTeXMode.mathmode: '\\Mapsto'},
+ 10506: {LaTeXMode.mathmode: '\\Uuparrow'},
+ 10507: {LaTeXMode.mathmode: '\\Ddownarrow'},
+ 10511: {LaTeXMode.mathmode: '\\dbkarow'},
+ 10512: {LaTeXMode.mathmode: '\\drbkarrow'},
+ 10514: {LaTeXMode.textmode: '\\UpArrowBar '},
+ 10515: {LaTeXMode.textmode: '\\DownArrowBar '},
+ 10518: {LaTeXMode.mathmode: '\\twoheadrightarrowtail'},
+ 10531: {LaTeXMode.textmode: '\\ElsevierGlyph{E20C}'},
+ 10532: {LaTeXMode.textmode: '\\ElsevierGlyph{E20D}'},
+ 10533: {LaTeXMode.textmode: '\\ElsevierGlyph{E20B}', LaTeXMode.mathmode: '\\hksearow'},
+ 10534: {LaTeXMode.textmode: '\\ElsevierGlyph{E20A}', LaTeXMode.mathmode: '\\hkswarow'},
+ 10535: {LaTeXMode.textmode: '\\ElsevierGlyph{E211}', LaTeXMode.mathmode: '\\tona'},
+ 10536: {LaTeXMode.textmode: '\\ElsevierGlyph{E20E}', LaTeXMode.mathmode: '\\toea'},
+ 10537: {LaTeXMode.textmode: '\\ElsevierGlyph{E20F}', LaTeXMode.mathmode: '\\tosa'},
+ 10538: {LaTeXMode.textmode: '\\ElsevierGlyph{E210}', LaTeXMode.mathmode: '\\towa'},
+ 10539: {LaTeXMode.mathmode: '\\rdiagovfdiag'},
+ 10540: {LaTeXMode.mathmode: '\\fdiagovrdiag'},
+ 10541: {LaTeXMode.mathmode: '\\seovnearrow'},
+ 10542: {LaTeXMode.mathmode: '\\neovsearrow'},
+ 10543: {LaTeXMode.mathmode: '\\fdiagovnearrow'},
+ 10544: {LaTeXMode.mathmode: '\\rdiagovsearrow'},
+ 10545: {LaTeXMode.mathmode: '\\neovnwarrow'},
+ 10546: {LaTeXMode.mathmode: '\\nwovnearrow'},
+ 10547: {LaTeXMode.textmode: '\\ElsevierGlyph{E21D}'},
+ 10550: {LaTeXMode.textmode: '\\ElsevierGlyph{E21A}'},
+ 10551: {LaTeXMode.textmode: '\\ElsevierGlyph{E219}'},
+ 10560: {LaTeXMode.textmode: '\\Elolarr ', LaTeXMode.mathmode: '\\circlearrowleft'},
+ 10561: {LaTeXMode.textmode: '\\Elorarr ', LaTeXMode.mathmode: '\\circlearrowright'},
+ 10562: {LaTeXMode.textmode: '\\ElzRlarr '},
+ 10564: {LaTeXMode.textmode: '\\ElzrLarr '},
+ 10567: {LaTeXMode.textmode: '\\Elzrarrx '},
+ 10574: {LaTeXMode.textmode: '\\LeftRightVector '},
+ 10575: {LaTeXMode.textmode: '\\RightUpDownVector '},
+ 10576: {LaTeXMode.textmode: '\\DownLeftRightVector '},
+ 10577: {LaTeXMode.textmode: '\\LeftUpDownVector '},
+ 10578: {LaTeXMode.textmode: '\\LeftVectorBar '},
+ 10579: {LaTeXMode.textmode: '\\RightVectorBar '},
+ 10580: {LaTeXMode.textmode: '\\RightUpVectorBar '},
+ 10581: {LaTeXMode.textmode: '\\RightDownVectorBar '},
+ 10582: {LaTeXMode.textmode: '\\DownLeftVectorBar '},
+ 10583: {LaTeXMode.textmode: '\\DownRightVectorBar '},
+ 10584: {LaTeXMode.textmode: '\\LeftUpVectorBar '},
+ 10585: {LaTeXMode.textmode: '\\LeftDownVectorBar '},
+ 10586: {LaTeXMode.textmode: '\\LeftTeeVector '},
+ 10587: {LaTeXMode.textmode: '\\RightTeeVector '},
+ 10588: {LaTeXMode.textmode: '\\RightUpTeeVector '},
+ 10589: {LaTeXMode.textmode: '\\RightDownTeeVector '},
+ 10590: {LaTeXMode.textmode: '\\DownLeftTeeVector '},
+ 10591: {LaTeXMode.textmode: '\\DownRightTeeVector '},
+ 10592: {LaTeXMode.textmode: '\\LeftUpTeeVector '},
+ 10593: {LaTeXMode.textmode: '\\LeftDownTeeVector '},
+ 10606: {LaTeXMode.textmode: '\\UpEquilibrium '},
+ 10607: {LaTeXMode.textmode: '\\ReverseUpEquilibrium '},
+ 10608: {LaTeXMode.textmode: '\\RoundImplies '},
+ 10620: {LaTeXMode.textmode: '\\ElsevierGlyph{E214}'},
+ 10621: {LaTeXMode.textmode: '\\ElsevierGlyph{E215}'},
+ 10624: {LaTeXMode.textmode: '\\Elztfnc ', LaTeXMode.mathmode: '\\Vvert'},
+ 10629: {LaTeXMode.textmode: '\\ElsevierGlyph{3018}'},
+ 10630: {LaTeXMode.textmode: '\\Elroang '},
+ 10643: {LaTeXMode.textmode: '<\\kern-0.58em('},
+ 10644: {LaTeXMode.textmode: '\\ElsevierGlyph{E291}'},
+ 10649: {LaTeXMode.textmode: '\\Elzddfnc '},
+ 10652: {LaTeXMode.textmode: '\\Angle '},
+ 10656: {LaTeXMode.textmode: '\\Elzlpargt '},
+ 10677: {LaTeXMode.textmode: '\\ElsevierGlyph{E260}'},
+ 10678: {LaTeXMode.textmode: '\\ElsevierGlyph{E61B}'},
+ 10680: {LaTeXMode.mathmode: '\\obslash'},
+ 10692: {LaTeXMode.mathmode: '\\boxdiag'},
+ 10693: {LaTeXMode.mathmode: '\\boxbslash'},
+ 10694: {LaTeXMode.mathmode: '\\boxast'},
+ 10695: {LaTeXMode.mathmode: '\\boxcircle'},
+ 10698: {LaTeXMode.textmode: '\\ElzLap '},
+ 10699: {LaTeXMode.textmode: '\\Elzdefas '},
+ 10703: {LaTeXMode.textmode: '\\NotLeftTriangleBar '},
+ 10704: {LaTeXMode.textmode: '\\NotRightTriangleBar '},
+ 10716: {LaTeXMode.textmode: '\\ElsevierGlyph{E372}'},
+ 10719: {LaTeXMode.mathmode: '\\dualmap'},
+ 10722: {LaTeXMode.mathmode: '\\shuffle'},
+ 10731: {LaTeXMode.textmode: '\\blacklozenge ', LaTeXMode.mathmode: '\\blacklozenge'},
+ 10740: {LaTeXMode.textmode: '\\RuleDelayed '},
+ 10752: {LaTeXMode.mathmode: '\\bigodot'},
+ 10753: {LaTeXMode.mathmode: '\\bigoplus'},
+ 10754: {LaTeXMode.mathmode: '\\bigotimes'},
+ 10755: {LaTeXMode.mathmode: '\\bigcupdot'},
+ 10756: {LaTeXMode.textmode: '\\Elxuplus ', LaTeXMode.mathmode: '\\biguplus'},
+ 10757: {LaTeXMode.textmode: '\\ElzThr ', LaTeXMode.mathmode: '\\bigsqcap'},
+ 10758: {LaTeXMode.textmode: '\\Elxsqcup ', LaTeXMode.mathmode: '\\bigsqcup'},
+ 10759: {LaTeXMode.textmode: '\\ElzInf ', LaTeXMode.mathmode: '\\conjquant'},
+ 10760: {LaTeXMode.textmode: '\\ElzSup ', LaTeXMode.mathmode: '\\disjquant'},
+ 10761: {LaTeXMode.mathmode: '\\bigtimes'},
+ 10765: {LaTeXMode.textmode: '\\ElzCint ', LaTeXMode.mathmode: '\\intbar'},
+ 10766: {LaTeXMode.mathmode: '\\intBar'},
+ 10767: {LaTeXMode.textmode: '\\clockoint '},
+ 10768: {LaTeXMode.textmode: '\\ElsevierGlyph{E395}'},
+ 10774: {LaTeXMode.textmode: '\\sqrint '},
+ 10776: {LaTeXMode.mathmode: '\\intx'},
+ 10777: {LaTeXMode.mathmode: '\\intcap'},
+ 10778: {LaTeXMode.mathmode: '\\intcup'},
+ 10779: {LaTeXMode.mathmode: '\\upint'},
+ 10780: {LaTeXMode.mathmode: '\\lowint'},
+ 10789: {LaTeXMode.textmode: '\\ElsevierGlyph{E25A}', LaTeXMode.mathmode: '\\plusdot'},
+ 10794: {LaTeXMode.textmode: '\\ElsevierGlyph{E25B}', LaTeXMode.mathmode: '\\minusdot'},
+ 10797: {LaTeXMode.textmode: '\\ElsevierGlyph{E25C}'},
+ 10798: {LaTeXMode.textmode: '\\ElsevierGlyph{E25D}'},
+ 10799: {LaTeXMode.textmode: '\\ElzTimes '},
+ 10802: {LaTeXMode.mathmode: '\\btimes'},
+ 10804: {LaTeXMode.textmode: '\\ElsevierGlyph{E25E}'},
+ 10805: {LaTeXMode.textmode: '\\ElsevierGlyph{E25E}'},
+ 10812: {LaTeXMode.textmode: '\\ElsevierGlyph{E259}', LaTeXMode.mathmode: '\\intprod'},
+ 10813: {LaTeXMode.mathmode: '\\intprodr'},
+ 10815: {LaTeXMode.textmode: '\\amalg ', LaTeXMode.mathmode: '\\amalg'},
+ 10835: {LaTeXMode.textmode: '\\ElzAnd '},
+ 10836: {LaTeXMode.textmode: '\\ElzOr '},
+ 10837: {LaTeXMode.textmode: '\\ElsevierGlyph{E36E}'},
+ 10838: {LaTeXMode.textmode: '\\ElOr '},
+ 10846: {LaTeXMode.textmode: '\\perspcorrespond ', LaTeXMode.mathmode: '\\doublebarwedge'},
+ 10847: {LaTeXMode.textmode: '\\Elzminhat '},
+ 10851: {LaTeXMode.textmode: '\\ElsevierGlyph{225A}'},
+ 10862: {LaTeXMode.textmode: '\\stackrel{*}{=}'},
+ 10869: {LaTeXMode.textmode: '\\Equal '},
+ 10871: {LaTeXMode.mathmode: '\\ddotseq'},
+ 10877: {LaTeXMode.textmode: '\\nleqslant ', LaTeXMode.mathmode: '\\nleqslant'},
+ 10878: {LaTeXMode.textmode: '\\ngeqslant ', LaTeXMode.mathmode: '\\ngeqslant'},
+ 10885: {LaTeXMode.textmode: '\\lessapprox ', LaTeXMode.mathmode: '\\lessapprox'},
+ 10886: {LaTeXMode.textmode: '\\gtrapprox ', LaTeXMode.mathmode: '\\gtrapprox'},
+ 10887: {LaTeXMode.textmode: '\\lneq ', LaTeXMode.mathmode: '\\lneq'},
+ 10888: {LaTeXMode.textmode: '\\gneq ', LaTeXMode.mathmode: '\\gneq'},
+ 10889: {LaTeXMode.textmode: '\\lnapprox ', LaTeXMode.mathmode: '\\lnapprox'},
+ 10890: {LaTeXMode.textmode: '\\gnapprox ', LaTeXMode.mathmode: '\\gnapprox'},
+ 10891: {LaTeXMode.textmode: '\\lesseqqgtr ', LaTeXMode.mathmode: '\\lesseqqgtr'},
+ 10892: {LaTeXMode.textmode: '\\gtreqqless ', LaTeXMode.mathmode: '\\gtreqqless'},
+ 10901: {LaTeXMode.textmode: '\\eqslantless ', LaTeXMode.mathmode: '\\eqslantless'},
+ 10902: {LaTeXMode.textmode: '\\eqslantgtr ', LaTeXMode.mathmode: '\\eqslantgtr'},
+ 10909: {LaTeXMode.textmode: '\\Pisymbol{ppi020}{117}'},
+ 10910: {LaTeXMode.textmode: '\\Pisymbol{ppi020}{105}'},
+ 10913: {LaTeXMode.textmode: '\\NotNestedLessLess '},
+ 10914: {LaTeXMode.textmode: '\\NotNestedGreaterGreater '},
+ 10915: {LaTeXMode.mathmode: '\\partialmeetcontraction'},
+ 10926: {LaTeXMode.mathmode: '\\bumpeqq'},
+ 10927: {LaTeXMode.textmode: '\\not\\preceq ', LaTeXMode.mathmode: '\\npreceq'},
+ 10928: {LaTeXMode.textmode: '\\not\\succeq ', LaTeXMode.mathmode: '\\nsucceq'},
+ 10933: {LaTeXMode.textmode: '\\precneqq ', LaTeXMode.mathmode: '\\precneqq'},
+ 10934: {LaTeXMode.textmode: '\\succneqq ', LaTeXMode.mathmode: '\\succneqq'},
+ 10935: {LaTeXMode.textmode: '\\precapprox ', LaTeXMode.mathmode: '\\precapprox'},
+ 10936: {LaTeXMode.textmode: '\\succapprox ', LaTeXMode.mathmode: '\\succapprox'},
+ 10937: {LaTeXMode.textmode: '\\precnapprox ', LaTeXMode.mathmode: '\\precnapprox'},
+ 10938: {LaTeXMode.textmode: '\\succnapprox ', LaTeXMode.mathmode: '\\succnapprox'},
+ 10949: {LaTeXMode.textmode: '\\nsubseteqq ', LaTeXMode.mathmode: '\\nsubseteqq'},
+ 10950: {LaTeXMode.textmode: '\\nsupseteqq'},
+ 10955: {LaTeXMode.textmode: '\\subsetneqq ', LaTeXMode.mathmode: '\\subsetneqq'},
+ 10956: {LaTeXMode.textmode: '\\supsetneqq ', LaTeXMode.mathmode: '\\supsetneqq'},
+ 10971: {LaTeXMode.mathmode: '\\mlcp'},
+ 10973: {LaTeXMode.mathmode: '\\forks'},
+ 10979: {LaTeXMode.mathmode: '\\dashV'},
+ 10980: {LaTeXMode.mathmode: '\\Dashv'},
+ 10987: {LaTeXMode.textmode: '\\ElsevierGlyph{E30D}'},
+ 10996: {LaTeXMode.mathmode: '\\interleave'},
+ 10998: {LaTeXMode.textmode: '\\Elztdcol '},
+ 11005: {LaTeXMode.textmode: '{\\rlap{\\textbackslash}{{/}\\!\\!{/}}}'},
+ 12298: {LaTeXMode.textmode: '\\ElsevierGlyph{300A}'},
+ 12299: {LaTeXMode.textmode: '\\ElsevierGlyph{300B}'},
+ 12312: {LaTeXMode.textmode: '\\ElsevierGlyph{3018}'},
+ 12313: {LaTeXMode.textmode: '\\ElsevierGlyph{3019}'},
+ 12314: {LaTeXMode.textmode: '\\openbracketleft '},
+ 12315: {LaTeXMode.textmode: '\\openbracketright '},
+ 64256: {LaTeXMode.textmode: 'ff'},
+ 64257: {LaTeXMode.textmode: 'fi'},
+ 64258: {LaTeXMode.textmode: 'fl'},
+ 64259: {LaTeXMode.textmode: 'ffi'},
+ 64260: {LaTeXMode.textmode: 'ffl'},
+ 65024: {LaTeXMode.textmode: '\\varsupsetneq ', LaTeXMode.mathmode: '\\varsupsetneq'},
+ 119808: {LaTeXMode.textmode: '\\mathbf{A}'},
+ 119809: {LaTeXMode.textmode: '\\mathbf{B}'},
+ 119810: {LaTeXMode.textmode: '\\mathbf{C}'},
+ 119811: {LaTeXMode.textmode: '\\mathbf{D}'},
+ 119812: {LaTeXMode.textmode: '\\mathbf{E}'},
+ 119813: {LaTeXMode.textmode: '\\mathbf{F}'},
+ 119814: {LaTeXMode.textmode: '\\mathbf{G}'},
+ 119815: {LaTeXMode.textmode: '\\mathbf{H}'},
+ 119816: {LaTeXMode.textmode: '\\mathbf{I}'},
+ 119817: {LaTeXMode.textmode: '\\mathbf{J}'},
+ 119818: {LaTeXMode.textmode: '\\mathbf{K}'},
+ 119819: {LaTeXMode.textmode: '\\mathbf{L}'},
+ 119820: {LaTeXMode.textmode: '\\mathbf{M}'},
+ 119821: {LaTeXMode.textmode: '\\mathbf{N}'},
+ 119822: {LaTeXMode.textmode: '\\mathbf{O}'},
+ 119823: {LaTeXMode.textmode: '\\mathbf{P}'},
+ 119824: {LaTeXMode.textmode: '\\mathbf{Q}'},
+ 119825: {LaTeXMode.textmode: '\\mathbf{R}'},
+ 119826: {LaTeXMode.textmode: '\\mathbf{S}'},
+ 119827: {LaTeXMode.textmode: '\\mathbf{T}'},
+ 119828: {LaTeXMode.textmode: '\\mathbf{U}'},
+ 119829: {LaTeXMode.textmode: '\\mathbf{V}'},
+ 119830: {LaTeXMode.textmode: '\\mathbf{W}'},
+ 119831: {LaTeXMode.textmode: '\\mathbf{X}'},
+ 119832: {LaTeXMode.textmode: '\\mathbf{Y}'},
+ 119833: {LaTeXMode.textmode: '\\mathbf{Z}'},
+ 119834: {LaTeXMode.textmode: '\\mathbf{a}'},
+ 119835: {LaTeXMode.textmode: '\\mathbf{b}'},
+ 119836: {LaTeXMode.textmode: '\\mathbf{c}'},
+ 119837: {LaTeXMode.textmode: '\\mathbf{d}'},
+ 119838: {LaTeXMode.textmode: '\\mathbf{e}'},
+ 119839: {LaTeXMode.textmode: '\\mathbf{f}'},
+ 119840: {LaTeXMode.textmode: '\\mathbf{g}'},
+ 119841: {LaTeXMode.textmode: '\\mathbf{h}'},
+ 119842: {LaTeXMode.textmode: '\\mathbf{i}'},
+ 119843: {LaTeXMode.textmode: '\\mathbf{j}'},
+ 119844: {LaTeXMode.textmode: '\\mathbf{k}'},
+ 119845: {LaTeXMode.textmode: '\\mathbf{l}'},
+ 119846: {LaTeXMode.textmode: '\\mathbf{m}'},
+ 119847: {LaTeXMode.textmode: '\\mathbf{n}'},
+ 119848: {LaTeXMode.textmode: '\\mathbf{o}'},
+ 119849: {LaTeXMode.textmode: '\\mathbf{p}'},
+ 119850: {LaTeXMode.textmode: '\\mathbf{q}'},
+ 119851: {LaTeXMode.textmode: '\\mathbf{r}'},
+ 119852: {LaTeXMode.textmode: '\\mathbf{s}'},
+ 119853: {LaTeXMode.textmode: '\\mathbf{t}'},
+ 119854: {LaTeXMode.textmode: '\\mathbf{u}'},
+ 119855: {LaTeXMode.textmode: '\\mathbf{v}'},
+ 119856: {LaTeXMode.textmode: '\\mathbf{w}'},
+ 119857: {LaTeXMode.textmode: '\\mathbf{x}'},
+ 119858: {LaTeXMode.textmode: '\\mathbf{y}'},
+ 119859: {LaTeXMode.textmode: '\\mathbf{z}'},
+ 119860: {LaTeXMode.textmode: '\\mathmit{A}'},
+ 119861: {LaTeXMode.textmode: '\\mathmit{B}'},
+ 119862: {LaTeXMode.textmode: '\\mathmit{C}'},
+ 119863: {LaTeXMode.textmode: '\\mathmit{D}'},
+ 119864: {LaTeXMode.textmode: '\\mathmit{E}'},
+ 119865: {LaTeXMode.textmode: '\\mathmit{F}'},
+ 119866: {LaTeXMode.textmode: '\\mathmit{G}'},
+ 119867: {LaTeXMode.textmode: '\\mathmit{H}'},
+ 119868: {LaTeXMode.textmode: '\\mathmit{I}'},
+ 119869: {LaTeXMode.textmode: '\\mathmit{J}'},
+ 119870: {LaTeXMode.textmode: '\\mathmit{K}'},
+ 119871: {LaTeXMode.textmode: '\\mathmit{L}'},
+ 119872: {LaTeXMode.textmode: '\\mathmit{M}'},
+ 119873: {LaTeXMode.textmode: '\\mathmit{N}'},
+ 119874: {LaTeXMode.textmode: '\\mathmit{O}'},
+ 119875: {LaTeXMode.textmode: '\\mathmit{P}'},
+ 119876: {LaTeXMode.textmode: '\\mathmit{Q}'},
+ 119877: {LaTeXMode.textmode: '\\mathmit{R}'},
+ 119878: {LaTeXMode.textmode: '\\mathmit{S}'},
+ 119879: {LaTeXMode.textmode: '\\mathmit{T}'},
+ 119880: {LaTeXMode.textmode: '\\mathmit{U}'},
+ 119881: {LaTeXMode.textmode: '\\mathmit{V}'},
+ 119882: {LaTeXMode.textmode: '\\mathmit{W}'},
+ 119883: {LaTeXMode.textmode: '\\mathmit{X}'},
+ 119884: {LaTeXMode.textmode: '\\mathmit{Y}'},
+ 119885: {LaTeXMode.textmode: '\\mathmit{Z}'},
+ 119886: {LaTeXMode.textmode: '\\mathmit{a}'},
+ 119887: {LaTeXMode.textmode: '\\mathmit{b}'},
+ 119888: {LaTeXMode.textmode: '\\mathmit{c}'},
+ 119889: {LaTeXMode.textmode: '\\mathmit{d}'},
+ 119890: {LaTeXMode.textmode: '\\mathmit{e}'},
+ 119891: {LaTeXMode.textmode: '\\mathmit{f}'},
+ 119892: {LaTeXMode.textmode: '\\mathmit{g}'},
+ 119894: {LaTeXMode.textmode: '\\mathmit{i}'},
+ 119895: {LaTeXMode.textmode: '\\mathmit{j}'},
+ 119896: {LaTeXMode.textmode: '\\mathmit{k}'},
+ 119897: {LaTeXMode.textmode: '\\mathmit{l}'},
+ 119898: {LaTeXMode.textmode: '\\mathmit{m}'},
+ 119899: {LaTeXMode.textmode: '\\mathmit{n}'},
+ 119900: {LaTeXMode.textmode: '\\mathmit{o}'},
+ 119901: {LaTeXMode.textmode: '\\mathmit{p}'},
+ 119902: {LaTeXMode.textmode: '\\mathmit{q}'},
+ 119903: {LaTeXMode.textmode: '\\mathmit{r}'},
+ 119904: {LaTeXMode.textmode: '\\mathmit{s}'},
+ 119905: {LaTeXMode.textmode: '\\mathmit{t}'},
+ 119906: {LaTeXMode.textmode: '\\mathmit{u}'},
+ 119907: {LaTeXMode.textmode: '\\mathmit{v}'},
+ 119908: {LaTeXMode.textmode: '\\mathmit{w}'},
+ 119909: {LaTeXMode.textmode: '\\mathmit{x}'},
+ 119910: {LaTeXMode.textmode: '\\mathmit{y}'},
+ 119911: {LaTeXMode.textmode: '\\mathmit{z}'},
+ 119912: {LaTeXMode.textmode: '\\mathbit{A}'},
+ 119913: {LaTeXMode.textmode: '\\mathbit{B}'},
+ 119914: {LaTeXMode.textmode: '\\mathbit{C}'},
+ 119915: {LaTeXMode.textmode: '\\mathbit{D}'},
+ 119916: {LaTeXMode.textmode: '\\mathbit{E}'},
+ 119917: {LaTeXMode.textmode: '\\mathbit{F}'},
+ 119918: {LaTeXMode.textmode: '\\mathbit{G}'},
+ 119919: {LaTeXMode.textmode: '\\mathbit{H}'},
+ 119920: {LaTeXMode.textmode: '\\mathbit{I}'},
+ 119921: {LaTeXMode.textmode: '\\mathbit{J}'},
+ 119922: {LaTeXMode.textmode: '\\mathbit{K}'},
+ 119923: {LaTeXMode.textmode: '\\mathbit{L}'},
+ 119924: {LaTeXMode.textmode: '\\mathbit{M}'},
+ 119925: {LaTeXMode.textmode: '\\mathbit{N}'},
+ 119926: {LaTeXMode.textmode: '\\mathbit{O}'},
+ 119927: {LaTeXMode.textmode: '\\mathbit{P}'},
+ 119928: {LaTeXMode.textmode: '\\mathbit{Q}'},
+ 119929: {LaTeXMode.textmode: '\\mathbit{R}'},
+ 119930: {LaTeXMode.textmode: '\\mathbit{S}'},
+ 119931: {LaTeXMode.textmode: '\\mathbit{T}'},
+ 119932: {LaTeXMode.textmode: '\\mathbit{U}'},
+ 119933: {LaTeXMode.textmode: '\\mathbit{V}'},
+ 119934: {LaTeXMode.textmode: '\\mathbit{W}'},
+ 119935: {LaTeXMode.textmode: '\\mathbit{X}'},
+ 119936: {LaTeXMode.textmode: '\\mathbit{Y}'},
+ 119937: {LaTeXMode.textmode: '\\mathbit{Z}'},
+ 119938: {LaTeXMode.textmode: '\\mathbit{a}'},
+ 119939: {LaTeXMode.textmode: '\\mathbit{b}'},
+ 119940: {LaTeXMode.textmode: '\\mathbit{c}'},
+ 119941: {LaTeXMode.textmode: '\\mathbit{d}'},
+ 119942: {LaTeXMode.textmode: '\\mathbit{e}'},
+ 119943: {LaTeXMode.textmode: '\\mathbit{f}'},
+ 119944: {LaTeXMode.textmode: '\\mathbit{g}'},
+ 119945: {LaTeXMode.textmode: '\\mathbit{h}'},
+ 119946: {LaTeXMode.textmode: '\\mathbit{i}'},
+ 119947: {LaTeXMode.textmode: '\\mathbit{j}'},
+ 119948: {LaTeXMode.textmode: '\\mathbit{k}'},
+ 119949: {LaTeXMode.textmode: '\\mathbit{l}'},
+ 119950: {LaTeXMode.textmode: '\\mathbit{m}'},
+ 119951: {LaTeXMode.textmode: '\\mathbit{n}'},
+ 119952: {LaTeXMode.textmode: '\\mathbit{o}'},
+ 119953: {LaTeXMode.textmode: '\\mathbit{p}'},
+ 119954: {LaTeXMode.textmode: '\\mathbit{q}'},
+ 119955: {LaTeXMode.textmode: '\\mathbit{r}'},
+ 119956: {LaTeXMode.textmode: '\\mathbit{s}'},
+ 119957: {LaTeXMode.textmode: '\\mathbit{t}'},
+ 119958: {LaTeXMode.textmode: '\\mathbit{u}'},
+ 119959: {LaTeXMode.textmode: '\\mathbit{v}'},
+ 119960: {LaTeXMode.textmode: '\\mathbit{w}'},
+ 119961: {LaTeXMode.textmode: '\\mathbit{x}'},
+ 119962: {LaTeXMode.textmode: '\\mathbit{y}'},
+ 119963: {LaTeXMode.textmode: '\\mathbit{z}'},
+ 119964: {LaTeXMode.textmode: '\\mathscr{A}'},
+ 119966: {LaTeXMode.textmode: '\\mathscr{C}'},
+ 119967: {LaTeXMode.textmode: '\\mathscr{D}'},
+ 119970: {LaTeXMode.textmode: '\\mathscr{G}'},
+ 119973: {LaTeXMode.textmode: '\\mathscr{J}'},
+ 119974: {LaTeXMode.textmode: '\\mathscr{K}'},
+ 119977: {LaTeXMode.textmode: '\\mathscr{N}'},
+ 119978: {LaTeXMode.textmode: '\\mathscr{O}'},
+ 119979: {LaTeXMode.textmode: '\\mathscr{P}'},
+ 119980: {LaTeXMode.textmode: '\\mathscr{Q}'},
+ 119982: {LaTeXMode.textmode: '\\mathscr{S}'},
+ 119983: {LaTeXMode.textmode: '\\mathscr{T}'},
+ 119984: {LaTeXMode.textmode: '\\mathscr{U}'},
+ 119985: {LaTeXMode.textmode: '\\mathscr{V}'},
+ 119986: {LaTeXMode.textmode: '\\mathscr{W}'},
+ 119987: {LaTeXMode.textmode: '\\mathscr{X}'},
+ 119988: {LaTeXMode.textmode: '\\mathscr{Y}'},
+ 119989: {LaTeXMode.textmode: '\\mathscr{Z}'},
+ 119990: {LaTeXMode.textmode: '\\mathscr{a}'},
+ 119991: {LaTeXMode.textmode: '\\mathscr{b}'},
+ 119992: {LaTeXMode.textmode: '\\mathscr{c}'},
+ 119993: {LaTeXMode.textmode: '\\mathscr{d}'},
+ 119995: {LaTeXMode.textmode: '\\mathscr{f}'},
+ 119997: {LaTeXMode.textmode: '\\mathscr{h}'},
+ 119998: {LaTeXMode.textmode: '\\mathscr{i}'},
+ 119999: {LaTeXMode.textmode: '\\mathscr{j}'},
+ 120000: {LaTeXMode.textmode: '\\mathscr{k}'},
+ 120001: {LaTeXMode.textmode: '\\mathscr{l}'},
+ 120002: {LaTeXMode.textmode: '\\mathscr{m}'},
+ 120003: {LaTeXMode.textmode: '\\mathscr{n}'},
+ 120005: {LaTeXMode.textmode: '\\mathscr{p}'},
+ 120006: {LaTeXMode.textmode: '\\mathscr{q}'},
+ 120007: {LaTeXMode.textmode: '\\mathscr{r}'},
+ 120008: {LaTeXMode.textmode: '\\mathscr{s}'},
+ 120009: {LaTeXMode.textmode: '\\mathscr{t}'},
+ 120010: {LaTeXMode.textmode: '\\mathscr{u}'},
+ 120011: {LaTeXMode.textmode: '\\mathscr{v}'},
+ 120012: {LaTeXMode.textmode: '\\mathscr{w}'},
+ 120013: {LaTeXMode.textmode: '\\mathscr{x}'},
+ 120014: {LaTeXMode.textmode: '\\mathscr{y}'},
+ 120015: {LaTeXMode.textmode: '\\mathscr{z}'},
+ 120016: {LaTeXMode.textmode: '\\mathbcal{A}'},
+ 120017: {LaTeXMode.textmode: '\\mathbcal{B}'},
+ 120018: {LaTeXMode.textmode: '\\mathbcal{C}'},
+ 120019: {LaTeXMode.textmode: '\\mathbcal{D}'},
+ 120020: {LaTeXMode.textmode: '\\mathbcal{E}'},
+ 120021: {LaTeXMode.textmode: '\\mathbcal{F}'},
+ 120022: {LaTeXMode.textmode: '\\mathbcal{G}'},
+ 120023: {LaTeXMode.textmode: '\\mathbcal{H}'},
+ 120024: {LaTeXMode.textmode: '\\mathbcal{I}'},
+ 120025: {LaTeXMode.textmode: '\\mathbcal{J}'},
+ 120026: {LaTeXMode.textmode: '\\mathbcal{K}'},
+ 120027: {LaTeXMode.textmode: '\\mathbcal{L}'},
+ 120028: {LaTeXMode.textmode: '\\mathbcal{M}'},
+ 120029: {LaTeXMode.textmode: '\\mathbcal{N}'},
+ 120030: {LaTeXMode.textmode: '\\mathbcal{O}'},
+ 120031: {LaTeXMode.textmode: '\\mathbcal{P}'},
+ 120032: {LaTeXMode.textmode: '\\mathbcal{Q}'},
+ 120033: {LaTeXMode.textmode: '\\mathbcal{R}'},
+ 120034: {LaTeXMode.textmode: '\\mathbcal{S}'},
+ 120035: {LaTeXMode.textmode: '\\mathbcal{T}'},
+ 120036: {LaTeXMode.textmode: '\\mathbcal{U}'},
+ 120037: {LaTeXMode.textmode: '\\mathbcal{V}'},
+ 120038: {LaTeXMode.textmode: '\\mathbcal{W}'},
+ 120039: {LaTeXMode.textmode: '\\mathbcal{X}'},
+ 120040: {LaTeXMode.textmode: '\\mathbcal{Y}'},
+ 120041: {LaTeXMode.textmode: '\\mathbcal{Z}'},
+ 120042: {LaTeXMode.textmode: '\\mathbcal{a}'},
+ 120043: {LaTeXMode.textmode: '\\mathbcal{b}'},
+ 120044: {LaTeXMode.textmode: '\\mathbcal{c}'},
+ 120045: {LaTeXMode.textmode: '\\mathbcal{d}'},
+ 120046: {LaTeXMode.textmode: '\\mathbcal{e}'},
+ 120047: {LaTeXMode.textmode: '\\mathbcal{f}'},
+ 120048: {LaTeXMode.textmode: '\\mathbcal{g}'},
+ 120049: {LaTeXMode.textmode: '\\mathbcal{h}'},
+ 120050: {LaTeXMode.textmode: '\\mathbcal{i}'},
+ 120051: {LaTeXMode.textmode: '\\mathbcal{j}'},
+ 120052: {LaTeXMode.textmode: '\\mathbcal{k}'},
+ 120053: {LaTeXMode.textmode: '\\mathbcal{l}'},
+ 120054: {LaTeXMode.textmode: '\\mathbcal{m}'},
+ 120055: {LaTeXMode.textmode: '\\mathbcal{n}'},
+ 120056: {LaTeXMode.textmode: '\\mathbcal{o}'},
+ 120057: {LaTeXMode.textmode: '\\mathbcal{p}'},
+ 120058: {LaTeXMode.textmode: '\\mathbcal{q}'},
+ 120059: {LaTeXMode.textmode: '\\mathbcal{r}'},
+ 120060: {LaTeXMode.textmode: '\\mathbcal{s}'},
+ 120061: {LaTeXMode.textmode: '\\mathbcal{t}'},
+ 120062: {LaTeXMode.textmode: '\\mathbcal{u}'},
+ 120063: {LaTeXMode.textmode: '\\mathbcal{v}'},
+ 120064: {LaTeXMode.textmode: '\\mathbcal{w}'},
+ 120065: {LaTeXMode.textmode: '\\mathbcal{x}'},
+ 120066: {LaTeXMode.textmode: '\\mathbcal{y}'},
+ 120067: {LaTeXMode.textmode: '\\mathbcal{z}'},
+ 120068: {LaTeXMode.textmode: '\\mathfrak{A}'},
+ 120069: {LaTeXMode.textmode: '\\mathfrak{B}'},
+ 120071: {LaTeXMode.textmode: '\\mathfrak{D}'},
+ 120072: {LaTeXMode.textmode: '\\mathfrak{E}'},
+ 120073: {LaTeXMode.textmode: '\\mathfrak{F}'},
+ 120074: {LaTeXMode.textmode: '\\mathfrak{G}'},
+ 120077: {LaTeXMode.textmode: '\\mathfrak{J}'},
+ 120078: {LaTeXMode.textmode: '\\mathfrak{K}'},
+ 120079: {LaTeXMode.textmode: '\\mathfrak{L}'},
+ 120080: {LaTeXMode.textmode: '\\mathfrak{M}'},
+ 120081: {LaTeXMode.textmode: '\\mathfrak{N}'},
+ 120082: {LaTeXMode.textmode: '\\mathfrak{O}'},
+ 120083: {LaTeXMode.textmode: '\\mathfrak{P}'},
+ 120084: {LaTeXMode.textmode: '\\mathfrak{Q}'},
+ 120086: {LaTeXMode.textmode: '\\mathfrak{S}'},
+ 120087: {LaTeXMode.textmode: '\\mathfrak{T}'},
+ 120088: {LaTeXMode.textmode: '\\mathfrak{U}'},
+ 120089: {LaTeXMode.textmode: '\\mathfrak{V}'},
+ 120090: {LaTeXMode.textmode: '\\mathfrak{W}'},
+ 120091: {LaTeXMode.textmode: '\\mathfrak{X}'},
+ 120092: {LaTeXMode.textmode: '\\mathfrak{Y}'},
+ 120094: {LaTeXMode.textmode: '\\mathfrak{a}'},
+ 120095: {LaTeXMode.textmode: '\\mathfrak{b}'},
+ 120096: {LaTeXMode.textmode: '\\mathfrak{c}'},
+ 120097: {LaTeXMode.textmode: '\\mathfrak{d}'},
+ 120098: {LaTeXMode.textmode: '\\mathfrak{e}'},
+ 120099: {LaTeXMode.textmode: '\\mathfrak{f}'},
+ 120100: {LaTeXMode.textmode: '\\mathfrak{g}'},
+ 120101: {LaTeXMode.textmode: '\\mathfrak{h}'},
+ 120102: {LaTeXMode.textmode: '\\mathfrak{i}'},
+ 120103: {LaTeXMode.textmode: '\\mathfrak{j}'},
+ 120104: {LaTeXMode.textmode: '\\mathfrak{k}'},
+ 120105: {LaTeXMode.textmode: '\\mathfrak{l}'},
+ 120106: {LaTeXMode.textmode: '\\mathfrak{m}'},
+ 120107: {LaTeXMode.textmode: '\\mathfrak{n}'},
+ 120108: {LaTeXMode.textmode: '\\mathfrak{o}'},
+ 120109: {LaTeXMode.textmode: '\\mathfrak{p}'},
+ 120110: {LaTeXMode.textmode: '\\mathfrak{q}'},
+ 120111: {LaTeXMode.textmode: '\\mathfrak{r}'},
+ 120112: {LaTeXMode.textmode: '\\mathfrak{s}'},
+ 120113: {LaTeXMode.textmode: '\\mathfrak{t}'},
+ 120114: {LaTeXMode.textmode: '\\mathfrak{u}'},
+ 120115: {LaTeXMode.textmode: '\\mathfrak{v}'},
+ 120116: {LaTeXMode.textmode: '\\mathfrak{w}'},
+ 120117: {LaTeXMode.textmode: '\\mathfrak{x}'},
+ 120118: {LaTeXMode.textmode: '\\mathfrak{y}'},
+ 120119: {LaTeXMode.textmode: '\\mathfrak{z}'},
+ 120120: {LaTeXMode.textmode: '\\mathbb{A}'},
+ 120121: {LaTeXMode.textmode: '\\mathbb{B}'},
+ 120123: {LaTeXMode.textmode: '\\mathbb{D}'},
+ 120124: {LaTeXMode.textmode: '\\mathbb{E}'},
+ 120125: {LaTeXMode.textmode: '\\mathbb{F}'},
+ 120126: {LaTeXMode.textmode: '\\mathbb{G}'},
+ 120128: {LaTeXMode.textmode: '\\mathbb{I}'},
+ 120129: {LaTeXMode.textmode: '\\mathbb{J}'},
+ 120130: {LaTeXMode.textmode: '\\mathbb{K}'},
+ 120131: {LaTeXMode.textmode: '\\mathbb{L}'},
+ 120132: {LaTeXMode.textmode: '\\mathbb{M}'},
+ 120134: {LaTeXMode.textmode: '\\mathbb{O}'},
+ 120138: {LaTeXMode.textmode: '\\mathbb{S}'},
+ 120139: {LaTeXMode.textmode: '\\mathbb{T}'},
+ 120140: {LaTeXMode.textmode: '\\mathbb{U}'},
+ 120141: {LaTeXMode.textmode: '\\mathbb{V}'},
+ 120142: {LaTeXMode.textmode: '\\mathbb{W}'},
+ 120143: {LaTeXMode.textmode: '\\mathbb{X}'},
+ 120144: {LaTeXMode.textmode: '\\mathbb{Y}'},
+ 120146: {LaTeXMode.textmode: '\\mathbb{a}'},
+ 120147: {LaTeXMode.textmode: '\\mathbb{b}'},
+ 120148: {LaTeXMode.textmode: '\\mathbb{c}'},
+ 120149: {LaTeXMode.textmode: '\\mathbb{d}'},
+ 120150: {LaTeXMode.textmode: '\\mathbb{e}'},
+ 120151: {LaTeXMode.textmode: '\\mathbb{f}'},
+ 120152: {LaTeXMode.textmode: '\\mathbb{g}'},
+ 120153: {LaTeXMode.textmode: '\\mathbb{h}'},
+ 120154: {LaTeXMode.textmode: '\\mathbb{i}'},
+ 120155: {LaTeXMode.textmode: '\\mathbb{j}'},
+ 120156: {LaTeXMode.textmode: '\\mathbb{k}'},
+ 120157: {LaTeXMode.textmode: '\\mathbb{l}'},
+ 120158: {LaTeXMode.textmode: '\\mathbb{m}'},
+ 120159: {LaTeXMode.textmode: '\\mathbb{n}'},
+ 120160: {LaTeXMode.textmode: '\\mathbb{o}'},
+ 120161: {LaTeXMode.textmode: '\\mathbb{p}'},
+ 120162: {LaTeXMode.textmode: '\\mathbb{q}'},
+ 120163: {LaTeXMode.textmode: '\\mathbb{r}'},
+ 120164: {LaTeXMode.textmode: '\\mathbb{s}'},
+ 120165: {LaTeXMode.textmode: '\\mathbb{t}'},
+ 120166: {LaTeXMode.textmode: '\\mathbb{u}'},
+ 120167: {LaTeXMode.textmode: '\\mathbb{v}'},
+ 120168: {LaTeXMode.textmode: '\\mathbb{w}'},
+ 120169: {LaTeXMode.textmode: '\\mathbb{x}'},
+ 120170: {LaTeXMode.textmode: '\\mathbb{y}'},
+ 120171: {LaTeXMode.textmode: '\\mathbb{z}'},
+ 120172: {LaTeXMode.textmode: '\\mathbfrak{A}'},
+ 120173: {LaTeXMode.textmode: '\\mathbfrak{B}'},
+ 120174: {LaTeXMode.textmode: '\\mathbfrak{C}'},
+ 120175: {LaTeXMode.textmode: '\\mathbfrak{D}'},
+ 120176: {LaTeXMode.textmode: '\\mathbfrak{E}'},
+ 120177: {LaTeXMode.textmode: '\\mathbfrak{F}'},
+ 120178: {LaTeXMode.textmode: '\\mathbfrak{G}'},
+ 120179: {LaTeXMode.textmode: '\\mathbfrak{H}'},
+ 120180: {LaTeXMode.textmode: '\\mathbfrak{I}'},
+ 120181: {LaTeXMode.textmode: '\\mathbfrak{J}'},
+ 120182: {LaTeXMode.textmode: '\\mathbfrak{K}'},
+ 120183: {LaTeXMode.textmode: '\\mathbfrak{L}'},
+ 120184: {LaTeXMode.textmode: '\\mathbfrak{M}'},
+ 120185: {LaTeXMode.textmode: '\\mathbfrak{N}'},
+ 120186: {LaTeXMode.textmode: '\\mathbfrak{O}'},
+ 120187: {LaTeXMode.textmode: '\\mathbfrak{P}'},
+ 120188: {LaTeXMode.textmode: '\\mathbfrak{Q}'},
+ 120189: {LaTeXMode.textmode: '\\mathbfrak{R}'},
+ 120190: {LaTeXMode.textmode: '\\mathbfrak{S}'},
+ 120191: {LaTeXMode.textmode: '\\mathbfrak{T}'},
+ 120192: {LaTeXMode.textmode: '\\mathbfrak{U}'},
+ 120193: {LaTeXMode.textmode: '\\mathbfrak{V}'},
+ 120194: {LaTeXMode.textmode: '\\mathbfrak{W}'},
+ 120195: {LaTeXMode.textmode: '\\mathbfrak{X}'},
+ 120196: {LaTeXMode.textmode: '\\mathbfrak{Y}'},
+ 120197: {LaTeXMode.textmode: '\\mathbfrak{Z}'},
+ 120198: {LaTeXMode.textmode: '\\mathbfrak{a}'},
+ 120199: {LaTeXMode.textmode: '\\mathbfrak{b}'},
+ 120200: {LaTeXMode.textmode: '\\mathbfrak{c}'},
+ 120201: {LaTeXMode.textmode: '\\mathbfrak{d}'},
+ 120202: {LaTeXMode.textmode: '\\mathbfrak{e}'},
+ 120203: {LaTeXMode.textmode: '\\mathbfrak{f}'},
+ 120204: {LaTeXMode.textmode: '\\mathbfrak{g}'},
+ 120205: {LaTeXMode.textmode: '\\mathbfrak{h}'},
+ 120206: {LaTeXMode.textmode: '\\mathbfrak{i}'},
+ 120207: {LaTeXMode.textmode: '\\mathbfrak{j}'},
+ 120208: {LaTeXMode.textmode: '\\mathbfrak{k}'},
+ 120209: {LaTeXMode.textmode: '\\mathbfrak{l}'},
+ 120210: {LaTeXMode.textmode: '\\mathbfrak{m}'},
+ 120211: {LaTeXMode.textmode: '\\mathbfrak{n}'},
+ 120212: {LaTeXMode.textmode: '\\mathbfrak{o}'},
+ 120213: {LaTeXMode.textmode: '\\mathbfrak{p}'},
+ 120214: {LaTeXMode.textmode: '\\mathbfrak{q}'},
+ 120215: {LaTeXMode.textmode: '\\mathbfrak{r}'},
+ 120216: {LaTeXMode.textmode: '\\mathbfrak{s}'},
+ 120217: {LaTeXMode.textmode: '\\mathbfrak{t}'},
+ 120218: {LaTeXMode.textmode: '\\mathbfrak{u}'},
+ 120219: {LaTeXMode.textmode: '\\mathbfrak{v}'},
+ 120220: {LaTeXMode.textmode: '\\mathbfrak{w}'},
+ 120221: {LaTeXMode.textmode: '\\mathbfrak{x}'},
+ 120222: {LaTeXMode.textmode: '\\mathbfrak{y}'},
+ 120223: {LaTeXMode.textmode: '\\mathbfrak{z}'},
+ 120224: {LaTeXMode.textmode: '\\mathsf{A}'},
+ 120225: {LaTeXMode.textmode: '\\mathsf{B}'},
+ 120226: {LaTeXMode.textmode: '\\mathsf{C}'},
+ 120227: {LaTeXMode.textmode: '\\mathsf{D}'},
+ 120228: {LaTeXMode.textmode: '\\mathsf{E}'},
+ 120229: {LaTeXMode.textmode: '\\mathsf{F}'},
+ 120230: {LaTeXMode.textmode: '\\mathsf{G}'},
+ 120231: {LaTeXMode.textmode: '\\mathsf{H}'},
+ 120232: {LaTeXMode.textmode: '\\mathsf{I}'},
+ 120233: {LaTeXMode.textmode: '\\mathsf{J}'},
+ 120234: {LaTeXMode.textmode: '\\mathsf{K}'},
+ 120235: {LaTeXMode.textmode: '\\mathsf{L}'},
+ 120236: {LaTeXMode.textmode: '\\mathsf{M}'},
+ 120237: {LaTeXMode.textmode: '\\mathsf{N}'},
+ 120238: {LaTeXMode.textmode: '\\mathsf{O}'},
+ 120239: {LaTeXMode.textmode: '\\mathsf{P}'},
+ 120240: {LaTeXMode.textmode: '\\mathsf{Q}'},
+ 120241: {LaTeXMode.textmode: '\\mathsf{R}'},
+ 120242: {LaTeXMode.textmode: '\\mathsf{S}'},
+ 120243: {LaTeXMode.textmode: '\\mathsf{T}'},
+ 120244: {LaTeXMode.textmode: '\\mathsf{U}'},
+ 120245: {LaTeXMode.textmode: '\\mathsf{V}'},
+ 120246: {LaTeXMode.textmode: '\\mathsf{W}'},
+ 120247: {LaTeXMode.textmode: '\\mathsf{X}'},
+ 120248: {LaTeXMode.textmode: '\\mathsf{Y}'},
+ 120249: {LaTeXMode.textmode: '\\mathsf{Z}'},
+ 120250: {LaTeXMode.textmode: '\\mathsf{a}'},
+ 120251: {LaTeXMode.textmode: '\\mathsf{b}'},
+ 120252: {LaTeXMode.textmode: '\\mathsf{c}'},
+ 120253: {LaTeXMode.textmode: '\\mathsf{d}'},
+ 120254: {LaTeXMode.textmode: '\\mathsf{e}'},
+ 120255: {LaTeXMode.textmode: '\\mathsf{f}'},
+ 120256: {LaTeXMode.textmode: '\\mathsf{g}'},
+ 120257: {LaTeXMode.textmode: '\\mathsf{h}'},
+ 120258: {LaTeXMode.textmode: '\\mathsf{i}'},
+ 120259: {LaTeXMode.textmode: '\\mathsf{j}'},
+ 120260: {LaTeXMode.textmode: '\\mathsf{k}'},
+ 120261: {LaTeXMode.textmode: '\\mathsf{l}'},
+ 120262: {LaTeXMode.textmode: '\\mathsf{m}'},
+ 120263: {LaTeXMode.textmode: '\\mathsf{n}'},
+ 120264: {LaTeXMode.textmode: '\\mathsf{o}'},
+ 120265: {LaTeXMode.textmode: '\\mathsf{p}'},
+ 120266: {LaTeXMode.textmode: '\\mathsf{q}'},
+ 120267: {LaTeXMode.textmode: '\\mathsf{r}'},
+ 120268: {LaTeXMode.textmode: '\\mathsf{s}'},
+ 120269: {LaTeXMode.textmode: '\\mathsf{t}'},
+ 120270: {LaTeXMode.textmode: '\\mathsf{u}'},
+ 120271: {LaTeXMode.textmode: '\\mathsf{v}'},
+ 120272: {LaTeXMode.textmode: '\\mathsf{w}'},
+ 120273: {LaTeXMode.textmode: '\\mathsf{x}'},
+ 120274: {LaTeXMode.textmode: '\\mathsf{y}'},
+ 120275: {LaTeXMode.textmode: '\\mathsf{z}'},
+ 120276: {LaTeXMode.textmode: '\\mathsfbf{A}'},
+ 120277: {LaTeXMode.textmode: '\\mathsfbf{B}'},
+ 120278: {LaTeXMode.textmode: '\\mathsfbf{C}'},
+ 120279: {LaTeXMode.textmode: '\\mathsfbf{D}'},
+ 120280: {LaTeXMode.textmode: '\\mathsfbf{E}'},
+ 120281: {LaTeXMode.textmode: '\\mathsfbf{F}'},
+ 120282: {LaTeXMode.textmode: '\\mathsfbf{G}'},
+ 120283: {LaTeXMode.textmode: '\\mathsfbf{H}'},
+ 120284: {LaTeXMode.textmode: '\\mathsfbf{I}'},
+ 120285: {LaTeXMode.textmode: '\\mathsfbf{J}'},
+ 120286: {LaTeXMode.textmode: '\\mathsfbf{K}'},
+ 120287: {LaTeXMode.textmode: '\\mathsfbf{L}'},
+ 120288: {LaTeXMode.textmode: '\\mathsfbf{M}'},
+ 120289: {LaTeXMode.textmode: '\\mathsfbf{N}'},
+ 120290: {LaTeXMode.textmode: '\\mathsfbf{O}'},
+ 120291: {LaTeXMode.textmode: '\\mathsfbf{P}'},
+ 120292: {LaTeXMode.textmode: '\\mathsfbf{Q}'},
+ 120293: {LaTeXMode.textmode: '\\mathsfbf{R}'},
+ 120294: {LaTeXMode.textmode: '\\mathsfbf{S}'},
+ 120295: {LaTeXMode.textmode: '\\mathsfbf{T}'},
+ 120296: {LaTeXMode.textmode: '\\mathsfbf{U}'},
+ 120297: {LaTeXMode.textmode: '\\mathsfbf{V}'},
+ 120298: {LaTeXMode.textmode: '\\mathsfbf{W}'},
+ 120299: {LaTeXMode.textmode: '\\mathsfbf{X}'},
+ 120300: {LaTeXMode.textmode: '\\mathsfbf{Y}'},
+ 120301: {LaTeXMode.textmode: '\\mathsfbf{Z}'},
+ 120302: {LaTeXMode.textmode: '\\mathsfbf{a}'},
+ 120303: {LaTeXMode.textmode: '\\mathsfbf{b}'},
+ 120304: {LaTeXMode.textmode: '\\mathsfbf{c}'},
+ 120305: {LaTeXMode.textmode: '\\mathsfbf{d}'},
+ 120306: {LaTeXMode.textmode: '\\mathsfbf{e}'},
+ 120307: {LaTeXMode.textmode: '\\mathsfbf{f}'},
+ 120308: {LaTeXMode.textmode: '\\mathsfbf{g}'},
+ 120309: {LaTeXMode.textmode: '\\mathsfbf{h}'},
+ 120310: {LaTeXMode.textmode: '\\mathsfbf{i}'},
+ 120311: {LaTeXMode.textmode: '\\mathsfbf{j}'},
+ 120312: {LaTeXMode.textmode: '\\mathsfbf{k}'},
+ 120313: {LaTeXMode.textmode: '\\mathsfbf{l}'},
+ 120314: {LaTeXMode.textmode: '\\mathsfbf{m}'},
+ 120315: {LaTeXMode.textmode: '\\mathsfbf{n}'},
+ 120316: {LaTeXMode.textmode: '\\mathsfbf{o}'},
+ 120317: {LaTeXMode.textmode: '\\mathsfbf{p}'},
+ 120318: {LaTeXMode.textmode: '\\mathsfbf{q}'},
+ 120319: {LaTeXMode.textmode: '\\mathsfbf{r}'},
+ 120320: {LaTeXMode.textmode: '\\mathsfbf{s}'},
+ 120321: {LaTeXMode.textmode: '\\mathsfbf{t}'},
+ 120322: {LaTeXMode.textmode: '\\mathsfbf{u}'},
+ 120323: {LaTeXMode.textmode: '\\mathsfbf{v}'},
+ 120324: {LaTeXMode.textmode: '\\mathsfbf{w}'},
+ 120325: {LaTeXMode.textmode: '\\mathsfbf{x}'},
+ 120326: {LaTeXMode.textmode: '\\mathsfbf{y}'},
+ 120327: {LaTeXMode.textmode: '\\mathsfbf{z}'},
+ 120328: {LaTeXMode.textmode: '\\mathsfsl{A}'},
+ 120329: {LaTeXMode.textmode: '\\mathsfsl{B}'},
+ 120330: {LaTeXMode.textmode: '\\mathsfsl{C}'},
+ 120331: {LaTeXMode.textmode: '\\mathsfsl{D}'},
+ 120332: {LaTeXMode.textmode: '\\mathsfsl{E}'},
+ 120333: {LaTeXMode.textmode: '\\mathsfsl{F}'},
+ 120334: {LaTeXMode.textmode: '\\mathsfsl{G}'},
+ 120335: {LaTeXMode.textmode: '\\mathsfsl{H}'},
+ 120336: {LaTeXMode.textmode: '\\mathsfsl{I}'},
+ 120337: {LaTeXMode.textmode: '\\mathsfsl{J}'},
+ 120338: {LaTeXMode.textmode: '\\mathsfsl{K}'},
+ 120339: {LaTeXMode.textmode: '\\mathsfsl{L}'},
+ 120340: {LaTeXMode.textmode: '\\mathsfsl{M}'},
+ 120341: {LaTeXMode.textmode: '\\mathsfsl{N}'},
+ 120342: {LaTeXMode.textmode: '\\mathsfsl{O}'},
+ 120343: {LaTeXMode.textmode: '\\mathsfsl{P}'},
+ 120344: {LaTeXMode.textmode: '\\mathsfsl{Q}'},
+ 120345: {LaTeXMode.textmode: '\\mathsfsl{R}'},
+ 120346: {LaTeXMode.textmode: '\\mathsfsl{S}'},
+ 120347: {LaTeXMode.textmode: '\\mathsfsl{T}'},
+ 120348: {LaTeXMode.textmode: '\\mathsfsl{U}'},
+ 120349: {LaTeXMode.textmode: '\\mathsfsl{V}'},
+ 120350: {LaTeXMode.textmode: '\\mathsfsl{W}'},
+ 120351: {LaTeXMode.textmode: '\\mathsfsl{X}'},
+ 120352: {LaTeXMode.textmode: '\\mathsfsl{Y}'},
+ 120353: {LaTeXMode.textmode: '\\mathsfsl{Z}'},
+ 120354: {LaTeXMode.textmode: '\\mathsfsl{a}'},
+ 120355: {LaTeXMode.textmode: '\\mathsfsl{b}'},
+ 120356: {LaTeXMode.textmode: '\\mathsfsl{c}'},
+ 120357: {LaTeXMode.textmode: '\\mathsfsl{d}'},
+ 120358: {LaTeXMode.textmode: '\\mathsfsl{e}'},
+ 120359: {LaTeXMode.textmode: '\\mathsfsl{f}'},
+ 120360: {LaTeXMode.textmode: '\\mathsfsl{g}'},
+ 120361: {LaTeXMode.textmode: '\\mathsfsl{h}'},
+ 120362: {LaTeXMode.textmode: '\\mathsfsl{i}'},
+ 120363: {LaTeXMode.textmode: '\\mathsfsl{j}'},
+ 120364: {LaTeXMode.textmode: '\\mathsfsl{k}'},
+ 120365: {LaTeXMode.textmode: '\\mathsfsl{l}'},
+ 120366: {LaTeXMode.textmode: '\\mathsfsl{m}'},
+ 120367: {LaTeXMode.textmode: '\\mathsfsl{n}'},
+ 120368: {LaTeXMode.textmode: '\\mathsfsl{o}'},
+ 120369: {LaTeXMode.textmode: '\\mathsfsl{p}'},
+ 120370: {LaTeXMode.textmode: '\\mathsfsl{q}'},
+ 120371: {LaTeXMode.textmode: '\\mathsfsl{r}'},
+ 120372: {LaTeXMode.textmode: '\\mathsfsl{s}'},
+ 120373: {LaTeXMode.textmode: '\\mathsfsl{t}'},
+ 120374: {LaTeXMode.textmode: '\\mathsfsl{u}'},
+ 120375: {LaTeXMode.textmode: '\\mathsfsl{v}'},
+ 120376: {LaTeXMode.textmode: '\\mathsfsl{w}'},
+ 120377: {LaTeXMode.textmode: '\\mathsfsl{x}'},
+ 120378: {LaTeXMode.textmode: '\\mathsfsl{y}'},
+ 120379: {LaTeXMode.textmode: '\\mathsfsl{z}'},
+ 120380: {LaTeXMode.textmode: '\\mathsfbfsl{A}'},
+ 120381: {LaTeXMode.textmode: '\\mathsfbfsl{B}'},
+ 120382: {LaTeXMode.textmode: '\\mathsfbfsl{C}'},
+ 120383: {LaTeXMode.textmode: '\\mathsfbfsl{D}'},
+ 120384: {LaTeXMode.textmode: '\\mathsfbfsl{E}'},
+ 120385: {LaTeXMode.textmode: '\\mathsfbfsl{F}'},
+ 120386: {LaTeXMode.textmode: '\\mathsfbfsl{G}'},
+ 120387: {LaTeXMode.textmode: '\\mathsfbfsl{H}'},
+ 120388: {LaTeXMode.textmode: '\\mathsfbfsl{I}'},
+ 120389: {LaTeXMode.textmode: '\\mathsfbfsl{J}'},
+ 120390: {LaTeXMode.textmode: '\\mathsfbfsl{K}'},
+ 120391: {LaTeXMode.textmode: '\\mathsfbfsl{L}'},
+ 120392: {LaTeXMode.textmode: '\\mathsfbfsl{M}'},
+ 120393: {LaTeXMode.textmode: '\\mathsfbfsl{N}'},
+ 120394: {LaTeXMode.textmode: '\\mathsfbfsl{O}'},
+ 120395: {LaTeXMode.textmode: '\\mathsfbfsl{P}'},
+ 120396: {LaTeXMode.textmode: '\\mathsfbfsl{Q}'},
+ 120397: {LaTeXMode.textmode: '\\mathsfbfsl{R}'},
+ 120398: {LaTeXMode.textmode: '\\mathsfbfsl{S}'},
+ 120399: {LaTeXMode.textmode: '\\mathsfbfsl{T}'},
+ 120400: {LaTeXMode.textmode: '\\mathsfbfsl{U}'},
+ 120401: {LaTeXMode.textmode: '\\mathsfbfsl{V}'},
+ 120402: {LaTeXMode.textmode: '\\mathsfbfsl{W}'},
+ 120403: {LaTeXMode.textmode: '\\mathsfbfsl{X}'},
+ 120404: {LaTeXMode.textmode: '\\mathsfbfsl{Y}'},
+ 120405: {LaTeXMode.textmode: '\\mathsfbfsl{Z}'},
+ 120406: {LaTeXMode.textmode: '\\mathsfbfsl{a}'},
+ 120407: {LaTeXMode.textmode: '\\mathsfbfsl{b}'},
+ 120408: {LaTeXMode.textmode: '\\mathsfbfsl{c}'},
+ 120409: {LaTeXMode.textmode: '\\mathsfbfsl{d}'},
+ 120410: {LaTeXMode.textmode: '\\mathsfbfsl{e}'},
+ 120411: {LaTeXMode.textmode: '\\mathsfbfsl{f}'},
+ 120412: {LaTeXMode.textmode: '\\mathsfbfsl{g}'},
+ 120413: {LaTeXMode.textmode: '\\mathsfbfsl{h}'},
+ 120414: {LaTeXMode.textmode: '\\mathsfbfsl{i}'},
+ 120415: {LaTeXMode.textmode: '\\mathsfbfsl{j}'},
+ 120416: {LaTeXMode.textmode: '\\mathsfbfsl{k}'},
+ 120417: {LaTeXMode.textmode: '\\mathsfbfsl{l}'},
+ 120418: {LaTeXMode.textmode: '\\mathsfbfsl{m}'},
+ 120419: {LaTeXMode.textmode: '\\mathsfbfsl{n}'},
+ 120420: {LaTeXMode.textmode: '\\mathsfbfsl{o}'},
+ 120421: {LaTeXMode.textmode: '\\mathsfbfsl{p}'},
+ 120422: {LaTeXMode.textmode: '\\mathsfbfsl{q}'},
+ 120423: {LaTeXMode.textmode: '\\mathsfbfsl{r}'},
+ 120424: {LaTeXMode.textmode: '\\mathsfbfsl{s}'},
+ 120425: {LaTeXMode.textmode: '\\mathsfbfsl{t}'},
+ 120426: {LaTeXMode.textmode: '\\mathsfbfsl{u}'},
+ 120427: {LaTeXMode.textmode: '\\mathsfbfsl{v}'},
+ 120428: {LaTeXMode.textmode: '\\mathsfbfsl{w}'},
+ 120429: {LaTeXMode.textmode: '\\mathsfbfsl{x}'},
+ 120430: {LaTeXMode.textmode: '\\mathsfbfsl{y}'},
+ 120431: {LaTeXMode.textmode: '\\mathsfbfsl{z}'},
+ 120432: {LaTeXMode.textmode: '\\mathtt{A}'},
+ 120433: {LaTeXMode.textmode: '\\mathtt{B}'},
+ 120434: {LaTeXMode.textmode: '\\mathtt{C}'},
+ 120435: {LaTeXMode.textmode: '\\mathtt{D}'},
+ 120436: {LaTeXMode.textmode: '\\mathtt{E}'},
+ 120437: {LaTeXMode.textmode: '\\mathtt{F}'},
+ 120438: {LaTeXMode.textmode: '\\mathtt{G}'},
+ 120439: {LaTeXMode.textmode: '\\mathtt{H}'},
+ 120440: {LaTeXMode.textmode: '\\mathtt{I}'},
+ 120441: {LaTeXMode.textmode: '\\mathtt{J}'},
+ 120442: {LaTeXMode.textmode: '\\mathtt{K}'},
+ 120443: {LaTeXMode.textmode: '\\mathtt{L}'},
+ 120444: {LaTeXMode.textmode: '\\mathtt{M}'},
+ 120445: {LaTeXMode.textmode: '\\mathtt{N}'},
+ 120446: {LaTeXMode.textmode: '\\mathtt{O}'},
+ 120447: {LaTeXMode.textmode: '\\mathtt{P}'},
+ 120448: {LaTeXMode.textmode: '\\mathtt{Q}'},
+ 120449: {LaTeXMode.textmode: '\\mathtt{R}'},
+ 120450: {LaTeXMode.textmode: '\\mathtt{S}'},
+ 120451: {LaTeXMode.textmode: '\\mathtt{T}'},
+ 120452: {LaTeXMode.textmode: '\\mathtt{U}'},
+ 120453: {LaTeXMode.textmode: '\\mathtt{V}'},
+ 120454: {LaTeXMode.textmode: '\\mathtt{W}'},
+ 120455: {LaTeXMode.textmode: '\\mathtt{X}'},
+ 120456: {LaTeXMode.textmode: '\\mathtt{Y}'},
+ 120457: {LaTeXMode.textmode: '\\mathtt{Z}'},
+ 120458: {LaTeXMode.textmode: '\\mathtt{a}'},
+ 120459: {LaTeXMode.textmode: '\\mathtt{b}'},
+ 120460: {LaTeXMode.textmode: '\\mathtt{c}'},
+ 120461: {LaTeXMode.textmode: '\\mathtt{d}'},
+ 120462: {LaTeXMode.textmode: '\\mathtt{e}'},
+ 120463: {LaTeXMode.textmode: '\\mathtt{f}'},
+ 120464: {LaTeXMode.textmode: '\\mathtt{g}'},
+ 120465: {LaTeXMode.textmode: '\\mathtt{h}'},
+ 120466: {LaTeXMode.textmode: '\\mathtt{i}'},
+ 120467: {LaTeXMode.textmode: '\\mathtt{j}'},
+ 120468: {LaTeXMode.textmode: '\\mathtt{k}'},
+ 120469: {LaTeXMode.textmode: '\\mathtt{l}'},
+ 120470: {LaTeXMode.textmode: '\\mathtt{m}'},
+ 120471: {LaTeXMode.textmode: '\\mathtt{n}'},
+ 120472: {LaTeXMode.textmode: '\\mathtt{o}'},
+ 120473: {LaTeXMode.textmode: '\\mathtt{p}'},
+ 120474: {LaTeXMode.textmode: '\\mathtt{q}'},
+ 120475: {LaTeXMode.textmode: '\\mathtt{r}'},
+ 120476: {LaTeXMode.textmode: '\\mathtt{s}'},
+ 120477: {LaTeXMode.textmode: '\\mathtt{t}'},
+ 120478: {LaTeXMode.textmode: '\\mathtt{u}'},
+ 120479: {LaTeXMode.textmode: '\\mathtt{v}'},
+ 120480: {LaTeXMode.textmode: '\\mathtt{w}'},
+ 120481: {LaTeXMode.textmode: '\\mathtt{x}'},
+ 120482: {LaTeXMode.textmode: '\\mathtt{y}'},
+ 120483: {LaTeXMode.textmode: '\\mathtt{z}'},
+ 120488: {LaTeXMode.textmode: '\\mathbf{\\Alpha}'},
+ 120489: {LaTeXMode.textmode: '\\mathbf{\\Beta}'},
+ 120490: {LaTeXMode.textmode: '\\mathbf{\\Gamma}'},
+ 120491: {LaTeXMode.textmode: '\\mathbf{\\Delta}'},
+ 120492: {LaTeXMode.textmode: '\\mathbf{\\Epsilon}'},
+ 120493: {LaTeXMode.textmode: '\\mathbf{\\Zeta}'},
+ 120494: {LaTeXMode.textmode: '\\mathbf{\\Eta}'},
+ 120495: {LaTeXMode.textmode: '\\mathbf{\\Theta}'},
+ 120496: {LaTeXMode.textmode: '\\mathbf{\\Iota}'},
+ 120497: {LaTeXMode.textmode: '\\mathbf{\\Kappa}'},
+ 120498: {LaTeXMode.textmode: '\\mathbf{\\Lambda}'},
+ 120499: {LaTeXMode.textmode: '\\mathbf{M}'},
+ 120500: {LaTeXMode.textmode: 'N'},
+ 120501: {LaTeXMode.textmode: '\\mathbf{\\Xi}'},
+ 120502: {LaTeXMode.textmode: 'O'},
+ 120503: {LaTeXMode.textmode: '\\mathbf{\\Pi}'},
+ 120504: {LaTeXMode.textmode: '\\mathbf{\\Rho}'},
+ 120505: {LaTeXMode.textmode: '\\mathbf{\\vartheta}'},
+ 120506: {LaTeXMode.textmode: '\\mathbf{\\Sigma}'},
+ 120507: {LaTeXMode.textmode: '\\mathbf{\\Tau}'},
+ 120508: {LaTeXMode.textmode: '\\mathbf{\\Upsilon}'},
+ 120509: {LaTeXMode.textmode: '\\mathbf{\\Phi}'},
+ 120510: {LaTeXMode.textmode: '\\mathbf{\\Chi}'},
+ 120511: {LaTeXMode.textmode: '\\mathbf{\\Psi}'},
+ 120512: {LaTeXMode.textmode: '\\mathbf{\\Omega}'},
+ 120513: {LaTeXMode.textmode: '\\mathbf{\\nabla}'},
+ 120514: {LaTeXMode.textmode: '\\mathbf{\\alpha}'},
+ 120515: {LaTeXMode.textmode: '\\mathbf{\\beta}'},
+ 120516: {LaTeXMode.textmode: '\\mathbf{\\gamma}'},
+ 120517: {LaTeXMode.textmode: '\\mathbf{\\delta}'},
+ 120518: {LaTeXMode.textmode: '\\mathbf{\\epsilon}'},
+ 120519: {LaTeXMode.textmode: '\\mathbf{\\zeta}'},
+ 120520: {LaTeXMode.textmode: '\\mathbf{\\eta}'},
+ 120521: {LaTeXMode.textmode: '\\mathbf{\\theta}'},
+ 120522: {LaTeXMode.textmode: '\\mathbf{\\iota}'},
+ 120523: {LaTeXMode.textmode: '\\mathbf{\\kappa}'},
+ 120524: {LaTeXMode.textmode: '\\mathbf{\\lambda}'},
+ 120525: {LaTeXMode.textmode: '\\mathbf{\\mu}'},
+ 120526: {LaTeXMode.textmode: '\\mathbf{\\nu}'},
+ 120527: {LaTeXMode.textmode: '\\mathbf{\\xi}'},
+ 120528: {LaTeXMode.textmode: '\\mathbf{o}'},
+ 120529: {LaTeXMode.textmode: '\\mathbf{\\pi}'},
+ 120530: {LaTeXMode.textmode: '\\mathbf{\\rho}'},
+ 120531: {LaTeXMode.textmode: '\\mathbf{\\varsigma}'},
+ 120532: {LaTeXMode.textmode: '\\mathbf{\\sigma}'},
+ 120533: {LaTeXMode.textmode: '\\mathbf{\\tau}'},
+ 120534: {LaTeXMode.textmode: '\\mathbf{\\upsilon}'},
+ 120535: {LaTeXMode.textmode: '\\mathbf{\\phi}'},
+ 120536: {LaTeXMode.textmode: '\\mathbf{\\chi}'},
+ 120537: {LaTeXMode.textmode: '\\mathbf{\\psi}'},
+ 120538: {LaTeXMode.textmode: '\\mathbf{\\omega}'},
+ 120539: {LaTeXMode.textmode: '\\partial '},
+ 120540: {LaTeXMode.textmode: '\\mathbf{\\varepsilon}'},
+ 120541: {LaTeXMode.textmode: '\\mathbf{\\vartheta}'},
+ 120542: {LaTeXMode.textmode: '\\mathbf{\\varkappa}'},
+ 120543: {LaTeXMode.textmode: '\\mathbf{\\phi}'},
+ 120544: {LaTeXMode.textmode: '\\mathbf{\\varrho}'},
+ 120545: {LaTeXMode.textmode: '\\mathbf{\\varpi}'},
+ 120546: {LaTeXMode.textmode: '\\mathmit{\\Alpha}'},
+ 120547: {LaTeXMode.textmode: '\\mathmit{\\Beta}'},
+ 120548: {LaTeXMode.textmode: '\\mathmit{\\Gamma}'},
+ 120549: {LaTeXMode.textmode: '\\mathmit{\\Delta}'},
+ 120550: {LaTeXMode.textmode: '\\mathmit{\\Epsilon}'},
+ 120551: {LaTeXMode.textmode: '\\mathmit{\\Zeta}'},
+ 120552: {LaTeXMode.textmode: '\\mathmit{\\Eta}'},
+ 120553: {LaTeXMode.textmode: '\\mathmit{\\Theta}'},
+ 120554: {LaTeXMode.textmode: '\\mathmit{\\Iota}'},
+ 120555: {LaTeXMode.textmode: '\\mathmit{\\Kappa}'},
+ 120556: {LaTeXMode.textmode: '\\mathmit{\\Lambda}'},
+ 120557: {LaTeXMode.textmode: '\\mathmit{M}'},
+ 120558: {LaTeXMode.textmode: 'N'},
+ 120559: {LaTeXMode.textmode: '\\mathmit{\\Xi}'},
+ 120560: {LaTeXMode.textmode: 'O'},
+ 120561: {LaTeXMode.textmode: '\\mathmit{\\Pi}'},
+ 120562: {LaTeXMode.textmode: '\\mathmit{\\Rho}'},
+ 120563: {LaTeXMode.textmode: '\\mathmit{\\vartheta}'},
+ 120564: {LaTeXMode.textmode: '\\mathmit{\\Sigma}'},
+ 120565: {LaTeXMode.textmode: '\\mathmit{\\Tau}'},
+ 120566: {LaTeXMode.textmode: '\\mathmit{\\Upsilon}'},
+ 120567: {LaTeXMode.textmode: '\\mathmit{\\Phi}'},
+ 120568: {LaTeXMode.textmode: '\\mathmit{\\Chi}'},
+ 120569: {LaTeXMode.textmode: '\\mathmit{\\Psi}'},
+ 120570: {LaTeXMode.textmode: '\\mathmit{\\Omega}'},
+ 120571: {LaTeXMode.textmode: '\\mathmit{\\nabla}'},
+ 120572: {LaTeXMode.textmode: '\\mathmit{\\alpha}'},
+ 120573: {LaTeXMode.textmode: '\\mathmit{\\beta}'},
+ 120574: {LaTeXMode.textmode: '\\mathmit{\\gamma}'},
+ 120575: {LaTeXMode.textmode: '\\mathmit{\\delta}'},
+ 120576: {LaTeXMode.textmode: '\\mathmit{\\epsilon}'},
+ 120577: {LaTeXMode.textmode: '\\mathmit{\\zeta}'},
+ 120578: {LaTeXMode.textmode: '\\mathmit{\\eta}'},
+ 120579: {LaTeXMode.textmode: '\\mathmit{\\theta}'},
+ 120580: {LaTeXMode.textmode: '\\mathmit{\\iota}'},
+ 120581: {LaTeXMode.textmode: '\\mathmit{\\kappa}'},
+ 120582: {LaTeXMode.textmode: '\\mathmit{\\lambda}'},
+ 120583: {LaTeXMode.textmode: '\\mathmit{\\mu}'},
+ 120584: {LaTeXMode.textmode: '\\mathmit{\\nu}'},
+ 120585: {LaTeXMode.textmode: '\\mathmit{\\xi}'},
+ 120586: {LaTeXMode.textmode: '\\mathmit{o}'},
+ 120587: {LaTeXMode.textmode: '\\mathmit{\\pi}'},
+ 120588: {LaTeXMode.textmode: '\\mathmit{\\rho}'},
+ 120589: {LaTeXMode.textmode: '\\mathmit{\\varsigma}'},
+ 120590: {LaTeXMode.textmode: '\\mathmit{\\sigma}'},
+ 120591: {LaTeXMode.textmode: '\\mathmit{\\tau}'},
+ 120592: {LaTeXMode.textmode: '\\mathmit{\\upsilon}'},
+ 120593: {LaTeXMode.textmode: '\\mathmit{\\phi}'},
+ 120594: {LaTeXMode.textmode: '\\mathmit{\\chi}'},
+ 120595: {LaTeXMode.textmode: '\\mathmit{\\psi}'},
+ 120596: {LaTeXMode.textmode: '\\mathmit{\\omega}'},
+ 120597: {LaTeXMode.textmode: '\\partial '},
+ 120598: {LaTeXMode.textmode: '\\in'},
+ 120599: {LaTeXMode.textmode: '\\mathmit{\\vartheta}'},
+ 120600: {LaTeXMode.textmode: '\\mathmit{\\varkappa}'},
+ 120601: {LaTeXMode.textmode: '\\mathmit{\\phi}'},
+ 120602: {LaTeXMode.textmode: '\\mathmit{\\varrho}'},
+ 120603: {LaTeXMode.textmode: '\\mathmit{\\varpi}'},
+ 120604: {LaTeXMode.textmode: '\\mathbit{\\Alpha}'},
+ 120605: {LaTeXMode.textmode: '\\mathbit{\\Beta}'},
+ 120606: {LaTeXMode.textmode: '\\mathbit{\\Gamma}'},
+ 120607: {LaTeXMode.textmode: '\\mathbit{\\Delta}'},
+ 120608: {LaTeXMode.textmode: '\\mathbit{\\Epsilon}'},
+ 120609: {LaTeXMode.textmode: '\\mathbit{\\Zeta}'},
+ 120610: {LaTeXMode.textmode: '\\mathbit{\\Eta}'},
+ 120611: {LaTeXMode.textmode: '\\mathbit{\\Theta}'},
+ 120612: {LaTeXMode.textmode: '\\mathbit{\\Iota}'},
+ 120613: {LaTeXMode.textmode: '\\mathbit{\\Kappa}'},
+ 120614: {LaTeXMode.textmode: '\\mathbit{\\Lambda}'},
+ 120615: {LaTeXMode.textmode: '\\mathbit{M}'},
+ 120616: {LaTeXMode.textmode: '\\mathbit{N}'},
+ 120617: {LaTeXMode.textmode: '\\mathbit{\\Xi}'},
+ 120618: {LaTeXMode.textmode: 'O'},
+ 120619: {LaTeXMode.textmode: '\\mathbit{\\Pi}'},
+ 120620: {LaTeXMode.textmode: '\\mathbit{\\Rho}'},
+ 120621: {LaTeXMode.textmode: '\\mathbit{O}'},
+ 120622: {LaTeXMode.textmode: '\\mathbit{\\Sigma}'},
+ 120623: {LaTeXMode.textmode: '\\mathbit{\\Tau}'},
+ 120624: {LaTeXMode.textmode: '\\mathbit{\\Upsilon}'},
+ 120625: {LaTeXMode.textmode: '\\mathbit{\\Phi}'},
+ 120626: {LaTeXMode.textmode: '\\mathbit{\\Chi}'},
+ 120627: {LaTeXMode.textmode: '\\mathbit{\\Psi}'},
+ 120628: {LaTeXMode.textmode: '\\mathbit{\\Omega}'},
+ 120629: {LaTeXMode.textmode: '\\mathbit{\\nabla}'},
+ 120630: {LaTeXMode.textmode: '\\mathbit{\\alpha}'},
+ 120631: {LaTeXMode.textmode: '\\mathbit{\\beta}'},
+ 120632: {LaTeXMode.textmode: '\\mathbit{\\gamma}'},
+ 120633: {LaTeXMode.textmode: '\\mathbit{\\delta}'},
+ 120634: {LaTeXMode.textmode: '\\mathbit{\\epsilon}'},
+ 120635: {LaTeXMode.textmode: '\\mathbit{\\zeta}'},
+ 120636: {LaTeXMode.textmode: '\\mathbit{\\eta}'},
+ 120637: {LaTeXMode.textmode: '\\mathbit{\\theta}'},
+ 120638: {LaTeXMode.textmode: '\\mathbit{\\iota}'},
+ 120639: {LaTeXMode.textmode: '\\mathbit{\\kappa}'},
+ 120640: {LaTeXMode.textmode: '\\mathbit{\\lambda}'},
+ 120641: {LaTeXMode.textmode: '\\mathbit{\\mu}'},
+ 120642: {LaTeXMode.textmode: '\\mathbit{\\nu}'},
+ 120643: {LaTeXMode.textmode: '\\mathbit{\\xi}'},
+ 120644: {LaTeXMode.textmode: '\\mathbit{o}'},
+ 120645: {LaTeXMode.textmode: '\\mathbit{\\pi}'},
+ 120646: {LaTeXMode.textmode: '\\mathbit{\\rho}'},
+ 120647: {LaTeXMode.textmode: '\\mathbit{\\varsigma}'},
+ 120648: {LaTeXMode.textmode: '\\mathbit{\\sigma}'},
+ 120649: {LaTeXMode.textmode: '\\mathbit{\\tau}'},
+ 120650: {LaTeXMode.textmode: '\\mathbit{\\upsilon}'},
+ 120651: {LaTeXMode.textmode: '\\mathbit{\\phi}'},
+ 120652: {LaTeXMode.textmode: '\\mathbit{\\chi}'},
+ 120653: {LaTeXMode.textmode: '\\mathbit{\\psi}'},
+ 120654: {LaTeXMode.textmode: '\\mathbit{\\omega}'},
+ 120655: {LaTeXMode.textmode: '\\partial '},
+ 120656: {LaTeXMode.textmode: '\\in'},
+ 120657: {LaTeXMode.textmode: '\\mathbit{\\vartheta}'},
+ 120658: {LaTeXMode.textmode: '\\mathbit{\\varkappa}'},
+ 120659: {LaTeXMode.textmode: '\\mathbit{\\phi}'},
+ 120660: {LaTeXMode.textmode: '\\mathbit{\\varrho}'},
+ 120661: {LaTeXMode.textmode: '\\mathbit{\\varpi}'},
+ 120662: {LaTeXMode.textmode: '\\mathsfbf{\\Alpha}'},
+ 120663: {LaTeXMode.textmode: '\\mathsfbf{\\Beta}'},
+ 120664: {LaTeXMode.textmode: '\\mathsfbf{\\Gamma}'},
+ 120665: {LaTeXMode.textmode: '\\mathsfbf{\\Delta}'},
+ 120666: {LaTeXMode.textmode: '\\mathsfbf{\\Epsilon}'},
+ 120667: {LaTeXMode.textmode: '\\mathsfbf{\\Zeta}'},
+ 120668: {LaTeXMode.textmode: '\\mathsfbf{\\Eta}'},
+ 120669: {LaTeXMode.textmode: '\\mathsfbf{\\Theta}'},
+ 120670: {LaTeXMode.textmode: '\\mathsfbf{\\Iota}'},
+ 120671: {LaTeXMode.textmode: '\\mathsfbf{\\Kappa}'},
+ 120672: {LaTeXMode.textmode: '\\mathsfbf{\\Lambda}'},
+ 120673: {LaTeXMode.textmode: '\\mathsfbf{M}'},
+ 120674: {LaTeXMode.textmode: '\\mathsfbf{N}'},
+ 120675: {LaTeXMode.textmode: '\\mathsfbf{\\Xi}'},
+ 120676: {LaTeXMode.textmode: 'O'},
+ 120677: {LaTeXMode.textmode: '\\mathsfbf{\\Pi}'},
+ 120678: {LaTeXMode.textmode: '\\mathsfbf{\\Rho}'},
+ 120679: {LaTeXMode.textmode: '\\mathsfbf{\\vartheta}'},
+ 120680: {LaTeXMode.textmode: '\\mathsfbf{\\Sigma}'},
+ 120681: {LaTeXMode.textmode: '\\mathsfbf{\\Tau}'},
+ 120682: {LaTeXMode.textmode: '\\mathsfbf{\\Upsilon}'},
+ 120683: {LaTeXMode.textmode: '\\mathsfbf{\\Phi}'},
+ 120684: {LaTeXMode.textmode: '\\mathsfbf{\\Chi}'},
+ 120685: {LaTeXMode.textmode: '\\mathsfbf{\\Psi}'},
+ 120686: {LaTeXMode.textmode: '\\mathsfbf{\\Omega}'},
+ 120687: {LaTeXMode.textmode: '\\mathsfbf{\\nabla}'},
+ 120688: {LaTeXMode.textmode: '\\mathsfbf{\\alpha}'},
+ 120689: {LaTeXMode.textmode: '\\mathsfbf{\\beta}'},
+ 120690: {LaTeXMode.textmode: '\\mathsfbf{\\gamma}'},
+ 120691: {LaTeXMode.textmode: '\\mathsfbf{\\delta}'},
+ 120692: {LaTeXMode.textmode: '\\mathsfbf{\\epsilon}'},
+ 120693: {LaTeXMode.textmode: '\\mathsfbf{\\zeta}'},
+ 120694: {LaTeXMode.textmode: '\\mathsfbf{\\eta}'},
+ 120695: {LaTeXMode.textmode: '\\mathsfbf{\\theta}'},
+ 120696: {LaTeXMode.textmode: '\\mathsfbf{\\iota}'},
+ 120697: {LaTeXMode.textmode: '\\mathsfbf{\\kappa}'},
+ 120698: {LaTeXMode.textmode: '\\mathsfbf{\\lambda}'},
+ 120699: {LaTeXMode.textmode: '\\mathsfbf{\\mu}'},
+ 120700: {LaTeXMode.textmode: '\\mathsfbf{\\nu}'},
+ 120701: {LaTeXMode.textmode: '\\mathsfbf{\\xi}'},
+ 120702: {LaTeXMode.textmode: '\\mathsfbf{o}'},
+ 120703: {LaTeXMode.textmode: '\\mathsfbf{\\pi}'},
+ 120704: {LaTeXMode.textmode: '\\mathsfbf{\\rho}'},
+ 120705: {LaTeXMode.textmode: '\\mathsfbf{\\varsigma}'},
+ 120706: {LaTeXMode.textmode: '\\mathsfbf{\\sigma}'},
+ 120707: {LaTeXMode.textmode: '\\mathsfbf{\\tau}'},
+ 120708: {LaTeXMode.textmode: '\\mathsfbf{\\upsilon}'},
+ 120709: {LaTeXMode.textmode: '\\mathsfbf{\\phi}'},
+ 120710: {LaTeXMode.textmode: '\\mathsfbf{\\chi}'},
+ 120711: {LaTeXMode.textmode: '\\mathsfbf{\\psi}'},
+ 120712: {LaTeXMode.textmode: '\\mathsfbf{\\omega}'},
+ 120713: {LaTeXMode.textmode: '\\partial '},
+ 120714: {LaTeXMode.textmode: '\\mathsfbf{\\varepsilon}'},
+ 120715: {LaTeXMode.textmode: '\\mathsfbf{\\vartheta}'},
+ 120716: {LaTeXMode.textmode: '\\mathsfbf{\\varkappa}'},
+ 120717: {LaTeXMode.textmode: '\\mathsfbf{\\phi}'},
+ 120718: {LaTeXMode.textmode: '\\mathsfbf{\\varrho}'},
+ 120719: {LaTeXMode.textmode: '\\mathsfbf{\\varpi}'},
+ 120720: {LaTeXMode.textmode: '\\mathsfbfsl{\\Alpha}'},
+ 120721: {LaTeXMode.textmode: '\\mathsfbfsl{\\Beta}'},
+ 120722: {LaTeXMode.textmode: '\\mathsfbfsl{\\Gamma}'},
+ 120723: {LaTeXMode.textmode: '\\mathsfbfsl{\\Delta}'},
+ 120724: {LaTeXMode.textmode: '\\mathsfbfsl{\\Epsilon}'},
+ 120725: {LaTeXMode.textmode: '\\mathsfbfsl{\\Zeta}'},
+ 120726: {LaTeXMode.textmode: '\\mathsfbfsl{\\Eta}'},
+ 120727: {LaTeXMode.textmode: '\\mathsfbfsl{\\vartheta}'},
+ 120728: {LaTeXMode.textmode: '\\mathsfbfsl{\\Iota}'},
+ 120729: {LaTeXMode.textmode: '\\mathsfbfsl{\\Kappa}'},
+ 120730: {LaTeXMode.textmode: '\\mathsfbfsl{\\Lambda}'},
+ 120731: {LaTeXMode.textmode: '\\mathsfbfsl{M}'},
+ 120732: {LaTeXMode.textmode: '\\mathsfbfsl{N}'},
+ 120733: {LaTeXMode.textmode: '\\mathsfbfsl{\\Xi}'},
+ 120734: {LaTeXMode.textmode: 'O'},
+ 120735: {LaTeXMode.textmode: '\\mathsfbfsl{\\Pi}'},
+ 120736: {LaTeXMode.textmode: '\\mathsfbfsl{\\Rho}'},
+ 120737: {LaTeXMode.textmode: '\\mathsfbfsl{\\vartheta}'},
+ 120738: {LaTeXMode.textmode: '\\mathsfbfsl{\\Sigma}'},
+ 120739: {LaTeXMode.textmode: '\\mathsfbfsl{\\Tau}'},
+ 120740: {LaTeXMode.textmode: '\\mathsfbfsl{\\Upsilon}'},
+ 120741: {LaTeXMode.textmode: '\\mathsfbfsl{\\Phi}'},
+ 120742: {LaTeXMode.textmode: '\\mathsfbfsl{\\Chi}'},
+ 120743: {LaTeXMode.textmode: '\\mathsfbfsl{\\Psi}'},
+ 120744: {LaTeXMode.textmode: '\\mathsfbfsl{\\Omega}'},
+ 120745: {LaTeXMode.textmode: '\\mathsfbfsl{\\nabla}'},
+ 120746: {LaTeXMode.textmode: '\\mathsfbfsl{\\alpha}'},
+ 120747: {LaTeXMode.textmode: '\\mathsfbfsl{\\beta}'},
+ 120748: {LaTeXMode.textmode: '\\mathsfbfsl{\\gamma}'},
+ 120749: {LaTeXMode.textmode: '\\mathsfbfsl{\\delta}'},
+ 120750: {LaTeXMode.textmode: '\\mathsfbfsl{\\epsilon}'},
+ 120751: {LaTeXMode.textmode: '\\mathsfbfsl{\\zeta}'},
+ 120752: {LaTeXMode.textmode: '\\mathsfbfsl{\\eta}'},
+ 120753: {LaTeXMode.textmode: '\\mathsfbfsl{\\vartheta}'},
+ 120754: {LaTeXMode.textmode: '\\mathsfbfsl{\\iota}'},
+ 120755: {LaTeXMode.textmode: '\\mathsfbfsl{\\kappa}'},
+ 120756: {LaTeXMode.textmode: '\\mathsfbfsl{\\lambda}'},
+ 120757: {LaTeXMode.textmode: '\\mathsfbfsl{\\mu}'},
+ 120758: {LaTeXMode.textmode: '\\mathsfbfsl{\\nu}'},
+ 120759: {LaTeXMode.textmode: '\\mathsfbfsl{\\xi}'},
+ 120760: {LaTeXMode.textmode: '\\mathsfbfsl{o}'},
+ 120761: {LaTeXMode.textmode: '\\mathsfbfsl{\\pi}'},
+ 120762: {LaTeXMode.textmode: '\\mathsfbfsl{\\rho}'},
+ 120763: {LaTeXMode.textmode: '\\mathsfbfsl{\\varsigma}'},
+ 120764: {LaTeXMode.textmode: '\\mathsfbfsl{\\sigma}'},
+ 120765: {LaTeXMode.textmode: '\\mathsfbfsl{\\tau}'},
+ 120766: {LaTeXMode.textmode: '\\mathsfbfsl{\\upsilon}'},
+ 120767: {LaTeXMode.textmode: '\\mathsfbfsl{\\phi}'},
+ 120768: {LaTeXMode.textmode: '\\mathsfbfsl{\\chi}'},
+ 120769: {LaTeXMode.textmode: '\\mathsfbfsl{\\psi}'},
+ 120770: {LaTeXMode.textmode: '\\mathsfbfsl{\\omega}'},
+ 120771: {LaTeXMode.textmode: '\\partial '},
+ 120772: {LaTeXMode.textmode: '\\in'},
+ 120773: {LaTeXMode.textmode: '\\mathsfbfsl{\\vartheta}'},
+ 120774: {LaTeXMode.textmode: '\\mathsfbfsl{\\varkappa}'},
+ 120775: {LaTeXMode.textmode: '\\mathsfbfsl{\\phi}'},
+ 120776: {LaTeXMode.textmode: '\\mathsfbfsl{\\varrho}'},
+ 120777: {LaTeXMode.textmode: '\\mathsfbfsl{\\varpi}'},
+ 120782: {LaTeXMode.textmode: '\\mathbf{0}'},
+ 120783: {LaTeXMode.textmode: '\\mathbf{1}'},
+ 120784: {LaTeXMode.textmode: '\\mathbf{2}'},
+ 120785: {LaTeXMode.textmode: '\\mathbf{3}'},
+ 120786: {LaTeXMode.textmode: '\\mathbf{4}'},
+ 120787: {LaTeXMode.textmode: '\\mathbf{5}'},
+ 120788: {LaTeXMode.textmode: '\\mathbf{6}'},
+ 120789: {LaTeXMode.textmode: '\\mathbf{7}'},
+ 120790: {LaTeXMode.textmode: '\\mathbf{8}'},
+ 120791: {LaTeXMode.textmode: '\\mathbf{9}'},
+ 120792: {LaTeXMode.textmode: '\\mathbb{0}'},
+ 120793: {LaTeXMode.textmode: '\\mathbb{1}'},
+ 120794: {LaTeXMode.textmode: '\\mathbb{2}'},
+ 120795: {LaTeXMode.textmode: '\\mathbb{3}'},
+ 120796: {LaTeXMode.textmode: '\\mathbb{4}'},
+ 120797: {LaTeXMode.textmode: '\\mathbb{5}'},
+ 120798: {LaTeXMode.textmode: '\\mathbb{6}'},
+ 120799: {LaTeXMode.textmode: '\\mathbb{7}'},
+ 120800: {LaTeXMode.textmode: '\\mathbb{8}'},
+ 120801: {LaTeXMode.textmode: '\\mathbb{9}'},
+ 120802: {LaTeXMode.textmode: '\\mathsf{0}'},
+ 120803: {LaTeXMode.textmode: '\\mathsf{1}'},
+ 120804: {LaTeXMode.textmode: '\\mathsf{2}'},
+ 120805: {LaTeXMode.textmode: '\\mathsf{3}'},
+ 120806: {LaTeXMode.textmode: '\\mathsf{4}'},
+ 120807: {LaTeXMode.textmode: '\\mathsf{5}'},
+ 120808: {LaTeXMode.textmode: '\\mathsf{6}'},
+ 120809: {LaTeXMode.textmode: '\\mathsf{7}'},
+ 120810: {LaTeXMode.textmode: '\\mathsf{8}'},
+ 120811: {LaTeXMode.textmode: '\\mathsf{9}'},
+ 120812: {LaTeXMode.textmode: '\\mathsfbf{0}'},
+ 120813: {LaTeXMode.textmode: '\\mathsfbf{1}'},
+ 120814: {LaTeXMode.textmode: '\\mathsfbf{2}'},
+ 120815: {LaTeXMode.textmode: '\\mathsfbf{3}'},
+ 120816: {LaTeXMode.textmode: '\\mathsfbf{4}'},
+ 120817: {LaTeXMode.textmode: '\\mathsfbf{5}'},
+ 120818: {LaTeXMode.textmode: '\\mathsfbf{6}'},
+ 120819: {LaTeXMode.textmode: '\\mathsfbf{7}'},
+ 120820: {LaTeXMode.textmode: '\\mathsfbf{8}'},
+ 120821: {LaTeXMode.textmode: '\\mathsfbf{9}'},
+ 120822: {LaTeXMode.textmode: '\\mathtt{0}'},
+ 120823: {LaTeXMode.textmode: '\\mathtt{1}'},
+ 120824: {LaTeXMode.textmode: '\\mathtt{2}'},
+ 120825: {LaTeXMode.textmode: '\\mathtt{3}'},
+ 120826: {LaTeXMode.textmode: '\\mathtt{4}'},
+ 120827: {LaTeXMode.textmode: '\\mathtt{5}'},
+ 120828: {LaTeXMode.textmode: '\\mathtt{6}'},
+ 120829: {LaTeXMode.textmode: '\\mathtt{7}'},
+ 120830: {LaTeXMode.textmode: '\\mathtt{8}'},
+ 120831: {LaTeXMode.textmode: '\\mathtt{9}'},
+ }
+
diff --git a/support/gladtex/manpage.md b/support/gladtex/manpage.md
new file mode 100644
index 0000000000..f4127db1f7
--- /dev/null
+++ b/support/gladtex/manpage.md
@@ -0,0 +1,249 @@
+% GLADTEX(1)
+% Sebastian Humenda
+% 8th of September 2018
+
+# NAME
+
+**GladTeX** - generate HTML with LaTeX formulas embedded as images
+
+# SYNOPSIS
+
+**gladtex** [OPTIONS] <INPUT FILE NAME>
+
+
+# DESCRIPTION
+
+**GladTeX** is a formula preprocessor for HTML files. It recognizes a special tag
+(`<eq>...</eq>`) marking formulas for conversion. The converted vector images
+are integrated into the output HTML document.
+This eases the process of creating HTML
+documents (or web sites) containing formulas.\
+The generated images are saved in a cache to not render the same image over
+and over again. This speeds up the process when formulas occur multiple times or
+when a document is extended gradually.
+
+The LaTeX formulas are preserved in the alt attribute of the embedded images,
+hence screen reader users benefit from an accessible HTML version of the
+document.
+
+Furthermore it can be used with Pandoc to convert Markdown documents and other
+formats with LaTeX formulas to HTML, EPUB and in fact to any HTML-based format,
+see the option `-P`.
+
+See [FILE FORMAT](#file-format) for an explanation of the file format and
+[EXAMPLES](#examples) for examples on how to use GladTeX on its own or with
+Pandoc.
+
+# OPTIONS
+
+**INPUT FILE NAME**
+: Input .htex file with LaTeX formulas (if omitted or -, stdin will be read).
+
+**-h** **--help**
+: Show this help message and exit.
+
+**-a**
+: Save text alternatives for images which are too long for the alt attribute
+ into a single separate file and link images to it.
+
+**-b** _BACKGROUND_COLOR_
+: Set background color for resulting images (default transparent). GladTeX
+ understands colors as provided by the `dvips` option of the xcolor LaTeX
+ package. Alternatively, a 6-digit hexadecimal value can be provided (as used
+ e.g. in HTML/CSS).
+
+**-c** _`FOREGROUND_COLOR`_
+: Set foreground color for resulting images. See the option above for a more
+in-depth explanation.
+
+**-d** _DIRECTORY_
+: Directory in which to store the generated images in (relative path).\
+ The given path is interpreted relatively to the input file. For instance,:
+
+ gladtex -d img dir/file.htex
+
+ will create a `dir/img` directory and link accordingly in `x/file.htex`.
+
+**-e** _`LATEX_MATHS_ENV`_
+: Set custom maths environment to surround the formula (e.g. flalign).
+
+**-E** _ENCODING_
+: Overwrite encoding to use (default UTF-8).
+
+**-f** _FONTSIZE_
+: Overwrite the default font size of 12pt. 12pt is the default in most
+ browsers and hence changing this might lead to less-portable documents.
+
+**-i** _CLASS_
+: CSS class to assign to inline math (default: 'inlinemath').
+
+**-K**
+: keep LaTeX file(s) when converting formulas
+
+ By default, the generated LaTeX document, containing the formula to be
+ converted, are removed after the conversion (no matter whether it was
+ successful or not). If it wasn't successful, it is sometimes helpful to look
+ at the complete document. This option will keep the file.
+
+**-l** _CLASS_
+: CSS class to assign to block-level math (default: 'displaymath').
+
+**-n**
+: Purge unreadable caches along with all eqn*.png files.
+
+ Caches can be unreadable if the used GladTeX version is incompatible. If
+ this option is unset, GladTeX will simply fail when the cache is unreadable.
+
+**-m**
+: Print error output in machine-readable format (less concise, better parseable).
+
+ Each line will start with a key, followed by a colon, followed by the value,
+ i.e. `line: 5`.
+
+**-o** _FILENAME_
+: Set output file name. '-' will print text to stdout. Bydefault, input file
+ name is used and the `.htex` extension is replaced by `.html`.
+
+**-p** _`LATEX_STATEMENT`_
+: Add given LaTeX code to preamble of document. That'll affect the conversion
+ of every image.
+
+**-P**
+: Act as a pandoc filter. In this mode, input is expected to be a Pandoc JSON
+ AST and the output will be a modified AST, with all formulas replaced
+ through HTML image tags. It makes sense to use `-` as the input file for
+ this option.
+
+**--png**
+: Switch from SVG to PNG as image output. This image has several known issues,
+ one of them being that images won't resize when zooming into the document.
+ It is also harder to work with for visually impaired users.
+
+**-r** _DPI_
+: Set resolution (size of images) to 'dpi' (115 by default). This is only
+ available with the `--png` option. Also see the `-f` option.
+
+**-R**
+: Replace non-ascii (unicode) characters by LaTeX commands.
+
+ GladTeX can automatically detect non-ascii characters in formulas and
+ replace them through their appropriate LaTeX commands. In the alt attribute
+ of the resulting image, alphabetical characters won't be replaced. That
+ means that the alt text from the image is not exactly the same than the
+ code used for generating the image, but it is far more readable.
+
+ For instance, the formula \$\\text{für alle} a\$, would be compiled as
+ \$\\text{f\\ddot{u}r alle} a\$ and displayed as "\\text{für alle} a" in the alt
+ attribute.
+
+
+**-u** _URL_
+: Base URL to image files (relative links are default).
+
+# FILE FORMAT
+
+A .htex file is essentially a HTML file containing LaTeX formulas. The formulas
+have to be surrounded by `<eq>` and `</eq>`.
+
+By default, formulas are rendered as inline maths, so they are squeezed to the
+height of the line. It is possible to render a formula as display maths by
+setting the env attribute to displaymath, i.e. `<eq env="displaymath">...</eq>`.
+
+# ENVIRONMENT VARIABLES
+
+GladTeX can be customised by environment variables:
+
+`DEBUG`
+: If this is set to 1, a full Python traceback, instead of a human-readable
+ error message, will be displayed.
+`GLADTEX_ARGS`:
+: When this environment variable is set, GladTeX switches into
+ **pandoc filter** mode: input is read from standard input, output written to
+ standard output and the `-P` switch is assumed. The contents of this
+ variable parsed as command-line switches.
+ See an example in [Output As EPUB]#output-asepub).
+
+# EXAMPLES
+
+## Sample HTEX document
+
+A sample HTEX document could look like this:
+
+~~~~
+<html><head><!-- meta information like charset --></head>
+<body>
+<h1>Some text</h1>
+<p>Circumference of a circle: <eq>u = \pi\cdot d</eq><p>
+<p>A useful matrix: <eq env="displaymath">\begin{pmatrix}
+1 &2 &3 &4\\
+5 &6 &7 &8\\
+9 &10&11&12
+\end{pmatrix}</eq></p>
+</body></html>
+~~~~
+
+This can be converted using
+
+ gladtex file.htex
+
+and the result will be a HTML document called `file.html` along with two files
+`eqn0000.png` and `eqn0001.png` in the same directory.
+
+## Markdown To HTML
+
+GladTeX can be used together with Pandoc. That can be handy to create an online
+version of a scientific paper written in Markdown. The MarkDown document would
+look like this:
+
+~~~~
+Some text
+=========
+
+Circumference of a circle: $u = \pi\cdot d$
+
+A useful matrix: $$\begin{pmatrix}
+1 &2 &3 &4\\
+5 &6 &7 &8\\
+9 &10&11&12 \end{pmatrix}$$
+~~~~
+
+The conversion is as easy as typing on the command-line:
+
+ pandoc -s -t html --gladtex file.md | gladtex -o file.html
+
+## Output as EPUB
+
+It is beyond of the scope of this document to introduce Pandoc, but with any
+input format, converting to EPUB with GladTeX replacing the images is as easy
+as:
+
+ pandoc -t json FILE.ext | gladtex -d img -P - | pandoc -f json -o book.epub
+
+Capitalised parameters should be replaced. This can be used with Markdown as
+input format, see previous section.
+
+If you want to call Pandoc as a filter without the pipes, you can use the
+environment variable `GLADTEX_ARGS`:
+
+ GLADTEX_ARGS='-d img' pandoc -o BOOK.EPUB -F gladtex FILE.ext
+
+
+# KNOWN LIMITATIONS
+
+LaTeX2E is ***not*** unicode aware. if you have any unicode (more precisely,
+non-ascii characters) signs in your documents, you have the choice to do one of
+the following:
+
+1. Look up the symbol in one of the many LaTeX formula listings and replace the
+ symbol with the appropriate command.
+2. Use the `-r` switch to let GladTeX replace the umlauts for you.
+
+PLEASE NOTE: It is impossible to use GladTeX with LuaLaTeX. At the time of writing, dvipng
+does not support the extended font features of the lualatex engine.
+
+
+# PROJECT HOME
+
+The project home is at <http://humenda.github.io/GladTeX>. The source can be
+found at <https://github.com/humenda/gladtex>.
+
diff --git a/support/gladtex/runtests b/support/gladtex/runtests
new file mode 100755
index 0000000000..521d5b3a33
--- /dev/null
+++ b/support/gladtex/runtests
@@ -0,0 +1,11 @@
+#!/bin/sh
+# change above to python, if your system doesn't have the 3 suffix for python 3
+set -e
+PYTHON=python
+# check whether a program called "python3" exists, else use "python"
+if command -v python3 >/dev/null 2>&1
+then
+ PYTHON=python3
+fi
+
+$PYTHON -m unittest discover tests
diff --git a/support/gladtex/setup.py b/support/gladtex/setup.py
new file mode 100644
index 0000000000..9f17c165ec
--- /dev/null
+++ b/support/gladtex/setup.py
@@ -0,0 +1,99 @@
+import distutils.command.install_scripts, distutils.command.build, distutils.command.clean
+from distutils.core import setup
+import os
+import shutil
+import sys
+#pylint: disable=unused-import
+try:
+ import py2exe # only works on windows
+except ImportError:
+ pass
+from gleetex import VERSION
+
+class ScriptInstaller(distutils.command.install_scripts.install_scripts):
+ """Custom script installer. Stript .py extension if not on Windows."""
+ def run(self):
+ distutils.command.install_scripts.install_scripts.run(self)
+ for script in self.get_outputs():
+ if script.endswith(".py") and not ('wind' in sys.platform or 'win32'
+ in sys.platform):
+ # strip file ending (if not on windows) to make it executable as
+ # a command
+ shutil.move(script, script[:-3])
+
+#pylint: disable=attribute-defined-outside-init
+class CleanCommand(distutils.command.clean.clean):
+ description = "clean all build files, including __pycache__ and others"
+ user_options = []
+ def initialize_options(self):
+ self.cwd = None
+
+ def finalize_options(self):
+ self.cwd = os.getcwd()
+
+ def run(self):
+ assert os.getcwd() == self.cwd, 'Must be in package root: %s' % self.cwd
+ for directory in ['build', '__pycache__', 'dist',
+ os.path.join('gleetex', '__pycache')]:
+ if os.path.exists(directory):
+ shutil.rmtree(directory)
+ if os.path.exists('gladtex.1'):
+ os.remove('gladtex.1')
+
+class CustomBuild(distutils.command.build.build):
+ """Also build manpage to build/gladtex.1; it is not installed
+ automatically."""
+ def initialize_options(self):
+ self.cwd = None
+ super().initialize_options()
+
+ def finalize_options(self):
+ self.cwd = os.getcwd()
+ super().finalize_options()
+
+ def run(self):
+ if not self.cwd:
+ if not 'manpage.md' in os.listdir('.'):
+ print("setup.py must be run from the source root")
+ sys.exit(91)
+ else:
+ assert os.getcwd() == self.cwd, 'Must be in package root: %s' % self.cwd
+ super().run()
+ if shutil.which('pandoc'): # only build man page, if pandoc present
+ self.build_manpage('manpage.md', os.path.join('build', 'gladtex.1'))
+ else:
+ print("w: pandoc not found, skipping man page conversion.")
+
+ def build_manpage(self, input_fn, output_fn):
+ """Convert `input_fn` from markdown into manpage format and save it to
+ `output_fn`. Pandoc is required."""
+ import subprocess
+ try:
+ cmd = ['pandoc', input_fn, '-s', '-t', 'man', '-o', output_fn]
+ proc = subprocess.Popen(cmd)
+ ret = proc.wait()
+ if ret:
+ raise subprocess.SubprocessError("Exit status %d when running '%s'" % (ret,
+ ' '.join(cmd)))
+ except FileNotFoundError:
+ sys.stderr.write("Pandoc was not found on the system, skipping man " +
+ "page creation.")
+
+
+setup(name='GladTeX',
+ version=VERSION,
+ description='generate html with LaTeX equations embedded as images',
+ author='Sebastian Humenda',
+ author_email='shumenda |at| gmx |dot| de',
+ url='https://humenda.github.io/GladTeX',
+ packages=['gleetex'],
+ console=['gladtex.py'], # Windows-only option, use python instead of pythonw
+ setup_requires=['py2app'], # require py2app on Mac OS
+ scripts=['gladtex.py'],
+ license = "LGPL3.0",
+ cmdclass = {"install_scripts": ScriptInstaller,
+ 'clean': CleanCommand,
+ 'build': CustomBuild}
+ )
+
+
diff --git a/support/gladtex/tests/test_cachedconverter.py b/support/gladtex/tests/test_cachedconverter.py
new file mode 100644
index 0000000000..b1c3bb786c
--- /dev/null
+++ b/support/gladtex/tests/test_cachedconverter.py
@@ -0,0 +1,143 @@
+#pylint: disable=too-many-public-methods,import-error,too-few-public-methods,missing-docstring,unused-variable
+import os
+import shutil
+import tempfile
+import unittest
+from unittest.mock import patch
+from gleetex import cachedconverter, image
+from gleetex.caching import JsonParserException
+from gleetex.image import remove_all
+
+def get_number_of_files(path):
+ return len(os.listdir(path))
+
+def mk_eqn(eqn, count=0, pos=(1,1)):
+ """Create formula. Each formula must look like this:
+ (eqn, pos, path, dsp, count) for self._convert_concurrently, this is a
+ shorthand with mocking a few values."""
+ return (pos, False, eqn)
+
+def write(path, content='dummy'):
+ with open(path, 'w', encoding='utf-8') as f:
+ f.write(str(content))
+
+class Tex2imgMock():
+ """Could use a proper mock, but this one allows a bit more tricking."""
+ def __init__(self, fmt):
+ self.__format = fmt
+ self.set_dpi = self.set_transparency = self.set_foreground_color \
+ = self.set_background_color = lambda x: None # do nothing
+
+ def create_dvi(self, dvi_fn):
+ with open(dvi_fn, 'w') as f:
+ f.write('dummy')
+
+ def create_image(self, dvi_fn):
+ if os.path.exists(dvi_fn):
+ os.remove(dvi_fn)
+ write(os.path.splitext(dvi_fn)[0] + '.' + self.__format.value)
+
+ def convert(self, tx, basename):
+ write(basename + '.tex', tx)
+ dvi = basename + '.dvi'
+ self.create_dvi(dvi)
+ self.create_image(dvi)
+ remove_all(dvi, basename + '.tex', basename + '.log', basename + '.aux')
+ return {'depth': 9, 'height': 8, 'width': 7}
+
+ def parse_log(self, _logdata):
+ return {}
+
+
+class TestCachedConverter(unittest.TestCase):
+ #pylint: disable=protected-access
+ def setUp(self):
+ self.original_directory = os.getcwd()
+ self.tmpdir = tempfile.mkdtemp()
+ os.chdir(self.tmpdir)
+
+
+ #pylint: disable=protected-access
+ def tearDown(self):
+ # restore static reference to converter
+ cachedconverter.CachedConverter._converter = image.Tex2img
+ os.chdir(self.original_directory)
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
+
+
+
+ @patch('gleetex.image.Tex2img', Tex2imgMock)
+ def test_that_subdirectory_is_created(self):
+ c = cachedconverter.CachedConverter('subdirectory')
+ formula = ({}, True, '\\textbf{FOO!}')
+ c.convert_all([formula])
+ # one directory exists
+ self.assertEqual(get_number_of_files('.'), 1,
+ "Found the following files, expected only 'subdirectory': " + \
+ ', '.join(os.listdir('.')))
+ # subdirectory contains 1 image and a cache
+ self.assertEqual(get_number_of_files('subdirectory'), 2, "expected two"+\
+ " files, found instead " + repr(os.listdir('subdirectory')))
+
+ def test_that_unknown_options_trigger_exception(self):
+ c = cachedconverter.CachedConverter('subdirectory')
+ self.assertRaises(ValueError, c.set_option, 'cxzbiucxzbiuxzb', 'muh')
+
+ def test_that_invalid_caches_trigger_error_by_default(self):
+ with open('gladtex.cache', 'w') as f:
+ f.write('invalid cache')
+ with self.assertRaises(JsonParserException):
+ c = cachedconverter.CachedConverter('')
+
+ @patch('gleetex.image.Tex2img', Tex2imgMock)
+ def test_that_invalid_caches_get_removed_if_specified(self):
+ formulas = [mk_eqn('tau')]
+ with open('gladtex.cache', 'w') as f:
+ f.write('invalid cache')
+ c = cachedconverter.CachedConverter('.', keep_old_cache=False)
+ c.convert_all(formulas)
+ # cache got overridden
+ with open('gladtex.cache') as f:
+ self.assertFalse('invalid' in f.read())
+
+ @patch('gleetex.image.Tex2img', Tex2imgMock)
+ def test_that_converted_formulas_are_cached(self):
+ formulas = [mk_eqn('\\tau')]
+ c = cachedconverter.CachedConverter('.')
+ c.convert_all(formulas)
+ self.assertTrue(c.get_data_for('\\tau', False))
+
+
+ @patch('gleetex.image.Tex2img', Tex2imgMock)
+ def test_that_file_names_are_correctly_picked(self):
+ formulas = [mk_eqn('\\tau')]
+ write('eqn000.svg')
+ write('eqn001.svg')
+ c = cachedconverter.CachedConverter('')
+ to_convert = c._get_formulas_to_convert(formulas)
+ self.assertTrue(len(to_convert), 1)
+ self.assertEqual(to_convert[0][2], 'eqn002.svg')
+
+ @patch('gleetex.image.Tex2img', Tex2imgMock)
+ def test_that_all_converted_formulas_are_in_cache_and_meta_info_correct(self):
+ formulas = [mk_eqn('a_{%d}' % i, pos=(i,i), count=i) for i in range(4)]
+ c = cachedconverter.CachedConverter('.')
+ c.convert_all(formulas)
+ # expect all formulas and a gladtex cache to exist
+ self.assertEqual(get_number_of_files('.'), len(formulas)+1,
+ "present files:\n" + ', '.join(os.listdir('.')))
+ for pos, dsp, formula in formulas:
+ data = c.get_data_for(formula, False)
+ self.assertEqual(data['pos'], {'depth': 9, 'height': 8, 'width': 7},
+ "expected the pos as defined in the dummy class")
+
+ @patch('gleetex.image.Tex2img', Tex2imgMock)
+ def test_that_inline_math_and_display_math_results_in_different_formulas(self):
+ # two formulas, second is displaymath
+ formula = r'\sum_{i=0}^n x_i'
+ formulas = [((1,1), False, formula), ((3,1), True, formula)]
+ c = cachedconverter.CachedConverter('.')
+ c.convert_all(formulas)
+ # expect all formulas and a gladtex cache to exist
+ self.assertEqual(get_number_of_files('.'), len(formulas)+1,
+ "present files:\n%s" % ', '.join(os.listdir('.')))
diff --git a/support/gladtex/tests/test_caching.py b/support/gladtex/tests/test_caching.py
new file mode 100644
index 0000000000..dd3aebac77
--- /dev/null
+++ b/support/gladtex/tests/test_caching.py
@@ -0,0 +1,149 @@
+#pylint: disable=too-many-public-methods,import-error,too-few-public-methods,missing-docstring,unused-variable
+import os
+import shutil
+import tempfile
+import unittest
+from gleetex import caching
+
+def write(path, content):
+ with open(path, 'w', encoding='utf-8') as f:
+ f.write(content)
+
+class test_caching(unittest.TestCase):
+ def setUp(self):
+ self.pos = {'height' : 8, 'depth' : 2, 'width' : 666}
+ self.original_directory = os.getcwd()
+ self.tmpdir = tempfile.mkdtemp()
+ os.chdir(self.tmpdir)
+
+ def tearDown(self):
+ os.chdir(self.original_directory)
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
+
+ def test_differently_spaced_formulas_are_the_same(self):
+ form1 = r'\tau \pi'
+ form2 = '\tau\\pi'
+ self.assertTrue(caching.normalize_formula(form1),
+ caching.normalize_formula(form2))
+
+ def test_trailing_and_leading_spaces_and_tabs_are_no_problem(self):
+ u = caching.normalize_formula
+ form1 = ' hi'
+ form2 = 'hi '
+ form3 = '\thi'
+ self.assertEqual(u(form1), u(form2))
+ self.assertEqual(u(form1), u(form3))
+
+ def test_that_empty_braces_are_ignored(self):
+ u = caching.normalize_formula
+ form1 = r'\sin{}x'
+ form2 = r'\sin x'
+ form3 = r'\sin{} x'
+ self.assertEqual(u(form1), u(form2))
+ self.assertEqual(u(form1), u(form3))
+ self.assertEqual(u(form2), u(form3))
+
+ def test_empty_cache_works_fine(self):
+ write('foo.png', 'muha')
+ c = caching.ImageCache('file.png')
+ formula = r"f(x) = \ln(x)"
+ c.add_formula(formula, self.pos, 'foo.png')
+ self.assertTrue(c.contains(formula, False))
+
+ def test_that_invalid_cach_entries_are_detected(self):
+ # entry is invalid if file doesn't exist
+ c = caching.ImageCache()
+ formula = r"f(x) = \ln(x)"
+ self.assertRaises(OSError, c.add_formula, formula, self.pos, 'file.png')
+
+ def test_that_correct_pos_and_path_are_returned_after_writing_the_cache_back(self):
+ c = caching.ImageCache()
+ formula = r"g(x) = \ln(x)"
+ write('file.png', 'dummy')
+ c.add_formula(formula, self.pos, 'file.png', displaymath=False)
+ c.write()
+ c = caching.ImageCache()
+ self.assertTrue(c.contains(formula, False))
+ data = c.get_data_for(formula, False)
+ self.assertEqual(data['pos'], self.pos)
+ self.assertEqual(data['path'], 'file.png')
+
+
+ def test_formulas_are_not_added_twice(self):
+ form1 = r'\ln(x) \neq e^x'
+ write('spass.png', 'binaryBinary_binary')
+ c = caching.ImageCache()
+ for i in range(1,10):
+ c.add_formula(form1, self.pos, 'spass.png')
+ self.assertEqual(len(c), 1)
+
+ def test_that_remove_actually_removes(self):
+ form1 = '\\int e^x dy'
+ write('happyness.png', 'binaryBinary_binary')
+ c = caching.ImageCache()
+ c.add_formula(form1, self.pos, 'happyness.png')
+ c.remove_formula(form1, False)
+ self.assertEqual(len(c), 0)
+
+ def test_removal_of_non_existing_formula_raises_exception(self):
+ c = caching.ImageCache()
+ self.assertRaises(KeyError, c.remove_formula, 'Haha!', False)
+
+ def test_that_invalid_version_is_detected(self):
+ c = caching.ImageCache('gladtex.cache')
+ c._ImageCache__set_version('invalid.stuff')
+ c.write()
+ self.assertRaises(caching.JsonParserException, caching.ImageCache, 'gladtex.cache')
+
+ def test_that_invalid_style_is_detected(self):
+ write('foo.png', "dummy")
+ c = caching.ImageCache('gladtex.cache')
+ c.add_formula('\\tau', self.pos, 'foo.png', False)
+ c.add_formula('\\theta', self.pos, 'foo.png', True)
+ self.assertRaises(ValueError, c.add_formula, '\\gamma', self.pos, 'foo.png',
+ 'some stuff')
+
+ def test_that_backslash_in_path_is_replaced_through_slash(self):
+ c = caching.ImageCache('gladtex.cache')
+ os.mkdir('bilder')
+ write(os.path.join('bilder', 'foo.png'), str(0xdeadbeef))
+ c.add_formula('\\tau', self.pos, 'bilder\\foo.png', False)
+ self.assertTrue('/' in c.get_data_for('\\tau', False)['path'])
+
+ def test_that_absolute_paths_trigger_OSError(self):
+ c = caching.ImageCache('gladtex.cache')
+ write('foo.png', "dummy")
+ fn = os.path.abspath('foo.png')
+ self.assertRaises(OSError, c.add_formula, '\\tau', self.pos,
+ fn, False)
+
+ def test_that_invalid_caches_are_removed_automatically_if_desired(self):
+ file_was_removed = lambda x: self.assertFalse(os.path.exists(x),
+ "expected that file %s was removed, but it still exists" % x)
+ write('gladtex.cache', 'some non-json rubbish')
+ c = caching.ImageCache('gladtex.cache', keep_old_cache=False)
+ file_was_removed('gladtex.cache')
+ # try the same in a subdirectory
+ os.mkdir('foo')
+ cache_path = os.path.join('foo', 'gladtex.cache')
+ eqn1_path = os.path.join('foo', 'eqn000.png')
+ eqn2_path = os.path.join('foo', 'eqn003.png')
+ write(cache_path, 'some non-json rubbish')
+ write(eqn1_path, 'binary')
+ write(eqn2_path, 'more binary')
+ c = caching.ImageCache(cache_path, keep_old_cache=False)
+ file_was_removed(cache_path)
+ file_was_removed(eqn1_path)
+ file_was_removed(eqn2_path)
+
+ def test_that_formulas_in_cache_with_no_file_raise_key_error(self):
+ c = caching.ImageCache('gladtex.cache', keep_old_cache=False)
+ write('foo.png', 'dummy')
+ c.add_formula('\\tau', self.pos, 'foo.png')
+ c.write()
+ os.remove('foo.png')
+ c = caching.ImageCache('gladtex.cache', keep_old_cache=False)
+ with self.assertRaises(KeyError):
+ c.get_data_for('foo.png', 'False')
+
+
diff --git a/support/gladtex/tests/test_htmlhandling.py b/support/gladtex/tests/test_htmlhandling.py
new file mode 100644
index 0000000000..359ef36965
--- /dev/null
+++ b/support/gladtex/tests/test_htmlhandling.py
@@ -0,0 +1,404 @@
+#pylint: disable=too-many-public-methods
+from functools import reduce
+import os, re, shutil, tempfile
+import unittest
+from gleetex import htmlhandling
+
+
+excl_filename = htmlhandling.HtmlImageFormatter.EXCLUSION_FILE_NAME
+
+HTML_SKELETON = '''<html><head><meta http-equiv="Content-Type" content="text/html; charset={0}" />
+</head><body>{1}</body>'''
+
+def read(file_name, mode='r', encoding='utf-8'):
+ """Read the file, return the string. Close file properly."""
+ with open(file_name, mode, encoding=encoding) as handle:
+ return handle.read()
+
+
+class HtmlparserTest(unittest.TestCase):
+ def setUp(self):
+ self.p = htmlhandling.EqnParser()
+
+ def test_start_tags_are_parsed_literally(self):
+ self.p.feed("<p i='o'>")
+ self.assertEqual(self.p.get_data()[0], "<p i='o'>",
+ "The HTML parser should copy start tags literally.")
+
+ def test_that_end_tags_are_copied_literally(self):
+ self.p.feed("</ p></P>")
+ self.assertEqual(''.join(self.p.get_data()), "</ p></P>")
+
+ def test_entities_are_unchanged(self):
+ self.p.feed("&#xa;")
+ self.assertEqual(self.p.get_data()[0], '&#xa;')
+
+ def test_charsets_are_copied(self):
+ self.p.feed('&gt;&rarr;')
+ self.assertEqual(''.join(self.p.get_data()[0]), '&gt;&rarr;')
+
+ def test_without_eqn_all_blocks_are_strings(self):
+ self.p.feed("<html>\n<head/><body><p>42</p><h1>blah</h1></body></html>")
+ self.assertTrue(reduce(lambda x,y: x and isinstance(y, str),
+ self.p.get_data()), "all chunks have to be strings")
+
+ def test_equation_is_detected(self):
+ self.p.feed('<eq>foo \\pi</eq>')
+ self.assertTrue(isinstance(self.p.get_data()[0], (tuple, list)))
+ self.assertEqual(self.p.get_data()[0][2], 'foo \\pi')
+
+ def test_tag_followed_by_eqn_is_correctly_recognized(self):
+ self.p.feed('<p foo="bar"><eq>bar</eq>')
+ self.assertEqual(self.p.get_data()[0], '<p foo="bar">')
+ self.assertTrue(isinstance(self.p.get_data(), list), "second item of data must be equation data list")
+
+ def test_document_with_tag_then_eqn_then_tag_works(self):
+ self.p.feed('<div style="invalid">bar</div><eq>baz</eq><sometag>')
+ eqn = None
+ # test should not depend on a specific position of equation, search for
+ # it
+ data = self.p.get_data()
+ for chunk in data:
+ if isinstance(chunk, (tuple, list)):
+ eqn = chunk
+ break
+ self.assertTrue(isinstance(data[0], str))
+ self.assertTrue(eqn is not None,
+ "No equation found, must be tuple/list object.")
+ self.assertTrue(isinstance(data[-1], str))
+
+ def test_equation_is_copied_literally(self):
+ self.p.feed('<eq ignore="me">my\nlittle\n\\tau</eq>')
+ self.assertEqual(self.p.get_data()[0][2], 'my\nlittle\n\\tau')
+
+ def test_unclosed_eqns_are_detected(self):
+ self.assertRaises(htmlhandling.ParseException, self.p.feed,
+ '<p><eq>\\endless\\formula')
+
+ def test_nested_formulas_trigger_exception(self):
+ self.assertRaises(htmlhandling.ParseException, self.p.feed,
+ "<eq>\\pi<eq></eq></eq>")
+ self.assertRaises(htmlhandling.ParseException, self.p.feed,
+ "<eq>\\pi<eq></p></eq>")
+
+ def test_formulas_without_displaymath_attribute_are_detected(self):
+ self.p.feed('<p><eq>\frac12</eq><br /><eq env="inline">bar</eq></p>')
+ formulas = [c for c in self.p.get_data() if isinstance(c, (tuple, list))]
+ self.assertEqual(len(formulas), 2) # there should be _2_ formulas
+ self.assertEqual(formulas[0][1], False) # no displaymath
+ self.assertEqual(formulas[1][1], False) # no displaymath
+
+ def test_that_unclosed_formulas_detected(self):
+ self.assertRaises(htmlhandling.ParseException, self.p.feed,
+ "<eq>\\pi<eq></p>")
+ self.assertRaises(htmlhandling.ParseException, self.p.feed,
+ "<eq>\\pi")
+
+
+ def test_formula_contains_only_formula(self):
+ p = htmlhandling.EqnParser()
+ p.feed("<p><eq>1<i<9</eq></p>")
+ formula = next(e for e in p.get_data() if isinstance(e, (list, tuple)))
+ self.assertEqual(formula[-1], "1<i<9")
+
+ p = htmlhandling.EqnParser()
+ p.feed('<p><eq env="displaymath">test</eq></p>')
+ formula = next(e for e in p.get_data() if isinstance(e, (list, tuple)))
+ self.assertEqual(formula[-1], "test")
+
+ p = htmlhandling.EqnParser()
+ p.feed("<p><eq>1<i<9</eq></p>")
+ formula = next(e for e in p.get_data() if isinstance(e, (list, tuple)))
+ self.assertEqual(formula[-1], "1<i<9")
+
+
+ def test_formula_with_html_sequences_are_unescaped(self):
+ self.p.feed('<eq>a&gt;b</eq>')
+ formula = self.p.get_data()[0]
+ self.assertEqual(formula[-1], "a>b")
+
+
+ def test_displaymath_is_recognized(self):
+ self.p.feed('<eq env="displaymath">\\sum\limits_{n=1}^{e^i} a^nl^n</eq>')
+ self.assertEqual(self.p.get_data()[0][1], True) # displaymath flag set
+
+ def test_encoding_is_parsed_from_HTML4(self):
+ iso8859_1 = HTML_SKELETON.format('iso-8859-15', 'öäüß').encode('iso-8859-1')
+ self.p.feed(iso8859_1)
+ self.assertEqual(self.p._EqnParser__encoding, 'iso-8859-15')
+
+ def test_encoding_is_parsed_from_HTML5(self):
+ document = r"""<!DOCTYPE html>
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
+ <head><meta charset="utf-8" />
+ <meta name="generator" content="pandoc" />
+ </head><body><p>hi</p></body></html>"""
+ self.p.feed(document.encode('utf-8'))
+ self.assertEqual(self.p._EqnParser__encoding.lower(), 'utf-8')
+
+
+ def test_strings_can_be_passed_tO_parser_as_well(self):
+ # no exception - everything is working as expected
+ self.p.feed(HTML_SKELETON.format('utf-8', 'æø'))
+
+class GetPositionTest(unittest.TestCase):
+ def test_that_line_number_is_correct(self):
+ self.assertEqual(htmlhandling.get_position('jojo', 0)[0], 0)
+ self.assertEqual(htmlhandling.get_position('jojo', 3)[0], 0)
+ self.assertEqual(htmlhandling.get_position('a\njojo', 3)[0], 1)
+ self.assertEqual(htmlhandling.get_position('a\n\njojo', 3)[0], 2)
+
+ def test_that_position_on_line_is_correct(self):
+ self.assertEqual(htmlhandling.get_position('jojo', 0)[1], 0)
+ self.assertEqual(htmlhandling.get_position('jojo', 3)[1], 3)
+ self.assertEqual(htmlhandling.get_position('a\njojo', 3)[1], 2)
+ self.assertEqual(htmlhandling.get_position('a\n\njojo', 3)[1], 1)
+
+
+
+
+class HtmlImageTest(unittest.TestCase):
+ def setUp(self):
+ self.pos = {'depth':99, 'height' : 88, 'width' : 77}
+ self.original_directory = os.getcwd()
+ self.tmpdir = tempfile.mkdtemp()
+ os.chdir(self.tmpdir)
+
+ def tearDown(self):
+ os.chdir(self.original_directory)
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
+
+ def test_that_no_file_is_written_if_no_content(self):
+ with htmlhandling.HtmlImageFormatter('foo.html'):
+ pass
+ self.assertFalse(os.path.exists('foo.html') )
+
+ def test_file_if_written_when_content_exists(self):
+ with htmlhandling.HtmlImageFormatter() as img:
+ img.format_excluded(self.pos, '\\tau\\tau', 'foo.png')
+ self.assertTrue(os.path.exists(excl_filename) )
+
+ def test_written_file_starts_and_ends_more_or_less_properly(self):
+ with htmlhandling.HtmlImageFormatter('.') as img:
+ img.format_excluded(self.pos, '\\tau\\tau', 'foo.png')
+ data = read(htmlhandling.HtmlImageFormatter.EXCLUSION_FILE_NAME, 'r', encoding='utf-8')
+ self.assertTrue('<html' in data and '</html>' in data)
+ self.assertTrue('<body' in data and '</body>' in data)
+ # make sure encoding is specified
+ self.assertTrue('<meta' in data and 'charset=' in data)
+
+ def test_id_contains_no_special_characters(self):
+ data = htmlhandling.gen_id('\\tau!\'{}][~^')
+ for character in {'!', "'", '\\', '{', '}'}:
+ self.assertFalse(character in data)
+
+ def test_formula_can_consist_only_of_numbers_and_id_is_generated(self):
+ data = htmlhandling.gen_id('9*8*7=504')
+ self.assertTrue(data.startswith('form'))
+ self.assertTrue(data.endswith('504'))
+
+ def test_that_empty_ids_raise_exception(self):
+ self.assertRaises(ValueError, htmlhandling.gen_id, '')
+
+ def test_that_same_characters_are_not_repeated(self):
+ id = htmlhandling.gen_id("jo{{{{{{{{ha")
+ self.assertEqual(id, "jo_ha")
+
+ def test_that_ids_are_max_150_characters_wide(self):
+ id = htmlhandling.gen_id('\\alpha\\cdot\\gamma + ' * 999)
+ self.assertTrue(len(id) == 150)
+
+ def test_that_ids_start_with_letter(self):
+ id = htmlhandling.gen_id('{}\\[]ÖÖÖö9343...·tau')
+ self.assertTrue(id[0].isalpha())
+
+ def test_that_link_to_external_image_points_to_file_and_formula(self):
+ with htmlhandling.HtmlImageFormatter() as img:
+ formatted_img = img.format_excluded(self.pos, '\\tau\\tau', 'foo.png')
+ expected_id = htmlhandling.gen_id('\\tau\\tau')
+ external_file = read(excl_filename, 'r', encoding='utf-8')
+ # find linked formula path
+ href = re.search('href="(.*?)"', formatted_img)
+ self.assertTrue(href != None)
+ # extract path and id from it
+ self.assertTrue('#' in href.groups()[0])
+ path, id = href.groups()[0].split('#')
+ self.assertEqual(path, excl_filename)
+ self.assertEqual(id, expected_id)
+
+ # check external file
+ self.assertTrue('<p id' in external_file)
+ self.assertTrue('="'+expected_id in external_file)
+
+
+ def test_that_link_to_external_image_points_to_file_basepath_and_formula(self):
+ os.mkdir('basepath')
+ with htmlhandling.HtmlImageFormatter('basepath') as img:
+ formatted_img = img.format_excluded(self.pos, '\\tau\\tau', 'foo.png')
+ expected_id = htmlhandling.gen_id('\\tau\\tau')
+ # find linked formula path
+ href = re.search('href="(.*?)"', formatted_img)
+ self.assertTrue(href != None)
+ # extract path and id from it
+ self.assertTrue('#' in href.groups()[0])
+ path, id = href.groups()[0].split('#')
+ self.assertEqual(path, 'basepath/' + excl_filename)
+ self.assertEqual(id, expected_id)
+
+ def test_height_and_width_is_in_formatted_html_img_tag(self):
+ data = None
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ data = img.get_html_img(self.pos, '\\tau\\tau', 'foo.png')
+ self.assertTrue('height=' in data and str(self.pos['height']) in data)
+ self.assertTrue('width=' in data and str(self.pos['width']) in data)
+
+ def test_no_formula_gets_lost_when_reparsing_external_formula_file(self):
+ with htmlhandling.HtmlImageFormatter() as img:
+ img.format_excluded(self.pos, '\\tau' * 999, 'foo.png')
+ with htmlhandling.HtmlImageFormatter() as img:
+ img.format_excluded(self.pos, '\\pi' * 666, 'foo_2.png')
+ data = read(excl_filename)
+ self.assertTrue('\\tau' in data)
+ self.assertTrue('\\pi' in data)
+
+ def test_too_long_formulas_are_not_outsourced_if_not_configured(self):
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ img.format(self.pos, '\\tau' * 999, 'foo.png')
+ self.assertFalse(os.path.exists('foo.html'))
+
+ def test_that_too_long_formulas_get_outsourced_if_configured(self):
+ with htmlhandling.HtmlImageFormatter() as img:
+ img.set_max_formula_length(90)
+ img.set_exclude_long_formulas(True)
+ img.format(self.pos, '\\tau' * 999, 'foo.png')
+ self.assertTrue(os.path.exists(excl_filename))
+ data = read(htmlhandling.HtmlImageFormatter.EXCLUSION_FILE_NAME)
+ self.assertTrue('\\tau\\tau' in data)
+
+ def test_url_is_included(self):
+ prefix = "http://crustulus.de/blog"
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ img.set_url(prefix)
+ data = img.format(self.pos, '\epsilon<0', 'foo.png')
+ self.assertTrue( prefix in data)
+
+ def test_url_doesnt_contain_double_slashes(self):
+ prefix = "http://crustulus.de/blog/"
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ img.set_url(prefix)
+ data = img.format(self.pos, r'\gamma\text{strahlung}', 'foo.png')
+ self.assertFalse('//' in data.replace('http://','ignore'))
+
+ # depth is used as negative offset, so negative depth should result in
+ # positive offset
+ def test_that_negative_depth_results_in_positive_offset(self):
+ self.pos['depth'] = '-999'
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ data = img.format(self.pos, r'\gamma\text{strahlung}', 'foo.png')
+ self.assertTrue('align: ' + str(self.pos['depth'])[1:] in data)
+
+ def test_that_displaymath_is_set_or_unset(self):
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ data = img.format(self.pos, r'\gamma\text{strahlung}', 'foo.png',
+ True)
+ self.assertTrue('="displaymath' in data)
+ data = img.format(self.pos, r'\gamma\text{strahlung}', 'foo.png',
+ False)
+ self.assertTrue('="inlinemath' in data)
+
+ def test_that_alternative_css_class_is_set_correctly(self):
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ img.set_display_math_css_class('no1')
+ img.set_inline_math_css_class('no2')
+ data = img.format(self.pos, r'\gamma\text{strahlung}', 'foo.png',
+ True)
+ self.assertTrue('="no1"' in data)
+ data = img.format(self.pos, r'\gamma\text{strahlung}', 'foo.png',
+ False)
+ self.assertTrue('="no2' in data)
+
+ def test_that_unicode_is_replaced_if_requested(self):
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ img.set_replace_nonascii(True)
+ data = img.format(self.pos, '←', 'foo.png')
+ self.assertTrue('\\leftarrow' in data,
+ 'expected: "\\leftarrow" to be in "%s"' % data)
+
+ def test_that_unicode_is_kept_if_not_requested_to_replace(self):
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ img.set_replace_nonascii(False)
+ data = img.format(self.pos, '←', 'foo.png')
+ self.assertTrue('←' in data)
+
+ def test_formatting_commands_are_stripped(self):
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ data = img.format(self.pos, 'a\,b\,c\,d', 'foo.png')
+ self.assertTrue('a b c d' in data)
+ data = img.format(self.pos, 'a\,b\;c\ d', 'foo.png')
+ self.assertTrue('a b c d' in data)
+
+ data = img.format(self.pos, '\Big\{foo\Big\}', 'foo.png')
+ self.assertTrue('\{foo' in data and '\}' in data)
+ data = img.format(self.pos, r'\left\{foo\right\}', 'foo.png')
+ self.assertTrue('\{' in data and 'foo' in data and '\}' in data)
+
+
+def htmleqn(formula, hr=True):
+ """Format a formula to appear as if it would have been outsourced into an
+ external file."""
+ return '%s\n<p id="%s"><pre>%s</pre></span></p>\n' % (\
+ ('<hr/>' if hr else ''), htmlhandling.gen_id(formula), formula)
+
+
+class OutsourcingParserTest(unittest.TestCase):
+ def setUp(self):
+ self.html = ('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"' +
+ '\n "http://www.w3.org/TR/html4/strict.dtd">\n<html>\n<head>\n' +
+ '<meta http-equiv="content-type" content="text/html; charset=utf-8"/>' +
+ '<title>Outsourced Formulas</title></head>\n<body>\n<h1>heading</h1>')
+
+ def get_html(self, string):
+ """Create html string with head / tail und put the specified string into
+ it."""
+ return self.html + string + '\n</body>\n</html>'
+
+
+ def test_formulas_are_recognized(self):
+ data = self.get_html(htmleqn('\\tau'))
+ parser = htmlhandling.OutsourcedFormulaParser()
+ parser.feed(data)
+ self.assertEqual(len(parser.get_formulas()), 1)
+
+ def test_formula_doesnt_contain_surrounding_rubbish(self):
+ data = self.get_html(htmleqn('\\gamma'))
+ parser = htmlhandling.OutsourcedFormulaParser()
+ parser.feed(data)
+ self.assertEqual(len(parser.get_formulas()), 1)
+ key = next(iter(parser.get_formulas()))
+ par = parser.get_formulas()[key]
+ self.assertFalse('<h1' in par)
+ self.assertFalse('body>' in par)
+ self.assertFalse('hr' in par)
+
+ def test_that_header_is_parsed_correctly(self):
+ p = htmlhandling.OutsourcedFormulaParser()
+ p.feed(self.get_html(htmleqn('test123', False)))
+ head = p.get_head()
+ self.assertTrue('DOCTYPE' in head)
+ self.assertTrue('<html' in head)
+ self.assertTrue('<title' in head)
+ self.assertTrue('</title' in head)
+ self.assertTrue('</head' in head)
+ self.assertTrue('<meta' in head)
+ self.assertTrue('charset=' in head)
+
+ def test_multiple_formulas_are_recognized_correctly(self):
+ p = htmlhandling.OutsourcedFormulaParser()
+ p.feed(self.get_html(htmleqn('\\tau', False) + '\n' +
+ htmleqn('\\gamma') + '\n' + htmleqn('\\epsilon<0')))
+ forms = p.get_formulas()
+ self.assertEqual(len(forms), 3)
+ self.assertTrue('\\gamma' in forms.values())
+ self.assertTrue('\\gamma' in forms.values())
+ self.assertTrue('\\epsilon<0' in forms.values())
+
diff --git a/support/gladtex/tests/test_imagecreation.py b/support/gladtex/tests/test_imagecreation.py
new file mode 100644
index 0000000000..83f87b22d2
--- /dev/null
+++ b/support/gladtex/tests/test_imagecreation.py
@@ -0,0 +1,165 @@
+#pylint: disable=too-many-public-methods,import-error,too-few-public-methods,missing-docstring,unused-variable
+import os
+import pprint
+import shutil
+import tempfile
+import unittest
+from unittest.mock import patch
+from subprocess import SubprocessError
+
+import gleetex.image as image
+from gleetex.image import Format
+from gleetex.typesetting import LaTeXDocument as doc
+
+LATEX_ERROR_OUTPUT = r"""
+This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2016/Debian) (preloaded format=latex)
+ restricted \write18 enabled.
+entering extended mode
+(./bla.tex
+LaTeX2e <2016/03/31> patch level 3
+Babel <3.9r> and hyphenation patterns for 10 language(s) loaded.
+(/usr/share/texlive/texmf-dist/tex/latex/base/article.cls
+Document Class: article 2014/09/29 v1.4h Standard LaTeX document class
+(/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo)) (./bla.aux)
+! Undefined control sequence.
+<recently read> \foo
+
+l.3 $\foo
+ $
+No pages of output.
+Transcript written on bla.log.
+"""
+
+
+def call_dummy(_lklklklklk, **blah):
+ """Dummy to prohibit subprocess execution."""
+ return str(blah)
+
+
+
+
+
+#pylint: disable=unused-argument
+def latex_error_mock(_cmd, **quark):
+ """Mock an error case."""
+ raise SubprocessError(LATEX_ERROR_OUTPUT)
+
+#pylint: disable=unused-argument
+def dvipng_mock(cmd, **kwargs):
+ """Mock an error case."""
+ fn = None
+ try:
+ fn = next(e for e in cmd if e.endswith('.png'))
+ except StopIteration:
+ try:
+ fn = next(e for e in cmd if e.endswith('.dvi'))
+ except StopIteration:
+ pass
+ if fn:
+ with open(fn, 'w') as f:
+ f.write("test case")
+ return 'This is dvipng 1.14 Copyright 2002-2010 Jan-Ake Larsson\n ' + \
+ 'depth=3 height=9 width=22'
+
+def touch(files):
+ for file in files:
+ dirname = os.path.dirname(file)
+ if dirname and not os.path.exists(dirname):
+ os.makedirs(dirname)
+ with open(file, 'w') as f:
+ f.write('\n')
+
+
+class test_imagecreation(unittest.TestCase):
+ def setUp(self):
+ self.original_directory = os.getcwd()
+ self.tmpdir = tempfile.mkdtemp()
+ os.chdir(self.tmpdir)
+ image.Tex2img.call = call_dummy
+
+ def tearDown(self):
+ os.chdir(self.original_directory)
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
+
+ @patch('gleetex.image.proc_call', latex_error_mock)
+ def test_that_error_of_incorrect_formula_is_parsed_correctly(self):
+ i = image.Tex2img(Format.Png)
+ try:
+ i.create_dvi(doc("\\foo"), 'foo.png')
+ except SubprocessError as e:
+ # expect undefined control sequence in error output
+ self.assertTrue('Undefined' in e.args[0])
+
+ @patch('gleetex.image.proc_call', call_dummy)
+ def test_that_intermediate_files_are_removed_after_successful_run(self):
+ files = ['foo.log', 'foo.aux', 'foo.tex']
+ touch(files)
+ i = image.Tex2img(Format.Png)
+ i.create_dvi(doc("\\frac\\pi\\tau"), 'foo.png')
+ for intermediate_file in files:
+ self.assertFalse(os.path.exists(intermediate_file), "File " +
+ intermediate_file + " should not exist.")
+
+ @patch('gleetex.image.proc_call', latex_error_mock)
+ def test_that_intermediate_files_are_removed_when_exception_is_raised(self):
+ files = ['foo.log', 'foo.aux', 'foo.tex']
+ touch(files)
+ # error case
+ i = image.Tex2img(Format.Png)
+ try:
+ i.convert(doc("\\foo"), 'foo')
+ except SubprocessError as e:
+ for intermediate_file in files:
+ self.assertFalse(os.path.exists(intermediate_file), "File " +
+ intermediate_file + " should not exist.")
+
+ @patch('gleetex.image.proc_call', dvipng_mock)
+ def test_intermediate_files_are_removed(self):
+ files = ['foo.tex', 'foo.log', 'foo.aux', 'foo.dvi']
+ touch(files)
+ i = image.Tex2img(Format.Png)
+ i.convert(doc('\\hat{x}'), 'foo')
+ for intermediate_file in files:
+ self.assertFalse(os.path.exists(intermediate_file))
+
+ @patch('gleetex.image.proc_call', latex_error_mock)
+ def test_intermediate_files_are_removed_when_exception_raised(self):
+ files = ['foo.tex', 'foo.log', 'foo.aux', 'foo.dvi']
+ touch(files)
+ i = image.Tex2img(Format.Png)
+ try:
+ i.convert(doc('\\hat{x}'), 'foo')
+ except SubprocessError:
+ self.assertFalse(os.path.exists('foo.tex'))
+ self.assertFalse(os.path.exists('foo.dvi'))
+ self.assertFalse(os.path.exists('foo.log'))
+ self.assertFalse(os.path.exists('foo.aux'))
+
+
+ @patch('gleetex.image.proc_call', lambda *x, **y: 'This is dvipng 1.14 ' + \
+ 'Copyright 2002-2010 Jan-Ake Larsson\n depth=3 height=9 width=22')
+ def test_that_values_for_positioning_png_are_returned(self):
+ i = image.Tex2img(Format.Png)
+ posdata = i.create_image('foo.dvi')
+ self.assertTrue('height' in posdata)
+ self.assertTrue('width' in posdata)
+
+
+ @patch('gleetex.image.proc_call', dvipng_mock)
+ def test_that_output_file_names_with_paths_are_ok_and_log_is_removed(self):
+ fname = lambda f: os.path.join('bilder', 'farce.' + f)
+ touch([fname('log'), fname('png')])
+ t = image.Tex2img(Format.Png)
+ t.convert(doc(r"\hat{es}\pi\pi\ldots"), fname('')[:-1])
+ self.assertFalse(os.path.exists("farce.log"))
+ self.assertTrue(os.path.exists(fname('png')),
+ "couldn't find file {}, directory structure:\n{}".format(
+ fname('png'), ''.join(pprint.pformat(list(os.walk('.'))))))
+ self.assertFalse(os.path.exists(fname('log')))
+
+
+class TestImageResolutionCorrectlyCalculated(unittest.TestCase):
+ def test_sizes_are_correctly_calculated(self):
+ self.assertEqual(int(image.fontsize2dpi(12)), 115)
+ self.assertEqual(int(image.fontsize2dpi(10)), 96)
+
diff --git a/support/gladtex/tests/test_typesetting.py b/support/gladtex/tests/test_typesetting.py
new file mode 100644
index 0000000000..374fe38511
--- /dev/null
+++ b/support/gladtex/tests/test_typesetting.py
@@ -0,0 +1,218 @@
+#pylint: disable=too-many-public-methods,import-error,too-few-public-methods,missing-docstring,unused-variable
+import unittest
+from gleetex.typesetting import LaTeXDocument
+import gleetex.typesetting as typesetting
+
+class test_typesetting(unittest.TestCase):
+ def test_formula_is_embedded(self):
+ formula = 'E = m \\cdot c^2'
+ doc = LaTeXDocument(formula)
+ self.assertTrue(formula in str(doc),
+ "formula must be contained in LaTeX typesetting as it was inserted.")
+
+ def test_if_displaymath_unset_correct_env_used(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_displaymath(False)
+ self.assertTrue('\\(' in str(doc))
+ self.assertTrue('\\)' in str(doc))
+
+ def test_if_displaymath_is_set_correct_env_used(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_displaymath(True)
+ self.assertTrue('\\[' in str(doc))
+ self.assertTrue('\\]' in str(doc))
+
+ def test_preamble_is_included(self):
+ preamble = '\\usepackage{eurosym}'
+ doc = LaTeXDocument('moooo')
+ doc.set_preamble_string(preamble)
+ self.assertTrue(preamble in str(doc))
+
+ def test_obviously_wrong_encoding_trigger_exception(self):
+ doc = LaTeXDocument('f00')
+ self.assertRaises(ValueError, doc.set_encoding, 'latin1:')
+ self.assertRaises(ValueError, doc.set_encoding, 'utf66')
+ # the following passes (assertRaisesNot)
+ doc.set_encoding('utf-8')
+
+ def test_that_latex_maths_env_is_used(self):
+ doc = LaTeXDocument('f00')
+ doc.set_latex_environment('flalign*')
+ self.assertTrue(r'\begin{flalign*}' in str(doc))
+ self.assertTrue(r'\end{flalign*}' in str(doc))
+
+################################################################################
+
+
+class test_replace_unicode_characters(unittest.TestCase):
+ def test_that_ascii_strings_are_returned_verbatim(self):
+ for string in ['abc.\\', '`~[]}{:<>']:
+ textmode = typesetting.replace_unicode_characters(string, False)
+ self.assertEqual(textmode, string, 'expected %s, got %s' % (string, textmode))
+ mathmode = typesetting.replace_unicode_characters(string, True)
+ self.assertEqual(textmode, string, 'expected %s, got %s' % (string, mathmode))
+
+ def test_that_alphabetical_characters_are_replaced_by_default(self):
+ textmode = typesetting.replace_unicode_characters('ö', False)
+ self.assertTrue('\\"' in textmode)
+ mathmode = typesetting.replace_unicode_characters('ö', True)
+ self.assertTrue('\\ddot' in mathmode)
+
+ def test_that_alphabetical_characters_are_kept_in_text_mode_if_specified(self):
+ self.assertEqual(typesetting.replace_unicode_characters('ö', False, # text mode
+ replace_alphabeticals=False), 'ö')
+ self.assertEqual(typesetting.replace_unicode_characters('æ', False,
+ replace_alphabeticals=False), 'æ')
+
+ def test_that_alphanumericals_are_replaced_in_mathmode_even_if_replace_alphabeticals_set(self):
+ self.assertNotEqual(typesetting.replace_unicode_characters('öäü', True,
+ replace_alphabeticals=True), 'öäü')
+ self.assertNotEqual(typesetting.replace_unicode_characters('æø', True,
+ replace_alphabeticals=True), 'æø')
+
+
+ def test_that_charachters_not_present_in_file_raise_exception(self):
+ with self.assertRaises(ValueError):
+ typesetting.replace_unicode_characters('€', True)
+
+ def test_that_formulas_are_replaced(self):
+ self.assertNotEqual(typesetting.replace_unicode_characters('π', True),
+ 'π')
+ self.assertNotEqual(typesetting.replace_unicode_characters('π', False),
+ 'π')
+
+class test_get_matching_brace(unittest.TestCase):
+ def test_closing_brace_found_when_only_one_brace_present(self):
+ text = 'text{ok}'
+ self.assertEqual(typesetting.get_matching_brace(text, 4), len(text) - 1)
+ self.assertEqual(typesetting.get_matching_brace(text + 'foo', 4), len(text) - 1)
+
+ def test_outer_brace_found(self):
+ text = 'text{o, bla\\"{o}dfdx.}ds'
+ self.assertEqual(typesetting.get_matching_brace(text, 4), len(text)-3)
+
+ def test_inner_brace_is_matched(self):
+ text = 'text{o, bla\\"{o}dfdx.}ds'
+ self.assertEqual(typesetting.get_matching_brace(text, 13), 15)
+
+ def test_that_unmatched_braces_raise_exception(self):
+ with self.assertRaises(ValueError):
+ typesetting.get_matching_brace('text{foooooooo', 4)
+ with self.assertRaises(ValueError):
+ typesetting.get_matching_brace('text{jo\"{o....}', 4)
+
+ def test_wrong_position_for_opening_brace_raises(self):
+ with self.assertRaises(ValueError):
+ typesetting.get_matching_brace('moo', 1)
+
+
+class test_escape_unicode_maths(unittest.TestCase):
+ """These tests assume that the tests written above work!"""
+ def test_that_mathmode_and_textmode_are_treated_differently(self):
+ math = typesetting.escape_unicode_maths('ö')
+ self.assertNotEqual(math, 'ö')
+ text = typesetting.escape_unicode_maths('\\text{ö}')
+ self.assertFalse('ö' in text)
+ # check whether characters got transcribed differently; it's enough to
+ # check one character of the generated sequence, they should differ
+ self.assertNotEqual(math[:2], text[6:8])
+
+ def test_that_flag_to_preserve_alphas_is_passed_through(self):
+ res = typesetting.escape_unicode_maths('\\text{ö}',
+ replace_alphabeticals=False)
+ self.assertEqual(res, '\\text{ö}')
+
+ def test_that_all_characters_are_preserved_when_no_replacements_happen(self):
+ text = 'This is a \\text{test} mate.'
+ self.assertEqual(typesetting.escape_unicode_maths(text), text)
+ self.assertEqual(typesetting.escape_unicode_maths(text,
+ replace_alphabeticals=False), text)
+ text = 'But yeah but no' * 20 + ', oh my god!'
+ self.assertEqual(typesetting.escape_unicode_maths(text), text)
+ self.assertEqual(typesetting.escape_unicode_maths(text,
+ replace_alphabeticals=False), text)
+
+ def test_that_everything_around_surrounded_character_is_preserved(self):
+ text = 'This is a \\text{über} test. ;)'
+ result = typesetting.escape_unicode_maths(text,
+ replace_alphabeticals=True)
+ ue_pos = text.index('ü')
+ # text in front is unchanged
+ self.assertEqual(result[:ue_pos], text[:ue_pos])
+ # find b character, which is the start of the remaining string
+ b_pos = result[ue_pos:].find('b') + ue_pos
+ # check that text after umlaut matches
+ self.assertEqual(result[b_pos:], text[ue_pos+1:])
+
+ text = 'But yeah but no' * 20 + ', oh my god!ø'
+ o_strok_pos = text.index('ø')
+ res = typesetting.escape_unicode_maths(text)
+ self.assertEqual(res[:o_strok_pos], text[:o_strok_pos])
+
+ def test_that_unknown_unicode_characters_raise_exception(self):
+ # you know that Santa Clause character? Seriously, if you don't know it,
+ # you should have a look. LaTeX does indeed not have command for this
+ # (2016, one never knows)
+ santa = chr(127877)
+ with self.assertRaises(typesetting.DocumentSerializationException):
+ typesetting.escape_unicode_maths(santa)
+
+ def test_that_two_text_environments_preserve_all_characters(self):
+ text = r'a\cdot b \text{equals} b\cdot c} \mbox{ is not equal } u^{v\cdot k}'
+ self.assertEqual(typesetting.escape_unicode_maths(text), text)
+
+ def test_color_names_in_backgroundare_accepted(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_background_color('cyan')
+ doc = str(doc)
+ self.assertTrue('pagecolor{cyan}' in doc,
+ "Expected \\pagecolor in document, got: %s" % doc)
+ self.assertTrue('\\color' not in doc)
+ self.assertFalse('definecolor' in doc)
+
+ def test_color_names_in_foregroundare_accepted(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_foreground_color('cyan')
+ doc = str(doc)
+ self.assertTrue('pagecolor' not in doc,
+ "Expected \\pagecolor in document, got: %s" % doc)
+ self.assertTrue('\\color{cyan' in doc,
+ "expeccted \\color{cyan, got:\n" + doc)
+ self.assertFalse('definecolor' in doc)
+
+ def test_hex_colours_with_leading_0s_work(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_background_color('00FFCC')
+ doc = str(doc)
+ self.assertTrue('pagecolor{background}' in doc,
+ "Expected \\pagecolor in document, got: %s" % doc)
+ self.assertTrue('definecolor' in doc)
+ self.assertTrue('00FFCC' in doc)
+
+ def test_color_rgb_in_foregroundare_accepted(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_foreground_color('FFAACC')
+ doc = str(doc)
+ self.assertTrue('pagecolor{}' not in doc,
+ "Expected \\pagecolor in document, got: %s" % doc)
+ self.assertTrue('\\color{foreground' in doc,
+ "document misses \\color command: %s" % doc)
+ self.assertTrue('definecolor' in doc)
+ self.assertTrue('FFAACC' in doc)
+
+ def test_color_rgb_in_backgroundare_accepted(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_background_color('FFAACC')
+ doc = str(doc)
+ self.assertTrue('pagecolor{background}' in doc,
+ "Expected \\pagecolor in document, got: %s" % doc)
+ self.assertTrue('\\color' not in doc)
+ self.assertTrue('definecolor' in doc)
+ self.assertTrue('FFAACC' in doc)
+
+ def test_no_colors_no_color_definitions(self):
+ doc = str(LaTeXDocument(r'A = \pi r^2'))
+ self.assertFalse('pagecolor' in doc)
+ self.assertFalse('\\color' in doc)
+ self.assertFalse('definecolor' in doc)
+
diff --git a/support/gladtex/update_unicode_table.py b/support/gladtex/update_unicode_table.py
new file mode 100644
index 0000000000..f32bc19fcd
--- /dev/null
+++ b/support/gladtex/update_unicode_table.py
@@ -0,0 +1,142 @@
+"""This script auto-generates gleetex/unicode_data.py. The purpose is to provide a
+table with mappings from unicode points to their LaTeX equivalent. This way,
+formulas can be converted using LaTeX2e, but the end-user can still use unicode
+in it's formulas. The unicode version can also be used to make the alternative
+text more readable."""
+
+import collections
+import enum
+import os
+import urllib.request
+import xml.etree.ElementTree as ET
+
+################################################################################
+# Constants
+
+class LaTeXMode(enum.Enum):
+ # exception: not a constant, but required for one of the constants
+ """Represent either math or text mode. Math mode in LaTeX is e.g.
+ everything between $ and $."""
+ textmode = 0
+ mathmode = 1
+
+# URL to XML file, which is used to generate the python source file
+UNICODE_TABLE_URL = "https://raw.githubusercontent.com/w3c/xml-entities/gh-pages/unicode.xml"
+# a list of commands to replace, if found
+BAD_COMMANDS = {
+ # decimal_codepoint: {LaTeXMode: new version}
+ 178: {LaTeXMode.mathmode: '^2'},
+ 179: {LaTeXMode.mathmode: '^3'},
+ 181: {LaTeXMode.mathmode: '\\mu'},
+ 185: {LaTeXMode.mathmode: '^1'},
+ 8211: {LaTeXMode.mathmode: '\\mathrm{\\textendash}'},
+ 8722: {LaTeXMode.mathmode: '-'}
+}
+
+################################################################################
+
+def get_unicode_table_xml():
+ with urllib.request.urlopen(UNICODE_TABLE_URL) as u:
+ return ET.fromstring(u.read())
+
+def create_unicode_latex_table(root):
+ """This function iterates over the XML tree and extracts all characters for
+ the unicode table. The resulting table will have the decimal unicode point
+ as key. The value is again a dict with the possible keys from LaTeX and the
+ LaTeX commands as string.
+ Certain unicode points are ignored, to prevent replacing normal or control
+ characters."""
+ unicode_table = {}
+ for character in root.find('charlist').iterfind('character'):
+ childtags = set(node.tag for node in character.getchildren())
+ # skip characters without LaTeX alternative
+ if 'latex' not in childtags and 'AMS' not in childtags and \
+ 'mathlatex' not in childtags:
+ continue # skip this character
+ attr = character.attrib.get
+ # if no mode (text or math) was specified, ignore character
+ if attr('mode') not in ('text', 'math', 'mixed', 'other'):
+ continue
+
+ # a defined character may have multiple codepoints (called ids); add
+ # each of the ids as a separate entry to the table
+ ids = tuple(map(int, attr('dec').split('-')))
+ if any(elem for elem in ids if elem < 161):
+ continue # ignore ASCII and a few control unicode characters
+
+ # extract textmode, mathmode and AMS commands:
+ commands = {}
+ if 'latex' in childtags:
+ commands[LaTeXMode.textmode] =\
+ next(character.iterfind('latex')).text
+ if 'AMS' in childtags:
+ commands[LaTeXMode.mathmode] = next(character.iterfind('AMS')).text
+ # only take LaTeX command from <mathlatex/>, if no AMS tag present and
+ # no set was specified. A `set` is a attempt to specify the LaTeX
+ # package which needs to be loaded.
+ if 'mathlatex' in childtags and LaTeXMode.mathmode not in commands:
+ mathnode = next(character.iterfind('mathlatex'))
+ if 'set' not in mathnode.attrib:
+ commands[LaTeXMode.mathmode] = mathnode.text
+
+ if commands: # if a usable textmode and a mathmode without unicode-math found:
+ for identification in ids:
+ # some code points are not usable for our purposes, so update
+ # the control sequences, if appropriate
+ if identification in BAD_COMMANDS:
+ commands.update(BAD_COMMANDS[identification])
+ unicode_table[identification] = commands
+ return unicode_table
+
+def serialize_table(table):
+ """Serialize the given unicode table to a python table, which could be
+ directly executed by eval. The decimal code points, serving as a key in the
+ dictionary, are sorted for the output."""
+ ordered_table = collections.OrderedDict()
+ for key in sorted(table.keys()):
+ ordered_table[key] = table[key]
+ python_string = ['unicode_table = {']
+ reprmode = lambda m, v: 'LaTeXMode.%s: %s' % (m.name, repr(v[m]))
+ for code_point, replacements in ordered_table.items():
+ # serialize by hand to have a fixed order of items; helpful for a
+ # minimal git diff
+ commands = ''
+ if LaTeXMode.textmode in replacements:
+ commands = reprmode(LaTeXMode.textmode, replacements)
+ if LaTeXMode.mathmode in replacements:
+ if commands:
+ commands += ', '
+ commands += reprmode(LaTeXMode.mathmode, replacements)
+ python_string.append('%s: {%s},' % (code_point, commands))
+ return '\n '.join(python_string) + '\n }\n'
+
+def generate_python_src_file(table, python_table):
+ """Generate a fully importable python source file, by dumping the enum
+ declarations, python imports, doc strings and the given python string with
+ the unicode table into the source and returning it as a whole string."""
+ enum_def = 'class LaTeXMode(enum.Enum):\n """%s"""\n ' % LaTeXMode.__doc__
+ enum_values = tuple(e for e in dir(LaTeXMode) if not e.startswith('_'))
+ enum_def += '\n '.join('%s = %s' % (name, getattr(LaTeXMode, name).value)
+ for name in enum_values)
+ return """\"\"\"
+DO NOT ALTER THIS FILE IN ANY WAY, IT IS GENERATED AUTOMATICALLY. SEE THE SCRIPT
+`update_unicode_table.py` FOR MORE INFORMATION.
+
+This file contains a table of unicode code point to LaTeX command mapping. It
+has %s entries and was derived from
+<%s>.\"\"\"
+#pylint: disable=too-many-lines,missing-docstring\n\n
+import enum\n
+%s\n\n%s\n""" % (len(table), UNICODE_TABLE_URL, enum_def, python_table)
+
+
+def main():
+ if not os.path.exists('gleetex'):
+ print("Error: Generator script must be run from GladTeX source root.")
+ table = create_unicode_latex_table(get_unicode_table_xml())
+ python_table = serialize_table(table)
+ with open('gleetex/unicode.py', 'w', encoding='utf-8') as f:
+ f.write(generate_python_src_file(table, python_table))
+
+if __name__ == '__main__':
+ main()