pax_global_header00006660000000000000000000000064141515265710014521gustar00rootroot0000000000000052 comment=744afe3b6afa5b3b1996aad14d184af3a0590dfb flatlatex-0.15/000077500000000000000000000000001415152657100134325ustar00rootroot00000000000000flatlatex-0.15/.gitignore000066400000000000000000000000521415152657100154170ustar00rootroot00000000000000__pycache__/ .cache build dist *.egg-info flatlatex-0.15/LICENCE000066400000000000000000000025461415152657100144260ustar00rootroot00000000000000Copyright (c) 2016-2018 Jean-Benoist Leger Copyright (c) 2018, Pierre-Elliott Bécue All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. flatlatex-0.15/TODO000066400000000000000000000000001415152657100141100ustar00rootroot00000000000000flatlatex-0.15/flatlatex/000077500000000000000000000000001415152657100154165ustar00rootroot00000000000000flatlatex-0.15/flatlatex/__init__.py000066400000000000000000000050401415152657100175260ustar00rootroot00000000000000# Copyright (c) 2016, Jean-Benoist Leger # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. 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. """ LaTeX math to Unicode text converter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ flatlatex is a basic converter from LaTeX math to human readable text math using unicode characters. Basic example: >>> import flatlatex >>> c = flatlatex.converter() >>> c.convert( ... ( ... r'\\forall \\eta>0\\, \\exists n\\in\\mathbb{N}\\, \\forall i>n\\,' ... r' |u_i-\\mathcal{l}|<\\eta' ... )) '∀η>0 ∃n∈ℕ ∀i>n |uᵢ-𝓵|<η' Commands can be added with LaTeX syntax: >>> import flatlatex >>> c = flatlatex.converter() >>> c.add_newcommand(r'\\newcommand\\prob{\\mathbb{P}}') >>> c.add_newcommand(r'\\newcommand\\binom[2]{\\frac{#2!}{#1!(#2-#1)!}}') >>> c.convert(r'\\prob(X=k)\\,=\\,\\binom{k}{n}\\times p^k(1-p)^{n-k}') 'ℙ(X=k) = (n!)/(k!(n-k)!)×pᵏ(1-p)ⁿ⁻ᵏ' The behavior can be change: >>> import flatlatex >>> c = flatlatex.converter() >>> c.convert(r'\\frac{8}{9}') '⁸⁄₉' >>> c.allow_zw = False >>> c.convert(r'\\frac{8}{9}') '8/9' """ __title__ = "flatlatex" __author__ = "Jean-Benoist Leger" __licence__ = "BSD-2" version_info = (0, 15) __version__ = ".".join(map(str, version_info)) from .conv import converter flatlatex-0.15/flatlatex/bin/000077500000000000000000000000001415152657100161665ustar00rootroot00000000000000flatlatex-0.15/flatlatex/bin/__init__.py000066400000000000000000000000001415152657100202650ustar00rootroot00000000000000flatlatex-0.15/flatlatex/bin/flatlatex.py000066400000000000000000000046551415152657100205360ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (c) 2018, Pierre-Elliott Bécue # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. 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. """ This is a rough binary command that casts a converter and calls convert on the user's input. """ import argparse import flatlatex def do_convert(arguments): """Do the actual conversion""" converter = flatlatex.converter( arguments.disallow_zero_width, arguments.disallow_combinings, ) return converter.convert(arguments.input[0]) def main(): parser = argparse.ArgumentParser( description="Convert latex entries to flat unicode" ) parser.add_argument('-C', '--disallow-combinings', help="Disallow combining characters to be used", action="store_false", default=True) parser.add_argument('-Z', '--disallow-zero-width', help="Disallow zero-width characters to be used", action="store_false", default=True) parser.add_argument("input", type=str, nargs=1, help="The latex string to convert") args = parser.parse_args() ret = do_convert(args) print(ret) flatlatex-0.15/flatlatex/conv.py000066400000000000000000000237241415152657100167450ustar00rootroot00000000000000# Copyright (c) 2016, Jean-Benoist Leger # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. 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. import regex import unicodedata from . import data from .transliteration import transliterator, transliterate from . import latexfuntypes from . import parser from .latexfuntypes import LatexSyntaxError class converter: """flatlatex converter class :attrib allow_zw: boolean which indicate if zero width characters are allowed (True by default). :attrib allow_combinings: boolean which indicate if combining characters are allowed (True by default). :attrib ignore_newlines: boolean which indicates if newlines must be removed (True by default). :attrib keep_spaces: boolean which indicates if spaces must be keeped (False by default). """ def __init__( self, allow_zw=True, allow_combinings=True, ignore_newlines=True, keep_spaces=False, ): """Initialize a convert method.""" self.__cmds = {} # transliteration for k, v in data.transliterators.items(): self.__cmds[k] = transliterator(v) # symbols def makefun(symb): return lambda x: symb for cmd in data.symbols: self.__cmds[cmd] = latexfuntypes.latexfun(makefun(data.symbols[cmd]), 0) # combinings def makefun_comb(comb): return lambda x: self.__latexfun_comb(comb, x) for cmd in data.combinings: self.__cmds[cmd] = latexfuntypes.latexfun( makefun_comb(data.combinings[cmd]), 1 ) # others self.__cmds[r"\frac"] = latexfuntypes.latexfun(self.__latexfun_frac, 2) self.__cmds[r"\sqrt"] = latexfuntypes.latexfun(self.__latexfun_sqrt, 1) # config section self.allow_zw = allow_zw self.allow_combinings = allow_combinings self.ignore_newlines = ignore_newlines self.keep_spaces = keep_spaces # newcommands for nc in data.newcommands: self.add_newcommand(nc) def _convert(self, expr): if self.ignore_newlines: expr = expr.replace("\r", "").replace("\n", "") parsed = parser.parse(expr, keep_spaces=self.keep_spaces) outvec = [] idx = 0 while idx < len(parsed): element = parsed[idx] if element[0] == "oper": outvec.append(("oper", element[1])) idx += 1 continue if element[0] == "char": outvec.append(("char", element[1])) idx += 1 continue if element[0] == "cmd": try: pycmd = self.__cmds[element[1]] except KeyError: outvec.append(("char", element[1])) idx += 1 continue consumed = 0 raw_args = [] for k in range(idx + 1, len(parsed)): if len(raw_args) == pycmd.nargs: break consumed += 1 if parsed[k] != ("char", " "): raw_args.append(parsed[k][1]) if len(raw_args) != pycmd.nargs: raise LatexSyntaxError args = [self._convert(arg) for arg in raw_args] outvec.append(("char", pycmd.fun(args))) idx += 1 + consumed continue if element[0] == "subexpr": outvec.append(("char", self._convert(element[1]))) idx += 1 continue raise Exception # subandsuperscript for idx in range(len(outvec)): if outvec[idx][0] == "oper": if len(outvec) <= idx + 1: raise LatexSyntaxError if outvec[idx + 1][0] == "oper": raise LatexSyntaxError for idx in range(len(outvec)): if outvec[idx][0] == "oper" and outvec[idx][1] == "^": if idx + 2 < len(outvec): if outvec[idx + 2][0] == "oper" and outvec[idx + 2][1] == "_": # we invert ^ and _ ( outvec[idx], outvec[idx + 1], outvec[idx + 2], outvec[idx + 3], ) = ( outvec[idx + 2], outvec[idx + 3], outvec[idx], outvec[idx + 1], ) # sub newoutvec = [] idx = 0 while idx < len(outvec): if idx + 1 < len(outvec): if outvec[idx + 1][0] == "oper" and outvec[idx + 1][1] == "_": newoutvec.append( ("char", self.__indexed(outvec[idx][1], outvec[idx + 2][1])) ) idx += 3 continue newoutvec.append(outvec[idx]) idx += 1 outvec = newoutvec # super newoutvec = [] idx = 0 while idx < len(outvec): if idx + 1 < len(outvec): if outvec[idx + 1][0] == "oper" and outvec[idx + 1][1] == "^": newoutvec.append( ("char", self.__exponent(outvec[idx][1], outvec[idx + 2][1])) ) idx += 3 continue newoutvec.append(outvec[idx]) idx += 1 outvec = newoutvec return "".join([x[1] for x in outvec]) def convert(self, x): """Convert LaTeX math to Unicode text. :param expr: LaTeX math expression to convert""" return unicodedata.normalize("NFC", self._convert(x)) def __indexed(self, a, b): f_sub = transliterate(data.subscript) bsub, ok = f_sub(b) if self.__is_complex_expr(a): a = "(" + a + ")" if ok: return a + bsub return a + "[" + b + "]" def __exponent(self, a, b): f_sup = transliterate(data.superscript) bsup, ok = f_sup(b) if self.__is_complex_expr(a): a = "(" + a + ")" if ok: return a + bsup if self.__is_complex_expr(b): b = "(" + b + ")" return a + "^" + b def __is_complex_expr(self, expr): return sum(1 for ch in expr if unicodedata.combining(ch) == 0) > 1 def __latexfun_comb(self, comb, inputs): expr = inputs[0] if len(expr) == 1: if self.allow_combinings: return expr + comb[0] return comb[1] + "(" + expr + ")" def add_newcommand(self, one_newcommand): """Add a command definiton using LaTeX syntax. :param expr: a valid \\newcommand (or \\renewcommand or \\def) definition. Examples: - r'\\newcommand\\prob{\\mathbb{P}}' - r'\\newcommand\\binom[2]{\\frac{#2!}{#1!(#2-#1)!}}' """ parsed = parser.parse(one_newcommand, keep_spaces=self.keep_spaces) if not (len(parsed) in (3, 6)): raise LatexSyntaxError ok = False if parsed[0][0] == "cmd": if parsed[0][1] in (r"\newcommand", r"\renewcommand", r"\def"): ok = True if not ok: raise LatexSyntaxError nargs = 0 if len(parsed) == 6: if parsed[2] == ("char", "[") and parsed[4] == ("char", "]"): nargs = int(parsed[3][1]) else: raise LatexSyntaxError cmdname = parsed[1][1] cmdexpr = parsed[-1][1] def thefun(args): expr = cmdexpr for i in range(len(args)): expr = regex.sub("#%i" % (i + 1), args[i], expr) return self._convert(expr) self.__cmds[cmdname] = latexfuntypes.latexfun(lambda x: thefun(x), nargs) return None def __latexfun_frac(self, inputs): a = inputs[0] b = inputs[1] try: ret = data.known_fracts[(a, b)] return ret except KeyError: pass if self.allow_zw: f_sub = transliterate(data.subscript) f_sup = transliterate(data.superscript) a_sup, ok_sup = f_sup(a) b_sub, ok_sub = f_sub(b) if ok_sup and ok_sub: return a_sup + "⁄" + b_sub if self.__is_complex_expr(a): a = "(" + a + ")" if self.__is_complex_expr(b): b = "(" + b + ")" return a + "/" + b def __latexfun_sqrt(self, inputs): a = inputs[0] if self.__is_complex_expr(a): a = "(" + a + ")" return "√" + a flatlatex-0.15/flatlatex/data.py000066400000000000000000000474011415152657100167070ustar00rootroot00000000000000# Copyright (c) 2016, Jean-Benoist Leger # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. 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. known_fracts = { ("1", "2"): "½", ("1", "3"): "⅓", ("2", "3"): "⅔", ("1", "4"): "¼", ("3", "4"): "¾", ("1", "5"): "⅕", ("2", "5"): "⅖", ("3", "5"): "⅗", ("4", "5"): "⅘", ("1", "6"): "⅙", ("5", "6"): "⅚", ("1", "7"): "⅐", ("1", "8"): "⅛", ("3", "8"): "⅜", ("5", "8"): "⅝", ("7", "8"): "⅞", ("1", "9"): "⅑", } subscript = { "0": "₀", "1": "₁", "2": "₂", "3": "₃", "4": "₄", "5": "₅", "6": "₆", "7": "₇", "8": "₈", "9": "₉", "+": "₊", "-": "₋", "=": "₌", "(": "₍", ")": "₎", "a": "ₐ", "e": "ₑ", "i": "ᵢ", "j": "\N{LATIN SUBSCRIPT SMALL LETTER J}", "o": "ₒ", "r": "ᵣ", "u": "ᵤ", "v": "ᵥ", "x": "ₓ", "β": "ᵦ", "γ": "ᵧ", "ρ": "ᵨ", "φ": "ᵩ", "χ": "ᵪ", } superscript = { "0": "⁰", "1": "¹", "2": "²", "3": "³", "4": "⁴", "5": "⁵", "6": "⁶", "7": "⁷", "8": "⁸", "9": "⁹", "+": "⁺", "-": "⁻", "=": "⁼", "(": "⁽", ")": "⁾", "a": "ᵃ", "b": "ᵇ", "c": "ᶜ", "d": "ᵈ", "e": "ᵉ", "f": "ᶠ", "g": "ᵍ", "h": "ʰ", "i": "ⁱ", "j": "ʲ", "k": "ᵏ", "l": "ˡ", "m": "ᵐ", "n": "ⁿ", "o": "ᵒ", "p": "ᵖ", "r": "ʳ", "s": "ˢ", "t": "ᵗ", "u": "ᵘ", "v": "ᵛ", "w": "ʷ", "x": "ˣ", "y": "ʸ", "z": "ᶻ", "A": "ᴬ", "B": "ᴮ", "D": "ᴰ", "E": "ᴱ", "G": "ᴳ", "H": "ᴴ", "I": "ᴵ", "J": "ᴶ", "K": "ᴷ", "L": "ᴸ", "M": "ᴹ", "N": "ᴺ", "O": "ᴼ", "P": "ᴾ", "R": "ᴿ", "T": "ᵀ", "U": "ᵁ", "V": "ⱽ", "W": "ᵂ", "α": "ᵅ", "β": "ᵝ", "γ": "ᵞ", "δ": "ᵟ", "∊": "ᵋ", "θ": "ᶿ", "ι": "ᶥ", "Φ": "ᶲ", "φ": "ᵠ", "χ": "ᵡ", } bb = { "A": "𝔸", "B": "𝔹", "C": "ℂ", "D": "𝔻", "E": "𝔼", "F": "𝔽", "G": "𝔾", "H": "ℍ", "I": "𝕀", "J": "𝕁", "K": "𝕂", "L": "𝕃", "M": "𝕄", "N": "ℕ", "O": "𝕆", "P": "ℙ", "Q": "ℚ", "R": "ℝ", "S": "𝕊", "T": "𝕋", "U": "𝕌", "V": "𝕍", "W": "𝕎", "X": "𝕏", "Y": "𝕐", "Z": "ℤ", "a": "𝕒", "b": "𝕓", "c": "𝕔", "d": "𝕕", "e": "𝕖", "f": "𝕗", "g": "𝕘", "h": "𝕙", "i": "𝕚", "j": "𝕛", "k": "𝕜", "l": "𝕝", "m": "𝕞", "n": "𝕟", "o": "𝕠", "p": "𝕡", "q": "𝕢", "r": "𝕣", "s": "𝕤", "t": "𝕥", "u": "𝕦", "v": "𝕧", "w": "𝕨", "x": "𝕩", "y": "𝕪", "z": "𝕫", "0": "𝟘", "1": "𝟙", "2": "𝟚", "3": "𝟛", "4": "𝟜", "5": "𝟝", "6": "𝟞", "7": "𝟟", "8": "𝟠", "9": "𝟡", } bf = { "A": "𝐀", "B": "𝐁", "C": "𝐂", "D": "𝐃", "E": "𝐄", "F": "𝐅", "G": "𝐆", "H": "𝐇", "I": "𝐈", "J": "𝐉", "K": "𝐊", "L": "𝐋", "M": "𝐌", "N": "𝐍", "O": "𝐎", "P": "𝐏", "Q": "𝐐", "R": "𝐑", "S": "𝐒", "T": "𝐓", "U": "𝐔", "V": "𝐕", "W": "𝐖", "X": "𝐗", "Y": "𝐘", "Z": "𝐙", "a": "𝐚", "b": "𝐛", "c": "𝐜", "d": "𝐝", "e": "𝐞", "f": "𝐟", "g": "𝐠", "h": "𝐡", "i": "𝐢", "j": "𝐣", "k": "𝐤", "l": "𝐥", "m": "𝐦", "n": "𝐧", "o": "𝐨", "p": "𝐩", "q": "𝐪", "r": "𝐫", "s": "𝐬", "t": "𝐭", "u": "𝐮", "v": "𝐯", "w": "𝐰", "x": "𝐱", "y": "𝐲", "z": "𝐳", "Α": "𝚨", "Β": "𝚩", "Γ": "𝚪", "Δ": "𝚫", "Ε": "𝚬", "Ζ": "𝚭", "Η": "𝚮", "Θ": "𝚯", "Ι": "𝚰", "Κ": "𝚱", "Λ": "𝚲", "Μ": "𝚳", "Ν": "𝚴", "Ξ": "𝚵", "Ο": "𝚶", "Π": "𝚷", "Ρ": "𝚸", "ϴ": "𝚹", "Σ": "𝚺", "Τ": "𝚻", "Υ": "𝚼", "Φ": "𝚽", "Χ": "𝚾", "Ψ": "𝚿", "Ω": "𝛀", "∇": "𝛁", "α": "𝛂", "β": "𝛃", "γ": "𝛄", "δ": "𝛅", "ε": "𝛆", "ζ": "𝛇", "η": "𝛈", "θ": "𝛉", "ι": "𝛊", "κ": "𝛋", "λ": "𝛌", "μ": "𝛍", "ν": "𝛎", "ξ": "𝛏", "ο": "𝛐", "π": "𝛑", "ρ": "𝛒", "ς": "𝛓", "σ": "𝛔", "τ": "𝛕", "υ": "𝛖", "φ": "𝛗", "χ": "𝛘", "ψ": "𝛙", "ω": "𝛚", "∂": "𝛛", "ϵ": "𝛜", "ϑ": "𝛝", "ϰ": "𝛞", "ϕ": "𝛟", "ϱ": "𝛠", "ϖ": "𝛡", "0": "𝟎", "1": "𝟏", "2": "𝟐", "3": "𝟑", "4": "𝟒", "5": "𝟓", "6": "𝟔", "7": "𝟕", "8": "𝟖", "9": "𝟗", } cal = { "A": "𝓐", "B": "𝓑", "C": "𝓒", "D": "𝓓", "E": "𝓔", "F": "𝓕", "G": "𝓖", "H": "𝓗", "I": "𝓘", "J": "𝓙", "K": "𝓚", "L": "𝓛", "M": "𝓜", "N": "𝓝", "O": "𝓞", "P": "𝓟", "Q": "𝓠", "R": "𝓡", "S": "𝓢", "T": "𝓣", "U": "𝓤", "V": "𝓥", "W": "𝓦", "X": "𝓧", "Y": "𝓨", "Z": "𝓩", "a": "𝓪", "b": "𝓫", "c": "𝓬", "d": "𝓭", "e": "𝓮", "f": "𝓯", "g": "𝓰", "h": "𝓱", "i": "𝓲", "j": "𝓳", "k": "𝓴", "l": "𝓵", "m": "𝓶", "n": "𝓷", "o": "𝓸", "p": "𝓹", "q": "𝓺", "r": "𝓻", "s": "𝓼", "t": "𝓽", "u": "𝓾", "v": "𝓿", "w": "𝔀", "x": "𝔁", "y": "𝔂", "z": "𝔃", } frak = { "A": "𝔄", "B": "𝔅", "C": "ℭ", "D": "𝔇", "E": "𝔈", "F": "𝔉", "G": "𝔊", "H": "ℌ", "I": "ℑ", "J": "𝔍", "K": "𝔎", "L": "𝔏", "M": "𝔐", "N": "𝔑", "O": "𝔒", "P": "𝔓", "Q": "𝔔", "R": "ℜ", "S": "𝔖", "T": "𝔗", "U": "𝔘", "V": "𝔙", "W": "𝔚", "X": "𝔛", "Y": "𝔜", "Z": "ℨ", "a": "𝔞", "b": "𝔟", "c": "𝔠", "d": "𝔡", "e": "𝔢", "f": "𝔣", "g": "𝔤", "h": "𝔥", "i": "𝔦", "j": "𝔧", "k": "𝔨", "l": "𝔩", "m": "𝔪", "n": "𝔫", "o": "𝔬", "p": "𝔭", "q": "𝔮", "r": "𝔯", "s": "𝔰", "t": "𝔱", "u": "𝔲", "v": "𝔳", "w": "𝔴", "x": "𝔵", "y": "𝔶", "z": "𝔷", } it = { "A": "𝐴", "B": "𝐵", "C": "𝐶", "D": "𝐷", "E": "𝐸", "F": "𝐹", "G": "𝐺", "H": "𝐻", "I": "𝐼", "J": "𝐽", "K": "𝐾", "L": "𝐿", "M": "𝑀", "N": "𝑁", "O": "𝑂", "P": "𝑃", "Q": "𝑄", "R": "𝑅", "S": "𝑆", "T": "𝑇", "U": "𝑈", "V": "𝑉", "W": "𝑊", "X": "𝑋", "Y": "𝑌", "Z": "𝑍", "a": "𝑎", "b": "𝑏", "c": "𝑐", "d": "𝑑", "e": "𝑒", "f": "𝑓", "g": "𝑔", "h": "ℎ", "i": "𝑖", "j": "𝑗", "k": "𝑘", "l": "𝑙", "m": "𝑚", "n": "𝑛", "o": "𝑜", "p": "𝑝", "q": "𝑞", "r": "𝑟", "s": "𝑠", "t": "𝑡", "u": "𝑢", "v": "𝑣", "w": "𝑤", "x": "𝑥", "y": "𝑦", "z": "𝑧", "Α": "𝛢", "Β": "𝛣", "Γ": "𝛤", "Δ": "𝛥", "Ε": "𝛦", "Ζ": "𝛧", "Η": "𝛨", "Θ": "𝛩", "Ι": "𝛪", "Κ": "𝛫", "Λ": "𝛬", "Μ": "𝛭", "Ν": "𝛮", "Ξ": "𝛯", "Ο": "𝛰", "Π": "𝛱", "Ρ": "𝛲", "ϴ": "𝛳", "Σ": "𝛴", "Τ": "𝛵", "Υ": "𝛶", "Φ": "𝛷", "Χ": "𝛸", "Ψ": "𝛹", "Ω": "𝛺", "∇": "𝛻", "α": "𝛼", "β": "𝛽", "γ": "𝛾", "δ": "𝛿", "ε": "𝜀", "ζ": "𝜁", "η": "𝜂", "θ": "𝜃", "ι": "𝜄", "κ": "𝜅", "λ": "𝜆", "μ": "𝜇", "ν": "𝜈", "ξ": "𝜉", "ο": "𝜊", "π": "𝜋", "ρ": "𝜌", "ς": "𝜍", "σ": "𝜎", "τ": "𝜏", "υ": "𝜐", "φ": "𝜑", "χ": "𝜒", "ψ": "𝜓", "ω": "𝜔", "∂": "𝜕", "ϵ": "𝜖", "ϑ": "𝜗", "ϰ": "𝜘", "ϕ": "𝜙", "ϱ": "𝜚", "ϖ": "𝜛", } mono = { "A": "𝙰", "B": "𝙱", "C": "𝙲", "D": "𝙳", "E": "𝙴", "F": "𝙵", "G": "𝙶", "H": "𝙷", "I": "𝙸", "J": "𝙹", "K": "𝙺", "L": "𝙻", "M": "𝙼", "N": "𝙽", "O": "𝙾", "P": "𝙿", "Q": "𝚀", "R": "𝚁", "S": "𝚂", "T": "𝚃", "U": "𝚄", "V": "𝚅", "W": "𝚆", "X": "𝚇", "Y": "𝚈", "Z": "𝚉", "a": "𝚊", "b": "𝚋", "c": "𝚌", "d": "𝚍", "e": "𝚎", "f": "𝚏", "g": "𝚐", "h": "𝚑", "i": "𝚒", "j": "𝚓", "k": "𝚔", "l": "𝚕", "m": "𝚖", "n": "𝚗", "o": "𝚘", "p": "𝚙", "q": "𝚚", "r": "𝚛", "s": "𝚜", "t": "𝚝", "u": "𝚞", "v": "𝚟", "w": "𝚠", "x": "𝚡", "y": "𝚢", "z": "𝚣", "0": "𝟶", "1": "𝟷", "2": "𝟸", "3": "𝟹", "4": "𝟺", "5": "𝟻", "6": "𝟼", "7": "𝟽", "8": "𝟾", "9": "𝟿", } transliterators = { r"\bb": bb, r"\bf": bf, r"\cal": cal, r"\frak": frak, r"\it": it, r"\mono": mono, } symbols = { r"\_": "_", r"\|": "‖", r"\lVert": "‖", r"\rVert": "‖", r"\\": "\\", r"\aleph": "ℵ", r"\alpha": "α", r"\amalg": "∐", r"\angle": "∠", r"\approx": "≈", r"\approxeq": "≊", r"\ast": "∗", r"\asymp": "≍", r"\backsim": "∽", r"\backsimeq": "⋍", r"\backslash": "\\", r"\barwedge": "⊼", r"\because": "∵", r"\beta": "β", r"\beth": "ℶ", r"\between": "≬", r"\bigcap": "⋂", r"\bigcup": "⋃", r"\bigvee": "⋁", r"\bigwedge": "⋀", r"\blacksquare": "∎", r"\bot": "⊤", r"\bowtie": "⋈", r"\boxdot": "⊡", r"\boxminus": "⊟", r"\boxplus": "⊞", r"\boxtimes": "⊠", r"\bullet": "•", r"\bumpeq": "≏", r"\Bumpeq": "≎", r"\cap": "∩", r"\Cap": "⋒", r"\cdot": "·", r"\cdots": "⋯", r"\chi": "χ", r"\circ": "∘", r"\circeq": "≗", r"\circlearrowleft": "↺", r"\circlearrowright": "↻", r"\circledast": "⊛", r"\circledcirc": "⊚", r"\circleddash": "⊝", r"\clubsuit": "♣", r"\varclubsuit": "♧", r"\complement": "∁", r"\cong": "≅", r"\cup": "∪", r"\Cup": "⋓", r"\curlyeqprec": "⋞", r"\curlyeqsucc": "⋟", r"\curlyvee": "⋎", r"\curlywedge": "⋏", r"\curvearrowleft": "↶", r"\curvearrowright": "↷", r"\dag": "†", r"\daleth": "ℸ", r"\dashleftarrow": "⇠", r"\dashrightarrow": "⇢", r"\dashv": "⊣", r"\ddag": "‡", r"\ddots": "⋱", r"\ddotsup": "⋰", r"\defeq": "≝", r"\delta": "δ", r"\Delta": "Δ", r"\diamond": "⋄", r"\diamondsuit": "♢", r"\vardiamondsuit": "♦", r"\div": "÷", r"\divideontimes": "⋇", r"\doteq": "≐", r"\doteqdot": "≑", r"\dotplus": "∔", r"\dots": "…", r"\downarrow": "↓", r"\Downarrow": "⇓", r"\downdownarrows": "⇊", r"\downharpoonleft": "⇃", r"\downharpoonright": "⇂", r"\ell": "ℓ", r"\emptyset": "∅", r"\epsilon": "ϵ", r"\eqcirc": "≖", r"\eqslantgtr": "⋝", r"\eqslantless": "⋜", r"\equiv": "≡", r"\eta": "η", r"\exists": "∃", r"\fallingdotseq": "≒", r"\Finv": "Ⅎ", r"\flat": "♭", r"\forall": "∀", r"\gamma": "γ", r"\Gamma": "Γ", r"\geq": "≥", r"\geqq": "≧", r"\gg": "≫", r"\ggg": "⋙", r"\gimel": "ℷ", r"\gneqq": "≩", r"\gnsim": "⋧", r"\gtrdot": "⋗", r"\gtreqless": "⋛", r"\gtrless": "≷", r"\gtrsim": "≳", r"\hbar": "ℏ", r"\heartsuit": "♡", r"\varheartsuit": "♥", r"\hookleftarrow": "↩", r"\hookrightarrow": "↪", r"\iiint": "∭", r"\iint": "∬", r"\Im": "ℑ", r"\in": "∈", r"\infty": "∞", r"\int": "∫", r"\intercal": "⊺", r"\iota": "ι", r"\kappa": "κ", r"\lambda": "λ", r"\Lambda": "Λ", r"\leadsto": "↝", r"\leftarrow": "←", r"\Leftarrow": "⇐", r"\leftarrowtail": "↢", r"\leftharpoondown": "↽", r"\leftharpoonup": "↼", r"\leftleftarrows": "⇇", r"\leftrightarrow": "↔", r"\Leftrightarrow": "⇔", r"\leftrightarrows": "⇆", r"\leftrightharpoons": "⇋", r"\leftrightsquigarrow": "↭", r"\leftthreetimes": "⋋", r"\leq": "≤", r"\leqq": "≦", r"\lessdot": "⋖", r"\lesseqgtr": "⋚", r"\lessgtr": "≶", r"\lesssim": "≲", r"\lhd": "⊲", r"\ll": "≪", r"\Lleftarrow": "⇚", r"\lll": "⋘", r"\lneqq": "≨", r"\lnot": "¬", r"\neg": "¬", r"\lnsim": "⋦", r"\looparrowleft": "↫", r"\looparrowright": "↬", r"\Lsh": "↰", r"\ltimes": "⋉", r"\mapsto": "↦", r"\measuredangle": "∡", r"\mho": "℧", r"\mid": "∣", r"\models": "⊨", r"\mp": "∓", r"\multimap": "⊸", r"\multimapdotbothA": "⊶", r"\multimapdotbothB": "⊷", r"\mu": "μ", r"\nabla": "∇", r"\natural": "♮", r"\ncong": "≇", r"\nearrow": "↗", r"\neq": "≠", r"\nexists": "∄", r"\ngeq": "≱", r"\ngtr": "≯", r"\ni": "∋", r"\nleftarrow": "↚", r"\nLeftarrow": "⇍", r"\nleftrightarrow": "↮", r"\nLeftrightarrow": "⇎", r"\nleq": "≰", r"\nless": "≮", r"\nmid": "∤", r"\notin": "∉", r"\nparallel": "∦", r"\nprec": "⊁", r"\nrightarrow": "↛", r"\nRightarrow": "⇏", r"\nsim": "≁", r"\nsubseteq": "⊈", r"\nsucc": "⊀", r"\nsupseteq": "⊉", r"\ntriangleleft": "⋪", r"\ntrianglelefteq": "⋬", r"\ntriangleright": "⋫", r"\ntrianglerighteq": "⋭", r"\nu": "ν", r"\nvdash": "⊬", r"\nvDash": "⊭", r"\nVdash": "⊯", r"\nwarrow": "↖", r"\odot": "⊙", r"\oiiint": "∰", r"\oiint": "∯", r"\oint": "∮", r"\omega": "ω", r"\Omega": "Ω", r"\ominus": "⊖", r"\oplus": "⊕", r"\oslash": "⊘", r"\otimes": "⊗", r"\P": "¶", r"\parallel": "∥", r"\partial": "∂", r"\perp": "⊥", r"\phi": "φ", r"\Phi": "Φ", r"\pitchfork": "⋔", r"\pi": "π", r"\Pi": "Π", r"\pm": "±", r"\pounds": "£", r"\prec": "≺", r"\preccurlyeq": "≼", r"\precnsim": "⋨", r"\precsim": "≾", r"\prod": "∏", r"\propto": "∝", r"\psi": "ψ", r"\Psi": "Ψ", r"\Re": "ℜ", r"\rhd": "⊳", r"\rho": "ρ", r"\rightarrow": "→", r"\Rightarrow": "⇒", r"\rightarrowtail": "↣", r"\rightharpoondown": "⇁", r"\rightharpoonup": "⇀", r"\rightleftarrows": "⇄", r"\rightleftharpoons": "⇌", r"\rightrightarrows": "⇉", r"\rightsquigarrow": "⇝", r"\rightthreetimes": "⋌", r"\risingdotseq": "≓", r"\Rsh": "↱", r"\rtimes": "⋊", r"\S": "§", r"\searrow": "↘", r"\swarrow": "↙", r"\setminus": "∖", r"\sharp": "♯", r"\sigma": "σ", r"\Sigma": "Σ", r"\sim": "∼", r"\simeq": "≃", r"\spadesuit": "♠", r"\varspadesuit": "♤", r"\sphericalangle": "∢", r"\sqcap": "⊓", r"\sqcup": "⊔", r"\sqsubset": "⊏", r"\sqsubseteq": "⊑", r"\sqsupset": "⊐", r"\sqsupseteq": "⊒", r"\star": "⋆", r"\subset": "⊂", r"\Subset": "⋐", r"\subseteq": "⊆", r"\subsetneq": "⊊", r"\succ": "≻", r"\succcurlyeq": "≽", r"\succnsim": "⋩", r"\succsim": "≿", r"\sum": "∑", r"\supset": "⊃", r"\Supset": "⋑", r"\supseteq": "⊇", r"\supsetneq": "⊋", r"\surd": "√", r"\tau": "τ", r"\therefore": "∴", r"\theta": "θ", r"\Theta": "Θ", r"\times": "×", r"\triangleq": "≜", r"\twoheadleftarrow": "↞", r"\twoheadrightarrow": "↠", r"\unlhd": "⊴", r"\unrhd": "⊵", r"\uparrow": "↑", r"\Uparrow": "⇑", r"\updownarrow": "↕", r"\Updownarrow": "⇕", r"\upharpoonleft": "↿", r"\upharpoonright": "↾", r"\uplus": "⊎", r"\upsilon": "υ", r"\upuparrows": "⇈", r"\varepsilon": "ε", r"\varphi": "ϕ", r"\varpi": "ϖ", r"\varrho": "ϱ", r"\varsigma": "ς", r"\vartheta": "ϑ", r"\vdash": "⊢", r"\vDash": "⊧", r"\Vdash": "⊩", r"\vdots": "⋮", r"\vee": "∨", r"\veebar": "⊻", r"\Vvdash": "⊪", r"\wedge": "∧", r"\wp": "℘", r"\wr": "≀", r"\xi": "ξ", r"\Xi": "Ξ", r"\zeta": "ζ", r"\ ": " ", r"\,": " ", r"\;": " ", r"\quad": " ", r"\qquad": " ", } combinings = { r"\hat": ("\u0302", "hat"), r"\grave": ("\u0300", "grave"), r"\dot": ("\u0307", "dot"), r"\not": ("\u0338", "not"), r"\overrightarrow": ("\u20d7", "overrightarrow"), r"\overline": ("\u0305", "overline"), r"\tilde": ("\u0303", "tilde"), r"\bar": ("\u0304", "bar"), r"\acute": ("\u0301", "acute"), r"\ddot": ("\u0308", "ddot"), r"\overleftarrow": ("\u20d6", "overleftarrow"), r"\check": ("\u030c", "check"), r"\vec": ("\u20d7", "vec"), r"\underline": ("\u0332", "underline"), } newcommands = ( r"\newcommand\mathcal[1]{\cal{#1}}", r"\newcommand\mathit[1]{\it{#1}}", r"\newcommand\mathbf[1]{\bf{#1}}", r"\newcommand\mathbb[1]{\bb{#1}}", r"\newcommand\mathmono[1]{\mono{#1}}", r"\newcommand\mathfrak[1]{\frak{#1}}", r"\newcommand\binom[2]{binom(#1,#2)}", r"\newcommand\'[1]{\acute{#1}}", r"\newcommand\`[1]{\grave{#1}}", r"\newcommand\^[1]{\hat{#1}}", r"\newcommand\widehat[1]{\hat{#1}}", ) flatlatex-0.15/flatlatex/latexfuntypes.py000066400000000000000000000027511415152657100207100ustar00rootroot00000000000000# Copyright (c) 2016, Jean-Benoist Leger # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. 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. class latexfun: def __init__(self, fun, nargs): self.fun = fun self.nargs = nargs class LatexSyntaxError(SyntaxError): pass flatlatex-0.15/flatlatex/parser.py000066400000000000000000000041341415152657100172660ustar00rootroot00000000000000# Copyright (c) 2016, Jean-Benoist Leger # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. 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. import regex from .latexfuntypes import LatexSyntaxError def parse_one_element(s): R = r"((?>\\(?:[^A-Za-z]|[A-Za-z]+))|(?>[^\{\}\\])|\{(?1)*\})" r = regex.match(R, s) if not r: raise LatexSyntaxError s = s[r.span()[1] :] c = r.captures()[0] if c[0] == "\\": return (("cmd", c), s) if c[0] == "{": return (("subexpr", c[1:-1]), s) if c in ("_", "^"): return (("oper", c), s) return (("char", c), s) def parse(s, keep_spaces=False): last = ("", None) ret = [] while len(s) > 0: m, s = parse_one_element(s) if not (m[1] == " "): ret.append(m) else: if keep_spaces and last[0] != "cmd": ret.append(m) last = m return ret flatlatex-0.15/flatlatex/tests/000077500000000000000000000000001415152657100165605ustar00rootroot00000000000000flatlatex-0.15/flatlatex/tests/__init__.py000066400000000000000000000000001415152657100206570ustar00rootroot00000000000000flatlatex-0.15/flatlatex/tests/test_conv.py000066400000000000000000000074451415152657100211500ustar00rootroot00000000000000# Copyright (c) 2016-2018, Jean-Benoist Leger # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. 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. import pytest from .. import converter @pytest.mark.parametrize("keep_spaces", (False, True)) def test_conv1(keep_spaces): c = converter(keep_spaces=keep_spaces) r = c.convert( ( r"\forall \eta>0\, \exists n\in\mathbb{N}\, \forall i>n\, " r"|u_i-\mathcal{l}|<\eta" ) ) assert r == "∀η>0 ∃n∈ℕ ∀i>n |uᵢ-𝓵|<η" @pytest.mark.parametrize("keep_spaces", (False, True)) def test_conv2(keep_spaces): c = converter(keep_spaces=keep_spaces) c.add_newcommand(r"\newcommand\prob{\mathbb{P}}") c.add_newcommand(r"\newcommand\binom[2]{\frac{#2!}{#1!(#2-#1)!}}") r = c.convert(r"\prob(X=k)\,=\,\binom{k}{n}\times p^k(1-p)^{n-k}") assert r == "ℙ(X=k) = (n!)/(k!(n-k)!)×pᵏ(1-p)ⁿ⁻ᵏ" def test_conv3(): c = converter() c.allow_zw = True r = c.convert(r"\frac{8}{9}") assert r == "⁸⁄₉" c.allow_zw = False r = c.convert(r"\frac{8}{9}") assert r == "8/9" def test_conv4(): c = converter() c.allow_combinings = True r = c.convert(r"\hat\alpha") assert r == "\u03B1\u0302" c.allow_combinings = False r = c.convert(r"\hat\alpha") assert r == "hat(\u03B1)" def test_conv5(): c = converter() r = c.convert(r"\hat{p}") assert r == "p\u0302" r = c.convert(r"p_1") assert r == "p\u2081" r = c.convert(r"\hat{p}_1") assert r == "p\u0302\u2081" r = c.convert(r"\hat{pc}_1") assert r == "(hat(pc))\u2081" @pytest.mark.parametrize("keep_spaces", (False, True)) def test_conv6(keep_spaces): c = converter(keep_spaces=keep_spaces) for x in ( r"\frac12", r"\frac1 2", r"\frac 12", r"\frac 1 2", r"\frac{1}2", r"\frac{1} 2", r"\frac {1}2", r"\frac {1} 2", r"\frac{1}{2}", r"\frac{1} {2}", r"\frac {1}{2}", r"\frac {1} {2}", r"\frac1{2}", r"\frac1 {2}", r"\frac 1{2}", r"\frac 1 {2}", ): r = c.convert(x) assert r == "\xbd" def test_conv7(): c = converter() for x, res in { r"\exists n": ("∃n", "∃n"), r"\exists\ n": ("∃ n", "∃ n"), r"\exists n": ("∃n", "∃ n"), r"\exists{} n": ("∃n", "∃ n"), r"\exists{}n": ("∃n", "∃n"), }.items(): for keep_spaces, obj in zip((False, True), res): c.keep_spaces = keep_spaces r = c.convert(x) assert r == obj, (x, keep_spaces) flatlatex-0.15/flatlatex/tests/test_data_consistancy.py000066400000000000000000000103011415152657100235120ustar00rootroot00000000000000# Copyright (c) 2017-2017, Jean-Benoist Leger # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. 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. from .. import data from .. import parser import ast def test_known_fracts(): assert type(data.known_fracts) is dict for k, v in data.known_fracts.items(): assert type(k) is tuple assert len(k) == 2 assert all([type(x) is str for x in k]) assert type(v) is str def test_transliteration_consistancy(): for d in ( data.subscript, data.superscript, data.bb, data.bf, data.cal, data.frak, data.it, data.mono, ): assert type(d) is dict for k, v in d.items(): assert type(k) is str assert type(v) is str def test_symbols_consistancy(): assert type(data.symbols) is dict for k, v in data.symbols.items(): assert type(k) is str out = parser.parse(k) assert len(out) == 1 assert out[0][0] == 'cmd' assert type(v) is str def test_combining_consistancy(): assert type(data.combinings) is dict for k, v in data.combinings.items(): assert type(k) is str out = parser.parse(k) assert len(out) == 1 assert out[0][0] == 'cmd' assert type(v) is tuple assert len(v) == 2 assert all([type(x) is str for x in v]) def test_newcommands_consistancy(): assert type(data.newcommands) is tuple for k in data.newcommands: parsed = parser.parse(k) assert len(parsed) in (3, 6) assert parsed[0][0] == 'cmd' assert parsed[0][1] in (r'\newcommand', r'\renewcommand', r'\def') if len(parsed) == 6: assert parsed[2] == ('char', '[') assert parsed[4] == ('char', ']') assert type(parsed[3][0]) is str a = int(parsed[3][1]) assert a >= 0 def test_replicated_command(): datasets = [ data.symbols.keys(), data.combinings.keys(), data.transliterators.keys(), ] datasets.append([parser.parse(nc)[1][1] for nc in data.newcommands]) for i in range(len(datasets)): for j in range(i + 1, len(datasets)): s1 = set(datasets[i]) s2 = set(datasets[j]) assert len(s1.intersection(s2)) == 0 def test_replicated_in_the_same_dict(): with open(data.__file__, encoding='utf8') as f: data_ast = ast.parse(f.read()) def toobj(x): if type(x) is ast.Tuple: return tuple(toobj(v) for v in x.elts) return x.s for seg in data_ast.body: if type(seg) is ast.Assign: if type(seg.value) is ast.Dict: keys = [toobj(k) for k in seg.value.keys] count = {} for k in keys: count[k] = count[k] + 1 if k in count else 1 for k, c in count.items(): assert c == 1, "duplicated key in dict: %s" % k flatlatex-0.15/flatlatex/tests/test_parser.py000066400000000000000000000042601415152657100214670ustar00rootroot00000000000000# Copyright (c) 2017-2018, Jean-Benoist Leger # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. 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. from .. import parser def test_parser(): expected = { r'\\': [('cmd', r'\\')], r'\cd': [('cmd', r'\cd')], r'ab': [('char', 'a'), ('char', 'b')], r'e{to{t}o}e': [ ('char', 'e'), ('subexpr', 'to{t}o'), ('char', 'e') ], r'g_f': [('char', 'g'), ('oper', '_'), ('char', 'f')], r'h^i': [('char', 'h'), ('oper', '^'), ('char', 'i')], } for k, v in expected.items(): assert all([ pki == vi for pki, vi in zip(parser.parse(k), v) ]), "parser '%s'" % k for k1, v1 in expected.items(): for k2, v2 in expected.items(): assert all([ pki == vi for pki, vi in zip(parser.parse(k1 + ' ' + k2), v1 + v2) ]), "parser '%s'+'%s'" % (k1, k2) flatlatex-0.15/flatlatex/transliteration.py000066400000000000000000000035401415152657100212140ustar00rootroot00000000000000# Copyright (c) 2016, Jean-Benoist Leger # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. 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. from . import latexfuntypes def transliterate(alphabet): alphabet_keys = alphabet.keys() def fun(flat_input): flat_output = "" success = True for c in flat_input: if c in alphabet_keys: flat_output += alphabet[c] else: flat_output += c success = False return (flat_output, success) return fun def transliterator(alphabet): return latexfuntypes.latexfun(lambda x: transliterate(alphabet)(x[0])[0], 1) flatlatex-0.15/release000077500000000000000000000003051415152657100147760ustar00rootroot00000000000000#!/bin/bash if ! [[ -f release ]] then echo "Must be run in ." >&2 exit 11 fi if ! py.test then exit 13 fi rm -rf dist/ python3 setup.py sdist bdist_wheel twine upload --sign dist/* flatlatex-0.15/setup.py000066400000000000000000000075301415152657100151510ustar00rootroot00000000000000"""Setup script for flatlatex Based on `sampleproject`, see: https://github.com/pypa/sampleproject """ # Always prefer setuptools over distutils from setuptools import setup, find_packages # To use a consistent encoding from codecs import open from os import path here = path.abspath(path.dirname(__file__)) # Get the long description from the __init__.py file with open(path.join(here, 'flatlatex', '__init__.py'), encoding='utf-8') as f: ast = compile(f.read(), '__init__.py', 'exec') fake_global = {'__name__': '__main__'} try: exec(ast, fake_global) except (SystemError, ImportError) as e: print('System error') long_description = fake_global['__doc__'] version = fake_global['__version__'] setup( name='flatlatex', # Versions should comply with PEP440. For a discussion on single-sourcing # the version across setup.py and the project code, see # https://packaging.python.org/en/latest/single_source_version.html version=version, description='A LaTeX math converter to unicode text', long_description=long_description, # The project's main homepage. url='https://github.com/jb-leger/flatlatex', # Author details author='Jean-Benoist Leger', author_email='jb@leger.tf', # Choose your license license='BSD-2', # See https://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=[ # How mature is this project? Common values are # 3 - Alpha # 4 - Beta # 5 - Production/Stable 'Development Status :: 3 - Alpha', # Indicate who your project is intended for 'Intended Audience :: Science/Research', 'Topic :: Text Processing :: Markup :: LaTeX', # Pick your license as you wish (should match "license" above) 'License :: OSI Approved', # Specify the Python versions you support here. In particular, ensure # that you indicate whether you support Python 2, Python 3 or both. 'Programming Language :: Python :: 3 :: Only', ], # What does your project relate to? keywords='latex math unicode', # You can just specify the packages manually here if your project is # simple. Or you can use find_packages(). packages=find_packages(exclude=['contrib', 'docs', 'tests']), # Alternatively, if you want to distribute just a my_module.py, uncomment # this: # py_modules=["my_module"], # List run-time dependencies here. These will be installed by pip when # your project is installed. For an analysis of "install_requires" vs pip's # requirements files see: # https://packaging.python.org/en/latest/requirements.html install_requires=['regex'], # List additional groups of dependencies here (e.g. development # dependencies). You can install these using the following syntax, # for example: # $ pip install -e .[dev,test] extras_require={ 'test': ['pytest'], }, # If there are data files included in your packages that need to be # installed, specify them here. If using Python 2.6 or less, then these # have to be included in MANIFEST.in as well. package_data={ }, # Although 'package_data' is the preferred approach, in some case you may # need to place data files outside of your packages. See: # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files # noqa # In this case, 'data_file' will be installed into '/my_data' data_files=[], python_requires='>=3', # To provide executable scripts, use entry points in preference to the # "scripts" keyword. Entry points provide cross-platform support and allow # pip to create the appropriate form of executable for the target platform. entry_points={ 'console_scripts': [ "flatlatex=flatlatex.bin.flatlatex:main", ], }, )