##// END OF EJS Templates
FIX, don't use regex
Jonathan Frederic -
Show More
@@ -1,115 +1,114
1 """Latex filters.
1 """Latex filters.
2
2
3 Module of useful filters for processing Latex within Jinja latex templates.
3 Module of useful filters for processing Latex within Jinja latex templates.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 import re
16 import re
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Globals and constants
19 # Globals and constants
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 #Latex substitutions for escaping latex.
22 # Latex substitutions for escaping latex.
23 LATEX_SUBS = (
23 # see: http://stackoverflow.com/questions/16259923/how-can-i-escape-latex-special-characters-inside-django-templates
24 (re.compile('\033\[[0-9;]+m'),''), # handle console escapes
24 LATEX_SUBS = {
25 (re.compile(r'\\'), r'{\\textbackslash}'),
25 '&': r'\&',
26 (re.compile(r'([{}_#%&$])'), r'\\\1'),
26 '%': r'\%',
27 (re.compile(r'~'), r'\~{}'),
27 '$': r'\$',
28 (re.compile(r'\^'), r'\^{}'),
28 '#': r'\#',
29 (re.compile(r'"'), r"''"),
29 '_': r'\letterunderscore{}',
30 (re.compile(r'\.\.\.+'), r'\\ldots'),
30 '{': r'\letteropenbrace{}',
31 )
31 '}': r'\letterclosebrace{}',
32 '~': r'\lettertilde{}',
33 '^': r'\letterhat{}',
34 '\\': r'\letterbackslash{}'}
35
32
36
33 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
34 # Functions
38 # Functions
35 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
36
40
37 __all__ = [
41 __all__ = ['escape_latex',
38 'escape_latex',
42 'strip_math_space']
39 'strip_math_space'
40 ]
41
42
43
43 def escape_latex(text):
44 def escape_latex(text):
44 """
45 """
45 Escape characters that may conflict with latex.
46 Escape characters that may conflict with latex.
46
47
47 Parameters
48 Parameters
48 ----------
49 ----------
49 text : str
50 text : str
50 Text containing characters that may conflict with Latex
51 Text containing characters that may conflict with Latex
51 """
52 """
52 return_text = text
53
53 for pattern, replacement in LATEX_SUBS:
54 return ''.join([LATEX_SUBS.get(c, c) for c in text])
54 return_text = pattern.sub(replacement, return_text)
55 return return_text
56
55
57
56
58 def strip_math_space(text):
57 def strip_math_space(text):
59 """
58 """
60 Remove the space between latex math commands and enclosing $ symbols.
59 Remove the space between latex math commands and enclosing $ symbols.
61 This filter is important because latex isn't as flexible as the notebook
60 This filter is important because latex isn't as flexible as the notebook
62 front end when it comes to flagging math using ampersand symbols.
61 front end when it comes to flagging math using ampersand symbols.
63
62
64 Parameters
63 Parameters
65 ----------
64 ----------
66 text : str
65 text : str
67 Text to filter.
66 Text to filter.
68 """
67 """
69
68
70 # First, scan through the markdown looking for $. If
69 # First, scan through the markdown looking for $. If
71 # a $ symbol is found, without a preceding \, assume
70 # a $ symbol is found, without a preceding \, assume
72 # it is the start of a math block. UNLESS that $ is
71 # it is the start of a math block. UNLESS that $ is
73 # not followed by another within two math_lines.
72 # not followed by another within two math_lines.
74 math_regions = []
73 math_regions = []
75 math_lines = 0
74 math_lines = 0
76 within_math = False
75 within_math = False
77 math_start_index = 0
76 math_start_index = 0
78 ptext = ''
77 ptext = ''
79 last_character = ""
78 last_character = ""
80 skip = False
79 skip = False
81 for index, char in enumerate(text):
80 for index, char in enumerate(text):
82
81
83 #Make sure the character isn't preceeded by a backslash
82 #Make sure the character isn't preceeded by a backslash
84 if (char == "$" and last_character != "\\"):
83 if (char == "$" and last_character != "\\"):
85
84
86 # Close the math region if this is an ending $
85 # Close the math region if this is an ending $
87 if within_math:
86 if within_math:
88 within_math = False
87 within_math = False
89 skip = True
88 skip = True
90 ptext = ptext+'$'+text[math_start_index+1:index].strip()+'$'
89 ptext = ptext+'$'+text[math_start_index+1:index].strip()+'$'
91 math_regions.append([math_start_index, index+1])
90 math_regions.append([math_start_index, index+1])
92 else:
91 else:
93
92
94 # Start a new math region
93 # Start a new math region
95 within_math = True
94 within_math = True
96 math_start_index = index
95 math_start_index = index
97 math_lines = 0
96 math_lines = 0
98
97
99 # If we are in a math region, count the number of lines parsed.
98 # If we are in a math region, count the number of lines parsed.
100 # Cancel the math region if we find two line breaks!
99 # Cancel the math region if we find two line breaks!
101 elif char == "\n":
100 elif char == "\n":
102 if within_math:
101 if within_math:
103 math_lines += 1
102 math_lines += 1
104 if math_lines > 1:
103 if math_lines > 1:
105 within_math = False
104 within_math = False
106 ptext = ptext+text[math_start_index:index]
105 ptext = ptext+text[math_start_index:index]
107
106
108 # Remember the last character so we can easily watch
107 # Remember the last character so we can easily watch
109 # for backslashes
108 # for backslashes
110 last_character = char
109 last_character = char
111 if not within_math and not skip:
110 if not within_math and not skip:
112 ptext = ptext+char
111 ptext = ptext+char
113 if skip:
112 if skip:
114 skip = False
113 skip = False
115 return ptext
114 return ptext
@@ -1,65 +1,65
1 """
1 """
2 Module with tests for Latex
2 Module with tests for Latex
3 """
3 """
4
4
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 from ...tests.base import TestsBase
17 from ...tests.base import TestsBase
18 from ..latex import escape_latex, strip_math_space
18 from ..latex import escape_latex, strip_math_space
19
19
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Class
22 # Class
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 class TestLatex(TestsBase):
25 class TestLatex(TestsBase):
26
26
27
27
28 def test_escape_latex(self):
28 def test_escape_latex(self):
29 """escape_latex test"""
29 """escape_latex test"""
30 tests = [
30 tests = [
31 (r'How are \you doing today?', r'How are \textbackslashyou doing today?'),
31 (r'How are \you doing today?', r'How are \letterbackslash{}you doing today?'),
32 (r'\escapechar=`\A\catcode`\|=0 |string|foo', r'\textbackslashescapechar=`\textbackslashA\textbackslashcatcode`\textbackslash|=0 |string|foo'),
32 (r'\escapechar=`\A\catcode`\|=0 |string|foo', r'\letterbackslash{}escapechar=`\letterbackslash{}A\letterbackslash{}catcode`\letterbackslash{}|=0 |string|foo'),
33 (r'# $ % & ~ _ ^ \ { }',r'\# \$ \% \& \~{} \_ \^{} \textbackslash \{ \}'),
33 (r'# $ % & ~ _ ^ \ { }', r'\# \$ \% \& \lettertilde{} \letterunderscore{} \letterhat{} \letterbackslash{} \letteropenbrace{} \letterclosebrace{}'),
34 ('','')]
34 ('','')]
35
35
36 for test in tests:
36 for test in tests:
37 yield self._try_escape_latex(test[0], test[1])
37 yield self._try_escape_latex(test[0], test[1])
38
38
39
39
40 def _try_escape_latex(self, test, result):
40 def _try_escape_latex(self, test, result):
41 """Try to remove latex from string"""
41 """Try to remove latex from string"""
42 self.assertEqual(escape_latex(test), result)
42 self.assertEqual(escape_latex(test), result)
43
43
44
44
45 def test_strip_math_space(self):
45 def test_strip_math_space(self):
46 """strip_math_space test"""
46 """strip_math_space test"""
47 tests = [
47 tests = [
48 ('$e$','$e$'),
48 ('$e$','$e$'),
49 ('$ e $','$e$'),
49 ('$ e $','$e$'),
50 ('xxx$e^i$yyy','xxx$e^i$yyy'),
50 ('xxx$e^i$yyy','xxx$e^i$yyy'),
51 ('xxx$ e^i $yyy','xxx$e^i$yyy'),
51 ('xxx$ e^i $yyy','xxx$e^i$yyy'),
52 ('xxx$e^i $yyy','xxx$e^i$yyy'),
52 ('xxx$e^i $yyy','xxx$e^i$yyy'),
53 ('xxx$ e^i$yyy','xxx$e^i$yyy'),
53 ('xxx$ e^i$yyy','xxx$e^i$yyy'),
54 ('\$ e $ e $','\$ e $e$'),
54 ('\$ e $ e $','\$ e $e$'),
55 ('','')]
55 ('','')]
56
56
57 for test in tests:
57 for test in tests:
58 yield self._try_strip_math_space(test[0], test[1])
58 yield self._try_strip_math_space(test[0], test[1])
59
59
60
60
61 def _try_strip_math_space(self, test, result):
61 def _try_strip_math_space(self, test, result):
62 """
62 """
63 Try to remove spaces between dollar symbols and contents correctly
63 Try to remove spaces between dollar symbols and contents correctly
64 """
64 """
65 self.assertEqual(strip_math_space(test), result)
65 self.assertEqual(strip_math_space(test), result)
General Comments 0
You need to be logged in to leave comments. Login now