CI_CD updates
This commit is contained in:
@@ -0,0 +1,840 @@
|
||||
# $Id: __init__.py 9283 2022-11-28 23:55:57Z milde $
|
||||
# Author: David Goodger <goodger@python.org>
|
||||
# Copyright: This module has been placed in the public domain.
|
||||
|
||||
"""
|
||||
Miscellaneous utilities for the documentation utilities.
|
||||
"""
|
||||
|
||||
__docformat__ = 'reStructuredText'
|
||||
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
from pathlib import PurePath, Path
|
||||
import re
|
||||
import itertools
|
||||
import warnings
|
||||
import unicodedata
|
||||
|
||||
from docutils import ApplicationError, DataError, __version_info__
|
||||
from docutils import io, nodes
|
||||
# for backwards compatibility
|
||||
from docutils.nodes import unescape # noqa: F401
|
||||
|
||||
|
||||
class SystemMessage(ApplicationError):
|
||||
|
||||
def __init__(self, system_message, level):
|
||||
Exception.__init__(self, system_message.astext())
|
||||
self.level = level
|
||||
|
||||
|
||||
class SystemMessagePropagation(ApplicationError):
|
||||
pass
|
||||
|
||||
|
||||
class Reporter:
|
||||
|
||||
"""
|
||||
Info/warning/error reporter and ``system_message`` element generator.
|
||||
|
||||
Five levels of system messages are defined, along with corresponding
|
||||
methods: `debug()`, `info()`, `warning()`, `error()`, and `severe()`.
|
||||
|
||||
There is typically one Reporter object per process. A Reporter object is
|
||||
instantiated with thresholds for reporting (generating warnings) and
|
||||
halting processing (raising exceptions), a switch to turn debug output on
|
||||
or off, and an I/O stream for warnings. These are stored as instance
|
||||
attributes.
|
||||
|
||||
When a system message is generated, its level is compared to the stored
|
||||
thresholds, and a warning or error is generated as appropriate. Debug
|
||||
messages are produced if the stored debug switch is on, independently of
|
||||
other thresholds. Message output is sent to the stored warning stream if
|
||||
not set to ''.
|
||||
|
||||
The Reporter class also employs a modified form of the "Observer" pattern
|
||||
[GoF95]_ to track system messages generated. The `attach_observer` method
|
||||
should be called before parsing, with a bound method or function which
|
||||
accepts system messages. The observer can be removed with
|
||||
`detach_observer`, and another added in its place.
|
||||
|
||||
.. [GoF95] Gamma, Helm, Johnson, Vlissides. *Design Patterns: Elements of
|
||||
Reusable Object-Oriented Software*. Addison-Wesley, Reading, MA, USA,
|
||||
1995.
|
||||
"""
|
||||
|
||||
levels = 'DEBUG INFO WARNING ERROR SEVERE'.split()
|
||||
"""List of names for system message levels, indexed by level."""
|
||||
|
||||
# system message level constants:
|
||||
(DEBUG_LEVEL,
|
||||
INFO_LEVEL,
|
||||
WARNING_LEVEL,
|
||||
ERROR_LEVEL,
|
||||
SEVERE_LEVEL) = range(5)
|
||||
|
||||
def __init__(self, source, report_level, halt_level, stream=None,
|
||||
debug=False, encoding=None, error_handler='backslashreplace'):
|
||||
"""
|
||||
:Parameters:
|
||||
- `source`: The path to or description of the source data.
|
||||
- `report_level`: The level at or above which warning output will
|
||||
be sent to `stream`.
|
||||
- `halt_level`: The level at or above which `SystemMessage`
|
||||
exceptions will be raised, halting execution.
|
||||
- `debug`: Show debug (level=0) system messages?
|
||||
- `stream`: Where warning output is sent. Can be file-like (has a
|
||||
``.write`` method), a string (file name, opened for writing),
|
||||
'' (empty string) or `False` (for discarding all stream messages)
|
||||
or `None` (implies `sys.stderr`; default).
|
||||
- `encoding`: The output encoding.
|
||||
- `error_handler`: The error handler for stderr output encoding.
|
||||
"""
|
||||
|
||||
self.source = source
|
||||
"""The path to or description of the source data."""
|
||||
|
||||
self.error_handler = error_handler
|
||||
"""The character encoding error handler."""
|
||||
|
||||
self.debug_flag = debug
|
||||
"""Show debug (level=0) system messages?"""
|
||||
|
||||
self.report_level = report_level
|
||||
"""The level at or above which warning output will be sent
|
||||
to `self.stream`."""
|
||||
|
||||
self.halt_level = halt_level
|
||||
"""The level at or above which `SystemMessage` exceptions
|
||||
will be raised, halting execution."""
|
||||
|
||||
if not isinstance(stream, io.ErrorOutput):
|
||||
stream = io.ErrorOutput(stream, encoding, error_handler)
|
||||
|
||||
self.stream = stream
|
||||
"""Where warning output is sent."""
|
||||
|
||||
self.encoding = encoding or getattr(stream, 'encoding', 'ascii')
|
||||
"""The output character encoding."""
|
||||
|
||||
self.observers = []
|
||||
"""List of bound methods or functions to call with each system_message
|
||||
created."""
|
||||
|
||||
self.max_level = -1
|
||||
"""The highest level system message generated so far."""
|
||||
|
||||
def set_conditions(self, category, report_level, halt_level,
|
||||
stream=None, debug=False):
|
||||
warnings.warn('docutils.utils.Reporter.set_conditions() deprecated; '
|
||||
'Will be removed in Docutils 0.21 or later. '
|
||||
'Set attributes via configuration settings or directly.',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
self.report_level = report_level
|
||||
self.halt_level = halt_level
|
||||
if not isinstance(stream, io.ErrorOutput):
|
||||
stream = io.ErrorOutput(stream, self.encoding, self.error_handler)
|
||||
self.stream = stream
|
||||
self.debug_flag = debug
|
||||
|
||||
def attach_observer(self, observer):
|
||||
"""
|
||||
The `observer` parameter is a function or bound method which takes one
|
||||
argument, a `nodes.system_message` instance.
|
||||
"""
|
||||
self.observers.append(observer)
|
||||
|
||||
def detach_observer(self, observer):
|
||||
self.observers.remove(observer)
|
||||
|
||||
def notify_observers(self, message):
|
||||
for observer in self.observers:
|
||||
observer(message)
|
||||
|
||||
def system_message(self, level, message, *children, **kwargs):
|
||||
"""
|
||||
Return a system_message object.
|
||||
|
||||
Raise an exception or generate a warning if appropriate.
|
||||
"""
|
||||
# `message` can be a `str` or `Exception` instance.
|
||||
if isinstance(message, Exception):
|
||||
message = str(message)
|
||||
|
||||
attributes = kwargs.copy()
|
||||
if 'base_node' in kwargs:
|
||||
source, line = get_source_line(kwargs['base_node'])
|
||||
del attributes['base_node']
|
||||
if source is not None:
|
||||
attributes.setdefault('source', source)
|
||||
if line is not None:
|
||||
attributes.setdefault('line', line)
|
||||
# assert source is not None, "line- but no source-argument"
|
||||
if 'source' not in attributes:
|
||||
# 'line' is absolute line number
|
||||
try:
|
||||
source, line = self.get_source_and_line(attributes.get('line'))
|
||||
except AttributeError:
|
||||
source, line = None, None
|
||||
if source is not None:
|
||||
attributes['source'] = source
|
||||
if line is not None:
|
||||
attributes['line'] = line
|
||||
# assert attributes['line'] is not None, (message, kwargs)
|
||||
# assert attributes['source'] is not None, (message, kwargs)
|
||||
attributes.setdefault('source', self.source)
|
||||
|
||||
msg = nodes.system_message(message, level=level,
|
||||
type=self.levels[level],
|
||||
*children, **attributes)
|
||||
if self.stream and (level >= self.report_level
|
||||
or self.debug_flag and level == self.DEBUG_LEVEL
|
||||
or level >= self.halt_level):
|
||||
self.stream.write(msg.astext() + '\n')
|
||||
if level >= self.halt_level:
|
||||
raise SystemMessage(msg, level)
|
||||
if level > self.DEBUG_LEVEL or self.debug_flag:
|
||||
self.notify_observers(msg)
|
||||
self.max_level = max(level, self.max_level)
|
||||
return msg
|
||||
|
||||
def debug(self, *args, **kwargs):
|
||||
"""
|
||||
Level-0, "DEBUG": an internal reporting issue. Typically, there is no
|
||||
effect on the processing. Level-0 system messages are handled
|
||||
separately from the others.
|
||||
"""
|
||||
if self.debug_flag:
|
||||
return self.system_message(self.DEBUG_LEVEL, *args, **kwargs)
|
||||
|
||||
def info(self, *args, **kwargs):
|
||||
"""
|
||||
Level-1, "INFO": a minor issue that can be ignored. Typically there is
|
||||
no effect on processing, and level-1 system messages are not reported.
|
||||
"""
|
||||
return self.system_message(self.INFO_LEVEL, *args, **kwargs)
|
||||
|
||||
def warning(self, *args, **kwargs):
|
||||
"""
|
||||
Level-2, "WARNING": an issue that should be addressed. If ignored,
|
||||
there may be unpredictable problems with the output.
|
||||
"""
|
||||
return self.system_message(self.WARNING_LEVEL, *args, **kwargs)
|
||||
|
||||
def error(self, *args, **kwargs):
|
||||
"""
|
||||
Level-3, "ERROR": an error that should be addressed. If ignored, the
|
||||
output will contain errors.
|
||||
"""
|
||||
return self.system_message(self.ERROR_LEVEL, *args, **kwargs)
|
||||
|
||||
def severe(self, *args, **kwargs):
|
||||
"""
|
||||
Level-4, "SEVERE": a severe error that must be addressed. If ignored,
|
||||
the output will contain severe errors. Typically level-4 system
|
||||
messages are turned into exceptions which halt processing.
|
||||
"""
|
||||
return self.system_message(self.SEVERE_LEVEL, *args, **kwargs)
|
||||
|
||||
|
||||
class ExtensionOptionError(DataError): pass
|
||||
class BadOptionError(ExtensionOptionError): pass
|
||||
class BadOptionDataError(ExtensionOptionError): pass
|
||||
class DuplicateOptionError(ExtensionOptionError): pass
|
||||
|
||||
|
||||
def extract_extension_options(field_list, options_spec):
|
||||
"""
|
||||
Return a dictionary mapping extension option names to converted values.
|
||||
|
||||
:Parameters:
|
||||
- `field_list`: A flat field list without field arguments, where each
|
||||
field body consists of a single paragraph only.
|
||||
- `options_spec`: Dictionary mapping known option names to a
|
||||
conversion function such as `int` or `float`.
|
||||
|
||||
:Exceptions:
|
||||
- `KeyError` for unknown option names.
|
||||
- `ValueError` for invalid option values (raised by the conversion
|
||||
function).
|
||||
- `TypeError` for invalid option value types (raised by conversion
|
||||
function).
|
||||
- `DuplicateOptionError` for duplicate options.
|
||||
- `BadOptionError` for invalid fields.
|
||||
- `BadOptionDataError` for invalid option data (missing name,
|
||||
missing data, bad quotes, etc.).
|
||||
"""
|
||||
option_list = extract_options(field_list)
|
||||
return assemble_option_dict(option_list, options_spec)
|
||||
|
||||
|
||||
def extract_options(field_list):
|
||||
"""
|
||||
Return a list of option (name, value) pairs from field names & bodies.
|
||||
|
||||
:Parameter:
|
||||
`field_list`: A flat field list, where each field name is a single
|
||||
word and each field body consists of a single paragraph only.
|
||||
|
||||
:Exceptions:
|
||||
- `BadOptionError` for invalid fields.
|
||||
- `BadOptionDataError` for invalid option data (missing name,
|
||||
missing data, bad quotes, etc.).
|
||||
"""
|
||||
option_list = []
|
||||
for field in field_list:
|
||||
if len(field[0].astext().split()) != 1:
|
||||
raise BadOptionError(
|
||||
'extension option field name may not contain multiple words')
|
||||
name = str(field[0].astext().lower())
|
||||
body = field[1]
|
||||
if len(body) == 0:
|
||||
data = None
|
||||
elif (len(body) > 1
|
||||
or not isinstance(body[0], nodes.paragraph)
|
||||
or len(body[0]) != 1
|
||||
or not isinstance(body[0][0], nodes.Text)):
|
||||
raise BadOptionDataError(
|
||||
'extension option field body may contain\n'
|
||||
'a single paragraph only (option "%s")' % name)
|
||||
else:
|
||||
data = body[0][0].astext()
|
||||
option_list.append((name, data))
|
||||
return option_list
|
||||
|
||||
|
||||
def assemble_option_dict(option_list, options_spec):
|
||||
"""
|
||||
Return a mapping of option names to values.
|
||||
|
||||
:Parameters:
|
||||
- `option_list`: A list of (name, value) pairs (the output of
|
||||
`extract_options()`).
|
||||
- `options_spec`: Dictionary mapping known option names to a
|
||||
conversion function such as `int` or `float`.
|
||||
|
||||
:Exceptions:
|
||||
- `KeyError` for unknown option names.
|
||||
- `DuplicateOptionError` for duplicate options.
|
||||
- `ValueError` for invalid option values (raised by conversion
|
||||
function).
|
||||
- `TypeError` for invalid option value types (raised by conversion
|
||||
function).
|
||||
"""
|
||||
options = {}
|
||||
for name, value in option_list:
|
||||
convertor = options_spec[name] # raises KeyError if unknown
|
||||
if convertor is None:
|
||||
raise KeyError(name) # or if explicitly disabled
|
||||
if name in options:
|
||||
raise DuplicateOptionError('duplicate option "%s"' % name)
|
||||
try:
|
||||
options[name] = convertor(value)
|
||||
except (ValueError, TypeError) as detail:
|
||||
raise detail.__class__('(option: "%s"; value: %r)\n%s'
|
||||
% (name, value, ' '.join(detail.args)))
|
||||
return options
|
||||
|
||||
|
||||
class NameValueError(DataError): pass
|
||||
|
||||
|
||||
def decode_path(path):
|
||||
"""
|
||||
Ensure `path` is Unicode. Return `str` instance.
|
||||
|
||||
Decode file/path string in a failsafe manner if not already done.
|
||||
"""
|
||||
# TODO: is this still required with Python 3?
|
||||
if isinstance(path, str):
|
||||
return path
|
||||
try:
|
||||
path = path.decode(sys.getfilesystemencoding(), 'strict')
|
||||
except AttributeError: # default value None has no decode method
|
||||
if not path:
|
||||
return ''
|
||||
raise ValueError('`path` value must be a String or ``None``, '
|
||||
f'not {path!r}')
|
||||
except UnicodeDecodeError:
|
||||
try:
|
||||
path = path.decode('utf-8', 'strict')
|
||||
except UnicodeDecodeError:
|
||||
path = path.decode('ascii', 'replace')
|
||||
return path
|
||||
|
||||
|
||||
def extract_name_value(line):
|
||||
"""
|
||||
Return a list of (name, value) from a line of the form "name=value ...".
|
||||
|
||||
:Exception:
|
||||
`NameValueError` for invalid input (missing name, missing data, bad
|
||||
quotes, etc.).
|
||||
"""
|
||||
attlist = []
|
||||
while line:
|
||||
equals = line.find('=')
|
||||
if equals == -1:
|
||||
raise NameValueError('missing "="')
|
||||
attname = line[:equals].strip()
|
||||
if equals == 0 or not attname:
|
||||
raise NameValueError(
|
||||
'missing attribute name before "="')
|
||||
line = line[equals+1:].lstrip()
|
||||
if not line:
|
||||
raise NameValueError(
|
||||
'missing value after "%s="' % attname)
|
||||
if line[0] in '\'"':
|
||||
endquote = line.find(line[0], 1)
|
||||
if endquote == -1:
|
||||
raise NameValueError(
|
||||
'attribute "%s" missing end quote (%s)'
|
||||
% (attname, line[0]))
|
||||
if len(line) > endquote + 1 and line[endquote + 1].strip():
|
||||
raise NameValueError(
|
||||
'attribute "%s" end quote (%s) not followed by '
|
||||
'whitespace' % (attname, line[0]))
|
||||
data = line[1:endquote]
|
||||
line = line[endquote+1:].lstrip()
|
||||
else:
|
||||
space = line.find(' ')
|
||||
if space == -1:
|
||||
data = line
|
||||
line = ''
|
||||
else:
|
||||
data = line[:space]
|
||||
line = line[space+1:].lstrip()
|
||||
attlist.append((attname.lower(), data))
|
||||
return attlist
|
||||
|
||||
|
||||
def new_reporter(source_path, settings):
|
||||
"""
|
||||
Return a new Reporter object.
|
||||
|
||||
:Parameters:
|
||||
`source` : string
|
||||
The path to or description of the source text of the document.
|
||||
`settings` : optparse.Values object
|
||||
Runtime settings.
|
||||
"""
|
||||
reporter = Reporter(
|
||||
source_path, settings.report_level, settings.halt_level,
|
||||
stream=settings.warning_stream, debug=settings.debug,
|
||||
encoding=settings.error_encoding,
|
||||
error_handler=settings.error_encoding_error_handler)
|
||||
return reporter
|
||||
|
||||
|
||||
def new_document(source_path, settings=None):
|
||||
"""
|
||||
Return a new empty document object.
|
||||
|
||||
:Parameters:
|
||||
`source_path` : string
|
||||
The path to or description of the source text of the document.
|
||||
`settings` : optparse.Values object
|
||||
Runtime settings. If none are provided, a default core set will
|
||||
be used. If you will use the document object with any Docutils
|
||||
components, you must provide their default settings as well.
|
||||
|
||||
For example, if parsing rST, at least provide the rst-parser
|
||||
settings, obtainable as follows:
|
||||
|
||||
Defaults for parser component::
|
||||
|
||||
settings = docutils.frontend.get_default_settings(
|
||||
docutils.parsers.rst.Parser)
|
||||
|
||||
Defaults and configuration file customizations::
|
||||
|
||||
settings = docutils.core.Publisher(
|
||||
parser=docutils.parsers.rst.Parser).get_settings()
|
||||
|
||||
"""
|
||||
# Import at top of module would lead to circular dependency!
|
||||
from docutils import frontend
|
||||
if settings is None:
|
||||
settings = frontend.get_default_settings()
|
||||
source_path = decode_path(source_path)
|
||||
reporter = new_reporter(source_path, settings)
|
||||
document = nodes.document(settings, reporter, source=source_path)
|
||||
document.note_source(source_path, -1)
|
||||
return document
|
||||
|
||||
|
||||
def clean_rcs_keywords(paragraph, keyword_substitutions):
|
||||
if len(paragraph) == 1 and isinstance(paragraph[0], nodes.Text):
|
||||
textnode = paragraph[0]
|
||||
for pattern, substitution in keyword_substitutions:
|
||||
match = pattern.search(textnode)
|
||||
if match:
|
||||
paragraph[0] = nodes.Text(pattern.sub(substitution, textnode))
|
||||
return
|
||||
|
||||
|
||||
def relative_path(source, target):
|
||||
"""
|
||||
Build and return a path to `target`, relative to `source` (both files).
|
||||
|
||||
Differences to `os.relpath()`:
|
||||
|
||||
* Inverse argument order.
|
||||
* `source` expects path to a FILE (while os.relpath expects a dir)!
|
||||
(Add a "dummy" file name if `source` points to a directory.)
|
||||
* Always use Posix path separator ("/") for the output.
|
||||
* Use `os.sep` for parsing the input (ignored by `os.relpath()`).
|
||||
* If there is no common prefix, return the absolute path to `target`.
|
||||
|
||||
"""
|
||||
source_parts = os.path.abspath(source or type(target)('dummy_file')
|
||||
).split(os.sep)
|
||||
target_parts = os.path.abspath(target).split(os.sep)
|
||||
# Check first 2 parts because '/dir'.split('/') == ['', 'dir']:
|
||||
if source_parts[:2] != target_parts[:2]:
|
||||
# Nothing in common between paths.
|
||||
# Return absolute path, using '/' for URLs:
|
||||
return '/'.join(target_parts)
|
||||
source_parts.reverse()
|
||||
target_parts.reverse()
|
||||
while (source_parts and target_parts
|
||||
and source_parts[-1] == target_parts[-1]):
|
||||
# Remove path components in common:
|
||||
source_parts.pop()
|
||||
target_parts.pop()
|
||||
target_parts.reverse()
|
||||
parts = ['..'] * (len(source_parts) - 1) + target_parts
|
||||
return '/'.join(parts)
|
||||
|
||||
|
||||
def get_stylesheet_reference(settings, relative_to=None):
|
||||
"""
|
||||
Retrieve a stylesheet reference from the settings object.
|
||||
|
||||
Deprecated. Use get_stylesheet_list() instead to
|
||||
enable specification of multiple stylesheets as a comma-separated
|
||||
list.
|
||||
"""
|
||||
warnings.warn('utils.get_stylesheet_reference()'
|
||||
' is obsoleted by utils.get_stylesheet_list()'
|
||||
' and will be removed in Docutils 2.0.',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
if settings.stylesheet_path:
|
||||
assert not settings.stylesheet, (
|
||||
'stylesheet and stylesheet_path are mutually exclusive.')
|
||||
if relative_to is None:
|
||||
relative_to = settings._destination
|
||||
return relative_path(relative_to, settings.stylesheet_path)
|
||||
else:
|
||||
return settings.stylesheet
|
||||
|
||||
|
||||
# Return 'stylesheet' or 'stylesheet_path' arguments as list.
|
||||
#
|
||||
# The original settings arguments are kept unchanged: you can test
|
||||
# with e.g. ``if settings.stylesheet_path: ...``.
|
||||
#
|
||||
# Differences to the depracated `get_stylesheet_reference()`:
|
||||
# * return value is a list
|
||||
# * no re-writing of the path (and therefore no optional argument)
|
||||
# (if required, use ``utils.relative_path(source, target)``
|
||||
# in the calling script)
|
||||
def get_stylesheet_list(settings):
|
||||
"""
|
||||
Retrieve list of stylesheet references from the settings object.
|
||||
"""
|
||||
assert not (settings.stylesheet and settings.stylesheet_path), (
|
||||
'stylesheet and stylesheet_path are mutually exclusive.')
|
||||
stylesheets = settings.stylesheet_path or settings.stylesheet or []
|
||||
# programmatically set default may be string with comma separated list:
|
||||
if not isinstance(stylesheets, list):
|
||||
stylesheets = [path.strip() for path in stylesheets.split(',')]
|
||||
if settings.stylesheet_path:
|
||||
# expand relative paths if found in stylesheet-dirs:
|
||||
stylesheets = [find_file_in_dirs(path, settings.stylesheet_dirs)
|
||||
for path in stylesheets]
|
||||
return stylesheets
|
||||
|
||||
|
||||
def find_file_in_dirs(path, dirs):
|
||||
"""
|
||||
Search for `path` in the list of directories `dirs`.
|
||||
|
||||
Return the first expansion that matches an existing file.
|
||||
"""
|
||||
path = Path(path)
|
||||
if path.is_absolute():
|
||||
return path.as_posix()
|
||||
for d in dirs:
|
||||
f = Path(d).expanduser() / path
|
||||
if f.exists():
|
||||
return f.as_posix()
|
||||
return path.as_posix()
|
||||
|
||||
|
||||
def get_trim_footnote_ref_space(settings):
|
||||
"""
|
||||
Return whether or not to trim footnote space.
|
||||
|
||||
If trim_footnote_reference_space is not None, return it.
|
||||
|
||||
If trim_footnote_reference_space is None, return False unless the
|
||||
footnote reference style is 'superscript'.
|
||||
"""
|
||||
if settings.setdefault('trim_footnote_reference_space', None) is None:
|
||||
return getattr(settings, 'footnote_references', None) == 'superscript'
|
||||
else:
|
||||
return settings.trim_footnote_reference_space
|
||||
|
||||
|
||||
def get_source_line(node):
|
||||
"""
|
||||
Return the "source" and "line" attributes from the `node` given or from
|
||||
its closest ancestor.
|
||||
"""
|
||||
while node:
|
||||
if node.source or node.line:
|
||||
return node.source, node.line
|
||||
node = node.parent
|
||||
return None, None
|
||||
|
||||
|
||||
def escape2null(text):
|
||||
"""Return a string with escape-backslashes converted to nulls."""
|
||||
parts = []
|
||||
start = 0
|
||||
while True:
|
||||
found = text.find('\\', start)
|
||||
if found == -1:
|
||||
parts.append(text[start:])
|
||||
return ''.join(parts)
|
||||
parts.append(text[start:found])
|
||||
parts.append('\x00' + text[found+1:found+2])
|
||||
start = found + 2 # skip character after escape
|
||||
|
||||
|
||||
def split_escaped_whitespace(text):
|
||||
"""
|
||||
Split `text` on escaped whitespace (null+space or null+newline).
|
||||
Return a list of strings.
|
||||
"""
|
||||
strings = text.split('\x00 ')
|
||||
strings = [string.split('\x00\n') for string in strings]
|
||||
# flatten list of lists of strings to list of strings:
|
||||
return list(itertools.chain(*strings))
|
||||
|
||||
|
||||
def strip_combining_chars(text):
|
||||
return ''.join(c for c in text if not unicodedata.combining(c))
|
||||
|
||||
|
||||
def find_combining_chars(text):
|
||||
"""Return indices of all combining chars in Unicode string `text`.
|
||||
|
||||
>>> from docutils.utils import find_combining_chars
|
||||
>>> find_combining_chars('A t̆ab̆lĕ')
|
||||
[3, 6, 9]
|
||||
|
||||
"""
|
||||
return [i for i, c in enumerate(text) if unicodedata.combining(c)]
|
||||
|
||||
|
||||
def column_indices(text):
|
||||
"""Indices of Unicode string `text` when skipping combining characters.
|
||||
|
||||
>>> from docutils.utils import column_indices
|
||||
>>> column_indices('A t̆ab̆lĕ')
|
||||
[0, 1, 2, 4, 5, 7, 8]
|
||||
|
||||
"""
|
||||
# TODO: account for asian wide chars here instead of using dummy
|
||||
# replacements in the tableparser?
|
||||
string_indices = list(range(len(text)))
|
||||
for index in find_combining_chars(text):
|
||||
string_indices[index] = None
|
||||
return [i for i in string_indices if i is not None]
|
||||
|
||||
|
||||
east_asian_widths = {'W': 2, # Wide
|
||||
'F': 2, # Full-width (wide)
|
||||
'Na': 1, # Narrow
|
||||
'H': 1, # Half-width (narrow)
|
||||
'N': 1, # Neutral (not East Asian, treated as narrow)
|
||||
'A': 1, # Ambiguous (s/b wide in East Asian context,
|
||||
} # narrow otherwise, but that doesn't work)
|
||||
"""Mapping of result codes from `unicodedata.east_asian_widt()` to character
|
||||
column widths."""
|
||||
|
||||
|
||||
def column_width(text):
|
||||
"""Return the column width of text.
|
||||
|
||||
Correct ``len(text)`` for wide East Asian and combining Unicode chars.
|
||||
"""
|
||||
width = sum(east_asian_widths[unicodedata.east_asian_width(c)]
|
||||
for c in text)
|
||||
# correction for combining chars:
|
||||
width -= len(find_combining_chars(text))
|
||||
return width
|
||||
|
||||
|
||||
def uniq(L):
|
||||
r = []
|
||||
for item in L:
|
||||
if item not in r:
|
||||
r.append(item)
|
||||
return r
|
||||
|
||||
|
||||
def normalize_language_tag(tag):
|
||||
"""Return a list of normalized combinations for a `BCP 47` language tag.
|
||||
|
||||
Example:
|
||||
|
||||
>>> from docutils.utils import normalize_language_tag
|
||||
>>> normalize_language_tag('de_AT-1901')
|
||||
['de-at-1901', 'de-at', 'de-1901', 'de']
|
||||
>>> normalize_language_tag('de-CH-x_altquot')
|
||||
['de-ch-x-altquot', 'de-ch', 'de-x-altquot', 'de']
|
||||
|
||||
"""
|
||||
# normalize:
|
||||
tag = tag.lower().replace('-', '_')
|
||||
# split (except singletons, which mark the following tag as non-standard):
|
||||
tag = re.sub(r'_([a-zA-Z0-9])_', r'_\1-', tag)
|
||||
subtags = [subtag for subtag in tag.split('_')]
|
||||
base_tag = (subtags.pop(0),)
|
||||
# find all combinations of subtags
|
||||
taglist = []
|
||||
for n in range(len(subtags), 0, -1):
|
||||
for tags in itertools.combinations(subtags, n):
|
||||
taglist.append('-'.join(base_tag+tags))
|
||||
taglist += base_tag
|
||||
return taglist
|
||||
|
||||
|
||||
def xml_declaration(encoding=None):
|
||||
"""Return an XML text declaration.
|
||||
|
||||
Include an encoding declaration, if `encoding`
|
||||
is not 'unicode', '', or None.
|
||||
"""
|
||||
if encoding and encoding.lower() != 'unicode':
|
||||
encoding_declaration = f' encoding="{encoding}"'
|
||||
else:
|
||||
encoding_declaration = ''
|
||||
return f'<?xml version="1.0"{encoding_declaration}?>\n'
|
||||
|
||||
|
||||
class DependencyList:
|
||||
|
||||
"""
|
||||
List of dependencies, with file recording support.
|
||||
|
||||
Note that the output file is not automatically closed. You have
|
||||
to explicitly call the close() method.
|
||||
"""
|
||||
|
||||
def __init__(self, output_file=None, dependencies=()):
|
||||
"""
|
||||
Initialize the dependency list, automatically setting the
|
||||
output file to `output_file` (see `set_output()`) and adding
|
||||
all supplied dependencies.
|
||||
|
||||
If output_file is None, no file output is done when calling add().
|
||||
"""
|
||||
self.list = []
|
||||
self.file = None
|
||||
if output_file:
|
||||
self.set_output(output_file)
|
||||
self.add(*dependencies)
|
||||
|
||||
def set_output(self, output_file):
|
||||
"""
|
||||
Set the output file and clear the list of already added
|
||||
dependencies.
|
||||
|
||||
`output_file` must be a string. The specified file is
|
||||
immediately overwritten.
|
||||
|
||||
If output_file is '-', the output will be written to stdout.
|
||||
"""
|
||||
if output_file:
|
||||
if output_file == '-':
|
||||
self.file = sys.stdout
|
||||
else:
|
||||
self.file = open(output_file, 'w', encoding='utf-8')
|
||||
|
||||
def add(self, *paths):
|
||||
"""
|
||||
Append `path` to `self.list` unless it is already there.
|
||||
|
||||
Also append to `self.file` unless it is already there
|
||||
or `self.file is `None`.
|
||||
"""
|
||||
for path in paths:
|
||||
if isinstance(path, PurePath):
|
||||
path = path.as_posix() # use '/' as separator
|
||||
if path not in self.list:
|
||||
self.list.append(path)
|
||||
if self.file is not None:
|
||||
self.file.write(path+'\n')
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close the output file.
|
||||
"""
|
||||
if self.file is not sys.stdout:
|
||||
self.file.close()
|
||||
self.file = None
|
||||
|
||||
def __repr__(self):
|
||||
try:
|
||||
output_file = self.file.name
|
||||
except AttributeError:
|
||||
output_file = None
|
||||
return '%s(%r, %s)' % (self.__class__.__name__, output_file, self.list)
|
||||
|
||||
|
||||
release_level_abbreviations = {
|
||||
'alpha': 'a',
|
||||
'beta': 'b',
|
||||
'candidate': 'rc',
|
||||
'final': ''}
|
||||
|
||||
|
||||
def version_identifier(version_info=None):
|
||||
"""
|
||||
Return a version identifier string built from `version_info`, a
|
||||
`docutils.VersionInfo` namedtuple instance or compatible tuple. If
|
||||
`version_info` is not provided, by default return a version identifier
|
||||
string based on `docutils.__version_info__` (i.e. the current Docutils
|
||||
version).
|
||||
"""
|
||||
if version_info is None:
|
||||
version_info = __version_info__
|
||||
if version_info.micro:
|
||||
micro = '.%s' % version_info.micro
|
||||
else:
|
||||
# 0 is omitted:
|
||||
micro = ''
|
||||
releaselevel = release_level_abbreviations[version_info.releaselevel]
|
||||
if version_info.serial:
|
||||
serial = version_info.serial
|
||||
else:
|
||||
# 0 is omitted:
|
||||
serial = ''
|
||||
if version_info.release:
|
||||
dev = ''
|
||||
else:
|
||||
dev = '.dev'
|
||||
version = '%s.%s%s%s%s%s' % (
|
||||
version_info.major,
|
||||
version_info.minor,
|
||||
micro,
|
||||
releaselevel,
|
||||
serial,
|
||||
dev)
|
||||
return version
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,136 @@
|
||||
# :Author: Georg Brandl; Lea Wiemann; Günter Milde
|
||||
# :Date: $Date: 2022-11-16 15:01:31 +0100 (Mi, 16. Nov 2022) $
|
||||
# :Copyright: This module has been placed in the public domain.
|
||||
|
||||
"""Lexical analysis of formal languages (i.e. code) using Pygments."""
|
||||
|
||||
from docutils import ApplicationError
|
||||
try:
|
||||
import pygments
|
||||
from pygments.lexers import get_lexer_by_name
|
||||
from pygments.formatters.html import _get_ttype_class
|
||||
with_pygments = True
|
||||
except ImportError:
|
||||
with_pygments = False
|
||||
|
||||
# Filter the following token types from the list of class arguments:
|
||||
unstyled_tokens = ['token', # Token (base token type)
|
||||
'text', # Token.Text
|
||||
''] # short name for Token and Text
|
||||
# (Add, e.g., Token.Punctuation with ``unstyled_tokens += 'punctuation'``.)
|
||||
|
||||
|
||||
class LexerError(ApplicationError):
|
||||
pass
|
||||
|
||||
|
||||
class Lexer:
|
||||
"""Parse `code` lines and yield "classified" tokens.
|
||||
|
||||
Arguments
|
||||
|
||||
code -- string of source code to parse,
|
||||
language -- formal language the code is written in,
|
||||
tokennames -- either 'long', 'short', or 'none' (see below).
|
||||
|
||||
Merge subsequent tokens of the same token-type.
|
||||
|
||||
Iterating over an instance yields the tokens as ``(tokentype, value)``
|
||||
tuples. The value of `tokennames` configures the naming of the tokentype:
|
||||
|
||||
'long': downcased full token type name,
|
||||
'short': short name defined by pygments.token.STANDARD_TYPES
|
||||
(= class argument used in pygments html output),
|
||||
'none': skip lexical analysis.
|
||||
"""
|
||||
|
||||
def __init__(self, code, language, tokennames='short'):
|
||||
"""
|
||||
Set up a lexical analyzer for `code` in `language`.
|
||||
"""
|
||||
self.code = code
|
||||
self.language = language
|
||||
self.tokennames = tokennames
|
||||
self.lexer = None
|
||||
# get lexical analyzer for `language`:
|
||||
if language in ('', 'text') or tokennames == 'none':
|
||||
return
|
||||
if not with_pygments:
|
||||
raise LexerError('Cannot analyze code. '
|
||||
'Pygments package not found.')
|
||||
try:
|
||||
self.lexer = get_lexer_by_name(self.language)
|
||||
except pygments.util.ClassNotFound:
|
||||
raise LexerError('Cannot analyze code. '
|
||||
'No Pygments lexer found for "%s".' % language)
|
||||
# self.lexer.add_filter('tokenmerge')
|
||||
# Since version 1.2. (released Jan 01, 2010) Pygments has a
|
||||
# TokenMergeFilter. # ``self.merge(tokens)`` in __iter__ could
|
||||
# be replaced by ``self.lexer.add_filter('tokenmerge')`` in __init__.
|
||||
# However, `merge` below also strips a final newline added by pygments.
|
||||
#
|
||||
# self.lexer.add_filter('tokenmerge')
|
||||
|
||||
def merge(self, tokens):
|
||||
"""Merge subsequent tokens of same token-type.
|
||||
|
||||
Also strip the final newline (added by pygments).
|
||||
"""
|
||||
tokens = iter(tokens)
|
||||
(lasttype, lastval) = next(tokens)
|
||||
for ttype, value in tokens:
|
||||
if ttype is lasttype:
|
||||
lastval += value
|
||||
else:
|
||||
yield lasttype, lastval
|
||||
(lasttype, lastval) = (ttype, value)
|
||||
if lastval.endswith('\n'):
|
||||
lastval = lastval[:-1]
|
||||
if lastval:
|
||||
yield lasttype, lastval
|
||||
|
||||
def __iter__(self):
|
||||
"""Parse self.code and yield "classified" tokens.
|
||||
"""
|
||||
if self.lexer is None:
|
||||
yield [], self.code
|
||||
return
|
||||
tokens = pygments.lex(self.code, self.lexer)
|
||||
for tokentype, value in self.merge(tokens):
|
||||
if self.tokennames == 'long': # long CSS class args
|
||||
classes = str(tokentype).lower().split('.')
|
||||
else: # short CSS class args
|
||||
classes = [_get_ttype_class(tokentype)]
|
||||
classes = [cls for cls in classes if cls not in unstyled_tokens]
|
||||
yield classes, value
|
||||
|
||||
|
||||
class NumberLines:
|
||||
"""Insert linenumber-tokens at the start of every code line.
|
||||
|
||||
Arguments
|
||||
|
||||
tokens -- iterable of ``(classes, value)`` tuples
|
||||
startline -- first line number
|
||||
endline -- last line number
|
||||
|
||||
Iterating over an instance yields the tokens with a
|
||||
``(['ln'], '<the line number>')`` token added for every code line.
|
||||
Multi-line tokens are split."""
|
||||
|
||||
def __init__(self, tokens, startline, endline):
|
||||
self.tokens = tokens
|
||||
self.startline = startline
|
||||
# pad linenumbers, e.g. endline == 100 -> fmt_str = '%3d '
|
||||
self.fmt_str = '%%%dd ' % len(str(endline))
|
||||
|
||||
def __iter__(self):
|
||||
lineno = self.startline
|
||||
yield ['ln'], self.fmt_str % lineno
|
||||
for ttype, value in self.tokens:
|
||||
lines = value.split('\n')
|
||||
for line in lines[:-1]:
|
||||
yield ttype, line + '\n'
|
||||
lineno += 1
|
||||
yield ['ln'], self.fmt_str % lineno
|
||||
yield ttype, lines[-1]
|
||||
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env python3
|
||||
# :Id: $Id: error_reporting.py 9078 2022-06-17 11:31:40Z milde $
|
||||
# :Copyright: © 2011 Günter Milde.
|
||||
# :License: Released under the terms of the `2-Clause BSD license`_, in short:
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved.
|
||||
# This file is offered as-is, without any warranty.
|
||||
#
|
||||
# .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause
|
||||
|
||||
"""
|
||||
Deprecated module to handle Exceptions across Python versions.
|
||||
|
||||
.. warning::
|
||||
This module is deprecated with the end of support for Python 2.7
|
||||
and will be removed in Docutils 0.21 or later.
|
||||
|
||||
Replacements:
|
||||
| SafeString -> str
|
||||
| ErrorString -> docutils.io.error_string()
|
||||
| ErrorOutput -> docutils.io.ErrorOutput
|
||||
|
||||
Error reporting should be safe from encoding/decoding errors.
|
||||
However, implicit conversions of strings and exceptions like
|
||||
|
||||
>>> u'%s world: %s' % ('H\xe4llo', Exception(u'H\xe4llo'))
|
||||
|
||||
fail in some Python versions:
|
||||
|
||||
* In Python <= 2.6, ``unicode(<exception instance>)`` uses
|
||||
`__str__` and fails with non-ASCII chars in`unicode` arguments.
|
||||
(work around http://bugs.python.org/issue2517):
|
||||
|
||||
* In Python 2, unicode(<exception instance>) fails, with non-ASCII
|
||||
chars in arguments. (Use case: in some locales, the errstr
|
||||
argument of IOError contains non-ASCII chars.)
|
||||
|
||||
* In Python 2, str(<exception instance>) fails, with non-ASCII chars
|
||||
in `unicode` arguments.
|
||||
|
||||
The `SafeString`, `ErrorString` and `ErrorOutput` classes handle
|
||||
common exceptions.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from docutils.io import _locale_encoding as locale_encoding # noqa
|
||||
|
||||
warnings.warn('The `docutils.utils.error_reporting` module is deprecated '
|
||||
'and will be removed in Docutils 0.21 or later.\n'
|
||||
'Details with help("docutils.utils.error_reporting").',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
|
||||
|
||||
if sys.version_info >= (3, 0):
|
||||
unicode = str # noqa
|
||||
|
||||
|
||||
class SafeString:
|
||||
"""
|
||||
A wrapper providing robust conversion to `str` and `unicode`.
|
||||
"""
|
||||
|
||||
def __init__(self, data, encoding=None, encoding_errors='backslashreplace',
|
||||
decoding_errors='replace'):
|
||||
self.data = data
|
||||
self.encoding = (encoding or getattr(data, 'encoding', None)
|
||||
or locale_encoding or 'ascii')
|
||||
self.encoding_errors = encoding_errors
|
||||
self.decoding_errors = decoding_errors
|
||||
|
||||
def __str__(self):
|
||||
try:
|
||||
return str(self.data)
|
||||
except UnicodeEncodeError:
|
||||
if isinstance(self.data, Exception):
|
||||
args = [str(SafeString(arg, self.encoding,
|
||||
self.encoding_errors))
|
||||
for arg in self.data.args]
|
||||
return ', '.join(args)
|
||||
if isinstance(self.data, unicode):
|
||||
if sys.version_info > (3, 0):
|
||||
return self.data
|
||||
else:
|
||||
return self.data.encode(self.encoding,
|
||||
self.encoding_errors)
|
||||
raise
|
||||
|
||||
def __unicode__(self):
|
||||
"""
|
||||
Return unicode representation of `self.data`.
|
||||
|
||||
Try ``unicode(self.data)``, catch `UnicodeError` and
|
||||
|
||||
* if `self.data` is an Exception instance, work around
|
||||
http://bugs.python.org/issue2517 with an emulation of
|
||||
Exception.__unicode__,
|
||||
|
||||
* else decode with `self.encoding` and `self.decoding_errors`.
|
||||
"""
|
||||
try:
|
||||
u = unicode(self.data)
|
||||
if isinstance(self.data, EnvironmentError):
|
||||
u = u.replace(": u'", ": '") # normalize filename quoting
|
||||
return u
|
||||
except UnicodeError as error: # catch ..Encode.. and ..Decode.. errors
|
||||
if isinstance(self.data, EnvironmentError):
|
||||
return "[Errno %s] %s: '%s'" % (
|
||||
self.data.errno,
|
||||
SafeString(self.data.strerror, self.encoding,
|
||||
self.decoding_errors),
|
||||
SafeString(self.data.filename, self.encoding,
|
||||
self.decoding_errors))
|
||||
if isinstance(self.data, Exception):
|
||||
args = [unicode(SafeString(
|
||||
arg, self.encoding,
|
||||
decoding_errors=self.decoding_errors))
|
||||
for arg in self.data.args]
|
||||
return u', '.join(args)
|
||||
if isinstance(error, UnicodeDecodeError):
|
||||
return unicode(self.data, self.encoding, self.decoding_errors)
|
||||
raise
|
||||
|
||||
|
||||
class ErrorString(SafeString):
|
||||
"""
|
||||
Safely report exception type and message.
|
||||
"""
|
||||
def __str__(self):
|
||||
return '%s: %s' % (self.data.__class__.__name__,
|
||||
super(ErrorString, self).__str__())
|
||||
|
||||
def __unicode__(self):
|
||||
return u'%s: %s' % (self.data.__class__.__name__,
|
||||
super(ErrorString, self).__unicode__())
|
||||
|
||||
|
||||
class ErrorOutput:
|
||||
"""
|
||||
Wrapper class for file-like error streams with
|
||||
failsafe de- and encoding of `str`, `bytes`, `unicode` and
|
||||
`Exception` instances.
|
||||
"""
|
||||
|
||||
def __init__(self, stream=None, encoding=None,
|
||||
encoding_errors='backslashreplace',
|
||||
decoding_errors='replace'):
|
||||
"""
|
||||
:Parameters:
|
||||
- `stream`: a file-like object,
|
||||
a string (path to a file),
|
||||
`None` (write to `sys.stderr`, default), or
|
||||
evaluating to `False` (write() requests are ignored).
|
||||
- `encoding`: `stream` text encoding. Guessed if None.
|
||||
- `encoding_errors`: how to treat encoding errors.
|
||||
"""
|
||||
if stream is None:
|
||||
stream = sys.stderr
|
||||
elif not stream:
|
||||
stream = False
|
||||
# if `stream` is a file name, open it
|
||||
elif isinstance(stream, str):
|
||||
stream = open(stream, 'w')
|
||||
elif isinstance(stream, unicode):
|
||||
stream = open(stream.encode(sys.getfilesystemencoding()), 'w')
|
||||
|
||||
self.stream = stream
|
||||
"""Where warning output is sent."""
|
||||
|
||||
self.encoding = (encoding or getattr(stream, 'encoding', None)
|
||||
or locale_encoding or 'ascii')
|
||||
"""The output character encoding."""
|
||||
|
||||
self.encoding_errors = encoding_errors
|
||||
"""Encoding error handler."""
|
||||
|
||||
self.decoding_errors = decoding_errors
|
||||
"""Decoding error handler."""
|
||||
|
||||
def write(self, data):
|
||||
"""
|
||||
Write `data` to self.stream. Ignore, if self.stream is False.
|
||||
|
||||
`data` can be a `string`, `unicode`, or `Exception` instance.
|
||||
"""
|
||||
if self.stream is False:
|
||||
return
|
||||
if isinstance(data, Exception):
|
||||
data = unicode(SafeString(data, self.encoding,
|
||||
self.encoding_errors,
|
||||
self.decoding_errors))
|
||||
try:
|
||||
self.stream.write(data)
|
||||
except UnicodeEncodeError:
|
||||
self.stream.write(data.encode(self.encoding, self.encoding_errors))
|
||||
except TypeError:
|
||||
if isinstance(data, unicode): # passed stream may expect bytes
|
||||
self.stream.write(data.encode(self.encoding,
|
||||
self.encoding_errors))
|
||||
return
|
||||
if self.stream in (sys.stderr, sys.stdout):
|
||||
self.stream.buffer.write(data) # write bytes to raw stream
|
||||
else:
|
||||
self.stream.write(unicode(data, self.encoding,
|
||||
self.decoding_errors))
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close the error-output stream.
|
||||
|
||||
Ignored if the stream is` sys.stderr` or `sys.stdout` or has no
|
||||
close() method.
|
||||
"""
|
||||
if self.stream in (sys.stdout, sys.stderr):
|
||||
return
|
||||
try:
|
||||
self.stream.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
@@ -0,0 +1,52 @@
|
||||
# :Id: $Id: __init__.py 9026 2022-03-04 15:57:13Z milde $
|
||||
# :Author: Guenter Milde.
|
||||
# :License: Released under the terms of the `2-Clause BSD license`_, in short:
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved.
|
||||
# This file is offered as-is, without any warranty.
|
||||
#
|
||||
# .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause
|
||||
|
||||
"""
|
||||
This is the Docutils (Python Documentation Utilities) "math" sub-package.
|
||||
|
||||
It contains various modules for conversion between different math formats
|
||||
(LaTeX, MathML, HTML).
|
||||
|
||||
:math2html: LaTeX math -> HTML conversion from eLyXer
|
||||
:latex2mathml: LaTeX math -> presentational MathML
|
||||
:unichar2tex: Unicode character to LaTeX math translation table
|
||||
:tex2unichar: LaTeX math to Unicode character translation dictionaries
|
||||
:tex2mathml_extern: Wrapper for 3rd party TeX -> MathML converters
|
||||
"""
|
||||
|
||||
# helpers for Docutils math support
|
||||
# =================================
|
||||
|
||||
|
||||
def toplevel_code(code):
|
||||
"""Return string (LaTeX math) `code` with environments stripped out."""
|
||||
chunks = code.split(r'\begin{')
|
||||
return r'\begin{'.join(chunk.split(r'\end{')[-1]
|
||||
for chunk in chunks)
|
||||
|
||||
|
||||
def pick_math_environment(code, numbered=False):
|
||||
"""Return the right math environment to display `code`.
|
||||
|
||||
The test simply looks for line-breaks (``\\``) outside environments.
|
||||
Multi-line formulae are set with ``align``, one-liners with
|
||||
``equation``.
|
||||
|
||||
If `numbered` evaluates to ``False``, the "starred" versions are used
|
||||
to suppress numbering.
|
||||
"""
|
||||
if toplevel_code(code).find(r'\\') >= 0:
|
||||
env = 'align'
|
||||
else:
|
||||
env = 'equation'
|
||||
if not numbered:
|
||||
env += '*'
|
||||
return env
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,185 @@
|
||||
# :Id: $Id: tex2mathml_extern.py 9250 2022-11-16 14:01:31Z milde $
|
||||
# :Copyright: © 2015 Günter Milde.
|
||||
# :License: Released under the terms of the `2-Clause BSD license`_, in short:
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved.
|
||||
# This file is offered as-is, without any warranty.
|
||||
#
|
||||
# .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause
|
||||
|
||||
"""Wrappers for TeX->MathML conversion by external tools
|
||||
|
||||
This module is provisional:
|
||||
the API is not settled and may change with any minor Docutils version.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
|
||||
document_template = r"""\documentclass{article}
|
||||
\usepackage{amsmath}
|
||||
\begin{document}
|
||||
%s
|
||||
\end{document}
|
||||
"""
|
||||
|
||||
|
||||
def latexml(math_code, reporter=None):
|
||||
"""Convert LaTeX math code to MathML with LaTeXML_
|
||||
|
||||
.. _LaTeXML: http://dlmf.nist.gov/LaTeXML/
|
||||
"""
|
||||
p = subprocess.Popen(['latexml',
|
||||
'-', # read from stdin
|
||||
'--preload=amsfonts',
|
||||
'--preload=amsmath',
|
||||
'--inputencoding=utf8',
|
||||
],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
close_fds=True)
|
||||
p.stdin.write((document_template % math_code).encode('utf-8'))
|
||||
p.stdin.close()
|
||||
latexml_code = p.stdout.read()
|
||||
latexml_err = p.stderr.read().decode('utf-8')
|
||||
if reporter and (latexml_err.find('Error') >= 0 or not latexml_code):
|
||||
reporter.error(latexml_err)
|
||||
|
||||
post_p = subprocess.Popen(['latexmlpost',
|
||||
'-',
|
||||
'--nonumbersections',
|
||||
'--format=xhtml',
|
||||
# '--linelength=78', # experimental
|
||||
'--'
|
||||
],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
close_fds=True)
|
||||
post_p.stdin.write(latexml_code)
|
||||
post_p.stdin.close()
|
||||
result = post_p.stdout.read().decode('utf-8')
|
||||
post_p_err = post_p.stderr.read().decode('utf-8')
|
||||
if reporter and (post_p_err.find('Error') >= 0 or not result):
|
||||
reporter.error(post_p_err)
|
||||
|
||||
# extract MathML code:
|
||||
start, end = result.find('<math'), result.find('</math>')+7
|
||||
result = result[start:end]
|
||||
if 'class="ltx_ERROR' in result:
|
||||
raise SyntaxError(result)
|
||||
return result
|
||||
|
||||
|
||||
def ttm(math_code, reporter=None):
|
||||
"""Convert LaTeX math code to MathML with TtM_
|
||||
|
||||
.. _TtM: http://hutchinson.belmont.ma.us/tth/mml/
|
||||
"""
|
||||
p = subprocess.Popen(['ttm',
|
||||
# '-i', # italic font for equations. Default roman.
|
||||
'-u', # unicode encoding. (Default iso-8859-1).
|
||||
'-r', # output raw MathML (no wrapper)
|
||||
],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
close_fds=True)
|
||||
p.stdin.write((document_template % math_code).encode('utf-8'))
|
||||
p.stdin.close()
|
||||
result = p.stdout.read()
|
||||
err = p.stderr.read().decode('utf-8')
|
||||
if err.find('**** Unknown') >= 0:
|
||||
msg = '\n'.join(line for line in err.splitlines()
|
||||
if line.startswith('****'))
|
||||
raise SyntaxError('\nMessage from external converter TtM:\n'+msg)
|
||||
if reporter and err.find('**** Error') >= 0 or not result:
|
||||
reporter.error(err)
|
||||
start, end = result.find('<math'), result.find('</math>')+7
|
||||
return result[start:end]
|
||||
|
||||
|
||||
def blahtexml(math_code, inline=True, reporter=None):
|
||||
"""Convert LaTeX math code to MathML with blahtexml_
|
||||
|
||||
.. _blahtexml: http://gva.noekeon.org/blahtexml/
|
||||
"""
|
||||
options = ['--mathml',
|
||||
'--indented',
|
||||
'--spacing', 'moderate',
|
||||
'--mathml-encoding', 'raw',
|
||||
'--other-encoding', 'raw',
|
||||
'--doctype-xhtml+mathml',
|
||||
'--annotate-TeX',
|
||||
]
|
||||
if inline:
|
||||
mathmode_arg = ''
|
||||
else:
|
||||
mathmode_arg = ' display="block"'
|
||||
options.append('--displaymath')
|
||||
|
||||
p = subprocess.Popen(['blahtexml']+options,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
close_fds=True)
|
||||
p.stdin.write(math_code.encode('utf-8'))
|
||||
p.stdin.close()
|
||||
result = p.stdout.read().decode('utf-8')
|
||||
err = p.stderr.read().decode('utf-8')
|
||||
|
||||
if result.find('<error>') >= 0:
|
||||
msg = result[result.find('<message>')+9:result.find('</message>')]
|
||||
raise SyntaxError('\nMessage from external converter blahtexml:\n%s'
|
||||
% msg)
|
||||
if reporter and (err.find('**** Error') >= 0 or not result):
|
||||
reporter.error(err)
|
||||
start, end = result.find('<markup>')+9, result.find('</markup>')
|
||||
result = ('<math xmlns="http://www.w3.org/1998/Math/MathML"%s>\n'
|
||||
'%s</math>\n') % (mathmode_arg, result[start:end])
|
||||
return result
|
||||
|
||||
|
||||
def pandoc(math_code, reporter=None):
|
||||
"""Convert LaTeX math code to MathML with pandoc_
|
||||
|
||||
.. _pandoc: https://pandoc.org/
|
||||
"""
|
||||
p = subprocess.Popen(['pandoc',
|
||||
'--mathml',
|
||||
'--from=latex',
|
||||
],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
close_fds=True)
|
||||
p.stdin.write(math_code.encode('utf-8'))
|
||||
p.stdin.close()
|
||||
result = p.stdout.read().decode('utf-8')
|
||||
err = p.stderr.read().decode('utf-8').strip()
|
||||
x = p.wait()
|
||||
|
||||
if err:
|
||||
if reporter:
|
||||
reporter.error(err)
|
||||
raise SyntaxError('\nError message from external converter pandoc:\n%s'
|
||||
% err)
|
||||
if x != 0:
|
||||
raise SyntaxError('\nError code from external converter pandoc:\n%s'
|
||||
% x)
|
||||
|
||||
start, end = result.find('<math'), result.find('</math>')+7
|
||||
return result[start:end]
|
||||
|
||||
|
||||
# self-test
|
||||
|
||||
if __name__ == "__main__":
|
||||
example = ('\\frac{\\partial \\sin^2(\\alpha)}{\\partial \\vec r}'
|
||||
'\\varpi \\mathbb{R} \\, \\text{Grüße}')
|
||||
# print(latexml('$'+example+'$'))
|
||||
# print(ttm('$'+example.replace('\\mathbb{R}', '')+'$'))
|
||||
print(blahtexml(example))
|
||||
# print(pandoc('$'+example+'$'))
|
||||
@@ -0,0 +1,730 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# LaTeX math to Unicode symbols translation dictionaries.
|
||||
# Generated with ``write_tex2unichar.py`` from the data in
|
||||
# http://milde.users.sourceforge.net/LUCR/Math/
|
||||
|
||||
# Includes commands from:
|
||||
# standard LaTeX
|
||||
# amssymb
|
||||
# amsmath
|
||||
# amsxtra
|
||||
# bbold
|
||||
# esint
|
||||
# mathabx
|
||||
# mathdots
|
||||
# txfonts
|
||||
# stmaryrd
|
||||
# wasysym
|
||||
|
||||
mathaccent = {
|
||||
'acute': '\u0301', # ́ COMBINING ACUTE ACCENT
|
||||
'bar': '\u0304', # ̄ COMBINING MACRON
|
||||
'breve': '\u0306', # ̆ COMBINING BREVE
|
||||
'check': '\u030c', # ̌ COMBINING CARON
|
||||
'ddddot': '\u20dc', # ⃜ COMBINING FOUR DOTS ABOVE
|
||||
'dddot': '\u20db', # ⃛ COMBINING THREE DOTS ABOVE
|
||||
'ddot': '\u0308', # ̈ COMBINING DIAERESIS
|
||||
'dot': '\u0307', # ̇ COMBINING DOT ABOVE
|
||||
'grave': '\u0300', # ̀ COMBINING GRAVE ACCENT
|
||||
'hat': '\u0302', # ̂ COMBINING CIRCUMFLEX ACCENT
|
||||
'mathring': '\u030a', # ̊ COMBINING RING ABOVE
|
||||
'not': '\u0338', # ̸ COMBINING LONG SOLIDUS OVERLAY
|
||||
'overleftrightarrow': '\u20e1', # ⃡ COMBINING LEFT RIGHT ARROW ABOVE
|
||||
'overline': '\u0305', # ̅ COMBINING OVERLINE
|
||||
'tilde': '\u0303', # ̃ COMBINING TILDE
|
||||
'underbar': '\u0331', # ̱ COMBINING MACRON BELOW
|
||||
'underleftarrow': '\u20ee', # ⃮ COMBINING LEFT ARROW BELOW
|
||||
'underline': '\u0332', # ̲ COMBINING LOW LINE
|
||||
'underrightarrow': '\u20ef', # ⃯ COMBINING RIGHT ARROW BELOW
|
||||
'vec': '\u20d7', # ⃗ COMBINING RIGHT ARROW ABOVE
|
||||
}
|
||||
|
||||
mathalpha = {
|
||||
'Bbbk': '\U0001d55c', # 𝕜 MATHEMATICAL DOUBLE-STRUCK SMALL K
|
||||
'Delta': '\u0394', # Δ GREEK CAPITAL LETTER DELTA
|
||||
'Gamma': '\u0393', # Γ GREEK CAPITAL LETTER GAMMA
|
||||
'Im': '\u2111', # ℑ BLACK-LETTER CAPITAL I
|
||||
'Lambda': '\u039b', # Λ GREEK CAPITAL LETTER LAMDA
|
||||
'Omega': '\u03a9', # Ω GREEK CAPITAL LETTER OMEGA
|
||||
'Phi': '\u03a6', # Φ GREEK CAPITAL LETTER PHI
|
||||
'Pi': '\u03a0', # Π GREEK CAPITAL LETTER PI
|
||||
'Psi': '\u03a8', # Ψ GREEK CAPITAL LETTER PSI
|
||||
'Re': '\u211c', # ℜ BLACK-LETTER CAPITAL R
|
||||
'Sigma': '\u03a3', # Σ GREEK CAPITAL LETTER SIGMA
|
||||
'Theta': '\u0398', # Θ GREEK CAPITAL LETTER THETA
|
||||
'Upsilon': '\u03a5', # Υ GREEK CAPITAL LETTER UPSILON
|
||||
'Xi': '\u039e', # Ξ GREEK CAPITAL LETTER XI
|
||||
'aleph': '\u2135', # ℵ ALEF SYMBOL
|
||||
'alpha': '\u03b1', # α GREEK SMALL LETTER ALPHA
|
||||
'beta': '\u03b2', # β GREEK SMALL LETTER BETA
|
||||
'beth': '\u2136', # ℶ BET SYMBOL
|
||||
'chi': '\u03c7', # χ GREEK SMALL LETTER CHI
|
||||
'daleth': '\u2138', # ℸ DALET SYMBOL
|
||||
'delta': '\u03b4', # δ GREEK SMALL LETTER DELTA
|
||||
'digamma': '\u03dd', # ϝ GREEK SMALL LETTER DIGAMMA
|
||||
'ell': '\u2113', # ℓ SCRIPT SMALL L
|
||||
'epsilon': '\u03f5', # ϵ GREEK LUNATE EPSILON SYMBOL
|
||||
'eta': '\u03b7', # η GREEK SMALL LETTER ETA
|
||||
'eth': '\xf0', # ð LATIN SMALL LETTER ETH
|
||||
'gamma': '\u03b3', # γ GREEK SMALL LETTER GAMMA
|
||||
'gimel': '\u2137', # ℷ GIMEL SYMBOL
|
||||
'hslash': '\u210f', # ℏ PLANCK CONSTANT OVER TWO PI
|
||||
'imath': '\u0131', # ı LATIN SMALL LETTER DOTLESS I
|
||||
'iota': '\u03b9', # ι GREEK SMALL LETTER IOTA
|
||||
'jmath': '\u0237', # ȷ LATIN SMALL LETTER DOTLESS J
|
||||
'kappa': '\u03ba', # κ GREEK SMALL LETTER KAPPA
|
||||
'lambda': '\u03bb', # λ GREEK SMALL LETTER LAMDA
|
||||
'mu': '\u03bc', # μ GREEK SMALL LETTER MU
|
||||
'nu': '\u03bd', # ν GREEK SMALL LETTER NU
|
||||
'omega': '\u03c9', # ω GREEK SMALL LETTER OMEGA
|
||||
'phi': '\u03d5', # ϕ GREEK PHI SYMBOL
|
||||
'pi': '\u03c0', # π GREEK SMALL LETTER PI
|
||||
'psi': '\u03c8', # ψ GREEK SMALL LETTER PSI
|
||||
'rho': '\u03c1', # ρ GREEK SMALL LETTER RHO
|
||||
'sigma': '\u03c3', # σ GREEK SMALL LETTER SIGMA
|
||||
'tau': '\u03c4', # τ GREEK SMALL LETTER TAU
|
||||
'theta': '\u03b8', # θ GREEK SMALL LETTER THETA
|
||||
'upsilon': '\u03c5', # υ GREEK SMALL LETTER UPSILON
|
||||
'varDelta': '\U0001d6e5', # 𝛥 MATHEMATICAL ITALIC CAPITAL DELTA
|
||||
'varGamma': '\U0001d6e4', # 𝛤 MATHEMATICAL ITALIC CAPITAL GAMMA
|
||||
'varLambda': '\U0001d6ec', # 𝛬 MATHEMATICAL ITALIC CAPITAL LAMDA
|
||||
'varOmega': '\U0001d6fa', # 𝛺 MATHEMATICAL ITALIC CAPITAL OMEGA
|
||||
'varPhi': '\U0001d6f7', # 𝛷 MATHEMATICAL ITALIC CAPITAL PHI
|
||||
'varPi': '\U0001d6f1', # 𝛱 MATHEMATICAL ITALIC CAPITAL PI
|
||||
'varPsi': '\U0001d6f9', # 𝛹 MATHEMATICAL ITALIC CAPITAL PSI
|
||||
'varSigma': '\U0001d6f4', # 𝛴 MATHEMATICAL ITALIC CAPITAL SIGMA
|
||||
'varTheta': '\U0001d6e9', # 𝛩 MATHEMATICAL ITALIC CAPITAL THETA
|
||||
'varUpsilon': '\U0001d6f6', # 𝛶 MATHEMATICAL ITALIC CAPITAL UPSILON
|
||||
'varXi': '\U0001d6ef', # 𝛯 MATHEMATICAL ITALIC CAPITAL XI
|
||||
'varepsilon': '\u03b5', # ε GREEK SMALL LETTER EPSILON
|
||||
'varkappa': '\u03f0', # ϰ GREEK KAPPA SYMBOL
|
||||
'varphi': '\u03c6', # φ GREEK SMALL LETTER PHI
|
||||
'varpi': '\u03d6', # ϖ GREEK PI SYMBOL
|
||||
'varrho': '\u03f1', # ϱ GREEK RHO SYMBOL
|
||||
'varsigma': '\u03c2', # ς GREEK SMALL LETTER FINAL SIGMA
|
||||
'vartheta': '\u03d1', # ϑ GREEK THETA SYMBOL
|
||||
'wp': '\u2118', # ℘ SCRIPT CAPITAL P
|
||||
'xi': '\u03be', # ξ GREEK SMALL LETTER XI
|
||||
'zeta': '\u03b6', # ζ GREEK SMALL LETTER ZETA
|
||||
}
|
||||
|
||||
mathbin = {
|
||||
'Cap': '\u22d2', # ⋒ DOUBLE INTERSECTION
|
||||
'Circle': '\u25cb', # ○ WHITE CIRCLE
|
||||
'Cup': '\u22d3', # ⋓ DOUBLE UNION
|
||||
'LHD': '\u25c0', # ◀ BLACK LEFT-POINTING TRIANGLE
|
||||
'RHD': '\u25b6', # ▶ BLACK RIGHT-POINTING TRIANGLE
|
||||
'amalg': '\u2a3f', # ⨿ AMALGAMATION OR COPRODUCT
|
||||
'ast': '\u2217', # ∗ ASTERISK OPERATOR
|
||||
'barwedge': '\u22bc', # ⊼ NAND
|
||||
'bigcirc': '\u25ef', # ◯ LARGE CIRCLE
|
||||
'bigtriangledown': '\u25bd', # ▽ WHITE DOWN-POINTING TRIANGLE
|
||||
'bigtriangleup': '\u25b3', # △ WHITE UP-POINTING TRIANGLE
|
||||
'bindnasrepma': '\u214b', # ⅋ TURNED AMPERSAND
|
||||
'blacklozenge': '\u29eb', # ⧫ BLACK LOZENGE
|
||||
'boxast': '\u29c6', # ⧆ SQUARED ASTERISK
|
||||
'boxbar': '\u25eb', # ◫ WHITE SQUARE WITH VERTICAL BISECTING LINE
|
||||
'boxbox': '\u29c8', # ⧈ SQUARED SQUARE
|
||||
'boxbslash': '\u29c5', # ⧅ SQUARED FALLING DIAGONAL SLASH
|
||||
'boxcircle': '\u29c7', # ⧇ SQUARED SMALL CIRCLE
|
||||
'boxdot': '\u22a1', # ⊡ SQUARED DOT OPERATOR
|
||||
'boxminus': '\u229f', # ⊟ SQUARED MINUS
|
||||
'boxplus': '\u229e', # ⊞ SQUARED PLUS
|
||||
'boxslash': '\u29c4', # ⧄ SQUARED RISING DIAGONAL SLASH
|
||||
'boxtimes': '\u22a0', # ⊠ SQUARED TIMES
|
||||
'bullet': '\u2022', # • BULLET
|
||||
'cap': '\u2229', # ∩ INTERSECTION
|
||||
'cdot': '\u22c5', # ⋅ DOT OPERATOR
|
||||
'circ': '\u2218', # ∘ RING OPERATOR
|
||||
'circledast': '\u229b', # ⊛ CIRCLED ASTERISK OPERATOR
|
||||
'circledbslash': '\u29b8', # ⦸ CIRCLED REVERSE SOLIDUS
|
||||
'circledcirc': '\u229a', # ⊚ CIRCLED RING OPERATOR
|
||||
'circleddash': '\u229d', # ⊝ CIRCLED DASH
|
||||
'circledgtr': '\u29c1', # ⧁ CIRCLED GREATER-THAN
|
||||
'circledless': '\u29c0', # ⧀ CIRCLED LESS-THAN
|
||||
'cup': '\u222a', # ∪ UNION
|
||||
'curlyvee': '\u22ce', # ⋎ CURLY LOGICAL OR
|
||||
'curlywedge': '\u22cf', # ⋏ CURLY LOGICAL AND
|
||||
'dagger': '\u2020', # † DAGGER
|
||||
'ddagger': '\u2021', # ‡ DOUBLE DAGGER
|
||||
'diamond': '\u22c4', # ⋄ DIAMOND OPERATOR
|
||||
'div': '\xf7', # ÷ DIVISION SIGN
|
||||
'divideontimes': '\u22c7', # ⋇ DIVISION TIMES
|
||||
'dotplus': '\u2214', # ∔ DOT PLUS
|
||||
'doublebarwedge': '\u2a5e', # ⩞ LOGICAL AND WITH DOUBLE OVERBAR
|
||||
'gtrdot': '\u22d7', # ⋗ GREATER-THAN WITH DOT
|
||||
'intercal': '\u22ba', # ⊺ INTERCALATE
|
||||
'interleave': '\u2af4', # ⫴ TRIPLE VERTICAL BAR BINARY RELATION
|
||||
'invamp': '\u214b', # ⅋ TURNED AMPERSAND
|
||||
'land': '\u2227', # ∧ LOGICAL AND
|
||||
'leftthreetimes': '\u22cb', # ⋋ LEFT SEMIDIRECT PRODUCT
|
||||
'lessdot': '\u22d6', # ⋖ LESS-THAN WITH DOT
|
||||
'lor': '\u2228', # ∨ LOGICAL OR
|
||||
'ltimes': '\u22c9', # ⋉ LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
|
||||
'mp': '\u2213', # ∓ MINUS-OR-PLUS SIGN
|
||||
'odot': '\u2299', # ⊙ CIRCLED DOT OPERATOR
|
||||
'ominus': '\u2296', # ⊖ CIRCLED MINUS
|
||||
'oplus': '\u2295', # ⊕ CIRCLED PLUS
|
||||
'oslash': '\u2298', # ⊘ CIRCLED DIVISION SLASH
|
||||
'otimes': '\u2297', # ⊗ CIRCLED TIMES
|
||||
'pm': '\xb1', # ± PLUS-MINUS SIGN
|
||||
'rightthreetimes': '\u22cc', # ⋌ RIGHT SEMIDIRECT PRODUCT
|
||||
'rtimes': '\u22ca', # ⋊ RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
|
||||
'setminus': '\u29f5', # ⧵ REVERSE SOLIDUS OPERATOR
|
||||
'slash': '\u2215', # ∕ DIVISION SLASH
|
||||
'smallsetminus': '\u2216', # ∖ SET MINUS
|
||||
'smalltriangledown': '\u25bf', # ▿ WHITE DOWN-POINTING SMALL TRIANGLE
|
||||
'smalltriangleleft': '\u25c3', # ◃ WHITE LEFT-POINTING SMALL TRIANGLE
|
||||
'smalltriangleright': '\u25b9', # ▹ WHITE RIGHT-POINTING SMALL TRIANGLE
|
||||
'sqcap': '\u2293', # ⊓ SQUARE CAP
|
||||
'sqcup': '\u2294', # ⊔ SQUARE CUP
|
||||
'sslash': '\u2afd', # ⫽ DOUBLE SOLIDUS OPERATOR
|
||||
'star': '\u22c6', # ⋆ STAR OPERATOR
|
||||
'talloblong': '\u2afe', # ⫾ WHITE VERTICAL BAR
|
||||
'times': '\xd7', # × MULTIPLICATION SIGN
|
||||
'triangleleft': '\u25c3', # ◃ WHITE LEFT-POINTING SMALL TRIANGLE
|
||||
'triangleright': '\u25b9', # ▹ WHITE RIGHT-POINTING SMALL TRIANGLE
|
||||
'uplus': '\u228e', # ⊎ MULTISET UNION
|
||||
'vee': '\u2228', # ∨ LOGICAL OR
|
||||
'veebar': '\u22bb', # ⊻ XOR
|
||||
'wedge': '\u2227', # ∧ LOGICAL AND
|
||||
'wr': '\u2240', # ≀ WREATH PRODUCT
|
||||
}
|
||||
|
||||
mathclose = {
|
||||
'Rbag': '\u27c6', # ⟆ RIGHT S-SHAPED BAG DELIMITER
|
||||
'lrcorner': '\u231f', # ⌟ BOTTOM RIGHT CORNER
|
||||
'rangle': '\u27e9', # ⟩ MATHEMATICAL RIGHT ANGLE BRACKET
|
||||
'rbag': '\u27c6', # ⟆ RIGHT S-SHAPED BAG DELIMITER
|
||||
'rbrace': '}', # } RIGHT CURLY BRACKET
|
||||
'rbrack': ']', # ] RIGHT SQUARE BRACKET
|
||||
'rceil': '\u2309', # ⌉ RIGHT CEILING
|
||||
'rfloor': '\u230b', # ⌋ RIGHT FLOOR
|
||||
'rgroup': '\u27ef', # ⟯ MATHEMATICAL RIGHT FLATTENED PARENTHESIS
|
||||
'rrbracket': '\u27e7', # ⟧ MATHEMATICAL RIGHT WHITE SQUARE BRACKET
|
||||
'rrparenthesis': '\u2988', # ⦈ Z NOTATION RIGHT IMAGE BRACKET
|
||||
'urcorner': '\u231d', # ⌝ TOP RIGHT CORNER
|
||||
'}': '}', # } RIGHT CURLY BRACKET
|
||||
}
|
||||
|
||||
mathfence = {
|
||||
'Vert': '\u2016', # ‖ DOUBLE VERTICAL LINE
|
||||
'vert': '|', # | VERTICAL LINE
|
||||
'|': '\u2016', # ‖ DOUBLE VERTICAL LINE
|
||||
}
|
||||
|
||||
mathop = {
|
||||
'bigcap': '\u22c2', # ⋂ N-ARY INTERSECTION
|
||||
'bigcup': '\u22c3', # ⋃ N-ARY UNION
|
||||
'biginterleave': '\u2afc', # ⫼ LARGE TRIPLE VERTICAL BAR OPERATOR
|
||||
'bigodot': '\u2a00', # ⨀ N-ARY CIRCLED DOT OPERATOR
|
||||
'bigoplus': '\u2a01', # ⨁ N-ARY CIRCLED PLUS OPERATOR
|
||||
'bigotimes': '\u2a02', # ⨂ N-ARY CIRCLED TIMES OPERATOR
|
||||
'bigsqcap': '\u2a05', # ⨅ N-ARY SQUARE INTERSECTION OPERATOR
|
||||
'bigsqcup': '\u2a06', # ⨆ N-ARY SQUARE UNION OPERATOR
|
||||
'biguplus': '\u2a04', # ⨄ N-ARY UNION OPERATOR WITH PLUS
|
||||
'bigvee': '\u22c1', # ⋁ N-ARY LOGICAL OR
|
||||
'bigwedge': '\u22c0', # ⋀ N-ARY LOGICAL AND
|
||||
'coprod': '\u2210', # ∐ N-ARY COPRODUCT
|
||||
'fatsemi': '\u2a1f', # ⨟ Z NOTATION SCHEMA COMPOSITION
|
||||
'fint': '\u2a0f', # ⨏ INTEGRAL AVERAGE WITH SLASH
|
||||
'iiiint': '\u2a0c', # ⨌ QUADRUPLE INTEGRAL OPERATOR
|
||||
'iiint': '\u222d', # ∭ TRIPLE INTEGRAL
|
||||
'iint': '\u222c', # ∬ DOUBLE INTEGRAL
|
||||
'int': '\u222b', # ∫ INTEGRAL
|
||||
'intop': '\u222b', # ∫ INTEGRAL
|
||||
'oiiint': '\u2230', # ∰ VOLUME INTEGRAL
|
||||
'oiint': '\u222f', # ∯ SURFACE INTEGRAL
|
||||
'oint': '\u222e', # ∮ CONTOUR INTEGRAL
|
||||
'ointctrclockwise': '\u2233', # ∳ ANTICLOCKWISE CONTOUR INTEGRAL
|
||||
'ointop': '\u222e', # ∮ CONTOUR INTEGRAL
|
||||
'prod': '\u220f', # ∏ N-ARY PRODUCT
|
||||
'sqint': '\u2a16', # ⨖ QUATERNION INTEGRAL OPERATOR
|
||||
'sum': '\u2211', # ∑ N-ARY SUMMATION
|
||||
'varointclockwise': '\u2232', # ∲ CLOCKWISE CONTOUR INTEGRAL
|
||||
'varprod': '\u2a09', # ⨉ N-ARY TIMES OPERATOR
|
||||
}
|
||||
|
||||
mathopen = {
|
||||
'Lbag': '\u27c5', # ⟅ LEFT S-SHAPED BAG DELIMITER
|
||||
'langle': '\u27e8', # ⟨ MATHEMATICAL LEFT ANGLE BRACKET
|
||||
'lbag': '\u27c5', # ⟅ LEFT S-SHAPED BAG DELIMITER
|
||||
'lbrace': '{', # { LEFT CURLY BRACKET
|
||||
'lbrack': '[', # [ LEFT SQUARE BRACKET
|
||||
'lceil': '\u2308', # ⌈ LEFT CEILING
|
||||
'lfloor': '\u230a', # ⌊ LEFT FLOOR
|
||||
'lgroup': '\u27ee', # ⟮ MATHEMATICAL LEFT FLATTENED PARENTHESIS
|
||||
'llbracket': '\u27e6', # ⟦ MATHEMATICAL LEFT WHITE SQUARE BRACKET
|
||||
'llcorner': '\u231e', # ⌞ BOTTOM LEFT CORNER
|
||||
'llparenthesis': '\u2987', # ⦇ Z NOTATION LEFT IMAGE BRACKET
|
||||
'ulcorner': '\u231c', # ⌜ TOP LEFT CORNER
|
||||
'{': '{', # { LEFT CURLY BRACKET
|
||||
}
|
||||
|
||||
mathord = {
|
||||
'#': '#', # # NUMBER SIGN
|
||||
'$': '$', # $ DOLLAR SIGN
|
||||
'%': '%', # % PERCENT SIGN
|
||||
'&': '&', # & AMPERSAND
|
||||
'AC': '\u223f', # ∿ SINE WAVE
|
||||
'APLcomment': '\u235d', # ⍝ APL FUNCTIONAL SYMBOL UP SHOE JOT
|
||||
'APLdownarrowbox': '\u2357', # ⍗ APL FUNCTIONAL SYMBOL QUAD DOWNWARDS ARROW
|
||||
'APLinput': '\u235e', # ⍞ APL FUNCTIONAL SYMBOL QUOTE QUAD
|
||||
'APLinv': '\u2339', # ⌹ APL FUNCTIONAL SYMBOL QUAD DIVIDE
|
||||
'APLleftarrowbox': '\u2347', # ⍇ APL FUNCTIONAL SYMBOL QUAD LEFTWARDS ARROW
|
||||
'APLlog': '\u235f', # ⍟ APL FUNCTIONAL SYMBOL CIRCLE STAR
|
||||
'APLrightarrowbox': '\u2348', # ⍈ APL FUNCTIONAL SYMBOL QUAD RIGHTWARDS ARROW
|
||||
'APLuparrowbox': '\u2350', # ⍐ APL FUNCTIONAL SYMBOL QUAD UPWARDS ARROW
|
||||
'Aries': '\u2648', # ♈ ARIES
|
||||
'Box': '\u2b1c', # ⬜ WHITE LARGE SQUARE
|
||||
'CIRCLE': '\u25cf', # ● BLACK CIRCLE
|
||||
'CheckedBox': '\u2611', # ☑ BALLOT BOX WITH CHECK
|
||||
'Diamond': '\u25c7', # ◇ WHITE DIAMOND
|
||||
'Diamondblack': '\u25c6', # ◆ BLACK DIAMOND
|
||||
'Diamonddot': '\u27d0', # ⟐ WHITE DIAMOND WITH CENTRED DOT
|
||||
'Finv': '\u2132', # Ⅎ TURNED CAPITAL F
|
||||
'Game': '\u2141', # ⅁ TURNED SANS-SERIF CAPITAL G
|
||||
'Gemini': '\u264a', # ♊ GEMINI
|
||||
'Jupiter': '\u2643', # ♃ JUPITER
|
||||
'LEFTCIRCLE': '\u25d6', # ◖ LEFT HALF BLACK CIRCLE
|
||||
'LEFTcircle': '\u25d0', # ◐ CIRCLE WITH LEFT HALF BLACK
|
||||
'Leo': '\u264c', # ♌ LEO
|
||||
'Libra': '\u264e', # ♎ LIBRA
|
||||
'Mars': '\u2642', # ♂ MALE SIGN
|
||||
'Mercury': '\u263f', # ☿ MERCURY
|
||||
'Neptune': '\u2646', # ♆ NEPTUNE
|
||||
'P': '\xb6', # ¶ PILCROW SIGN
|
||||
'Pluto': '\u2647', # ♇ PLUTO
|
||||
'RIGHTCIRCLE': '\u25d7', # ◗ RIGHT HALF BLACK CIRCLE
|
||||
'RIGHTcircle': '\u25d1', # ◑ CIRCLE WITH RIGHT HALF BLACK
|
||||
'S': '\xa7', # § SECTION SIGN
|
||||
'Saturn': '\u2644', # ♄ SATURN
|
||||
'Scorpio': '\u264f', # ♏ SCORPIUS
|
||||
'Square': '\u2610', # ☐ BALLOT BOX
|
||||
'Sun': '\u2609', # ☉ SUN
|
||||
'Taurus': '\u2649', # ♉ TAURUS
|
||||
'Uranus': '\u2645', # ♅ URANUS
|
||||
'Venus': '\u2640', # ♀ FEMALE SIGN
|
||||
'XBox': '\u2612', # ☒ BALLOT BOX WITH X
|
||||
'Yup': '\u2144', # ⅄ TURNED SANS-SERIF CAPITAL Y
|
||||
'_': '_', # _ LOW LINE
|
||||
'angle': '\u2220', # ∠ ANGLE
|
||||
'aquarius': '\u2652', # ♒ AQUARIUS
|
||||
'aries': '\u2648', # ♈ ARIES
|
||||
'arrowvert': '\u23d0', # ⏐ VERTICAL LINE EXTENSION
|
||||
'backprime': '\u2035', # ‵ REVERSED PRIME
|
||||
'backslash': '\\', # \ REVERSE SOLIDUS
|
||||
'bigstar': '\u2605', # ★ BLACK STAR
|
||||
'blacksmiley': '\u263b', # ☻ BLACK SMILING FACE
|
||||
'blacksquare': '\u25fc', # ◼ BLACK MEDIUM SQUARE
|
||||
'blacktriangle': '\u25b4', # ▴ BLACK UP-POINTING SMALL TRIANGLE
|
||||
'blacktriangledown': '\u25be', # ▾ BLACK DOWN-POINTING SMALL TRIANGLE
|
||||
'blacktriangleup': '\u25b4', # ▴ BLACK UP-POINTING SMALL TRIANGLE
|
||||
'bot': '\u22a5', # ⊥ UP TACK
|
||||
'boy': '\u2642', # ♂ MALE SIGN
|
||||
'bracevert': '\u23aa', # ⎪ CURLY BRACKET EXTENSION
|
||||
'cancer': '\u264b', # ♋ CANCER
|
||||
'capricornus': '\u2651', # ♑ CAPRICORN
|
||||
'cdots': '\u22ef', # ⋯ MIDLINE HORIZONTAL ELLIPSIS
|
||||
'cent': '\xa2', # ¢ CENT SIGN
|
||||
'checkmark': '\u2713', # ✓ CHECK MARK
|
||||
'circledR': '\u24c7', # Ⓡ CIRCLED LATIN CAPITAL LETTER R
|
||||
'circledS': '\u24c8', # Ⓢ CIRCLED LATIN CAPITAL LETTER S
|
||||
'clubsuit': '\u2663', # ♣ BLACK CLUB SUIT
|
||||
'complement': '\u2201', # ∁ COMPLEMENT
|
||||
'diagdown': '\u27cd', # ⟍ MATHEMATICAL FALLING DIAGONAL
|
||||
'diagup': '\u27cb', # ⟋ MATHEMATICAL RISING DIAGONAL
|
||||
'diameter': '\u2300', # ⌀ DIAMETER SIGN
|
||||
'diamondsuit': '\u2662', # ♢ WHITE DIAMOND SUIT
|
||||
'earth': '\u2641', # ♁ EARTH
|
||||
'emptyset': '\u2205', # ∅ EMPTY SET
|
||||
'exists': '\u2203', # ∃ THERE EXISTS
|
||||
'female': '\u2640', # ♀ FEMALE SIGN
|
||||
'flat': '\u266d', # ♭ MUSIC FLAT SIGN
|
||||
'forall': '\u2200', # ∀ FOR ALL
|
||||
'fourth': '\u2057', # ⁗ QUADRUPLE PRIME
|
||||
'frownie': '\u2639', # ☹ WHITE FROWNING FACE
|
||||
'gemini': '\u264a', # ♊ GEMINI
|
||||
'girl': '\u2640', # ♀ FEMALE SIGN
|
||||
'heartsuit': '\u2661', # ♡ WHITE HEART SUIT
|
||||
'infty': '\u221e', # ∞ INFINITY
|
||||
'invdiameter': '\u2349', # ⍉ APL FUNCTIONAL SYMBOL CIRCLE BACKSLASH
|
||||
'invneg': '\u2310', # ⌐ REVERSED NOT SIGN
|
||||
'jupiter': '\u2643', # ♃ JUPITER
|
||||
'ldots': '\u2026', # … HORIZONTAL ELLIPSIS
|
||||
'leftmoon': '\u263e', # ☾ LAST QUARTER MOON
|
||||
'leo': '\u264c', # ♌ LEO
|
||||
'libra': '\u264e', # ♎ LIBRA
|
||||
'lmoustache': '\u23b0', # ⎰ UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION
|
||||
'lnot': '\xac', # ¬ NOT SIGN
|
||||
'lozenge': '\u25ca', # ◊ LOZENGE
|
||||
'male': '\u2642', # ♂ MALE SIGN
|
||||
'maltese': '\u2720', # ✠ MALTESE CROSS
|
||||
'mathcent': '\xa2', # ¢ CENT SIGN
|
||||
'mathdollar': '$', # $ DOLLAR SIGN
|
||||
'mathsterling': '\xa3', # £ POUND SIGN
|
||||
'measuredangle': '\u2221', # ∡ MEASURED ANGLE
|
||||
'medbullet': '\u26ab', # ⚫ MEDIUM BLACK CIRCLE
|
||||
'medcirc': '\u26aa', # ⚪ MEDIUM WHITE CIRCLE
|
||||
'mercury': '\u263f', # ☿ MERCURY
|
||||
'mho': '\u2127', # ℧ INVERTED OHM SIGN
|
||||
'nabla': '\u2207', # ∇ NABLA
|
||||
'natural': '\u266e', # ♮ MUSIC NATURAL SIGN
|
||||
'neg': '\xac', # ¬ NOT SIGN
|
||||
'neptune': '\u2646', # ♆ NEPTUNE
|
||||
'nexists': '\u2204', # ∄ THERE DOES NOT EXIST
|
||||
'notbackslash': '\u2340', # ⍀ APL FUNCTIONAL SYMBOL BACKSLASH BAR
|
||||
'partial': '\u2202', # ∂ PARTIAL DIFFERENTIAL
|
||||
'pisces': '\u2653', # ♓ PISCES
|
||||
'pluto': '\u2647', # ♇ PLUTO
|
||||
'pounds': '\xa3', # £ POUND SIGN
|
||||
'prime': '\u2032', # ′ PRIME
|
||||
'quarternote': '\u2669', # ♩ QUARTER NOTE
|
||||
'rightmoon': '\u263d', # ☽ FIRST QUARTER MOON
|
||||
'rmoustache': '\u23b1', # ⎱ UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION
|
||||
'sagittarius': '\u2650', # ♐ SAGITTARIUS
|
||||
'saturn': '\u2644', # ♄ SATURN
|
||||
'scorpio': '\u264f', # ♏ SCORPIUS
|
||||
'second': '\u2033', # ″ DOUBLE PRIME
|
||||
'sharp': '\u266f', # ♯ MUSIC SHARP SIGN
|
||||
'smiley': '\u263a', # ☺ WHITE SMILING FACE
|
||||
'spadesuit': '\u2660', # ♠ BLACK SPADE SUIT
|
||||
'spddot': '\xa8', # ¨ DIAERESIS
|
||||
'sphat': '^', # ^ CIRCUMFLEX ACCENT
|
||||
'sphericalangle': '\u2222', # ∢ SPHERICAL ANGLE
|
||||
'sptilde': '~', # ~ TILDE
|
||||
'square': '\u25fb', # ◻ WHITE MEDIUM SQUARE
|
||||
'sun': '\u263c', # ☼ WHITE SUN WITH RAYS
|
||||
'surd': '\u221a', # √ SQUARE ROOT
|
||||
'taurus': '\u2649', # ♉ TAURUS
|
||||
'third': '\u2034', # ‴ TRIPLE PRIME
|
||||
'top': '\u22a4', # ⊤ DOWN TACK
|
||||
'twonotes': '\u266b', # ♫ BEAMED EIGHTH NOTES
|
||||
'uranus': '\u2645', # ♅ URANUS
|
||||
'varEarth': '\u2641', # ♁ EARTH
|
||||
'varclubsuit': '\u2667', # ♧ WHITE CLUB SUIT
|
||||
'vardiamondsuit': '\u2666', # ♦ BLACK DIAMOND SUIT
|
||||
'varheartsuit': '\u2665', # ♥ BLACK HEART SUIT
|
||||
'varspadesuit': '\u2664', # ♤ WHITE SPADE SUIT
|
||||
'virgo': '\u264d', # ♍ VIRGO
|
||||
'wasylozenge': '\u2311', # ⌑ SQUARE LOZENGE
|
||||
'yen': '\xa5', # ¥ YEN SIGN
|
||||
}
|
||||
|
||||
mathover = {
|
||||
'overbrace': '\u23de', # ⏞ TOP CURLY BRACKET
|
||||
'wideparen': '\u23dc', # ⏜ TOP PARENTHESIS
|
||||
}
|
||||
|
||||
mathpunct = {
|
||||
'ddots': '\u22f1', # ⋱ DOWN RIGHT DIAGONAL ELLIPSIS
|
||||
'vdots': '\u22ee', # ⋮ VERTICAL ELLIPSIS
|
||||
}
|
||||
|
||||
mathradical = {
|
||||
'sqrt[3]': '\u221b', # ∛ CUBE ROOT
|
||||
'sqrt[4]': '\u221c', # ∜ FOURTH ROOT
|
||||
}
|
||||
|
||||
mathrel = {
|
||||
'Bot': '\u2aeb', # ⫫ DOUBLE UP TACK
|
||||
'Bumpeq': '\u224e', # ≎ GEOMETRICALLY EQUIVALENT TO
|
||||
'Coloneqq': '\u2a74', # ⩴ DOUBLE COLON EQUAL
|
||||
'Doteq': '\u2251', # ≑ GEOMETRICALLY EQUAL TO
|
||||
'Downarrow': '\u21d3', # ⇓ DOWNWARDS DOUBLE ARROW
|
||||
'Leftarrow': '\u21d0', # ⇐ LEFTWARDS DOUBLE ARROW
|
||||
'Leftrightarrow': '\u21d4', # ⇔ LEFT RIGHT DOUBLE ARROW
|
||||
'Lleftarrow': '\u21da', # ⇚ LEFTWARDS TRIPLE ARROW
|
||||
'Longleftarrow': '\u27f8', # ⟸ LONG LEFTWARDS DOUBLE ARROW
|
||||
'Longleftrightarrow': '\u27fa', # ⟺ LONG LEFT RIGHT DOUBLE ARROW
|
||||
'Longmapsfrom': '\u27fd', # ⟽ LONG LEFTWARDS DOUBLE ARROW FROM BAR
|
||||
'Longmapsto': '\u27fe', # ⟾ LONG RIGHTWARDS DOUBLE ARROW FROM BAR
|
||||
'Longrightarrow': '\u27f9', # ⟹ LONG RIGHTWARDS DOUBLE ARROW
|
||||
'Lsh': '\u21b0', # ↰ UPWARDS ARROW WITH TIP LEFTWARDS
|
||||
'Mapsfrom': '\u2906', # ⤆ LEFTWARDS DOUBLE ARROW FROM BAR
|
||||
'Mapsto': '\u2907', # ⤇ RIGHTWARDS DOUBLE ARROW FROM BAR
|
||||
'Nearrow': '\u21d7', # ⇗ NORTH EAST DOUBLE ARROW
|
||||
'Nwarrow': '\u21d6', # ⇖ NORTH WEST DOUBLE ARROW
|
||||
'Perp': '\u2aeb', # ⫫ DOUBLE UP TACK
|
||||
'Rightarrow': '\u21d2', # ⇒ RIGHTWARDS DOUBLE ARROW
|
||||
'Rrightarrow': '\u21db', # ⇛ RIGHTWARDS TRIPLE ARROW
|
||||
'Rsh': '\u21b1', # ↱ UPWARDS ARROW WITH TIP RIGHTWARDS
|
||||
'Searrow': '\u21d8', # ⇘ SOUTH EAST DOUBLE ARROW
|
||||
'Subset': '\u22d0', # ⋐ DOUBLE SUBSET
|
||||
'Supset': '\u22d1', # ⋑ DOUBLE SUPERSET
|
||||
'Swarrow': '\u21d9', # ⇙ SOUTH WEST DOUBLE ARROW
|
||||
'Top': '\u2aea', # ⫪ DOUBLE DOWN TACK
|
||||
'Uparrow': '\u21d1', # ⇑ UPWARDS DOUBLE ARROW
|
||||
'Updownarrow': '\u21d5', # ⇕ UP DOWN DOUBLE ARROW
|
||||
'VDash': '\u22ab', # ⊫ DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
|
||||
'Vdash': '\u22a9', # ⊩ FORCES
|
||||
'Vvdash': '\u22aa', # ⊪ TRIPLE VERTICAL BAR RIGHT TURNSTILE
|
||||
'apprge': '\u2273', # ≳ GREATER-THAN OR EQUIVALENT TO
|
||||
'apprle': '\u2272', # ≲ LESS-THAN OR EQUIVALENT TO
|
||||
'approx': '\u2248', # ≈ ALMOST EQUAL TO
|
||||
'approxeq': '\u224a', # ≊ ALMOST EQUAL OR EQUAL TO
|
||||
'asymp': '\u224d', # ≍ EQUIVALENT TO
|
||||
'backepsilon': '\u220d', # ∍ SMALL CONTAINS AS MEMBER
|
||||
'backsim': '\u223d', # ∽ REVERSED TILDE
|
||||
'backsimeq': '\u22cd', # ⋍ REVERSED TILDE EQUALS
|
||||
'barin': '\u22f6', # ⋶ ELEMENT OF WITH OVERBAR
|
||||
'barleftharpoon': '\u296b', # ⥫ LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH
|
||||
'barrightharpoon': '\u296d', # ⥭ RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH
|
||||
'because': '\u2235', # ∵ BECAUSE
|
||||
'between': '\u226c', # ≬ BETWEEN
|
||||
'blacktriangleleft': '\u25c2', # ◂ BLACK LEFT-POINTING SMALL TRIANGLE
|
||||
'blacktriangleright': '\u25b8', # ▸ BLACK RIGHT-POINTING SMALL TRIANGLE
|
||||
'bowtie': '\u22c8', # ⋈ BOWTIE
|
||||
'bumpeq': '\u224f', # ≏ DIFFERENCE BETWEEN
|
||||
'circeq': '\u2257', # ≗ RING EQUAL TO
|
||||
'circlearrowleft': '\u21ba', # ↺ ANTICLOCKWISE OPEN CIRCLE ARROW
|
||||
'circlearrowright': '\u21bb', # ↻ CLOCKWISE OPEN CIRCLE ARROW
|
||||
'coloneq': '\u2254', # ≔ COLON EQUALS
|
||||
'coloneqq': '\u2254', # ≔ COLON EQUALS
|
||||
'cong': '\u2245', # ≅ APPROXIMATELY EQUAL TO
|
||||
'corresponds': '\u2259', # ≙ ESTIMATES
|
||||
'curlyeqprec': '\u22de', # ⋞ EQUAL TO OR PRECEDES
|
||||
'curlyeqsucc': '\u22df', # ⋟ EQUAL TO OR SUCCEEDS
|
||||
'curvearrowleft': '\u21b6', # ↶ ANTICLOCKWISE TOP SEMICIRCLE ARROW
|
||||
'curvearrowright': '\u21b7', # ↷ CLOCKWISE TOP SEMICIRCLE ARROW
|
||||
'dasharrow': '\u21e2', # ⇢ RIGHTWARDS DASHED ARROW
|
||||
'dashleftarrow': '\u21e0', # ⇠ LEFTWARDS DASHED ARROW
|
||||
'dashrightarrow': '\u21e2', # ⇢ RIGHTWARDS DASHED ARROW
|
||||
'dashv': '\u22a3', # ⊣ LEFT TACK
|
||||
'dlsh': '\u21b2', # ↲ DOWNWARDS ARROW WITH TIP LEFTWARDS
|
||||
'doteq': '\u2250', # ≐ APPROACHES THE LIMIT
|
||||
'doteqdot': '\u2251', # ≑ GEOMETRICALLY EQUAL TO
|
||||
'downarrow': '\u2193', # ↓ DOWNWARDS ARROW
|
||||
'downdownarrows': '\u21ca', # ⇊ DOWNWARDS PAIRED ARROWS
|
||||
'downdownharpoons': '\u2965', # ⥥ DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
|
||||
'downharpoonleft': '\u21c3', # ⇃ DOWNWARDS HARPOON WITH BARB LEFTWARDS
|
||||
'downharpoonright': '\u21c2', # ⇂ DOWNWARDS HARPOON WITH BARB RIGHTWARDS
|
||||
'downuparrows': '\u21f5', # ⇵ DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW
|
||||
'downupharpoons': '\u296f', # ⥯ DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
|
||||
'drsh': '\u21b3', # ↳ DOWNWARDS ARROW WITH TIP RIGHTWARDS
|
||||
'eqcirc': '\u2256', # ≖ RING IN EQUAL TO
|
||||
'eqcolon': '\u2255', # ≕ EQUALS COLON
|
||||
'eqqcolon': '\u2255', # ≕ EQUALS COLON
|
||||
'eqsim': '\u2242', # ≂ MINUS TILDE
|
||||
'eqslantgtr': '\u2a96', # ⪖ SLANTED EQUAL TO OR GREATER-THAN
|
||||
'eqslantless': '\u2a95', # ⪕ SLANTED EQUAL TO OR LESS-THAN
|
||||
'equiv': '\u2261', # ≡ IDENTICAL TO
|
||||
'fallingdotseq': '\u2252', # ≒ APPROXIMATELY EQUAL TO OR THE IMAGE OF
|
||||
'frown': '\u2322', # ⌢ FROWN
|
||||
'ge': '\u2265', # ≥ GREATER-THAN OR EQUAL TO
|
||||
'geq': '\u2265', # ≥ GREATER-THAN OR EQUAL TO
|
||||
'geqq': '\u2267', # ≧ GREATER-THAN OVER EQUAL TO
|
||||
'geqslant': '\u2a7e', # ⩾ GREATER-THAN OR SLANTED EQUAL TO
|
||||
'gets': '\u2190', # ← LEFTWARDS ARROW
|
||||
'gg': '\u226b', # ≫ MUCH GREATER-THAN
|
||||
'ggcurly': '\u2abc', # ⪼ DOUBLE SUCCEEDS
|
||||
'ggg': '\u22d9', # ⋙ VERY MUCH GREATER-THAN
|
||||
'gggtr': '\u22d9', # ⋙ VERY MUCH GREATER-THAN
|
||||
'gnapprox': '\u2a8a', # ⪊ GREATER-THAN AND NOT APPROXIMATE
|
||||
'gneq': '\u2a88', # ⪈ GREATER-THAN AND SINGLE-LINE NOT EQUAL TO
|
||||
'gneqq': '\u2269', # ≩ GREATER-THAN BUT NOT EQUAL TO
|
||||
'gnsim': '\u22e7', # ⋧ GREATER-THAN BUT NOT EQUIVALENT TO
|
||||
'gtrapprox': '\u2a86', # ⪆ GREATER-THAN OR APPROXIMATE
|
||||
'gtreqless': '\u22db', # ⋛ GREATER-THAN EQUAL TO OR LESS-THAN
|
||||
'gtreqqless': '\u2a8c', # ⪌ GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
|
||||
'gtrless': '\u2277', # ≷ GREATER-THAN OR LESS-THAN
|
||||
'gtrsim': '\u2273', # ≳ GREATER-THAN OR EQUIVALENT TO
|
||||
'hash': '\u22d5', # ⋕ EQUAL AND PARALLEL TO
|
||||
'hookleftarrow': '\u21a9', # ↩ LEFTWARDS ARROW WITH HOOK
|
||||
'hookrightarrow': '\u21aa', # ↪ RIGHTWARDS ARROW WITH HOOK
|
||||
'iddots': '\u22f0', # ⋰ UP RIGHT DIAGONAL ELLIPSIS
|
||||
'impliedby': '\u27f8', # ⟸ LONG LEFTWARDS DOUBLE ARROW
|
||||
'implies': '\u27f9', # ⟹ LONG RIGHTWARDS DOUBLE ARROW
|
||||
'in': '\u2208', # ∈ ELEMENT OF
|
||||
'le': '\u2264', # ≤ LESS-THAN OR EQUAL TO
|
||||
'leadsto': '\u2933', # ⤳ WAVE ARROW POINTING DIRECTLY RIGHT
|
||||
'leftarrow': '\u2190', # ← LEFTWARDS ARROW
|
||||
'leftarrowtail': '\u21a2', # ↢ LEFTWARDS ARROW WITH TAIL
|
||||
'leftarrowtriangle': '\u21fd', # ⇽ LEFTWARDS OPEN-HEADED ARROW
|
||||
'leftbarharpoon': '\u296a', # ⥪ LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH
|
||||
'leftharpoondown': '\u21bd', # ↽ LEFTWARDS HARPOON WITH BARB DOWNWARDS
|
||||
'leftharpoonup': '\u21bc', # ↼ LEFTWARDS HARPOON WITH BARB UPWARDS
|
||||
'leftleftarrows': '\u21c7', # ⇇ LEFTWARDS PAIRED ARROWS
|
||||
'leftleftharpoons': '\u2962', # ⥢ LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN
|
||||
'leftrightarrow': '\u2194', # ↔ LEFT RIGHT ARROW
|
||||
'leftrightarrows': '\u21c6', # ⇆ LEFTWARDS ARROW OVER RIGHTWARDS ARROW
|
||||
'leftrightarrowtriangle': '\u21ff', # ⇿ LEFT RIGHT OPEN-HEADED ARROW
|
||||
'leftrightharpoon': '\u294a', # ⥊ LEFT BARB UP RIGHT BARB DOWN HARPOON
|
||||
'leftrightharpoons': '\u21cb', # ⇋ LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
|
||||
'leftrightsquigarrow': '\u21ad', # ↭ LEFT RIGHT WAVE ARROW
|
||||
'leftslice': '\u2aa6', # ⪦ LESS-THAN CLOSED BY CURVE
|
||||
'leftsquigarrow': '\u21dc', # ⇜ LEFTWARDS SQUIGGLE ARROW
|
||||
'leftturn': '\u21ba', # ↺ ANTICLOCKWISE OPEN CIRCLE ARROW
|
||||
'leq': '\u2264', # ≤ LESS-THAN OR EQUAL TO
|
||||
'leqq': '\u2266', # ≦ LESS-THAN OVER EQUAL TO
|
||||
'leqslant': '\u2a7d', # ⩽ LESS-THAN OR SLANTED EQUAL TO
|
||||
'lessapprox': '\u2a85', # ⪅ LESS-THAN OR APPROXIMATE
|
||||
'lesseqgtr': '\u22da', # ⋚ LESS-THAN EQUAL TO OR GREATER-THAN
|
||||
'lesseqqgtr': '\u2a8b', # ⪋ LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
|
||||
'lessgtr': '\u2276', # ≶ LESS-THAN OR GREATER-THAN
|
||||
'lesssim': '\u2272', # ≲ LESS-THAN OR EQUIVALENT TO
|
||||
'lhd': '\u22b2', # ⊲ NORMAL SUBGROUP OF
|
||||
'lightning': '\u21af', # ↯ DOWNWARDS ZIGZAG ARROW
|
||||
'll': '\u226a', # ≪ MUCH LESS-THAN
|
||||
'llcurly': '\u2abb', # ⪻ DOUBLE PRECEDES
|
||||
'lll': '\u22d8', # ⋘ VERY MUCH LESS-THAN
|
||||
'llless': '\u22d8', # ⋘ VERY MUCH LESS-THAN
|
||||
'lnapprox': '\u2a89', # ⪉ LESS-THAN AND NOT APPROXIMATE
|
||||
'lneq': '\u2a87', # ⪇ LESS-THAN AND SINGLE-LINE NOT EQUAL TO
|
||||
'lneqq': '\u2268', # ≨ LESS-THAN BUT NOT EQUAL TO
|
||||
'lnsim': '\u22e6', # ⋦ LESS-THAN BUT NOT EQUIVALENT TO
|
||||
'longleftarrow': '\u27f5', # ⟵ LONG LEFTWARDS ARROW
|
||||
'longleftrightarrow': '\u27f7', # ⟷ LONG LEFT RIGHT ARROW
|
||||
'longmapsfrom': '\u27fb', # ⟻ LONG LEFTWARDS ARROW FROM BAR
|
||||
'longmapsto': '\u27fc', # ⟼ LONG RIGHTWARDS ARROW FROM BAR
|
||||
'longrightarrow': '\u27f6', # ⟶ LONG RIGHTWARDS ARROW
|
||||
'looparrowleft': '\u21ab', # ↫ LEFTWARDS ARROW WITH LOOP
|
||||
'looparrowright': '\u21ac', # ↬ RIGHTWARDS ARROW WITH LOOP
|
||||
'lrtimes': '\u22c8', # ⋈ BOWTIE
|
||||
'mapsfrom': '\u21a4', # ↤ LEFTWARDS ARROW FROM BAR
|
||||
'mapsto': '\u21a6', # ↦ RIGHTWARDS ARROW FROM BAR
|
||||
'mid': '\u2223', # ∣ DIVIDES
|
||||
'models': '\u22a7', # ⊧ MODELS
|
||||
'multimap': '\u22b8', # ⊸ MULTIMAP
|
||||
'multimapboth': '\u29df', # ⧟ DOUBLE-ENDED MULTIMAP
|
||||
'multimapdotbothA': '\u22b6', # ⊶ ORIGINAL OF
|
||||
'multimapdotbothB': '\u22b7', # ⊷ IMAGE OF
|
||||
'multimapinv': '\u27dc', # ⟜ LEFT MULTIMAP
|
||||
'nLeftarrow': '\u21cd', # ⇍ LEFTWARDS DOUBLE ARROW WITH STROKE
|
||||
'nLeftrightarrow': '\u21ce', # ⇎ LEFT RIGHT DOUBLE ARROW WITH STROKE
|
||||
'nRightarrow': '\u21cf', # ⇏ RIGHTWARDS DOUBLE ARROW WITH STROKE
|
||||
'nVDash': '\u22af', # ⊯ NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
|
||||
'nVdash': '\u22ae', # ⊮ DOES NOT FORCE
|
||||
'ncong': '\u2247', # ≇ NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
|
||||
'ne': '\u2260', # ≠ NOT EQUAL TO
|
||||
'nearrow': '\u2197', # ↗ NORTH EAST ARROW
|
||||
'neq': '\u2260', # ≠ NOT EQUAL TO
|
||||
'ngeq': '\u2271', # ≱ NEITHER GREATER-THAN NOR EQUAL TO
|
||||
'ngtr': '\u226f', # ≯ NOT GREATER-THAN
|
||||
'ngtrless': '\u2279', # ≹ NEITHER GREATER-THAN NOR LESS-THAN
|
||||
'ni': '\u220b', # ∋ CONTAINS AS MEMBER
|
||||
'nleftarrow': '\u219a', # ↚ LEFTWARDS ARROW WITH STROKE
|
||||
'nleftrightarrow': '\u21ae', # ↮ LEFT RIGHT ARROW WITH STROKE
|
||||
'nleq': '\u2270', # ≰ NEITHER LESS-THAN NOR EQUAL TO
|
||||
'nless': '\u226e', # ≮ NOT LESS-THAN
|
||||
'nlessgtr': '\u2278', # ≸ NEITHER LESS-THAN NOR GREATER-THAN
|
||||
'nmid': '\u2224', # ∤ DOES NOT DIVIDE
|
||||
'notasymp': '\u226d', # ≭ NOT EQUIVALENT TO
|
||||
'notin': '\u2209', # ∉ NOT AN ELEMENT OF
|
||||
'notni': '\u220c', # ∌ DOES NOT CONTAIN AS MEMBER
|
||||
'notowner': '\u220c', # ∌ DOES NOT CONTAIN AS MEMBER
|
||||
'notslash': '\u233f', # ⌿ APL FUNCTIONAL SYMBOL SLASH BAR
|
||||
'nparallel': '\u2226', # ∦ NOT PARALLEL TO
|
||||
'nprec': '\u2280', # ⊀ DOES NOT PRECEDE
|
||||
'npreceq': '\u22e0', # ⋠ DOES NOT PRECEDE OR EQUAL
|
||||
'nrightarrow': '\u219b', # ↛ RIGHTWARDS ARROW WITH STROKE
|
||||
'nsim': '\u2241', # ≁ NOT TILDE
|
||||
'nsimeq': '\u2244', # ≄ NOT ASYMPTOTICALLY EQUAL TO
|
||||
'nsubseteq': '\u2288', # ⊈ NEITHER A SUBSET OF NOR EQUAL TO
|
||||
'nsucc': '\u2281', # ⊁ DOES NOT SUCCEED
|
||||
'nsucceq': '\u22e1', # ⋡ DOES NOT SUCCEED OR EQUAL
|
||||
'nsupseteq': '\u2289', # ⊉ NEITHER A SUPERSET OF NOR EQUAL TO
|
||||
'ntriangleleft': '\u22ea', # ⋪ NOT NORMAL SUBGROUP OF
|
||||
'ntrianglelefteq': '\u22ec', # ⋬ NOT NORMAL SUBGROUP OF OR EQUAL TO
|
||||
'ntriangleright': '\u22eb', # ⋫ DOES NOT CONTAIN AS NORMAL SUBGROUP
|
||||
'ntrianglerighteq': '\u22ed', # ⋭ DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
|
||||
'nvDash': '\u22ad', # ⊭ NOT TRUE
|
||||
'nvdash': '\u22ac', # ⊬ DOES NOT PROVE
|
||||
'nwarrow': '\u2196', # ↖ NORTH WEST ARROW
|
||||
'owns': '\u220b', # ∋ CONTAINS AS MEMBER
|
||||
'parallel': '\u2225', # ∥ PARALLEL TO
|
||||
'perp': '\u27c2', # ⟂ PERPENDICULAR
|
||||
'pitchfork': '\u22d4', # ⋔ PITCHFORK
|
||||
'prec': '\u227a', # ≺ PRECEDES
|
||||
'precapprox': '\u2ab7', # ⪷ PRECEDES ABOVE ALMOST EQUAL TO
|
||||
'preccurlyeq': '\u227c', # ≼ PRECEDES OR EQUAL TO
|
||||
'preceq': '\u2aaf', # ⪯ PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
|
||||
'preceqq': '\u2ab3', # ⪳ PRECEDES ABOVE EQUALS SIGN
|
||||
'precnapprox': '\u2ab9', # ⪹ PRECEDES ABOVE NOT ALMOST EQUAL TO
|
||||
'precneqq': '\u2ab5', # ⪵ PRECEDES ABOVE NOT EQUAL TO
|
||||
'precnsim': '\u22e8', # ⋨ PRECEDES BUT NOT EQUIVALENT TO
|
||||
'precsim': '\u227e', # ≾ PRECEDES OR EQUIVALENT TO
|
||||
'propto': '\u221d', # ∝ PROPORTIONAL TO
|
||||
'restriction': '\u21be', # ↾ UPWARDS HARPOON WITH BARB RIGHTWARDS
|
||||
'rhd': '\u22b3', # ⊳ CONTAINS AS NORMAL SUBGROUP
|
||||
'rightarrow': '\u2192', # → RIGHTWARDS ARROW
|
||||
'rightarrowtail': '\u21a3', # ↣ RIGHTWARDS ARROW WITH TAIL
|
||||
'rightarrowtriangle': '\u21fe', # ⇾ RIGHTWARDS OPEN-HEADED ARROW
|
||||
'rightbarharpoon': '\u296c', # ⥬ RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH
|
||||
'rightharpoondown': '\u21c1', # ⇁ RIGHTWARDS HARPOON WITH BARB DOWNWARDS
|
||||
'rightharpoonup': '\u21c0', # ⇀ RIGHTWARDS HARPOON WITH BARB UPWARDS
|
||||
'rightleftarrows': '\u21c4', # ⇄ RIGHTWARDS ARROW OVER LEFTWARDS ARROW
|
||||
'rightleftharpoon': '\u294b', # ⥋ LEFT BARB DOWN RIGHT BARB UP HARPOON
|
||||
'rightleftharpoons': '\u21cc', # ⇌ RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
|
||||
'rightrightarrows': '\u21c9', # ⇉ RIGHTWARDS PAIRED ARROWS
|
||||
'rightrightharpoons': '\u2964', # ⥤ RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN
|
||||
'rightslice': '\u2aa7', # ⪧ GREATER-THAN CLOSED BY CURVE
|
||||
'rightsquigarrow': '\u21dd', # ⇝ RIGHTWARDS SQUIGGLE ARROW
|
||||
'rightturn': '\u21bb', # ↻ CLOCKWISE OPEN CIRCLE ARROW
|
||||
'risingdotseq': '\u2253', # ≓ IMAGE OF OR APPROXIMATELY EQUAL TO
|
||||
'searrow': '\u2198', # ↘ SOUTH EAST ARROW
|
||||
'sim': '\u223c', # ∼ TILDE OPERATOR
|
||||
'simeq': '\u2243', # ≃ ASYMPTOTICALLY EQUAL TO
|
||||
'smile': '\u2323', # ⌣ SMILE
|
||||
'sqsubset': '\u228f', # ⊏ SQUARE IMAGE OF
|
||||
'sqsubseteq': '\u2291', # ⊑ SQUARE IMAGE OF OR EQUAL TO
|
||||
'sqsupset': '\u2290', # ⊐ SQUARE ORIGINAL OF
|
||||
'sqsupseteq': '\u2292', # ⊒ SQUARE ORIGINAL OF OR EQUAL TO
|
||||
'strictfi': '\u297c', # ⥼ LEFT FISH TAIL
|
||||
'strictif': '\u297d', # ⥽ RIGHT FISH TAIL
|
||||
'subset': '\u2282', # ⊂ SUBSET OF
|
||||
'subseteq': '\u2286', # ⊆ SUBSET OF OR EQUAL TO
|
||||
'subseteqq': '\u2ac5', # ⫅ SUBSET OF ABOVE EQUALS SIGN
|
||||
'subsetneq': '\u228a', # ⊊ SUBSET OF WITH NOT EQUAL TO
|
||||
'subsetneqq': '\u2acb', # ⫋ SUBSET OF ABOVE NOT EQUAL TO
|
||||
'succ': '\u227b', # ≻ SUCCEEDS
|
||||
'succapprox': '\u2ab8', # ⪸ SUCCEEDS ABOVE ALMOST EQUAL TO
|
||||
'succcurlyeq': '\u227d', # ≽ SUCCEEDS OR EQUAL TO
|
||||
'succeq': '\u2ab0', # ⪰ SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
|
||||
'succeqq': '\u2ab4', # ⪴ SUCCEEDS ABOVE EQUALS SIGN
|
||||
'succnapprox': '\u2aba', # ⪺ SUCCEEDS ABOVE NOT ALMOST EQUAL TO
|
||||
'succneqq': '\u2ab6', # ⪶ SUCCEEDS ABOVE NOT EQUAL TO
|
||||
'succnsim': '\u22e9', # ⋩ SUCCEEDS BUT NOT EQUIVALENT TO
|
||||
'succsim': '\u227f', # ≿ SUCCEEDS OR EQUIVALENT TO
|
||||
'supset': '\u2283', # ⊃ SUPERSET OF
|
||||
'supseteq': '\u2287', # ⊇ SUPERSET OF OR EQUAL TO
|
||||
'supseteqq': '\u2ac6', # ⫆ SUPERSET OF ABOVE EQUALS SIGN
|
||||
'supsetneq': '\u228b', # ⊋ SUPERSET OF WITH NOT EQUAL TO
|
||||
'supsetneqq': '\u2acc', # ⫌ SUPERSET OF ABOVE NOT EQUAL TO
|
||||
'swarrow': '\u2199', # ↙ SOUTH WEST ARROW
|
||||
'therefore': '\u2234', # ∴ THEREFORE
|
||||
'to': '\u2192', # → RIGHTWARDS ARROW
|
||||
'trianglelefteq': '\u22b4', # ⊴ NORMAL SUBGROUP OF OR EQUAL TO
|
||||
'triangleq': '\u225c', # ≜ DELTA EQUAL TO
|
||||
'trianglerighteq': '\u22b5', # ⊵ CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
|
||||
'twoheadleftarrow': '\u219e', # ↞ LEFTWARDS TWO HEADED ARROW
|
||||
'twoheadrightarrow': '\u21a0', # ↠ RIGHTWARDS TWO HEADED ARROW
|
||||
'uparrow': '\u2191', # ↑ UPWARDS ARROW
|
||||
'updownarrow': '\u2195', # ↕ UP DOWN ARROW
|
||||
'updownarrows': '\u21c5', # ⇅ UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW
|
||||
'updownharpoons': '\u296e', # ⥮ UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
|
||||
'upharpoonleft': '\u21bf', # ↿ UPWARDS HARPOON WITH BARB LEFTWARDS
|
||||
'upharpoonright': '\u21be', # ↾ UPWARDS HARPOON WITH BARB RIGHTWARDS
|
||||
'upuparrows': '\u21c8', # ⇈ UPWARDS PAIRED ARROWS
|
||||
'upupharpoons': '\u2963', # ⥣ UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
|
||||
'vDash': '\u22a8', # ⊨ TRUE
|
||||
'vartriangle': '\u25b5', # ▵ WHITE UP-POINTING SMALL TRIANGLE
|
||||
'vartriangleleft': '\u22b2', # ⊲ NORMAL SUBGROUP OF
|
||||
'vartriangleright': '\u22b3', # ⊳ CONTAINS AS NORMAL SUBGROUP
|
||||
'vdash': '\u22a2', # ⊢ RIGHT TACK
|
||||
'wasytherefore': '\u2234', # ∴ THEREFORE
|
||||
}
|
||||
|
||||
mathunder = {
|
||||
'underbrace': '\u23df', # ⏟ BOTTOM CURLY BRACKET
|
||||
}
|
||||
|
||||
space = {
|
||||
' ': ' ', # SPACE
|
||||
',': '\u2006', # SIX-PER-EM SPACE
|
||||
':': '\u205f', # MEDIUM MATHEMATICAL SPACE
|
||||
'medspace': '\u205f', # MEDIUM MATHEMATICAL SPACE
|
||||
'quad': '\u2001', # EM QUAD
|
||||
'thinspace': '\u2006', # SIX-PER-EM SPACE
|
||||
}
|
||||
@@ -0,0 +1,808 @@
|
||||
# LaTeX math to Unicode symbols translation table
|
||||
# for use with the translate() method of unicode objects.
|
||||
# Generated with ``write_unichar2tex.py`` from the data in
|
||||
# http://milde.users.sourceforge.net/LUCR/Math/
|
||||
|
||||
# Includes commands from: standard LaTeX, amssymb, amsmath
|
||||
|
||||
uni2tex_table = {
|
||||
0xa0: '~',
|
||||
0xa3: '\\pounds ',
|
||||
0xa5: '\\yen ',
|
||||
0xa7: '\\S ',
|
||||
0xac: '\\neg ',
|
||||
0xb1: '\\pm ',
|
||||
0xb6: '\\P ',
|
||||
0xd7: '\\times ',
|
||||
0xf0: '\\eth ',
|
||||
0xf7: '\\div ',
|
||||
0x131: '\\imath ',
|
||||
0x237: '\\jmath ',
|
||||
0x393: '\\Gamma ',
|
||||
0x394: '\\Delta ',
|
||||
0x398: '\\Theta ',
|
||||
0x39b: '\\Lambda ',
|
||||
0x39e: '\\Xi ',
|
||||
0x3a0: '\\Pi ',
|
||||
0x3a3: '\\Sigma ',
|
||||
0x3a5: '\\Upsilon ',
|
||||
0x3a6: '\\Phi ',
|
||||
0x3a8: '\\Psi ',
|
||||
0x3a9: '\\Omega ',
|
||||
0x3b1: '\\alpha ',
|
||||
0x3b2: '\\beta ',
|
||||
0x3b3: '\\gamma ',
|
||||
0x3b4: '\\delta ',
|
||||
0x3b5: '\\varepsilon ',
|
||||
0x3b6: '\\zeta ',
|
||||
0x3b7: '\\eta ',
|
||||
0x3b8: '\\theta ',
|
||||
0x3b9: '\\iota ',
|
||||
0x3ba: '\\kappa ',
|
||||
0x3bb: '\\lambda ',
|
||||
0x3bc: '\\mu ',
|
||||
0x3bd: '\\nu ',
|
||||
0x3be: '\\xi ',
|
||||
0x3c0: '\\pi ',
|
||||
0x3c1: '\\rho ',
|
||||
0x3c2: '\\varsigma ',
|
||||
0x3c3: '\\sigma ',
|
||||
0x3c4: '\\tau ',
|
||||
0x3c5: '\\upsilon ',
|
||||
0x3c6: '\\varphi ',
|
||||
0x3c7: '\\chi ',
|
||||
0x3c8: '\\psi ',
|
||||
0x3c9: '\\omega ',
|
||||
0x3d1: '\\vartheta ',
|
||||
0x3d5: '\\phi ',
|
||||
0x3d6: '\\varpi ',
|
||||
0x3dd: '\\digamma ',
|
||||
0x3f0: '\\varkappa ',
|
||||
0x3f1: '\\varrho ',
|
||||
0x3f5: '\\epsilon ',
|
||||
0x3f6: '\\backepsilon ',
|
||||
0x2001: '\\quad ',
|
||||
0x2003: '\\quad ',
|
||||
0x2006: '\\, ',
|
||||
0x2016: '\\| ',
|
||||
0x2020: '\\dagger ',
|
||||
0x2021: '\\ddagger ',
|
||||
0x2022: '\\bullet ',
|
||||
0x2026: '\\ldots ',
|
||||
0x2032: '\\prime ',
|
||||
0x2035: '\\backprime ',
|
||||
0x205f: '\\: ',
|
||||
0x2102: '\\mathbb{C}',
|
||||
0x210b: '\\mathcal{H}',
|
||||
0x210c: '\\mathfrak{H}',
|
||||
0x210d: '\\mathbb{H}',
|
||||
0x210f: '\\hslash ',
|
||||
0x2110: '\\mathcal{I}',
|
||||
0x2111: '\\Im ',
|
||||
0x2112: '\\mathcal{L}',
|
||||
0x2113: '\\ell ',
|
||||
0x2115: '\\mathbb{N}',
|
||||
0x2118: '\\wp ',
|
||||
0x2119: '\\mathbb{P}',
|
||||
0x211a: '\\mathbb{Q}',
|
||||
0x211b: '\\mathcal{R}',
|
||||
0x211c: '\\Re ',
|
||||
0x211d: '\\mathbb{R}',
|
||||
0x2124: '\\mathbb{Z}',
|
||||
0x2127: '\\mho ',
|
||||
0x2128: '\\mathfrak{Z}',
|
||||
0x212c: '\\mathcal{B}',
|
||||
0x212d: '\\mathfrak{C}',
|
||||
0x2130: '\\mathcal{E}',
|
||||
0x2131: '\\mathcal{F}',
|
||||
0x2132: '\\Finv ',
|
||||
0x2133: '\\mathcal{M}',
|
||||
0x2135: '\\aleph ',
|
||||
0x2136: '\\beth ',
|
||||
0x2137: '\\gimel ',
|
||||
0x2138: '\\daleth ',
|
||||
0x2190: '\\leftarrow ',
|
||||
0x2191: '\\uparrow ',
|
||||
0x2192: '\\rightarrow ',
|
||||
0x2193: '\\downarrow ',
|
||||
0x2194: '\\leftrightarrow ',
|
||||
0x2195: '\\updownarrow ',
|
||||
0x2196: '\\nwarrow ',
|
||||
0x2197: '\\nearrow ',
|
||||
0x2198: '\\searrow ',
|
||||
0x2199: '\\swarrow ',
|
||||
0x219a: '\\nleftarrow ',
|
||||
0x219b: '\\nrightarrow ',
|
||||
0x219e: '\\twoheadleftarrow ',
|
||||
0x21a0: '\\twoheadrightarrow ',
|
||||
0x21a2: '\\leftarrowtail ',
|
||||
0x21a3: '\\rightarrowtail ',
|
||||
0x21a6: '\\mapsto ',
|
||||
0x21a9: '\\hookleftarrow ',
|
||||
0x21aa: '\\hookrightarrow ',
|
||||
0x21ab: '\\looparrowleft ',
|
||||
0x21ac: '\\looparrowright ',
|
||||
0x21ad: '\\leftrightsquigarrow ',
|
||||
0x21ae: '\\nleftrightarrow ',
|
||||
0x21b0: '\\Lsh ',
|
||||
0x21b1: '\\Rsh ',
|
||||
0x21b6: '\\curvearrowleft ',
|
||||
0x21b7: '\\curvearrowright ',
|
||||
0x21ba: '\\circlearrowleft ',
|
||||
0x21bb: '\\circlearrowright ',
|
||||
0x21bc: '\\leftharpoonup ',
|
||||
0x21bd: '\\leftharpoondown ',
|
||||
0x21be: '\\upharpoonright ',
|
||||
0x21bf: '\\upharpoonleft ',
|
||||
0x21c0: '\\rightharpoonup ',
|
||||
0x21c1: '\\rightharpoondown ',
|
||||
0x21c2: '\\downharpoonright ',
|
||||
0x21c3: '\\downharpoonleft ',
|
||||
0x21c4: '\\rightleftarrows ',
|
||||
0x21c6: '\\leftrightarrows ',
|
||||
0x21c7: '\\leftleftarrows ',
|
||||
0x21c8: '\\upuparrows ',
|
||||
0x21c9: '\\rightrightarrows ',
|
||||
0x21ca: '\\downdownarrows ',
|
||||
0x21cb: '\\leftrightharpoons ',
|
||||
0x21cc: '\\rightleftharpoons ',
|
||||
0x21cd: '\\nLeftarrow ',
|
||||
0x21ce: '\\nLeftrightarrow ',
|
||||
0x21cf: '\\nRightarrow ',
|
||||
0x21d0: '\\Leftarrow ',
|
||||
0x21d1: '\\Uparrow ',
|
||||
0x21d2: '\\Rightarrow ',
|
||||
0x21d3: '\\Downarrow ',
|
||||
0x21d4: '\\Leftrightarrow ',
|
||||
0x21d5: '\\Updownarrow ',
|
||||
0x21da: '\\Lleftarrow ',
|
||||
0x21db: '\\Rrightarrow ',
|
||||
0x21dd: '\\rightsquigarrow ',
|
||||
0x21e0: '\\dashleftarrow ',
|
||||
0x21e2: '\\dashrightarrow ',
|
||||
0x2200: '\\forall ',
|
||||
0x2201: '\\complement ',
|
||||
0x2202: '\\partial ',
|
||||
0x2203: '\\exists ',
|
||||
0x2204: '\\nexists ',
|
||||
0x2205: '\\emptyset ',
|
||||
0x2207: '\\nabla ',
|
||||
0x2208: '\\in ',
|
||||
0x2209: '\\notin ',
|
||||
0x220b: '\\ni ',
|
||||
0x220f: '\\prod ',
|
||||
0x2210: '\\coprod ',
|
||||
0x2211: '\\sum ',
|
||||
0x2212: '-',
|
||||
0x2213: '\\mp ',
|
||||
0x2214: '\\dotplus ',
|
||||
0x2215: '\\slash ',
|
||||
0x2216: '\\smallsetminus ',
|
||||
0x2217: '\\ast ',
|
||||
0x2218: '\\circ ',
|
||||
0x2219: '\\bullet ',
|
||||
0x221a: '\\surd ',
|
||||
0x221b: '\\sqrt[3] ',
|
||||
0x221c: '\\sqrt[4] ',
|
||||
0x221d: '\\propto ',
|
||||
0x221e: '\\infty ',
|
||||
0x2220: '\\angle ',
|
||||
0x2221: '\\measuredangle ',
|
||||
0x2222: '\\sphericalangle ',
|
||||
0x2223: '\\mid ',
|
||||
0x2224: '\\nmid ',
|
||||
0x2225: '\\parallel ',
|
||||
0x2226: '\\nparallel ',
|
||||
0x2227: '\\wedge ',
|
||||
0x2228: '\\vee ',
|
||||
0x2229: '\\cap ',
|
||||
0x222a: '\\cup ',
|
||||
0x222b: '\\int ',
|
||||
0x222c: '\\iint ',
|
||||
0x222d: '\\iiint ',
|
||||
0x222e: '\\oint ',
|
||||
0x2234: '\\therefore ',
|
||||
0x2235: '\\because ',
|
||||
0x2236: ':',
|
||||
0x223c: '\\sim ',
|
||||
0x223d: '\\backsim ',
|
||||
0x2240: '\\wr ',
|
||||
0x2241: '\\nsim ',
|
||||
0x2242: '\\eqsim ',
|
||||
0x2243: '\\simeq ',
|
||||
0x2245: '\\cong ',
|
||||
0x2247: '\\ncong ',
|
||||
0x2248: '\\approx ',
|
||||
0x224a: '\\approxeq ',
|
||||
0x224d: '\\asymp ',
|
||||
0x224e: '\\Bumpeq ',
|
||||
0x224f: '\\bumpeq ',
|
||||
0x2250: '\\doteq ',
|
||||
0x2251: '\\Doteq ',
|
||||
0x2252: '\\fallingdotseq ',
|
||||
0x2253: '\\risingdotseq ',
|
||||
0x2256: '\\eqcirc ',
|
||||
0x2257: '\\circeq ',
|
||||
0x225c: '\\triangleq ',
|
||||
0x2260: '\\neq ',
|
||||
0x2261: '\\equiv ',
|
||||
0x2264: '\\leq ',
|
||||
0x2265: '\\geq ',
|
||||
0x2266: '\\leqq ',
|
||||
0x2267: '\\geqq ',
|
||||
0x2268: '\\lneqq ',
|
||||
0x2269: '\\gneqq ',
|
||||
0x226a: '\\ll ',
|
||||
0x226b: '\\gg ',
|
||||
0x226c: '\\between ',
|
||||
0x226e: '\\nless ',
|
||||
0x226f: '\\ngtr ',
|
||||
0x2270: '\\nleq ',
|
||||
0x2271: '\\ngeq ',
|
||||
0x2272: '\\lesssim ',
|
||||
0x2273: '\\gtrsim ',
|
||||
0x2276: '\\lessgtr ',
|
||||
0x2277: '\\gtrless ',
|
||||
0x227a: '\\prec ',
|
||||
0x227b: '\\succ ',
|
||||
0x227c: '\\preccurlyeq ',
|
||||
0x227d: '\\succcurlyeq ',
|
||||
0x227e: '\\precsim ',
|
||||
0x227f: '\\succsim ',
|
||||
0x2280: '\\nprec ',
|
||||
0x2281: '\\nsucc ',
|
||||
0x2282: '\\subset ',
|
||||
0x2283: '\\supset ',
|
||||
0x2286: '\\subseteq ',
|
||||
0x2287: '\\supseteq ',
|
||||
0x2288: '\\nsubseteq ',
|
||||
0x2289: '\\nsupseteq ',
|
||||
0x228a: '\\subsetneq ',
|
||||
0x228b: '\\supsetneq ',
|
||||
0x228e: '\\uplus ',
|
||||
0x228f: '\\sqsubset ',
|
||||
0x2290: '\\sqsupset ',
|
||||
0x2291: '\\sqsubseteq ',
|
||||
0x2292: '\\sqsupseteq ',
|
||||
0x2293: '\\sqcap ',
|
||||
0x2294: '\\sqcup ',
|
||||
0x2295: '\\oplus ',
|
||||
0x2296: '\\ominus ',
|
||||
0x2297: '\\otimes ',
|
||||
0x2298: '\\oslash ',
|
||||
0x2299: '\\odot ',
|
||||
0x229a: '\\circledcirc ',
|
||||
0x229b: '\\circledast ',
|
||||
0x229d: '\\circleddash ',
|
||||
0x229e: '\\boxplus ',
|
||||
0x229f: '\\boxminus ',
|
||||
0x22a0: '\\boxtimes ',
|
||||
0x22a1: '\\boxdot ',
|
||||
0x22a2: '\\vdash ',
|
||||
0x22a3: '\\dashv ',
|
||||
0x22a4: '\\top ',
|
||||
0x22a5: '\\bot ',
|
||||
0x22a7: '\\models ',
|
||||
0x22a8: '\\vDash ',
|
||||
0x22a9: '\\Vdash ',
|
||||
0x22aa: '\\Vvdash ',
|
||||
0x22ac: '\\nvdash ',
|
||||
0x22ad: '\\nvDash ',
|
||||
0x22ae: '\\nVdash ',
|
||||
0x22af: '\\nVDash ',
|
||||
0x22b2: '\\vartriangleleft ',
|
||||
0x22b3: '\\vartriangleright ',
|
||||
0x22b4: '\\trianglelefteq ',
|
||||
0x22b5: '\\trianglerighteq ',
|
||||
0x22b8: '\\multimap ',
|
||||
0x22ba: '\\intercal ',
|
||||
0x22bb: '\\veebar ',
|
||||
0x22bc: '\\barwedge ',
|
||||
0x22c0: '\\bigwedge ',
|
||||
0x22c1: '\\bigvee ',
|
||||
0x22c2: '\\bigcap ',
|
||||
0x22c3: '\\bigcup ',
|
||||
0x22c4: '\\diamond ',
|
||||
0x22c5: '\\cdot ',
|
||||
0x22c6: '\\star ',
|
||||
0x22c7: '\\divideontimes ',
|
||||
0x22c8: '\\bowtie ',
|
||||
0x22c9: '\\ltimes ',
|
||||
0x22ca: '\\rtimes ',
|
||||
0x22cb: '\\leftthreetimes ',
|
||||
0x22cc: '\\rightthreetimes ',
|
||||
0x22cd: '\\backsimeq ',
|
||||
0x22ce: '\\curlyvee ',
|
||||
0x22cf: '\\curlywedge ',
|
||||
0x22d0: '\\Subset ',
|
||||
0x22d1: '\\Supset ',
|
||||
0x22d2: '\\Cap ',
|
||||
0x22d3: '\\Cup ',
|
||||
0x22d4: '\\pitchfork ',
|
||||
0x22d6: '\\lessdot ',
|
||||
0x22d7: '\\gtrdot ',
|
||||
0x22d8: '\\lll ',
|
||||
0x22d9: '\\ggg ',
|
||||
0x22da: '\\lesseqgtr ',
|
||||
0x22db: '\\gtreqless ',
|
||||
0x22de: '\\curlyeqprec ',
|
||||
0x22df: '\\curlyeqsucc ',
|
||||
0x22e0: '\\npreceq ',
|
||||
0x22e1: '\\nsucceq ',
|
||||
0x22e6: '\\lnsim ',
|
||||
0x22e7: '\\gnsim ',
|
||||
0x22e8: '\\precnsim ',
|
||||
0x22e9: '\\succnsim ',
|
||||
0x22ea: '\\ntriangleleft ',
|
||||
0x22eb: '\\ntriangleright ',
|
||||
0x22ec: '\\ntrianglelefteq ',
|
||||
0x22ed: '\\ntrianglerighteq ',
|
||||
0x22ee: '\\vdots ',
|
||||
0x22ef: '\\cdots ',
|
||||
0x22f1: '\\ddots ',
|
||||
0x2308: '\\lceil ',
|
||||
0x2309: '\\rceil ',
|
||||
0x230a: '\\lfloor ',
|
||||
0x230b: '\\rfloor ',
|
||||
0x231c: '\\ulcorner ',
|
||||
0x231d: '\\urcorner ',
|
||||
0x231e: '\\llcorner ',
|
||||
0x231f: '\\lrcorner ',
|
||||
0x2322: '\\frown ',
|
||||
0x2323: '\\smile ',
|
||||
0x23aa: '\\bracevert ',
|
||||
0x23b0: '\\lmoustache ',
|
||||
0x23b1: '\\rmoustache ',
|
||||
0x23d0: '\\arrowvert ',
|
||||
0x23de: '\\overbrace ',
|
||||
0x23df: '\\underbrace ',
|
||||
0x24c7: '\\circledR ',
|
||||
0x24c8: '\\circledS ',
|
||||
0x25b2: '\\blacktriangle ',
|
||||
0x25b3: '\\bigtriangleup ',
|
||||
0x25b7: '\\triangleright ',
|
||||
0x25bc: '\\blacktriangledown ',
|
||||
0x25bd: '\\bigtriangledown ',
|
||||
0x25c1: '\\triangleleft ',
|
||||
0x25c7: '\\Diamond ',
|
||||
0x25ca: '\\lozenge ',
|
||||
0x25ef: '\\bigcirc ',
|
||||
0x25fb: '\\square ',
|
||||
0x25fc: '\\blacksquare ',
|
||||
0x2605: '\\bigstar ',
|
||||
0x2660: '\\spadesuit ',
|
||||
0x2661: '\\heartsuit ',
|
||||
0x2662: '\\diamondsuit ',
|
||||
0x2663: '\\clubsuit ',
|
||||
0x266d: '\\flat ',
|
||||
0x266e: '\\natural ',
|
||||
0x266f: '\\sharp ',
|
||||
0x2713: '\\checkmark ',
|
||||
0x2720: '\\maltese ',
|
||||
0x27c2: '\\perp ',
|
||||
0x27cb: '\\diagup ',
|
||||
0x27cd: '\\diagdown ',
|
||||
0x27e8: '\\langle ',
|
||||
0x27e9: '\\rangle ',
|
||||
0x27ee: '\\lgroup ',
|
||||
0x27ef: '\\rgroup ',
|
||||
0x27f5: '\\longleftarrow ',
|
||||
0x27f6: '\\longrightarrow ',
|
||||
0x27f7: '\\longleftrightarrow ',
|
||||
0x27f8: '\\Longleftarrow ',
|
||||
0x27f9: '\\Longrightarrow ',
|
||||
0x27fa: '\\Longleftrightarrow ',
|
||||
0x27fc: '\\longmapsto ',
|
||||
0x29eb: '\\blacklozenge ',
|
||||
0x29f5: '\\setminus ',
|
||||
0x2a00: '\\bigodot ',
|
||||
0x2a01: '\\bigoplus ',
|
||||
0x2a02: '\\bigotimes ',
|
||||
0x2a04: '\\biguplus ',
|
||||
0x2a06: '\\bigsqcup ',
|
||||
0x2a0c: '\\iiiint ',
|
||||
0x2a3f: '\\amalg ',
|
||||
0x2a5e: '\\doublebarwedge ',
|
||||
0x2a7d: '\\leqslant ',
|
||||
0x2a7e: '\\geqslant ',
|
||||
0x2a85: '\\lessapprox ',
|
||||
0x2a86: '\\gtrapprox ',
|
||||
0x2a87: '\\lneq ',
|
||||
0x2a88: '\\gneq ',
|
||||
0x2a89: '\\lnapprox ',
|
||||
0x2a8a: '\\gnapprox ',
|
||||
0x2a8b: '\\lesseqqgtr ',
|
||||
0x2a8c: '\\gtreqqless ',
|
||||
0x2a95: '\\eqslantless ',
|
||||
0x2a96: '\\eqslantgtr ',
|
||||
0x2aaf: '\\preceq ',
|
||||
0x2ab0: '\\succeq ',
|
||||
0x2ab5: '\\precneqq ',
|
||||
0x2ab6: '\\succneqq ',
|
||||
0x2ab7: '\\precapprox ',
|
||||
0x2ab8: '\\succapprox ',
|
||||
0x2ab9: '\\precnapprox ',
|
||||
0x2aba: '\\succnapprox ',
|
||||
0x2ac5: '\\subseteqq ',
|
||||
0x2ac6: '\\supseteqq ',
|
||||
0x2acb: '\\subsetneqq ',
|
||||
0x2acc: '\\supsetneqq ',
|
||||
0x2b1c: '\\Box ',
|
||||
0x1d400: '\\mathbf{A}',
|
||||
0x1d401: '\\mathbf{B}',
|
||||
0x1d402: '\\mathbf{C}',
|
||||
0x1d403: '\\mathbf{D}',
|
||||
0x1d404: '\\mathbf{E}',
|
||||
0x1d405: '\\mathbf{F}',
|
||||
0x1d406: '\\mathbf{G}',
|
||||
0x1d407: '\\mathbf{H}',
|
||||
0x1d408: '\\mathbf{I}',
|
||||
0x1d409: '\\mathbf{J}',
|
||||
0x1d40a: '\\mathbf{K}',
|
||||
0x1d40b: '\\mathbf{L}',
|
||||
0x1d40c: '\\mathbf{M}',
|
||||
0x1d40d: '\\mathbf{N}',
|
||||
0x1d40e: '\\mathbf{O}',
|
||||
0x1d40f: '\\mathbf{P}',
|
||||
0x1d410: '\\mathbf{Q}',
|
||||
0x1d411: '\\mathbf{R}',
|
||||
0x1d412: '\\mathbf{S}',
|
||||
0x1d413: '\\mathbf{T}',
|
||||
0x1d414: '\\mathbf{U}',
|
||||
0x1d415: '\\mathbf{V}',
|
||||
0x1d416: '\\mathbf{W}',
|
||||
0x1d417: '\\mathbf{X}',
|
||||
0x1d418: '\\mathbf{Y}',
|
||||
0x1d419: '\\mathbf{Z}',
|
||||
0x1d41a: '\\mathbf{a}',
|
||||
0x1d41b: '\\mathbf{b}',
|
||||
0x1d41c: '\\mathbf{c}',
|
||||
0x1d41d: '\\mathbf{d}',
|
||||
0x1d41e: '\\mathbf{e}',
|
||||
0x1d41f: '\\mathbf{f}',
|
||||
0x1d420: '\\mathbf{g}',
|
||||
0x1d421: '\\mathbf{h}',
|
||||
0x1d422: '\\mathbf{i}',
|
||||
0x1d423: '\\mathbf{j}',
|
||||
0x1d424: '\\mathbf{k}',
|
||||
0x1d425: '\\mathbf{l}',
|
||||
0x1d426: '\\mathbf{m}',
|
||||
0x1d427: '\\mathbf{n}',
|
||||
0x1d428: '\\mathbf{o}',
|
||||
0x1d429: '\\mathbf{p}',
|
||||
0x1d42a: '\\mathbf{q}',
|
||||
0x1d42b: '\\mathbf{r}',
|
||||
0x1d42c: '\\mathbf{s}',
|
||||
0x1d42d: '\\mathbf{t}',
|
||||
0x1d42e: '\\mathbf{u}',
|
||||
0x1d42f: '\\mathbf{v}',
|
||||
0x1d430: '\\mathbf{w}',
|
||||
0x1d431: '\\mathbf{x}',
|
||||
0x1d432: '\\mathbf{y}',
|
||||
0x1d433: '\\mathbf{z}',
|
||||
0x1d434: 'A',
|
||||
0x1d435: 'B',
|
||||
0x1d436: 'C',
|
||||
0x1d437: 'D',
|
||||
0x1d438: 'E',
|
||||
0x1d439: 'F',
|
||||
0x1d43a: 'G',
|
||||
0x1d43b: 'H',
|
||||
0x1d43c: 'I',
|
||||
0x1d43d: 'J',
|
||||
0x1d43e: 'K',
|
||||
0x1d43f: 'L',
|
||||
0x1d440: 'M',
|
||||
0x1d441: 'N',
|
||||
0x1d442: 'O',
|
||||
0x1d443: 'P',
|
||||
0x1d444: 'Q',
|
||||
0x1d445: 'R',
|
||||
0x1d446: 'S',
|
||||
0x1d447: 'T',
|
||||
0x1d448: 'U',
|
||||
0x1d449: 'V',
|
||||
0x1d44a: 'W',
|
||||
0x1d44b: 'X',
|
||||
0x1d44c: 'Y',
|
||||
0x1d44d: 'Z',
|
||||
0x1d44e: 'a',
|
||||
0x1d44f: 'b',
|
||||
0x1d450: 'c',
|
||||
0x1d451: 'd',
|
||||
0x1d452: 'e',
|
||||
0x1d453: 'f',
|
||||
0x1d454: 'g',
|
||||
0x1d456: 'i',
|
||||
0x1d457: 'j',
|
||||
0x1d458: 'k',
|
||||
0x1d459: 'l',
|
||||
0x1d45a: 'm',
|
||||
0x1d45b: 'n',
|
||||
0x1d45c: 'o',
|
||||
0x1d45d: 'p',
|
||||
0x1d45e: 'q',
|
||||
0x1d45f: 'r',
|
||||
0x1d460: 's',
|
||||
0x1d461: 't',
|
||||
0x1d462: 'u',
|
||||
0x1d463: 'v',
|
||||
0x1d464: 'w',
|
||||
0x1d465: 'x',
|
||||
0x1d466: 'y',
|
||||
0x1d467: 'z',
|
||||
0x1d49c: '\\mathcal{A}',
|
||||
0x1d49e: '\\mathcal{C}',
|
||||
0x1d49f: '\\mathcal{D}',
|
||||
0x1d4a2: '\\mathcal{G}',
|
||||
0x1d4a5: '\\mathcal{J}',
|
||||
0x1d4a6: '\\mathcal{K}',
|
||||
0x1d4a9: '\\mathcal{N}',
|
||||
0x1d4aa: '\\mathcal{O}',
|
||||
0x1d4ab: '\\mathcal{P}',
|
||||
0x1d4ac: '\\mathcal{Q}',
|
||||
0x1d4ae: '\\mathcal{S}',
|
||||
0x1d4af: '\\mathcal{T}',
|
||||
0x1d4b0: '\\mathcal{U}',
|
||||
0x1d4b1: '\\mathcal{V}',
|
||||
0x1d4b2: '\\mathcal{W}',
|
||||
0x1d4b3: '\\mathcal{X}',
|
||||
0x1d4b4: '\\mathcal{Y}',
|
||||
0x1d4b5: '\\mathcal{Z}',
|
||||
0x1d504: '\\mathfrak{A}',
|
||||
0x1d505: '\\mathfrak{B}',
|
||||
0x1d507: '\\mathfrak{D}',
|
||||
0x1d508: '\\mathfrak{E}',
|
||||
0x1d509: '\\mathfrak{F}',
|
||||
0x1d50a: '\\mathfrak{G}',
|
||||
0x1d50d: '\\mathfrak{J}',
|
||||
0x1d50e: '\\mathfrak{K}',
|
||||
0x1d50f: '\\mathfrak{L}',
|
||||
0x1d510: '\\mathfrak{M}',
|
||||
0x1d511: '\\mathfrak{N}',
|
||||
0x1d512: '\\mathfrak{O}',
|
||||
0x1d513: '\\mathfrak{P}',
|
||||
0x1d514: '\\mathfrak{Q}',
|
||||
0x1d516: '\\mathfrak{S}',
|
||||
0x1d517: '\\mathfrak{T}',
|
||||
0x1d518: '\\mathfrak{U}',
|
||||
0x1d519: '\\mathfrak{V}',
|
||||
0x1d51a: '\\mathfrak{W}',
|
||||
0x1d51b: '\\mathfrak{X}',
|
||||
0x1d51c: '\\mathfrak{Y}',
|
||||
0x1d51e: '\\mathfrak{a}',
|
||||
0x1d51f: '\\mathfrak{b}',
|
||||
0x1d520: '\\mathfrak{c}',
|
||||
0x1d521: '\\mathfrak{d}',
|
||||
0x1d522: '\\mathfrak{e}',
|
||||
0x1d523: '\\mathfrak{f}',
|
||||
0x1d524: '\\mathfrak{g}',
|
||||
0x1d525: '\\mathfrak{h}',
|
||||
0x1d526: '\\mathfrak{i}',
|
||||
0x1d527: '\\mathfrak{j}',
|
||||
0x1d528: '\\mathfrak{k}',
|
||||
0x1d529: '\\mathfrak{l}',
|
||||
0x1d52a: '\\mathfrak{m}',
|
||||
0x1d52b: '\\mathfrak{n}',
|
||||
0x1d52c: '\\mathfrak{o}',
|
||||
0x1d52d: '\\mathfrak{p}',
|
||||
0x1d52e: '\\mathfrak{q}',
|
||||
0x1d52f: '\\mathfrak{r}',
|
||||
0x1d530: '\\mathfrak{s}',
|
||||
0x1d531: '\\mathfrak{t}',
|
||||
0x1d532: '\\mathfrak{u}',
|
||||
0x1d533: '\\mathfrak{v}',
|
||||
0x1d534: '\\mathfrak{w}',
|
||||
0x1d535: '\\mathfrak{x}',
|
||||
0x1d536: '\\mathfrak{y}',
|
||||
0x1d537: '\\mathfrak{z}',
|
||||
0x1d538: '\\mathbb{A}',
|
||||
0x1d539: '\\mathbb{B}',
|
||||
0x1d53b: '\\mathbb{D}',
|
||||
0x1d53c: '\\mathbb{E}',
|
||||
0x1d53d: '\\mathbb{F}',
|
||||
0x1d53e: '\\mathbb{G}',
|
||||
0x1d540: '\\mathbb{I}',
|
||||
0x1d541: '\\mathbb{J}',
|
||||
0x1d542: '\\mathbb{K}',
|
||||
0x1d543: '\\mathbb{L}',
|
||||
0x1d544: '\\mathbb{M}',
|
||||
0x1d546: '\\mathbb{O}',
|
||||
0x1d54a: '\\mathbb{S}',
|
||||
0x1d54b: '\\mathbb{T}',
|
||||
0x1d54c: '\\mathbb{U}',
|
||||
0x1d54d: '\\mathbb{V}',
|
||||
0x1d54e: '\\mathbb{W}',
|
||||
0x1d54f: '\\mathbb{X}',
|
||||
0x1d550: '\\mathbb{Y}',
|
||||
0x1d55c: '\\Bbbk ',
|
||||
0x1d5a0: '\\mathsf{A}',
|
||||
0x1d5a1: '\\mathsf{B}',
|
||||
0x1d5a2: '\\mathsf{C}',
|
||||
0x1d5a3: '\\mathsf{D}',
|
||||
0x1d5a4: '\\mathsf{E}',
|
||||
0x1d5a5: '\\mathsf{F}',
|
||||
0x1d5a6: '\\mathsf{G}',
|
||||
0x1d5a7: '\\mathsf{H}',
|
||||
0x1d5a8: '\\mathsf{I}',
|
||||
0x1d5a9: '\\mathsf{J}',
|
||||
0x1d5aa: '\\mathsf{K}',
|
||||
0x1d5ab: '\\mathsf{L}',
|
||||
0x1d5ac: '\\mathsf{M}',
|
||||
0x1d5ad: '\\mathsf{N}',
|
||||
0x1d5ae: '\\mathsf{O}',
|
||||
0x1d5af: '\\mathsf{P}',
|
||||
0x1d5b0: '\\mathsf{Q}',
|
||||
0x1d5b1: '\\mathsf{R}',
|
||||
0x1d5b2: '\\mathsf{S}',
|
||||
0x1d5b3: '\\mathsf{T}',
|
||||
0x1d5b4: '\\mathsf{U}',
|
||||
0x1d5b5: '\\mathsf{V}',
|
||||
0x1d5b6: '\\mathsf{W}',
|
||||
0x1d5b7: '\\mathsf{X}',
|
||||
0x1d5b8: '\\mathsf{Y}',
|
||||
0x1d5b9: '\\mathsf{Z}',
|
||||
0x1d5ba: '\\mathsf{a}',
|
||||
0x1d5bb: '\\mathsf{b}',
|
||||
0x1d5bc: '\\mathsf{c}',
|
||||
0x1d5bd: '\\mathsf{d}',
|
||||
0x1d5be: '\\mathsf{e}',
|
||||
0x1d5bf: '\\mathsf{f}',
|
||||
0x1d5c0: '\\mathsf{g}',
|
||||
0x1d5c1: '\\mathsf{h}',
|
||||
0x1d5c2: '\\mathsf{i}',
|
||||
0x1d5c3: '\\mathsf{j}',
|
||||
0x1d5c4: '\\mathsf{k}',
|
||||
0x1d5c5: '\\mathsf{l}',
|
||||
0x1d5c6: '\\mathsf{m}',
|
||||
0x1d5c7: '\\mathsf{n}',
|
||||
0x1d5c8: '\\mathsf{o}',
|
||||
0x1d5c9: '\\mathsf{p}',
|
||||
0x1d5ca: '\\mathsf{q}',
|
||||
0x1d5cb: '\\mathsf{r}',
|
||||
0x1d5cc: '\\mathsf{s}',
|
||||
0x1d5cd: '\\mathsf{t}',
|
||||
0x1d5ce: '\\mathsf{u}',
|
||||
0x1d5cf: '\\mathsf{v}',
|
||||
0x1d5d0: '\\mathsf{w}',
|
||||
0x1d5d1: '\\mathsf{x}',
|
||||
0x1d5d2: '\\mathsf{y}',
|
||||
0x1d5d3: '\\mathsf{z}',
|
||||
0x1d670: '\\mathtt{A}',
|
||||
0x1d671: '\\mathtt{B}',
|
||||
0x1d672: '\\mathtt{C}',
|
||||
0x1d673: '\\mathtt{D}',
|
||||
0x1d674: '\\mathtt{E}',
|
||||
0x1d675: '\\mathtt{F}',
|
||||
0x1d676: '\\mathtt{G}',
|
||||
0x1d677: '\\mathtt{H}',
|
||||
0x1d678: '\\mathtt{I}',
|
||||
0x1d679: '\\mathtt{J}',
|
||||
0x1d67a: '\\mathtt{K}',
|
||||
0x1d67b: '\\mathtt{L}',
|
||||
0x1d67c: '\\mathtt{M}',
|
||||
0x1d67d: '\\mathtt{N}',
|
||||
0x1d67e: '\\mathtt{O}',
|
||||
0x1d67f: '\\mathtt{P}',
|
||||
0x1d680: '\\mathtt{Q}',
|
||||
0x1d681: '\\mathtt{R}',
|
||||
0x1d682: '\\mathtt{S}',
|
||||
0x1d683: '\\mathtt{T}',
|
||||
0x1d684: '\\mathtt{U}',
|
||||
0x1d685: '\\mathtt{V}',
|
||||
0x1d686: '\\mathtt{W}',
|
||||
0x1d687: '\\mathtt{X}',
|
||||
0x1d688: '\\mathtt{Y}',
|
||||
0x1d689: '\\mathtt{Z}',
|
||||
0x1d68a: '\\mathtt{a}',
|
||||
0x1d68b: '\\mathtt{b}',
|
||||
0x1d68c: '\\mathtt{c}',
|
||||
0x1d68d: '\\mathtt{d}',
|
||||
0x1d68e: '\\mathtt{e}',
|
||||
0x1d68f: '\\mathtt{f}',
|
||||
0x1d690: '\\mathtt{g}',
|
||||
0x1d691: '\\mathtt{h}',
|
||||
0x1d692: '\\mathtt{i}',
|
||||
0x1d693: '\\mathtt{j}',
|
||||
0x1d694: '\\mathtt{k}',
|
||||
0x1d695: '\\mathtt{l}',
|
||||
0x1d696: '\\mathtt{m}',
|
||||
0x1d697: '\\mathtt{n}',
|
||||
0x1d698: '\\mathtt{o}',
|
||||
0x1d699: '\\mathtt{p}',
|
||||
0x1d69a: '\\mathtt{q}',
|
||||
0x1d69b: '\\mathtt{r}',
|
||||
0x1d69c: '\\mathtt{s}',
|
||||
0x1d69d: '\\mathtt{t}',
|
||||
0x1d69e: '\\mathtt{u}',
|
||||
0x1d69f: '\\mathtt{v}',
|
||||
0x1d6a0: '\\mathtt{w}',
|
||||
0x1d6a1: '\\mathtt{x}',
|
||||
0x1d6a2: '\\mathtt{y}',
|
||||
0x1d6a3: '\\mathtt{z}',
|
||||
0x1d6a4: '\\imath ',
|
||||
0x1d6a5: '\\jmath ',
|
||||
0x1d6aa: '\\mathbf{\\Gamma}',
|
||||
0x1d6ab: '\\mathbf{\\Delta}',
|
||||
0x1d6af: '\\mathbf{\\Theta}',
|
||||
0x1d6b2: '\\mathbf{\\Lambda}',
|
||||
0x1d6b5: '\\mathbf{\\Xi}',
|
||||
0x1d6b7: '\\mathbf{\\Pi}',
|
||||
0x1d6ba: '\\mathbf{\\Sigma}',
|
||||
0x1d6bc: '\\mathbf{\\Upsilon}',
|
||||
0x1d6bd: '\\mathbf{\\Phi}',
|
||||
0x1d6bf: '\\mathbf{\\Psi}',
|
||||
0x1d6c0: '\\mathbf{\\Omega}',
|
||||
0x1d6e4: '\\mathit{\\Gamma}',
|
||||
0x1d6e5: '\\mathit{\\Delta}',
|
||||
0x1d6e9: '\\mathit{\\Theta}',
|
||||
0x1d6ec: '\\mathit{\\Lambda}',
|
||||
0x1d6ef: '\\mathit{\\Xi}',
|
||||
0x1d6f1: '\\mathit{\\Pi}',
|
||||
0x1d6f4: '\\mathit{\\Sigma}',
|
||||
0x1d6f6: '\\mathit{\\Upsilon}',
|
||||
0x1d6f7: '\\mathit{\\Phi}',
|
||||
0x1d6f9: '\\mathit{\\Psi}',
|
||||
0x1d6fa: '\\mathit{\\Omega}',
|
||||
0x1d6fc: '\\alpha ',
|
||||
0x1d6fd: '\\beta ',
|
||||
0x1d6fe: '\\gamma ',
|
||||
0x1d6ff: '\\delta ',
|
||||
0x1d700: '\\varepsilon ',
|
||||
0x1d701: '\\zeta ',
|
||||
0x1d702: '\\eta ',
|
||||
0x1d703: '\\theta ',
|
||||
0x1d704: '\\iota ',
|
||||
0x1d705: '\\kappa ',
|
||||
0x1d706: '\\lambda ',
|
||||
0x1d707: '\\mu ',
|
||||
0x1d708: '\\nu ',
|
||||
0x1d709: '\\xi ',
|
||||
0x1d70b: '\\pi ',
|
||||
0x1d70c: '\\rho ',
|
||||
0x1d70d: '\\varsigma ',
|
||||
0x1d70e: '\\sigma ',
|
||||
0x1d70f: '\\tau ',
|
||||
0x1d710: '\\upsilon ',
|
||||
0x1d711: '\\varphi ',
|
||||
0x1d712: '\\chi ',
|
||||
0x1d713: '\\psi ',
|
||||
0x1d714: '\\omega ',
|
||||
0x1d715: '\\partial ',
|
||||
0x1d716: '\\epsilon ',
|
||||
0x1d717: '\\vartheta ',
|
||||
0x1d718: '\\varkappa ',
|
||||
0x1d719: '\\phi ',
|
||||
0x1d71a: '\\varrho ',
|
||||
0x1d71b: '\\varpi ',
|
||||
0x1d7ce: '\\mathbf{0}',
|
||||
0x1d7cf: '\\mathbf{1}',
|
||||
0x1d7d0: '\\mathbf{2}',
|
||||
0x1d7d1: '\\mathbf{3}',
|
||||
0x1d7d2: '\\mathbf{4}',
|
||||
0x1d7d3: '\\mathbf{5}',
|
||||
0x1d7d4: '\\mathbf{6}',
|
||||
0x1d7d5: '\\mathbf{7}',
|
||||
0x1d7d6: '\\mathbf{8}',
|
||||
0x1d7d7: '\\mathbf{9}',
|
||||
0x1d7e2: '\\mathsf{0}',
|
||||
0x1d7e3: '\\mathsf{1}',
|
||||
0x1d7e4: '\\mathsf{2}',
|
||||
0x1d7e5: '\\mathsf{3}',
|
||||
0x1d7e6: '\\mathsf{4}',
|
||||
0x1d7e7: '\\mathsf{5}',
|
||||
0x1d7e8: '\\mathsf{6}',
|
||||
0x1d7e9: '\\mathsf{7}',
|
||||
0x1d7ea: '\\mathsf{8}',
|
||||
0x1d7eb: '\\mathsf{9}',
|
||||
0x1d7f6: '\\mathtt{0}',
|
||||
0x1d7f7: '\\mathtt{1}',
|
||||
0x1d7f8: '\\mathtt{2}',
|
||||
0x1d7f9: '\\mathtt{3}',
|
||||
0x1d7fa: '\\mathtt{4}',
|
||||
0x1d7fb: '\\mathtt{5}',
|
||||
0x1d7fc: '\\mathtt{6}',
|
||||
0x1d7fd: '\\mathtt{7}',
|
||||
0x1d7fe: '\\mathtt{8}',
|
||||
0x1d7ff: '\\mathtt{9}',
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
# :Id: $Id: punctuation_chars.py 9270 2022-11-24 20:28:03Z milde $
|
||||
# :Copyright: © 2011, 2017 Günter Milde.
|
||||
# :License: Released under the terms of the `2-Clause BSD license`_, in short:
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved.
|
||||
# This file is offered as-is, without any warranty.
|
||||
#
|
||||
# .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause
|
||||
#
|
||||
# This file is generated by
|
||||
# ``docutils/tools/dev/generate_punctuation_chars.py``.
|
||||
# ::
|
||||
|
||||
"""Docutils character category patterns.
|
||||
|
||||
Patterns for the implementation of the `inline markup recognition rules`_
|
||||
in the reStructuredText parser `docutils.parsers.rst.states.py` based
|
||||
on Unicode character categories.
|
||||
The patterns are used inside ``[ ]`` in regular expressions.
|
||||
|
||||
Rule (5) requires determination of matching open/close pairs. However, the
|
||||
pairing of open/close quotes is ambiguous due to different typographic
|
||||
conventions in different languages. The ``quote_pairs`` function tests
|
||||
whether two characters form an open/close pair.
|
||||
|
||||
The patterns are generated by
|
||||
``docutils/tools/dev/generate_punctuation_chars.py`` to prevent dependence
|
||||
on the Python version and avoid the time-consuming generation with every
|
||||
Docutils run. See there for motives and implementation details.
|
||||
|
||||
The category of some characters changed with the development of the
|
||||
Unicode standard. The current lists are generated with the help of the
|
||||
"unicodedata" module of Python 2.7.13 (based on Unicode version 5.2.0).
|
||||
|
||||
.. _inline markup recognition rules:
|
||||
https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html
|
||||
#inline-markup-recognition-rules
|
||||
"""
|
||||
|
||||
openers = (
|
||||
'"\'(<\\[{\u0f3a\u0f3c\u169b\u2045\u207d\u208d\u2329\u2768'
|
||||
'\u276a\u276c\u276e\u2770\u2772\u2774\u27c5\u27e6\u27e8\u27ea'
|
||||
'\u27ec\u27ee\u2983\u2985\u2987\u2989\u298b\u298d\u298f\u2991'
|
||||
'\u2993\u2995\u2997\u29d8\u29da\u29fc\u2e22\u2e24\u2e26\u2e28'
|
||||
'\u3008\u300a\u300c\u300e\u3010\u3014\u3016\u3018\u301a\u301d'
|
||||
'\u301d\ufd3e\ufe17\ufe35\ufe37\ufe39\ufe3b\ufe3d\ufe3f\ufe41'
|
||||
'\ufe43\ufe47\ufe59\ufe5b\ufe5d\uff08\uff3b\uff5b\uff5f\uff62'
|
||||
'\xab\u2018\u201c\u2039\u2e02\u2e04\u2e09\u2e0c\u2e1c\u2e20'
|
||||
'\u201a\u201e\xbb\u2019\u201d\u203a\u2e03\u2e05\u2e0a\u2e0d'
|
||||
'\u2e1d\u2e21\u201b\u201f'
|
||||
)
|
||||
closers = (
|
||||
'"\')>\\]}\u0f3b\u0f3d\u169c\u2046\u207e\u208e\u232a\u2769'
|
||||
'\u276b\u276d\u276f\u2771\u2773\u2775\u27c6\u27e7\u27e9\u27eb'
|
||||
'\u27ed\u27ef\u2984\u2986\u2988\u298a\u298c\u298e\u2990\u2992'
|
||||
'\u2994\u2996\u2998\u29d9\u29db\u29fd\u2e23\u2e25\u2e27\u2e29'
|
||||
'\u3009\u300b\u300d\u300f\u3011\u3015\u3017\u3019\u301b\u301e'
|
||||
'\u301f\ufd3f\ufe18\ufe36\ufe38\ufe3a\ufe3c\ufe3e\ufe40\ufe42'
|
||||
'\ufe44\ufe48\ufe5a\ufe5c\ufe5e\uff09\uff3d\uff5d\uff60\uff63'
|
||||
'\xbb\u2019\u201d\u203a\u2e03\u2e05\u2e0a\u2e0d\u2e1d\u2e21'
|
||||
'\u201b\u201f\xab\u2018\u201c\u2039\u2e02\u2e04\u2e09\u2e0c'
|
||||
'\u2e1c\u2e20\u201a\u201e'
|
||||
)
|
||||
delimiters = (
|
||||
'\\-/:\u058a\xa1\xb7\xbf\u037e\u0387\u055a-\u055f\u0589'
|
||||
'\u05be\u05c0\u05c3\u05c6\u05f3\u05f4\u0609\u060a\u060c'
|
||||
'\u060d\u061b\u061e\u061f\u066a-\u066d\u06d4\u0700-\u070d'
|
||||
'\u07f7-\u07f9\u0830-\u083e\u0964\u0965\u0970\u0df4\u0e4f'
|
||||
'\u0e5a\u0e5b\u0f04-\u0f12\u0f85\u0fd0-\u0fd4\u104a-\u104f'
|
||||
'\u10fb\u1361-\u1368\u1400\u166d\u166e\u16eb-\u16ed\u1735'
|
||||
'\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u180a\u1944\u1945'
|
||||
'\u19de\u19df\u1a1e\u1a1f\u1aa0-\u1aa6\u1aa8-\u1aad\u1b5a-'
|
||||
'\u1b60\u1c3b-\u1c3f\u1c7e\u1c7f\u1cd3\u2010-\u2017\u2020-'
|
||||
'\u2027\u2030-\u2038\u203b-\u203e\u2041-\u2043\u2047-'
|
||||
'\u2051\u2053\u2055-\u205e\u2cf9-\u2cfc\u2cfe\u2cff\u2e00'
|
||||
'\u2e01\u2e06-\u2e08\u2e0b\u2e0e-\u2e1b\u2e1e\u2e1f\u2e2a-'
|
||||
'\u2e2e\u2e30\u2e31\u3001-\u3003\u301c\u3030\u303d\u30a0'
|
||||
'\u30fb\ua4fe\ua4ff\ua60d-\ua60f\ua673\ua67e\ua6f2-\ua6f7'
|
||||
'\ua874-\ua877\ua8ce\ua8cf\ua8f8-\ua8fa\ua92e\ua92f\ua95f'
|
||||
'\ua9c1-\ua9cd\ua9de\ua9df\uaa5c-\uaa5f\uaade\uaadf\uabeb'
|
||||
'\ufe10-\ufe16\ufe19\ufe30-\ufe32\ufe45\ufe46\ufe49-\ufe4c'
|
||||
'\ufe50-\ufe52\ufe54-\ufe58\ufe5f-\ufe61\ufe63\ufe68\ufe6a'
|
||||
'\ufe6b\uff01-\uff03\uff05-\uff07\uff0a\uff0c-\uff0f\uff1a'
|
||||
'\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65'
|
||||
'\U00010100\U00010101\U0001039f\U000103d0\U00010857'
|
||||
'\U0001091f\U0001093f\U00010a50-\U00010a58\U00010a7f'
|
||||
'\U00010b39-\U00010b3f\U000110bb\U000110bc\U000110be-'
|
||||
'\U000110c1\U00012470-\U00012473'
|
||||
)
|
||||
closing_delimiters = r'\\.,;!?'
|
||||
|
||||
|
||||
# Matching open/close quotes
|
||||
# --------------------------
|
||||
|
||||
# Matching open/close pairs are at the same position in
|
||||
# `punctuation_chars.openers` and `punctuation_chars.closers`.
|
||||
# Additional matches (due to different typographic conventions
|
||||
# in different languages) are stored in `quote_pairs`.
|
||||
|
||||
quote_pairs = {
|
||||
# open char: matching closing characters # use case
|
||||
'\xbb': '\xbb', # » » Swedish
|
||||
'\u2018': '\u201a', # ‘ ‚ Albanian/Greek/Turkish
|
||||
'\u2019': '\u2019', # ’ ’ Swedish
|
||||
'\u201a': '\u2018\u2019', # ‚ ‘ German, ‚ ’ Polish
|
||||
'\u201c': '\u201e', # “ „ Albanian/Greek/Turkish
|
||||
'\u201e': '\u201c\u201d', # „ “ German, „ ” Polish
|
||||
'\u201d': '\u201d', # ” ” Swedish
|
||||
'\u203a': '\u203a', # › › Swedish
|
||||
}
|
||||
"""Additional open/close quote pairs."""
|
||||
|
||||
|
||||
def match_chars(c1, c2):
|
||||
"""Test whether `c1` and `c2` are a matching open/close character pair."""
|
||||
try:
|
||||
i = openers.index(c1)
|
||||
except ValueError: # c1 not in openers
|
||||
return False
|
||||
return c2 == closers[i] or c2 in quote_pairs.get(c1, '')
|
||||
@@ -0,0 +1,86 @@
|
||||
"""Convert to and from Roman numerals"""
|
||||
|
||||
__author__ = "Mark Pilgrim (f8dy@diveintopython.org)"
|
||||
__version__ = "1.4"
|
||||
__date__ = "8 August 2001"
|
||||
__copyright__ = """Copyright (c) 2001 Mark Pilgrim
|
||||
|
||||
This program is part of "Dive Into Python", a free Python tutorial for
|
||||
experienced programmers. Visit http://diveintopython.org/ for the
|
||||
latest version.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the Python 2.1.1 license, available at
|
||||
http://www.python.org/2.1.1/license.html
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
# Define exceptions
|
||||
class RomanError(Exception): pass
|
||||
class OutOfRangeError(RomanError): pass
|
||||
class NotIntegerError(RomanError): pass
|
||||
class InvalidRomanNumeralError(RomanError): pass
|
||||
|
||||
|
||||
# Define digit mapping
|
||||
romanNumeralMap = (('M', 1000),
|
||||
('CM', 900),
|
||||
('D', 500),
|
||||
('CD', 400),
|
||||
('C', 100),
|
||||
('XC', 90),
|
||||
('L', 50),
|
||||
('XL', 40),
|
||||
('X', 10),
|
||||
('IX', 9),
|
||||
('V', 5),
|
||||
('IV', 4),
|
||||
('I', 1))
|
||||
|
||||
|
||||
def toRoman(n):
|
||||
"""convert integer to Roman numeral"""
|
||||
if not (0 < n < 5000):
|
||||
raise OutOfRangeError("number out of range (must be 1..4999)")
|
||||
if int(n) != n:
|
||||
raise NotIntegerError("decimals can not be converted")
|
||||
|
||||
result = ""
|
||||
for numeral, integer in romanNumeralMap:
|
||||
while n >= integer:
|
||||
result += numeral
|
||||
n -= integer
|
||||
return result
|
||||
|
||||
|
||||
# Define pattern to detect valid Roman numerals
|
||||
romanNumeralPattern = re.compile("""
|
||||
^ # beginning of string
|
||||
M{0,4} # thousands - 0 to 4 M's
|
||||
(CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
|
||||
# or 500-800 (D, followed by 0 to 3 C's)
|
||||
(XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
|
||||
# or 50-80 (L, followed by 0 to 3 X's)
|
||||
(IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
|
||||
# or 5-8 (V, followed by 0 to 3 I's)
|
||||
$ # end of string
|
||||
""", re.VERBOSE)
|
||||
|
||||
|
||||
def fromRoman(s):
|
||||
"""convert Roman numeral to integer"""
|
||||
if not s:
|
||||
raise InvalidRomanNumeralError('Input can not be blank')
|
||||
|
||||
if not romanNumeralPattern.search(s):
|
||||
raise InvalidRomanNumeralError('Invalid Roman numeral: %s' % s)
|
||||
|
||||
result = 0
|
||||
index = 0
|
||||
for numeral, integer in romanNumeralMap:
|
||||
while s[index:index+len(numeral)] == numeral:
|
||||
result += integer
|
||||
index += len(numeral)
|
||||
return result
|
||||
@@ -0,0 +1,993 @@
|
||||
#!/usr/bin/python3
|
||||
# :Id: $Id: smartquotes.py 9335 2023-04-06 18:40:57Z milde $
|
||||
# :Copyright: © 2010 Günter Milde,
|
||||
# original `SmartyPants`_: © 2003 John Gruber
|
||||
# smartypants.py: © 2004, 2007 Chad Miller
|
||||
# :Maintainer: docutils-develop@lists.sourceforge.net
|
||||
# :License: Released under the terms of the `2-Clause BSD license`_, in short:
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notices and this notice are preserved.
|
||||
# This file is offered as-is, without any warranty.
|
||||
#
|
||||
# .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause
|
||||
|
||||
|
||||
r"""
|
||||
=========================
|
||||
Smart Quotes for Docutils
|
||||
=========================
|
||||
|
||||
Synopsis
|
||||
========
|
||||
|
||||
"SmartyPants" is a free web publishing plug-in for Movable Type, Blosxom, and
|
||||
BBEdit that easily translates plain ASCII punctuation characters into "smart"
|
||||
typographic punctuation characters.
|
||||
|
||||
``smartquotes.py`` is an adaption of "SmartyPants" to Docutils_.
|
||||
|
||||
* Using Unicode instead of HTML entities for typographic punctuation
|
||||
characters, it works for any output format that supports Unicode.
|
||||
* Supports `language specific quote characters`__.
|
||||
|
||||
__ https://en.wikipedia.org/wiki/Non-English_usage_of_quotation_marks
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
`John Gruber`_ did all of the hard work of writing this software in Perl for
|
||||
`Movable Type`_ and almost all of this useful documentation. `Chad Miller`_
|
||||
ported it to Python to use with Pyblosxom_.
|
||||
Adapted to Docutils_ by Günter Milde.
|
||||
|
||||
Additional Credits
|
||||
==================
|
||||
|
||||
Portions of the SmartyPants original work are based on Brad Choate's nifty
|
||||
MTRegex plug-in. `Brad Choate`_ also contributed a few bits of source code to
|
||||
this plug-in. Brad Choate is a fine hacker indeed.
|
||||
|
||||
`Jeremy Hedley`_ and `Charles Wiltgen`_ deserve mention for exemplary beta
|
||||
testing of the original SmartyPants.
|
||||
|
||||
`Rael Dornfest`_ ported SmartyPants to Blosxom.
|
||||
|
||||
.. _Brad Choate: http://bradchoate.com/
|
||||
.. _Jeremy Hedley: http://antipixel.com/
|
||||
.. _Charles Wiltgen: http://playbacktime.com/
|
||||
.. _Rael Dornfest: http://raelity.org/
|
||||
|
||||
|
||||
Copyright and License
|
||||
=====================
|
||||
|
||||
SmartyPants_ license (3-Clause BSD license):
|
||||
|
||||
Copyright (c) 2003 John Gruber (http://daringfireball.net/)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name "SmartyPants" nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors
|
||||
"as is" and any express or implied warranties, including, but not
|
||||
limited to, the implied warranties of merchantability and fitness for
|
||||
a particular purpose are disclaimed. In no event shall the copyright
|
||||
owner or contributors be liable for any direct, indirect, incidental,
|
||||
special, exemplary, or consequential damages (including, but not
|
||||
limited to, procurement of substitute goods or services; loss of use,
|
||||
data, or profits; or business interruption) however caused and on any
|
||||
theory of liability, whether in contract, strict liability, or tort
|
||||
(including negligence or otherwise) arising in any way out of the use
|
||||
of this software, even if advised of the possibility of such damage.
|
||||
|
||||
smartypants.py license (2-Clause BSD license):
|
||||
|
||||
smartypants.py is a derivative work of SmartyPants.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
This software is provided by the copyright holders and contributors
|
||||
"as is" and any express or implied warranties, including, but not
|
||||
limited to, the implied warranties of merchantability and fitness for
|
||||
a particular purpose are disclaimed. In no event shall the copyright
|
||||
owner or contributors be liable for any direct, indirect, incidental,
|
||||
special, exemplary, or consequential damages (including, but not
|
||||
limited to, procurement of substitute goods or services; loss of use,
|
||||
data, or profits; or business interruption) however caused and on any
|
||||
theory of liability, whether in contract, strict liability, or tort
|
||||
(including negligence or otherwise) arising in any way out of the use
|
||||
of this software, even if advised of the possibility of such damage.
|
||||
|
||||
.. _John Gruber: http://daringfireball.net/
|
||||
.. _Chad Miller: http://web.chad.org/
|
||||
|
||||
.. _Pyblosxom: http://pyblosxom.bluesock.org/
|
||||
.. _SmartyPants: http://daringfireball.net/projects/smartypants/
|
||||
.. _Movable Type: http://www.movabletype.org/
|
||||
.. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause
|
||||
.. _Docutils: https://docutils.sourceforge.io/
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
SmartyPants can perform the following transformations:
|
||||
|
||||
- Straight quotes ( " and ' ) into "curly" quote characters
|
||||
- Backticks-style quotes (\`\`like this'') into "curly" quote characters
|
||||
- Dashes (``--`` and ``---``) into en- and em-dash entities
|
||||
- Three consecutive dots (``...`` or ``. . .``) into an ellipsis entity
|
||||
|
||||
This means you can write, edit, and save your posts using plain old
|
||||
ASCII straight quotes, plain dashes, and plain dots, but your published
|
||||
posts (and final HTML output) will appear with smart quotes, em-dashes,
|
||||
and proper ellipses.
|
||||
|
||||
SmartyPants does not modify characters within ``<pre>``, ``<code>``, ``<kbd>``,
|
||||
``<math>`` or ``<script>`` tag blocks. Typically, these tags are used to
|
||||
display text where smart quotes and other "smart punctuation" would not be
|
||||
appropriate, such as source code or example markup.
|
||||
|
||||
|
||||
Backslash Escapes
|
||||
=================
|
||||
|
||||
If you need to use literal straight quotes (or plain hyphens and periods),
|
||||
`smartquotes` accepts the following backslash escape sequences to force
|
||||
ASCII-punctuation. Mind, that you need two backslashes as Docutils expands it,
|
||||
too.
|
||||
|
||||
======== =========
|
||||
Escape Character
|
||||
======== =========
|
||||
``\\`` \\
|
||||
``\\"`` \\"
|
||||
``\\'`` \\'
|
||||
``\\.`` \\.
|
||||
``\\-`` \\-
|
||||
``\\``` \\`
|
||||
======== =========
|
||||
|
||||
This is useful, for example, when you want to use straight quotes as
|
||||
foot and inch marks: 6\\'2\\" tall; a 17\\" iMac.
|
||||
|
||||
|
||||
Caveats
|
||||
=======
|
||||
|
||||
Why You Might Not Want to Use Smart Quotes in Your Weblog
|
||||
---------------------------------------------------------
|
||||
|
||||
For one thing, you might not care.
|
||||
|
||||
Most normal, mentally stable individuals do not take notice of proper
|
||||
typographic punctuation. Many design and typography nerds, however, break
|
||||
out in a nasty rash when they encounter, say, a restaurant sign that uses
|
||||
a straight apostrophe to spell "Joe's".
|
||||
|
||||
If you're the sort of person who just doesn't care, you might well want to
|
||||
continue not caring. Using straight quotes -- and sticking to the 7-bit
|
||||
ASCII character set in general -- is certainly a simpler way to live.
|
||||
|
||||
Even if you *do* care about accurate typography, you still might want to
|
||||
think twice before educating the quote characters in your weblog. One side
|
||||
effect of publishing curly quote characters is that it makes your
|
||||
weblog a bit harder for others to quote from using copy-and-paste. What
|
||||
happens is that when someone copies text from your blog, the copied text
|
||||
contains the 8-bit curly quote characters (as well as the 8-bit characters
|
||||
for em-dashes and ellipses, if you use these options). These characters
|
||||
are not standard across different text encoding methods, which is why they
|
||||
need to be encoded as characters.
|
||||
|
||||
People copying text from your weblog, however, may not notice that you're
|
||||
using curly quotes, and they'll go ahead and paste the unencoded 8-bit
|
||||
characters copied from their browser into an email message or their own
|
||||
weblog. When pasted as raw "smart quotes", these characters are likely to
|
||||
get mangled beyond recognition.
|
||||
|
||||
That said, my own opinion is that any decent text editor or email client
|
||||
makes it easy to stupefy smart quote characters into their 7-bit
|
||||
equivalents, and I don't consider it my problem if you're using an
|
||||
indecent text editor or email client.
|
||||
|
||||
|
||||
Algorithmic Shortcomings
|
||||
------------------------
|
||||
|
||||
One situation in which quotes will get curled the wrong way is when
|
||||
apostrophes are used at the start of leading contractions. For example::
|
||||
|
||||
'Twas the night before Christmas.
|
||||
|
||||
In the case above, SmartyPants will turn the apostrophe into an opening
|
||||
secondary quote, when in fact it should be the `RIGHT SINGLE QUOTATION MARK`
|
||||
character which is also "the preferred character to use for apostrophe"
|
||||
(Unicode). I don't think this problem can be solved in the general case --
|
||||
every word processor I've tried gets this wrong as well. In such cases, it's
|
||||
best to inset the `RIGHT SINGLE QUOTATION MARK` (’) by hand.
|
||||
|
||||
In English, the same character is used for apostrophe and closing secondary
|
||||
quote (both plain and "smart" ones). For other locales (French, Italean,
|
||||
Swiss, ...) "smart" secondary closing quotes differ from the curly apostrophe.
|
||||
|
||||
.. class:: language-fr
|
||||
|
||||
Il dit : "C'est 'super' !"
|
||||
|
||||
If the apostrophe is used at the end of a word, it cannot be distinguished
|
||||
from a secondary quote by the algorithm. Therefore, a text like::
|
||||
|
||||
.. class:: language-de-CH
|
||||
|
||||
"Er sagt: 'Ich fass' es nicht.'"
|
||||
|
||||
will get a single closing guillemet instead of an apostrophe.
|
||||
|
||||
This can be prevented by use use of the `RIGHT SINGLE QUOTATION MARK` in
|
||||
the source::
|
||||
|
||||
- "Er sagt: 'Ich fass' es nicht.'"
|
||||
+ "Er sagt: 'Ich fass’ es nicht.'"
|
||||
|
||||
|
||||
Version History
|
||||
===============
|
||||
|
||||
1.9 2022-03-04
|
||||
- Code cleanup. Require Python 3.
|
||||
|
||||
1.8.1 2017-10-25
|
||||
- Use open quote after Unicode whitespace, ZWSP, and ZWNJ.
|
||||
- Code cleanup.
|
||||
|
||||
1.8: 2017-04-24
|
||||
- Command line front-end.
|
||||
|
||||
1.7.1: 2017-03-19
|
||||
- Update and extend language-dependent quotes.
|
||||
- Differentiate apostrophe from single quote.
|
||||
|
||||
1.7: 2012-11-19
|
||||
- Internationalization: language-dependent quotes.
|
||||
|
||||
1.6.1: 2012-11-06
|
||||
- Refactor code, code cleanup,
|
||||
- `educate_tokens()` generator as interface for Docutils.
|
||||
|
||||
1.6: 2010-08-26
|
||||
- Adaption to Docutils:
|
||||
- Use Unicode instead of HTML entities,
|
||||
- Remove code special to pyblosxom.
|
||||
|
||||
1.5_1.6: Fri, 27 Jul 2007 07:06:40 -0400
|
||||
- Fixed bug where blocks of precious unalterable text was instead
|
||||
interpreted. Thanks to Le Roux and Dirk van Oosterbosch.
|
||||
|
||||
1.5_1.5: Sat, 13 Aug 2005 15:50:24 -0400
|
||||
- Fix bogus magical quotation when there is no hint that the
|
||||
user wants it, e.g., in "21st century". Thanks to Nathan Hamblen.
|
||||
- Be smarter about quotes before terminating numbers in an en-dash'ed
|
||||
range.
|
||||
|
||||
1.5_1.4: Thu, 10 Feb 2005 20:24:36 -0500
|
||||
- Fix a date-processing bug, as reported by jacob childress.
|
||||
- Begin a test-suite for ensuring correct output.
|
||||
- Removed import of "string", since I didn't really need it.
|
||||
(This was my first every Python program. Sue me!)
|
||||
|
||||
1.5_1.3: Wed, 15 Sep 2004 18:25:58 -0400
|
||||
- Abort processing if the flavour is in forbidden-list. Default of
|
||||
[ "rss" ] (Idea of Wolfgang SCHNERRING.)
|
||||
- Remove stray virgules from en-dashes. Patch by Wolfgang SCHNERRING.
|
||||
|
||||
1.5_1.2: Mon, 24 May 2004 08:14:54 -0400
|
||||
- Some single quotes weren't replaced properly. Diff-tesuji played
|
||||
by Benjamin GEIGER.
|
||||
|
||||
1.5_1.1: Sun, 14 Mar 2004 14:38:28 -0500
|
||||
- Support upcoming pyblosxom 0.9 plugin verification feature.
|
||||
|
||||
1.5_1.0: Tue, 09 Mar 2004 08:08:35 -0500
|
||||
- Initial release
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
options = r"""
|
||||
Options
|
||||
=======
|
||||
|
||||
Numeric values are the easiest way to configure SmartyPants' behavior:
|
||||
|
||||
:0: Suppress all transformations. (Do nothing.)
|
||||
|
||||
:1: Performs default SmartyPants transformations: quotes (including
|
||||
\`\`backticks'' -style), em-dashes, and ellipses. "``--``" (dash dash)
|
||||
is used to signify an em-dash; there is no support for en-dashes
|
||||
|
||||
:2: Same as smarty_pants="1", except that it uses the old-school typewriter
|
||||
shorthand for dashes: "``--``" (dash dash) for en-dashes, "``---``"
|
||||
(dash dash dash)
|
||||
for em-dashes.
|
||||
|
||||
:3: Same as smarty_pants="2", but inverts the shorthand for dashes:
|
||||
"``--``" (dash dash) for em-dashes, and "``---``" (dash dash dash) for
|
||||
en-dashes.
|
||||
|
||||
:-1: Stupefy mode. Reverses the SmartyPants transformation process, turning
|
||||
the characters produced by SmartyPants into their ASCII equivalents.
|
||||
E.g. the LEFT DOUBLE QUOTATION MARK (“) is turned into a simple
|
||||
double-quote (\"), "—" is turned into two dashes, etc.
|
||||
|
||||
|
||||
The following single-character attribute values can be combined to toggle
|
||||
individual transformations from within the smarty_pants attribute. For
|
||||
example, ``"1"`` is equivalent to ``"qBde"``.
|
||||
|
||||
:q: Educates normal quote characters: (") and (').
|
||||
|
||||
:b: Educates \`\`backticks'' -style double quotes.
|
||||
|
||||
:B: Educates \`\`backticks'' -style double quotes and \`single' quotes.
|
||||
|
||||
:d: Educates em-dashes.
|
||||
|
||||
:D: Educates em-dashes and en-dashes, using old-school typewriter
|
||||
shorthand: (dash dash) for en-dashes, (dash dash dash) for em-dashes.
|
||||
|
||||
:i: Educates em-dashes and en-dashes, using inverted old-school typewriter
|
||||
shorthand: (dash dash) for em-dashes, (dash dash dash) for en-dashes.
|
||||
|
||||
:e: Educates ellipses.
|
||||
|
||||
:w: Translates any instance of ``"`` into a normal double-quote
|
||||
character. This should be of no interest to most people, but
|
||||
of particular interest to anyone who writes their posts using
|
||||
Dreamweaver, as Dreamweaver inexplicably uses this entity to represent
|
||||
a literal double-quote character. SmartyPants only educates normal
|
||||
quotes, not entities (because ordinarily, entities are used for
|
||||
the explicit purpose of representing the specific character they
|
||||
represent). The "w" option must be used in conjunction with one (or
|
||||
both) of the other quote options ("q" or "b"). Thus, if you wish to
|
||||
apply all SmartyPants transformations (quotes, en- and em-dashes, and
|
||||
ellipses) and also translate ``"`` entities into regular quotes
|
||||
so SmartyPants can educate them, you should pass the following to the
|
||||
smarty_pants attribute:
|
||||
"""
|
||||
|
||||
|
||||
class smartchars:
|
||||
"""Smart quotes and dashes"""
|
||||
|
||||
endash = '–' # "–" EN DASH
|
||||
emdash = '—' # "—" EM DASH
|
||||
ellipsis = '…' # "…" HORIZONTAL ELLIPSIS
|
||||
apostrophe = '’' # "’" RIGHT SINGLE QUOTATION MARK
|
||||
|
||||
# quote characters (language-specific, set in __init__())
|
||||
# https://en.wikipedia.org/wiki/Non-English_usage_of_quotation_marks
|
||||
# https://de.wikipedia.org/wiki/Anf%C3%BChrungszeichen#Andere_Sprachen
|
||||
# https://fr.wikipedia.org/wiki/Guillemet
|
||||
# https://typographisme.net/post/Les-espaces-typographiques-et-le-web
|
||||
# https://www.btb.termiumplus.gc.ca/tpv2guides/guides/redac/index-fra.html
|
||||
# https://en.wikipedia.org/wiki/Hebrew_punctuation#Quotation_marks
|
||||
# [7] https://www.tustep.uni-tuebingen.de/bi/bi00/bi001t1-anfuehrung.pdf
|
||||
# [8] https://www.korrekturavdelingen.no/anforselstegn.htm
|
||||
# [9] Typografisk håndbok. Oslo: Spartacus. 2000. s. 67. ISBN 8243001530.
|
||||
# [10] https://www.typografi.org/sitat/sitatart.html
|
||||
# [11] https://mk.wikipedia.org/wiki/Правопис_и_правоговор_на_македонскиот_јазик # noqa:E501
|
||||
# [12] https://hrvatska-tipografija.com/polunavodnici/
|
||||
# [13] https://pl.wikipedia.org/wiki/Cudzys%C5%82%C3%B3w
|
||||
#
|
||||
# See also configuration option "smartquote-locales".
|
||||
quotes = {
|
||||
'af': '“”‘’',
|
||||
'af-x-altquot': '„”‚’',
|
||||
'bg': '„“‚‘', # https://bg.wikipedia.org/wiki/Кавички
|
||||
'ca': '«»“”',
|
||||
'ca-x-altquot': '“”‘’',
|
||||
'cs': '„“‚‘',
|
||||
'cs-x-altquot': '»«›‹',
|
||||
'da': '»«›‹',
|
||||
'da-x-altquot': '„“‚‘',
|
||||
# 'da-x-altquot2': '””’’',
|
||||
'de': '„“‚‘',
|
||||
'de-x-altquot': '»«›‹',
|
||||
'de-ch': '«»‹›',
|
||||
'el': '«»“”', # '«»‟”' https://hal.science/hal-02101618
|
||||
'en': '“”‘’',
|
||||
'en-uk-x-altquot': '‘’“”', # Attention: " → ‘ and ' → “ !
|
||||
'eo': '“”‘’',
|
||||
'es': '«»“”',
|
||||
'es-x-altquot': '“”‘’',
|
||||
'et': '„“‚‘', # no secondary quote listed in
|
||||
'et-x-altquot': '«»‹›', # the sources above (wikipedia.org)
|
||||
'eu': '«»‹›',
|
||||
'fi': '””’’',
|
||||
'fi-x-altquot': '»»››',
|
||||
'fr': ('« ', ' »', '“', '”'), # full no-break space
|
||||
'fr-x-altquot': ('« ', ' »', '“', '”'), # narrow no-break space
|
||||
'fr-ch': '«»‹›', # https://typoguide.ch/
|
||||
'fr-ch-x-altquot': ('« ', ' »', '‹ ', ' ›'), # narrow no-break space # noqa:E501
|
||||
'gl': '«»“”',
|
||||
'he': '”“»«', # Hebrew is RTL, test position:
|
||||
'he-x-altquot': '„”‚’', # low quotation marks are opening.
|
||||
# 'he-x-altquot': '“„‘‚', # RTL: low quotation marks opening
|
||||
'hr': '„”‘’', # Croatian [12]
|
||||
'hr-x-altquot': '»«›‹',
|
||||
'hsb': '„“‚‘',
|
||||
'hsb-x-altquot': '»«›‹',
|
||||
'hu': '„”«»',
|
||||
'is': '„“‚‘',
|
||||
'it': '«»“”',
|
||||
'it-ch': '«»‹›',
|
||||
'it-x-altquot': '“”‘’',
|
||||
# 'it-x-altquot2': '“„‘‚', # [7] in headlines
|
||||
'ja': '「」『』',
|
||||
'ko': '“”‘’',
|
||||
'lt': '„“‚‘',
|
||||
'lv': '„“‚‘',
|
||||
'mk': '„“‚‘', # Macedonian [11]
|
||||
'nl': '“”‘’',
|
||||
'nl-x-altquot': '„”‚’',
|
||||
# 'nl-x-altquot2': '””’’',
|
||||
'nb': '«»’’', # Norsk bokmål (canonical form 'no')
|
||||
'nn': '«»’’', # Nynorsk [10]
|
||||
'nn-x-altquot': '«»‘’', # [8], [10]
|
||||
# 'nn-x-altquot2': '«»«»', # [9], [10]
|
||||
# 'nn-x-altquot3': '„“‚‘', # [10]
|
||||
'no': '«»’’', # Norsk bokmål [10]
|
||||
'no-x-altquot': '«»‘’', # [8], [10]
|
||||
# 'no-x-altquot2': '«»«»', # [9], [10
|
||||
# 'no-x-altquot3': '„“‚‘', # [10]
|
||||
'pl': '„”«»',
|
||||
'pl-x-altquot': '«»‚’',
|
||||
# 'pl-x-altquot2': '„”‚’', # [13]
|
||||
'pt': '«»“”',
|
||||
'pt-br': '“”‘’',
|
||||
'ro': '„”«»',
|
||||
'ru': '«»„“',
|
||||
'sh': '„”‚’', # Serbo-Croatian
|
||||
'sh-x-altquot': '»«›‹',
|
||||
'sk': '„“‚‘', # Slovak
|
||||
'sk-x-altquot': '»«›‹',
|
||||
'sl': '„“‚‘', # Slovenian
|
||||
'sl-x-altquot': '»«›‹',
|
||||
'sq': '«»‹›', # Albanian
|
||||
'sq-x-altquot': '“„‘‚',
|
||||
'sr': '„”’’',
|
||||
'sr-x-altquot': '»«›‹',
|
||||
'sv': '””’’',
|
||||
'sv-x-altquot': '»»››',
|
||||
'tr': '“”‘’',
|
||||
'tr-x-altquot': '«»‹›',
|
||||
# 'tr-x-altquot2': '“„‘‚', # [7] antiquated?
|
||||
'uk': '«»„“',
|
||||
'uk-x-altquot': '„“‚‘',
|
||||
'zh-cn': '“”‘’',
|
||||
'zh-tw': '「」『』',
|
||||
}
|
||||
|
||||
def __init__(self, language='en'):
|
||||
self.language = language
|
||||
try:
|
||||
(self.opquote, self.cpquote,
|
||||
self.osquote, self.csquote) = self.quotes[language.lower()]
|
||||
except KeyError:
|
||||
self.opquote, self.cpquote, self.osquote, self.csquote = '""\'\''
|
||||
|
||||
|
||||
default_smartypants_attr = '1'
|
||||
|
||||
|
||||
def smartyPants(text, attr=default_smartypants_attr, language='en'):
|
||||
"""Main function for "traditional" use."""
|
||||
|
||||
return "".join(t for t in educate_tokens(tokenize(text), attr, language))
|
||||
|
||||
|
||||
def educate_tokens(text_tokens, attr=default_smartypants_attr, language='en'):
|
||||
"""Return iterator that "educates" the items of `text_tokens`."""
|
||||
# Parse attributes:
|
||||
# 0 : do nothing
|
||||
# 1 : set all
|
||||
# 2 : set all, using old school en- and em- dash shortcuts
|
||||
# 3 : set all, using inverted old school en and em- dash shortcuts
|
||||
#
|
||||
# q : quotes
|
||||
# b : backtick quotes (``double'' only)
|
||||
# B : backtick quotes (``double'' and `single')
|
||||
# d : dashes
|
||||
# D : old school dashes
|
||||
# i : inverted old school dashes
|
||||
# e : ellipses
|
||||
# w : convert " entities to " for Dreamweaver users
|
||||
|
||||
convert_quot = False # translate " entities into normal quotes?
|
||||
do_dashes = False
|
||||
do_backticks = False
|
||||
do_quotes = False
|
||||
do_ellipses = False
|
||||
do_stupefy = False
|
||||
|
||||
# if attr == "0": # pass tokens unchanged (see below).
|
||||
if attr == '1': # Do everything, turn all options on.
|
||||
do_quotes = True
|
||||
do_backticks = True
|
||||
do_dashes = 1
|
||||
do_ellipses = True
|
||||
elif attr == '2':
|
||||
# Do everything, turn all options on, use old school dash shorthand.
|
||||
do_quotes = True
|
||||
do_backticks = True
|
||||
do_dashes = 2
|
||||
do_ellipses = True
|
||||
elif attr == '3':
|
||||
# Do everything, use inverted old school dash shorthand.
|
||||
do_quotes = True
|
||||
do_backticks = True
|
||||
do_dashes = 3
|
||||
do_ellipses = True
|
||||
elif attr == '-1': # Special "stupefy" mode.
|
||||
do_stupefy = True
|
||||
else:
|
||||
if 'q' in attr: do_quotes = True # noqa: E701
|
||||
if 'b' in attr: do_backticks = True # noqa: E701
|
||||
if 'B' in attr: do_backticks = 2 # noqa: E701
|
||||
if 'd' in attr: do_dashes = 1 # noqa: E701
|
||||
if 'D' in attr: do_dashes = 2 # noqa: E701
|
||||
if 'i' in attr: do_dashes = 3 # noqa: E701
|
||||
if 'e' in attr: do_ellipses = True # noqa: E701
|
||||
if 'w' in attr: convert_quot = True # noqa: E701
|
||||
|
||||
prev_token_last_char = ' '
|
||||
# Last character of the previous text token. Used as
|
||||
# context to curl leading quote characters correctly.
|
||||
|
||||
for (ttype, text) in text_tokens:
|
||||
|
||||
# skip HTML and/or XML tags as well as empty text tokens
|
||||
# without updating the last character
|
||||
if ttype == 'tag' or not text:
|
||||
yield text
|
||||
continue
|
||||
|
||||
# skip literal text (math, literal, raw, ...)
|
||||
if ttype == 'literal':
|
||||
prev_token_last_char = text[-1:]
|
||||
yield text
|
||||
continue
|
||||
|
||||
last_char = text[-1:] # Remember last char before processing.
|
||||
|
||||
text = processEscapes(text)
|
||||
|
||||
if convert_quot:
|
||||
text = text.replace('"', '"')
|
||||
|
||||
if do_dashes == 1:
|
||||
text = educateDashes(text)
|
||||
elif do_dashes == 2:
|
||||
text = educateDashesOldSchool(text)
|
||||
elif do_dashes == 3:
|
||||
text = educateDashesOldSchoolInverted(text)
|
||||
|
||||
if do_ellipses:
|
||||
text = educateEllipses(text)
|
||||
|
||||
# Note: backticks need to be processed before quotes.
|
||||
if do_backticks:
|
||||
text = educateBackticks(text, language)
|
||||
|
||||
if do_backticks == 2:
|
||||
text = educateSingleBackticks(text, language)
|
||||
|
||||
if do_quotes:
|
||||
# Replace plain quotes in context to prevent conversion to
|
||||
# 2-character sequence in French.
|
||||
context = prev_token_last_char.replace('"', ';').replace("'", ';')
|
||||
text = educateQuotes(context+text, language)[1:]
|
||||
|
||||
if do_stupefy:
|
||||
text = stupefyEntities(text, language)
|
||||
|
||||
# Remember last char as context for the next token
|
||||
prev_token_last_char = last_char
|
||||
|
||||
text = processEscapes(text, restore=True)
|
||||
|
||||
yield text
|
||||
|
||||
|
||||
def educateQuotes(text, language='en'):
|
||||
"""
|
||||
Parameter: - text string (unicode or bytes).
|
||||
- language (`BCP 47` language tag.)
|
||||
Returns: The `text`, with "educated" curly quote characters.
|
||||
|
||||
Example input: "Isn't this fun?"
|
||||
Example output: “Isn’t this fun?“;
|
||||
"""
|
||||
|
||||
smart = smartchars(language)
|
||||
ch_classes = {'open': '[([{]', # opening braces
|
||||
'close': r'[^\s]', # everything except whitespace
|
||||
'punct': r"""[-!" #\$\%'()*+,.\/:;<=>?\@\[\\\]\^_`{|}~]""",
|
||||
'dash': '[-–—]' # hyphen and em/en dashes
|
||||
r'|&[mn]dash;|&\#8211;|&\#8212;|&\#x201[34];',
|
||||
'sep': '[\\s\u200B\u200C]| ', # Whitespace, ZWSP, ZWNJ
|
||||
}
|
||||
|
||||
# Special case if the very first character is a quote
|
||||
# followed by punctuation at a non-word-break. Use closing quotes.
|
||||
# TODO: example (when does this match?)
|
||||
text = re.sub(r"^'(?=%s\\B)" % ch_classes['punct'], smart.csquote, text)
|
||||
text = re.sub(r'^"(?=%s\\B)' % ch_classes['punct'], smart.cpquote, text)
|
||||
|
||||
# Special case for adjacent quotes
|
||||
# like "'Quoted' words in a larger quote."
|
||||
text = re.sub('"\'(?=\\w)', smart.opquote+smart.osquote, text)
|
||||
text = re.sub('\'"(?=\\w)', smart.osquote+smart.opquote, text)
|
||||
|
||||
# Special case: "opening character" followed by quote,
|
||||
# optional punctuation and space like "[", '(', or '-'.
|
||||
text = re.sub(r"(%(open)s|%(dash)s)'(?=%(punct)s? )" % ch_classes,
|
||||
r'\1%s'%smart.csquote, text)
|
||||
text = re.sub(r'(%(open)s|%(dash)s)"(?=%(punct)s? )' % ch_classes,
|
||||
r'\1%s'%smart.cpquote, text)
|
||||
|
||||
# Special case for decade abbreviations (the '80s):
|
||||
if language.startswith('en'): # TODO similar cases in other languages?
|
||||
text = re.sub(r"'(?=\d{2}s)", smart.apostrophe, text)
|
||||
|
||||
# Get most opening secondary quotes:
|
||||
opening_secondary_quotes_regex = re.compile("""
|
||||
(# ?<= # look behind fails: requires fixed-width pattern
|
||||
%(sep)s | # a whitespace char, or
|
||||
%(open)s | # opening brace, or
|
||||
%(dash)s # em/en-dash
|
||||
)
|
||||
' # the quote
|
||||
(?=\\w|%(punct)s) # word character or punctuation
|
||||
""" % ch_classes, re.VERBOSE)
|
||||
|
||||
text = opening_secondary_quotes_regex.sub(r'\1'+smart.osquote, text)
|
||||
|
||||
# In many locales, secondary closing quotes are different from apostrophe:
|
||||
if smart.csquote != smart.apostrophe:
|
||||
apostrophe_regex = re.compile(r"(?<=(\w|\d))'(?=\w)")
|
||||
text = apostrophe_regex.sub(smart.apostrophe, text)
|
||||
# TODO: keep track of quoting level to recognize apostrophe in, e.g.,
|
||||
# "Ich fass' es nicht."
|
||||
|
||||
closing_secondary_quotes_regex = re.compile(r"(?<!\s)'")
|
||||
text = closing_secondary_quotes_regex.sub(smart.csquote, text)
|
||||
|
||||
# Any remaining secondary quotes should be opening ones:
|
||||
text = text.replace(r"'", smart.osquote)
|
||||
|
||||
# Get most opening primary quotes:
|
||||
opening_primary_quotes_regex = re.compile("""
|
||||
(
|
||||
%(sep)s | # a whitespace char, or
|
||||
%(open)s | # zero width separating char, or
|
||||
%(dash)s # em/en-dash
|
||||
)
|
||||
" # the quote, followed by
|
||||
(?=\\w|%(punct)s) # a word character or punctuation
|
||||
""" % ch_classes, re.VERBOSE)
|
||||
|
||||
text = opening_primary_quotes_regex.sub(r'\1'+smart.opquote, text)
|
||||
|
||||
# primary closing quotes:
|
||||
closing_primary_quotes_regex = re.compile(r"""
|
||||
(
|
||||
(?<!\s)" | # no whitespace before
|
||||
"(?=\s) # whitespace behind
|
||||
)
|
||||
""", re.VERBOSE)
|
||||
text = closing_primary_quotes_regex.sub(smart.cpquote, text)
|
||||
|
||||
# Any remaining quotes should be opening ones.
|
||||
text = text.replace(r'"', smart.opquote)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def educateBackticks(text, language='en'):
|
||||
"""
|
||||
Parameter: String (unicode or bytes).
|
||||
Returns: The `text`, with ``backticks'' -style double quotes
|
||||
translated into HTML curly quote entities.
|
||||
Example input: ``Isn't this fun?''
|
||||
Example output: “Isn't this fun?“;
|
||||
"""
|
||||
smart = smartchars(language)
|
||||
|
||||
text = text.replace(r'``', smart.opquote)
|
||||
text = text.replace(r"''", smart.cpquote)
|
||||
return text
|
||||
|
||||
|
||||
def educateSingleBackticks(text, language='en'):
|
||||
"""
|
||||
Parameter: String (unicode or bytes).
|
||||
Returns: The `text`, with `backticks' -style single quotes
|
||||
translated into HTML curly quote entities.
|
||||
|
||||
Example input: `Isn't this fun?'
|
||||
Example output: ‘Isn’t this fun?’
|
||||
"""
|
||||
smart = smartchars(language)
|
||||
|
||||
text = text.replace(r'`', smart.osquote)
|
||||
text = text.replace(r"'", smart.csquote)
|
||||
return text
|
||||
|
||||
|
||||
def educateDashes(text):
|
||||
"""
|
||||
Parameter: String (unicode or bytes).
|
||||
Returns: The `text`, with each instance of "--" translated to
|
||||
an em-dash character.
|
||||
"""
|
||||
|
||||
text = text.replace(r'---', smartchars.endash) # en (yes, backwards)
|
||||
text = text.replace(r'--', smartchars.emdash) # em (yes, backwards)
|
||||
return text
|
||||
|
||||
|
||||
def educateDashesOldSchool(text):
|
||||
"""
|
||||
Parameter: String (unicode or bytes).
|
||||
Returns: The `text`, with each instance of "--" translated to
|
||||
an en-dash character, and each "---" translated to
|
||||
an em-dash character.
|
||||
"""
|
||||
|
||||
text = text.replace(r'---', smartchars.emdash)
|
||||
text = text.replace(r'--', smartchars.endash)
|
||||
return text
|
||||
|
||||
|
||||
def educateDashesOldSchoolInverted(text):
|
||||
"""
|
||||
Parameter: String (unicode or bytes).
|
||||
Returns: The `text`, with each instance of "--" translated to
|
||||
an em-dash character, and each "---" translated to
|
||||
an en-dash character. Two reasons why: First, unlike the
|
||||
en- and em-dash syntax supported by
|
||||
EducateDashesOldSchool(), it's compatible with existing
|
||||
entries written before SmartyPants 1.1, back when "--" was
|
||||
only used for em-dashes. Second, em-dashes are more
|
||||
common than en-dashes, and so it sort of makes sense that
|
||||
the shortcut should be shorter to type. (Thanks to Aaron
|
||||
Swartz for the idea.)
|
||||
"""
|
||||
text = text.replace(r'---', smartchars.endash) # em
|
||||
text = text.replace(r'--', smartchars.emdash) # en
|
||||
return text
|
||||
|
||||
|
||||
def educateEllipses(text):
|
||||
"""
|
||||
Parameter: String (unicode or bytes).
|
||||
Returns: The `text`, with each instance of "..." translated to
|
||||
an ellipsis character.
|
||||
|
||||
Example input: Huh...?
|
||||
Example output: Huh…?
|
||||
"""
|
||||
|
||||
text = text.replace(r'...', smartchars.ellipsis)
|
||||
text = text.replace(r'. . .', smartchars.ellipsis)
|
||||
return text
|
||||
|
||||
|
||||
def stupefyEntities(text, language='en'):
|
||||
"""
|
||||
Parameter: String (unicode or bytes).
|
||||
Returns: The `text`, with each SmartyPants character translated to
|
||||
its ASCII counterpart.
|
||||
|
||||
Example input: “Hello — world.”
|
||||
Example output: "Hello -- world."
|
||||
"""
|
||||
smart = smartchars(language)
|
||||
|
||||
text = text.replace(smart.endash, "-")
|
||||
text = text.replace(smart.emdash, "--")
|
||||
text = text.replace(smart.osquote, "'") # open secondary quote
|
||||
text = text.replace(smart.csquote, "'") # close secondary quote
|
||||
text = text.replace(smart.opquote, '"') # open primary quote
|
||||
text = text.replace(smart.cpquote, '"') # close primary quote
|
||||
text = text.replace(smart.ellipsis, '...')
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def processEscapes(text, restore=False):
|
||||
r"""
|
||||
Parameter: String (unicode or bytes).
|
||||
Returns: The `text`, with after processing the following backslash
|
||||
escape sequences. This is useful if you want to force a "dumb"
|
||||
quote or other character to appear.
|
||||
|
||||
Escape Value
|
||||
------ -----
|
||||
\\ \
|
||||
\" "
|
||||
\' '
|
||||
\. .
|
||||
\- -
|
||||
\` `
|
||||
"""
|
||||
replacements = ((r'\\', r'\'),
|
||||
(r'\"', r'"'),
|
||||
(r"\'", r'''),
|
||||
(r'\.', r'.'),
|
||||
(r'\-', r'-'),
|
||||
(r'\`', r'`'))
|
||||
if restore:
|
||||
for (ch, rep) in replacements:
|
||||
text = text.replace(rep, ch[1])
|
||||
else:
|
||||
for (ch, rep) in replacements:
|
||||
text = text.replace(ch, rep)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def tokenize(text):
|
||||
"""
|
||||
Parameter: String containing HTML markup.
|
||||
Returns: An iterator that yields the tokens comprising the input
|
||||
string. Each token is either a tag (possibly with nested,
|
||||
tags contained therein, such as <a href="<MTFoo>">, or a
|
||||
run of text between tags. Each yielded element is a
|
||||
two-element tuple; the first is either 'tag' or 'text';
|
||||
the second is the actual value.
|
||||
|
||||
Based on the _tokenize() subroutine from Brad Choate's MTRegex plugin.
|
||||
"""
|
||||
tag_soup = re.compile(r'([^<]*)(<[^>]*>)')
|
||||
token_match = tag_soup.search(text)
|
||||
previous_end = 0
|
||||
|
||||
while token_match is not None:
|
||||
if token_match.group(1):
|
||||
yield 'text', token_match.group(1)
|
||||
yield 'tag', token_match.group(2)
|
||||
previous_end = token_match.end()
|
||||
token_match = tag_soup.search(text, token_match.end())
|
||||
|
||||
if previous_end < len(text):
|
||||
yield 'text', text[previous_end:]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
import itertools
|
||||
import locale
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, '') # set to user defaults
|
||||
defaultlanguage = locale.getlocale()[0]
|
||||
except: # noqa catchall
|
||||
defaultlanguage = 'en'
|
||||
|
||||
# Normalize and drop unsupported subtags:
|
||||
defaultlanguage = defaultlanguage.lower().replace('-', '_')
|
||||
# split (except singletons, which mark the following tag as non-standard):
|
||||
defaultlanguage = re.sub(r'_([a-zA-Z0-9])_', r'_\1-', defaultlanguage)
|
||||
_subtags = [subtag for subtag in defaultlanguage.split('_')]
|
||||
_basetag = _subtags.pop(0)
|
||||
# find all combinations of subtags
|
||||
for n in range(len(_subtags), 0, -1):
|
||||
for tags in itertools.combinations(_subtags, n):
|
||||
_tag = '-'.join((_basetag, *tags))
|
||||
if _tag in smartchars.quotes:
|
||||
defaultlanguage = _tag
|
||||
break
|
||||
else:
|
||||
if _basetag in smartchars.quotes:
|
||||
defaultlanguage = _basetag
|
||||
else:
|
||||
defaultlanguage = 'en'
|
||||
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Filter <input> making ASCII punctuation "smart".')
|
||||
# TODO: require input arg or other means to print USAGE instead of waiting.
|
||||
# parser.add_argument("input", help="Input stream, use '-' for stdin.")
|
||||
parser.add_argument("-a", "--action", default="1",
|
||||
help="what to do with the input (see --actionhelp)")
|
||||
parser.add_argument("-e", "--encoding", default="utf-8",
|
||||
help="text encoding")
|
||||
parser.add_argument("-l", "--language", default=defaultlanguage,
|
||||
help="text language (BCP47 tag), "
|
||||
f"Default: {defaultlanguage}")
|
||||
parser.add_argument("-q", "--alternative-quotes", action="store_true",
|
||||
help="use alternative quote style")
|
||||
parser.add_argument("--doc", action="store_true",
|
||||
help="print documentation")
|
||||
parser.add_argument("--actionhelp", action="store_true",
|
||||
help="list available actions")
|
||||
parser.add_argument("--stylehelp", action="store_true",
|
||||
help="list available quote styles")
|
||||
parser.add_argument("--test", action="store_true",
|
||||
help="perform short self-test")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.doc:
|
||||
print(__doc__)
|
||||
elif args.actionhelp:
|
||||
print(options)
|
||||
elif args.stylehelp:
|
||||
print()
|
||||
print("Available styles (primary open/close, secondary open/close)")
|
||||
print("language tag quotes")
|
||||
print("============ ======")
|
||||
for key in sorted(smartchars.quotes.keys()):
|
||||
print("%-14s %s" % (key, smartchars.quotes[key]))
|
||||
elif args.test:
|
||||
# Unit test output goes to stderr.
|
||||
import unittest
|
||||
|
||||
class TestSmartypantsAllAttributes(unittest.TestCase):
|
||||
# the default attribute is "1", which means "all".
|
||||
def test_dates(self):
|
||||
self.assertEqual(smartyPants("1440-80's"), "1440-80’s")
|
||||
self.assertEqual(smartyPants("1440-'80s"), "1440-’80s")
|
||||
self.assertEqual(smartyPants("1440---'80s"), "1440–’80s")
|
||||
self.assertEqual(smartyPants("1960's"), "1960’s")
|
||||
self.assertEqual(smartyPants("one two '60s"), "one two ’60s")
|
||||
self.assertEqual(smartyPants("'60s"), "’60s")
|
||||
|
||||
def test_educated_quotes(self):
|
||||
self.assertEqual(smartyPants('"Isn\'t this fun?"'),
|
||||
'“Isn’t this fun?”')
|
||||
|
||||
def test_html_tags(self):
|
||||
text = '<a src="foo">more</a>'
|
||||
self.assertEqual(smartyPants(text), text)
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(
|
||||
TestSmartypantsAllAttributes)
|
||||
unittest.TextTestRunner().run(suite)
|
||||
|
||||
else:
|
||||
if args.alternative_quotes:
|
||||
if '-x-altquot' in args.language:
|
||||
args.language = args.language.replace('-x-altquot', '')
|
||||
else:
|
||||
args.language += '-x-altquot'
|
||||
text = sys.stdin.read()
|
||||
print(smartyPants(text, attr=args.action, language=args.language))
|
||||
@@ -0,0 +1,138 @@
|
||||
# $Id: urischemes.py 9315 2023-01-15 19:27:55Z milde $
|
||||
# Author: David Goodger <goodger@python.org>
|
||||
# Copyright: This module has been placed in the public domain.
|
||||
|
||||
"""
|
||||
`schemes` is a dictionary with lowercase URI addressing schemes as
|
||||
keys and descriptions as values. It was compiled from the index at
|
||||
http://www.iana.org/assignments/uri-schemes (revised 2005-11-28)
|
||||
and an older list at https://www.w3.org/Addressing/schemes.html.
|
||||
"""
|
||||
|
||||
# Many values are blank and should be filled in with useful descriptions.
|
||||
|
||||
schemes = {
|
||||
'about': 'provides information on Navigator',
|
||||
'acap': 'Application Configuration Access Protocol; RFC 2244',
|
||||
'addbook': "To add vCard entries to Communicator's Address Book",
|
||||
'afp': 'Apple Filing Protocol',
|
||||
'afs': 'Andrew File System global file names',
|
||||
'aim': 'AOL Instant Messenger',
|
||||
'callto': 'for NetMeeting links',
|
||||
'castanet': 'Castanet Tuner URLs for Netcaster',
|
||||
'chttp': 'cached HTTP supported by RealPlayer',
|
||||
'cid': 'content identifier; RFC 2392',
|
||||
'crid': 'TV-Anytime Content Reference Identifier; RFC 4078',
|
||||
'data': 'allows inclusion of small data items as "immediate" data; '
|
||||
'RFC 2397',
|
||||
'dav': 'Distributed Authoring and Versioning Protocol; RFC 2518',
|
||||
'dict': 'dictionary service protocol; RFC 2229',
|
||||
'dns': 'Domain Name System resources',
|
||||
'eid': 'External ID; non-URL data; general escape mechanism to allow '
|
||||
'access to information for applications that are too '
|
||||
'specialized to justify their own schemes',
|
||||
'fax': 'a connection to a terminal that can handle telefaxes '
|
||||
'(facsimiles); RFC 2806',
|
||||
'feed': 'NetNewsWire feed',
|
||||
'file': 'Host-specific file names; RFC 1738',
|
||||
'finger': '',
|
||||
'freenet': '',
|
||||
'ftp': 'File Transfer Protocol; RFC 1738',
|
||||
'go': 'go; RFC 3368',
|
||||
'gopher': 'The Gopher Protocol',
|
||||
'gsm-sms': 'Global System for Mobile Communications Short Message '
|
||||
'Service',
|
||||
'h323': 'video (audiovisual) communication on local area networks; '
|
||||
'RFC 3508',
|
||||
'h324': 'video and audio communications over low bitrate connections '
|
||||
'such as POTS modem connections',
|
||||
'hdl': 'CNRI handle system',
|
||||
'hnews': 'an HTTP-tunneling variant of the NNTP news protocol',
|
||||
'http': 'Hypertext Transfer Protocol; RFC 2616',
|
||||
'https': 'HTTP over SSL; RFC 2818',
|
||||
'hydra': 'SubEthaEdit URI. '
|
||||
'See http://www.codingmonkeys.de/subethaedit.',
|
||||
'iioploc': 'Internet Inter-ORB Protocol Location?',
|
||||
'ilu': 'Inter-Language Unification',
|
||||
'im': 'Instant Messaging; RFC 3860',
|
||||
'imap': 'Internet Message Access Protocol; RFC 2192',
|
||||
'info': 'Information Assets with Identifiers in Public Namespaces',
|
||||
'ior': 'CORBA interoperable object reference',
|
||||
'ipp': 'Internet Printing Protocol; RFC 3510',
|
||||
'irc': 'Internet Relay Chat',
|
||||
'iris.beep': 'iris.beep; RFC 3983',
|
||||
'iseek': 'See www.ambrosiasw.com; a little util for OS X.',
|
||||
'jar': 'Java archive',
|
||||
'javascript': 'JavaScript code; '
|
||||
'evaluates the expression after the colon',
|
||||
'jdbc': 'JDBC connection URI.',
|
||||
'ldap': 'Lightweight Directory Access Protocol',
|
||||
'lifn': '',
|
||||
'livescript': '',
|
||||
'lrq': '',
|
||||
'mailbox': 'Mail folder access',
|
||||
'mailserver': 'Access to data available from mail servers',
|
||||
'mailto': 'Electronic mail address; RFC 2368',
|
||||
'md5': '',
|
||||
'mid': 'message identifier; RFC 2392',
|
||||
'mocha': '',
|
||||
'modem': 'a connection to a terminal that can handle incoming data '
|
||||
'calls; RFC 2806',
|
||||
'mtqp': 'Message Tracking Query Protocol; RFC 3887',
|
||||
'mupdate': 'Mailbox Update (MUPDATE) Protocol; RFC 3656',
|
||||
'news': 'USENET news; RFC 1738',
|
||||
'nfs': 'Network File System protocol; RFC 2224',
|
||||
'nntp': 'USENET news using NNTP access; RFC 1738',
|
||||
'opaquelocktoken': 'RFC 2518',
|
||||
'phone': '',
|
||||
'pop': 'Post Office Protocol; RFC 2384',
|
||||
'pop3': 'Post Office Protocol v3',
|
||||
'pres': 'Presence; RFC 3859',
|
||||
'printer': '',
|
||||
'prospero': 'Prospero Directory Service; RFC 4157',
|
||||
'rdar': 'URLs found in Darwin source '
|
||||
'(http://www.opensource.apple.com/darwinsource/).',
|
||||
'res': '',
|
||||
'rtsp': 'real time streaming protocol; RFC 2326',
|
||||
'rvp': '',
|
||||
'rwhois': '',
|
||||
'rx': 'Remote Execution',
|
||||
'sdp': '',
|
||||
'service': 'service location; RFC 2609',
|
||||
'shttp': 'secure hypertext transfer protocol',
|
||||
'sip': 'Session Initiation Protocol; RFC 3261',
|
||||
'sips': 'secure session intitiaion protocol; RFC 3261',
|
||||
'smb': 'SAMBA filesystems.',
|
||||
'snews': 'For NNTP postings via SSL',
|
||||
'snmp': 'Simple Network Management Protocol; RFC 4088',
|
||||
'soap.beep': 'RFC 3288',
|
||||
'soap.beeps': 'RFC 3288',
|
||||
'ssh': 'Reference to interactive sessions via ssh.',
|
||||
't120': 'real time data conferencing (audiographics)',
|
||||
'tag': 'RFC 4151',
|
||||
'tcp': '',
|
||||
'tel': 'a connection to a terminal that handles normal voice '
|
||||
'telephone calls, a voice mailbox or another voice messaging '
|
||||
'system or a service that can be operated using DTMF tones; '
|
||||
'RFC 3966.',
|
||||
'telephone': 'telephone',
|
||||
'telnet': 'Reference to interactive sessions; RFC 4248',
|
||||
'tftp': 'Trivial File Transfer Protocol; RFC 3617',
|
||||
'tip': 'Transaction Internet Protocol; RFC 2371',
|
||||
'tn3270': 'Interactive 3270 emulation sessions',
|
||||
'tv': '',
|
||||
'urn': 'Uniform Resource Name; RFC 2141',
|
||||
'uuid': '',
|
||||
'vemmi': 'versatile multimedia interface; RFC 2122',
|
||||
'videotex': '',
|
||||
'view-source': 'displays HTML code that was generated with JavaScript',
|
||||
'wais': 'Wide Area Information Servers; RFC 4156',
|
||||
'whodp': '',
|
||||
'whois++': 'Distributed directory service.',
|
||||
'x-man-page': 'Opens man page in Terminal.app on OS X '
|
||||
'(see macosxhints.com)',
|
||||
'xmlrpc.beep': 'RFC 3529',
|
||||
'xmlrpc.beeps': 'RFC 3529',
|
||||
'z39.50r': 'Z39.50 Retrieval; RFC 2056',
|
||||
'z39.50s': 'Z39.50 Session; RFC 2056',
|
||||
}
|
||||
Reference in New Issue
Block a user